即時需量訂閱
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