diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index 9a2dc969..b813dba5 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -63,7 +63,8 @@
"Bash(done)",
"Bash(file /tmp/tile_*.png)",
"Bash(for REF in \"http://localhost:8080/\" \"https://dev.fastbee.cn/\" \"https://www.tianditu.gov.cn/\")",
- "Read(//tmp/**)"
+ "Read(//tmp/**)",
+ "Bash(node -e ' *)"
]
}
}
diff --git a/yack-app/apis/modules/mqttTest.js b/yack-app/apis/modules/mqttTest.js
new file mode 100644
index 00000000..65a64364
--- /dev/null
+++ b/yack-app/apis/modules/mqttTest.js
@@ -0,0 +1,42 @@
+const http = uni.$u.http;
+
+// 指令编码
+export function encode(params) {
+ return http.get('/iot/message/encode', {
+ params: params
+ });
+}
+
+// 指令解码
+export function decode(params) {
+ return http.get('/iot/message/decode', {
+ params: params
+ });
+}
+
+// 平台下发指令
+export function messagePost(data) {
+ return http.post('/iot/message/post', data);
+}
+
+// 查询设备指令偏好设置列表
+export function preferencesList(params) {
+ return http.get('/iot/preferences/list', {
+ params: params
+ });
+}
+
+// 新增设备指令偏好设置
+export function addPreferences(data) {
+ return http.post('/iot/preferences', data);
+}
+
+// 修改设备指令偏好设置
+export function editPreferences(data) {
+ return http.put('/iot/preferences', data);
+}
+
+// 删除设备指令偏好设置
+export function delPreferences(data) {
+ return http.delete('/iot/preferences/' + data.id);
+}
diff --git a/yack-app/env.config.js b/yack-app/env.config.js
index c6f8a021..1cfea6fa 100644
--- a/yack-app/env.config.js
+++ b/yack-app/env.config.js
@@ -1,5 +1,5 @@
// H5端开发和生产环境协议
-let protocalDev = "wss://";
+let protocalDev = "ws://";
let protocalProd = "wss://";
// 条件编译,微信端和移动端使用wxs协议
@@ -19,7 +19,7 @@ const CONFIG = {
development: {
officialWebUrl: 'http://localhost:8080/',
baseUrl: 'http://localhost:8080/',
- mqttServer: protocalDev + 'localhost/mqtt',
+ mqttServer: protocalDev + '47.121.180.229:8083/mqtt',
decoderUrl: 'http://localhost:8080/',
qqmapKey,
xinzhiKey,
diff --git a/yack-app/locale/en-US.json b/yack-app/locale/en-US.json
index a476c7a2..c75992af 100644
--- a/yack-app/locale/en-US.json
+++ b/yack-app/locale/en-US.json
@@ -49,6 +49,8 @@
"navBar.updateDevice": "Device Update",
"navBar.eventLog": "Event Log",
"navBar.orderLog": "Instruction log",
+ "navBar.dataDebug": "Data Debug",
+ "navBar.commandBuilder": "Command Builder",
"navBar.player": "Player",
"common.add": "add",
"common.delete": "Delete",
@@ -240,6 +242,72 @@
"createBtn": "Create Device",
"content": "This feature requires login to use. Do you want to log in?"
},
+ "debug": {
+ "title": "Data Debug",
+ "send": "Send",
+ "reveiceType": "Reply",
+ "reportType": "Report",
+ "sendType": "Send",
+ "hex": "HEX",
+ "json": "JSON",
+ "ascii": "ASCII",
+ "inputPlaceholder": "Please enter the content to send",
+ "typePlaceholder": "Select format",
+ "analysis": "Parse",
+ "analysised": "Parsed",
+ "name": "Name",
+ "identifier": "Identifier",
+ "value": "Value",
+ "more": "More",
+ "quickCommand": "Quick Commands",
+ "addCommand": "Add Command",
+ "editCommand": "Edit Command",
+ "slaveAddress": "Slave Address",
+ "issueType": "Command Type",
+ "functionCode": "Function Code",
+ "startAddress": "Start Register Address",
+ "registerCount": "Register Count",
+ "coilCount": "Coil Count",
+ "writeCoilValue": "Coil Value",
+ "writeRegisterValue": "Register Value",
+ "registerValue": "Register Value",
+ "reportValue": "Report Value",
+ "coilValue": "Coil Value",
+ "generateCommand": "Generate",
+ "generatedCommand": "Generated Command",
+ "saveAsCommand": "Save As Command",
+ "copy": "Copy",
+ "cancel": "Cancel",
+ "confirm": "Confirm",
+ "commandName": "Command Name",
+ "inputCommandName": "Please enter command name",
+ "commandContent": "Command Content",
+ "commandScope": "Command Scope",
+ "productCommand": "Product Command (shared by same product)",
+ "deviceCommand": "Device Command (this device only)",
+ "deleteCommand": "Delete Command",
+ "deleteConfirm": "Are you sure to delete this command?",
+ "reportCommand": "Device Report",
+ "issueCommand": "Platform Issue",
+ "code01": "01 Read Coils",
+ "code02": "02 Read Discrete Inputs",
+ "code03": "03 Read Holding Registers",
+ "code04": "04 Read Input Registers",
+ "code05": "05 Write Single Coil",
+ "code06": "06 Write Single Register",
+ "code15": "15 Write Multiple Coils",
+ "code16": "16 Write Multiple Registers",
+ "sendEmpty": "Please enter the content to send",
+ "sendFail": "Send failed",
+ "analysisEmpty": "No data to parse",
+ "analysisFail": "Parse failed",
+ "generateFail": "Generate failed",
+ "saveSuccess": "Saved successfully",
+ "saveFail": "Save failed",
+ "deleteSuccess": "Deleted successfully",
+ "deleteFail": "Delete failed",
+ "copySuccess": "Copied successfully"
+ },
"about": {
"about": "about",
"authorQQ": "Author QQ : 164770707",
diff --git a/yack-app/locale/zh-CN.json b/yack-app/locale/zh-CN.json
index 9bcf2f3c..0cf9d83e 100644
--- a/yack-app/locale/zh-CN.json
+++ b/yack-app/locale/zh-CN.json
@@ -46,6 +46,8 @@
"navBar.updateDevice": "设备修改",
"navBar.eventLog": "事件日志",
"navBar.orderLog": "指令日志",
+ "navBar.dataDebug": "数据调试",
+ "navBar.commandBuilder": "指令生成",
"navBar.player": "播放器",
"common.fastbee": "岩安测控",
"common.add": "添加",
@@ -241,6 +243,72 @@
"createBtn": "创建设备",
"content": "该功能需要登录才可使用,是否去登录?"
},
+ "debug": {
+ "title": "数据调试",
+ "send": "发送",
+ "reveiceType": "回复",
+ "reportType": "上报",
+ "sendType": "下发",
+ "hex": "HEX",
+ "json": "JSON",
+ "ascii": "ASCII",
+ "inputPlaceholder": "请输入要发送的内容",
+ "typePlaceholder": "选择格式",
+ "analysis": "解析",
+ "analysised": "已解析",
+ "name": "参数名",
+ "identifier": "标识符",
+ "value": "参数值",
+ "more": "更多",
+ "quickCommand": "快捷指令",
+ "addCommand": "新建指令",
+ "editCommand": "编辑指令",
+ "slaveAddress": "从机地址",
+ "issueType": "指令类型",
+ "functionCode": "功能码",
+ "startAddress": "起始寄存器地址",
+ "registerCount": "寄存器个数",
+ "coilCount": "线圈个数",
+ "writeCoilValue": "线圈值",
+ "writeRegisterValue": "寄存器值",
+ "registerValue": "寄存器值",
+ "reportValue": "上报值",
+ "coilValue": "线圈值",
+ "generateCommand": "生成指令",
+ "generatedCommand": "生成的指令",
+ "saveAsCommand": "存为指令",
+ "copy": "复制",
+ "cancel": "取消",
+ "confirm": "确定",
+ "commandName": "指令名称",
+ "inputCommandName": "请输入指令名称",
+ "commandContent": "指令内容",
+ "commandScope": "指令范围",
+ "productCommand": "产品指令(同类产品通用)",
+ "deviceCommand": "设备指令(仅本设备)",
+ "deleteCommand": "删除指令",
+ "deleteConfirm": "确认删除该指令吗?",
+ "reportCommand": "设备上报",
+ "issueCommand": "平台下发",
+ "code01": "01 读线圈",
+ "code02": "02 读离散输入",
+ "code03": "03 读保持寄存器",
+ "code04": "04 读输入寄存器",
+ "code05": "05 写单个线圈",
+ "code06": "06 写单个寄存器",
+ "code15": "15 写多个线圈",
+ "code16": "16 写多个寄存器",
+ "sendEmpty": "请输入要发送的内容",
+ "sendFail": "发送失败",
+ "analysisEmpty": "暂无可解析的数据",
+ "analysisFail": "解析失败",
+ "generateFail": "生成失败",
+ "saveSuccess": "保存成功",
+ "saveFail": "保存失败",
+ "deleteSuccess": "删除成功",
+ "deleteFail": "删除失败",
+ "copySuccess": "复制成功"
+ },
"about": {
"about": "关于",
"authorQQ": "作者企鹅:164770707",
diff --git a/yack-app/pages.json b/yack-app/pages.json
index 9cdedb1f..c4c217e0 100644
--- a/yack-app/pages.json
+++ b/yack-app/pages.json
@@ -533,6 +533,26 @@
"enablePullDownRefresh": true
}
},
+ {
+ "path": "home/device/debug/index",
+ "meta": {
+ "title": "%navBar.dataDebug%"
+ },
+ "style": {
+ "navigationBarTitleText": "%navBar.dataDebug%",
+ "enablePullDownRefresh": false
+ }
+ },
+ {
+ "path": "home/device/debug/edit",
+ "meta": {
+ "title": "%navBar.commandBuilder%"
+ },
+ "style": {
+ "navigationBarTitleText": "%navBar.commandBuilder%",
+ "enablePullDownRefresh": false
+ }
+ },
{
"path": "login/register",
"style": {
diff --git a/yack-app/pages/tabBar/home/index.vue b/yack-app/pages/tabBar/home/index.vue
index ddc65e4f..8f17acea 100644
--- a/yack-app/pages/tabBar/home/index.vue
+++ b/yack-app/pages/tabBar/home/index.vue
@@ -242,7 +242,8 @@
pageSize: 10,
groupId: 0,
deviceName: null,
- showChild: true
+ showChild: true,
+ entrance: 2
},
loadStatus: 'loadmore', // 加载更多
deviceList: [], // 设备列表
diff --git a/yack-app/pagesA/home/device/index.vue b/yack-app/pagesA/home/device/index.vue
index 95d4ef01..334b0a60 100644
--- a/yack-app/pagesA/home/device/index.vue
+++ b/yack-app/pagesA/home/device/index.vue
@@ -331,12 +331,17 @@
this.deviceForm.rssid = message.rssid;
}
};
- //不是modbus不转发到子页面,其他设备的页面有回调方法
- if (this.isSubDev) {
- /*发送设备上报到子模块*/
- if (topic.endsWith('ws/service')) {
- console.log('接收到【设备数据上报】主题:', topic)
- console.log('接收到【设备数据上报】主题:', message)
+ /*发送设备上报到数据调试页面*/
+ if (topic.endsWith('ws/service')) {
+ console.log('接收到【设备数据上报】主题:', topic)
+ console.log('接收到【设备数据上报】主题:', message)
+ this.$bus.$emit("updateData", {
+ serialNumber: topics[2],
+ productId: this.deviceForm.productId,
+ data: message,
+ })
+ //不是modbus不转发到子页面,其他设备的页面有回调方法
+ if (this.isSubDev) {
this.$bus.$emit("updateDeviceStatus", {
serialNumber: topics[2],
productId: this.deviceForm.productId,
diff --git a/yack-app/pagesA/home/device/log/index.vue b/yack-app/pagesA/home/device/log/index.vue
index 859b80a5..5d0f9c63 100644
--- a/yack-app/pagesA/home/device/log/index.vue
+++ b/yack-app/pagesA/home/device/log/index.vue
@@ -6,6 +6,8 @@
@click="handleEventLogClick">
+
@@ -58,6 +60,17 @@
uni.navigateTo({
url: `/pagesB/home/device/log/order?deviceId=${this.deviceId}&serialNumber=${this.serialNumber}`
});
+ },
+ handleDebugClick () {
+ const device = {
+ deviceId: this.deviceInfo.deviceId,
+ serialNumber: this.deviceInfo.serialNumber,
+ productId: this.deviceInfo.productId,
+ protocolCode: this.deviceInfo.protocolCode,
+ };
+ uni.navigateTo({
+ url: `/pagesB/home/device/debug/index?device=${encodeURIComponent(JSON.stringify(device))}`
+ });
}
}
};
diff --git a/yack-app/pagesB/home/device/debug/edit.vue b/yack-app/pagesB/home/device/debug/edit.vue
new file mode 100644
index 00000000..0861df5f
--- /dev/null
+++ b/yack-app/pagesB/home/device/debug/edit.vue
@@ -0,0 +1,615 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{currentIssueTypeLabel}}
+
+
+
+
+ {{currentFunctionCodeLabel}}
+
+
+
+
+
+
+ 0x{{form.startAddressHex}}
+
+
+
+
+
+
+
+
+
+
+
+
+ 0x{{form.writeValueHex}}
+
+
+
+
+
+
+ {{$tt('debug.registerValue')}}
+
+
+ #{{index}}
+
+ 0x{{item.valueHex}}
+
+
+
+
+
+
+ {{$tt('debug.coilValue')}}
+
+
+ #{{index}}
+
+
+
+
+
+
+
+ {{$tt('debug.reportValue')}}
+
+
+ #{{index}}
+
+ 0x{{item.valueHex}}
+
+
+
+
+
+
+
+
+
+ {{generatedCommand}}
+
+
+ 点击下方按钮生成指令
+
+
+
+
+
+ {{$tt('debug.cancel')}}
+ {{$tt('debug.generateCommand')}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yack-app/pagesB/home/device/debug/index.vue b/yack-app/pagesB/home/device/debug/index.vue
new file mode 100644
index 00000000..321ab823
--- /dev/null
+++ b/yack-app/pagesB/home/device/debug/index.vue
@@ -0,0 +1,655 @@
+
+
+
+
+ {{$tt('home.serialNumber')}}:
+ {{device.serialNumber}}
+
+
+
+
+
+
+
+ {{item.type === 0 ? $tt('debug.sendType') : item.type === 1 ? $tt('debug.reveiceType') : $tt('debug.reportType')}}
+
+ {{item.time}}
+
+ {{$tt('debug.analysis')}}
+
+
+ {{$tt('debug.analysised')}}
+
+
+
+
+
+ JSON:
+ {{formatJsonValue(item.value, item.valueAscii)}}
+
+
+
+
+ HEX:
+ {{item.value}}
+
+
+ ASCII:
+ {{item.valueAscii}}
+
+
+
+
+
+
+ {{$tt('debug.name')}}:
+ {{item.analysisVal.name || '--'}}
+
+
+ {{$tt('debug.identifier')}}:
+ {{item.analysisVal.id || '--'}}
+
+
+ {{$tt('debug.value')}}:
+ {{item.analysisVal.value || '--'}}
+
+
+ {{$tt('debug.more')}}
+
+
+
+
+
+
+
+
+
+ {{$tt('debug.quickCommand')}}
+ {{$tt('debug.addCommand')}}
+
+
+
+
+ {{item.name}}
+ {{item.command}}
+
+
+
+
+
+
+
+
+
+
+ {{currentDataTypeLabel}}
+
+
+
+ {{$tt('debug.send')}}
+
+
+
+
+
+
+
+ showDeleteModal = false" :title="$tt('debug.deleteCommand')" :content="deleteContent"
+ @confirm="handleConfirmDelete">
+
+
+
+ showMorePopup = false">
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yack-app/utils/common.js b/yack-app/utils/common.js
index f6a27579..88f53543 100644
--- a/yack-app/utils/common.js
+++ b/yack-app/utils/common.js
@@ -26,7 +26,7 @@ export function tansParams (params) {
/**
* 参数处理,避免小程序跳转10层后无法跳转
- * @param url
+ * @param url
*/
export function navigateBackTo (url) {
const backUrl = url.slice(1);
@@ -35,4 +35,130 @@ export function navigateBackTo (url) {
const index = pages.findIndex(item => item.route === backUrl);
const backnum = num - index - 1;
uni.navigateBack({ delta: backnum });
+}
+
+/**
+ * 深复制
+ * @param {any} obj 需要复制的对象
+ */
+export function deepClone (obj) {
+ if (typeof obj == 'object') {
+ const objClone = Array.isArray(obj) ? [] : {}
+ for (const key in obj) {
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
+ if (obj[key] && typeof obj[key] == 'object') {
+ objClone[key] = deepClone(obj[key])
+ } else {
+ objClone[key] = obj[key]
+ }
+ }
+ }
+ return objClone
+ }
+ return obj
+}
+
+/**
+ * @desc 时间戳转日期毫秒
+ * @param {Date} format 时间格式 Y:年,M:月,D:天 h:小时,m:分钟,s:秒 默认:'Y/M/D h:m'
+ */
+export function formatDate (arg, format) {
+ let now
+ if (!arg) {
+ return null
+ } else {
+ now = new Date(arg)
+ }
+ format = format || 'Y.M.D h:m'
+
+ const year = now.getFullYear()
+ let month = now.getMonth() + 1
+ month = month >= 10 ? month : '0' + month
+ let date = now.getDate()
+ date = date >= 10 ? date : '0' + date
+ let hour = now.getHours()
+ hour = hour >= 10 ? hour : '0' + hour
+ let minute = now.getMinutes()
+ minute = minute >= 10 ? minute : '0' + minute
+ let second = now.getSeconds()
+ second = second >= 10 ? second : '0' + second
+ return format
+ .replace('Y', year)
+ .replace('M', month)
+ .replace('D', date)
+ .replace('h', hour)
+ .replace('m', minute)
+ .replace('s', second)
+}
+
+/**
+ * 复制文本(uni-app 剪贴板)
+ * @param {string} copytext
+ */
+export function copyText (copytext) {
+ uni.setClipboardData({
+ data: copytext,
+ success: () => {},
+ })
+ return { type: 'success', message: '复制成功' }
+}
+
+/**
+ * hex转10进制
+ * @param hexStr
+ */
+export function hex2int (hexStr) {
+ let twoStr = parseInt(hexStr, 16).toString(2) // 将十六转十进制,再转2进制
+ let bitNum = hexStr.length * 4 // 1个字节 = 8bit ,0xff 一个 "f"就是4位
+ if (twoStr.length < bitNum) {
+ while (twoStr.length < bitNum) {
+ twoStr = "0" + twoStr
+ }
+ }
+ if (twoStr.substring(0, 1) == "0") {
+ // 正数
+ const two = parseInt(twoStr, 2) // 二进制转十进制
+ return two
+ } else {
+ // 负数
+ let twoStr_unsign = ""
+ let two = parseInt(twoStr, 2) - 1 // 补码:(负数)反码+1,符号位不变;相对十进制来说也是 +1,但这里是负数,+1就是绝对值数据-1
+ twoStr = two.toString(2)
+ twoStr_unsign = twoStr.substring(1, bitNum) // 舍弃首位(符号位)
+ // 去除首字符,将0转为1,将1转为0 反码
+ twoStr_unsign = twoStr_unsign.replace(/0/g, "z")
+ twoStr_unsign = twoStr_unsign.replace(/1/g, "0")
+ twoStr_unsign = twoStr_unsign.replace(/z/g, "1")
+ two = -parseInt(twoStr_unsign, 2)
+ return two
+ }
+}
+
+/**
+ * 10进制转16进制
+ * @param hexStr
+ */
+export function int2hex (hexStr) {
+ let hexInt = typeof hexStr == 'string' ? parseInt(hexStr, 10) : hexStr
+ let hex = ''
+ const LENGTH_04 = 4
+ const LENGTH_15 = 15
+ if (hexInt >= 0) {
+ hex = hexInt.toString(16).toLocaleLowerCase()
+ } else {
+ let twoStr = (-hexInt - 1).toString(2)
+ let sb = '000000000000000'
+ twoStr = sb.slice(0, LENGTH_15 - twoStr.length) + twoStr
+ // 将0转为1,将1转为0 反码
+ twoStr = twoStr.replace(/0/g, "z")
+ twoStr = twoStr.replace(/1/g, "0")
+ twoStr = twoStr.replace(/z/g, "1")
+ hex = parseInt(('1' + twoStr), 2).toString(16).toLocaleLowerCase()
+ }
+ // 长度不等于4 补全
+ if (hex.length != LENGTH_04) {
+ let sb = "0000"
+ hex = sb.slice(0, LENGTH_04 - hex.length) + hex
+ }
+ return hex.toUpperCase()
}
\ No newline at end of file