diff --git a/src/stores/useElecTotalMeterStore.ts b/src/stores/useElecTotalMeterStore.ts new file mode 100644 index 0000000..5c20d7a --- /dev/null +++ b/src/stores/useElecTotalMeterStore.ts @@ -0,0 +1,136 @@ +import { ref } from "vue"; +import { defineStore } from "pinia"; +import dayjs from "dayjs"; +import type { NiagaraElecData, ElecCostSummary } from "../utils/types"; +import { CalcuEleCost } from "../utils/CalcuEleCost"; + +const useElecStore = defineStore("elecData", () => { + const elecData = ref([]); + // @ts-ignore + let timerId = null; + const elecCostSummary = ref(null); + + // get data from baja + const getElecDataFromBaja = () => { + // @ts-ignore + window.require && + // @ts-ignore + window.requirejs(["baja!"], (baja: any) => { + console.log("進入 bajaSubscriber 準備執行用電度數 BQL 訂閱"); + + // 定義BQL 查詢 + const Total_kwhBql = `local:|foxs:4918|station:|neql:EMS:Total_kwh|bql:select slotPath,parent.displayName,displayName,NumericInterval.historyConfig.id,out`; + + // 執行各電表的 BQL 查詢 + fetchElecData(baja, Total_kwhBql); + }); + }; + + const fetchElecData = (baja: any, bql: string) => { + let eleclist: NiagaraElecData[] = []; + baja.Ord.make(bql).get({ + cursor: { + before: () => {}, + each: (record: any) => { + eleclist.push({ + slotPath: record.get("slotPath"), + displayName: record.get("parent$2edisplayName"), + id: record.get("NumericInterval$2ehistoryConfig$2eid").$cEncStr, + out: record.get("out")?.get("value") ?? null, + }); + }, + after: () => { + const validElecList = eleclist.filter( + (item) => item.id !== undefined + ); + elecData.value = []; + elecData.value.push(...validElecList); + validElecList.forEach((item) => { + gettimeToHistory(item); + }); + }, + }, + }); + }; + + const gettimeToHistory = (item: NiagaraElecData) => { + const id = item.id; + const startTime = dayjs( + `${dayjs().year()}-01-01T00:00:00.000+08:00` + ).format("YYYY-MM-DDTHH:mm:ss.000+08:00"); + const endTime = dayjs().format("YYYY-MM-DDTHH:mm:ss.000+08:00"); + + const ordString = `local:|foxs:4918|history:${id}?period=timerange;;start=${startTime};end=${endTime}|bql:history:HistoryRollup.rollup(baja:RelTime '3600000')`; //每小时一个rollup + console.log(ordString); + // @ts-ignore + window.require && + // @ts-ignore + window.requirejs(["baja!"], (baja: any) => { + console.log("進入 bajaSubscriber 準備執行 BQL 訂閱"); + const dataMap = new Map(); // key: timestamp string, value: energy diff + let lastTimestamp: string | null = null; + let lastValue: number | null = null; + baja.Ord.make(ordString).get({ + cursor: { + before: () => { + console.log(`開始訂閱 ${id} 的歷史資料`); + }, + each: (record: any) => { + const currentValue = record.get("min"); + const timestamp = record.get("timestamp").$cEncStr; + if (currentValue !== null && currentValue !== 0 && timestamp !== null) { + if (lastTimestamp !== null && lastValue !== null && lastValue !== 0) { + const diff = currentValue - lastValue; + dataMap.set(lastTimestamp, diff); + } + lastValue = currentValue; + lastTimestamp = timestamp; + } + }, + after: () => { + console.log("⏱️ 每小時差值 map:", dataMap); + elecCostSummary.value = CalcuEleCost(dataMap); + }, + limit: -1, + offset: 0, + }, + }); + }); + }; + + // 定時更新資料的函數 + const updateHistoryData = () => { + console.log("定時器觸發,重新獲取歷史資料"); + if (elecData.value && elecData.value.length > 0) { + elecData.value.forEach((item) => { + gettimeToHistory(item); + }); + } + }; + + // 啟動定時器 + const startTimer = () => { + timerId = setInterval(() => { + updateHistoryData(); + }, 60 * 60 * 1000); // 每小時執行一次 + }; + + // 停止定時器 + const stopTimer = () => { + // @ts-ignore + if (timerId) { + clearInterval(timerId); + timerId = null; + console.log("計時器已停止"); + } + }; + return { + getElecDataFromBaja, + startTimer, + stopTimer, + elecData, + elecCostSummary, + }; +}); + +export default useElecStore; diff --git a/src/utils/CalcuEleCost.ts b/src/utils/CalcuEleCost.ts index e69de29..70addc5 100644 --- a/src/utils/CalcuEleCost.ts +++ b/src/utils/CalcuEleCost.ts @@ -0,0 +1,133 @@ +import type { DailyResult, ElecCostSummary, DailyEntry } from "../utils/types"; +import useElecPriceStore from "../stores/useElecPriceStore"; +const storeElecPrice = useElecPriceStore(); + +export const CalcuEleCost = ( + input: Map +): ElecCostSummary => { + const dailyData: Map = new Map(); + let totalFlowCost = 0; // 總電價 + let totalEleCost = 0; //總用電 + let total_Off = 0; //總離峰用電 + let total_half = 0; //總半尖峰用電 + let total_peak = 0; //總尖峰用電 + let total_OffCost = 0; //總離峰電價 + let total_halfCost = 0; //總半尖峰電價 + let total_peakCost = 0; //總尖峰電價 + + const dailyResults: DailyResult[] = []; + const elecPrices = storeElecPrice.elecData; + const Summer_Off_Prices = elecPrices.find((item:any) => item.displayName === "流動週日離峰夏月")?.out || 0; + const Summer_HalfPeak_Prices_Saturday = elecPrices.find((item:any) => item.displayName === "流動週六半尖峰夏月")?.out || 0; + const Summer_HalfPeak_Prices_Weekday = elecPrices.find((item:any) => item.displayName === "流動平日半尖峰夏月")?.out || 0; + const Summer_Peak_Prices = elecPrices.find((item:any) => item.displayName === "流動平日尖峰夏月")?.out || 0; + const Non_Summer_Off_Prices = elecPrices.find((item:any) => item.displayName === "流動週日離峰非夏月")?.out || 0; + const Non_Summer_HalfPeak_Prices_Saturday = elecPrices.find((item:any) => item.displayName === "流動週六半尖峰非夏月")?.out || 0; + const Non_Summer_HalfPeak_Prices_Weekday = elecPrices.find((item:any) => item.displayName === "流動平日半尖峰非夏月")?.out || 0; + // 1. 將輸入資料按日期分組 + input.forEach((value, key) => { + const dateStr = key.substring(0, 10); + if (!dailyData.has(dateStr)) { + dailyData.set(dateStr, []); + } + dailyData.get(dateStr)?.push({ time: new Date(key), value }); + }); + + for (const [dateStr, entries] of dailyData.entries()) { + if (!entries || entries.length === 0) continue; + + const sampleDate = entries[0].time; + const dayOfWeek = sampleDate.getDay(); + const month = sampleDate.getMonth() + 1; + + let off = 0; //離峰用電 + let half = 0; //半尖峰用電 + let peak = 0; //尖峰用電 + let offcost = 0; //離峰電價 + let halfcost = 0; //半尖峰電價 + let peakcost = 0; //尖峰電價 + let dailyFlowCost = 0; //當日電價 + let dailyEleCost = 0; //當日用電 + + const isSummer = month >= 6 && month <= 9; + + entries.forEach(({ time, value }) => { + const hour = time.getHours(); + + if (isSummer) { + if (dayOfWeek === 0) { + off += value; + } else if (dayOfWeek === 6) { + if (hour < 9) off += value; + else half += value; + } else { + if (hour >= 16 && hour < 22) peak += value; + else if ((hour >= 9 && hour < 16) || hour >= 22) half += value; + else off += value; + } + } else { + if (dayOfWeek === 0) { + off += value; + } else { + if (hour < 6 || (hour >= 11 && hour < 14)) off += value; + else half += value; + } + } + }); + + if (isSummer) { + console.log("夏月計費"); + const summerHalfPeakRate = + dayOfWeek === 6 + ? Summer_HalfPeak_Prices_Saturday + : Summer_HalfPeak_Prices_Weekday; + offcost = off * Summer_Off_Prices; + halfcost = half * summerHalfPeakRate; + peakcost = peak * Summer_Peak_Prices; + dailyFlowCost = offcost + halfcost + peakcost; + dailyEleCost = off + half + peak; + } else { + console.log("非夏月計費"); + const nonSummerHalfPeakRate = + dayOfWeek === 6 + ? Non_Summer_HalfPeak_Prices_Saturday + : Non_Summer_HalfPeak_Prices_Weekday; + offcost = off * Non_Summer_Off_Prices; + halfcost = half * nonSummerHalfPeakRate; + dailyFlowCost = offcost + halfcost; + dailyEleCost = off + half; + } + + totalFlowCost += dailyFlowCost; + totalEleCost += dailyEleCost; + total_Off += off; + total_half += half; + total_peak += peak; + total_OffCost += offcost; + total_halfCost += halfcost; + total_peakCost += peakcost; + dailyResults.push({ + dateStr, + off, + half, + peak, + offcost, + halfcost, + peakcost, + dailyEleCost, + dailyFlowCost, + }); + + } + return { + dailyResults, + totalEleCost, + totalFlowCost, + total_Off, + total_half, + total_peak, + total_OffCost, + total_halfCost, + total_peakCost, + }; +}; diff --git a/src/views/EnergyChart.vue b/src/views/EnergyChart.vue index 3f2ed58..63f4c36 100644 --- a/src/views/EnergyChart.vue +++ b/src/views/EnergyChart.vue @@ -57,7 +57,7 @@ -

區間計費度數 2025-03-01 ~ 2025-03-12

+

區間計費度數 {{ billingDateRange }}

@@ -65,151 +65,245 @@