即時需量訂閱
This commit is contained in:
parent
affc5291f4
commit
7f4d1e6e07
@ -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",
|
||||
],
|
||||
const storeDemand = useElecDemandStore();
|
||||
|
||||
const demand_chart = ref(null);
|
||||
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: [320, 310, 300, 305, 310, 300],
|
||||
data: seriesData.value.P,
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
},
|
||||
lineStyle: { width: 3 },
|
||||
},
|
||||
{
|
||||
name: "契約容量",
|
||||
type: "line",
|
||||
data: [400, 400, 400, 400, 400, 400],
|
||||
data: seriesData.value.Engel,
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
},
|
||||
lineStyle: { width: 3 },
|
||||
},
|
||||
{
|
||||
name: "警戒容量",
|
||||
type: "line",
|
||||
data: [350, 350, 350, 350, 350, 350],
|
||||
data: seriesData.value.kW1,
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
},
|
||||
lineStyle: { width: 3 },
|
||||
},
|
||||
{
|
||||
name: "復歸值",
|
||||
type: "line",
|
||||
data: [280, 300, 290, 295, 300, 290],
|
||||
data: seriesData.value.kWAlarmRest,
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
},
|
||||
lineStyle: { width: 3 },
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const demand_chart = ref(null);
|
||||
const defaultChartOption = ref({
|
||||
// 圖表選項
|
||||
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>
|
||||
|
@ -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();
|
||||
});
|
||||
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
@ -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); // 每小時執行一次
|
||||
};
|
||||
|
||||
// 停止定時器
|
||||
|
@ -5,3 +5,10 @@ export interface NiagaraElecData {
|
||||
id: string;
|
||||
out: number;
|
||||
}
|
||||
|
||||
export interface NiagaraElecDemandData {
|
||||
slotPath: string;
|
||||
displayName: string;
|
||||
name: string;
|
||||
out: number;
|
||||
}
|
Loading…
Reference in New Issue
Block a user