即時需量訂閱

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>
import { ref, onMounted, onUnmounted } from "vue";
import { ref, onMounted, onUnmounted, watch } from "vue";
import * as echarts from "echarts";
import useElecStore from "../stores/useElecDemandStore";
import dayjs from "dayjs";
import useElecDemandStore from "../stores/useElecDemandStore";
const store = useElecStore();
//
const data = {
categories: [
"16:22:29",
"16:22:37",
"16:22:47",
"16:23:00",
"16:23:08",
"16:23:18",
],
series: [
{
name: "即時趨勢",
type: "line",
data: [320, 310, 300, 305, 310, 300],
smooth: true,
lineStyle: {
width: 3,
},
},
{
name: "契約容量",
type: "line",
data: [400, 400, 400, 400, 400, 400],
smooth: true,
lineStyle: {
width: 3,
},
},
{
name: "警戒容量",
type: "line",
data: [350, 350, 350, 350, 350, 350],
smooth: true,
lineStyle: {
width: 3,
},
},
{
name: "復歸值",
type: "line",
data: [280, 300, 290, 295, 300, 290],
smooth: true,
lineStyle: {
width: 3,
},
},
],
};
const storeDemand = useElecDemandStore();
const demand_chart = ref(null);
const defaultChartOption = ref({
let chartInstance = null;
//
const categories = ref([]); //
const seriesData = ref({
P: [], //
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: [
{
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 },
},
],
});
}
};
//
const chartOption = ref({
tooltip: {
trigger: "axis",
},
legend: {
data: data.series.map((s) => s.name),
data: ["即時趨勢", "契約容量", "警戒容量", "復歸值"],
orient: "horizontal",
bottom: "0%",
},
@ -73,21 +114,65 @@ const defaultChartOption = ref({
},
xAxis: {
type: "category",
data: data.categories,
data: categories.value,
},
yAxis: {
type: "value",
},
series: data.series,
color: ['#5470c6', '#91cc75', '#fac858', '#52aea1'] ,
series: [
{
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 () => {
// await store.getElecDataFromBaja();
//
await storeDemand.getElecDemandFromBaja();
//
chartInstance = echarts.init(demand_chart.value);
chartInstance.setOption(defaultChartOption.value);
chartInstance.setOption(chartOption.value);
});
onUnmounted(() => {
@ -96,7 +181,9 @@ onUnmounted(() => {
}
});
</script>
<template>
<div ref="demand_chart" style="width: 100%; height: 200px"></div>
</template>
<style scoped></style>

View File

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

View File

@ -1,44 +1,115 @@
import { ref } from "vue";
import { defineStore } from "pinia";
// import dayjs from "dayjs";
import type { NiagaraElecData } from "../utils/types";
import type { NiagaraElecDemandData } from "../utils/types";
const useElecStore = defineStore("elecData", () => {
const elecData = ref<NiagaraElecData[]>([]);
const useElecDemandStore = defineStore("elecDemand", () => {
const elecData = ref<NiagaraElecDemandData[]>([]);
const subscribers = ref<any[]>([]);
// get data from baja
const getElecDataFromBaja = () => {
const getElecDemandFromBaja = () => {
// @ts-ignore
window.require &&
// @ts-ignore
window.requirejs(["baja!"], (baja: any) => {
console.log("進入 bajaSubscriber 準備執行 BQL 訂閱");
let eleclist: NiagaraElecData[] = [];
let eleclist: NiagaraElecDemandData[] = [];
baja.Ord.make(
`local:|foxs:4912|station:|neql:EMS:kw|bql:select slotPath,parent.displayName,name`
).get({
cursor: {
before: () => {
},
before: () => {},
each: (record: any) => {
console.log("record", record);
// eleclist.push({
// slotPath: record.get("slotPath"),
// displayName: record.get("parent$2edisplayName"),
// id: record.get("NumericInterval$2ehistoryConfig$2eid").$cEncStr,
// out: record.get("out").get("value"),
// });
const slotPath = record.get("slotPath");
const displayName = record.get("parent$2edisplayName");
const name = record.get("name");
const newItem: NiagaraElecDemandData = {
slotPath: slotPath,
displayName: displayName,
name: name,
out: 0,
};
eleclist.push(newItem);
},
after: () => {
elecData.value = eleclist;
console.log("Niagara 用電:", elecData.value);
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
);
}
});
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 useElecStore;
export default useElecDemandStore;

View File

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

View File

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