Skip to content

Commit f29436e

Browse files
committed
Add detailed trip maps and charge charts
1 parent b7d768c commit f29436e

6 files changed

Lines changed: 357 additions & 27 deletions

File tree

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,23 @@ sudo docker compose up -d
108108

109109
`WEB_PORT` 默认是 `80`。如果机器前面还有负载均衡或网关,建议让它直接转发到宿主机 `80`,不要额外发布 TeslaMate、API、PostgreSQL 或 MQTT 端口。
110110

111+
### 3. 只更新 dashboard
112+
113+
如果改动只涉及后端 API 或前端页面,不需要重建数据库、TeslaMate 或 MQTT。生产环境拉取新镜像后只重启 `api``web`
114+
115+
```bash
116+
sudo docker compose pull api web
117+
sudo docker compose up -d --no-deps api web
118+
```
119+
120+
本地源码验证时同样可以只重建这两个服务:
121+
122+
```bash
123+
sudo docker compose -f compose.local.yaml up -d --build --no-deps api web
124+
```
125+
126+
只有修改了 `database` 镜像、恢复脚本、TeslaMate、MQTT 或需要重新导入备份时,才需要处理对应服务或数据库 volume。
127+
111128
## 导入备份
112129

113130
把备份文件放在 `compose.yaml` 同级目录,数据库 volume 首次创建时会自动导入。优先识别这些文件名:

backend/app/main.py

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -636,13 +636,50 @@ def dashboard(
636636
and c.efficiency is not null
637637
then (((d.start_rated_range_km - d.end_rated_range_km) * c.efficiency) / d.distance * 100)::float8
638638
else null
639-
end as estimated_kwh_per_100km
639+
end as estimated_kwh_per_100km,
640+
coalesce(route.route_points, '[]'::jsonb) as route_points
640641
from public.drives d
641642
join public.cars c on c.id = d.car_id
642643
left join public.addresses sa on sa.id = d.start_address_id
643644
left join public.addresses ea on ea.id = d.end_address_id
644645
left join public.geofences sg on sg.id = d.start_geofence_id
645646
left join public.geofences eg on eg.id = d.end_geofence_id
647+
left join lateral (
648+
select jsonb_agg(
649+
jsonb_build_object(
650+
'sampled_at', sampled.date,
651+
'latitude', sampled.latitude::float8,
652+
'longitude', sampled.longitude::float8,
653+
'speed', sampled.speed,
654+
'power', sampled.power,
655+
'battery_level', sampled.battery_level
656+
)
657+
order by sampled.date
658+
) as route_points
659+
from (
660+
select date, latitude, longitude, speed, power, battery_level
661+
from (
662+
select
663+
p.date,
664+
p.latitude,
665+
p.longitude,
666+
p.speed,
667+
p.power,
668+
p.battery_level,
669+
(row_number() over (order by p.date))::int as rn,
670+
(count(*) over ())::int as total
671+
from public.positions p
672+
where p.drive_id = d.id
673+
and p.latitude is not null
674+
and p.longitude is not null
675+
) ranked
676+
where total <= 160
677+
or rn = 1
678+
or rn = total
679+
or ((rn - 1) %% greatest(1, ceil(total / 160.0)::int)) = 0
680+
order by date
681+
) sampled
682+
) route on true
646683
where d.car_id = %s
647684
and (%s::timestamp is null or d.start_date >= %s::timestamp)
648685
and d.start_date <= %s::timestamp
@@ -724,7 +761,8 @@ def dashboard(
724761
ch.latest_charge_at,
725762
ch.max_power_kw,
726763
ch.fast_charger,
727-
({active_charge_condition()})::bool as is_active
764+
({active_charge_condition()})::bool as is_active,
765+
coalesce(charge_samples.samples, '[]'::jsonb) as samples
728766
from public.charging_processes cp
729767
left join public.addresses a on a.id = cp.address_id
730768
left join public.geofences g on g.id = cp.geofence_id
@@ -745,6 +783,47 @@ def dashboard(
745783
order by date desc
746784
limit 1
747785
) latest on true
786+
left join lateral (
787+
select jsonb_agg(
788+
jsonb_build_object(
789+
'sampled_at', sampled.date,
790+
'minute', extract(epoch from (sampled.date - cp.start_date)) / 60.0,
791+
'charger_power_kw', sampled.charger_power,
792+
'battery_level', sampled.battery_level,
793+
'charge_energy_added_kwh', sampled.charge_energy_added::float8,
794+
'charger_voltage', sampled.charger_voltage,
795+
'charger_actual_current', sampled.charger_actual_current
796+
)
797+
order by sampled.date
798+
) as samples
799+
from (
800+
select
801+
date,
802+
charger_power,
803+
battery_level,
804+
charge_energy_added,
805+
charger_voltage,
806+
charger_actual_current
807+
from (
808+
select
809+
c.date,
810+
c.charger_power,
811+
c.battery_level,
812+
c.charge_energy_added,
813+
c.charger_voltage,
814+
c.charger_actual_current,
815+
(row_number() over (order by c.date))::int as rn,
816+
(count(*) over ())::int as total
817+
from public.charges c
818+
where c.charging_process_id = cp.id
819+
) ranked
820+
where total <= 160
821+
or rn = 1
822+
or rn = total
823+
or ((rn - 1) %% greatest(1, ceil(total / 160.0)::int)) = 0
824+
order by date
825+
) sampled
826+
) charge_samples on true
748827
left join lateral (
749828
select date as latest_seen_at
750829
from public.positions

frontend/package-lock.json

Lines changed: 51 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,17 @@
1010
},
1111
"dependencies": {
1212
"@vitejs/plugin-react": "^4.3.4",
13+
"leaflet": "^1.9.4",
1314
"lucide-react": "^0.468.0",
15+
"react": "^18.3.1",
16+
"react-dom": "^18.3.1",
17+
"react-leaflet": "^4.2.1",
1418
"recharts": "^2.15.0",
1519
"typescript": "^5.7.2",
16-
"vite": "^6.0.7",
17-
"react": "^18.3.1",
18-
"react-dom": "^18.3.1"
20+
"vite": "^6.0.7"
1921
},
2022
"devDependencies": {
23+
"@types/leaflet": "^1.9.21",
2124
"@types/react": "^18.3.18",
2225
"@types/react-dom": "^18.3.5"
2326
}

0 commit comments

Comments
 (0)