即時需量訂閱

This commit is contained in:
huliang 2025-04-23 16:52:10 +08:00
parent affc5291f4
commit 7f4d1e6e07
5 changed files with 251 additions and 89 deletions

View File

@ -1,66 +1,107 @@
<script setup> <script setup>
import { ref, onMounted, onUnmounted } from "vue"; import { ref, onMounted, onUnmounted, watch } from "vue";
import * as echarts from "echarts"; import * as echarts from "echarts";
import useElecStore from "../stores/useElecDemandStore"; import dayjs from "dayjs";
import useElecDemandStore from "../stores/useElecDemandStore";
const store = useElecStore(); const storeDemand = useElecDemandStore();
//
const data = { const demand_chart = ref(null);
categories: [ let chartInstance = null;
"16:22:29",
"16:22:37", //
"16:22:47", const categories = ref([]); //
"16:23:00", const seriesData = ref({
"16:23:08", P: [], //
"16:23:18", Engel: [], //
], kW1: [], //
kWAlarmRest: [], //
});
// 使 dayjs HH:mm:ss
const getCurrentTimestamp = () => {
return dayjs().format("HH:mm:ss");
};
//
const updateChartData = (elecData) => {
//
categories.value.push(getCurrentTimestamp());
//
const dataMap = {
P: null,
Engel: null,
kW1: null,
kWAlarmRest: null,
};
// name
elecData.forEach((item) => {
if (dataMap.hasOwnProperty(item.name)) {
dataMap[item.name] = item.out;
}
});
//
Object.keys(dataMap).forEach((key) => {
if (dataMap[key] !== null) {
seriesData.value[key].push(dataMap[key]);
} else {
// 使 0
seriesData.value[key].push(
seriesData.value[key][seriesData.value[key].length - 1] || 0
);
}
});
//
if (chartInstance) {
chartInstance.setOption({
xAxis: {
data: categories.value,
},
series: [ series: [
{ {
name: "即時趨勢", name: "即時趨勢",
type: "line", type: "line",
data: [320, 310, 300, 305, 310, 300], data: seriesData.value.P,
smooth: true, smooth: true,
lineStyle: { lineStyle: { width: 3 },
width: 3,
},
}, },
{ {
name: "契約容量", name: "契約容量",
type: "line", type: "line",
data: [400, 400, 400, 400, 400, 400], data: seriesData.value.Engel,
smooth: true, smooth: true,
lineStyle: { lineStyle: { width: 3 },
width: 3,
},
}, },
{ {
name: "警戒容量", name: "警戒容量",
type: "line", type: "line",
data: [350, 350, 350, 350, 350, 350], data: seriesData.value.kW1,
smooth: true, smooth: true,
lineStyle: { lineStyle: { width: 3 },
width: 3,
},
}, },
{ {
name: "復歸值", name: "復歸值",
type: "line", type: "line",
data: [280, 300, 290, 295, 300, 290], data: seriesData.value.kWAlarmRest,
smooth: true, smooth: true,
lineStyle: { lineStyle: { width: 3 },
width: 3,
},
}, },
], ],
});
}
}; };
const demand_chart = ref(null); //
const defaultChartOption = ref({ const chartOption = ref({
tooltip: { tooltip: {
trigger: "axis", trigger: "axis",
}, },
legend: { legend: {
data: data.series.map((s) => s.name), data: ["即時趨勢", "契約容量", "警戒容量", "復歸值"],
orient: "horizontal", orient: "horizontal",
bottom: "0%", bottom: "0%",
}, },
@ -73,21 +114,65 @@ const defaultChartOption = ref({
}, },
xAxis: { xAxis: {
type: "category", type: "category",
data: data.categories, data: categories.value,
}, },
yAxis: { yAxis: {
type: "value", type: "value",
}, },
series: data.series, series: [
color: ['#5470c6', '#91cc75', '#fac858', '#52aea1'] , {
name: "即時趨勢",
type: "line",
data: seriesData.value.P,
smooth: true,
lineStyle: { width: 3 },
},
{
name: "契約容量",
type: "line",
data: seriesData.value.Engel,
smooth: true,
lineStyle: { width: 3 },
},
{
name: "警戒容量",
type: "line",
data: seriesData.value.kW1,
smooth: true,
lineStyle: { width: 3 },
},
{
name: "復歸值",
type: "line",
data: seriesData.value.kWAlarmRest,
smooth: true,
lineStyle: { width: 3 },
},
],
color: ["#5470c6", "#91cc75", "#fac858", "#52aea1"],
}); });
let chartInstance = null; // // elecData
watch(
() => storeDemand.elecData,
(newElecData) => {
console.log("storeDemand.elecData updated:", newElecData);
if (newElecData && newElecData.length > 0) {
const allOutZero = newElecData.every((item) => item.out === 0);
if (!allOutZero) {
updateChartData(newElecData);
}
}
},
{ deep: true, immediate: true }
);
onMounted(async () => { onMounted(async () => {
// await store.getElecDataFromBaja(); //
await storeDemand.getElecDemandFromBaja();
//
chartInstance = echarts.init(demand_chart.value); chartInstance = echarts.init(demand_chart.value);
chartInstance.setOption(defaultChartOption.value); chartInstance.setOption(chartOption.value);
}); });
onUnmounted(() => { onUnmounted(() => {
@ -96,7 +181,9 @@ onUnmounted(() => {
} }
}); });
</script> </script>
<template> <template>
<div ref="demand_chart" style="width: 100%; height: 200px"></div> <div ref="demand_chart" style="width: 100%; height: 200px"></div>
</template> </template>
<style scoped></style> <style scoped></style>

View File

@ -8,10 +8,6 @@ const store = useElecStore();
const chartContainer = ref(null); const chartContainer = ref(null);
let chart = null; let chart = null;
const initChart = () => {
chart = echarts.init(chartContainer.value);
};
const updateChart = (elecData) => { const updateChart = (elecData) => {
if (!chart) { if (!chart) {
return; return;
@ -68,12 +64,12 @@ watch(
updateChart(newElecData); updateChart(newElecData);
} }
}, },
{ deep: true, immediate: true } { deep: true }
); );
onMounted(async () => { onMounted(async () => {
await store.getElecDataFromBaja(); await store.getElecDataFromBaja();
initChart(); chart = echarts.init(chartContainer.value);
store.startTimer(); store.startTimer();
}); });

View File

@ -1,44 +1,115 @@
import { ref } from "vue"; import { ref } from "vue";
import { defineStore } from "pinia"; import { defineStore } from "pinia";
// import dayjs from "dayjs"; import type { NiagaraElecDemandData } from "../utils/types";
import type { NiagaraElecData } from "../utils/types";
const useElecStore = defineStore("elecData", () => { const useElecDemandStore = defineStore("elecDemand", () => {
const elecData = ref<NiagaraElecData[]>([]); const elecData = ref<NiagaraElecDemandData[]>([]);
const subscribers = ref<any[]>([]);
// get data from baja // get data from baja
const getElecDataFromBaja = () => { const getElecDemandFromBaja = () => {
// @ts-ignore // @ts-ignore
window.require && window.require &&
// @ts-ignore // @ts-ignore
window.requirejs(["baja!"], (baja: any) => { window.requirejs(["baja!"], (baja: any) => {
console.log("進入 bajaSubscriber 準備執行 BQL 訂閱"); let eleclist: NiagaraElecDemandData[] = [];
let eleclist: NiagaraElecData[] = [];
baja.Ord.make( baja.Ord.make(
`local:|foxs:4912|station:|neql:EMS:kw|bql:select slotPath,parent.displayName,name` `local:|foxs:4912|station:|neql:EMS:kw|bql:select slotPath,parent.displayName,name`
).get({ ).get({
cursor: { cursor: {
before: () => { before: () => {},
},
each: (record: any) => { each: (record: any) => {
console.log("record", record); const slotPath = record.get("slotPath");
// eleclist.push({ const displayName = record.get("parent$2edisplayName");
// slotPath: record.get("slotPath"), const name = record.get("name");
// displayName: record.get("parent$2edisplayName"),
// id: record.get("NumericInterval$2ehistoryConfig$2eid").$cEncStr, const newItem: NiagaraElecDemandData = {
// out: record.get("out").get("value"), slotPath: slotPath,
// }); displayName: displayName,
name: name,
out: 0,
};
eleclist.push(newItem);
}, },
after: () => { after: () => {
elecData.value = eleclist; elecData.value = [];
console.log("Niagara 用電:", elecData.value); elecData.value.push(...eleclist);
eleclist.forEach((item, index) => {
subscribeToHistory(item, index);
});
}, },
}, },
}); });
}); });
}; };
return { getElecDataFromBaja, elecData }; const subscribeToHistory = (item: NiagaraElecDemandData, index: number) => {
const slotPath = item.slotPath;
const ordString = `local:|foxs:4912|station:|${slotPath}`;
// @ts-ignore
window.require &&
// @ts-ignore
window.requirejs(["baja!"], (baja: any) => {
// 建立訂閱器
const subscriber = new baja.Subscriber();
// 定義 changed 事件的處理函數
subscriber.attach("changed", (prop: any) => {
try {
if (prop && prop.getName() === "out") {
// 取得 out 的新值
const match = prop.$display.match(/^(\d+(\.\d+)?)/);
const newValue = match ? parseFloat(match[0]) : 0;
// 更新 elecData 中對應的 out 值
const updatedIndex = elecData.value.findIndex(
(data) => data.slotPath === item.slotPath
);
if (updatedIndex !== -1) {
const newElecData = [...elecData.value];
newElecData[updatedIndex] = {
...newElecData[updatedIndex],
out: Number(newValue),
};
elecData.value = newElecData;
console.log(
`Niagara 用電需求 ${item.name} 更新:`,
newValue
);
}
}
} catch (error: any) {
console.error(
`處理 ${item.name || index} 告警變化失敗: ${error.message}`,
error
);
}
}); });
export default useElecStore; baja.Ord.make(ordString)
.get({ subscriber })
.then(() => {
console.log(`Successfuly subscribed to ${item.name}`);
})
.catch((err: any) => {
console.error(`訂閱 ${item.name || index} 失敗: ${err.message}`);
subscriber.detach("changed"); // 移除事件監聽器
});
subscribers.value.push(subscriber);
});
};
const clearAllSubscriber = () => {
subscribers.value.forEach((subscriber) => {
subscriber.detach("changed");
subscriber.unsubscribeAll(); // 移除所有訂閱
});
subscribers.value = [];
console.log("所有訂閱已清除");
};
return { getElecDemandFromBaja, elecData, clearAllSubscriber };
});
export default useElecDemandStore;

View File

@ -3,7 +3,7 @@ import { defineStore } from "pinia";
import dayjs from "dayjs"; import dayjs from "dayjs";
import type { NiagaraElecData } from "../utils/types"; import type { NiagaraElecData } from "../utils/types";
const useElecStore = defineStore("elecData", () => { const useElecStore = defineStore("elecDist", () => {
const elecData = ref<NiagaraElecData[]>([]); const elecData = ref<NiagaraElecData[]>([]);
// @ts-ignore // @ts-ignore
let timerId = null; let timerId = null;
@ -40,7 +40,8 @@ const useElecStore = defineStore("elecData", () => {
const validElecList = eleclist.filter( const validElecList = eleclist.filter(
(item) => item.id !== undefined (item) => item.id !== undefined
); );
elecData.value = [...elecData.value, ...validElecList]; elecData.value = [];
elecData.value.push(...validElecList);
validElecList.forEach((item) => { validElecList.forEach((item) => {
subscribeToHistory(item); subscribeToHistory(item);
}); });
@ -108,7 +109,7 @@ const useElecStore = defineStore("elecData", () => {
const startTimer = () => { const startTimer = () => {
timerId = setInterval(() => { timerId = setInterval(() => {
updateHistoryData(); updateHistoryData();
}, 60 * 1000); // 每小時執行一次 }, 60 * 60 * 1000); // 每小時執行一次
}; };
// 停止定時器 // 停止定時器

View File

@ -5,3 +5,10 @@ export interface NiagaraElecData {
id: string; id: string;
out: number; out: number;
} }
export interface NiagaraElecDemandData {
slotPath: string;
displayName: string;
name: string;
out: number;
}