diff --git a/src/assets/img/icon/exclamation-cricle-red-fill.svg b/src/assets/img/icon/exclamation-cricle-red-fill.svg new file mode 100644 index 0000000..62fc034 --- /dev/null +++ b/src/assets/img/icon/exclamation-cricle-red-fill.svg @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/src/components/MyModal.vue b/src/components/MyModal.vue new file mode 100644 index 0000000..6fec489 --- /dev/null +++ b/src/components/MyModal.vue @@ -0,0 +1,133 @@ + + + \ No newline at end of file diff --git a/src/components/Operator.vue b/src/components/Operator.vue index 1744b0e..8ba426c 100644 --- a/src/components/Operator.vue +++ b/src/components/Operator.vue @@ -487,6 +487,7 @@ import { useOperatorStore, useWebSocketStore, useSettingStore } from '@/store' import { startDisinfectionJSON, getStateJSON } from '@/mock/command' import { showSuccessToast, showFailToast } from 'vant' import { time_To_hhmmss } from '@/utils' +import MyModal from '../utils/MyModal' const operatorStore = useOperatorStore() const webSocketStore = useWebSocketStore() @@ -570,20 +571,29 @@ const startDisinfect = () => { realStart() } -const realStart = () => { - localStorage.setItem('logVal', logVal.value) - if ( - operatorStore.disinfectStatus == 0 || - operatorStore.disinfectStatus == 5 - ) { - localStorage.removeItem('bin') - localStorage.removeItem('envir1') - localStorage.removeItem('envir2') - webSocketStore.sendCommandMsg( - startDisinfectionJSON(parseInt(logVal.value), parseInt(roomSize.value)), - ) - props.changeShowOperator(false) - } +// 开始消毒 +async function realStart() { + operatorStore.updateShowStartReady(true) + localStorage.setItem('logVal', logVal.value) + if ( operatorStore.disinfectStatus == 0 || operatorStore.disinfectStatus == 5 ) { + localStorage.removeItem('bin') + localStorage.removeItem('envir1') + localStorage.removeItem('envir2') + + try { + await webSocketStore.call('startDisinfection', { + loglevel : parseInt(logVal.value), + roomVolume : parseInt(roomSize.value), + }); + } catch ( e ) { + operatorStore.updateShowStartReady(false) + await MyModal.error(`无法开始消毒 : ${e.message || e}`); + return ; + } + + operatorStore.updateShowStartReady(false) + props.changeShowOperator(false) + } } const showLogPicker = () => { diff --git a/src/main.js b/src/main.js index a7d2205..1148c9d 100644 --- a/src/main.js +++ b/src/main.js @@ -23,6 +23,7 @@ import 'vant/lib/index.css' import '@/assets/css/reset.css' import './style.scss' import MyInput from 'cpns/MyInput.vue' +import MyModal from 'cpns/MyModal.vue' createApp(App) .use(router) @@ -41,4 +42,5 @@ createApp(App) .use(Toast) .use(store) .component('my-input', MyInput) + .component('my-modal', MyModal) .mount('#app') diff --git a/src/store/modules/websocket.js b/src/store/modules/websocket.js index 2a5f42d..b47eafd 100644 --- a/src/store/modules/websocket.js +++ b/src/store/modules/websocket.js @@ -23,10 +23,31 @@ export const useWebSocketStore = defineStore({ socketCommandInstance: null, // 事件上报websocket 实例 socketEventInstance: null, + // Event Handlers + eventHandlers : {}, + + // Call ID Counter + callIdCounter : 0, + // Call Resolve Handler Map + callPromiseHandlers : {}, + // Call Param Merge Commands + callparamMergeCmds : [ + 'cleanDisinfectionRecord', 'startDisinfection', 'changeDisinfectionParameter','setSettingVal' + ], } }, // actions actions: { + // register event handler + registerEventHandler( event, handler ) { + this.eventHandlers[event] = this.eventHandlers[event] || []; + this.eventHandlers[event].push(handler); + }, + + + + + initCommandSocket() { const url = import.meta.env.VITE_BASE_WS1_URL const init = new Socket(url) @@ -41,8 +62,27 @@ export const useWebSocketStore = defineStore({ const runningStore = useRunningStore() const historyStore = useHistoryStore() init.connect() + + let $this = this; init.ws.onmessage = function (ev) { const { messageId, timeStamp } = JSON.parse(ev.data) + + // 优先处理call-response + if ( undefined !== $this.callPromiseHandlers[messageId] ) { + let response = JSON.parse(ev.data); + const handler = $this.callPromiseHandlers[messageId]; + delete $this.callPromiseHandlers[messageId]; + console.log(`[Call Response : ${messageId}] ${handler.message.command} => ${JSON.stringify(response)}`); + if ( 0 === response.ackcode ) { + handler.resolve(response); + } else { + handler.reject(response.ackDisplayInfo); + } + return; + } + + + console.log(JSON.parse(ev.data)) switch (messageId) { case 'getState': @@ -275,6 +315,31 @@ export const useWebSocketStore = defineStore({ sendCommandMsg(message) { this.socketCommandInstance?.msg(message) }, + + // call and wait for response + call( command, params=null ) { + this.callIdCounter += 1; + if ( this.callIdCounter > 1000000 ) { + this.callIdCounter = 0; + } + const callId = `call-${this.callIdCounter}`; + return new Promise(( resolve, reject ) => { + let message = {}; + message.command = command; + message.messageId = callId; + if ( null !== params ) { + if ( this.callparamMergeCmds.includes(command) ) { + message = { ...message, ...params }; + } else { + message.params = params; + } + } + this.callPromiseHandlers[callId] = { resolve, reject, message }; + console.log(`[Call Request : ${callId}] ${command}(${JSON.stringify(params)})`); + this.sendCommandMsg(message); + }); + }, + initEventSocket() { const url = import.meta.env.VITE_BASE_WS2_URL const init = new Socket(url) @@ -284,9 +349,21 @@ export const useWebSocketStore = defineStore({ const settingStore = useSettingStore() const operatorStore = useOperatorStore() const echartsStore = useEchartsStore() + + let $this = this; init.ws.onmessage = function (ev) { // console.log(JSON.parse(ev.data)) const { command, timeStamp } = JSON.parse(ev.data) + if ( undefined !== $this.eventHandlers[command] ) { + let data = JSON.parse(ev.data); + data = data.data; + for ( const handler of $this.eventHandlers[command] ) { + handler(data); + } + return ; + } + + switch (command) { case 'RealtimeSensorDataReport': const { sensor_data } = JSON.parse(ev.data) diff --git a/src/utils/MyModal.js b/src/utils/MyModal.js new file mode 100644 index 0000000..a74a7c6 --- /dev/null +++ b/src/utils/MyModal.js @@ -0,0 +1,60 @@ +import { createApp, h } from 'vue'; +import MyModalComponent from 'cpns/MyModal.vue'; +export default class MyModal { + // show error message + static error( message ) { + return new Promise( resolve => { + const modalContainer = document.createElement('div'); + document.body.appendChild(modalContainer); + const modalApp = createApp({ + render() { + return h(MyModalComponent, { + ref : 'modal', + icon : 'warning', + type : 'info', + content : message, + onOk : () => { + modalApp.unmount(); + document.body.removeChild(modalContainer); + resolve(); + } + }); + }, + }); + + const vm = modalApp.mount(modalContainer); + vm.$refs.modal.show(); + }); + } + + // show confirm message + static confirm( message ) { + return new Promise( resolve => { + const modalContainer = document.createElement('div'); + document.body.appendChild(modalContainer); + const modalApp = createApp({ + render() { + return h(MyModalComponent, { + ref : 'modal', + icon : 'warning', + type : 'confirm', + content : message, + onOk : () => { + modalApp.unmount(); + document.body.removeChild(modalContainer); + resolve(true); + }, + onCancel : () => { + modalApp.unmount(); + document.body.removeChild(modalContainer); + resolve(false); + } + }); + }, + }); + + const vm = modalApp.mount(modalContainer); + vm.$refs.modal.show(); + }); + } +} \ No newline at end of file