From e7e1a9d5777924d5ad1e541472377e93089f141b Mon Sep 17 00:00:00 2001 From: George Date: Sun, 18 Jan 2026 21:14:47 +0800 Subject: [PATCH] =?UTF-8?q?20260118=EF=BC=9Abug=E5=85=AC=E5=BC=8F=E8=AE=A1?= =?UTF-8?q?=E7=AE=97=E8=B4=9F=E6=95=B0+=E5=80=BC=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fastbee/common/utils/CaculateUtils.java | 141 +++++++++--------- .../java/com/fastbee/server/TCPServer.java | 18 ++- .../iot/service/impl/DeviceServiceImpl.java | 8 +- .../service/impl/ThingsModelServiceImpl.java | 1 + .../views/iot/device/device-things-model.vue | 50 ++++++- vue/src/views/iot/device/index.vue | 33 +++- vue/src/views/login.vue | 6 +- 7 files changed, 170 insertions(+), 87 deletions(-) diff --git a/fastbee-common/src/main/java/com/fastbee/common/utils/CaculateUtils.java b/fastbee-common/src/main/java/com/fastbee/common/utils/CaculateUtils.java index 3612814f..305d7e48 100644 --- a/fastbee-common/src/main/java/com/fastbee/common/utils/CaculateUtils.java +++ b/fastbee-common/src/main/java/com/fastbee/common/utils/CaculateUtils.java @@ -23,14 +23,13 @@ public class CaculateUtils { */ private static final String symbol = "+-,*/,(),%^"; - /** * 公式计算 字符串 * * @param exeStr */ public static BigDecimal execute(String exeStr, Map replaceMap) { - //替换掉占位符 + // 替换掉占位符 exeStr = caculateReplace(exeStr, replaceMap); exeStr = exeStr.replaceAll("\\s*", ""); List suffixList = suffixHandle(exeStr); @@ -48,8 +47,8 @@ public class CaculateUtils { BigDecimal a = BigDecimal.ZERO; BigDecimal b = BigDecimal.ZERO; - // 构建一个操作数栈 每当获得操作符号时取出最上面两个数进行计算。 - Stack caculateStack = new Stack(); + // 构建一个操作数栈 每当获得操作符号时取出最上面两个数进行计算。 + Deque caculateStack = new ArrayDeque<>(); if (suffixList.size() > 1) { for (int i = 0; i < suffixList.size(); i++) { @@ -78,9 +77,8 @@ public class CaculateUtils { return a; } - /** - * 计算 使用double 进行计算 如果需要可以在这里使用bigdecimal 进行计算 + * 计算 使用double 进行计算 如果需要可以在这里使用bigdecimal 进行计算 * * @param a * @param b @@ -104,7 +102,7 @@ public class CaculateUtils { b = new BigDecimal(b + ".0"); } int length = b.toString().split("\\.")[1].length(); - return a.divide(b, length, BigDecimal.ROUND_HALF_UP); + return a.divide(b, length, BigDecimal.ROUND_HALF_UP); case '^': // 幂运算:a的b次方 double result = Math.pow(a.doubleValue(), b.doubleValue()); @@ -120,11 +118,18 @@ public class CaculateUtils { */ public static List suffixHandle(String exeStr) { StringBuilder buf = new StringBuilder(); - //Stack stack = new Stack(); + // Stack stack = new Stack(); Deque stack = new ArrayDeque<>(); char[] exeChars = exeStr.toCharArray(); List res = new ArrayList<>(); - for (char x : exeChars) { + for (int i = 0; i < exeChars.length; i++) { + char x = exeChars[i]; + char prev = i > 0 ? exeChars[i - 1] : '\0'; + // 处理一元正负号:位于开头或运算符/左括号之后 + if ((x == '-' || x == '+') && (i == 0 || (symbol.indexOf(prev) > -1 && prev != ')'))) { + buf.append(x); + continue; + } // 判断是不是操作符号 if (symbol.indexOf(x) > -1) { // 不管怎样先将数据添加进列表 @@ -132,27 +137,27 @@ public class CaculateUtils { // 添加数据到res String temp = buf.toString(); // 验证是否为数 - if (!isNumber(temp)) throw new RuntimeException(buf.append(" 格式不对").toString()); + if (!isNumber(temp)) + throw new RuntimeException(buf.append(" 格式不对").toString()); // 添加到结果列表中 res.add(temp); // 清空临时buf buf.delete(0, buf.length()); } - if (stack.size() > 0) { + if (!stack.isEmpty()) { - //2.判断是不是开是括号 + // 2.判断是不是开是括号 if (x == '(') { stack.push(x); continue; } - //3.判断是不是闭合括号 + // 3.判断是不是闭合括号 if (x == ')') { - while (stack.size() > 0) { - char con = (char) stack.peek(); + while (!stack.isEmpty()) { + char con = stack.peek(); if (con == '(') { stack.pop(); - continue; } else { res.add(String.valueOf(stack.pop())); } @@ -160,7 +165,7 @@ public class CaculateUtils { continue; } // 取出最后最近的一个操作符 - char last = (char) stack.peek(); + char last = stack.peek(); if (compare(x, last) > 0) { stack.push(x); } else if (compare(x, last) <= 0) { @@ -176,15 +181,15 @@ public class CaculateUtils { buf.append(x); } } - if (buf.length() > 0) res.add(buf.toString()); - while (stack.size() > 0) { + if (buf.length() > 0) + res.add(buf.toString()); + while (!stack.isEmpty()) { res.add(String.valueOf(stack.pop())); } return res; } - /** * 比较两个操作符号的优先级 * @@ -202,7 +207,6 @@ public class CaculateUtils { } } - /** * 判断是否为数 字符串 * @@ -210,7 +214,7 @@ public class CaculateUtils { * @return */ public static boolean isNumber(String str) { - Pattern pattern = Pattern.compile("[0-9]+\\.{0,1}[0-9]*"); + Pattern pattern = Pattern.compile("[-+]?[0-9]+\\.{0,1}[0-9]*"); Matcher isNum = pattern.matcher(str); return isNum.matches(); } @@ -221,9 +225,9 @@ public class CaculateUtils { String key = entry.getKey(); String value = entry.getValue() == null ? "1" : entry.getValue(); // 如果占位符包含正则表达式特殊字符,使用 Pattern.quote() 转义 - if (key.contains("{") || key.contains("}") || key.contains("(") || key.contains(")") - || key.contains("[") || key.contains("]") || key.contains("*") || key.contains("+") - || key.contains("?") || key.contains("^") || key.contains("$") || key.contains("|")) { + if (key.contains("{") || key.contains("}") || key.contains("(") || key.contains(")") + || key.contains("[") || key.contains("]") || key.contains("*") || key.contains("+") + || key.contains("?") || key.contains("^") || key.contains("$") || key.contains("|")) { // 转义正则表达式特殊字符 String escapedKey = Pattern.quote(key); str = str.replaceAll(escapedKey, value); @@ -239,47 +243,49 @@ public class CaculateUtils { DataInputStream mDataInputStream = new DataInputStream(mByteArrayInputStream); try { float v = mDataInputStream.readFloat(); - return String.format("%.6f",v); - }catch (Exception e){ + return String.format("%.6f", v); + } catch (Exception e) { throw new ServiceException("modbus16转浮点数错误"); - } - finally { + } finally { mDataInputStream.close(); mByteArrayInputStream.close(); } } - public static String handleToUnSign16(String value,String dataType){ + public static String handleToUnSign16(String value, String dataType) { long l = Long.parseLong(value); - if (dataType.equals(ModbusDataType.U_SHORT.getType())){ - return toUnSign16(l); - }else { + if (dataType.equals(ModbusDataType.U_SHORT.getType())) { + return toUnSign16(l); + } else { return value; } } /** * 转16位无符号整形 + * * @param value * @return */ public static String toUnSign16(long value) { long unSigned = value & 0xFFFF; - return unSigned +""; // 将字节数组转换为十六进制字符串 + return unSigned + ""; // 将字节数组转换为十六进制字符串 } /** * 32位有符号CDAB数据类型 + * * @param value * @return */ public static String toSign32_CDAB(long value) { byte[] bytes = intToBytes2((int) value); - return bytesToInt2(bytes)+""; + return bytesToInt2(bytes) + ""; } /** * 32位无符号ABCD数据类型 + * * @param value * @return */ @@ -289,6 +295,7 @@ public class CaculateUtils { /** * 32位无符号CDAB数据类型 + * * @param value * @return */ @@ -300,6 +307,7 @@ public class CaculateUtils { /** * 转32位浮点数 ABCD + * * @param bytes * @return */ @@ -310,11 +318,13 @@ public class CaculateUtils { /** * 转32位浮点数 CDAB + * * @param bytes * @return */ public static Float toFloat32_DCBA(byte[] bytes) { - int intValue = ((bytes[3] & 0xFF) << 24) | ((bytes[2] & 0xFF) << 16) | ((bytes[1] & 0xFF) << 8) | ((bytes[0] & 0xFF)) ; + int intValue = ((bytes[3] & 0xFF) << 24) | ((bytes[2] & 0xFF) << 16) | ((bytes[1] & 0xFF) << 8) + | ((bytes[0] & 0xFF)); return Float.intBitsToFloat(intValue); } @@ -359,7 +369,6 @@ public class CaculateUtils { return value; } - /** * byte数组中取int数值,本方法适用于(低位在后,高位在前)的顺序。和intToBytes2()配套使用 */ @@ -368,7 +377,7 @@ public class CaculateUtils { } /** - * 将int数值转换为占四个字节的byte数组,本方法适用于(高位在前,低位在后)的顺序。 和bytesToInt2()配套使用 + * 将int数值转换为占四个字节的byte数组,本方法适用于(高位在前,低位在后)的顺序。 和bytesToInt2()配套使用 */ public static byte[] intToBytes2(int value) { byte[] src = new byte[4]; @@ -379,58 +388,56 @@ public class CaculateUtils { return src; } - public static String subHexValue(String hexString){ - //截取报文中的值 + public static String subHexValue(String hexString) { + // 截取报文中的值 String substring = hexString.substring(4, 6); int index = Integer.parseInt(substring); - return hexString.substring(6, 6 + index*2); + return hexString.substring(6, 6 + index * 2); } - public static void main(String[] args) throws IOException { Map replaceMap = new HashMap<>(); - replaceMap.put("A", "1.5"); - replaceMap.put("B", "2.5"); - replaceMap.put("C", "3.5"); - replaceMap.put("D", "4.5"); - replaceMap.put("E", "10"); -// replaceMap.put("%s", "1"); -// replaceMap.put("%s1", "5"); - BigDecimal execute = execute("A + B * (C - D) % E", replaceMap); +// replaceMap.put("A", "1.5"); +// replaceMap.put("B", "2.5"); +// replaceMap.put("C", "3.5"); +// replaceMap.put("D", "4.5"); + replaceMap.put("AngZ", "-10"); + // replaceMap.put("%s", "1"); + // replaceMap.put("%s1", "5"); + BigDecimal execute = execute("AngZ+88.59", replaceMap); System.out.println(execute); - - Map map = new HashMap<>(); - map.put("%s", "10"); - String caculate = caculateReplace("%s*2", map); - System.out.println(caculate); - System.out.println(execute("%s%3.00",map)); + Map map = new HashMap<>(); + map.put("%s", "10"); + String caculate = caculateReplace("%s*2", map); + System.out.println(caculate); + System.out.println(execute("%s%3.00", map)); String s4 = toUnSign16(-1); - System.out.println("转16位无符号:"+s4); + System.out.println("转16位无符号:" + s4); String s1 = toSign32_CDAB(40100); - System.out.println("转32位有符号-CDAB序"+s1); + System.out.println("转32位有符号-CDAB序" + s1); String s2 = toUnSign32_ABCD(-10); - System.out.println("转32位无符号-ABCD序:"+s2); + System.out.println("转32位无符号-ABCD序:" + s2); String s3 = toUnSign32_CDAB(123456789); - System.out.println("转32位无符号-CDAB序:"+s3); + System.out.println("转32位无符号-CDAB序:" + s3); String hexToBytes = "3fea3d71"; byte[] bytes = ByteBufUtil.decodeHexDump(hexToBytes); float v1 = toFloat32_ABCD(bytes); - System.out.println("转32位浮点型-ABCD序:"+v1); + System.out.println("转32位浮点型-ABCD序:" + v1); - String hexToBytes1= "800041EE"; + String hexToBytes1 = "800041EE"; long i = Long.parseLong(hexToBytes1, 16); System.out.println(i); byte[] bytes1 = ByteBufUtil.decodeHexDump(hexToBytes1); float v2 = toFloat32_DCBA(bytes1); - System.out.println("转32位浮点型-CDAB序:"+v2); + System.out.println("转32位浮点型-CDAB序:" + v2); int signedShort = -32627; // 16位有符号整形 // 将有符号短整型转换为无符号短整型 @@ -441,10 +448,10 @@ public class CaculateUtils { long l = Long.parseLong("00501F40", 16); System.out.println(l); - int val1 = -6553510; - byte[] bytes2 = intToBytes2(val1); - int i1 = bytesToInt2(bytes2); - System.out.println(i1); + int val1 = -6553510; + byte[] bytes2 = intToBytes2(val1); + int i1 = bytesToInt2(bytes2); + System.out.println(i1); } } diff --git a/fastbee-server/iot-server-core/src/main/java/com/fastbee/server/TCPServer.java b/fastbee-server/iot-server-core/src/main/java/com/fastbee/server/TCPServer.java index 44965e17..59852b04 100644 --- a/fastbee-server/iot-server-core/src/main/java/com/fastbee/server/TCPServer.java +++ b/fastbee-server/iot-server-core/src/main/java/com/fastbee/server/TCPServer.java @@ -60,12 +60,18 @@ public class TCPServer extends Server { @Override protected void initChannel(NioSocketChannel channel) throws Exception { channel.pipeline() - .addLast(new IdleStateHandler(config.readerIdleTime, config.writerIdleTime, config.allIdleTime)) //设置心跳时间 - .addLast(FastBeeConstant.SERVER.FRAMEDECODER, frameDecoder())//粘包处理器 - .addLast(FastBeeConstant.SERVER.ADAPTER, adapter)//消息适配器 - .addLast(FastBeeConstant.SERVER.DECODER, decoder) //报文解码器 - .addLast(FastBeeConstant.SERVER.ENCODER, encoder) //报文编码器 - .addLast(FastBeeConstant.SERVER.DISPATCHER, dispatcher); //消息分发 + // 设置心跳时间 + .addLast(new IdleStateHandler(config.readerIdleTime, config.writerIdleTime, config.allIdleTime)) + // 第一步、添加粘包处理器 + .addLast(FastBeeConstant.SERVER.FRAMEDECODER, frameDecoder()) + // 第二步、添加消息适配器 + .addLast(FastBeeConstant.SERVER.ADAPTER, adapter) + // 第三步、添加报文解码器 + .addLast(FastBeeConstant.SERVER.DECODER, decoder) + // 第四步、添加报文编码器 + .addLast(FastBeeConstant.SERVER.ENCODER, encoder) + // 第五步、添加消息分发器 + .addLast(FastBeeConstant.SERVER.DISPATCHER, dispatcher); } }); } diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/DeviceServiceImpl.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/DeviceServiceImpl.java index a2481b84..d2837384 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/DeviceServiceImpl.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/DeviceServiceImpl.java @@ -600,8 +600,12 @@ public class DeviceServiceImpl implements IDeviceService { } // 执行计算 - value = String.valueOf(CaculateUtils.execute(formula, params)); - log.info("=>纯计算物模型结果[{}],标识符:[{}],结果:{}", input.getProductId(), identity, value); + try { + value = String.valueOf(CaculateUtils.execute(formula, params)); + } catch (Exception e) { + log.error("=>计算公式执行失败[{}],标识符:[{}],公式:[{}],错误:{}", input.getProductId(), identity, formula, e.getMessage()); + } + //log.info("=>纯计算物模型结果[{}],标识符:[{}],结果:{}", input.getProductId(), identity, value); item.setValue(value); } /* ★★★★★★★★★★★★★★★★★★★★★★ 数据计算 -结束 ★★★★★★★★★★★★★★★★★★★★★★ */ diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/ThingsModelServiceImpl.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/ThingsModelServiceImpl.java index c037fb95..52ef9e2d 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/ThingsModelServiceImpl.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/ThingsModelServiceImpl.java @@ -264,6 +264,7 @@ public class ThingsModelServiceImpl implements IThingsModelService { } ThingsModel input = new ThingsModel(); input.setProductId(thingsModel.getProductId()); + input.setIsCalculated(0); // 只需要查产品的物模型即可 input.setLanguage(SecurityUtils.getLanguage()); List list = thingsModelMapper.selectThingsModelList(input); Boolean isRepeat = list.stream().anyMatch(x -> x.getIdentifier().equals(thingsModel.getIdentifier()) && x.getModelId().longValue() != thingsModel.getModelId()); diff --git a/vue/src/views/iot/device/device-things-model.vue b/vue/src/views/iot/device/device-things-model.vue index 5a8921b0..b5387356 100644 --- a/vue/src/views/iot/device/device-things-model.vue +++ b/vue/src/views/iot/device/device-things-model.vue @@ -177,8 +177,8 @@
- - 清空 + + 清空
@@ -646,11 +646,53 @@ export default { }, /** 插入运算符 */ insertOperator(op) { - this.form.formula = (this.form.formula || '') + op; + this.insertAtCursor(op); }, /** 插入变量(标识符使用大括号包裹) */ insertVariable(identifier) { - this.form.formula = (this.form.formula || '') + `{${identifier}}`; + this.insertAtCursor(`{${identifier}}`); + }, + /** 在光标位置插入内容(支持替换选中文本) */ + insertAtCursor(text) { + const input = this.$refs.formulaInput; + if (!input) { + this.form.formula = (this.form.formula || '') + text; + return; + } + // 获取 textarea 的 DOM 元素 + const textarea = input.$refs.textarea || input.$el.querySelector('textarea'); + if (!textarea) { + this.form.formula = (this.form.formula || '') + text; + return; + } + + const formula = this.form.formula || ''; + const start = textarea.selectionStart; + const end = textarea.selectionEnd; + + // 在光标位置插入或替换选中的文本 + const newFormula = formula.substring(0, start) + text + formula.substring(end); + this.form.formula = newFormula; + + // 设置光标位置到插入内容之后 + this.$nextTick(() => { + const newCursorPos = start + text.length; + textarea.focus(); + textarea.setSelectionRange(newCursorPos, newCursorPos); + }); + }, + /** 清空公式 */ + clearFormula() { + this.form.formula = ''; + this.focusFormulaInput(); + }, + /** 聚焦到公式输入框 */ + focusFormulaInput() { + this.$nextTick(() => { + if (this.$refs.formulaInput) { + this.$refs.formulaInput.focus(); + } + }); }, /** 格式化物模型specs */ formatThingsSpecs() { diff --git a/vue/src/views/iot/device/index.vue b/vue/src/views/iot/device/index.vue index 327c4437..509026c6 100644 --- a/vue/src/views/iot/device/index.vue +++ b/vue/src/views/iot/device/index.vue @@ -389,6 +389,11 @@ export default { }; }, created() { + // 页码筛选 + let pageNum = this.$route.query.pageNum; + if (pageNum != null) { + this.queryParams.pageNum = Number(pageNum); + } // 产品筛选 let productId = this.$route.query.productId; if (productId != null) { @@ -413,14 +418,15 @@ export default { this.connectMqtt(); }, activated() { + // 页码筛选(优先恢复页码,无论是否有其他参数变化) + let pageNum = this.$route.query.pageNum; + if (pageNum != null) { + this.queryParams.pageNum = Number(pageNum); + } + const time = this.$route.query.t; if (time != null && time != this.uniqueId) { this.uniqueId = time; - // 页码筛选 - let pageNum = this.$route.query.pageNum; - if (pageNum != null) { - this.queryParams.pageNum = Number(pageNum); - } // 产品筛选 let productId = this.$route.query.productId; if (productId != null) { @@ -443,6 +449,9 @@ export default { this.queryParams.groupId = null; } this.getList(); + } else if (pageNum != null) { + // 浏览器回退时,即使没有 t 参数也要刷新列表 + this.getList(); } }, methods: { @@ -625,6 +634,8 @@ export default { this.loading = true; this.queryParams.params = {}; this.getGroupList(); + // 同步更新 URL 中的页码参数(用于浏览器回退时恢复页码) + this.updateUrlPageNum(); listDeviceShort(this.queryParams).then((response) => { this.deviceList = response.rows; this.total = response.total; @@ -635,6 +646,18 @@ export default { this.loading = false; }); }, + /** 更新 URL 中的页码参数 */ + updateUrlPageNum() { + const currentPageNum = this.$route.query.pageNum; + if (Number(currentPageNum) !== this.queryParams.pageNum) { + this.$router.replace({ + query: { + ...this.$route.query, + pageNum: this.queryParams.pageNum, + }, + }).catch(() => {}); + } + }, /** 搜索按钮操作 */ handleQuery() { this.queryParams.pageNum = 1; diff --git a/vue/src/views/login.vue b/vue/src/views/login.vue index 911a7129..bdc28b04 100644 --- a/vue/src/views/login.vue +++ b/vue/src/views/login.vue @@ -31,7 +31,7 @@
- +
@@ -46,7 +46,7 @@
- + @@ -114,7 +114,7 @@
- +