diff --git a/src/components/forge/Forge.vue b/src/components/forge/Forge.vue index 51a602c..8ee6a32 100644 --- a/src/components/forge/Forge.vue +++ b/src/components/forge/Forge.vue @@ -10,37 +10,40 @@ import { provide, inject, } from "vue"; -import { getUrn, getAccessToken } from "@/apis/forge"; import { twMerge } from "tailwind-merge"; import useSystemStatusByBaja from "@/hooks/baja/useSystemStatusByBaja"; +import useSystemHeatmap from "@/hooks/baja/useSystemHeatmap"; import ForgeInfoModal from "./ForgeInfoModal.vue"; -import useAlarmStore from "@/stores/useAlarmStore"; +import useSearchParams from "@/hooks/useSearchParam"; +const { searchParams } = useSearchParams(); +const { updateTemp } = useSystemHeatmap(); const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL; const { forgeLock } = inject("app_toggle"); const props = defineProps({ fullScreen: Boolean, initialData: Object, + pipeData: Array, realTime: String, + meterList: Array, + heatmapDevices: Array, }); -const heat_bar_isShow = ref(false); -const updateHeatBarIsShow = (isShow) => { - heat_bar_isShow.value = isShow; -}; - const { - subscribeData, visibleDbid, - updateDbidPosition, + updateMeterPositions, hideAllObjects, updateForgeViewer, forgeViewer, urn, loadModel, updateInitialData, + updatePipeData, subComponents, - clearSprites -} = useSystemStatusByBaja(updateHeatBarIsShow); + clearSprites, +} = useSystemStatusByBaja(); + +// 用於存儲 meter label 的 2D 位置 +const meterPositions = ref(new Map()); watch( () => props.initialData, @@ -52,19 +55,44 @@ watch( } ); -const store = useAlarmStore(); -const subscribeDataWithErrorMsg = computed(() => { - let data = { ...subscribeData.value }; - - for (let [key, value] of Object.entries(subscribeData.value)) { - const alarm = store.alarmData.find( - ({ device_number }) => device_number === key - ); - data[key].alarmMsg = alarm ? alarm.msg : ""; +watch( + () => props.pipeData, + (newValue, oldValue) => { + if (JSON.stringify(newValue) === JSON.stringify(oldValue)) return; + newValue && updatePipeData(newValue); + }, + { + deep: true, } - console.log("baja update data: ", data); - return data; -}); +); + +watch( + () => props.heatmapDevices, + (newValue) => { + if (!newValue || newValue.length === 0) return; + newValue && + updateTemp(newValue[0]?.forge_dbid, parseFloat(newValue[0]?.temperature)); + }, + { + deep: true, + } +); + +// 更新 meter labels 的位置 +const updateMeterPositionsLocal = () => { + if (!props.meterList) return; + const newPositions = updateMeterPositions(props.meterList); + meterPositions.value = newPositions; +}; + +// 監聽 meterList 變化 +watch( + () => props.meterList, + () => { + updateMeterPositionsLocal(); + }, + { deep: true } +); const forgeDom = ref(null); @@ -107,17 +135,17 @@ const initForge = () => { viewer.isLoadDone() ); updateForgeViewer(viewer); + // 初始化 meter 位置 + updateMeterPositionsLocal(); } ); + + // 添加 camera 變化事件監聽,更新 meter 位置 viewer.addEventListener( Autodesk.Viewing.CAMERA_CHANGE_EVENT, - function (e) { - viewer.isLoadDone() && updateDbidPosition(this, subscribeData.value); - console.log( - "camera position changed: ", - NOP_VIEWER.navigation.getTarget(), - e.camera.position - ); + () => { + console.log("CAMERA_CHANGE_EVENT"); + updateMeterPositionsLocal(); } ); }); @@ -129,23 +157,6 @@ onMounted(() => { initForge(); }); -// 傳遞目前點擊資訊 -const currentInfoModalData = ref(null); -const isMobile = (pointerType) => { - return pointerType !== "mouse"; // is desktop -}; -const getCurrentInfoModalData = (e, position, value) => { - const mobile = isMobile(e.pointerType); - currentInfoModalData.value = { - initPos: mobile - ? { left: `50%`, top: `50%` } - : { left: `${position.left}px`, top: `${position.top}px` }, - value, - isMobile: mobile, - }; - forge_info_modal.showModal(); -}; - watch([forgeViewer, forgeLock], ([newViewer, newLock]) => { if (newViewer && newLock !== undefined) { newViewer.setNavigationLock(newLock); // 鎖定視角 @@ -166,7 +177,7 @@ onUnmounted(() => { diff --git a/src/hooks/baja/useSystemHeatmap.js b/src/hooks/baja/useSystemHeatmap.js index 536aeb3..ded6e03 100644 --- a/src/hooks/baja/useSystemHeatmap.js +++ b/src/hooks/baja/useSystemHeatmap.js @@ -2,7 +2,7 @@ import { ref, watch, markRaw, onUnmounted } from "vue"; import useSearchParams from "@/hooks/useSearchParam"; -export default function useSystemHeatmap(updateHeatBarIsShow) { +export default function useSystemHeatmap() { const { searchParams } = useSearchParams(); // Viewer and DataVisualization Extension References @@ -25,12 +25,13 @@ export default function useSystemHeatmap(updateHeatBarIsShow) { const updateHeatMapData = (deviceArr) => { deviceList.value = Object.values(deviceArr).map((d) => ({ ...d, - id: d.device_number, // An ID to identify this device + id: d.forge_dbid, // An ID to identify this device roomDbId: d.room_dbid, - position: d.device_coordinate_3d, // World coordinates of this device + position: JSON.parse(d.device_coordinate_3d), sensorTypes: ["temperature"], // The types/properties this device exposes - temp: 10, + temp: 25, dbId: d.forge_dbid, + subSys: "freezer", })); }; @@ -132,22 +133,10 @@ export default function useSystemHeatmap(updateHeatBarIsShow) { // Watcher for Device List Changes watch(deviceList, (newValue) => { - console.log("熱圖更新", newValue); + console.log("熱圖更新", newValue, searchParams.value.option); switch (parseInt(searchParams.value.option)) { - case 2: + case 1: createHeatMap("frozen"); - updateHeatBarIsShow(true); - break; - case 3: - createHeatMap("gland"); - updateHeatBarIsShow(true); - break; - case 4: - createHeatMap("packing"); - break; - case 5: - createHeatMap("formula"); - updateHeatBarIsShow(true); break; default: if (isHeatMapSetup.value) { @@ -159,7 +148,6 @@ export default function useSystemHeatmap(updateHeatBarIsShow) { "跳過 removeSurfaceShading,因為尚未 setupSurfaceShading" ); } - updateHeatBarIsShow(false); break; } }); diff --git a/src/hooks/baja/useSystemStatusByBaja.js b/src/hooks/baja/useSystemStatusByBaja.js index a912fbc..29e38e9 100644 --- a/src/hooks/baja/useSystemStatusByBaja.js +++ b/src/hooks/baja/useSystemStatusByBaja.js @@ -3,9 +3,12 @@ import { getDashboardDevice } from "@/apis/dashboard"; import useSearchParams from "@/hooks/useSearchParam"; import useSystemHeatmap from "./useSystemHeatmap"; -export default function useSystemStatusByBaja(updateHeatBarIsShow) { +export default function useSystemStatusByBaja() { const rawData = ref([]); const forgeViewer = ref(null); + const visibleDbid = ref([]); + const heatmapDevices = ref([]); + const urn = ref(""); const lightColorMap = { 1: "#009100", @@ -20,106 +23,56 @@ export default function useSystemStatusByBaja(updateHeatBarIsShow) { let cameraEventAdded = false; // 追蹤是否已經添加了相機事件監聽器 const initialData = ref(null); + const pipeList = ref(null); + const subscribeData = ref({}); const updateInitialData = (data = false) => { initialData.value = data; + if (data && Array.isArray(data.dbid_list)) { + visibleDbid.value = data.dbid_list.map((d) => d.forge_dbid); + heatmapDevices.value = data.dbid_list.filter((d) => d.room_dbid !== null); + } else { + visibleDbid.value = []; + } }; - const { updateHeatMapData, updateTemp, initHeatMap } = - useSystemHeatmap(updateHeatBarIsShow); + function updatePipeData(data) { + const newDbids = data.map((item) => item.forge_dbid); + const oldDbids = pipeList.value + ? pipeList.value.map((item) => item.forge_dbid) + : []; + const toHide = oldDbids.filter((dbid) => !newDbids.includes(dbid)); + console.log("更新水管數據", forgeViewer.value, data); + if (forgeViewer.value) { + if (toHide.length > 0 && forgeViewer.value.hide) + forgeViewer.value.hide(toHide); + if (newDbids.length > 0 && forgeViewer.value.show) + forgeViewer.value.show(newDbids); + } - const updateForgeViewer = (viewer) => { + pipeList.value = data; + clearSprites(); // 清理之前的 Sprites + // 如果有特定的 dbid 需要顯示動畫,則創建動畫 + if (forgeViewer.value && pipeList.value.length > 0) { + pipeList.value.forEach(({ forge_dbid, reverse }) => { + if (reverse !== null) { + createSprites(forgeViewer.value, forge_dbid, reverse); + } + }); + } + } + + const { updateHeatMapData, initHeatMap } = useSystemHeatmap(); + + const updateForgeViewer = async (viewer) => { if (!viewer) { forgeViewer.value = null; return; } forgeViewer.value = markRaw(viewer); - initHeatMap(viewer); - }; - - const getSubPoint = (normal, close, error, sub_points = []) => { - let points = { - ...Object.fromEntries(sub_points.map((p) => [p, ""])), - }; - if (normal) points[normal] = ""; - if (close) points[close] = ""; - if (error) points[error] = ""; - return points; - }; - const subscribeData = ref({}); - - watch(rawData, () => { - let sub_data = {}; - - rawData.value.forEach((d) => { - sub_data = { - ...sub_data, - ...Object.fromEntries( - (d.device || []).map((dev) => [ - dev.device_number, - { - ...dev, - labelText: d.labelText, - show_value: d.labelText, - device_normal_point_name: d.device_normal_point_name, - device_close_point_name: d.device_close_point_name, - device_error_point_name: d.device_error_point_name, - device_normal_point_value: d.device_normal_point_value, - device_close_point_value: d.device_close_point_value, - device_error_point_value: d.device_error_point_value, - device_normal_color: d.device_normal_color, - device_close_color: d.device_close_color, - device_error_color: d.device_error_color, - forge_dbid: parseInt(dev.forge_dbid), - device_coordinate_3d: dev.device_coordinate_3d - ? JSON.parse(dev.device_coordinate_3d) - : { x: 0, y: 0 }, - points: getSubPoint( - d.device_normal_point_name, - d.device_close_point_name, - d.device_error_point_name, - d.points || [] - ), - subSys: d.subSys, - is_show: true, - currentColor: d.device_normal_color, - }, - ]) - ), - }; - }); - subscribeData.value = sub_data; - updateHeatMapData(sub_data); - // updateSubscribeDataFromBaja(sub_data); - }); - - const visibleDbid = computed(() => { - let visible = []; - - rawData.value.forEach((d) => { - visible = [ - ...visible, - ...d.device.map((dev) => parseInt(dev.forge_dbid)), - ]; - }); - - // return visible; - return [ - 26, 32, 38, 43, 48, 53, 58, 63, 70, 76, 879, 1068, 1011, 1065, 855, 1109, - 867, 963, 1044, 966, 1139, 936, 1136, 957, 1133, 954, 610, 1130, 951, - 1041, 939, 1145, 987, 999, 1148, 1002, 1151, 981, 813, 1088, 825, 1091, - 804, - ]; - }); - - const getDevice = async (option = 1) => { - const res = await getDashboardDevice({ - option: parseInt(option), - }); - rawData.value = res.data.map((d) => ({ - ...d, - key: d.subSys, - })); + await initHeatMap(viewer); // 等待初始化完成 + updateHeatMapData(heatmapDevices.value); + updatePipeData(pipeList.value || []); }; // subscribe from baja @@ -130,238 +83,64 @@ export default function useSystemStatusByBaja(updateHeatBarIsShow) { [point]: facets, }; }; - const updateDeviceData = (device_number, point, value) => { - // 檢查 subscribeData.value[device_number] 是否存在 - if (!subscribeData.value[device_number]) { - console.log(`Device ${device_number} is not initialized.`); - return; - } - - const correspondPoint = initialData.value.points.find( - ({ name }) => name === point - ); - // console.log("sub 回傳值 ", typeof value) - const text = correspondPoint - ? correspondPoint.values.find( - ({ value: pValue }) => pValue === parseInt(value) - )?.text || "" - : value; - - // console.log("baja update data sub", correspondPoint, device_number, point, value); - subscribeData.value[device_number].points[point] = text; - if ( - point.toLowerCase() === "temp" && - parseInt(searchParams.value.option) > 1 - ) { - updateTemp(device_number, value); - } - // 當點位是 "light",且目前顏色不是錯誤顏色時,從 lightColorMap 取顏色 - if ( - point.toLowerCase() === "light" && - subscribeData.value[device_number].currentColor !== - subscribeData.value[device_number].device_error_color - ) { - subscribeData.value[device_number].currentColor = - lightColorMap[Number(value)] || - subscribeData.value[device_number].device_normal_color; - } - - // 當點位是錯誤點時,判斷值是否等於錯誤值,決定是否使用錯誤顏色 - if (point === subscribeData.value[device_number].device_error_point_name) { - subscribeData.value[device_number].currentColor = - value === subscribeData.value[device_number].device_error_point_value - ? subscribeData.value[device_number].device_error_color - : subscribeData.value[device_number].device_normal_color; - } - - updateLabelText(device_number, point, text); - }; - - const transformDeviceNumber = (device_number) => { - const transformed = device_number.replaceAll("_", "/"); - // 找到最後一個 / 的位置,並截取到該位置之前 - const lastSlashIndex = transformed.lastIndexOf("/"); - return lastSlashIndex !== -1 - ? transformed.substring(0, lastSlashIndex) - : transformed; - }; - - const updateLabelText = (key, point, value) => { - let text = subscribeData.value[key].labelText.replace(`%${point}`, value); - Object.keys(subscribeData.value[key].points) - .filter((p) => p !== point) - .forEach((p) => { - text = text.replace(`%${p}`, subscribeData.value[key].points[p]); - }); - subscribeData.value[key].show_value = text; - }; const subComponents = ref(null); - const updateSubscribeDataFromBaja = (data) => { - for (let [key, value] of Object.entries(data)) { - window.require && - window.requirejs(["baja!"], (baja) => { - console.log("進入 bajaSubscriber 準備執行BQL訂閱"); - const ordKey = key; - const ordPath = transformDeviceNumber(key); - console.log("ordPath", ordPath, "ordKey", ordKey, value); - const fullOrdPath = `local:|foxs:|station:|slot:/Drivers/NiagaraNetwork/PCCV/points/${ordPath}/${ordKey}`; // 完整路徑 - console.log("嘗試訪問路徑:", fullOrdPath); // 打印完整路徑 - - baja.Ord.make(fullOrdPath) - .get() - .then((folder) => { - console.log("成功獲取 folder:", folder); - const batch = new baja.comm.Batch(); - const sub = new baja.Subscriber(); - sub.attach({ - changed: function (prop, cx) { - console.log("數據變更觸發:", prop.$getDisplayName()); - if (prop.$getDisplayName() !== "Out") return; - if ( - Object.hasOwn( - booleanPointFacets.value, - prop.$complex.$propInParent.$slotName - ) - ) { - const facets = - booleanPointFacets.value[ - prop.$complex.$propInParent.$slotName - ]; - - for (let [facetKey, facetValue] of Object.entries(facets)) { - if (facetValue === prop.$getValue().getValueDisplay()) { - updateDeviceData( - key, - prop.$complex.$propInParent.$slotName, - facetKey - ); - } - } - } else { - updateDeviceData( - key, - prop.$complex.$propInParent.$slotName, - prop.$getValue().getValueDisplay() - ); - } - }, - }); - console.log("開始遍歷控制點"); - folder - .getSlots() - .is("control:ControlPoint") - .eachValue((point) => { - console.log("找到控制點:", point.getDisplayName()); - console.log("配置的點位:", Object.keys(value.points)); - - if ( - Object.keys(value.points).includes(point.getDisplayName()) - ) { - console.log( - "匹配到點位,開始訂閱:", - point.getDisplayName() - ); - baja.Ord.make( - `local:|foxs:|station:|slot:/Drivers/NiagaraNetwork/PCCV/points/${ordPath}/${ordKey}/${point.getDisplayName()}` - ) - .get() - .then((component) => { - console.log("獲取到 component:", component); - if ( - point.getType().getTypeSpec() === - "control:BooleanWritable" - ) { - const facets = component.getFacets1().toObject(); - updateFacets(point.getDisplayName(), facets); - for (let [facetKey, facetValue] of Object.entries( - facets - )) { - if ( - facetValue === - component.getOut().getValue().toString() - ) { - updateDeviceData( - key, - point.getDisplayName(), - facetKey - ); - } - } - } else { - updateDeviceData( - key, - point.getDisplayName(), - component.getOut().getValue() - ); - } - - sub - .subscribe({ - comps: component, // Can also just be an singular Component instance - batch, // if defined, any network calls will be batched into this object (optional) - }) - .then(() => { - console.log("subscribed successfully"); - subComponents.value = sub; - }) - .catch(function (err) { - baja.error( - "some components failed to subscribe: " + err - ); - }); - }); - } - }); + const updateMeterPositions = (meterList) => { + if (!forgeViewer.value || !meterList) return new Map(); + + const viewer = forgeViewer.value; + const tree = viewer.model?.getData()?.instanceTree; + const fragList = viewer.model?.getFragmentList(); + + if (!tree || !fragList) return new Map(); + + const newPositions = new Map(); + + meterList.forEach((meter) => { + if (meter.forge_dbid) { + try { + const nodebBox = new window.THREE.Box3(); + + // 取得物件的 bounding box + tree.enumNodeFragments( + meter.forge_dbid, + (fragId) => { + const fragbBox = new window.THREE.Box3(); + fragList.getWorldBounds(fragId, fragbBox); + nodebBox.union(fragbBox); + }, + true + ); + + if (!nodebBox.isEmpty()) { + const center = nodebBox.getCenter(new window.THREE.Vector3()); + const pos2d = viewer.worldToClient(center); + newPositions.set(meter.main_id, { + x: pos2d.x, + y: pos2d.y - 100, // 往上偏移 100px + visible: viewer.isNodeVisible(meter.forge_dbid) }); - }); - } - }; - - const updateDbidPosition = (viewer, data) => { - if (!viewer) return; - if (!forgeViewer.value) forgeViewer.value = markRaw(viewer); - const tree = viewer.model.getData().instanceTree; - const fragList = viewer.model.getFragmentList(); - for (let [key, value] of Object.entries(data)) { - const nodebBox = new window.THREE.Box3(); - - // for each fragId on the list, get the bounding box - tree.enumNodeFragments( - value.forge_dbid, - (fragId) => { - const fragbBox = new window.THREE.Box3(); - fragList.getWorldBounds(fragId, fragbBox); - nodebBox.union(fragbBox); // create a unifed bounding box - }, - true - ); - subscribeData.value[key].device_coordinate_3d = viewer.worldToClient( - nodebBox.getCenter() - ); - subscribeData.value[key].is_show = viewer.isNodeVisible(value.forge_dbid); - } + } + } catch (error) { + console.warn(`無法計算 dbid ${meter.forge_dbid} 的位置:`, error); + } + } + }); + + return newPositions; }; const fitToView = () => { const { x, y, z } = JSON.parse(searchParams.value.camera_position); - const newPosition = new THREE.Vector3(x, y, z); //!<<< 相机的新位置 - + const newPosition = new THREE.Vector3(x, y, z); const { x: x1, y: y1, z: z1, - } = JSON.parse(searchParams.value.target_position); //!<<< 计算新焦点位置 - const newTarget = new THREE.Vector3(x1, y1, z1); //!<<< 焦點的新位置 - - forgeViewer.value.navigation.getCamera().setView({ - position: newPosition.clone(), - target: newTarget.clone(), - }); - setTimeout(() => { - updateDbidPosition(forgeViewer.value, subscribeData.value); - }, 700); + } = JSON.parse(searchParams.value.target_position); + const newTarget = new THREE.Vector3(x1, y1, z1); + forgeViewer.value.navigation.setView(newPosition, newTarget); }; const hideAllObjects = (instanceTree, filDbids = []) => { @@ -374,7 +153,6 @@ export default function useSystemStatusByBaja(updateHeatBarIsShow) { if (filDbids.length > 0) { forgeViewer.value.show(filDbids); } - fitToView(); forgeViewer.value.impl.invalidate(true); }; @@ -459,42 +237,21 @@ export default function useSystemStatusByBaja(updateHeatBarIsShow) { watch([forgeViewer, visibleDbid], ([viewer, dbids]) => { console.log("監聽到 forgeViewer 或 visibleDbid 的變化", viewer, dbids); - stopBlinking(); - clearSprites(); // 清理之前的 Sprites if (viewer) { hideAllObjects(viewer.model.getData().instanceTree, dbids); } - - // 判斷新狀態是否需要閃爍和 Sprites - if (viewer && dbids.includes(879)) { - // startBlinking([879, 1068, 1011, 1065, 855, 1109, 867,963,1044,966,1139,936,1136,957,1133,954,610,1130,951,1041,939,1145,987,999,1148,1002,1151,981,813,1088,825,1091,804]); - const spriteConfigs = [ - { dbId: 879, reverse: false }, - { dbId: 1011, reverse: false }, - { dbId: 855, reverse: true }, - { dbId: 867, reverse: true }, - { dbId: 963, reverse: false }, - { dbId: 966, reverse: true }, - { dbId: 936, reverse: true }, - { dbId: 957, reverse: true }, - { dbId: 954, reverse: true }, - { dbId: 951, reverse: true }, - { dbId: 939, reverse: true }, - { dbId: 987, reverse: true }, - { dbId: 999, reverse: true }, - { dbId: 1002, reverse: true }, - { dbId: 981, reverse: true }, - { dbId: 813, reverse: true }, - { dbId: 825, reverse: false }, - { dbId: 804, reverse: false }, - ]; - spriteConfigs.forEach(({ dbId, reverse }) => { - createSprites(viewer, dbId, reverse); - }); - } }); + watch( + [forgeViewer, () => searchParams.value.camera_position], + ([viewer, newValue]) => { + if (viewer && newValue) { + fitToView(); + } + } + ); + // 動態Sprites創建函數 const createSprites = async (viewer, dbId, reverse = false) => { try { @@ -612,15 +369,6 @@ export default function useSystemStatusByBaja(updateHeatBarIsShow) { animEnd = tmp; } - console.log(`dbId ${dbId} 動畫資訊:`, { - axis: axis, - size: size, - animStart: animStart, - animEnd: animEnd, - distance: animStart.distanceTo(animEnd), - reverse: reverse, - }); - // 4. 建立 SpriteViewable,初始在 animStart const viewable = new DataVizCore.SpriteViewable( animStart.clone(), @@ -655,19 +403,6 @@ export default function useSystemStatusByBaja(updateHeatBarIsShow) { arrow.style.transform = `rotate(${angle}deg)`; } - // camera 移動時也要更新所有箭頭(只添加一次事件監聽器) - if (!cameraEventAdded) { - viewer.addEventListener( - Autodesk.Viewing.CAMERA_CHANGE_EVENT, - () => { - spriteAnimations.forEach((animation) => { - animation.updateArrow(animation.currentPos); - }); - } - ); - cameraEventAdded = true; - } - // 動畫主程式 let t = 0; let currentPos = animStart.clone(); @@ -725,8 +460,6 @@ export default function useSystemStatusByBaja(updateHeatBarIsShow) { // 移除這個 dbId 的記錄 spriteAnimations.delete(dbId); - - console.log(`已清理 dbId ${dbId} 的 Sprite 資源`); } }; @@ -751,9 +484,10 @@ export default function useSystemStatusByBaja(updateHeatBarIsShow) { return { subscribeData, visibleDbid, - updateDbidPosition, + updateMeterPositions, hideAllObjects, updateForgeViewer, + updatePipeData, forgeViewer, loadModel, urn, diff --git a/src/views/dashboard/Dashboard.vue b/src/views/dashboard/Dashboard.vue index 44f426f..d6b8e32 100644 --- a/src/views/dashboard/Dashboard.vue +++ b/src/views/dashboard/Dashboard.vue @@ -16,14 +16,18 @@ import { import { getAssetFloorList } from "@/apis/asset"; import { getOperationCompanyList } from "@/apis/operation"; import useSearchParams from "@/hooks/useSearchParam"; +import useSystemHeatmap from "@/hooks/baja/useSystemHeatmap"; import dayjs from "dayjs"; const initialData = ref(null); const realTimeData = ref(null); +const pipeData = ref([]); const realTime = ref(null); // 電表列表 const meterList = ref([]); const selectedMeter = ref(null); +// 冷藏室及時溫度 +const heatmapDevices = ref([]); // 樓層列表 const floors = ref([]); // 廠商列表 @@ -48,10 +52,37 @@ const getDevice = async (option = 1) => { }); realTimeData.value = res.data; if (res.data?.meterData) { - meterList.value = (res.data?.meterData || []).sort(); + // 先複製 meterList + let meters = (res.data?.meterData || []).sort(); + // 取得 dbid_list + const dbidList = initialData.value?.dbid_list || []; + // 對每個 meter 做 forge_dbid 對應 + meters = meters.map((meter) => { + const dbidItem = dbidList.find( + (d) => d.forge_dbid === meter.forge_dbid + ); + return { + ...meter, + device_coordinate_3d: JSON.parse(dbidItem?.device_coordinate_3d) || null, + }; + }); + meterList.value = meters; + // 如果 selectedMeter 沒有值,且 meterList 有資料,預設選擇第一個 + if ( + selectedMeter.value == null && + meterList.value[0]?.main_id !== undefined + ) { + selectedMeter.value = meterList.value[0].main_id; + } + } else if (res.data?.refrigerationData) { + heatmapDevices.value = res.data.refrigerationData; } else { meterList.value = []; + heatmapDevices.value = []; } + // 水管數據 + pipeData.value = res.data?.pipeData || []; + // 更新熱點圖數據時間 realTime.value = dayjs().format("YYYY-MM-DD HH:mm:ss"); console.log("實時數據:", realTimeData.value); } catch (err) { @@ -82,7 +113,7 @@ const startInterval = (option) => { // 設定每5秒執行一次 intervalId = setInterval(() => { getDevice(option); - }, 5000); + }, 10 * 1000); }; // 停止定時器 @@ -128,7 +159,14 @@ onUnmounted(() => { - +