集成mqtt

dev_xq_0.0.1
谢庆 2 weeks ago
parent c3950561f9
commit 8a3837f511

@ -0,0 +1,175 @@
# STOMP 集成完成
## 已创建的文件
### 核心文件
1. **`src/utils/stompService.ts`** - STOMP 核心服务
- 完整的 STOMP 客户端封装(基于 @stomp/stompjs
- 支持连接、订阅、发布
- 自动重连机制
- 服务注册和消息分发
### 示例文件
2. **`src/utils/STOMP_USAGE.md`** - 详细使用文档
- API 文档
- 使用示例
- 迁移指南
- 故障排除
3. **`src/services/nodeService.ts`** - Node 服务示例
- 展示如何封装业务服务
- 设备节点管理
- 消息处理逻辑
4. **`src/components/StompDemo.vue`** - Vue 组件示例
- 完整的演示界面
- 连接控制
- 消息发送和日志
## 快速开始
### 1. 在 main.ts 中初始化
```typescript
import { createApp } from 'vue';
import App from './App.vue';
import { stompService } from './utils/stompService';
const app = createApp(App);
// 连接 STOMP 服务器默认地址ws://localhost:8080/stomp-endpoint
stompService.connect({
// 可选配置
// brokerUrl: 'ws://your-server:8080/stomp-endpoint',
// username: 'admin',
// password: 'password',
reconnectDelay: 5000
});
app.mount('#app');
```
### 2. 在组件中使用
```vue
<script lang="ts" setup>
import { onMounted } from 'vue';
import { stompService, MqttCmd } from '@/utils/stompService';
onMounted(() => {
// 注册服务
stompService.registerService('myService', {
mqttReady() {
console.log('STOMP 已就绪');
},
msgNode(value) {
console.log('收到 Node 消息:', value);
}
});
});
// 发送消息
function sendCommand() {
stompService.send('/app/web/write', MqttCmd.Control, {
nodeId: 'node-001',
action: 'turnOn'
});
}
</script>
```
### 3. 查看演示
`StompDemo.vue` 组件添加到您的路由中:
```typescript
// router/index.ts
import StompDemo from '@/components/StompDemo.vue';
{
path: '/stomp-demo',
name: 'StompDemo',
component: StompDemo
}
```
然后访问 `/stomp-demo` 查看演示界面。
## 主要特性
**自动重连** - 连接丢失后自动重连(默认 5 秒间隔)
**服务注册** - 支持多个业务服务注册
**消息分发** - 自动将消息分发到对应服务
**TypeScript** - 完整的类型支持
**错误处理** - 完善的错误处理和日志
**易于扩展** - 可轻松添加新服务
## 消息命令类型
```typescript
MqttCmd = {
Channel: 'channel', // 通道状态
NodeInit: 'nodeInit', // Node 初始化
Node: 'node', // Node 更新
User: 'user', // 用户登录
Control: 'control', // 控制命令
VideoInit: 'videoInit', // 视频初始化
VideoCtl: 'videoCtl', // 视频控制
Lock: 'lock', // 锁控
Alarm: 'alarm', // 告警
Info: 'info', // 通知提示
SocketDebugger: 'socketDebugger' // 调试
}
```
## 从 ExtJS 迁移
### ExtJS 版本
```javascript
Rec.mqtt.connect();
Rec.service.node = { msgNode: function(data) {} };
Rec.mqtt.send(cmd, value);
```
### Vue 版本
```typescript
import { stompService } from '@/utils/stompService';
stompService.connect();
stompService.registerService('node', { msgNode: (data) => {} });
stompService.send('/app/web/write', cmd, value);
```
## 连接地址
**默认连接地址:**
- 开发环境:`ws://localhost:8080/stomp-endpoint`
- 生产环境:`ws://your-domain/stomp-endpoint`
**自定义连接地址:**
```typescript
stompService.connect({
brokerUrl: 'ws://192.168.1.100:8080/stomp-endpoint'
});
```
## 注意事项
1. **连接时机**: 建议在 `main.ts` 中应用启动时连接
2. **服务注册**: 在组件的 `onMounted` 生命周期注册
3. **资源清理**: 组件卸载时根据需要清理
4. **TypeScript**: 所有方法都有完整类型定义
## 需要帮助?
- 查看 `src/utils/STOMP_USAGE.md` 获取详细文档
- 查看 `src/components/StompDemo.vue` 获取完整示例
- 查看 `src/services/nodeService.ts` 获取服务封装示例
## 下一步
1. 根据您的业务需求创建相应的服务
2. 将现有的 ExtJS 服务迁移到新的架构
3. 添加更多错误处理和日志记录
4. 实现消息持久化和离线缓存

531
package-lock.json generated

@ -8,6 +8,7 @@
"name": "maotu", "name": "maotu",
"version": "0.3.1", "version": "0.3.1",
"dependencies": { "dependencies": {
"@stomp/stompjs": "^7.3.0",
"@tweenjs/tween.js": "^25.0.0", "@tweenjs/tween.js": "^25.0.0",
"@vueuse/core": "^10.6.1", "@vueuse/core": "^10.6.1",
"ace-builds": "^1.32.0", "ace-builds": "^1.32.0",
@ -22,7 +23,9 @@
"html2canvas": "^1.4.1", "html2canvas": "^1.4.1",
"less": "^4.2.0", "less": "^4.2.0",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"mqtt": "^5.15.0",
"pinia": "^3.0.4", "pinia": "^3.0.4",
"sockjs-client": "^1.6.1",
"stats.js": "^0.17.0", "stats.js": "^0.17.0",
"three": "^0.182.0", "three": "^0.182.0",
"vue": "^3.3.4", "vue": "^3.3.4",
@ -547,9 +550,9 @@
} }
}, },
"node_modules/@babel/runtime": { "node_modules/@babel/runtime": {
"version": "7.28.6", "version": "7.29.2",
"resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.28.6.tgz", "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.29.2.tgz",
"integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@ -2084,6 +2087,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@stomp/stompjs": {
"version": "7.3.0",
"resolved": "https://registry.npmmirror.com/@stomp/stompjs/-/stompjs-7.3.0.tgz",
"integrity": "sha512-nKMLoFfJhrQAqkvvKd1vLq/cVBGCMwPRCD0LqW7UT1fecRx9C3GoKEIR2CYwVuErGeZu8w0kFkl2rlhPlqHVgQ==",
"license": "Apache-2.0"
},
"node_modules/@tootallnate/once": { "node_modules/@tootallnate/once": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmmirror.com/@tootallnate/once/-/once-2.0.0.tgz", "resolved": "https://registry.npmmirror.com/@tootallnate/once/-/once-2.0.0.tgz",
@ -2195,7 +2204,6 @@
"version": "18.19.130", "version": "18.19.130",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-18.19.130.tgz", "resolved": "https://registry.npmmirror.com/@types/node/-/node-18.19.130.tgz",
"integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~5.26.4" "undici-types": "~5.26.4"
@ -2207,6 +2215,15 @@
"integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/readable-stream": {
"version": "4.0.23",
"resolved": "https://registry.npmmirror.com/@types/readable-stream/-/readable-stream-4.0.23.tgz",
"integrity": "sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/semver": { "node_modules/@types/semver": {
"version": "7.7.1", "version": "7.7.1",
"resolved": "https://registry.npmmirror.com/@types/semver/-/semver-7.7.1.tgz", "resolved": "https://registry.npmmirror.com/@types/semver/-/semver-7.7.1.tgz",
@ -2274,6 +2291,15 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/ws": {
"version": "8.18.1",
"resolved": "https://registry.npmmirror.com/@types/ws/-/ws-8.18.1.tgz",
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "6.21.0", "version": "6.21.0",
"resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz",
@ -3340,6 +3366,18 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0" "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
} }
}, },
"node_modules/abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"license": "MIT",
"dependencies": {
"event-target-shim": "^5.0.0"
},
"engines": {
"node": ">=6.5"
}
},
"node_modules/ace-builds": { "node_modules/ace-builds": {
"version": "1.43.5", "version": "1.43.5",
"resolved": "https://registry.npmmirror.com/ace-builds/-/ace-builds-1.43.5.tgz", "resolved": "https://registry.npmmirror.com/ace-builds/-/ace-builds-1.43.5.tgz",
@ -3812,6 +3850,26 @@
"node": ">= 0.6.0" "node": ">= 0.6.0"
} }
}, },
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/baseline-browser-mapping": { "node_modules/baseline-browser-mapping": {
"version": "2.9.14", "version": "2.9.14",
"resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.14.tgz", "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.14.tgz",
@ -3854,6 +3912,34 @@
"url": "https://github.com/sponsors/antfu" "url": "https://github.com/sponsors/antfu"
} }
}, },
"node_modules/bl": {
"version": "6.1.6",
"resolved": "https://registry.npmmirror.com/bl/-/bl-6.1.6.tgz",
"integrity": "sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg==",
"license": "MIT",
"dependencies": {
"@types/readable-stream": "^4.0.0",
"buffer": "^6.0.3",
"inherits": "^2.0.4",
"readable-stream": "^4.2.0"
}
},
"node_modules/bl/node_modules/readable-stream": {
"version": "4.7.0",
"resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-4.7.0.tgz",
"integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
"license": "MIT",
"dependencies": {
"abort-controller": "^3.0.0",
"buffer": "^6.0.3",
"events": "^3.3.0",
"process": "^0.11.10",
"string_decoder": "^1.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/bluebird": { "node_modules/bluebird": {
"version": "3.7.2", "version": "3.7.2",
"resolved": "https://registry.npmmirror.com/bluebird/-/bluebird-3.7.2.tgz", "resolved": "https://registry.npmmirror.com/bluebird/-/bluebird-3.7.2.tgz",
@ -3891,6 +3977,24 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/broker-factory": {
"version": "3.1.14",
"resolved": "https://registry.npmmirror.com/broker-factory/-/broker-factory-3.1.14.tgz",
"integrity": "sha512-L45k5HMbPIrMid0nTOZ/UPXG/c0aRuQKVrSDFIb1zOkvfiyHgYmIjc3cSiN1KwQIvRDOtKE0tfb3I9EZ3CmpQQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.29.2",
"fast-unique-numbers": "^9.0.27",
"tslib": "^2.8.1",
"worker-factory": "^7.0.49"
}
},
"node_modules/broker-factory/node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/browserslist": { "node_modules/browserslist": {
"version": "4.28.1", "version": "4.28.1",
"resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.1.tgz", "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.1.tgz",
@ -3926,6 +4030,36 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
} }
}, },
"node_modules/buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmmirror.com/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"license": "MIT"
},
"node_modules/cac": { "node_modules/cac": {
"version": "6.7.14", "version": "6.7.14",
"resolved": "https://registry.npmmirror.com/cac/-/cac-6.7.14.tgz", "resolved": "https://registry.npmmirror.com/cac/-/cac-6.7.14.tgz",
@ -4276,6 +4410,12 @@
"node": ">=14" "node": ">=14"
} }
}, },
"node_modules/commist": {
"version": "3.2.0",
"resolved": "https://registry.npmmirror.com/commist/-/commist-3.2.0.tgz",
"integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==",
"license": "MIT"
},
"node_modules/component-emitter": { "node_modules/component-emitter": {
"version": "1.3.1", "version": "1.3.1",
"resolved": "https://registry.npmmirror.com/component-emitter/-/component-emitter-1.3.1.tgz", "resolved": "https://registry.npmmirror.com/component-emitter/-/component-emitter-1.3.1.tgz",
@ -4306,6 +4446,21 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/concat-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/concat-stream/-/concat-stream-2.0.0.tgz",
"integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
"engines": [
"node >= 6.0"
],
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.0.2",
"typedarray": "^0.0.6"
}
},
"node_modules/confbox": { "node_modules/confbox": {
"version": "0.2.2", "version": "0.2.2",
"resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.2.2.tgz", "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.2.2.tgz",
@ -4683,7 +4838,6 @@
"version": "4.4.3", "version": "4.4.3",
"resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"ms": "^2.1.3" "ms": "^2.1.3"
@ -5711,6 +5865,33 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmmirror.com/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"license": "MIT",
"engines": {
"node": ">=0.8.x"
}
},
"node_modules/eventsource": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/eventsource/-/eventsource-2.0.2.tgz",
"integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==",
"license": "MIT",
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/expand-brackets": { "node_modules/expand-brackets": {
"version": "2.1.4", "version": "2.1.4",
"resolved": "https://registry.npmmirror.com/expand-brackets/-/expand-brackets-2.1.4.tgz", "resolved": "https://registry.npmmirror.com/expand-brackets/-/expand-brackets-2.1.4.tgz",
@ -5872,6 +6053,25 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/fast-unique-numbers": {
"version": "9.0.27",
"resolved": "https://registry.npmmirror.com/fast-unique-numbers/-/fast-unique-numbers-9.0.27.tgz",
"integrity": "sha512-nDA9ADeINN8SA2u2wCtU+siWFTTDqQR37XvgPIDDmboWQeExz7X0mImxuaN+kJddliIqy2FpVRmnvRZ+j8i1/A==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.29.2",
"tslib": "^2.8.1"
},
"engines": {
"node": ">=18.2.0"
}
},
"node_modules/fast-unique-numbers/node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/fastq": { "node_modules/fastq": {
"version": "1.20.1", "version": "1.20.1",
"resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.20.1.tgz", "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.20.1.tgz",
@ -5882,6 +6082,18 @@
"reusify": "^1.0.4" "reusify": "^1.0.4"
} }
}, },
"node_modules/faye-websocket": {
"version": "0.11.4",
"resolved": "https://registry.npmmirror.com/faye-websocket/-/faye-websocket-0.11.4.tgz",
"integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
"license": "Apache-2.0",
"dependencies": {
"websocket-driver": ">=0.5.1"
},
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/fflate": { "node_modules/fflate": {
"version": "0.8.2", "version": "0.8.2",
"resolved": "https://registry.npmmirror.com/fflate/-/fflate-0.8.2.tgz", "resolved": "https://registry.npmmirror.com/fflate/-/fflate-0.8.2.tgz",
@ -6582,6 +6794,12 @@
"he": "bin/he" "he": "bin/he"
} }
}, },
"node_modules/help-me": {
"version": "5.0.0",
"resolved": "https://registry.npmmirror.com/help-me/-/help-me-5.0.0.tgz",
"integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==",
"license": "MIT"
},
"node_modules/hookable": { "node_modules/hookable": {
"version": "5.5.3", "version": "5.5.3",
"resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz", "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz",
@ -6636,6 +6854,12 @@
"dev": true, "dev": true,
"license": "BSD-2-Clause" "license": "BSD-2-Clause"
}, },
"node_modules/http-parser-js": {
"version": "0.5.10",
"resolved": "https://registry.npmmirror.com/http-parser-js/-/http-parser-js-0.5.10.tgz",
"integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==",
"license": "MIT"
},
"node_modules/http-proxy-agent": { "node_modules/http-proxy-agent": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
@ -6678,6 +6902,26 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "BSD-3-Clause"
},
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz",
@ -6761,7 +7005,6 @@
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/ini": { "node_modules/ini": {
@ -6786,6 +7029,15 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/ip-address": {
"version": "10.1.0",
"resolved": "https://registry.npmmirror.com/ip-address/-/ip-address-10.1.0.tgz",
"integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
"license": "MIT",
"engines": {
"node": ">= 12"
}
},
"node_modules/is-accessor-descriptor": { "node_modules/is-accessor-descriptor": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmmirror.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz", "resolved": "https://registry.npmmirror.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz",
@ -7423,6 +7675,16 @@
"node": ">=14" "node": ">=14"
} }
}, },
"node_modules/js-sdsl": {
"version": "4.3.0",
"resolved": "https://registry.npmmirror.com/js-sdsl/-/js-sdsl-4.3.0.tgz",
"integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/js-sdsl"
}
},
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
@ -7758,7 +8020,6 @@
"version": "10.4.3", "version": "10.4.3",
"resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz", "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/magic-string": { "node_modules/magic-string": {
@ -7946,7 +8207,6 @@
"version": "1.2.8", "version": "1.2.8",
"resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true,
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
@ -8034,6 +8294,65 @@
"pathe": "^2.0.1" "pathe": "^2.0.1"
} }
}, },
"node_modules/mqtt": {
"version": "5.15.0",
"resolved": "https://registry.npmmirror.com/mqtt/-/mqtt-5.15.0.tgz",
"integrity": "sha512-KC+wAssYk83Qu5bT8YDzDYgUJxPhbLeVsDvpY2QvL28PnXYJzC2WkKruyMUgBAZaQ7h9lo9k2g4neRNUUxzgMw==",
"license": "MIT",
"dependencies": {
"@types/readable-stream": "^4.0.21",
"@types/ws": "^8.18.1",
"commist": "^3.2.0",
"concat-stream": "^2.0.0",
"debug": "^4.4.1",
"help-me": "^5.0.0",
"lru-cache": "^10.4.3",
"minimist": "^1.2.8",
"mqtt-packet": "^9.0.2",
"number-allocator": "^1.0.14",
"readable-stream": "^4.7.0",
"rfdc": "^1.4.1",
"socks": "^2.8.6",
"split2": "^4.2.0",
"worker-timers": "^8.0.23",
"ws": "^8.18.3"
},
"bin": {
"mqtt": "build/bin/mqtt.js",
"mqtt_pub": "build/bin/pub.js",
"mqtt_sub": "build/bin/sub.js"
},
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/mqtt-packet": {
"version": "9.0.2",
"resolved": "https://registry.npmmirror.com/mqtt-packet/-/mqtt-packet-9.0.2.tgz",
"integrity": "sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA==",
"license": "MIT",
"dependencies": {
"bl": "^6.0.8",
"debug": "^4.3.4",
"process-nextick-args": "^2.0.1"
}
},
"node_modules/mqtt/node_modules/readable-stream": {
"version": "4.7.0",
"resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-4.7.0.tgz",
"integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
"license": "MIT",
"dependencies": {
"abort-controller": "^3.0.0",
"buffer": "^6.0.3",
"events": "^3.3.0",
"process": "^0.11.10",
"string_decoder": "^1.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/mrmime": { "node_modules/mrmime": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmmirror.com/mrmime/-/mrmime-2.0.1.tgz", "resolved": "https://registry.npmmirror.com/mrmime/-/mrmime-2.0.1.tgz",
@ -8048,7 +8367,6 @@
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/muggle-string": { "node_modules/muggle-string": {
@ -8323,6 +8641,16 @@
"url": "https://github.com/fb55/nth-check?sponsor=1" "url": "https://github.com/fb55/nth-check?sponsor=1"
} }
}, },
"node_modules/number-allocator": {
"version": "1.0.14",
"resolved": "https://registry.npmmirror.com/number-allocator/-/number-allocator-1.0.14.tgz",
"integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==",
"license": "MIT",
"dependencies": {
"debug": "^4.3.1",
"js-sdsl": "4.3.0"
}
},
"node_modules/nwsapi": { "node_modules/nwsapi": {
"version": "2.2.23", "version": "2.2.23",
"resolved": "https://registry.npmmirror.com/nwsapi/-/nwsapi-2.2.23.tgz", "resolved": "https://registry.npmmirror.com/nwsapi/-/nwsapi-2.2.23.tgz",
@ -9087,6 +9415,21 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1" "url": "https://github.com/chalk/ansi-styles?sponsor=1"
} }
}, },
"node_modules/process": {
"version": "0.11.10",
"resolved": "https://registry.npmmirror.com/process/-/process-0.11.10.tgz",
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
"license": "MIT",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"license": "MIT"
},
"node_modules/proto-list": { "node_modules/proto-list": {
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npmmirror.com/proto-list/-/proto-list-1.2.4.tgz", "resolved": "https://registry.npmmirror.com/proto-list/-/proto-list-1.2.4.tgz",
@ -9165,7 +9508,6 @@
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmmirror.com/querystringify/-/querystringify-2.2.0.tgz", "resolved": "https://registry.npmmirror.com/querystringify/-/querystringify-2.2.0.tgz",
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/queue-microtask": { "node_modules/queue-microtask": {
@ -9223,7 +9565,6 @@
"version": "3.6.2", "version": "3.6.2",
"resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"inherits": "^2.0.3", "inherits": "^2.0.3",
@ -9356,7 +9697,6 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz", "resolved": "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/resize-detector": { "node_modules/resize-detector": {
@ -9589,7 +9929,6 @@
"version": "5.2.1", "version": "5.2.1",
"resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@ -9989,6 +10328,16 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/smart-buffer": {
"version": "4.2.0",
"resolved": "https://registry.npmmirror.com/smart-buffer/-/smart-buffer-4.2.0.tgz",
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
"license": "MIT",
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/snapdragon": { "node_modules/snapdragon": {
"version": "0.8.2", "version": "0.8.2",
"resolved": "https://registry.npmmirror.com/snapdragon/-/snapdragon-0.8.2.tgz", "resolved": "https://registry.npmmirror.com/snapdragon/-/snapdragon-0.8.2.tgz",
@ -10114,6 +10463,48 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/sockjs-client": {
"version": "1.6.1",
"resolved": "https://registry.npmmirror.com/sockjs-client/-/sockjs-client-1.6.1.tgz",
"integrity": "sha512-2g0tjOR+fRs0amxENLi/q5TiJTqY+WXFOzb5UwXndlK6TO3U/mirZznpx6w34HVMoc3g7cY24yC/ZMIYnDlfkw==",
"license": "MIT",
"dependencies": {
"debug": "^3.2.7",
"eventsource": "^2.0.2",
"faye-websocket": "^0.11.4",
"inherits": "^2.0.4",
"url-parse": "^1.5.10"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://tidelift.com/funding/github/npm/sockjs-client"
}
},
"node_modules/sockjs-client/node_modules/debug": {
"version": "3.2.7",
"resolved": "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz",
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.1"
}
},
"node_modules/socks": {
"version": "2.8.7",
"resolved": "https://registry.npmmirror.com/socks/-/socks-2.8.7.tgz",
"integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==",
"license": "MIT",
"dependencies": {
"ip-address": "^10.0.1",
"smart-buffer": "^4.2.0"
},
"engines": {
"node": ">= 10.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/source-map": { "node_modules/source-map": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
@ -10205,6 +10596,15 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/split2": {
"version": "4.2.0",
"resolved": "https://registry.npmmirror.com/split2/-/split2-4.2.0.tgz",
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
"license": "ISC",
"engines": {
"node": ">= 10.x"
}
},
"node_modules/sprintf-js": { "node_modules/sprintf-js": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz", "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz",
@ -10318,7 +10718,6 @@
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz", "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"safe-buffer": "~5.2.0" "safe-buffer": "~5.2.0"
@ -11326,6 +11725,12 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmmirror.com/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
"license": "MIT"
},
"node_modules/typedarray.prototype.slice": { "node_modules/typedarray.prototype.slice": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmmirror.com/typedarray.prototype.slice/-/typedarray.prototype.slice-1.0.5.tgz", "resolved": "https://registry.npmmirror.com/typedarray.prototype.slice/-/typedarray.prototype.slice-1.0.5.tgz",
@ -11419,7 +11824,6 @@
"version": "5.26.5", "version": "5.26.5",
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-5.26.5.tgz", "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/union-value": { "node_modules/union-value": {
@ -11610,7 +12014,6 @@
"version": "1.5.10", "version": "1.5.10",
"resolved": "https://registry.npmmirror.com/url-parse/-/url-parse-1.5.10.tgz", "resolved": "https://registry.npmmirror.com/url-parse/-/url-parse-1.5.10.tgz",
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"querystringify": "^2.1.1", "querystringify": "^2.1.1",
@ -11631,7 +12034,6 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/utrie": { "node_modules/utrie": {
@ -12146,6 +12548,29 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/websocket-driver": {
"version": "0.7.4",
"resolved": "https://registry.npmmirror.com/websocket-driver/-/websocket-driver-0.7.4.tgz",
"integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
"license": "Apache-2.0",
"dependencies": {
"http-parser-js": ">=0.5.1",
"safe-buffer": ">=5.1.0",
"websocket-extensions": ">=0.1.1"
},
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/websocket-extensions": {
"version": "0.1.4",
"resolved": "https://registry.npmmirror.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
"integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/whatwg-encoding": { "node_modules/whatwg-encoding": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmmirror.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", "resolved": "https://registry.npmmirror.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
@ -12322,6 +12747,77 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/worker-factory": {
"version": "7.0.49",
"resolved": "https://registry.npmmirror.com/worker-factory/-/worker-factory-7.0.49.tgz",
"integrity": "sha512-lW7tpgy6aUv2dFsQhv1yv+XFzdkCf/leoKRTGMPVK5/die6RrUjqgJHJf556qO+ZfytNG6wPXc17E8zzsOLUDw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.29.2",
"fast-unique-numbers": "^9.0.27",
"tslib": "^2.8.1"
}
},
"node_modules/worker-factory/node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/worker-timers": {
"version": "8.0.31",
"resolved": "https://registry.npmmirror.com/worker-timers/-/worker-timers-8.0.31.tgz",
"integrity": "sha512-ngkq5S6JuZyztom8tDgBzorLo9byhBMko/sXfgiUD945AuzKGg1GCgDMCC3NaYkicLpGKXutONM36wEX8UbBCA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.29.2",
"tslib": "^2.8.1",
"worker-timers-broker": "^8.0.16",
"worker-timers-worker": "^9.0.14"
}
},
"node_modules/worker-timers-broker": {
"version": "8.0.16",
"resolved": "https://registry.npmmirror.com/worker-timers-broker/-/worker-timers-broker-8.0.16.tgz",
"integrity": "sha512-JyP3AvUGyPGbBGW7XiUewm2+0pN/aYo1QpVf5kdXAfkDZcN3p7NbWrG6XnyDEpDIvfHk/+LCnOW/NsuiU9riYA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.29.2",
"broker-factory": "^3.1.14",
"fast-unique-numbers": "^9.0.27",
"tslib": "^2.8.1",
"worker-timers-worker": "^9.0.14"
}
},
"node_modules/worker-timers-broker/node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/worker-timers-worker": {
"version": "9.0.14",
"resolved": "https://registry.npmmirror.com/worker-timers-worker/-/worker-timers-worker-9.0.14.tgz",
"integrity": "sha512-/qF06C60sXmSLfUl7WglvrDIbspmPOM8UrG63Dnn4bi2x4/DfqHS/+dxF5B+MdHnYO5tVuZYLHdAodrKdabTIg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.29.2",
"tslib": "^2.8.1",
"worker-factory": "^7.0.49"
}
},
"node_modules/worker-timers-worker/node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/worker-timers/node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/wrap-ansi": { "node_modules/wrap-ansi": {
"version": "8.1.0", "version": "8.1.0",
"resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
@ -12434,7 +12930,6 @@
"version": "8.19.0", "version": "8.19.0",
"resolved": "https://registry.npmmirror.com/ws/-/ws-8.19.0.tgz", "resolved": "https://registry.npmmirror.com/ws/-/ws-8.19.0.tgz",
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=10.0.0"

@ -29,6 +29,7 @@
}, },
"typings": "dist/export.d.ts", "typings": "dist/export.d.ts",
"dependencies": { "dependencies": {
"@stomp/stompjs": "^7.3.0",
"@tweenjs/tween.js": "^25.0.0", "@tweenjs/tween.js": "^25.0.0",
"@vueuse/core": "^10.6.1", "@vueuse/core": "^10.6.1",
"ace-builds": "^1.32.0", "ace-builds": "^1.32.0",
@ -43,7 +44,9 @@
"html2canvas": "^1.4.1", "html2canvas": "^1.4.1",
"less": "^4.2.0", "less": "^4.2.0",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"mqtt": "^5.15.0",
"pinia": "^3.0.4", "pinia": "^3.0.4",
"sockjs-client": "^1.6.1",
"stats.js": "^0.17.0", "stats.js": "^0.17.0",
"three": "^0.182.0", "three": "^0.182.0",
"vue": "^3.3.4", "vue": "^3.3.4",

@ -4,6 +4,7 @@
<!-- <VueThreeMachine /> --> <!-- <VueThreeMachine /> -->
<!-- <vueThreeEquipment /> --> <!-- <vueThreeEquipment /> -->
<!-- <VueGradeGauge /> --> <!-- <VueGradeGauge /> -->
<StompDemo />
<el-config-provider :locale="zhCn"> <el-config-provider :locale="zhCn">
<router-view /> <router-view />
</el-config-provider> </el-config-provider>
@ -17,6 +18,9 @@ import { leftAsideStore } from '@/export';
import viewIndex from '@/layout/view_index.vue'; import viewIndex from '@/layout/view_index.vue';
import zhCn from 'element-plus/es/locale/lang/zh-cn'; import zhCn from 'element-plus/es/locale/lang/zh-cn';
// xq // xq
// F:\vue\workspace\maotu-webtopo\src\components\StompDemo.vue
import StompDemo from '@/components/StompDemo.vue';
import VueMyTest from '@/components/my-test.vue'; import VueMyTest from '@/components/my-test.vue';
import VueMyButton from '@/components/my-button.vue'; import VueMyButton from '@/components/my-button.vue';
import vueInstrument from '@/components/vue-xq-test/vue-instrument.vue'; import vueInstrument from '@/components/vue-xq-test/vue-instrument.vue';

@ -45,8 +45,8 @@
--el-box-shadow: 0px 12px 32px 4px rgba(0, 0, 0, 0.36), 0px 8px 20px rgba(0, 0, 0, 0.72); --el-box-shadow: 0px 12px 32px 4px rgba(0, 0, 0, 0.36), 0px 8px 20px rgba(0, 0, 0, 0.72);
--el-box-shadow-light: 0px 0px 12px rgba(0, 0, 0, 0.72); --el-box-shadow-light: 0px 0px 12px rgba(0, 0, 0, 0.72);
--el-box-shadow-lighter: 0px 0px 6px rgba(0, 0, 0, 0.72); --el-box-shadow-lighter: 0px 0px 6px rgba(0, 0, 0, 0.72);
--el-box-shadow-dark: 0px 16px 48px 16px rgba(0, 0, 0, 0.72), 0px 12px 32px #000000, --el-box-shadow-dark:
0px 8px 16px -8px #000000; 0px 16px 48px 16px rgba(0, 0, 0, 0.72), 0px 12px 32px #000000, 0px 8px 16px -8px #000000;
--el-bg-color-page: #0a0a0a; --el-bg-color-page: #0a0a0a;
--el-bg-color: #141414; --el-bg-color: #141414;
--el-bg-color-overlay: #1d1e1f; --el-bg-color-overlay: #1d1e1f;

@ -0,0 +1,222 @@
<template>
<el-card class="stomp-demo">
<template #header>
<div class="card-header">
<span>STOMP 连接演示</span>
<el-tag :type="isConnected ? 'success' : 'danger'">
{{ isConnected ? '已连接' : '未连接' }}
</el-tag>
</div>
</template>
<!-- 连接控制 -->
<div class="control-section">
<el-button type="primary" @click="connect" :disabled="isConnected">连接</el-button>
<el-button type="danger" @click="disconnect" :disabled="!isConnected">断开</el-button>
</div>
<!-- 消息发送 -->
<div class="message-section">
<h3>发送消息</h3>
<el-form :model="messageForm" label-width="80px" size="small">
<el-form-item label="命令类型">
<el-select v-model="messageForm.cmd" style="width: 100%">
<el-option
v-for="item in cmdOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="消息内容">
<el-input
v-model="messageForm.value"
type="textarea"
:rows="4"
placeholder='例如:{"nodeId": "node-001", "action": "turnOn"}'
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="sendMessage" :disabled="!isConnected"> 发送 </el-button>
</el-form-item>
</el-form>
</div>
<!-- 消息日志 -->
<div class="log-section">
<h3>消息日志</h3>
<el-table :data="messageLogs" style="width: 100%" max-height="300" size="small">
<el-table-column prop="time" label="时间" width="180" />
<el-table-column prop="cmd" label="命令" width="150" />
<el-table-column prop="direction" label="方向" width="80">
<template #default="{ row }">
<el-tag :type="row.direction === '发送' ? 'primary' : 'success'" size="small">
{{ row.direction }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="content" label="内容" />
</el-table>
<el-button size="small" @click="clearLogs" style="margin-top: 10px"> 清空日志 </el-button>
</div>
</el-card>
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted, onUnmounted } from 'vue';
import { ElMessage } from 'element-plus';
import { stompService, MqttCmd, type StompMessage } from '../utils/stompService';
//
const isConnected = ref(false);
//
const messageForm = reactive({
cmd: MqttCmd.Control,
value: ''
});
//
const cmdOptions = [
{ label: '控制命令', value: MqttCmd.Control },
{ label: 'Node 更新', value: MqttCmd.Node },
{ label: '告警', value: MqttCmd.Alarm },
{ label: '通知', value: MqttCmd.Info },
{ label: '通道状态', value: MqttCmd.Channel }
];
//
const messageLogs = ref<
Array<{
time: string;
cmd: number;
direction: string;
content: string;
}>
>([]);
//
const addLog = (cmd: string, direction: string, content: string) => {
messageLogs.value.unshift({
time: new Date().toLocaleTimeString(),
cmd,
direction,
content: typeof content === 'string' ? content : JSON.stringify(content)
});
//
if (messageLogs.value.length > 50) {
messageLogs.value.pop();
}
};
//
const clearLogs = () => {
messageLogs.value = [];
};
// STOMP
const connect = () => {
stompService.connect({
reconnectDelay: 5000
});
ElMessage.info('正在连接 STOMP...');
};
//
const disconnect = () => {
stompService.disconnect();
ElMessage.info('已断开 STOMP 连接');
};
//
const sendMessage = () => {
if (!isConnected.value) {
ElMessage.warning('请先连接 STOMP');
return;
}
try {
const valueObj = JSON.parse(messageForm.value || '{}');
stompService.send('/app/web/write', messageForm.cmd, valueObj);
addLog(messageForm.cmd, '发送', valueObj);
ElMessage.success('消息已发送');
} catch (error) {
ElMessage.error('消息格式错误,请输入有效的 JSON');
}
};
//
onMounted(() => {
stompService.registerService('demoComponent', {
mqttReady() {
console.log('[Demo] STOMP 已就绪');
isConnected.value = true;
ElMessage.success('STOMP 连接成功');
},
onConnectionLost() {
console.log('[Demo] STOMP 连接丢失');
isConnected.value = false;
ElMessage.warning('STOMP 连接已断开,正在重连...');
}
});
// Node
stompService.registerService('demoNode', {
msgNodeInit(value) {
addLog(MqttCmd.NodeInit, '接收', value);
},
msgNode(value) {
addLog(MqttCmd.Node, '接收', value);
}
});
//
stompService.registerService('demoAlarm', {
alarm(value) {
addLog(MqttCmd.Alarm, '接收', value);
ElMessage.warning(`收到告警:${JSON.stringify(value)}`);
}
});
});
//
onUnmounted(() => {
//
// stompService.disconnect();
});
</script>
<style scoped>
.stomp-demo {
margin: 20px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.control-section {
margin-bottom: 20px;
display: flex;
gap: 10px;
}
.message-section,
.log-section {
margin-top: 20px;
}
h3 {
margin: 10px 0;
font-size: 14px;
color: var(--el-text-color-primary);
}
</style>

@ -83,8 +83,8 @@
? 1000 ? 1000
: 0 : 0
: lineRenderProps.itemJson.props.ani_play.val : lineRenderProps.itemJson.props.ani_play.val
? 0 ? 0
: 1000 : 1000
" "
:dur="`${ :dur="`${
lineRenderProps.itemJson.props.ani_dur.val < 1 lineRenderProps.itemJson.props.ani_dur.val < 1

@ -83,8 +83,8 @@
? 1000 ? 1000
: 0 : 0
: lineRenderProps.itemJson.props.ani_play.val : lineRenderProps.itemJson.props.ani_play.val
? 0 ? 0
: 1000 : 1000
" "
:dur="`${ :dur="`${
lineRenderProps.itemJson.props.ani_dur.val < 1 lineRenderProps.itemJson.props.ani_dur.val < 1
@ -147,8 +147,8 @@
? 1000 ? 1000
: 0 : 0
: lineRenderProps.itemJson.props.ani_play.val : lineRenderProps.itemJson.props.ani_play.val
? 0 ? 0
: 1000 : 1000
" "
:dur="`${ :dur="`${
lineRenderProps.itemJson.props.ani_dur.val < 1 lineRenderProps.itemJson.props.ani_dur.val < 1
@ -534,7 +534,8 @@ watch(
</script> </script>
<style scoped> <style scoped>
.cursor-remove { .cursor-remove {
cursor: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjQgMjQiPgoJPGcgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS1kYXNoYXJyYXk9IjIyIiBzdHJva2UtZGFzaG9mZnNldD0iMjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIyIj4KCQk8cGF0aCBkPSJNMTkgNUw1IDE5Ij48YW5pbWF0ZSBmaWxsPSJmcmVlemUiIGF0dHJpYnV0ZU5hbWU9InN0cm9rZS1kYXNob2Zmc2V0IiBiZWdpbj0iMC4zcyIgZHVyPSIwLjNzIiB2YWx1ZXM9IjIyOzAiLz48L3BhdGg+CgkJPHBhdGggZD0iTTUgNUwxOSAxOSI+PGFuaW1hdGUgZmlsbD0iZnJlZXplIiBhdHRyaWJ1dGVOYW1lPSJzdHJva2UtZGFzaG9mZnNldCIgZHVyPSIwLjNzIiB2YWx1ZXM9IjIyOzAiLz48L3BhdGg+Cgk8L2c+Cjwvc3ZnPg==), cursor:
url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjQgMjQiPgoJPGcgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS1kYXNoYXJyYXk9IjIyIiBzdHJva2UtZGFzaG9mZnNldD0iMjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIyIj4KCQk8cGF0aCBkPSJNMTkgNUw1IDE5Ij48YW5pbWF0ZSBmaWxsPSJmcmVlemUiIGF0dHJpYnV0ZU5hbWU9InN0cm9rZS1kYXNob2Zmc2V0IiBiZWdpbj0iMC4zcyIgZHVyPSIwLjNzIiB2YWx1ZXM9IjIyOzAiLz48L3BhdGg+CgkJPHBhdGggZD0iTTUgNUwxOSAxOSI+PGFuaW1hdGUgZmlsbD0iZnJlZXplIiBhdHRyaWJ1dGVOYW1lPSJzdHJva2UtZGFzaG9mZnNldCIgZHVyPSIwLjNzIiB2YWx1ZXM9IjIyOzAiLz48L3BhdGg+Cgk8L2c+Cjwvc3ZnPg==),
auto; auto;
} }
</style> </style>

@ -0,0 +1,135 @@
/**
* Node
* STOMP
*/
import { stompService } from '@/utils/stompService';
interface NodeData {
id: string;
name: string;
status: string;
[key: string]: any;
}
interface NodeRuntime {
node: NodeData;
[key: string]: any;
}
class NodeService {
public runtimes: Record<string, NodeRuntime> = {};
constructor() {
// 注册服务
stompService.registerService('node', this);
}
/**
* STOMP
*/
mqttReady(): void {
console.log('[NodeService] STOMP 已就绪');
// 可以在这里请求初始化数据
}
/**
*
*/
onConnectionLost(): void {
console.log('[NodeService] STOMP 连接丢失');
}
/**
* Node
* @param value
*/
msgNodeInit(value: any): void {
console.log('[NodeService] 收到 Node 初始化数据:', value);
// 处理初始化逻辑
if (value && Array.isArray(value.nodes)) {
value.nodes.forEach((node: NodeData) => {
this.runtimes[node.id] = {
node,
lastUpdate: new Date()
};
});
}
}
/**
* Node
* @param value
*/
msgNode(value: any): void {
console.log('[NodeService] 收到 Node 更新:', value);
if (value && value.id) {
const runtime = this.runtimes[value.id];
if (runtime) {
// 更新现有节点
runtime.node = { ...runtime.node, ...value };
runtime.lastUpdate = new Date();
} else {
// 添加新节点
this.runtimes[value.id] = {
node: value,
lastUpdate: new Date()
};
}
// 触发更新事件(可以使用 mitt 或其他事件总线)
this.emitNodeUpdate(value.id);
}
}
/**
*
* @param nodeId ID
*/
getNode(nodeId: string): NodeData | null {
const runtime = this.runtimes[nodeId];
return runtime ? runtime.node : null;
}
/**
*
*/
getAllNodes(): NodeData[] {
return Object.values(this.runtimes).map((runtime) => runtime.node);
}
/**
*
* @param nodeId ID
* @param updates
*/
updateNode(nodeId: string, updates: Partial<NodeData>): void {
const runtime = this.runtimes[nodeId];
if (runtime) {
runtime.node = { ...runtime.node, ...updates };
runtime.lastUpdate = new Date();
// 发送更新到服务器
stompService.send('/app/web/write', 'nodeUpdate', {
id: nodeId,
updates
});
}
}
/**
*
* @param nodeId ID
*/
private emitNodeUpdate(nodeId: string): void {
// 这里可以使用 mitt 事件总线
// emitter.emit('node-update', nodeId);
console.log(`[NodeService] 节点 ${nodeId} 已更新`);
}
}
// 导出单例
export const nodeService = new NodeService();
export default nodeService;

@ -0,0 +1,300 @@
# MQTT 服务使用指南
## 简介
这是一个基于 `mqtt` 库封装的 MQTT 服务,提供了完整的连接、订阅、发布和消息处理功能。
## 主要功能
- ✅ 自动连接和重连
- ✅ 服务注册机制
- ✅ 消息自动分发
- ✅ TypeScript 类型支持
- ✅ 错误处理和日志记录
## 使用方法
### 1. 在 main.ts 中初始化
```typescript
import { mqttService } from './utils/mqttService';
// 应用启动时连接 MQTT
const app = createApp(App);
// 连接 MQTT 服务器
mqttService.connect({
// brokerUrl: 'ws://your-mqtt-broker:8083/mqtt', // 可选,默认使用当前域名
// username: 'your-username', // 可选
// password: 'your-password', // 可选
// clientId: 'your-client-id', // 可选,自动生成
reconnectPeriod: 5000 // 重连间隔,默认 5000ms
});
app.mount('#app');
```
### 2. 注册服务
```typescript
// 在您的组件或 store 中
import { mqttService } from '@/utils/mqttService';
// 注册您的服务
mqttService.registerService('node', {
// 当 MQTT 连接成功时会调用此方法
mqttReady() {
console.log('Node 服务已就绪');
},
// 处理 Node 初始化消息
msgNodeInit(data: any) {
console.log('收到 Node 初始化数据:', data);
},
// 处理 Node 更新消息
msgNode(data: any) {
console.log('收到 Node 更新:', data);
},
// 运行时的数据
runtimes: {}
});
// 注册其他服务
mqttService.registerService('channel', {
online(cid: string, isOnline: boolean) {
console.log(`通道 ${cid} 状态:`, isOnline);
}
});
mqttService.registerService('video', {
initVideo(data: any) {
console.log('初始化视频:', data);
},
playVideo(data: any) {
console.log('播放视频:', data);
}
});
```
### 3. 发送消息
```typescript
import { mqttService, MqttCmd } from '@/utils/mqttService';
// 发送控制命令
mqttService.send('/app/web/write', MqttCmd.Control, {
nodeId: 'node-001',
action: 'turnOn'
});
// 发送 Node 更新
mqttService.send('/app/web/write', MqttCmd.Node, {
node: {
id: 'node-001',
status: 'active'
}
});
```
### 4. 订阅主题
```typescript
import { mqttService } from '@/utils/mqttService';
// 订阅自定义主题
mqttService.subscribe('/topic/custom', (message) => {
console.log('收到自定义消息:', message);
});
// 取消订阅
mqttService.unsubscribe('/topic/custom');
```
### 5. 在 Vue 组件中使用
```vue
<template>
<div>
<el-button @click="sendCommand">发送命令</el-button>
<div>连接状态:{{ isConnected ? '已连接' : '未连接' }}</div>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { mqttService, MqttCmd } from '@/utils/mqttService';
const isConnected = ref(false);
// 注册服务
onMounted(() => {
mqttService.registerService('myComponent', {
mqttReady() {
console.log('组件服务已就绪');
isConnected.value = true;
},
onConnectionLost() {
isConnected.value = false;
}
});
});
// 发送命令
function sendCommand() {
mqttService.send('/app/web/write', MqttCmd.Control, {
command: 'test'
});
}
// 组件卸载时清理
onUnmounted(() => {
// 如果需要,可以在这里断开连接
// mqttService.disconnect();
});
</script>
```
## 消息命令类型
```typescript
export const MqttCmd = {
Channel: 'channel', // 通道状态
NodeInit: 'nodeInit', // Node 初始化
Node: 'node', // Node 更新
User: 'user', // 用户登录
Control: 'control', // 控制命令
VideoInit: 'videoInit', // 视频初始化
VideoCtl: 'videoCtl', // 视频控制
Lock: 'lock', // 锁控
Alarm: 'alarm', // 告警
Info: 'info', // 通知提示
SocketDebugger: 'socketDebugger' // 调试
};
```
## API 文档
### mqttService.connect(config?)
连接 MQTT 服务器
**参数:**
- `config?: MqttConfig` - 连接配置
- `brokerUrl?: string` - MQTT 服务器地址
- `username?: string` - 用户名
- `password?: string` - 密码
- `clientId?: string` - 客户端 ID
- `reconnectPeriod?: number` - 重连间隔(毫秒)
### mqttService.disconnect()
断开 MQTT 连接
### mqttService.send(topic, cmd, value)
发送消息
**参数:**
- `topic: string` - MQTT 主题
- `cmd: string` - 命令类型
- `value: any` - 消息内容
### mqttService.subscribe(topic, callback?)
订阅主题
**参数:**
- `topic: string` - 主题
- `callback?: (message: MqttMessage) => void` - 消息回调
### mqttService.unsubscribe(topic)
取消订阅
**参数:**
- `topic: string` - 主题
### mqttService.registerService(name, service)
注册服务
**参数:**
- `name: string` - 服务名称
- `service: any` - 服务对象
### mqttService.getClientStatus()
获取客户端状态
**返回:** `{ connected: boolean; client: MqttClient | null }`
## 注意事项
1. **连接时机**: 建议在应用启动时main.ts连接 MQTT
2. **服务注册**: 在组件的 `onMounted` 生命周期注册服务
3. **清理资源**: 在组件卸载时根据需要断开连接或取消订阅
4. **错误处理**: 服务已内置重连机制,无需手动处理
5. **类型安全**: 所有方法都有 TypeScript 类型支持
## 迁移指南
从旧的 ExtJS 版本迁移:
### 旧代码
```javascript
Rec.mqtt.connect();
Rec.service.node = { ... };
Rec.mqtt.send(cmd, value);
```
### 新代码
```typescript
import { mqttService } from '@/utils/mqttService';
mqttService.connect();
mqttService.registerService('node', { ... });
mqttService.send('/app/web/write', cmd, value);
```
## 示例项目结构
```
src/
├── utils/
│ └── mqttService.ts # MQTT 服务
├── services/
│ ├── nodeService.ts # Node 服务
│ ├── channelService.ts # 通道服务
│ └── videoService.ts # 视频服务
└── views/
└── YourComponent.vue # 使用 MQTT 的组件
```
## 故障排除
### 连接失败
- 检查 MQTT 服务器地址是否正确
- 检查网络连接
- 查看浏览器控制台日志
### 消息未收到
- 确认服务已注册
- 检查主题订阅是否正确
- 查看消息格式是否符合预期
### 重连问题
- 检查 `reconnectPeriod` 设置
- 查看服务器是否在线
- 检查认证信息是否正确

@ -0,0 +1,309 @@
# STOMP 服务使用指南
## 简介
这是一个基于 `@stomp/stompjs` 库封装的 STOMP 服务,提供了完整的连接、订阅、发布和消息处理功能。
**注意:** 您的项目使用的是 **STOMP over WebSocket** 协议,而不是 MQTT。
## 主要功能
- ✅ 自动连接和重连
- ✅ 服务注册机制
- ✅ 消息自动分发
- ✅ TypeScript 类型支持
- ✅ 错误处理和日志记录
## 使用方法
### 1. 在 main.ts 中初始化
```typescript
import { stompService } from './utils/stompService';
// 应用启动时连接 STOMP
const app = createApp(App);
// 连接 STOMP 服务器
stompService.connect({
// brokerUrl: 'ws://your-server:8080/stomp-endpoint', // 可选,默认使用当前域名
// username: 'your-username', // 可选
// password: 'your-password', // 可选
reconnectDelay: 5000 // 重连间隔,默认 5000ms
});
app.mount('#app');
```
### 2. 注册服务
```typescript
// 在您的组件或 store 中
import { stompService } from '@/utils/stompService';
// 注册您的服务
stompService.registerService('node', {
// 当 STOMP 连接成功时会调用此方法
mqttReady() {
console.log('Node 服务已就绪');
},
// 处理 Node 初始化消息
msgNodeInit(data: any) {
console.log('收到 Node 初始化数据:', data);
},
// 处理 Node 更新消息
msgNode(data: any) {
console.log('收到 Node 更新:', data);
},
// 运行时的数据
runtimes: {}
});
// 注册其他服务
stompService.registerService('channel', {
online(cid: string, isOnline: boolean) {
console.log(`通道 ${cid} 状态:`, isOnline);
}
});
stompService.registerService('video', {
initVideo(data: any) {
console.log('初始化视频:', data);
},
playVideo(data: any) {
console.log('播放视频:', data);
}
});
```
### 3. 发送消息
```typescript
import { stompService, MqttCmd } from '@/utils/stompService';
// 发送控制命令
stompService.send('/app/web/write', MqttCmd.Control, {
nodeId: 'node-001',
action: 'turnOn'
});
// 发送 Node 更新
stompService.send('/app/web/write', MqttCmd.Node, {
node: {
id: 'node-001',
status: 'active'
}
});
```
### 4. 订阅主题
```typescript
import { stompService } from '@/utils/stompService';
// 订阅自定义主题
stompService.subscribe('/topic/custom', (message) => {
console.log('收到自定义消息:', message);
});
// 取消订阅
stompService.unsubscribe('/topic/custom');
```
### 5. 在 Vue 组件中使用
```vue
<template>
<div>
<el-button @click="sendCommand">发送命令</el-button>
<div>连接状态:{{ isConnected ? '已连接' : '未连接' }}</div>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { stompService, MqttCmd } from '@/utils/stompService';
const isConnected = ref(false);
// 注册服务
onMounted(() => {
stompService.registerService('myComponent', {
mqttReady() {
console.log('组件服务已就绪');
isConnected.value = true;
},
onConnectionLost() {
isConnected.value = false;
}
});
});
// 发送命令
function sendCommand() {
stompService.send('/app/web/write', MqttCmd.Control, {
command: 'test'
});
}
// 组件卸载时清理
onUnmounted(() => {
// 如果需要,可以在这里断开连接
// stompService.disconnect();
});
</script>
```
## 消息命令类型
```typescript
export const MqttCmd = {
Channel: 'channel', // 通道状态
NodeInit: 'nodeInit', // Node 初始化
Node: 'node', // Node 更新
User: 'user', // 用户登录
Control: 'control', // 控制命令
VideoInit: 'videoInit', // 视频初始化
VideoCtl: 'videoCtl', // 视频控制
Lock: 'lock', // 锁控
Alarm: 'alarm', // 告警
Info: 'info', // 通知提示
SocketDebugger: 'socketDebugger' // 调试
};
```
## API 文档
### stompService.connect(config?)
连接 STOMP 服务器
**参数:**
- `config?: StompServiceConfig` - 连接配置
- `brokerUrl?: string` - STOMP 服务器地址
- `username?: string` - 用户名
- `password?: string` - 密码
- `reconnectDelay?: number` - 重连间隔(毫秒)
### stompService.disconnect()
断开 STOMP 连接
### stompService.send(destination, cmd, value)
发送消息
**参数:**
- `destination: string` - STOMP 目标地址
- `cmd: string` - 命令类型
- `value: any` - 消息内容
### stompService.subscribe(destination, callback?)
订阅主题
**参数:**
- `destination: string` - 目标地址
- `callback?: (message: StompMessage) => void` - 消息回调
### stompService.unsubscribe(destination)
取消订阅
**参数:**
- `destination: string` - 目标地址
### stompService.registerService(name, service)
注册服务
**参数:**
- `name: string` - 服务名称
- `service: any` - 服务对象
### stompService.getClientStatus()
获取客户端状态
**返回:** `{ connected: boolean; client: Client | null }`
## 注意事项
1. **连接时机**: 建议在应用启动时main.ts连接 STOMP
2. **服务注册**: 在组件的 `onMounted` 生命周期注册服务
3. **清理资源**: 组件卸载时根据需要断开连接或取消订阅
4. **错误处理**: 服务已内置重连机制,无需手动处理
5. **类型安全**: 所有方法都有 TypeScript 类型支持
## 迁移指南
从旧的 ExtJS 版本迁移:
### 旧代码
```javascript
Rec.mqtt.connect();
Rec.service.node = { ... };
Rec.mqtt.send(cmd, value);
```
### 新代码
```typescript
import { stompService } from '@/utils/stompService';
stompService.connect();
stompService.registerService('node', { ... });
stompService.send('/app/web/write', cmd, value);
```
## 示例项目结构
```
src/
├── utils/
│ └── stompService.ts # STOMP 服务
├── services/
│ ├── nodeService.ts # Node 服务
│ ├── channelService.ts # 通道服务
│ └── videoService.ts # 视频服务
└── views/
└── YourComponent.vue # 使用 STOMP 的组件
```
## 故障排除
### 连接失败
- 检查 STOMP 服务器地址是否正确
- 检查 WebSocket 是否可用(某些浏览器需要 HTTPS
- 查看浏览器控制台日志
### 消息未收到
- 确认服务已注册
- 检查主题订阅是否正确
- 查看消息格式是否符合预期
### 重连问题
- 检查 `reconnectDelay` 设置
- 查看服务器是否在线
- 检查认证信息是否正确
## 与 MQTT 的区别
STOMP 是一个简单的文本协议,基于 WebSocket
- 使用 `destination` 而不是 `topic`
- 使用 `send/publish` 发送消息
- 使用 `subscribe` 订阅消息
- 连接地址格式:`ws://host/stomp-endpoint`

@ -76,7 +76,6 @@ if (typeof window !== 'undefined') {
// 获取所有节点 // 获取所有节点
async function getData() { async function getData() {
try { try {
debugger;
const response = await modelApi.node_nrt_get(); const response = await modelApi.node_nrt_get();
if (!response.ok) { if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
@ -111,31 +110,10 @@ if (typeof window !== 'undefined') {
} }
// 监听页面刷新/加载事件 // 监听页面刷新/加载事件
// window.addEventListener('load', () => { window.addEventListener('load', () => {
// getData(); getData();
// }); });
// 也可以直接挂载到全局对象 // 也可以直接挂载到全局对象
// (window as any).myGlobalFunction = myGlobalFunction; // (window as any).myGlobalFunction = myGlobalFunction;
// (window as any).anotherGlobalFunction = anotherGlobalFunction; (window as any).anotherGlobalFunction = anotherGlobalFunction;
// 接收父页面消息
window.addEventListener('message', function (event) {
if (event.data.type === 'SET_GLOBAL_DATA') {
// 接收父页面发送的数据
window.globalData = event.data.payload;
console.log('接收到父页面数据:', window.globalData);
}
});
// 通知父页面 Vue 已准备就绪
window.addEventListener('load', () => {
if (window.parent !== window) {
window.parent.postMessage(
{
type: 'VUE_APP_READY'
},
'*'
);
}
});

@ -0,0 +1,455 @@
import { Client, Message, IFrame } from '@stomp/stompjs';
import type { StompConfig } from '@stomp/stompjs';
// 定义消息命令常量
export const MqttCmd = {
Channel: 1, // 通道消息
NodeInit: 2, // 节点初始化
Node: 3, // 节点消息
VideoInit: 4, // 视频初始化
VideoCtl: 5, // PTZ 控制
VideoPlay: 6, // 视频播放
Lock: 7, // 锁控消息
Control: 10, // 控制消息反馈
Alarm: 20, // 告警消息
Info: 100, // 通知
Error: 101, // 错误
DirVideo: 201, // 视频消息
DirLock: 202, // 锁控消息
DirLockWeb: 203, // 锁控 WEB 消息
User: 1000, // 用户登陆
SocketDebugger: 3001 // SocketDebugger 消息
};
// 定义消息接口
export interface StompMessage {
cmd: number;
value: any;
}
// 定义服务注册表
export interface ServiceRegistry {
[key: string]: any;
}
// STOMP 配置接口
export interface StompServiceConfig {
brokerUrl?: string;
username?: string;
password?: string;
reconnectDelay?: number;
}
class StompService {
private client: Client | null = null;
private isConnected: boolean = false;
private services: ServiceRegistry = {};
private config: StompServiceConfig = {};
/**
*
* @param name
* @param service
*/
public registerService(name: string, service: any): void {
this.services[name] = service;
console.log(`[STOMP] 服务已注册:${name}`);
}
/**
* STOMP
* @param config
*/
public connect(config: StompServiceConfig = {}): void {
if (this.client) {
console.warn('[STOMP] 客户端已存在,跳过连接');
return;
}
// 保存配置
this.config = config;
// 构建连接地址
const protocol = location.protocol === 'https:' ? 'wss://' : 'ws://';
// ${location.host} localhost:5173
const host = 'localhost:8080';
const brokerUrl = config.brokerUrl || `${protocol}${host}/stomp-endpoint`;
// STOMP 配置
const stompConfig: StompConfig = {
brokerURL: brokerUrl,
connectHeaders: {
login: config.username || '',
passcode: config.password || ''
},
debug: (str: string) => {}, // 关闭调试日志
reconnectDelay: config.reconnectDelay || 5000, // 重连延迟(毫秒)
// 连接成功回调
onConnect: (frame: IFrame) => {
this.onConnected(frame);
},
// 连接失败回调
onStompError: (frame: IFrame) => {
console.error('[STOMP] 服务器错误:', frame);
},
// WebSocket 关闭回调
onWebSocketClose: () => {
this.onConnectionLost();
},
// WebSocket 错误回调
onWebSocketError: (evt: Event) => {
console.error('[STOMP] WebSocket 错误:', evt);
this.handleConnectionError(evt);
}
};
try {
// 创建 STOMP 客户端
this.client = new Client(stompConfig);
// 激活连接
this.client.activate();
console.log('[STOMP] 正在连接至:', brokerUrl);
} catch (error) {
console.error('[STOMP] 连接失败:', error);
this.handleConnectionError(error);
}
}
/**
*
*/
public disconnect(): void {
if (this.client) {
this.client.deactivate();
this.client = null;
this.isConnected = false;
console.log('[STOMP] 已断开连接');
}
}
/**
*
* @param destination
* @param cmd
* @param value
*/
public send(destination: string, cmd: number, value: any): void {
if (!this.client || !this.isConnected) {
console.error('[STOMP] 客户端未连接,无法发送消息');
return;
}
const message: StompMessage = {
cmd,
value
};
try {
this.client.publish({
destination,
body: JSON.stringify(message)
});
// console.log(`[STOMP] 发送消息:${destination}`, message);
} catch (error) {
console.error('[STOMP] 发送消息失败:', error);
}
}
/**
*
* @param destination
* @param callback
*/
public subscribe(destination: string, callback?: (message: StompMessage) => void): void {
if (!this.client || !this.isConnected) {
console.error('[STOMP] 客户端未连接,无法订阅');
return;
}
try {
this.client.subscribe(destination, (message: Message) => {
this.onMessageArrived(message);
});
console.log(`[STOMP] 订阅成功:${destination}`);
// 如果有自定义回调,添加到内部消息处理
if (callback) {
this.services[`_custom_${destination}`] = { callback };
}
} catch (error) {
console.error('[STOMP] 订阅失败:', error);
}
}
/**
*
* @param destination
*/
public unsubscribe(destination: string): void {
if (!this.client) {
return;
}
try {
// STOMP 客户端会自动处理取消订阅
delete this.services[`_custom_${destination}`];
console.log(`[STOMP] 取消订阅:${destination}`);
} catch (error) {
console.error('[STOMP] 取消订阅失败:', error);
}
}
/**
*
* @param frame STOMP
*/
private onConnected(frame: IFrame): void {
this.isConnected = true;
console.log('[STOMP] 连接成功!');
// 订阅默认主题
this.subscribe('/topic/web/read');
// 通知所有服务 STOMP 已就绪
for (const serviceName in this.services) {
if (this.services[serviceName].mqttReady || this.services[serviceName].stompReady) {
const callback =
this.services[serviceName].mqttReady || this.services[serviceName].stompReady;
callback.call(this.services[serviceName]);
}
}
}
/**
*
*/
private onConnectionLost(): void {
this.isConnected = false;
console.log('[STOMP] 连接丢失!');
// 通知所有服务
for (const serviceName in this.services) {
if (this.services[serviceName].onConnectionLost) {
this.services[serviceName].onConnectionLost();
}
}
}
/**
*
* @param message STOMP
*/
private onMessageArrived(message: Message): void {
try {
const msgStr = message.body || '';
const msgObject: StompMessage = JSON.parse(msgStr);
// console.log('[STOMP] 收到消息:', message.destination, msgObject);
// 根据命令类型分发到不同的服务
this.dispatchMessage(msgObject);
// 处理自定义回调
const customService = this.services[`_custom_${message.destination}`];
if (customService && customService.callback) {
customService.callback(msgObject);
}
} catch (error) {
console.error('[STOMP] 消息解析失败:', error);
}
}
/**
*
* @param msgObject
*/
private dispatchMessage(msgObject: StompMessage): void {
switch (msgObject.cmd) {
case MqttCmd.Channel:
// 通道消息
if (this.services.channel) {
this.services.channel.online(msgObject.value.cid, msgObject.value.online);
}
break;
case MqttCmd.NodeInit:
// Node 初始化消息
if (this.services.node) {
this.services.node.msgNodeInit(msgObject.value);
}
break;
case MqttCmd.Node:
debugger
// 单一 Node 更新消息
if (this.services.node) {
this.services.nod
console.log('[STOMP] 收到 Node 更新消息:', msgObject.value);
}
break;
case MqttCmd.User:
// 用户登录消息 - 需要验证
const uid = this.getCookie('uid');
if (msgObject.value.uid !== uid) return;
const clientId = this.getCookie('client-id');
if (msgObject.value.clientid === clientId) return;
// 退出登录
window.location.href = window.location.origin + '/logout';
break;
case MqttCmd.Control:
// 控制返回
this.showToast(msgObject.value);
break;
case MqttCmd.VideoInit:
// 视频初始化
if (this.services.video) {
this.services.video.initVideo(msgObject.value);
}
break;
case MqttCmd.VideoCtl:
// 视频 PTZ 控制
if (this.services.video) {
this.services.video.playVideo(msgObject.value);
}
break;
case MqttCmd.Lock:
// 锁控消息
if (this.services.lock) {
this.services.lock.onMsg(msgObject.value);
}
break;
case MqttCmd.Alarm:
// 告警消息
if (this.services.node) {
if (!this.services.node.runtimes) {
this.services.node.runtimes = {};
}
this.services.node.runtimes[msgObject.value.node.id] = msgObject.value;
}
if (this.services.banner) {
this.services.banner.alarm(msgObject.value);
}
break;
case MqttCmd.Info:
// 通知和提示
this.showToast(msgObject.value);
break;
case MqttCmd.SocketDebugger:
if (this.services.SocketDebugger) {
this.services.SocketDebugger.onMsg(msgObject.value);
}
break;
default:
console.warn('[STOMP] 未知命令类型:', msgObject.cmd);
}
}
/**
*
* @param error
*/
private handleConnectionError(error: any): void {
console.error('[STOMP] 连接错误:', error);
// STOMP 客户端会自动重连,这里只需要通知服务
for (const serviceName in this.services) {
if (this.services[serviceName].onConnectionError) {
this.services[serviceName].onConnectionError(error);
}
}
}
/**
* UUID
*/
private generateUUID(): string {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
const r = (Math.random() * 16) | 0;
const v = c === 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
/**
* Cookie
*/
private getCookie(cname: string): string {
const name = cname + '=';
const ca = document.cookie.split(';');
for (let i = 0; i < ca.length; i++) {
let c = ca[i].trim();
if (c.indexOf(name) === 0) {
return c.substring(name.length, c.length);
}
}
return '';
}
/**
* Cookie
*/
private setCookie(cname: string, cvalue: string, exdays: number): void {
const d = new Date();
d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000);
const expires = 'expires=' + d.toUTCString();
document.cookie = cname + '=' + cvalue + '; ' + expires;
}
/**
* Cookie
*/
private clearCookie(name: string): void {
this.setCookie(name, '', -1);
}
/**
*
*/
private showToast(message: any): void {
console.log('[STOMP Toast]:', message);
// 如果使用了 Element Plus
try {
import('element-plus').then(({ ElMessage }) => {
ElMessage.info(typeof message === 'string' ? message : JSON.stringify(message));
});
} catch (e) {
// 如果没有 Element Plus使用 alert
alert(typeof message === 'string' ? message : JSON.stringify(message));
}
}
/**
*
*/
public getClientStatus(): { connected: boolean; client: Client | null } {
return {
connected: this.isConnected,
client: this.client
};
}
}
// 导出单例
export const stompService = new StompService();
// 默认导出
export default stompService;
Loading…
Cancel
Save