feat(monitor): match motion to report intervals
This commit is contained in:
@@ -39,17 +39,18 @@ type MonitorWorkspaceResponse struct {
|
||||
}
|
||||
|
||||
type MonitorMapPoint struct {
|
||||
VIN string `json:"vin"`
|
||||
Plate string `json:"plate"`
|
||||
Protocol string `json:"protocol"`
|
||||
Protocols []string `json:"protocols"`
|
||||
Longitude float64 `json:"longitude"`
|
||||
Latitude float64 `json:"latitude"`
|
||||
SpeedKmh float64 `json:"speedKmh"`
|
||||
SOCPercent float64 `json:"socPercent"`
|
||||
TotalMileageKm float64 `json:"totalMileageKm"`
|
||||
LastSeen string `json:"lastSeen"`
|
||||
Status string `json:"status"`
|
||||
VIN string `json:"vin"`
|
||||
Plate string `json:"plate"`
|
||||
Protocol string `json:"protocol"`
|
||||
Protocols []string `json:"protocols"`
|
||||
Longitude float64 `json:"longitude"`
|
||||
Latitude float64 `json:"latitude"`
|
||||
SpeedKmh float64 `json:"speedKmh"`
|
||||
SOCPercent float64 `json:"socPercent"`
|
||||
TotalMileageKm float64 `json:"totalMileageKm"`
|
||||
LastSeen string `json:"lastSeen"`
|
||||
ReportIntervalMs *int64 `json:"reportIntervalMs,omitempty"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type MonitorMapCluster struct {
|
||||
@@ -1024,6 +1025,7 @@ type VehicleRealtimeRow struct {
|
||||
SOCPercent float64 `json:"socPercent"`
|
||||
TotalMileageKm float64 `json:"totalMileageKm"`
|
||||
LastSeen string `json:"lastSeen"`
|
||||
ReportIntervalMs *int64 `json:"reportIntervalMs,omitempty"`
|
||||
}
|
||||
|
||||
type HistoryLocationRow struct {
|
||||
|
||||
@@ -394,6 +394,7 @@ func buildVehicleRealtimeSQL(query url.Values) SQLQuery {
|
||||
}
|
||||
groupSQL := `FROM vehicle_realtime_location l ` +
|
||||
`LEFT JOIN vehicle_identity_binding b ON b.vin = l.vin ` +
|
||||
`LEFT JOIN vehicle_realtime_snapshot s ON s.vin = l.vin AND s.protocol = l.protocol ` +
|
||||
`WHERE ` + strings.Join(where, " AND ") + ` ` +
|
||||
`GROUP BY l.vin, b.plate, b.phone, b.oem ` +
|
||||
havingSQL
|
||||
@@ -414,7 +415,8 @@ func buildVehicleRealtimeSQL(query url.Values) SQLQuery {
|
||||
`COALESCE(SUBSTRING_INDEX(GROUP_CONCAT(CAST(l.speed_kmh AS CHAR) ORDER BY ` + orderExpr + `), ',', 1), '') AS speed_kmh, ` +
|
||||
`COALESCE(SUBSTRING_INDEX(GROUP_CONCAT(CAST(l.soc_percent AS CHAR) ORDER BY ` + orderExpr + `), ',', 1), '') AS soc_percent, ` +
|
||||
`COALESCE(SUBSTRING_INDEX(GROUP_CONCAT(CAST(l.total_mileage_km AS CHAR) ORDER BY ` + orderExpr + `), ',', 1), '') AS total_mileage_km, ` +
|
||||
`COALESCE(DATE_FORMAT(MAX(l.updated_at), '%Y-%m-%d %H:%i:%s'), '') AS last_seen ` +
|
||||
`COALESCE(DATE_FORMAT(MAX(l.updated_at), '%Y-%m-%d %H:%i:%s'), '') AS last_seen, ` +
|
||||
`CAST(SUBSTRING_INDEX(GROUP_CONCAT(COALESCE(s.access_report_interval_ms, -1) ORDER BY ` + orderExpr + `), ',', 1) AS SIGNED) AS report_interval_ms ` +
|
||||
groupSQL +
|
||||
`ORDER BY MAX(l.updated_at) DESC, l.vin ASC LIMIT ? OFFSET ?`,
|
||||
Args: args,
|
||||
|
||||
@@ -401,7 +401,8 @@ func (s *ProductionStore) VehicleRealtime(ctx context.Context, query url.Values)
|
||||
var onlineProtocols string
|
||||
var online int
|
||||
var longitude, latitude, speed, soc, mileage string
|
||||
if err := rows.Scan(&row.VIN, &row.Plate, &row.Phone, &row.OEM, &protocols, &onlineProtocols, &row.SourceCount, &row.OnlineSourceCount, &online, &row.BindingStatus, &row.PrimaryProtocol, &longitude, &latitude, &speed, &soc, &mileage, &row.LastSeen); err != nil {
|
||||
var reportIntervalMs int64
|
||||
if err := rows.Scan(&row.VIN, &row.Plate, &row.Phone, &row.OEM, &protocols, &onlineProtocols, &row.SourceCount, &row.OnlineSourceCount, &online, &row.BindingStatus, &row.PrimaryProtocol, &longitude, &latitude, &speed, &soc, &mileage, &row.LastSeen, &reportIntervalMs); err != nil {
|
||||
return Page[VehicleRealtimeRow]{}, err
|
||||
}
|
||||
row.Protocols = splitCSV(protocols)
|
||||
@@ -413,6 +414,9 @@ func (s *ProductionStore) VehicleRealtime(ctx context.Context, query url.Values)
|
||||
row.SpeedKmh = parseFloatString(speed)
|
||||
row.SOCPercent = parseFloatString(soc)
|
||||
row.TotalMileageKm = parseFloatString(mileage)
|
||||
if reportIntervalMs > 0 {
|
||||
row.ReportIntervalMs = &reportIntervalMs
|
||||
}
|
||||
items = append(items, row)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
|
||||
@@ -200,7 +200,7 @@ func TestBuildRealtimeLocationSQL(t *testing.T) {
|
||||
func TestBuildVehicleRealtimeSQL(t *testing.T) {
|
||||
query := url.Values{"vin": {"粤A"}, "protocol": {"JT808"}, "online": {"online"}, "limit": {"10"}, "offset": {"20"}}
|
||||
built := buildVehicleRealtimeSQL(query)
|
||||
for _, want := range []string{"vehicle_realtime_location", "vehicle_identity_binding", "GROUP BY l.vin", "GROUP_CONCAT(DISTINCT l.protocol", "online_source_count", "vehicle_realtime_count"} {
|
||||
for _, want := range []string{"vehicle_realtime_location", "vehicle_identity_binding", "vehicle_realtime_snapshot", "access_report_interval_ms", "GROUP BY l.vin", "GROUP_CONCAT(DISTINCT l.protocol", "online_source_count", "vehicle_realtime_count"} {
|
||||
if !strings.Contains(built.Text+built.CountText, want) {
|
||||
t.Fatalf("SQL missing %q: %s / %s", want, built.Text, built.CountText)
|
||||
}
|
||||
|
||||
@@ -606,7 +606,8 @@ func buildMonitorMapResponse(vehicles Page[VehicleRealtimeRow], query url.Values
|
||||
VIN: vehicle.VIN, Plate: vehicle.Plate, Protocol: vehicle.PrimaryProtocol, Protocols: vehicle.Protocols,
|
||||
Longitude: vehicle.Longitude, Latitude: vehicle.Latitude, SpeedKmh: vehicle.SpeedKmh,
|
||||
SOCPercent: vehicle.SOCPercent, TotalMileageKm: vehicle.TotalMileageKm, LastSeen: vehicle.LastSeen,
|
||||
Status: monitorVehicleStatus(vehicle),
|
||||
ReportIntervalMs: vehicle.ReportIntervalMs,
|
||||
Status: monitorVehicleStatus(vehicle),
|
||||
})
|
||||
}
|
||||
if zoom >= monitorPointZoom && len(points) <= monitorPointLimit {
|
||||
|
||||
@@ -312,8 +312,9 @@ func TestMonitorWorkspaceSharesOneRealtimeSnapshot(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMonitorMapUsesMeaningfulClustersAndReleasesPointsAtDetailZoom(t *testing.T) {
|
||||
reportIntervalMs := int64(30000)
|
||||
vehicles := Page[VehicleRealtimeRow]{Items: []VehicleRealtimeRow{
|
||||
{VIN: "NEAR-1", Plate: "粤A00001", Online: true, Longitude: 113.260, Latitude: 23.130},
|
||||
{VIN: "NEAR-1", Plate: "粤A00001", Online: true, Longitude: 113.260, Latitude: 23.130, ReportIntervalMs: &reportIntervalMs},
|
||||
{VIN: "NEAR-2", Plate: "粤A00002", Online: true, Longitude: 113.265, Latitude: 23.135},
|
||||
{VIN: "SINGLE", Plate: "粤A00003", Online: true, Longitude: 113.600, Latitude: 23.500},
|
||||
}, Total: 3, Limit: 3}
|
||||
@@ -333,6 +334,9 @@ func TestMonitorMapUsesMeaningfulClustersAndReleasesPointsAtDetailZoom(t *testin
|
||||
if detail.Mode != "points" || len(detail.Points) != 3 || len(detail.Clusters) != 0 {
|
||||
t.Fatalf("zoom 11 must release controlled data as exact points: %+v", detail)
|
||||
}
|
||||
if detail.Points[0].ReportIntervalMs == nil || *detail.Points[0].ReportIntervalMs != reportIntervalMs {
|
||||
t.Fatalf("map point must preserve the primary protocol report interval: %+v", detail.Points[0])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMonitorMapTenThousandVehicles(b *testing.B) {
|
||||
|
||||
Reference in New Issue
Block a user