即時需量訂閱
This commit is contained in:
parent
affc5291f4
commit
7f4d1e6e07
@ -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 = {
|
|
||||||
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 demand_chart = ref(null);
|
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: {
|
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>
|
||||||
|
@ -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();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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;
|
@ -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); // 每小時執行一次
|
||||||
};
|
};
|
||||||
|
|
||||||
// 停止定時器
|
// 停止定時器
|
||||||
|
@ -4,4 +4,11 @@ export interface NiagaraElecData {
|
|||||||
displayName: string;
|
displayName: string;
|
||||||
id: string;
|
id: string;
|
||||||
out: number;
|
out: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NiagaraElecDemandData {
|
||||||
|
slotPath: string;
|
||||||
|
displayName: string;
|
||||||
|
name: string;
|
||||||
|
out: number;
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user