Sfoglia il codice sorgente

feature:与工控机程序通信获取工控机实时信息

yingjian.wu 4 mesi fa
parent
commit
f308ed2c5b

+ 6 - 5
src/main/java/com/qlm/controller/jinzai/ProdBatchController.java

@@ -47,7 +47,7 @@ public class ProdBatchController extends CommonController {
         //创建时间、工厂ID、产线ID、WMS状态
         //创建时间、工厂ID、产线ID、WMS状态
         String createTime = getPara("createTime");
         String createTime = getPara("createTime");
         String factoryId = getPara("factoryId");
         String factoryId = getPara("factoryId");
-        String lineId = getPara("lineId");
+        String productName = getPara("productName");
         String wmsStatus = getPara("wmsStatus");
         String wmsStatus = getPara("wmsStatus");
         try {
         try {
             StringBuilder whereSql = new StringBuilder(" from jinzai_upload_master where 1=1");
             StringBuilder whereSql = new StringBuilder(" from jinzai_upload_master where 1=1");
@@ -61,13 +61,14 @@ public class ProdBatchController extends CommonController {
 //                whereSql.append(" and factory_id = ?");
 //                whereSql.append(" and factory_id = ?");
 //                params.add(factoryId);
 //                params.add(factoryId);
 //            }
 //            }
-//            if(StrKit.notBlank(lineId)){
-//                whereSql.append(" and line_id = ?");
-//                params.add(lineId);
-//            }
+            if(StrKit.notBlank(productName)){
+                whereSql.append(" and pinxiang like ?");
+                params.add("%" + productName + "%");
+            }
 //            if(StrKit.notBlank(wmsStatus)){
 //            if(StrKit.notBlank(wmsStatus)){
 //                whereSql.append(" and wms_status = ?");
 //                whereSql.append(" and wms_status = ?");
 //            }
 //            }
+            whereSql.append(" order by create_time desc");
             Page<Record> paginate = Db.paginate(pageNumber, pageSize, "select *", whereSql.toString(), params.toArray());
             Page<Record> paginate = Db.paginate(pageNumber, pageSize, "select *", whereSql.toString(), params.toArray());
             if(paginate  == null){
             if(paginate  == null){
                 renderJson(new PageResult<>(0, pageNumber, pageSize, new ArrayList<>()));
                 renderJson(new PageResult<>(0, pageNumber, pageSize, new ArrayList<>()));

+ 12 - 0
src/main/java/com/qlm/jfinal/JfinalConfig.java

@@ -33,6 +33,7 @@ import com.qlm.entity.ScanRecord;
 import com.qlm.entity.User;
 import com.qlm.entity.User;
 import com.qlm.interceptor.GlobalHandler;
 import com.qlm.interceptor.GlobalHandler;
 import com.qlm.log.LogerFactory;
 import com.qlm.log.LogerFactory;
+import com.qlm.netty.NettyServer;
 import com.qlm.tongji.entity.Summary;
 import com.qlm.tongji.entity.Summary;
 import com.qlm.tools.core.ClassUtil;
 import com.qlm.tools.core.ClassUtil;
 //import com.qlm.controller.IndexController;
 //import com.qlm.controller.IndexController;
@@ -149,6 +150,17 @@ public class JfinalConfig extends JFinalConfig {
 		// TODO Auto-generated method stub
 		// TODO Auto-generated method stub
 		super.afterJFinalStart();
 		super.afterJFinalStart();
 		com.qlm.log.Log.info("当前服务启动成功");
 		com.qlm.log.Log.info("当前服务启动成功");
+	
+		// 启动Netty服务
+		new Thread(() -> {
+		    try {
+		        int port = 8888;
+		        NettyServer server = new NettyServer(port);
+		        server.start();
+		    } catch (InterruptedException e) {
+		        com.qlm.log.Log.error("Netty服务启动失败: {}", e);
+		    }
+		}).start();
 	}
 	}
 
 
 	private void scanClass(Routes me) {
 	private void scanClass(Routes me) {

+ 4 - 4
src/main/java/com/qlm/netty/NettyServer.java

@@ -1,7 +1,7 @@
 package com.qlm.netty;
 package com.qlm.netty;
 
 
-import com.qlm.netty.codec.CustomDecoder;
-import com.qlm.netty.codec.CustomEncoder;
+import com.qlm.netty.codec.StringDecoder;
+import com.qlm.netty.codec.StringEncoder;
 import com.qlm.netty.handler.NettyServerHandler;
 import com.qlm.netty.handler.NettyServerHandler;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelFuture;
@@ -45,8 +45,8 @@ public class NettyServer {
                             // 添加空闲状态处理器(60秒读取超时)
                             // 添加空闲状态处理器(60秒读取超时)
                             ch.pipeline().addLast(new IdleStateHandler(60, 0, 0, TimeUnit.SECONDS));
                             ch.pipeline().addLast(new IdleStateHandler(60, 0, 0, TimeUnit.SECONDS));
                             // 添加自定义编解码器
                             // 添加自定义编解码器
-                            ch.pipeline().addLast(new CustomDecoder());
-                            ch.pipeline().addLast(new CustomEncoder());
+                            ch.pipeline().addLast(new StringDecoder());
+                            ch.pipeline().addLast(new StringEncoder());
                             // 添加业务处理器
                             // 添加业务处理器
                             ch.pipeline().addLast(new NettyServerHandler());
                             ch.pipeline().addLast(new NettyServerHandler());
                         }
                         }

+ 0 - 52
src/main/java/com/qlm/netty/codec/CustomDecoder.java

@@ -1,52 +0,0 @@
-package com.qlm.netty.codec;
-
-import com.qlm.netty.protocol.CustomProtocol;
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
-
-public class CustomDecoder extends LengthFieldBasedFrameDecoder {
-
-    // 长度字段的偏移量
-    private static final int LENGTH_FIELD_OFFSET = 21;  // deviceId(20字节) + type(1字节)
-    // 长度字段的长度
-    private static final int LENGTH_FIELD_LENGTH = 4;
-
-    public CustomDecoder() {
-        // maxFrameLength: 最大帧长度
-        // lengthFieldOffset: 长度字段的偏移量
-        // lengthFieldLength: 长度字段的长度
-        // lengthAdjustment: 长度调整值
-        // initialBytesToStrip: 跳过的初始字节数
-        super(Integer.MAX_VALUE, LENGTH_FIELD_OFFSET, LENGTH_FIELD_LENGTH, 0, LENGTH_FIELD_OFFSET + LENGTH_FIELD_LENGTH);
-    }
-
-    @Override
-    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
-        ByteBuf frame = (ByteBuf) super.decode(ctx, in);
-        if (frame == null) {
-            return null;
-        }
-
-        try {
-            // 读取设备ID (20字节)
-            byte[] deviceIdBytes = new byte[20];
-            frame.readBytes(deviceIdBytes);
-            String deviceId = new String(deviceIdBytes).trim();
-
-            // 读取消息类型
-            byte type = frame.readByte();
-
-            // 读取消息长度
-            int length = frame.readInt();
-
-            // 读取消息内容
-            byte[] content = new byte[length];
-            frame.readBytes(content);
-
-            return new CustomProtocol(deviceId, type, content);
-        } finally {
-            frame.release();
-        }
-    }
-}

+ 0 - 33
src/main/java/com/qlm/netty/codec/CustomEncoder.java

@@ -1,33 +0,0 @@
-package com.qlm.netty.codec;
-
-import com.qlm.netty.protocol.CustomProtocol;
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.MessageToByteEncoder;
-
-public class CustomEncoder extends MessageToByteEncoder<CustomProtocol> {
-
-    @Override
-    protected void encode(ChannelHandlerContext ctx, CustomProtocol msg, ByteBuf out) throws Exception {
-        if (msg == null) {
-            throw new NullPointerException("CustomProtocol is null");
-        }
-
-        // 写入设备ID (固定20字节)
-        byte[] deviceIdBytes = new byte[20];
-        byte[] srcDeviceId = msg.getDeviceId().getBytes();
-        System.arraycopy(srcDeviceId, 0, deviceIdBytes, 0, Math.min(srcDeviceId.length, deviceIdBytes.length));
-        out.writeBytes(deviceIdBytes);
-
-        // 写入消息类型
-        out.writeByte(msg.getType());
-
-        // 写入消息长度
-        out.writeInt(msg.getLength());
-
-        // 写入消息内容
-        if (msg.getLength() > 0) {
-            out.writeBytes(msg.getContent());
-        }
-    }
-}

+ 29 - 0
src/main/java/com/qlm/netty/codec/StringDecoder.java

@@ -0,0 +1,29 @@
+package com.qlm.netty.codec;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.ByteToMessageDecoder;
+
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+/**
+* @program: jinzaifoodadmin
+* @ClassName: StringDecoder
+* @description: TODO
+* @author: wuyingjian
+* @create: 2025-08-27 16:58
+* @Version 1.0
+**/public class StringDecoder extends ByteToMessageDecoder {
+    @Override
+    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
+        // 读取所有可读字节
+        int readableBytes = in.readableBytes();
+        if (readableBytes > 0) {
+            byte[] bytes = new byte[readableBytes];
+            in.readBytes(bytes);
+            String message = new String(bytes, StandardCharsets.UTF_8);
+            out.add(message);
+        }
+    }
+}

+ 23 - 0
src/main/java/com/qlm/netty/codec/StringEncoder.java

@@ -0,0 +1,23 @@
+package com.qlm.netty.codec;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToByteEncoder;
+
+import java.nio.charset.StandardCharsets;
+
+/**
+* @program: jinzaifoodadmin
+* @ClassName: StringEncoder
+* @description: TODO
+* @author: wuyingjian
+* @create: 2025-08-27 16:58
+* @Version 1.0
+**/public class StringEncoder extends MessageToByteEncoder<String> {
+    @Override
+    protected void encode(ChannelHandlerContext ctx, String msg, ByteBuf out) throws Exception {
+        // 将字符串转换为字节数组并写入ByteBuf
+        byte[] bytes = msg.getBytes(StandardCharsets.UTF_8);
+        out.writeBytes(bytes);
+    }
+}

+ 58 - 37
src/main/java/com/qlm/netty/handler/NettyServerHandler.java

@@ -1,57 +1,50 @@
 package com.qlm.netty.handler;
 package com.qlm.netty.handler;
 
 
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSON;
+import com.jfinal.plugin.activerecord.Db;
+import com.jfinal.plugin.activerecord.Record;
 import com.qlm.netty.model.IndustrialControlData;
 import com.qlm.netty.model.IndustrialControlData;
-import com.qlm.netty.protocol.CustomProtocol;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
 import io.netty.channel.ChannelInboundHandlerAdapter;
 import io.netty.handler.timeout.IdleStateEvent;
 import io.netty.handler.timeout.IdleStateEvent;
+import io.netty.util.ReferenceCountUtil;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 
 
+import java.util.Date;
+
+/**
+ * @author wuyingjianwu
+ * @date 2025/08/29 10:05
+ * 描述 : Netty服务端处理类
+ */
 public class NettyServerHandler extends ChannelInboundHandlerAdapter {
 public class NettyServerHandler extends ChannelInboundHandlerAdapter {
     private static final Logger logger = LoggerFactory.getLogger(NettyServerHandler.class);
     private static final Logger logger = LoggerFactory.getLogger(NettyServerHandler.class);
 
 
-    // 心跳消息类型
-    private static final byte HEARTBEAT_TYPE = 1;
-    // 数据消息类型
-    private static final byte DATA_TYPE = 2;
-
     @Override
     @Override
-    public void channelActive(ChannelHandlerContext ctx) throws Exception {
+    public void channelActive(ChannelHandlerContext ctx) {
         logger.info("客户端连接成功: {}", ctx.channel().remoteAddress());
         logger.info("客户端连接成功: {}", ctx.channel().remoteAddress());
+        ctx.writeAndFlush("你已成功连接Netty服务端!");
+
     }
     }
 
 
     @Override
     @Override
-    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
-        CustomProtocol protocol = (CustomProtocol) msg;
-        String deviceId = protocol.getDeviceId();
-
-        switch (protocol.getType()) {
-            case HEARTBEAT_TYPE:
-                logger.info("收到来自设备 {} 的心跳消息", deviceId);
-                // 回复心跳
-                ctx.writeAndFlush(new CustomProtocol(deviceId, HEARTBEAT_TYPE, new byte[0]));
-                break;
-
-            case DATA_TYPE:
-                // 解析数据
-                String dataStr = new String(protocol.getContent());
-                IndustrialControlData data = JSON.parseObject(dataStr, IndustrialControlData.class);
-                data.setDeviceId(deviceId);
-
-                logger.info("收到来自设备 {} 的数据: {}", deviceId, data);
-
-                // 这里可以添加数据处理逻辑,例如存储到数据库
-                // processIndustrialData(data);
-
-                // 回复确认消息
-                ctx.writeAndFlush(new CustomProtocol(deviceId, DATA_TYPE, "SUCCESS".getBytes()));
-                break;
-
-            default:
-                logger.warn("收到未知类型的消息: {}", protocol.getType());
-                break;
+    public void channelRead(ChannelHandlerContext ctx, Object msg) {
+        logger.info("收到来自工控机程序的消息");
+        try {
+            logger.info("收到来自工控机程序的消息: {}",msg);
+            IndustrialControlData data = JSON.parseObject(msg.toString(), IndustrialControlData.class);
+            // 这里可以添加数据处理逻辑,例如存储到数据库
+            processIndustrialData(data);
+            // 回复确认消息
+            ctx.writeAndFlush("SUCCESS");
+        }catch (Exception e){
+            logger.error("处理数据异常: {}",e);
+            ctx.writeAndFlush("ERROR");
+        }
+        finally {
+            // 释放资源
+            ReferenceCountUtil.release(msg);
         }
         }
     }
     }
 
 
@@ -86,8 +79,36 @@ public class NettyServerHandler extends ChannelInboundHandlerAdapter {
         logger.info("客户端断开连接: {}", ctx.channel().remoteAddress());
         logger.info("客户端断开连接: {}", ctx.channel().remoteAddress());
     }
     }
 
 
-    // 数据处理方法,可根据实际需求实现
+    /**
+     * 处理工控机数据
+     * @param data
+     */
     private void processIndustrialData(IndustrialControlData data) {
     private void processIndustrialData(IndustrialControlData data) {
         // 这里实现数据存储或其他业务逻辑
         // 这里实现数据存储或其他业务逻辑
+        String deviceId = data.getDeviceId();
+        Record device = Db.findFirst("select * from t_jz_device where id = ?", deviceId);
+        if(device == null){
+            logger.info("设备不存在: {}", deviceId);
+            return;
+        }
+        //查询设备详细信息
+        Record deviceDetail = Db.findFirst("select * from t_device_detail where device_id = ?", deviceId);
+        Record deviceDetailDb = new Record();
+        deviceDetailDb.set("device_id", deviceId);
+        deviceDetailDb.set("product_id", data.getProductId());
+        deviceDetailDb.set("cpu_usage", data.getCpuUsage());
+        deviceDetailDb.set("memory_usage", data.getMemoryUsage());
+        deviceDetailDb.set("disk_free", data.getDiskFreeSpace());
+        deviceDetailDb.set("current_task_quantity", data.getProductionTaskCount());
+        deviceDetailDb.set("current_quantity", data.getProductionTaskNum());
+        deviceDetailDb.set("total_quantity", data.getProductionTaskTotal());
+        deviceDetailDb.set("uploaded_quantity", data.getProductionTaskTransferNum());
+        deviceDetailDb.set("product_id",data.getProductId());
+        if (deviceDetail == null) {
+            Db.save("t_device_detail", deviceDetailDb);
+        }else{
+            deviceDetailDb.set("updated_time",new Date());
+            Db.update("t_device_detail", "device_id",deviceDetailDb);
+        }
     }
     }
 }
 }

+ 113 - 22
src/main/java/com/qlm/netty/model/IndustrialControlData.java

@@ -6,41 +6,132 @@ import java.util.Date;
 public class IndustrialControlData implements Serializable {
 public class IndustrialControlData implements Serializable {
     private static final long serialVersionUID = 1L;
     private static final long serialVersionUID = 1L;
 
 
-    private String deviceId;        // 设备ID
-    private double cpuUsage;        // CPU使用率 (%)
-    private double memoryUsage;     // 内存使用率 (%)
-    private double diskFreeSpace;   // 磁盘剩余空间 (GB)
-    private Date timestamp;         // 数据采集时间
+    /**
+     * 设备ID
+     */
+    private String deviceId;
+    /**
+     * CPU使用率 (%)
+     */
+    private String cpuUsage;
+    /**
+     * 内存使用率 (%)
+     */
+    private String memoryUsage;
+    /**
+     * 磁盘剩余空间 (GB)
+     */
+    private String diskFreeSpace;
 
 
-    public IndustrialControlData() {}
+    /**
+     * 产品ID
+     */
+    private String productId;
 
 
-    public IndustrialControlData(String deviceId, double cpuUsage, double memoryUsage, double diskFreeSpace) {
+    /**
+     * 正在进行的生产任务数量
+     */
+    private String productionTaskCount;
+
+    /**
+     * 目前生产数量(件)
+     */
+    private String productionTaskNum;
+
+    /**
+     * 总数
+     */
+    private String productionTaskTotal;
+
+    /**
+     * 已传平台数量(件)
+     */
+    private String productionTaskTransferNum;
+
+    public String getDeviceId() {
+        return deviceId;
+    }
+
+    public void setDeviceId(String deviceId) {
         this.deviceId = deviceId;
         this.deviceId = deviceId;
+    }
+
+    public String getCpuUsage() {
+        return cpuUsage;
+    }
+
+    public void setCpuUsage(String cpuUsage) {
         this.cpuUsage = cpuUsage;
         this.cpuUsage = cpuUsage;
+    }
+
+    public String getMemoryUsage() {
+        return memoryUsage;
+    }
+
+    public void setMemoryUsage(String memoryUsage) {
         this.memoryUsage = memoryUsage;
         this.memoryUsage = memoryUsage;
+    }
+
+    public String getDiskFreeSpace() {
+        return diskFreeSpace;
+    }
+
+    public void setDiskFreeSpace(String diskFreeSpace) {
         this.diskFreeSpace = diskFreeSpace;
         this.diskFreeSpace = diskFreeSpace;
-        this.timestamp = new Date();
     }
     }
 
 
-    public String getDeviceId() { return deviceId; }
-    public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
-    public double getCpuUsage() { return cpuUsage; }
-    public void setCpuUsage(double cpuUsage) { this.cpuUsage = cpuUsage; }
-    public double getMemoryUsage() { return memoryUsage; }
-    public void setMemoryUsage(double memoryUsage) { this.memoryUsage = memoryUsage; }
-    public double getDiskFreeSpace() { return diskFreeSpace; }
-    public void setDiskFreeSpace(double diskFreeSpace) { this.diskFreeSpace = diskFreeSpace; }
-    public Date getTimestamp() { return timestamp; }
-    public void setTimestamp(Date timestamp) { this.timestamp = timestamp; }
+    public String getProductId() {
+        return productId;
+    }
+
+    public void setProductId(String productId) {
+        this.productId = productId;
+    }
+
+    public String getProductionTaskCount() {
+        return productionTaskCount;
+    }
+
+    public void setProductionTaskCount(String productionTaskCount) {
+        this.productionTaskCount = productionTaskCount;
+    }
+
+    public String getProductionTaskNum() {
+        return productionTaskNum;
+    }
+
+    public void setProductionTaskNum(String productionTaskNum) {
+        this.productionTaskNum = productionTaskNum;
+    }
+
+    public String getProductionTaskTotal() {
+        return productionTaskTotal;
+    }
+
+    public void setProductionTaskTotal(String productionTaskTotal) {
+        this.productionTaskTotal = productionTaskTotal;
+    }
+
+    public String getProductionTaskTransferNum() {
+        return productionTaskTransferNum;
+    }
+
+    public void setProductionTaskTransferNum(String productionTaskTransferNum) {
+        this.productionTaskTransferNum = productionTaskTransferNum;
+    }
 
 
     @Override
     @Override
     public String toString() {
     public String toString() {
         return "IndustrialControlData{" +
         return "IndustrialControlData{" +
                 "deviceId='" + deviceId + '\'' +
                 "deviceId='" + deviceId + '\'' +
-                ", cpuUsage=" + cpuUsage +
-                ", memoryUsage=" + memoryUsage +
-                ", diskFreeSpace=" + diskFreeSpace +
-                ", timestamp=" + timestamp +
+                ", cpuUsage='" + cpuUsage + '\'' +
+                ", memoryUsage='" + memoryUsage + '\'' +
+                ", diskFreeSpace='" + diskFreeSpace + '\'' +
+                ", productId='" + productId + '\'' +
+                ", productionTaskCount='" + productionTaskCount + '\'' +
+                ", productionTaskNum='" + productionTaskNum + '\'' +
+                ", productionTaskTotal='" + productionTaskTotal + '\'' +
+                ", productionTaskTransferNum='" + productionTaskTransferNum + '\'' +
                 '}';
                 '}';
     }
     }
 }
 }

+ 0 - 35
src/main/java/com/qlm/netty/protocol/CustomProtocol.java

@@ -1,35 +0,0 @@
-package com.qlm.netty.protocol;
-
-import java.io.Serializable;
-
-public class CustomProtocol implements Serializable {
-    private static final long serialVersionUID = 1L;
-
-    // 设备ID
-    private String deviceId;
-    // 消息类型 (1:心跳 2:数据)
-    private byte type;
-    // 消息长度
-    private int length;
-    // 消息内容
-    private byte[] content;
-
-    // 构造函数、getter和setter
-    public CustomProtocol() {}
-
-    public CustomProtocol(String deviceId, byte type, byte[] content) {
-        this.deviceId = deviceId;
-        this.type = type;
-        this.content = content;
-        this.length = content.length;
-    }
-
-    public String getDeviceId() { return deviceId; }
-    public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
-    public byte getType() { return type; }
-    public void setType(byte type) { this.type = type; }
-    public int getLength() { return length; }
-    public void setLength(int length) { this.length = length; }
-    public byte[] getContent() { return content; }
-    public void setContent(byte[] content) { this.content = content; }
-}

+ 2 - 2
src/main/webapp/page/jinzai/production_task_upload.jsp

@@ -209,7 +209,7 @@
             param['factoryId'] = factoryId;
             param['factoryId'] = factoryId;
         }
         }
         if (productId) {
         if (productId) {
-            param['productId'] = productId;
+            param['productName'] = productId;
         }
         }
         if (wmsStatus) {
         if (wmsStatus) {
             param['wmsStatus'] = wmsStatus;
             param['wmsStatus'] = wmsStatus;
@@ -288,7 +288,7 @@
                 $('#productId').empty();
                 $('#productId').empty();
                 if (res.data && res.data.length) {
                 if (res.data && res.data.length) {
                     res.data.forEach(item => {
                     res.data.forEach(item => {
-                        $('#productId').append('<option value="' + item.id + '">' + item.product_name + '</option>');
+                        $('#productId').append('<option value="' + item.product_name + '">' + item.product_name + '</option>');
                     });
                     });
                 }
                 }
                 $('#productId').selectpicker('refresh');
                 $('#productId').selectpicker('refresh');