M5-T07: add latest-reading cards and Recharts trend charts; readings return most-recent rows

This commit is contained in:
2026-06-22 14:17:59 +02:00
parent 2dc469274a
commit 68165f0e01
13 changed files with 1454 additions and 23 deletions
+86
View File
@@ -8,6 +8,9 @@
* ['modbus-devices'] — device list
* ['modbus-device', uuid] — single device
* ['modbus-profiles'] — profile list (rarely changes)
* ['modbus-latest', uuid] — latest reading per device
* ['modbus-metrics', uuid] — profile metric metadata per device
* ['modbus-readings', uuid, params] — time-range readings per device
*
* On success, mutations invalidate the device list so the UI refreshes.
*/
@@ -25,6 +28,22 @@ export type ModbusDeviceCreate = components['schemas']['ModbusDeviceCreate']
export type ModbusDeviceUpdate = components['schemas']['ModbusDeviceUpdate']
export type ProfileSummary = components['schemas']['ProfileSummary']
export type ModbusTestReadResponse = components['schemas']['ModbusTestReadResponse']
export type ModbusLatestResponse = components['schemas']['ModbusLatestResponse']
export type ModbusMetricsResponse = components['schemas']['ModbusMetricsResponse']
export type MetricInfo = components['schemas']['MetricInfo']
export type ModbusReadingResponse = components['schemas']['ModbusReadingResponse']
export type ModbusReadingsResponse = components['schemas']['ModbusReadingsResponse']
// ---------------------------------------------------------------------------
// Reading query params
// ---------------------------------------------------------------------------
export interface ReadingsQueryParams {
start?: string | null
end?: string | null
/** Capped server-side; default max is 1000 to avoid pulling full history. */
limit?: number
}
// ---------------------------------------------------------------------------
// Query: list all devices
@@ -112,3 +131,70 @@ export function useTestReadDevice() {
}),
})
}
// ---------------------------------------------------------------------------
// Query: latest reading for a device
// ---------------------------------------------------------------------------
export function useLatestReading(uuid: string) {
return useQuery({
queryKey: ['modbus-latest', uuid],
queryFn: async () => {
const res = await apiClient.GET('/api/modbus/devices/{uuid}/latest', {
params: { path: { uuid } },
})
return res.data
},
// Refresh every 10 s to show up-to-date readings.
refetchInterval: 10_000,
})
}
// ---------------------------------------------------------------------------
// Query: profile metric metadata for a device (label/unit per key)
// ---------------------------------------------------------------------------
export function useMetrics(uuid: string) {
return useQuery({
queryKey: ['modbus-metrics', uuid],
queryFn: async () => {
const res = await apiClient.GET('/api/modbus/devices/{uuid}/metrics', {
params: { path: { uuid } },
})
return res.data
},
// Metrics are static (tied to profile version); 5 min stale time.
staleTime: 5 * 60 * 1000,
})
}
// ---------------------------------------------------------------------------
// Query: time-range readings for a device (window + limit — never full-table)
// ---------------------------------------------------------------------------
/** Hard upper-bound on readings fetched; prevents accidental full-table pulls. */
const READINGS_MAX_LIMIT = 1000
export function useReadings(uuid: string, params: ReadingsQueryParams) {
const { start, end, limit } = params
const effectiveLimit = Math.min(limit ?? READINGS_MAX_LIMIT, READINGS_MAX_LIMIT)
return useQuery({
queryKey: ['modbus-readings', uuid, { start, end, limit: effectiveLimit }],
queryFn: async () => {
const res = await apiClient.GET('/api/modbus/devices/{uuid}/readings', {
params: {
path: { uuid },
query: {
...(start ? { start } : {}),
...(end ? { end } : {}),
limit: effectiveLimit,
},
},
})
return res.data
},
// Enabled only when we have valid uuid; start/end may be null (full window).
enabled: !!uuid,
})
}