作者 钟来

明牛协议解析完成

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