|
|
@@ -114,6 +114,10 @@ function isRangeGreaterThan120Minutes(range: string[]) {
|
|
|
}
|
|
|
|
|
|
const REALTIME_RIGHT_BLANK_MS = 60 * 1000
|
|
|
+const SUSPENSION_WEIGHT_IDENTIFIER = '1001'
|
|
|
+const SUSPENSION_LIGHT_IDENTIFIER = '1008'
|
|
|
+const SUSPENSION_COMBINED_NAME = '悬重\\悬轻'
|
|
|
+const suspensionWeightAndLightEnabled = ref(false)
|
|
|
const selectedDate = ref<string[]>(getInitialDateRange())
|
|
|
const chartRef = ref<HTMLDivElement | null>(null)
|
|
|
const targetArea = ref<HTMLElement | null>(null)
|
|
|
@@ -150,11 +154,39 @@ async function loadDimensions() {
|
|
|
if (!query.id) return
|
|
|
dimensionLoading.value = true
|
|
|
try {
|
|
|
- const gateway = [
|
|
|
+ const gatewaySource = [
|
|
|
...(((await IotDeviceApi.getIotDeviceTds(Number(query.id))) as IotTdItem[]) ?? [])
|
|
|
]
|
|
|
.sort((a, b) => (b.modelOrder ?? 0) - (a.modelOrder ?? 0))
|
|
|
.map(mapToDimension)
|
|
|
+ const gatewayIdentifiers = new Set(gatewaySource.map((item) => String(item.identifier)))
|
|
|
+
|
|
|
+ suspensionWeightAndLightEnabled.value =
|
|
|
+ gatewayIdentifiers.has(SUSPENSION_WEIGHT_IDENTIFIER) &&
|
|
|
+ gatewayIdentifiers.has(SUSPENSION_LIGHT_IDENTIFIER)
|
|
|
+
|
|
|
+ if (suspensionWeightAndLightEnabled.value) {
|
|
|
+ const weightDimension = gatewaySource.find(
|
|
|
+ (item) => String(item.identifier) === SUSPENSION_WEIGHT_IDENTIFIER
|
|
|
+ )
|
|
|
+ const lightDimension = gatewaySource.find(
|
|
|
+ (item) => String(item.identifier) === SUSPENSION_LIGHT_IDENTIFIER
|
|
|
+ )
|
|
|
+ const weightValue = Number(weightDimension?.value ?? 0)
|
|
|
+ const lightValue = Number(lightDimension?.value ?? 0)
|
|
|
+
|
|
|
+ if (weightDimension && Number.isFinite(weightValue) && Number.isFinite(lightValue)) {
|
|
|
+ weightDimension.value = weightValue - lightValue
|
|
|
+ }
|
|
|
+
|
|
|
+ if (weightDimension) weightDimension.name = SUSPENSION_COMBINED_NAME
|
|
|
+ }
|
|
|
+
|
|
|
+ const gateway = gatewaySource.filter(
|
|
|
+ (item) =>
|
|
|
+ !suspensionWeightAndLightEnabled.value ||
|
|
|
+ String(item.identifier) !== SUSPENSION_LIGHT_IDENTIFIER
|
|
|
+ )
|
|
|
// const car = [
|
|
|
// ...(((await IotDeviceApi.getIotDeviceZHBDTds(Number(query.id))) as IotTdItem[]) ?? [])
|
|
|
// ]
|
|
|
@@ -203,7 +235,10 @@ function getSeriesRange(name: string) {
|
|
|
const configuredMax = normalizeRangeValue(dimension?.axisMax)
|
|
|
const configured =
|
|
|
configuredMin !== undefined && configuredMax !== undefined && configuredMin < configuredMax
|
|
|
- const min = configured ? configuredMin : 0
|
|
|
+ const isSuspensionCurve =
|
|
|
+ suspensionWeightAndLightEnabled.value &&
|
|
|
+ String(dimension?.identifier) === SUSPENSION_WEIGHT_IDENTIFIER
|
|
|
+ const min = configured ? configuredMin : isSuspensionCurve ? -100 : 0
|
|
|
const max = configured ? configuredMax : 100
|
|
|
|
|
|
return {
|
|
|
@@ -626,6 +661,7 @@ function updateDimensionValues(valueMap: Map<string, number>) {
|
|
|
function handleMessageUpdate(_topic: string, payload: any) {
|
|
|
const list = Array.isArray(payload) ? payload : Array.isArray(payload?.data) ? payload.data : []
|
|
|
const valueMap = new Map<string, number>()
|
|
|
+ const pointMap = new Map<string, ChartPoint>()
|
|
|
const updatedNames = new Set<string>()
|
|
|
|
|
|
list.forEach((item) => {
|
|
|
@@ -635,17 +671,49 @@ function handleMessageUpdate(_topic: string, payload: any) {
|
|
|
if (!identity || !Number.isFinite(numberValue)) return
|
|
|
|
|
|
valueMap.set(identity, numberValue)
|
|
|
+ const point = {
|
|
|
+ ts: getPointTimestamp(item.remark),
|
|
|
+ value: numberValue
|
|
|
+ }
|
|
|
+ pointMap.set(identity, point)
|
|
|
+
|
|
|
+ if (
|
|
|
+ suspensionWeightAndLightEnabled.value &&
|
|
|
+ (identity === SUSPENSION_WEIGHT_IDENTIFIER || identity === SUSPENSION_LIGHT_IDENTIFIER)
|
|
|
+ ) {
|
|
|
+ return
|
|
|
+ }
|
|
|
|
|
|
const dimension = dimensions.value.find((item) => item.identifier === identity)
|
|
|
if (!dimension) return
|
|
|
|
|
|
- appendChartPoint(dimension.name, {
|
|
|
- ts: getPointTimestamp(item.remark),
|
|
|
- value: numberValue
|
|
|
- })
|
|
|
+ appendChartPoint(dimension.name, point)
|
|
|
updatedNames.add(dimension.name)
|
|
|
})
|
|
|
|
|
|
+ if (suspensionWeightAndLightEnabled.value) {
|
|
|
+ const weightPoint = pointMap.get(SUSPENSION_WEIGHT_IDENTIFIER)
|
|
|
+ const lightPoint = pointMap.get(SUSPENSION_LIGHT_IDENTIFIER)
|
|
|
+
|
|
|
+ if (weightPoint || lightPoint) {
|
|
|
+ const combinedPoint = {
|
|
|
+ ts: Math.max(weightPoint?.ts ?? 0, lightPoint?.ts ?? 0),
|
|
|
+ value: (weightPoint?.value ?? 0) - (lightPoint?.value ?? 0)
|
|
|
+ }
|
|
|
+
|
|
|
+ valueMap.set(SUSPENSION_WEIGHT_IDENTIFIER, combinedPoint.value)
|
|
|
+
|
|
|
+ const dimension = dimensions.value.find(
|
|
|
+ (item) => String(item.identifier) === SUSPENSION_WEIGHT_IDENTIFIER
|
|
|
+ )
|
|
|
+
|
|
|
+ if (dimension) {
|
|
|
+ appendChartPoint(dimension.name, combinedPoint)
|
|
|
+ updatedNames.add(dimension.name)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if (!updatedNames.size) return
|
|
|
|
|
|
updateDimensionValues(valueMap)
|
|
|
@@ -665,6 +733,34 @@ async function startRealtimeMqtt() {
|
|
|
connect(`wss://aims.deepoil.cc/mqtt`, { password: token.value }, handleMessageUpdate)
|
|
|
}
|
|
|
|
|
|
+function normalizeHistoryPoints(data: unknown): ChartPoint[] {
|
|
|
+ if (!Array.isArray(data)) return []
|
|
|
+
|
|
|
+ return data
|
|
|
+ .map((item) => ({
|
|
|
+ ts: Number(item.ts),
|
|
|
+ value: Number(item.value)
|
|
|
+ }))
|
|
|
+ .filter((item) => Number.isFinite(item.ts) && Number.isFinite(item.value))
|
|
|
+ .sort((a, b) => a.ts - b.ts)
|
|
|
+}
|
|
|
+
|
|
|
+function mergeSuspensionHistory(weightPoints: ChartPoint[], lightPoints: ChartPoint[]) {
|
|
|
+ const mergedPoints = new Map<number, { weight?: number; light?: number }>()
|
|
|
+
|
|
|
+ weightPoints.forEach((point) => {
|
|
|
+ mergedPoints.set(point.ts, { ...mergedPoints.get(point.ts), weight: point.value })
|
|
|
+ })
|
|
|
+ lightPoints.forEach((point) => {
|
|
|
+ mergedPoints.set(point.ts, { ...mergedPoints.get(point.ts), light: point.value })
|
|
|
+ })
|
|
|
+
|
|
|
+ return Array.from(mergedPoints, ([ts, value]) => ({
|
|
|
+ ts,
|
|
|
+ value: (value.weight ?? 0) - (value.light ?? 0)
|
|
|
+ })).sort((a, b) => a.ts - b.ts)
|
|
|
+}
|
|
|
+
|
|
|
async function initLoadChartData({
|
|
|
realtime = true,
|
|
|
resetDate = realtime
|
|
|
@@ -696,9 +792,26 @@ async function initLoadChartData({
|
|
|
selectedDate.value[1]
|
|
|
)
|
|
|
|
|
|
- chartData.value[item.name] = (((res as any[]) ?? []) as any[])
|
|
|
- .sort((a, b) => a.ts - b.ts)
|
|
|
- .map((item) => ({ ts: item.ts, value: item.value }))
|
|
|
+ if (
|
|
|
+ suspensionWeightAndLightEnabled.value &&
|
|
|
+ String(item.identifier) === SUSPENSION_WEIGHT_IDENTIFIER
|
|
|
+ ) {
|
|
|
+ const lightRes = await getChartData(
|
|
|
+ data.value.deviceCode,
|
|
|
+ SUSPENSION_LIGHT_IDENTIFIER,
|
|
|
+ selectedDate.value[0],
|
|
|
+ selectedDate.value[1]
|
|
|
+ )
|
|
|
+
|
|
|
+ chartData.value[item.name] = mergeSuspensionHistory(
|
|
|
+ normalizeHistoryPoints(res),
|
|
|
+ normalizeHistoryPoints(lightRes)
|
|
|
+ )
|
|
|
+ } else {
|
|
|
+ chartData.value[item.name] = (((res as any[]) ?? []) as any[])
|
|
|
+ .sort((a, b) => a.ts - b.ts)
|
|
|
+ .map((item) => ({ ts: item.ts, value: item.value }))
|
|
|
+ }
|
|
|
} catch (error) {
|
|
|
console.error(error)
|
|
|
} finally {
|