作者 钟来

明牛协议解析完成

正在显示 29 个修改的文件 包含 1505 行增加71 行删除
@@ -8,6 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -8,6 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired;
8 import org.springframework.stereotype.Component; 8 import org.springframework.stereotype.Component;
9 import org.springframework.util.AntPathMatcher; 9 import org.springframework.util.AntPathMatcher;
10 import org.springframework.util.StreamUtils; 10 import org.springframework.util.StreamUtils;
  11 +import org.springframework.web.util.ContentCachingRequestWrapper;
11 12
12 import javax.servlet.*; 13 import javax.servlet.*;
13 import javax.servlet.annotation.WebFilter; 14 import javax.servlet.annotation.WebFilter;
@@ -41,7 +42,7 @@ public class HttpServletRequestReplacedFilter implements Filter { @@ -41,7 +42,7 @@ public class HttpServletRequestReplacedFilter implements Filter {
41 public void doFilter(ServletRequest request, ServletResponse response, 42 public void doFilter(ServletRequest request, ServletResponse response,
42 FilterChain chain) throws IOException, ServletException { 43 FilterChain chain) throws IOException, ServletException {
43 44
44 - RequestReaderHttpServletRequestWrapper requestWrapper = null; 45 + ContentCachingRequestWrapper requestWrapper = null;
45 if (request instanceof HttpServletRequest) { 46 if (request instanceof HttpServletRequest) {
46 HttpServletRequest httpServletRequest = (HttpServletRequest) request; 47 HttpServletRequest httpServletRequest = (HttpServletRequest) request;
47 //解决Invalid character found in method name的问题 48 //解决Invalid character found in method name的问题
@@ -56,8 +57,8 @@ public class HttpServletRequestReplacedFilter implements Filter { @@ -56,8 +57,8 @@ public class HttpServletRequestReplacedFilter implements Filter {
56 57
57 if (!match(httpServletRequest.getRequestURI().toString())) 58 if (!match(httpServletRequest.getRequestURI().toString()))
58 { 59 {
59 - requestWrapper = new RequestReaderHttpServletRequestWrapper(httpServletRequest);  
60 - printLog(httpServletRequest,requestWrapper.getBody()); 60 + requestWrapper = new ContentCachingRequestWrapper(httpServletRequest);
  61 + printLog(requestWrapper);
61 } 62 }
62 } 63 }
63 //获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中。 64 //获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中。
@@ -69,29 +70,40 @@ public class HttpServletRequestReplacedFilter implements Filter { @@ -69,29 +70,40 @@ public class HttpServletRequestReplacedFilter implements Filter {
69 } 70 }
70 71
71 } 72 }
  73 + private boolean isMultipart(HttpServletRequest request) {
  74 + String contentType = request.getContentType();
  75 + return contentType != null && contentType.startsWith("multipart/");
  76 + }
72 @Override 77 @Override
73 public void destroy() { 78 public void destroy() {
74 } 79 }
75 80
76 - private void printLog(HttpServletRequest httpServletRequest,byte[] bodyBytes) throws UnsupportedEncodingException {  
77 - logger.info("---------------------开始进入"+httpServletRequest.getRequestURL().toString()+"登录拦截----------------------------");  
78 - Map<String,Object> map = new HashMap<>();  
79 - map.put("parameterValue",httpServletRequest.getParameterMap()); 81 + private void printLog(ContentCachingRequestWrapper request) throws IOException {
  82 + logger.info("------ 请求 URL: {} ------", request.getRequestURL());
  83 +
  84 + Map<String, Object> map = new HashMap<>();
80 85
81 - // 打印请求头信息  
82 - Map<String,Object> headermap = new HashMap<>();  
83 - Enumeration<String> headerNames = httpServletRequest.getHeaderNames(); 86 + // headers
  87 + Map<String, Object> headerMap = new HashMap<>();
  88 + Enumeration<String> headerNames = request.getHeaderNames();
84 while (headerNames.hasMoreElements()) { 89 while (headerNames.hasMoreElements()) {
85 - String headerName = headerNames.nextElement();  
86 - String headerValue = httpServletRequest.getHeader(headerName);  
87 - headermap.put(headerName, headerValue); 90 + String h = headerNames.nextElement();
  91 + headerMap.put(h, request.getHeader(h));
  92 + }
  93 + map.put("header", headerMap);
  94 +
  95 + // params
  96 + map.put("parameterValue", request.getParameterMap());
  97 +
  98 + // ★ multipart 请求不能提前读取,否则破坏文件上传
  99 + if (!isMultipart(request)) {
  100 + // body
  101 + String body = new String(request.getContentAsByteArray(),
  102 + request.getCharacterEncoding() == null ? "UTF-8" : request.getCharacterEncoding());
  103 + map.put("body", body);
88 } 104 }
89 - map.put("header",headermap);  
90 105
91 - //获取请求body  
92 - String body = new String(bodyBytes, httpServletRequest.getCharacterEncoding());  
93 - map.put("body",body);  
94 - logger.info("---------------------参数:"+ GsonConstructor.get().toJson(map)+"----------------------------"); 106 + logger.info("------ 参数:{} ------", GsonConstructor.get().toJson(map));
95 } 107 }
96 108
97 // 设置排除路径 109 // 设置排除路径
  1 +package com.zhonglai.luhui.device.domain;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +
  6 +@ApiModel("流水鱼plc设备")
  7 +public class IotProductPlcDevice {
  8 + @ApiModelProperty(value="主键")
  9 + private Integer id;
  10 + @ApiModelProperty(value="名称")
  11 + private String name;
  12 + @ApiModelProperty(value="可分配编号")
  13 + private String numbers;
  14 + @ApiModelProperty(value="标识符")
  15 + private String identifier;
  16 +
  17 + public String getIdentifier() {
  18 + return identifier;
  19 + }
  20 +
  21 + public void setIdentifier(String identifier) {
  22 + this.identifier = identifier;
  23 + }
  24 +
  25 + public Integer getId() {
  26 + return id;
  27 + }
  28 +
  29 + public void setId(Integer id) {
  30 + this.id = id;
  31 + }
  32 +
  33 + public String getName() {
  34 + return name;
  35 + }
  36 +
  37 + public void setName(String name) {
  38 + this.name = name;
  39 + }
  40 +
  41 + public String getNumbers() {
  42 + return numbers;
  43 + }
  44 +
  45 + public void setNumbers(String numbers) {
  46 + this.numbers = numbers;
  47 + }
  48 +}
@@ -59,5 +59,6 @@ public interface UserTerminalGroupRelationMapper @@ -59,5 +59,6 @@ public interface UserTerminalGroupRelationMapper
59 */ 59 */
60 public int deleteUserTerminalGroupRelationByIds(Integer[] ids); 60 public int deleteUserTerminalGroupRelationByIds(Integer[] ids);
61 61
  62 + public int deleteUserTerminalGroupRelationByTerminalIds(String[] ids);
62 List<UserTerminalGroupRelation> selectListByTerminalIds(String[] iot_terminal_ids); 63 List<UserTerminalGroupRelation> selectListByTerminalIds(String[] iot_terminal_ids);
63 } 64 }
@@ -51,7 +51,7 @@ public interface IUserTerminalGroupRelationService @@ -51,7 +51,7 @@ public interface IUserTerminalGroupRelationService
51 * @return 结果 51 * @return 结果
52 */ 52 */
53 public int deleteUserTerminalGroupRelationByIds(Integer[] ids); 53 public int deleteUserTerminalGroupRelationByIds(Integer[] ids);
54 - 54 + public int deleteUserTerminalGroupRelationByTerminalIds(String[] ids);
55 /** 55 /**
56 * 删除终端分组关系信息 56 * 删除终端分组关系信息
57 * 57 *
@@ -96,6 +96,11 @@ public class UserTerminalGroupRelationServiceImpl implements IUserTerminalGroupR @@ -96,6 +96,11 @@ public class UserTerminalGroupRelationServiceImpl implements IUserTerminalGroupR
96 return userTerminalGroupRelationMapper.deleteUserTerminalGroupRelationByIds(ids); 96 return userTerminalGroupRelationMapper.deleteUserTerminalGroupRelationByIds(ids);
97 } 97 }
98 98
  99 + public int deleteUserTerminalGroupRelationByTerminalIds(String[] ids)
  100 + {
  101 + return userTerminalGroupRelationMapper.deleteUserTerminalGroupRelationByTerminalIds(ids);
  102 + }
  103 +
99 /** 104 /**
100 * 删除终端分组关系信息 105 * 删除终端分组关系信息
101 * 106 *
@@ -40,11 +40,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" @@ -40,11 +40,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
40 <if test="create_time != null">create_time,</if> 40 <if test="create_time != null">create_time,</if>
41 <if test="name != null">name,</if> 41 <if test="name != null">name,</if>
42 <if test="user_info_id != null">user_info_id,</if> 42 <if test="user_info_id != null">user_info_id,</if>
  43 + <if test="group_type != null">group_type,</if>
43 </trim> 44 </trim>
44 <trim prefix="values (" suffix=")" suffixOverrides=","> 45 <trim prefix="values (" suffix=")" suffixOverrides=",">
45 <if test="create_time != null">#{create_time},</if> 46 <if test="create_time != null">#{create_time},</if>
46 <if test="name != null">#{name},</if> 47 <if test="name != null">#{name},</if>
47 <if test="user_info_id != null">#{user_info_id},</if> 48 <if test="user_info_id != null">#{user_info_id},</if>
  49 + <if test="group_type != null">#{group_type},</if>
48 </trim> 50 </trim>
49 </insert> 51 </insert>
50 52
@@ -54,6 +56,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" @@ -54,6 +56,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
54 <if test="create_time != null">create_time = #{create_time},</if> 56 <if test="create_time != null">create_time = #{create_time},</if>
55 <if test="name != null">name = #{name},</if> 57 <if test="name != null">name = #{name},</if>
56 <if test="user_info_id != null">user_info_id = #{user_info_id},</if> 58 <if test="user_info_id != null">user_info_id = #{user_info_id},</if>
  59 + <if test="group_type != null">group_type = #{group_type},</if>
57 </trim> 60 </trim>
58 where id = #{id} 61 where id = #{id}
59 </update> 62 </update>
@@ -79,6 +79,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" @@ -79,6 +79,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
79 </foreach> 79 </foreach>
80 </delete> 80 </delete>
81 81
  82 + <delete id="deleteUserTerminalGroupRelationByTerminalIds" parameterType="String">
  83 + delete from user_terminal_group_relation where iot_terminal_id in
  84 + <foreach item="id" collection="array" open="(" separator="," close=")">
  85 + #{id}
  86 + </foreach>
  87 + </delete>
  88 +
82 <select id="selectListByTerminalIds" parameterType="String" resultMap="UserTerminalGroupRelationResult"> 89 <select id="selectListByTerminalIds" parameterType="String" resultMap="UserTerminalGroupRelationResult">
83 select * from `user_terminal_group_relation` where iot_terminal_id in 90 select * from `user_terminal_group_relation` where iot_terminal_id in
84 <foreach item="id" collection="array" open="(" separator="," close=")"> 91 <foreach item="id" collection="array" open="(" separator="," close=")">
1 package com.zhonglai.luhui.admin.controller.iot; 1 package com.zhonglai.luhui.admin.controller.iot;
2 2
  3 +import java.io.IOException;
3 import java.util.HashMap; 4 import java.util.HashMap;
4 import java.util.List; 5 import java.util.List;
5 import java.util.Map; 6 import java.util.Map;
  7 +import javax.servlet.http.HttpServletRequest;
6 import javax.servlet.http.HttpServletResponse; 8 import javax.servlet.http.HttpServletResponse;
7 9
  10 +import cn.hutool.http.HttpRequest;
  11 +import cn.hutool.http.HttpResponse;
  12 +import cn.hutool.http.HttpUtil;
  13 +import cn.hutool.json.JSONObject;
  14 +import com.alibaba.fastjson.JSON;
  15 +import com.alibaba.fastjson.JSONArray;
8 import com.google.gson.JsonObject; 16 import com.google.gson.JsonObject;
  17 +import com.ruoyi.system.domain.entity.SysUser;
9 import com.zhonglai.luhui.action.BaseController; 18 import com.zhonglai.luhui.action.BaseController;
  19 +import com.zhonglai.luhui.admin.dto.MNPLCPointEntity;
10 import com.zhonglai.luhui.dao.service.PublicService; 20 import com.zhonglai.luhui.dao.service.PublicService;
11 import com.zhonglai.luhui.device.domain.IotDevice; 21 import com.zhonglai.luhui.device.domain.IotDevice;
12 import com.zhonglai.luhui.security.utils.SecurityUtils; 22 import com.zhonglai.luhui.security.utils.SecurityUtils;
13 import com.zhonglai.luhui.sys.utils.ExcelUtil; 23 import com.zhonglai.luhui.sys.utils.ExcelUtil;
14 import io.swagger.annotations.Api; 24 import io.swagger.annotations.Api;
  25 +import io.swagger.annotations.ApiImplicitParam;
15 import io.swagger.annotations.ApiOperation; 26 import io.swagger.annotations.ApiOperation;
  27 +import io.swagger.v3.oas.annotations.Parameter;
  28 +import io.swagger.v3.oas.annotations.enums.ParameterIn;
  29 +import org.springframework.http.MediaType;
16 import org.springframework.security.access.prepost.PreAuthorize; 30 import org.springframework.security.access.prepost.PreAuthorize;
17 import org.springframework.beans.factory.annotation.Autowired; 31 import org.springframework.beans.factory.annotation.Autowired;
18 import org.springframework.transaction.annotation.Transactional; 32 import org.springframework.transaction.annotation.Transactional;
19 -import org.springframework.web.bind.annotation.GetMapping;  
20 -import org.springframework.web.bind.annotation.PostMapping;  
21 -import org.springframework.web.bind.annotation.PutMapping;  
22 -import org.springframework.web.bind.annotation.DeleteMapping;  
23 -import org.springframework.web.bind.annotation.PathVariable;  
24 -import org.springframework.web.bind.annotation.RequestBody;  
25 -import org.springframework.web.bind.annotation.RequestMapping;  
26 -import org.springframework.web.bind.annotation.RestController; 33 +import org.springframework.web.bind.annotation.*;
27 import com.ruoyi.common.annotation.Log; 34 import com.ruoyi.common.annotation.Log;
28 import com.ruoyi.common.core.domain.AjaxResult; 35 import com.ruoyi.common.core.domain.AjaxResult;
29 import com.ruoyi.common.enums.BusinessType; 36 import com.ruoyi.common.enums.BusinessType;
30 import com.zhonglai.luhui.device.domain.IotProduct; 37 import com.zhonglai.luhui.device.domain.IotProduct;
31 import com.zhonglai.luhui.device.service.IIotProductService; 38 import com.zhonglai.luhui.device.service.IIotProductService;
32 import com.ruoyi.common.core.page.TableDataInfo; 39 import com.ruoyi.common.core.page.TableDataInfo;
  40 +import org.springframework.web.multipart.MultipartFile;
33 41
34 /** 42 /**
35 * 产品Controller 43 * 产品Controller
@@ -136,4 +144,99 @@ public class IotProductController extends BaseController @@ -136,4 +144,99 @@ public class IotProductController extends BaseController
136 return toAjax(IotProductService.deleteIotProductByIds(ids)); 144 return toAjax(IotProductService.deleteIotProductByIds(ids));
137 } 145 }
138 146
139 -} 147 + @ApiOperation("导入明牛变量表")
  148 + @ApiImplicitParam(name = "file", value = "文件", required = true, dataType = "MultipartFile", dataTypeClass = MultipartFile.class)
  149 + @PostMapping(value = "/importMNVariableData")
  150 + public AjaxResult importMNVariableData(@RequestParam(value = "file") MultipartFile file) throws Exception {
  151 + // 检查文件是否为空,防止 NullPointerException
  152 + if (file == null || file.isEmpty()) {
  153 + return AjaxResult.error("上传的文件不能为空");
  154 + }
  155 +
  156 + ExcelUtil<MNPLCPointEntity> util = new ExcelUtil<>(MNPLCPointEntity.class);
  157 + List<MNPLCPointEntity> mnplcPointEntityList = util.importExcel(file.getInputStream());
  158 + if(null != mnplcPointEntityList && mnplcPointEntityList.size() !=0 )
  159 + {
  160 + StringBuffer stringBuffer = new StringBuffer("变量名称\t变量描述");
  161 + for (MNPLCPointEntity mnplcPointEntity:mnplcPointEntityList)
  162 + {
  163 + stringBuffer.append("\n");
  164 + stringBuffer.append(mnplcPointEntity.getVariableName());
  165 + stringBuffer.append("\t");
  166 + stringBuffer.append(mnplcPointEntity.getVariableDescription());
  167 + }
  168 + StringBuffer prompt = new StringBuffer("任务说明:\n" +
  169 + "你将收到一个包含\"变量名称\"和\"变量描述\"的表格。你的任务是:\n" +
  170 + "\n" +
  171 + "1. **识别设备**:从变量描述中提取所有设备名称。设备名称可能出现在变量描述中,如\"漩涡风机001\",\"蛋分射流泵1\",\"系统控制柜01\"等。\n" +
  172 + "2. **归类属性**:将每个变量名称归类到对应的设备下,作为该设备的属性。注意:\n" +
  173 + " - 同一设备可能有多个变量名称。\n" +
  174 + " - 变量名称与变量描述之间的对应关系通过描述中的设备名称识别。\n" +
  175 + " - 如果变量描述中没有明确设备名称,将这些变量统一归类到\"网关数据\"下。\n" +
  176 + "3. **生成 JSON**:最终输出为 JSON 格式的字符串,要求:\n" +
  177 + " - JSON 为数组,每个元素是一个对象,键是设备名称,值是该设备的属性数组,用英文逗号分隔。\n" +
  178 + " - 示例:\n" +
  179 + " [\n" +
  180 + " {\n" +
  181 + " \"系统控制柜01\": \"SYS_AUTO_01,SYS_01_ALARM_RESET\"\n" +
  182 + " },\n" +
  183 + " {\n" +
  184 + " \"漩涡风机001\": \"C001_RUN,C001_ALARM,C001_QT\"\n" +
  185 + " },\n" +
  186 + " {\n" +
  187 + " \"网关数据\": \"SYS_NH,1-LSL,2-LSL,...\"\n" +
  188 + " }\n" +
  189 + " ]\n" +
  190 + "4. **额外要求**:\n" +
  191 + " - 输出只包含 JSON 字符串,不需要其他解释或文字。\n" +
  192 + " - 保持设备和属性的顺序与原表格中出现的顺序一致。\n" +
  193 + "\n" +
  194 + "表格数据如下:\n" );
  195 + prompt.append(stringBuffer);
  196 + prompt.append("\n");
  197 +
  198 + HttpRequest httpRequest = HttpUtil.createPost("https://api.deepseek.com/v1/chat/completions");
  199 + httpRequest.header("Content-Type", "application/json");
  200 + httpRequest.header("Authorization", "Bearer sk-bf4107d81fa842eb85f3e3abfa6348ae");
  201 + JSONObject data = new JSONObject();
  202 + data.put("model", "deepseek-chat");
  203 +
  204 + JSONArray messages = new JSONArray();
  205 + messages.add(new JSONObject()
  206 + .put("role", "system")
  207 + .put("content", "你是一个专业的数据处理专家,请将表格中的数据处理为JSON格式。")
  208 + );
  209 + messages.add(new JSONObject()
  210 + .put("role", "user")
  211 + .put("content", prompt.toString())
  212 + );
  213 +
  214 + data.put("messages", messages);
  215 + data.put("stream", false);
  216 +
  217 + String requestBody = data.toString(); // 自动为你处理所有转义
  218 + HttpResponse response = httpRequest.body(requestBody).execute();
  219 +
  220 + // 修复空指针异常:增加对 response 对象的 null 检查
  221 + if(response != null) {
  222 + if(response.isOk())
  223 + {
  224 + String body = response.body();
  225 + if (body.indexOf("content")>0)
  226 + {
  227 + JSONArray jsonArray = JSON.parseObject(body).getJSONArray("choices").getJSONObject(0).getJSONObject("message").getJSONArray("content");
  228 + if (jsonArray != null && jsonArray.size() > 0)
  229 + {
  230 +
  231 + }
  232 + }
  233 + return AjaxResult.success(body);
  234 + }
  235 + return AjaxResult.error(response.getStatus(), response.body());
  236 + } else {
  237 + return AjaxResult.error("请求失败,无法连接到AI服务");
  238 + }
  239 + }
  240 + return AjaxResult.success(mnplcPointEntityList);
  241 + }
  242 +}
  1 +package com.zhonglai.luhui.admin.dto;
  2 +
  3 +import com.ruoyi.common.annotation.Excel;
  4 +
  5 +public class MNPLCPointEntity {
  6 + @Excel(name = "变量ID")
  7 + private Integer variableId; // 变量ID
  8 + @Excel(name = "变量名称")
  9 + private String variableName; // 变量名称
  10 + @Excel(name = "所属分组")
  11 + private String groupName; // 所属分组
  12 + @Excel(name = "地址类型")
  13 + private String addressType; // 地址类型
  14 + @Excel(name = "地址偏移")
  15 + private String addressOffset; // 地址偏移
  16 + @Excel(name = "地址类型2")
  17 + private String addressType2; // 地址类型2
  18 + @Excel(name = "地址偏移2")
  19 + private String addressOffset2; // 地址偏移2
  20 + @Excel(name = "数据类型")
  21 + private String dataType; // 数据类型
  22 + @Excel(name = "数据精度")
  23 + private Integer dataPrecision; // 数据精度
  24 + @Excel(name = "字符串长度")
  25 + private Integer stringLength; // 字符串长度
  26 + @Excel(name = "字符串编码")
  27 + private String stringEncoding; // 字符串编码
  28 + @Excel(name = "线性换算")
  29 + private String linearConversion; // 线性换算
  30 + @Excel(name = "输入最小值")
  31 + private String inputMin; // 输入最小值
  32 + @Excel(name = "输入最大值")
  33 + private String inputMax; // 输入最大值
  34 + @Excel(name = "输出最小值")
  35 + private String outputMin; // 输出最小值
  36 + @Excel(name = "输出最大值")
  37 + private String outputMax; // 输出最大值
  38 + @Excel(name = "输出数据类型")
  39 + private String outputDataType; // 输出数据类型
  40 + @Excel(name = "安全类别")
  41 + private String safetyCategory; // 安全类别
  42 + @Excel(name = "读写状态")
  43 + private String readWriteStatus; // 读写状态
  44 + @Excel(name = "单位")
  45 + private String unit; // 单位
  46 + @Excel(name = "是否启动有效范围")
  47 + private String enableRange; // 是否启动有效范围
  48 + @Excel(name = "最大值")
  49 + private String maxValue; // 最大值
  50 + @Excel(name = "最小值")
  51 + private String minValue; // 最小值
  52 + @Excel(name = "所属设备")
  53 + private String deviceName; // 所属设备
  54 + @Excel(name = "变量描述")
  55 + private String variableDescription; // 变量描述
  56 + @Excel(name = "变量控制优先级")
  57 + private Integer controlPriority; // 变量控制优先级
  58 +
  59 + public Integer getVariableId() {
  60 + return variableId;
  61 + }
  62 +
  63 + public void setVariableId(Integer variableId) {
  64 + this.variableId = variableId;
  65 + }
  66 +
  67 + public String getVariableName() {
  68 + return variableName;
  69 + }
  70 +
  71 + public void setVariableName(String variableName) {
  72 + this.variableName = variableName;
  73 + }
  74 +
  75 + public String getGroupName() {
  76 + return groupName;
  77 + }
  78 +
  79 + public void setGroupName(String groupName) {
  80 + this.groupName = groupName;
  81 + }
  82 +
  83 + public String getAddressType() {
  84 + return addressType;
  85 + }
  86 +
  87 + public void setAddressType(String addressType) {
  88 + this.addressType = addressType;
  89 + }
  90 +
  91 + public String getAddressOffset() {
  92 + return addressOffset;
  93 + }
  94 +
  95 + public void setAddressOffset(String addressOffset) {
  96 + this.addressOffset = addressOffset;
  97 + }
  98 +
  99 + public String getAddressType2() {
  100 + return addressType2;
  101 + }
  102 +
  103 + public void setAddressType2(String addressType2) {
  104 + this.addressType2 = addressType2;
  105 + }
  106 +
  107 + public String getAddressOffset2() {
  108 + return addressOffset2;
  109 + }
  110 +
  111 + public void setAddressOffset2(String addressOffset2) {
  112 + this.addressOffset2 = addressOffset2;
  113 + }
  114 +
  115 + public String getDataType() {
  116 + return dataType;
  117 + }
  118 +
  119 + public void setDataType(String dataType) {
  120 + this.dataType = dataType;
  121 + }
  122 +
  123 + public Integer getDataPrecision() {
  124 + return dataPrecision;
  125 + }
  126 +
  127 + public void setDataPrecision(Integer dataPrecision) {
  128 + this.dataPrecision = dataPrecision;
  129 + }
  130 +
  131 + public Integer getStringLength() {
  132 + return stringLength;
  133 + }
  134 +
  135 + public void setStringLength(Integer stringLength) {
  136 + this.stringLength = stringLength;
  137 + }
  138 +
  139 + public String getStringEncoding() {
  140 + return stringEncoding;
  141 + }
  142 +
  143 + public void setStringEncoding(String stringEncoding) {
  144 + this.stringEncoding = stringEncoding;
  145 + }
  146 +
  147 + public String getLinearConversion() {
  148 + return linearConversion;
  149 + }
  150 +
  151 + public void setLinearConversion(String linearConversion) {
  152 + this.linearConversion = linearConversion;
  153 + }
  154 +
  155 + public String getInputMin() {
  156 + return inputMin;
  157 + }
  158 +
  159 + public void setInputMin(String inputMin) {
  160 + this.inputMin = inputMin;
  161 + }
  162 +
  163 + public String getInputMax() {
  164 + return inputMax;
  165 + }
  166 +
  167 + public void setInputMax(String inputMax) {
  168 + this.inputMax = inputMax;
  169 + }
  170 +
  171 + public String getOutputMin() {
  172 + return outputMin;
  173 + }
  174 +
  175 + public void setOutputMin(String outputMin) {
  176 + this.outputMin = outputMin;
  177 + }
  178 +
  179 + public String getOutputMax() {
  180 + return outputMax;
  181 + }
  182 +
  183 + public void setOutputMax(String outputMax) {
  184 + this.outputMax = outputMax;
  185 + }
  186 +
  187 + public String getOutputDataType() {
  188 + return outputDataType;
  189 + }
  190 +
  191 + public void setOutputDataType(String outputDataType) {
  192 + this.outputDataType = outputDataType;
  193 + }
  194 +
  195 + public String getSafetyCategory() {
  196 + return safetyCategory;
  197 + }
  198 +
  199 + public void setSafetyCategory(String safetyCategory) {
  200 + this.safetyCategory = safetyCategory;
  201 + }
  202 +
  203 + public String getReadWriteStatus() {
  204 + return readWriteStatus;
  205 + }
  206 +
  207 + public void setReadWriteStatus(String readWriteStatus) {
  208 + this.readWriteStatus = readWriteStatus;
  209 + }
  210 +
  211 + public String getUnit() {
  212 + return unit;
  213 + }
  214 +
  215 + public void setUnit(String unit) {
  216 + this.unit = unit;
  217 + }
  218 +
  219 + public String getEnableRange() {
  220 + return enableRange;
  221 + }
  222 +
  223 + public void setEnableRange(String enableRange) {
  224 + this.enableRange = enableRange;
  225 + }
  226 +
  227 + public String getMaxValue() {
  228 + return maxValue;
  229 + }
  230 +
  231 + public void setMaxValue(String maxValue) {
  232 + this.maxValue = maxValue;
  233 + }
  234 +
  235 + public String getMinValue() {
  236 + return minValue;
  237 + }
  238 +
  239 + public void setMinValue(String minValue) {
  240 + this.minValue = minValue;
  241 + }
  242 +
  243 + public String getDeviceName() {
  244 + return deviceName;
  245 + }
  246 +
  247 + public void setDeviceName(String deviceName) {
  248 + this.deviceName = deviceName;
  249 + }
  250 +
  251 + public String getVariableDescription() {
  252 + return variableDescription;
  253 + }
  254 +
  255 + public void setVariableDescription(String variableDescription) {
  256 + this.variableDescription = variableDescription;
  257 + }
  258 +
  259 + public Integer getControlPriority() {
  260 + return controlPriority;
  261 + }
  262 +
  263 + public void setControlPriority(Integer controlPriority) {
  264 + this.controlPriority = controlPriority;
  265 + }
  266 +}
  1 +package com.zhonglai.luhui.admin.service;
  2 +
  3 +import com.fasterxml.jackson.databind.ObjectMapper;
  4 +import com.zhonglai.luhui.admin.dto.MNPLCPointEntity;
  5 +import com.zhonglai.luhui.sys.utils.ExcelUtil;
  6 +import org.apache.poi.ss.usermodel.Row;
  7 +import org.apache.poi.ss.usermodel.Sheet;
  8 +import org.apache.poi.ss.usermodel.Workbook;
  9 +import org.apache.poi.ss.usermodel.WorkbookFactory;
  10 +
  11 +import java.io.File;
  12 +import java.io.FileInputStream;
  13 +import java.util.*;
  14 +import java.util.regex.Matcher;
  15 +import java.util.regex.Pattern;
  16 +
  17 +public class MNPLCPointAnalysisService {
  18 + // 工控常用属性词(可继续扩展)
  19 + public static final List<String> ATTR_WORDS = Arrays.asList(
  20 + "运行","故障","报警","告警","信号","远程","控制","允许","模式","切换",
  21 + "启停","启动","停止","反馈","设定","设置",
  22 + "高限","低限","上限","下限","高","低",
  23 + "水温","电流","功率","频率","状态","位置"
  24 + );
  25 +
  26 + // 从变量描述里提取设备名
  27 + public static String extractFromDesc(String desc) {
  28 +
  29 + for (String key : ATTR_WORDS) {
  30 + int index = desc.indexOf(key);
  31 + if (index > 0) {
  32 + return desc.substring(0, index); // 设备名部分
  33 + }
  34 + }
  35 + return desc; // 未匹配到属性词,整体视为设备名
  36 + }
  37 +
  38 + // 从变量名称中提取序号,如 LL003 → 3
  39 + public static String extractNumberFromVarName(String varName) {
  40 + Matcher m = Pattern.compile("(\\d+)").matcher(varName);
  41 + if (m.find()) {
  42 + return m.group(1);
  43 + }
  44 + return "";
  45 + }
  46 +
  47 + // 最终设备名融合逻辑(描述 + 名称)
  48 + public static String getDeviceName(String varName, String desc) {
  49 + String nameFromDesc = extractFromDesc(desc).trim();
  50 + String numberFromName = extractNumberFromVarName(varName);
  51 +
  52 + // 如果描述中不含序号,但名称中有 → 补上
  53 + if (!nameFromDesc.matches(".*\\d+.*") && !numberFromName.isEmpty()) {
  54 + return nameFromDesc + numberFromName;
  55 + }
  56 +
  57 + return nameFromDesc;
  58 + }
  59 +
  60 + public static List<MNPLCPointEntity> read(String file) throws Exception {
  61 + ExcelUtil<MNPLCPointEntity> util = new ExcelUtil<>(MNPLCPointEntity.class);
  62 + List<MNPLCPointEntity> mnplcPointEntityList = util.importExcel(new FileInputStream(file));
  63 + return mnplcPointEntityList;
  64 + }
  65 +
  66 + public static void main(String[] args) throws Exception {
  67 +
  68 + String file1 = "E:\\work\\idea\\Luhui\\lh-modules\\lh-device-protocol-parser\\lh-device-protocol-agreement\\lh-device-mn-plc\\src\\main\\resources\\变量.xlsx";
  69 + String file2 = "E:\\work\\idea\\Luhui\\lh-modules\\lh-device-protocol-parser\\lh-device-protocol-agreement\\lh-device-mn-plc\\src\\main\\resources\\常德水槽变量.xlsx";
  70 +
  71 + // 合并两张表
  72 + List<MNPLCPointEntity> rows = new ArrayList<>();
  73 + rows.addAll(read(file1));
  74 + rows.addAll(read(file2));
  75 +
  76 + // 设备 → 点位列表
  77 + Map<String, List<String>> deviceMap = new LinkedHashMap<>();
  78 +
  79 + for (MNPLCPointEntity row : rows) {
  80 + String varName = row.getVariableName();
  81 + String desc = row.getVariableDescription();
  82 +
  83 + // 自动识别设备名
  84 + String deviceName = getDeviceName(varName, desc);
  85 +
  86 + deviceMap
  87 + .computeIfAbsent(deviceName, k -> new ArrayList<>())
  88 + .add(varName);
  89 + }
  90 +
  91 + // 生成目标格式
  92 + List<Map<String, String>> result = new ArrayList<>();
  93 +
  94 + for (Map.Entry<String,List<String>> e : deviceMap.entrySet()) {
  95 + Map<String,String> item = new LinkedHashMap<>();
  96 + String device = e.getKey();
  97 + String vars = String.join(",", e.getValue());
  98 + item.put(device, vars);
  99 + result.add(item);
  100 + }
  101 +
  102 + // 输出 JSON
  103 + ObjectMapper mapper = new ObjectMapper();
  104 + String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result);
  105 + System.out.println(json);
  106 + }
  107 +}
@@ -8,27 +8,18 @@ import java.util.List; @@ -8,27 +8,18 @@ import java.util.List;
8 8
9 @ApiModel("通过终端新增终端分组关系") 9 @ApiModel("通过终端新增终端分组关系")
10 public class AddIotTerminalRelationDto { 10 public class AddIotTerminalRelationDto {
11 - @ApiModelProperty("设备id")  
12 - private String deviceId;  
13 - @ApiModelProperty("分组类型")  
14 - private String groupType; 11 + @ApiModelProperty("分组id")
  12 + private Integer groupId;
  13 +
15 @ApiModelProperty("终端列表") 14 @ApiModelProperty("终端列表")
16 private List<IotTerminal> iotTerminalList; 15 private List<IotTerminal> iotTerminalList;
17 16
18 - public String getDeviceId() {  
19 - return deviceId;  
20 - }  
21 -  
22 - public void setDeviceId(String deviceId) {  
23 - this.deviceId = deviceId;  
24 - }  
25 -  
26 - public String getGroupType() {  
27 - return groupType; 17 + public Integer getGroupId() {
  18 + return groupId;
28 } 19 }
29 20
30 - public void setGroupType(String groupType) {  
31 - this.groupType = groupType; 21 + public void setGroupId(Integer groupId) {
  22 + this.groupId = groupId;
32 } 23 }
33 24
34 public List<IotTerminal> getIotTerminalList() { 25 public List<IotTerminal> getIotTerminalList() {
1 package com.zhonglai.luhui.api.controller.user; 1 package com.zhonglai.luhui.api.controller.user;
2 2
3 import java.util.List; 3 import java.util.List;
  4 +import java.util.Map;
4 5
5 import com.ruoyi.common.utils.DateUtils; 6 import com.ruoyi.common.utils.DateUtils;
  7 +import com.ruoyi.common.utils.StringUtils;
6 import com.zhonglai.luhui.action.BaseController; 8 import com.zhonglai.luhui.action.BaseController;
7 import com.zhonglai.luhui.dao.service.PublicService; 9 import com.zhonglai.luhui.dao.service.PublicService;
8 import com.zhonglai.luhui.device.service.IUserTerminalGroupService; 10 import com.zhonglai.luhui.device.service.IUserTerminalGroupService;
@@ -55,6 +57,35 @@ public class UserTerminalGroupController extends BaseController @@ -55,6 +57,35 @@ public class UserTerminalGroupController extends BaseController
55 return getDataTable(list); 57 return getDataTable(list);
56 } 58 }
57 59
  60 + @ApiOperation("查询终端分组列表带统计")
  61 + @ApiImplicitParam(value = "分组类型",name = "group_type")
  62 + @GetMapping("/listCount")
  63 + public AjaxResult listCount(String group_type)
  64 + {
  65 + String typeStr = "";
  66 + if(StringUtils.isNotEmpty(group_type))
  67 + {
  68 + typeStr += " and( 1=2";
  69 + for (String type : group_type.split(","))
  70 + {
  71 + typeStr+=" OR utg.`group_type` LIKE '%|"+type+"|%'";
  72 + }
  73 + typeStr +=")";
  74 + }
  75 + List<Map<String,Object>> list = publicService.getObjectListBySQL("SELECT \n" +
  76 + " utg.id AS group_id,\n" +
  77 + " utg.name AS group_name,\n" +
  78 + " COUNT(CASE WHEN it.online = 3 THEN 1 END) AS online_count,\n" +
  79 + " COUNT(CASE WHEN it.online = 4 THEN 1 END) AS offline_count,\n" +
  80 + " COUNT(utgr.iot_terminal_id) AS total_devices\n" +
  81 + "FROM user_terminal_group utg\n" +
  82 + "LEFT JOIN user_terminal_group_relation utgr ON utg.id = utgr.iot_terminal_group_id\n" +
  83 + "LEFT JOIN iot_terminal it ON utgr.iot_terminal_id = it.id\n" +
  84 + "WHERE utg.user_info_id = "+SecurityUtils.getUserId()+typeStr+" \n" +
  85 + "GROUP BY utg.id, utg.name\n" +
  86 + "ORDER BY utg.id");
  87 + return AjaxResult.success(list);
  88 + }
58 89
59 /** 90 /**
60 * 获取终端分组详细信息 91 * 获取终端分组详细信息
@@ -94,9 +94,9 @@ public class UserTerminalGroupRelationController extends BaseController @@ -94,9 +94,9 @@ public class UserTerminalGroupRelationController extends BaseController
94 } 94 }
95 if (null != product_id) 95 if (null != product_id)
96 { 96 {
97 - stringBuffer.append(" AND b.product_id="+product_id); 97 + stringBuffer.append(" AND b.product_id="+product_id);
98 } 98 }
99 - List<Map<String,Object>> list = publicService.getObjectListBySQL("SELECT b.*,a.id group_id FROM `user_terminal_group_relation` a LEFT JOIN `iot_terminal` b ON a.`iot_terminal_id`=b.`id` WHERE a.user_info_id="+SecurityUtils.getUserId().intValue()+stringBuffer); 99 + List<Map<String,Object>> list = publicService.getObjectListBySQL("SELECT b.*,a.id group_id,c.`things_model_value` host_things_model_value,c.`things_model_config` host_things_model_config FROM `user_terminal_group_relation` a LEFT JOIN `iot_terminal` b ON a.`iot_terminal_id`=b.`id` LEFT JOIN `iot_device` c ON b.`device_id`=c.`client_id` WHERE a.user_info_id="+SecurityUtils.getUserId().intValue()+stringBuffer);
100 return AjaxResult.success(list); 100 return AjaxResult.success(list);
101 } 101 }
102 102
@@ -189,11 +189,11 @@ public class UserTerminalGroupRelationController extends BaseController @@ -189,11 +189,11 @@ public class UserTerminalGroupRelationController extends BaseController
189 @Transactional 189 @Transactional
190 @Log(title = "终端分组关系", businessType = BusinessType.DELETE) 190 @Log(title = "终端分组关系", businessType = BusinessType.DELETE)
191 @DeleteMapping("/{ids}") 191 @DeleteMapping("/{ids}")
192 - public AjaxResult remove(@PathVariable Integer[] ids) 192 + public AjaxResult remove(@PathVariable String[] ids)
193 { 193 {
194 String idss = "'"+Arrays.toString(ids).replace(",","','")+"'"; 194 String idss = "'"+Arrays.toString(ids).replace(",","','")+"'";
195 publicService.updateBySql("UPDATE `iot_terminal` SET user_info_id=null WHERE id IN("+idss+")"); 195 publicService.updateBySql("UPDATE `iot_terminal` SET user_info_id=null WHERE id IN("+idss+")");
196 - return toAjax(userTerminalGroupRelationService.deleteUserTerminalGroupRelationByIds(ids)); 196 + return toAjax(userTerminalGroupRelationService.deleteUserTerminalGroupRelationByTerminalIds(ids));
197 } 197 }
198 198
199 @ApiOperation("统计用户设备") 199 @ApiOperation("统计用户设备")
@@ -240,37 +240,47 @@ public class UserTerminalGroupRelationController extends BaseController @@ -240,37 +240,47 @@ public class UserTerminalGroupRelationController extends BaseController
240 public AjaxResult addIotTerminalRelation(@RequestBody AddIotTerminalRelationDto addIotTerminalRelationDto) 240 public AjaxResult addIotTerminalRelation(@RequestBody AddIotTerminalRelationDto addIotTerminalRelationDto)
241 { 241 {
242 Integer userid = SecurityUtils.getUserId().intValue(); 242 Integer userid = SecurityUtils.getUserId().intValue();
243 - String deviceId = addIotTerminalRelationDto.getDeviceId();  
244 - IotDevice iotDevice = iotDeviceService.selectIotDeviceByClient_id(deviceId);  
245 - List<Map<String,Object>> list = publicService.getObjectListBySQL("SELECT a.* FROM `user_terminal_group` a LEFT JOIN `user_terminal_group_relation` b ON a.`id`=b.`iot_terminal_group_id` LEFT JOIN `iot_terminal` c ON c.`id`=b.`iot_terminal_id` WHERE c.`device_id`='"+deviceId+"'");  
246 - Integer groupId = null;  
247 - String grouName = null;  
248 - if (null != list && list.size()>0) 243 + UserTerminalGroup userTerminalGroup = userTerminalGroupService.selectUserTerminalGroupById(addIotTerminalRelationDto.getGroupId());
  244 + if(null == userTerminalGroup)
249 { 245 {
250 - groupId = (Integer) list.get(0).get("id");  
251 - grouName = (String) list.get(0).get("name");  
252 - }else{  
253 - UserTerminalGroup userTerminalGroup = new UserTerminalGroup();  
254 - userTerminalGroup.setUser_info_id(userid);  
255 - userTerminalGroup.setName(iotDevice.getName());  
256 - userTerminalGroup.setCreate_time(DateUtils.getNowTimeMilly());  
257 - userTerminalGroup.setGroup_type(addIotTerminalRelationDto.getGroupType());  
258 - userTerminalGroupService.insertUserTerminalGroup(userTerminalGroup);  
259 - groupId = userTerminalGroup.getId();  
260 - grouName = userTerminalGroup.getName(); 246 + return AjaxResult.error("请选择正确的分组");
261 } 247 }
  248 + if(userid - userTerminalGroup.getUser_info_id()!=0)
  249 + {
  250 + return AjaxResult.error("该分组不属于你");
  251 + }
  252 + List<UserTerminalGroupRelation> list = new ArrayList<>();
  253 + String ids = "";
262 for (IotTerminal iotTerminal:addIotTerminalRelationDto.getIotTerminalList()) 254 for (IotTerminal iotTerminal:addIotTerminalRelationDto.getIotTerminalList())
263 { 255 {
  256 + if(!"".equals(ids))
  257 + {
  258 + ids += ",";
  259 + }
  260 + ids += "'"+iotTerminal.getId()+"'";
  261 +
264 iotTerminalService.updateIotTerminal(iotTerminal); 262 iotTerminalService.updateIotTerminal(iotTerminal);
  263 +
265 UserTerminalGroupRelation userTerminalGroupRelation = new UserTerminalGroupRelation(); 264 UserTerminalGroupRelation userTerminalGroupRelation = new UserTerminalGroupRelation();
266 userTerminalGroupRelation.setIot_terminal_id(iotTerminal.getId()); 265 userTerminalGroupRelation.setIot_terminal_id(iotTerminal.getId());
267 - userTerminalGroupRelation.setIot_terminal_group_id(groupId); 266 + userTerminalGroupRelation.setIot_terminal_group_id(addIotTerminalRelationDto.getGroupId());
268 userTerminalGroupRelation.setUser_info_id(userid); 267 userTerminalGroupRelation.setUser_info_id(userid);
269 userTerminalGroupRelation.setCreate_time(DateUtils.getNowTimeMilly()); 268 userTerminalGroupRelation.setCreate_time(DateUtils.getNowTimeMilly());
270 - userTerminalGroupRelation.setIot_terminal_group_name(grouName); 269 + userTerminalGroupRelation.setIot_terminal_group_name(userTerminalGroup.getName());
271 userTerminalGroupRelation.setProduct_id(iotTerminal.getProduct_id()); 270 userTerminalGroupRelation.setProduct_id(iotTerminal.getProduct_id());
272 - userTerminalGroupRelationService.insertUserTerminalGroupRelation(userTerminalGroupRelation); 271 + list.add(userTerminalGroupRelation);
  272 + }
  273 + List<Map<String,Object>> userIdlist = publicService.getObjectListBySQL("SELECT user_info_id,device_id FROM `iot_terminal` WHERE device_id IN(SELECT device_id FROM `iot_terminal` WHERE id IN("+ids+"))");
  274 + if(null != userIdlist && userIdlist.size() >0)
  275 + {
  276 + for (Map<String,Object> map:userIdlist)
  277 + {
  278 + if(null != map.get("user_info_id") && !String.valueOf(map.get("user_info_id")).equals(String.valueOf(userid)))
  279 + {
  280 + return AjaxResult.error("设备"+map.get("device_id")+"已被其他人绑定");
  281 + }
  282 + }
273 } 283 }
274 - return toAjax(1); 284 + return toAjax(publicService.insertAll(list));
275 } 285 }
276 } 286 }
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
  3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5 + <modelVersion>4.0.0</modelVersion>
  6 + <parent>
  7 + <groupId>com.zhonglai.luhui</groupId>
  8 + <artifactId>lh-device-protocol-agreement</artifactId>
  9 + <version>1.0-SNAPSHOT</version>
  10 + </parent>
  11 +
  12 + <artifactId>lh-device-mn-plc</artifactId>
  13 +
  14 + <description>
  15 + 解析明牛plc上传的数据
  16 + </description>
  17 +
  18 + <properties>
  19 + <maven.compiler.source>8</maven.compiler.source>
  20 + <maven.compiler.target>8</maven.compiler.target>
  21 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  22 + </properties>
  23 +
  24 + <dependencies>
  25 + <dependency>
  26 + <groupId>com.zhonglai.luhui</groupId>
  27 + <artifactId>lh-device-protocol-factory</artifactId>
  28 + </dependency>
  29 + </dependencies>
  30 +</project>
  1 +package com.zhonglai.luhui.device.protocol.mnplc.analysis;
  2 +
  3 +import com.google.gson.JsonElement;
  4 +import com.google.gson.JsonObject;
  5 +import com.google.gson.JsonPrimitive;
  6 +import com.ruoyi.common.utils.GsonConstructor;
  7 +import com.ruoyi.common.utils.StringUtils;
  8 +import com.zhonglai.luhui.device.analysis.comm.clien.ClienConnection;
  9 +import com.zhonglai.luhui.device.analysis.comm.clien.impl.ClienConnectionImpl;
  10 +import com.zhonglai.luhui.device.analysis.comm.dto.ApiClientRePlyDto;
  11 +import com.zhonglai.luhui.device.analysis.comm.factory.Topic;
  12 +import com.zhonglai.luhui.device.analysis.dto.MessageCode;
  13 +import com.zhonglai.luhui.device.analysis.util.TopicUtil;
  14 +import com.zhonglai.luhui.device.domain.IotProductPayloadModelNumber;
  15 +import com.zhonglai.luhui.device.domain.IotProductPlcDevice;
  16 +import com.zhonglai.luhui.device.protocol.factory.analysis.ProtocolParserFactory;
  17 +import com.zhonglai.luhui.device.protocol.factory.analysis.topic.Online;
  18 +import com.zhonglai.luhui.device.protocol.factory.config.DeviceCach;
  19 +import com.zhonglai.luhui.device.protocol.factory.config.PLCDeviceChannelRelationConfig;
  20 +import com.zhonglai.luhui.device.protocol.factory.config.ProductPayloadModelNumberCach;
  21 +import com.zhonglai.luhui.device.protocol.factory.control.DeviceCommandListenService;
  22 +import com.zhonglai.luhui.device.protocol.factory.dto.AnalysisResult;
  23 +import com.zhonglai.luhui.device.protocol.factory.dto.ParserDeviceHostDto;
  24 +import com.zhonglai.luhui.device.protocol.mnplc.analysis.topic.AddPost;
  25 +
  26 +
  27 +public class MNPLCProtocolParserFactoryImpl implements ProtocolParserFactory {
  28 + private static final String topicModel = "/{{roleid}}/{{username}}/{{clientid}}/{{payloadtype}}/{{topicType}}";
  29 + @Override
  30 + public Topic analysisTopic(String topicStr) {
  31 + Topic topic = TopicUtil.initTopicFromModelStr(topicStr,topicModel);
  32 + return topic;
  33 + }
  34 +
  35 + @Override
  36 + public AnalysisResult analysisPayload(Topic topic, byte[] payload) {
  37 + ParserDeviceHostDto parserDeviceHostDto = DeviceCach.getDeviceHost(topic.getClientid());
  38 +
  39 + switch (topic.getTopicType())
  40 + {
  41 + case "online":
  42 + return new Online().analysisPayload(payload);
  43 + case "ONLINE":
  44 + return new Online().analysisPayload(payload);
  45 + case "ADD_POST":
  46 + return new AddPost().analysisPayload(toformatPayLoad(parserDeviceHostDto,GsonConstructor.get().fromJson(new String(payload),JsonObject.class)));
  47 + case "PUT_REQ":
  48 + JsonObject data = null;
  49 +
  50 +
  51 + if(DeviceCommandListenService.hasClienConnection(topic.getClientid()))
  52 + {
  53 + ClienConnection clienConnection = DeviceCommandListenService.getClienConnection(topic.getClientid());
  54 + if(null != clienConnection && clienConnection instanceof ClienConnectionImpl)
  55 + {
  56 + ClienConnectionImpl clienConnectionImpl = (ClienConnectionImpl) clienConnection;
  57 + if(null != clienConnectionImpl.getCommd())
  58 + {
  59 + data = GsonConstructor.get().fromJson(toformatPayLoad(parserDeviceHostDto,GsonConstructor.get().fromJson(new String(clienConnectionImpl.getCommd()),JsonObject.class)),JsonObject.class);
  60 + }
  61 + }
  62 + }
  63 + JsonObject jsonObject = GsonConstructor.get().fromJson(new String(payload),JsonObject.class);
  64 + ApiClientRePlyDto apiClientRePlyDto = new ApiClientRePlyDto();
  65 + if(jsonObject.has("result") && "1".equals(jsonObject.get("result").getAsString()))
  66 + {
  67 + apiClientRePlyDto.setCode(MessageCode.DEFAULT_SUCCESS_CODE);
  68 + apiClientRePlyDto.setMessage("操作成功,等待"+parserDeviceHostDto.getDevice_life()+"秒后返回结果");
  69 + }
  70 + return new AnalysisResult(false, true, data, apiClientRePlyDto);
  71 + default:
  72 + return new AnalysisResult(false,false,null);
  73 + }
  74 + }
  75 +
  76 + private String toformatPayLoad(ParserDeviceHostDto parserDeviceHostDto, JsonObject jsonObject)
  77 + {
  78 + if(null != parserDeviceHostDto && null != parserDeviceHostDto.getIotProduct())
  79 + {
  80 +// Integer iotProductid = parserDeviceHostDto.getIotProduct().getId();
  81 + JsonObject rj = new JsonObject();
  82 + for (String key : jsonObject.keySet())
  83 + {
  84 + String[] keyArr = key.split("_");
  85 + String identifier = keyArr[1];
  86 + String temp_data_identifier = keyArr[keyArr.length-1];
  87 + String data_identifier = temp_data_identifier;
  88 +
  89 + String number = "0";
  90 + String number_name = "PLC主机";
  91 + if ("SYS".equals(identifier))
  92 + {
  93 + }else{
  94 + IotProductPlcDevice iotProductPlcDevice = PLCDeviceChannelRelationConfig.getChannelRelation(identifier);
  95 + if(null == iotProductPlcDevice)
  96 + {
  97 + System.out.println("未找到设备通道关系:"+identifier);
  98 + continue;
  99 + }
  100 + number = keyArr[0]+"_"+iotProductPlcDevice.getNumbers().split(",")[0]+"_"+keyArr[2];
  101 + number_name = keyArr[0]+"区"+keyArr[2]+"号"+iotProductPlcDevice.getName()+keyArr[2];
  102 + if("YEL".equals(identifier))
  103 + {
  104 + int type = jsonObject.get(key.replace("SENSOR","TYPE")).getAsInt();
  105 + if(type==0)
  106 + {
  107 + continue;
  108 + }
  109 + number += "_"+keyArr[3];
  110 + number_name += keyArr[3]+"号探头";
  111 + if (data_identifier.equals("SENSOR"))
  112 + {
  113 + switch (type)
  114 + {
  115 + case 3:
  116 + data_identifier = "pH";
  117 + break;
  118 + case 5:
  119 + data_identifier = "dom";
  120 + break;
  121 + case 16:
  122 + data_identifier = "yw";
  123 + break;
  124 + case 17:
  125 + data_identifier = "yl";
  126 + break;
  127 + }
  128 + }
  129 + }
  130 + }
  131 +
  132 + if(StringUtils.isNotEmpty(number))
  133 + {
  134 + JsonObject numberjson = rj.getAsJsonObject(number);
  135 + if(null == numberjson)
  136 + {
  137 + numberjson = new JsonObject();
  138 + if(StringUtils.isNoneBlank(number_name))
  139 + {
  140 + numberjson.addProperty("%sysdeviceinfoname%",number_name);
  141 + }
  142 + rj.add(number,numberjson);
  143 + }
  144 + numberjson.add(data_identifier,jsonObject.get(key).isJsonNull()?new JsonPrimitive(0):jsonObject.get(key));
  145 +
  146 + }
  147 + }
  148 + return rj.toString();
  149 + }
  150 + return new JsonObject().toString();
  151 + }
  152 +}
  1 +package com.zhonglai.luhui.device.protocol.mnplc.analysis.topic;
  2 +
  3 +import com.google.gson.JsonObject;
  4 +import com.ruoyi.common.utils.GsonConstructor;
  5 +import com.zhonglai.luhui.device.protocol.factory.dto.AnalysisResult;
  6 +
  7 +public class AddPost {
  8 + public AnalysisResult analysisPayload( byte[] payload)
  9 + {
  10 + return analysisPayload(new String(payload));
  11 + }
  12 +
  13 + public AnalysisResult analysisPayload(String payload)
  14 + {
  15 + JsonObject jsonObject = GsonConstructor.get().fromJson(payload, JsonObject.class);
  16 + return new AnalysisResult(false,false,jsonObject);
  17 + }
  18 +}
  1 +package com.zhonglai.luhui.device.protocol.mnplc.control;
  2 +
  3 +import com.google.gson.JsonElement;
  4 +import com.google.gson.JsonObject;
  5 +import com.google.gson.reflect.TypeToken;
  6 +import com.ruoyi.common.utils.DateUtils;
  7 +import com.ruoyi.common.utils.GsonConstructor;
  8 +import com.zhonglai.luhui.device.analysis.comm.factory.Topic;
  9 +import com.zhonglai.luhui.device.domain.IotProduct;
  10 +import com.zhonglai.luhui.device.domain.IotProductPlcDevice;
  11 +import com.zhonglai.luhui.device.protocol.factory.config.DeviceCach;
  12 +import com.zhonglai.luhui.device.protocol.factory.config.PLCDeviceChannelRelationConfig;
  13 +import com.zhonglai.luhui.device.protocol.factory.control.DeviceCommandServiceFactory;
  14 +import com.zhonglai.luhui.device.protocol.factory.dto.NoticeMessageDto;
  15 +import com.zhonglai.luhui.device.protocol.factory.dto.ParserDeviceHostDto;
  16 +import org.slf4j.Logger;
  17 +import org.slf4j.LoggerFactory;
  18 +
  19 +import java.lang.reflect.Type;
  20 +import java.util.Map;
  21 +
  22 +/**
  23 + * 指令控制监听实现
  24 + */
  25 +public class DeviceCommandListenServiceImpl implements DeviceCommandServiceFactory {
  26 + private static final String topicModel = "/{{roleid}}/{{username}}/{{clientid}}/{{payloadtype}}/{{topicType}}";
  27 +
  28 + private static final Logger log = LoggerFactory.getLogger(DeviceCommandListenServiceImpl.class);
  29 + @Override
  30 + public NoticeMessageDto read(String deviceId, JsonObject jsonObject) {
  31 + return null;
  32 + }
  33 +
  34 + @Override
  35 + public NoticeMessageDto write(String deviceId, JsonObject jsonObject) {
  36 + ParserDeviceHostDto parserDeviceHostDto = DeviceCach.getDeviceHost(deviceId);
  37 +
  38 + Topic topic = getWriteTopic(deviceId,parserDeviceHostDto.getIotProduct());
  39 + NoticeMessageDto noticeMessageDto = new NoticeMessageDto();
  40 + noticeMessageDto.setTopic(topic);
  41 + noticeMessageDto.setTopicconfig(topicModel);
  42 +
  43 + //将jsonObject里面的两层键值比如:{"1":{"key","value","key1",1}}提取成一层放在commdjson里面如{"key":"value","key1":1}
  44 + JsonObject commdjson = flattenJsonObject(jsonObject);
  45 +
  46 + noticeMessageDto.setCommd(commdjson.toString().getBytes());
  47 +
  48 + return noticeMessageDto;
  49 + }
  50 +
  51 + @Override
  52 + public NoticeMessageDto notice(String deviceId, JsonObject jsonObject) {
  53 + return null;
  54 + }
  55 +
  56 + @Override
  57 + public NoticeMessageDto host(String deviceId, JsonObject jsonObject) {
  58 + ParserDeviceHostDto parserDeviceHostDto = DeviceCach.getDeviceHost(deviceId);
  59 +
  60 + Topic topic = new Topic();
  61 + topic.setTopicType("HOST");
  62 + topic.setClientid(deviceId);
  63 + topic.setRoleid(parserDeviceHostDto.getIotProduct().getRole_id()+"");
  64 + topic.setUsername(parserDeviceHostDto.getIotProduct().getMqtt_username());
  65 + topic.setPayloadtype("Json");
  66 + topic.setMessageid(DateUtils.getNowTimeMilly()+"");
  67 +
  68 + NoticeMessageDto noticeMessageDto = new NoticeMessageDto();
  69 + noticeMessageDto.setTopic(topic);
  70 + noticeMessageDto.setTopicconfig(topicModel);
  71 +
  72 + noticeMessageDto.setCommd(jsonObject.toString().getBytes());
  73 +
  74 + return noticeMessageDto;
  75 + }
  76 +
  77 + /**
  78 + * 将嵌套的JsonObject扁平化。
  79 + *
  80 + * @param jsonObject 嵌套的JsonObject
  81 + * @return 扁平化的JsonObject
  82 + */
  83 + public JsonObject flattenJsonObject(JsonObject jsonObject) {
  84 + // 使用Gson将嵌套的JsonObject转换为Map
  85 + Type typeOfMap = new TypeToken<Map<String, JsonElement>>(){}.getType();
  86 + Map<String, JsonElement> map = GsonConstructor.get().fromJson(jsonObject, typeOfMap);
  87 +
  88 + // 创建一个新的JsonObject用于存储扁平化的结果
  89 + JsonObject flatJson = new JsonObject();
  90 +
  91 + // 遍历Map中的所有键值对
  92 + for (Map.Entry<String, JsonElement> entry : map.entrySet()) {
  93 + JsonElement value = entry.getValue();
  94 +
  95 + String key = entry.getKey();
  96 + // 检查值是否为JsonObject
  97 + if (value.isJsonObject()) {
  98 + // 如果是JsonObject,则将其键值对添加到flatJson中
  99 + JsonObject innerJson = value.getAsJsonObject();
  100 + for (Map.Entry<String, JsonElement> innerEntry : innerJson.entrySet()) {
  101 + flatJson.add(reverseKey(key,innerEntry.getKey()), innerEntry.getValue());
  102 + }
  103 + } else {
  104 + // 如果不是JsonObject,则直接添加到flatJson中
  105 + flatJson.add(entry.getKey(), value);
  106 + }
  107 + }
  108 +
  109 + return flatJson;
  110 + }
  111 +
  112 + private Topic getWriteTopic(String deviceId, IotProduct iotProduct)
  113 + {
  114 + Topic topic = new Topic();
  115 + topic.setTopicType("PUT");
  116 + topic.setClientid(deviceId);
  117 + topic.setRoleid(iotProduct.getRole_id()+"");
  118 + topic.setUsername(iotProduct.getMqtt_username());
  119 + topic.setPayloadtype("Json");
  120 + return topic;
  121 + }
  122 +
  123 + /**
  124 + * number + dataIdentifier 逆向解析为原始上报 key
  125 + *
  126 + * 支持:
  127 + * SYS → A_SYS_1_temp
  128 + * YEL 探头 → A_YEL_1_SENSOR_3 和 A_YEL_1_TYPE_3
  129 + * 普通设备 → A_ABC_1_value
  130 + */
  131 + private String reverseKey(String number, String dataIdentifier)
  132 + {
  133 + String[] arr = number.split("_");
  134 + if (arr.length < 3) {
  135 + return null;
  136 + }
  137 +
  138 + String area = arr[0]; // A
  139 + String mappedNumber = arr[1]; // 09
  140 + String index = arr[2]; // 1
  141 + String probe = arr.length > 3 ? arr[3] : null; // 探头编号(如果有)
  142 +
  143 + // 反向查找设备类型(identifier)
  144 + String identifier = getIdentifierByMappedNumber(mappedNumber);
  145 + if (identifier == null) {
  146 + return null;
  147 + }
  148 +
  149 + // ---------- SYS 类型 ----------
  150 + if ("SYS".equals(identifier))
  151 + {
  152 + // 生成:A_SYS_1_xxx
  153 + return area + "_SYS_" + index + "_" + dataIdentifier;
  154 + }
  155 +
  156 + // ---------- YEL(探头设备) ----------
  157 + if ("YEL".equals(identifier))
  158 + {
  159 + if (probe == null) {
  160 + // YEL 必须包含探头编号
  161 + return null;
  162 + }
  163 +
  164 + // dataIdentifier 是业务数据(例如 pH、doh-set)
  165 + // 原始数据分为 SENSOR_xxx + TYPE_xxx
  166 + // 本方法根据 dataIdentifier 只恢复 SENSOR_xxx
  167 + return area + "_YEL_" + index + "_" + probe + "_" + dataIdentifier;
  168 + }
  169 +
  170 + // ---------- 普通设备 ----------
  171 + // 例如:A_ABC_1_value
  172 + return area + "_" + identifier + "_" + index
  173 + + (probe != null ? "_" + probe : "")
  174 + + "_" + dataIdentifier;
  175 + }
  176 +
  177 + /**
  178 + * 根据 PLC 设备 numbers 值反推 identifier。
  179 + */
  180 + private String getIdentifierByMappedNumber(String mappedNumber)
  181 + {
  182 + for (String identifier : PLCDeviceChannelRelationConfig.getAllIdentifiers())
  183 + {
  184 + IotProductPlcDevice dev = PLCDeviceChannelRelationConfig.getChannelRelation(identifier);
  185 + if (dev == null) continue;
  186 +
  187 + for (String num : dev.getNumbers().split(",")) {
  188 + if (num.equals(mappedNumber)) {
  189 + return identifier;
  190 + }
  191 + }
  192 + }
  193 + return null;
  194 + }
  195 +
  196 +}
  1 +package com.zhonglai.luhui.device.protocol.mnplc.purification;
  2 +
  3 +import com.alibaba.fastjson.JSONObject;
  4 +import com.google.gson.JsonElement;
  5 +import com.google.gson.JsonObject;
  6 +import com.ruoyi.common.utils.GsonConstructor;
  7 +import com.ruoyi.common.utils.spring.SpringUtils;
  8 +import com.zhonglai.luhui.device.analysis.comm.dto.DeviceSensorData;
  9 +import com.zhonglai.luhui.device.analysis.comm.dto.thingsmodels.ThingsModelDataTypeEnum;
  10 +import com.zhonglai.luhui.device.analysis.comm.dto.thingsmodels.ThingsModelItemBase;
  11 +import com.zhonglai.luhui.device.analysis.comm.factory.Topic;
  12 +import com.zhonglai.luhui.device.analysis.comm.util.DateUtils;
  13 +import com.zhonglai.luhui.device.domain.IotThingsModel;
  14 +import com.zhonglai.luhui.device.protocol.factory.dto.*;
  15 +import com.zhonglai.luhui.device.protocol.factory.purification.ProtocolPurificationFactory;
  16 +import com.zhonglai.luhui.device.protocol.factory.service.IotThingsModelService;
  17 +import org.apache.commons.lang3.EnumUtils;
  18 +
  19 +import java.util.ArrayList;
  20 +import java.util.List;
  21 +
  22 +/**
  23 + * 默认的清洗服务
  24 + */
  25 +public class MNPLCProtocolPurificationFactoryImpl implements ProtocolPurificationFactory {
  26 +
  27 + @Override
  28 + public ProtocolPurificationModel purification(Integer product_id,Topic topic, AnalysisResult analysisResult ) {
  29 + JsonObject protocolParserModel = analysisResult.getJsonObject();
  30 + if(null != protocolParserModel && protocolParserModel.size()!=0)
  31 + {
  32 + ProtocolPurificationModel protocolPurificationModel = new ProtocolPurificationModel();
  33 + ParserDeviceHostDto parserDeviceHostDto = analysisHost(product_id,topic,protocolParserModel,protocolPurificationModel.getDeviceSensorDataList());
  34 + protocolPurificationModel.setParserDeviceHostDto(parserDeviceHostDto);
  35 +
  36 + List<ParserDeviceInfoDto> parserDeviceInfoDtoList = analysisDeviceInfo(parserDeviceHostDto.getUpdateTime(),product_id,topic,protocolParserModel,protocolPurificationModel.getDeviceSensorDataList());
  37 + protocolPurificationModel.setParserDeviceInfoDtoList(parserDeviceInfoDtoList);
  38 + return protocolPurificationModel;
  39 + }
  40 + return null;
  41 + }
  42 +
  43 + private ParserDeviceHostDto analysisHost(Integer product_id,Topic topic, JsonObject data,List<DeviceSensorData> deviceSensorDataList )
  44 + {
  45 + int time = DateUtils.getNowTimeMilly();
  46 + if(data.has("0"))
  47 + {
  48 + JsonObject jsonObjectData = data.get("0").getAsJsonObject();
  49 + JsonObject summary = null;
  50 + //解析设备摘要
  51 + if(jsonObjectData.has("summary") && null != jsonObjectData.get("summary") && jsonObjectData.get("summary").isJsonObject())
  52 + {
  53 + summary = jsonObjectData.get("summary").getAsJsonObject();
  54 + jsonObjectData.remove("summary");
  55 + }
  56 +
  57 + String online = null;
  58 + //解析在线状态
  59 + if(jsonObjectData.has("online") && null != jsonObjectData.get("online"))
  60 + {
  61 + online = jsonObjectData.get("online").getAsString();
  62 + jsonObjectData.remove("online");
  63 + }
  64 +
  65 + //信号
  66 + Integer rssi = null;
  67 + if(jsonObjectData.has("rssi") && null != jsonObjectData.get("rssi"))
  68 + {
  69 + rssi = jsonObjectData.get("rssi").getAsInt();
  70 + jsonObjectData.remove("rssi");
  71 + }
  72 +
  73 + ParserDeviceHostDto parserDeviceHostDto = null;
  74 + if(null != jsonObjectData && jsonObjectData.size() != 0)
  75 + {
  76 + parserDeviceHostDto = analysisJsonData(time,"0",product_id,topic,jsonObjectData).pushDeviceSensorData(deviceSensorDataList).toParserDeviceHostDto(topic.getClientid(),time);
  77 + }
  78 + if(null == parserDeviceHostDto)
  79 + {
  80 + parserDeviceHostDto = new ParserDeviceHostDto();
  81 + parserDeviceHostDto.setId(topic.getClientid());
  82 + }
  83 + if(null != summary && summary.size() != 0)
  84 + {
  85 + parserDeviceHostDto.setSummary(summary);
  86 + }
  87 + parserDeviceHostDto.setOnline(online);
  88 + parserDeviceHostDto.setRssi(rssi);
  89 + parserDeviceHostDto.setUpdateTime(time);
  90 + return parserDeviceHostDto;
  91 + }
  92 + ParserDeviceHostDto parserDeviceHostDto = new ParserDeviceHostDto();
  93 + parserDeviceHostDto.setId(topic.getClientid());
  94 + parserDeviceHostDto.setUpdateTime(time);
  95 + return parserDeviceHostDto;
  96 + }
  97 +
  98 + private List<ParserDeviceInfoDto> analysisDeviceInfo(Integer time,Integer product_id,Topic topic, JsonObject data,List<DeviceSensorData> deviceSensorDataList)
  99 + {
  100 + List<ParserDeviceInfoDto> list = new ArrayList<>();
  101 +
  102 + for (String key:data.keySet())
  103 + {
  104 + if(!key.equals("0") && data.get(key).isJsonObject())
  105 + {
  106 + ParserDeviceInfoDto parserDeviceInfoDto = analysisJsonData(time,key,product_id,topic,data.get(key).getAsJsonObject()).pushDeviceSensorData(deviceSensorDataList).toParserDeviceInfoDto(topic.getClientid()+"_"+key,time);
  107 + list.add(parserDeviceInfoDto);
  108 + }
  109 + }
  110 +
  111 + return list;
  112 + }
  113 +
  114 + private AnalysisDto analysisJsonData(Integer time,String sensorNumber,Integer product_id,Topic topic, JsonObject jsonObjectData )
  115 + {
  116 + if(null != jsonObjectData && jsonObjectData.size() !=0)
  117 + {
  118 + AnalysisDto analysisDto = new AnalysisDto();
  119 +
  120 + JsonObject things_model_value = new JsonObject(); //存放数据
  121 + analysisDto.setThings_model_value(things_model_value);
  122 +
  123 + JsonObject things_model_config = new JsonObject(); //存放配置
  124 + analysisDto.setThings_model_config(things_model_config);
  125 + for (String key: jsonObjectData.keySet())
  126 + {
  127 + IotThingsModel thingsModel = SpringUtils.getBean(IotThingsModelService.class).getIotThingsModel(product_id, key);
  128 +
  129 + JsonElement jsonElement = jsonObjectData.get(key);
  130 + if(null != jsonElement && !jsonElement.isJsonNull() )
  131 + {
  132 + ThingsModelItemBase thingsModelItemBase = getThingsModelItemBase(thingsModel,jsonElement);
  133 + if(null == thingsModelItemBase || !thingsModelItemBase.checkValue())
  134 + {
  135 + continue;
  136 + }
  137 +
  138 + switch (thingsModel.getIs_config())
  139 + {
  140 + case 0:
  141 + things_model_value.add(key,GsonConstructor.get().fromJson(JSONObject.toJSONString(thingsModelItemBase),JsonObject.class));
  142 + break;
  143 + case 1:
  144 + things_model_config.add(key,GsonConstructor.get().fromJson(JSONObject.toJSONString(thingsModelItemBase),JsonObject.class));
  145 + break;
  146 + case 2:
  147 + things_model_config.add(key,GsonConstructor.get().fromJson(JSONObject.toJSONString(thingsModelItemBase),JsonObject.class));
  148 + things_model_value.add(key,GsonConstructor.get().fromJson(JSONObject.toJSONString(thingsModelItemBase),JsonObject.class));
  149 + break;
  150 + default:
  151 + things_model_value.add(key,GsonConstructor.get().fromJson(JSONObject.toJSONString(thingsModelItemBase),JsonObject.class));
  152 + break;
  153 + }
  154 +
  155 + //记录数据日志
  156 + if(1==thingsModelItemBase.getIs_save_log() )
  157 + {
  158 + List<DeviceSensorData> deviceSensorDataList = analysisDto.getDeviceSensorDataList();
  159 + if(null == deviceSensorDataList)
  160 + {
  161 + deviceSensorDataList = new ArrayList<>();
  162 + analysisDto.setDeviceSensorDataList(deviceSensorDataList);
  163 + }
  164 + deviceSensorDataList.add(getDeviceSensorData(time,sensorNumber,topic,thingsModel,thingsModelItemBase));
  165 + }
  166 + }
  167 + }
  168 +
  169 + return analysisDto;
  170 + }
  171 + return null;
  172 + }
  173 +
  174 + private ThingsModelItemBase getThingsModelItemBase(IotThingsModel thingsModel, JsonElement jsonElement) {
  175 + String dataType = thingsModel.getData_type();
  176 + ThingsModelDataTypeEnum thingsModelDataTypeEnum;
  177 +
  178 + try {
  179 + if (dataType == null) {
  180 + thingsModelDataTypeEnum = ThingsModelDataTypeEnum.STRING;
  181 + } else {
  182 + // 统一转大写再校验
  183 + String upperType = dataType.toUpperCase();
  184 + if (EnumUtils.isValidEnum(ThingsModelDataTypeEnum.class, upperType)) {
  185 + thingsModelDataTypeEnum = Enum.valueOf(ThingsModelDataTypeEnum.class, upperType);
  186 + } else {
  187 + thingsModelDataTypeEnum = ThingsModelDataTypeEnum.STRING;
  188 + }
  189 + }
  190 + } catch (IllegalArgumentException | NullPointerException e) {
  191 + // 容错处理,回退到 STRING
  192 + thingsModelDataTypeEnum = ThingsModelDataTypeEnum.STRING;
  193 + }
  194 +
  195 + return ThingsModelItemBase.newhingsModel(thingsModelDataTypeEnum, thingsModel, jsonElement);
  196 + }
  197 +
  198 +
  199 + private DeviceSensorData getDeviceSensorData(Integer time,String sensorNumber,Topic topic,IotThingsModel thingsModel,ThingsModelItemBase thingsModelItemBase)
  200 + {
  201 + DeviceSensorData sensorData = new DeviceSensorData();
  202 + sensorData.setDataType(thingsModel.getIdentifier());
  203 + sensorData.setDataValue(thingsModelItemBase.getSaveView());
  204 + sensorData.setCreatTime(time);
  205 + sensorData.setDeviceModel(topic.getUsername());
  206 + if("0".equals(sensorNumber))
  207 + {
  208 + sensorData.setDeviceInfoId(topic.getClientid());
  209 + }else {
  210 + sensorData.setDeviceInfoId(topic.getClientid()+"_"+sensorNumber);
  211 + }
  212 + return sensorData;
  213 +
  214 + }
  215 +
  216 +}
  1 +# 设备命名规范文档
  2 +
  3 +## 一、命名总体原则
  4 +
  5 +本命名规范用于设备点位、监测量、状态量等字段的统一命名规则。所有命名中的**英文全部使用大写**
  6 +
  7 +更新后的命名结构:
  8 +
  9 +```
  10 +区域_前缀_编号_参数名
  11 +```
  12 +
  13 +### 1. 区域
  14 +
  15 +* 使用区域代号,如 A、B、C 或 01、02。
  16 +* 用于区分设备所在区域、池体或生产单元。
  17 +
  18 +### 2. 前缀
  19 +
  20 +* 依据设备类型使用标准化英文缩写(全部大写)。
  21 +* 同类设备保持一致前缀。
  22 +
  23 +### 3. 编号
  24 +
  25 +* 使用阿拉伯数字编号,如 1、2、3。
  26 +* 若设备区域内为单台,可约定编号为 1。
  27 +
  28 +### 4. 参数名
  29 +
  30 +常用参数名如下:
  31 +
  32 +* RUN:运行状态(0 停止 / 1 运行)
  33 +* ALARM:报警状态
  34 +* STATUS:综合状态
  35 +* VALUE:数值型采集量(如水位、流量等)
  36 +* LEVEL:液位值
  37 +* MODE:运行模式
  38 +* OTHER:现场扩展参数
  39 +
  40 +格式示例:`A_TLJ_1_RUN`
  41 +
  42 +---
  43 +
  44 +## 二、设备命名前缀及示例
  45 +
  46 +以下为设备的标准命名前缀、示例及说明(采用区域 A 示例):
  47 +
  48 +| 设备名称 | 前缀(大写) | 示例 | 示例说明 |
  49 +|--------|--------|-----------------|------------------------------|
  50 +| 系统级别参数 | SYS | A_SYS_0_SZGD | A 区 市政电源供电信号,<br/>这里的编号0表示不是plc下挂 |
  51 +| 喂料机 | TLJ | A_TLJ_1_RUN | A 区 1 号喂料机运行状态 |
  52 +| 增氧机 | ZYJ | A_ZYJ_2_ALARM | A 区 2 号增氧机告警状态 |
  53 +| 排污 | PW | A_PW_1_RUN | A 区 1 号排污设备运行状态 |
  54 +| 曝气 | PQ | A_PQ_1_RUN | A 区 1 号曝气设备运行状态 |
  55 +| 流量计 | LLJ | A_LLJ_1_VALUE | A 区 1 号流量计读数 |
  56 +| 水位计 | SWJ | A_SWJ_1_VALUE | A 区 1 号水位计水位值 |
  57 +| 推水机 | TSJ | A_TSJ_1_RUN | A 区 1 号推水机运行状态 |
  58 +| 鱼儿乐 | YEL | A_YEL_1_RUN | A 区 1 号鱼儿乐运行状态 |
  59 +| 中转泵 | ZZB | A_ZZB_1_RUN | A 区 1 号中转泵运行状态 |
  60 +| 中转池 | ZZC | A_ZZC_1_LEVEL | A 区 1 号中转池液位 |
  61 +| 微滤机 | WLJ | A_WLJ_1_RUN | A 区 1 号微滤机运行状态 |
  62 +| 循环水池 | XHSC | A_XHSC_1_LEVEL | A 区 1 号循环水池液位 |
  63 +| 杀菌 | SJ | A_SJ_1_RUN | A 区 1 号杀菌设备运行状态 |
  64 +| 生物滤筒 | SWLT | A_SWLT_1_RUN | A 区 1 号生物滤筒运行状态 |
  65 +| 循环水泵 | XHSB | A_XHSB_1_RUN | A 区 1 号循环水泵运行状态 |
  66 +| 底增氧风机 | DZYFJ | A_DZYFJ_1_RUN | A 区 1 号底部增氧风机运行状态 |
  67 +| 推水风机 | TSFJ | A_TSFJ_1_RUN | A 区 1 号推水风机运行状态 |
  68 +| 漩涡风机 | XWFJ | A_XWFJ_1_RUN | A 区 1 号漩涡风机运行状态 |
  69 +| 蛋分射流泵 | DFSLB | A_DFSLB_1_RUN | A 区 1 号蛋分射流泵运行状态 |
  70 +| 系统控制柜 | XTKZG | A_XTKZG_1_STATUS | A 区 1 号系统控制柜综合状态 |
  71 +| 紫外线杀菌器 | UV | A_UV_1_RUN | A 区 1 号紫外线杀菌器运行状态 |
  72 +| 变频控制柜 | BPKZG | A_BPKZG_1_STATUS | A 区 1 号变频控制柜状态 |
  73 +| 液位计 | YWJ | A_YWJ_1_VALUE | A 区 1 号液位计读数 |
  74 +| 热泵 | RB | A_RB_1_RUN | A 区 1 号热泵运行状态 |
  75 +---
  76 +
  77 +## 三、命名示例说明
  78 +
  79 +### 1. 状态型
  80 +
  81 +```
  82 +B_TSJ_3_RUN
  83 +```
  84 +
  85 +表示:B 区 3 号推水机运行状态
  86 +
  87 +### 2. 数值型
  88 +
  89 +```
  90 +C_LLJ_2_VALUE
  91 +```
  92 +
  93 +表示:C 区 2 号流量计流量值
  94 +
  95 +### 3. 控制柜类
  96 +
  97 +```
  98 +A_XTKZG_1_STATUS
  99 +```
  100 +
  101 +表示:A 区 1 号系统控制柜综合状态
  102 +
  103 +---
  104 +
  105 +## 四、扩展说明
  106 +
  107 +### (新增)5. 多通道/多子设备扩展能力
  108 +
  109 +针对含 **主机 + 多个传感器通道** 的设备(如鱼儿乐 YEL),在原有命名规则基础上增加 **子编号**,形成更完整的多级结构:
  110 +
  111 +```
  112 +区域_前缀_设备编号_子编号_参数名
  113 +```
  114 +
  115 +#### 子编号规则:
  116 +
  117 +* `0` = 主机本体
  118 +* `1~N` = 各传感器通道
  119 +
  120 +#### 扩展示例:
  121 +
  122 +| 点位示例 | 含义说明 |
  123 +|----------------| ------------------- |
  124 +| A_YEL_1_0_alarm | A 区 1 号鱼儿乐主机告警 |
  125 +| A_YEL_1_0_at | A 区 1 号鱼儿乐主机气温 |
  126 +| A_YEL_1_0_bar | A 区 1 号鱼儿乐主机气压 |
  127 +| A_YEL_1_1_TYPE | A 区 1 号鱼儿乐 1 号传感器类型 |
  128 +| A_YEL_1_1_alarm | A 区 1 号鱼儿乐 1 号传感器告警 |
  129 +| A_YEL_1_1_wt | A 区 1 号鱼儿乐 1 号传感器水温 |
  130 +
  131 +##### 常用参数名如下:
  132 +
  133 +###### 可读参数:
  134 +* wt:温度
  135 +* dom:溶氧
  136 +* alarm:故障代码
  137 +* state:控制器状态码
  138 +* at:气温
  139 +* bar:气压
  140 +* mode:控制器模式
  141 +* nh3n:氨氮
  142 +* pH:PH
  143 +* nitrite:亚硝酸盐
  144 +* bga:藻类
  145 +* yl:压力
  146 +* ls:流速
  147 +* sw:水位
  148 +* 14:信号强度(0-30)
  149 +* cod:COD
  150 +* sal:盐度
  151 +* dop:溶氧百分比/溶氧饱和度
  152 +* yw:液位
  153 +* orp:ORP
  154 +* turbidity:浊度
  155 +* co2:二氧化碳
  156 +* doh:溶氧高限
  157 +* dou:溶氧上限
  158 +* dol:溶氧下限
  159 +* doa:溶氧警戒线
  160 +###### 可写参数:
  161 +* doh-set:溶氧高限
  162 +* dou-set:溶氧上限
  163 +* dol-set:溶氧下限
  164 +* doa-set:溶氧警戒线
  165 +
  166 +###### 定制参数:
  167 +* SENSOR:定制传感器数据,需要根据TYPE来定义数据类型,比如:A_YEL_1_1_SENSOR 表示 A 区 1 号鱼儿乐1号传感器定制的传感器数据,需要根据TYPE来定义数据类型
  168 +
  169 +此扩展适用于所有具备:主机 + 多个测量通道、多探头结构、模块化采集单元等设备类型。
  170 +
  171 +1. 区域可根据实际项目划分,如区域编码、池号、车间编号。
  172 +2. 若未来新增设备类型,可继续依照前缀规范扩展。
  173 +3. 所有英文保持大写,确保统一性与可识别性。
  174 +
  175 +---
@@ -25,5 +25,6 @@ @@ -25,5 +25,6 @@
25 <module>lh-device-defaul</module> 25 <module>lh-device-defaul</module>
26 <module>lh-device-6wp</module> 26 <module>lh-device-6wp</module>
27 <module>lh-device-wendubao</module> 27 <module>lh-device-wendubao</module>
  28 + <module>lh-device-mn-plc</module>
28 </modules> 29 </modules>
29 </project> 30 </project>
  1 +package com.zhonglai.luhui.device.protocol.factory.config;
  2 +
  3 +import com.zhonglai.luhui.device.analysis.comm.dao.BaseDao;
  4 +import com.zhonglai.luhui.device.domain.IotProductPlcDevice;
  5 +
  6 +import java.util.HashMap;
  7 +import java.util.Map;
  8 +import java.util.Set;
  9 +
  10 +public class PLCDeviceChannelRelationConfig {
  11 + private static Map<String, IotProductPlcDevice> channelRelationMap = new HashMap<String, IotProductPlcDevice>();
  12 + public static IotProductPlcDevice getChannelRelation(String channel) {
  13 + return channelRelationMap.get(channel);
  14 + }
  15 +
  16 + public static void addChannelRelation(String channel, IotProductPlcDevice relation) {
  17 + channelRelationMap.put(channel, relation);
  18 + }
  19 +
  20 + public static Set<String> getAllIdentifiers()
  21 + {
  22 + return channelRelationMap.keySet();
  23 + }
  24 +}
@@ -65,6 +65,9 @@ public class SysCommandListenService implements RocketMQReplyListener<MessageExt @@ -65,6 +65,9 @@ public class SysCommandListenService implements RocketMQReplyListener<MessageExt
65 case upProductPayloadModelNumber: 65 case upProductPayloadModelNumber:
66 iotThingsModelService.upProductPayloadModelNumberCach(deviceCommand.getData().get("product_ids").getAsInt()); 66 iotThingsModelService.upProductPayloadModelNumberCach(deviceCommand.getData().get("product_ids").getAsInt());
67 return new Message(MessageCode.DEFAULT_SUCCESS_CODE,"更新产品模型编号关系成功"); 67 return new Message(MessageCode.DEFAULT_SUCCESS_CODE,"更新产品模型编号关系成功");
  68 + case upProductPlcDevice:
  69 + iotThingsModelService.upProductPlcDeviceCach(deviceCommand.getData().get("identifier").getAsString());
  70 + return new Message(MessageCode.DEFAULT_SUCCESS_CODE,"更新产品模型编号关系成功");
68 case delSubscribe: //取消订阅 71 case delSubscribe: //取消订阅
69 mqttSubscribeService.unSubscribe(deviceCommand.getData().get("ip").getAsString(),deviceCommand.getData().get("product_ids").getAsString()); 72 mqttSubscribeService.unSubscribe(deviceCommand.getData().get("ip").getAsString(),deviceCommand.getData().get("product_ids").getAsString());
70 default: 73 default:
@@ -39,6 +39,11 @@ public enum CommandType { @@ -39,6 +39,11 @@ public enum CommandType {
39 upProductPayloadModelNumber, 39 upProductPayloadModelNumber,
40 40
41 /** 41 /**
  42 + * 流水鱼plc设备
  43 + */
  44 + upProductPlcDevice,
  45 +
  46 + /**
42 * 删除订阅 47 * 删除订阅
43 */ 48 */
44 delSubscribe, 49 delSubscribe,
@@ -9,8 +9,10 @@ import com.zhonglai.luhui.device.analysis.comm.dto.thingsmodels.ThingsModelBase; @@ -9,8 +9,10 @@ import com.zhonglai.luhui.device.analysis.comm.dto.thingsmodels.ThingsModelBase;
9 import com.zhonglai.luhui.device.analysis.comm.dto.thingsmodels.ThingsModelDataTypeEnum; 9 import com.zhonglai.luhui.device.analysis.comm.dto.thingsmodels.ThingsModelDataTypeEnum;
10 import com.zhonglai.luhui.device.analysis.comm.dto.thingsmodels.ThingsModelItemBase; 10 import com.zhonglai.luhui.device.analysis.comm.dto.thingsmodels.ThingsModelItemBase;
11 import com.zhonglai.luhui.device.domain.IotProductPayloadModelNumber; 11 import com.zhonglai.luhui.device.domain.IotProductPayloadModelNumber;
  12 +import com.zhonglai.luhui.device.domain.IotProductPlcDevice;
12 import com.zhonglai.luhui.device.domain.IotProductTranslate; 13 import com.zhonglai.luhui.device.domain.IotProductTranslate;
13 import com.zhonglai.luhui.device.domain.IotThingsModel; 14 import com.zhonglai.luhui.device.domain.IotThingsModel;
  15 +import com.zhonglai.luhui.device.protocol.factory.config.PLCDeviceChannelRelationConfig;
14 import com.zhonglai.luhui.device.protocol.factory.config.ProductPayloadModelNumberCach; 16 import com.zhonglai.luhui.device.protocol.factory.config.ProductPayloadModelNumberCach;
15 import org.apache.commons.lang3.EnumUtils; 17 import org.apache.commons.lang3.EnumUtils;
16 import org.springframework.beans.factory.annotation.Autowired; 18 import org.springframework.beans.factory.annotation.Autowired;
@@ -53,6 +55,8 @@ public class IotThingsModelService { @@ -53,6 +55,8 @@ public class IotThingsModelService {
53 } 55 }
54 56
55 upProductPayloadModelNumberCach(null); 57 upProductPayloadModelNumberCach(null);
  58 +
  59 + upProductPlcDeviceCach(null);
56 } 60 }
57 61
58 62
@@ -138,6 +142,18 @@ public class IotThingsModelService { @@ -138,6 +142,18 @@ public class IotThingsModelService {
138 } 142 }
139 } 143 }
140 144
  145 + public void upProductPlcDeviceCach(String identifier)
  146 + {
  147 + List<IotProductPlcDevice> list = persistenceDBService.getProductPlcDeviceList(identifier);
  148 + if (null != list && list.size() != 0)
  149 + {
  150 + for (IotProductPlcDevice iotProductPlcDevice:list)
  151 + {
  152 + PLCDeviceChannelRelationConfig.addChannelRelation(iotProductPlcDevice.getIdentifier(),iotProductPlcDevice);
  153 + }
  154 + }
  155 + }
  156 +
141 /** 157 /**
142 * 逆向翻译模式数据 158 * 逆向翻译模式数据
143 * @param product_id 159 * @param product_id
@@ -197,6 +197,11 @@ public class PersistenceDBService { @@ -197,6 +197,11 @@ public class PersistenceDBService {
197 return defaultDbService.getProductPayloadModelNumberList(product_id); 197 return defaultDbService.getProductPayloadModelNumberList(product_id);
198 } 198 }
199 199
  200 + public List<IotProductPlcDevice> getProductPlcDeviceList(String identifier)
  201 + {
  202 + return defaultDbService.getProductPlcDeviceList(identifier);
  203 + }
  204 +
200 /** 205 /**
201 * 获取默认数据库的操作服务 206 * 获取默认数据库的操作服务
202 * @return 207 * @return
@@ -288,6 +288,15 @@ public class DefaultDbService { @@ -288,6 +288,15 @@ public class DefaultDbService {
288 return baseDao.findBysql("SELECT * FROM `iot_product_payload_model_number` WHERE product_id=?", IotProductPayloadModelNumber.class,product_id); 288 return baseDao.findBysql("SELECT * FROM `iot_product_payload_model_number` WHERE product_id=?", IotProductPayloadModelNumber.class,product_id);
289 } 289 }
290 290
  291 + public List<IotProductPlcDevice> getProductPlcDeviceList(String identifier)
  292 + {
  293 + if (null == identifier)
  294 + {
  295 + return baseDao.findBysql("SELECT * FROM `iot_product_plc_device`", IotProductPlcDevice.class);
  296 + }
  297 + return baseDao.findBysql("SELECT * FROM `iot_product_plc_device` WHERE identifier=?", IotProductPlcDevice.class,identifier);
  298 + }
  299 +
291 public String getProductUsernames() 300 public String getProductUsernames()
292 { 301 {
293 return baseDao.findBysql("SELECT GROUP_CONCAT(mqtt_username) users FROM `iot_product`",String.class); 302 return baseDao.findBysql("SELECT GROUP_CONCAT(mqtt_username) users FROM `iot_product`",String.class);
@@ -29,11 +29,11 @@ mqtt: @@ -29,11 +29,11 @@ mqtt:
29 client: 29 client:
30 #客户端操作时间 30 #客户端操作时间
31 operationTime: 10 31 operationTime: 10
32 - productids: 13 32 + productids: 39,40
33 33
34 #rocketmq配置信息 34 #rocketmq配置信息
35 rocketmq: 35 rocketmq:
36 #nameservice服务器地址(多个以英文逗号隔开) 36 #nameservice服务器地址(多个以英文逗号隔开)
37 name-server: 8.129.224.117:9876 37 name-server: 8.129.224.117:9876
38 consumerGroup: lh-mqtt-service-listen${random.uuid} 38 consumerGroup: lh-mqtt-service-listen${random.uuid}
39 - operationToken: local_lh-mqtt-service-listen  
  39 + operationToken: localhost