干燥机配套车间生产管理系统/云平台服务端
bsw215583320
2023-12-04 07df3edd01d77d77d2653a6a9ee325e53e607955
增加opc设备维护和控制
已添加10个文件
已修改11个文件
922 ■■■■ 文件已修改
jeecg-boot-base-core/src/main/java/org/jeecg/config/WebSocketConfig.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
jeecg-module-dry/jeecg-module-dry-api/pom.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jeecg-module-dry/jeecg-module-dry-api/src/main/java/org/jeecg/modules/dry/common/CommonDict.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jeecg-module-dry/jeecg-module-dry-api/src/main/java/org/jeecg/modules/dry/entity/DryOpcDevice.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jeecg-module-dry/jeecg-module-dry-api/src/main/java/org/jeecg/modules/dry/util/DryUtil.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jeecg-module-dry/jeecg-module-dry-api/src/main/java/org/jeecg/modules/dry/vo/DryOpcMsgVo.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jeecg-module-dry/jeecg-module-dry-api/src/main/java/org/jeecg/modules/dry/vo/DryOrderVo.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jeecg-module-dry/jeecg-module-dry-api/src/main/java/org/jeecg/modules/dry/vo/RealTimeDataVo.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/controller/DryEquipmentController.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/controller/DryOpcDeviceController.java 205 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/controller/OpcController.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/listener/InitListener.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/mapper/DryOpcDeviceMapper.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/mapper/xml/DryOpcDeviceMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/runner/OpcCustomRunner.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/service/IDryOpcDeviceService.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/service/impl/DryOpcDeviceServiceImpl.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/service/impl/DryRealTimeDataServiceImpl.java 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/websocket/DrySocket.java 290 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jeecg-module-dry/jeecg-module-dry-start/src/main/resources/application.yml 补丁 | 查看 | 原始文档 | blame | 历史
jeecg-module-system/jeecg-system-start/src/main/resources/application.yml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jeecg-boot-base-core/src/main/java/org/jeecg/config/WebSocketConfig.java
@@ -32,7 +32,7 @@
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(websocketFilter());
        //TODO ä¸´æ—¶æ³¨é‡ŠæŽ‰ï¼Œæµ‹è¯•下线上socket总断的问题
        bean.addUrlPatterns("/websocket/*","/eoaSocket/*","/eoaNewChatSocket/*", "/newsWebsocket/*", "/vxeSocket/*");
        bean.addUrlPatterns("/websocket/*","/eoaSocket/*","/eoaNewChatSocket/*", "/newsWebsocket/*", "/vxeSocket/*","/drySocket/*");
        return bean;
    }
jeecg-module-dry/jeecg-module-dry-api/pom.xml
@@ -25,6 +25,28 @@
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>org.eclipse.milo</groupId>-->
<!--            <artifactId>sdk-client</artifactId>-->
<!--            <version>0.6.11</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.eclipse.milo</groupId>-->
<!--            <artifactId>sdk-server</artifactId>-->
<!--            <version>0.6.11</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.bouncycastle</groupId>-->
<!--            <artifactId>bcprov-jdk15on</artifactId>-->
<!--            <version>1.70</version>-->
<!--        </dependency>-->
        <dependency>
            <groupId>com.kangaroohy</groupId>
            <artifactId>milo-spring-boot-starter</artifactId>
            <version>3.0.4</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
jeecg-module-dry/jeecg-module-dry-api/src/main/java/org/jeecg/modules/dry/common/CommonDict.java
@@ -5,5 +5,18 @@
public class CommonDict {
    /**
     * æ•…障字典
     */
    public static Map<String, String> faultDict = new HashMap<>();
    /**
     * æŠ¥è­¦å­—å…¸
     */
    public static Map<String, String> waringDict = new HashMap<>();
    /**
     * çŠ¶æ€å­—å…¸
     */
    public static Map<String, String> statusDict = new HashMap<>();
}
jeecg-module-dry/jeecg-module-dry-api/src/main/java/org/jeecg/modules/dry/entity/DryOpcDevice.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,61 @@
package org.jeecg.modules.dry.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
 * @Description: dry_opc_device
 * @Author: jeecg-boot
 * @Date:   2023-11-28
 * @Version: V1.0
 */
@Data
@TableName("dry_opc_device")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="dry_opc_device对象", description="dry_opc_device")
public class DryOpcDevice implements Serializable {
    private static final long serialVersionUID = 1L;
    /**id*/
    @TableId(type = IdType.ASSIGN_ID)
    @ApiModelProperty(value = "id")
    private java.lang.String id;
    /**名称*/
    @Excel(name = "名称", width = 15)
    @ApiModelProperty(value = "名称")
    private java.lang.String name;
    /**点位标识*/
    @Excel(name = "点位标识", width = 15)
    @ApiModelProperty(value = "点位标识")
    private java.lang.String identifier;
    /**排序*/
    @Excel(name = "排序", width = 15)
    @ApiModelProperty(value = "排序")
    private java.lang.Integer sortOrder;
    /**类型(0 ç¯ï¼Œ1 æ‘„像头)*/
    @Excel(name = "类型(0 ç¯ï¼Œ1 æ‘„像头)", width = 15, dicCode = "device_type")
    @Dict(dicCode = "device_type")
    @ApiModelProperty(value = "类型(0 ç¯ï¼Œ1 æ‘„像头)")
    private java.lang.String type;
    /**逻辑删除*/
    @Excel(name = "逻辑删除", width = 15)
    @ApiModelProperty(value = "逻辑删除")
    private java.lang.Integer del;
    @TableField(exist = false)
    private boolean value;
}
jeecg-module-dry/jeecg-module-dry-api/src/main/java/org/jeecg/modules/dry/util/DryUtil.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package org.jeecg.modules.dry.util;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.SpringContextUtils;
public class DryUtil {
    public static String getTemporaryToken() {
        RedisUtil redisUtil = SpringContextUtils.getBean(RedisUtil.class);
        // æ¨¡æ‹Ÿç™»å½•生成Token
        String token = JwtUtil.sign("admin", "b668043e3ea4bc2d");
        // è®¾ç½®Token缓存有效时间为 5 åˆ†é’Ÿ
        redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
        redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, 5 * 60 * 1000);
        return token;
    }
}
jeecg-module-dry/jeecg-module-dry-api/src/main/java/org/jeecg/modules/dry/vo/DryOpcMsgVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package org.jeecg.modules.dry.vo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.jeecg.common.aspect.annotation.Dict;
import org.jeecgframework.poi.excel.annotation.Excel;
import java.io.Serializable;
/**
 * @Description: dry_opc_device
 * @Author: jeecg-boot
 * @Date:   2023-11-28
 * @Version: V1.0
 */
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
public class DryOpcMsgVo implements Serializable {
    private static final long serialVersionUID = 1L;
    /**点位标识*/
    @ApiModelProperty(value = "点位标识")
    private String identifier;
    /** ç‚¹ä½å€¼ **/
    private boolean value;
    public DryOpcMsgVo(String id, boolean value) {
        this.identifier = id;
        this.value = value;
    }
}
jeecg-module-dry/jeecg-module-dry-api/src/main/java/org/jeecg/modules/dry/vo/DryOrderVo.java
@@ -159,7 +159,16 @@
    /**蒸汽阀状态 0:关闭 1:开启*/
    private Integer state_valve;
    /**报警**/
    private String warning;
    /**状态**/
    private Map<String, Object> eqp_state;
    /**设备状态**/
    private String eqp_status;
    private String level;
    public DryOrderVo(RealTimeDataVo realTimeDataVo) {
        this.id = IdUtil.fastSimpleUUID();
jeecg-module-dry/jeecg-module-dry-api/src/main/java/org/jeecg/modules/dry/vo/RealTimeDataVo.java
@@ -8,6 +8,7 @@
import java.util.Date;
import java.util.List;
import java.util.Map;
@Data
@ToString
@@ -84,7 +85,7 @@
    /**故障**/
    private String fault;
    private String eqp_fault;
    private Integer ai_total_time;
@@ -111,6 +112,22 @@
    /**工单状态*/
    private Integer workorder_status;
    /**各部位状态**/
    private Map<String, Object> eqp_state;
    /**设备状态**/
    private String eqp_status;
    /**设备报警**/
    private String eqp_warning;
    private String level;
    /**设备故障**/
   // private String fault;
    /**---------------------生产记录相关字段定义---s-------------------------*/
    private Boolean report_flag;       // æäº¤æ ‡å¿—
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/controller/DryEquipmentController.java
@@ -12,8 +12,10 @@
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.config.TenantContext;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.jeecg.modules.dry.common.CacheConstants;
import org.jeecg.modules.dry.entity.DryEquipment;
import org.jeecg.modules.dry.service.IDryEqpTypeService;
import org.jeecg.modules.dry.service.IDryEquipmentService;
@@ -57,6 +59,9 @@
    @Autowired
    private IDryEqpTypeService dryEqpTypeService;
     @Autowired
     private RedisUtil redisUtil;
    /**
     * åˆ†é¡µåˆ—表查询
     *
@@ -134,7 +139,9 @@
    @RequiresPermissions("dry:dry_equipment:edit")
    @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
    public Result<String> edit(@RequestBody DryEquipment dryEquipment) {
        redisUtil.del(CacheConstants.RedisKeyEnum.EQP_MAP.getCode());
        dryEquipmentService.updateById(dryEquipment);
        return Result.OK("编辑成功!");
    }
@@ -149,6 +156,7 @@
    @RequiresPermissions("dry:dry_equipment:delete")
    @DeleteMapping(value = "/delete")
    public Result<String> delete(@RequestParam(name="id",required=true) String id) {
        redisUtil.del(CacheConstants.RedisKeyEnum.EQP_MAP.getCode());
        dryEquipmentService.removeById(id);
        return Result.OK("删除成功!");
    }
@@ -164,6 +172,7 @@
    @RequiresPermissions("dry:dry_equipment:deleteBatch")
    @DeleteMapping(value = "/deleteBatch")
    public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
        redisUtil.del(CacheConstants.RedisKeyEnum.EQP_MAP.getCode());
        this.dryEquipmentService.removeByIds(Arrays.asList(ids.split(",")));
        return Result.OK("批量删除成功!");
    }
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/controller/DryOpcDeviceController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,205 @@
package org.jeecg.modules.dry.controller;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.kangaroohy.milo.model.ReadWriteEntity;
import com.kangaroohy.milo.service.MiloService;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.dry.entity.DryOpcDevice;
import org.jeecg.modules.dry.service.IDryOpcDeviceService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
 /**
 * @Description: dry_opc_device
 * @Author: jeecg-boot
 * @Date:   2023-11-28
 * @Version: V1.0
 */
@Api(tags="dry_opc_device")
@RestController
@RequestMapping("/dry/dryOpcDevice")
@Slf4j
public class DryOpcDeviceController extends JeecgController<DryOpcDevice, IDryOpcDeviceService> {
    @Autowired
    private IDryOpcDeviceService dryOpcDeviceService;
    @Autowired
    private MiloService miloService;
    /**
     * åˆ†é¡µåˆ—表查询
     *
     * @param dryOpcDevice
     * @param pageNo
     * @param pageSize
     * @param req
     * @return
     */
    //@AutoLog(value = "dry_opc_device-分页列表查询")
    @ApiOperation(value="dry_opc_device-分页列表查询", notes="dry_opc_device-分页列表查询")
    @GetMapping(value = "/list")
    public Result<IPage<DryOpcDevice>> queryPageList(DryOpcDevice dryOpcDevice,
                                   @RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
                                   @RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
                                   HttpServletRequest req) {
        QueryWrapper<DryOpcDevice> queryWrapper = QueryGenerator.initQueryWrapper(dryOpcDevice, req.getParameterMap());
        Page<DryOpcDevice> page = new Page<DryOpcDevice>(pageNo, pageSize);
        IPage<DryOpcDevice> pageList = dryOpcDeviceService.page(page, queryWrapper);
        return Result.OK(pageList);
    }
     @GetMapping(value = "/listAll")
     public Result<List<DryOpcDevice>> queryList(DryOpcDevice dryOpcDevice,
                                                      HttpServletRequest req) {
         QueryWrapper<DryOpcDevice> queryWrapper = QueryGenerator.initQueryWrapper(dryOpcDevice, req.getParameterMap());
         queryWrapper.orderByAsc("sort_order");
         List<DryOpcDevice> list = dryOpcDeviceService.list(queryWrapper);
        list.forEach(item -> {
            try {
                ReadWriteEntity readWriteEntity = miloService.readFromOpcUa(item.getIdentifier());
                item.setValue((Boolean) readWriteEntity.getValue());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
         return Result.OK(list);
     }
    /**
     *   æ·»åŠ 
     *
     * @param dryOpcDevice
     * @return
     */
    @AutoLog(value = "dry_opc_device-添加")
    @ApiOperation(value="dry_opc_device-添加", notes="dry_opc_device-添加")
    @RequiresPermissions("dry:dry_opc_device:add")
    @PostMapping(value = "/add")
    public Result<String> add(@RequestBody DryOpcDevice dryOpcDevice) {
        dryOpcDeviceService.save(dryOpcDevice);
        return Result.OK("添加成功!");
    }
    /**
     *  ç¼–辑
     *
     * @param dryOpcDevice
     * @return
     */
    @AutoLog(value = "dry_opc_device-编辑")
    @ApiOperation(value="dry_opc_device-编辑", notes="dry_opc_device-编辑")
    @RequiresPermissions("dry:dry_opc_device:edit")
    @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
    public Result<String> edit(@RequestBody DryOpcDevice dryOpcDevice) {
        dryOpcDeviceService.updateById(dryOpcDevice);
        return Result.OK("编辑成功!");
    }
    /**
     *   é€šè¿‡id删除
     *
     * @param id
     * @return
     */
    @AutoLog(value = "dry_opc_device-通过id删除")
    @ApiOperation(value="dry_opc_device-通过id删除", notes="dry_opc_device-通过id删除")
    @RequiresPermissions("dry:dry_opc_device:delete")
    @DeleteMapping(value = "/delete")
    public Result<String> delete(@RequestParam(name="id",required=true) String id) {
        dryOpcDeviceService.removeById(id);
        return Result.OK("删除成功!");
    }
    /**
     *  æ‰¹é‡åˆ é™¤
     *
     * @param ids
     * @return
     */
    @AutoLog(value = "dry_opc_device-批量删除")
    @ApiOperation(value="dry_opc_device-批量删除", notes="dry_opc_device-批量删除")
    @RequiresPermissions("dry:dry_opc_device:deleteBatch")
    @DeleteMapping(value = "/deleteBatch")
    public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
        this.dryOpcDeviceService.removeByIds(Arrays.asList(ids.split(",")));
        return Result.OK("批量删除成功!");
    }
    /**
     * é€šè¿‡id查询
     *
     * @param id
     * @return
     */
    //@AutoLog(value = "dry_opc_device-通过id查询")
    @ApiOperation(value="dry_opc_device-通过id查询", notes="dry_opc_device-通过id查询")
    @GetMapping(value = "/queryById")
    public Result<DryOpcDevice> queryById(@RequestParam(name="id",required=true) String id) {
        DryOpcDevice dryOpcDevice = dryOpcDeviceService.getById(id);
        if(dryOpcDevice==null) {
            return Result.error("未找到对应数据");
        }
        return Result.OK(dryOpcDevice);
    }
    /**
    * å¯¼å‡ºexcel
    *
    * @param request
    * @param dryOpcDevice
    */
    @RequiresPermissions("dry:dry_opc_device:exportXls")
    @RequestMapping(value = "/exportXls")
    public ModelAndView exportXls(HttpServletRequest request, DryOpcDevice dryOpcDevice) {
        return super.exportXls(request, dryOpcDevice, DryOpcDevice.class, "dry_opc_device");
    }
    /**
      * é€šè¿‡excel导入数据
    *
    * @param request
    * @param response
    * @return
    */
    @RequiresPermissions("dry:dry_opc_device:importExcel")
    @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
    public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
        return super.importExcel(request, response, DryOpcDevice.class);
    }
}
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/controller/OpcController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
package org.jeecg.modules.dry.controller;
import com.kangaroohy.milo.model.ReadWriteEntity;
import com.kangaroohy.milo.service.MiloService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode;
import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.dry.common.OPCUA;
import org.jeecg.modules.dry.entity.DryOpcDevice;
import org.jeecg.modules.dry.service.IDryOpcDeviceService;
import org.jeecg.modules.dry.vo.CommandMessageVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.concurrent.ExecutionException;
/**
 * opc控制器,用于控制现场摄像头电源,灯的开关
 */
@Api(tags="OPC控制器")
@RestController
@RequestMapping("/dry/opc")
@Slf4j
public class OpcController {
    @Autowired
    private MiloService miloService;
    @Autowired
    private IDryOpcDeviceService opcDeviceService;
    @ApiOperation(value="发送控制指令", notes="向服务端发送控制指令,由服务端转发给控制模块")
    @PostMapping("/sendWriteCommand")
    public Result<?> sendWriteCommand(@RequestBody CommandMessageVo msgVo) throws Exception {
        miloService.writeToOpcUa(ReadWriteEntity.builder()
                .identifier(msgVo.getCode())
                //Kep中是Boolean类型
                .value(Boolean.valueOf(msgVo.getMsg()))
                .build());
        return Result.OK();
    }
    @ApiOperation(value="发送控制指令", notes="向服务端发送控制指令,由服务端转发给控制模块")
    @PostMapping("/initDevice")
    public Result<?> initDevice() throws Exception {
        List<DryOpcDevice> list = opcDeviceService.list();
        list.forEach(item -> {
        });
        return Result.OK();
    }
}
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/listener/InitListener.java
@@ -4,8 +4,10 @@
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.io.ByteStreams;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.jeecg.modules.dry.common.CacheConstants;
import org.jeecg.modules.dry.common.CommonDict;
import org.jeecg.modules.dry.common.OPCUA;
import org.jeecg.modules.dry.vo.DryFault;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
@@ -17,10 +19,17 @@
import java.util.List;
import java.util.Map;
@Component
//@Component
public class InitListener implements ApplicationListener<ApplicationStartedEvent> {
    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
//        try {
//            OpcUaClient client = OPCUA.createClient();
//            client.connect().get();
//            OPCUA.managedSubscriptionEvent(client);
//        } catch (Exception e) {
//            throw new RuntimeException(e);
//        }
        InputStream fault = getClass().getResourceAsStream("/fault.json");
        try {
            JSONObject jsonObject = JSONObject.parseObject(fault, JSONObject.class);
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/mapper/DryOpcDeviceMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
package org.jeecg.modules.dry.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.jeecg.modules.dry.entity.DryOpcDevice;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
 * @Description: dry_opc_device
 * @Author: jeecg-boot
 * @Date:   2023-11-28
 * @Version: V1.0
 */
public interface DryOpcDeviceMapper extends BaseMapper<DryOpcDevice> {
}
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/mapper/xml/DryOpcDeviceMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.jeecg.modules.dry.mapper.DryOpcDeviceMapper">
</mapper>
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/runner/OpcCustomRunner.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
package org.jeecg.modules.dry.runner;
import com.alibaba.fastjson.JSON;
import com.kangaroohy.milo.service.MiloService;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.dry.entity.DryOpcDevice;
import org.jeecg.modules.dry.service.IDryOpcDeviceService;
import org.jeecg.modules.dry.vo.DryOpcMsgVo;
import org.jeecg.modules.dry.websocket.DrySocket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
@Slf4j
public class OpcCustomRunner implements ApplicationRunner {
    @Autowired
    private MiloService miloService;
    @Autowired
    private IDryOpcDeviceService dryOpcDeviceService;
    @Autowired
    private DrySocket drySocket;
    @Override
    public void run(ApplicationArguments args) throws Exception {
        subscript();
    }
    private void subscript() throws Exception {
        List<String> ids = new ArrayList<>();
        List<DryOpcDevice> list = dryOpcDeviceService.list();
        list.forEach(item -> {
            ids.add(item.getIdentifier());
        });
//        ids.add("通道 1.设备 1.标记 1");
//        ids.add("通道 1.设备 1.标记 2");
//        ids.add("channel1.device1.tag1");
//        ids.add("channel1.device1.tag2");
//        ids.add("channel1.device1.tag3");
//        ids.add("channel1.device1.tag4");
//        ids.add("channel1.device1.tag5");
//        ids.add("channel1.device1.tag6");
        miloService.subscriptionFromOpcUa(ids, (id,value)->{
            log.info("subscription ç‚¹ä½ï¼š{} è®¢é˜…到消息:{}", id, value);
            DryOpcMsgVo dryOpcMsgVo = new DryOpcMsgVo(id, (Boolean) value);
            drySocket.pushMessage("1003", JSON.toJSONString(dryOpcMsgVo));
        } );
    }
}
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/service/IDryOpcDeviceService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
package org.jeecg.modules.dry.service;
import org.jeecg.modules.dry.entity.DryOpcDevice;
import com.baomidou.mybatisplus.extension.service.IService;
/**
 * @Description: dry_opc_device
 * @Author: jeecg-boot
 * @Date:   2023-11-28
 * @Version: V1.0
 */
public interface IDryOpcDeviceService extends IService<DryOpcDevice> {
}
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/service/impl/DryOpcDeviceServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package org.jeecg.modules.dry.service.impl;
import org.jeecg.modules.dry.entity.DryOpcDevice;
import org.jeecg.modules.dry.mapper.DryOpcDeviceMapper;
import org.jeecg.modules.dry.service.IDryOpcDeviceService;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
/**
 * @Description: dry_opc_device
 * @Author: jeecg-boot
 * @Date:   2023-11-28
 * @Version: V1.0
 */
@Service
public class DryOpcDeviceServiceImpl extends ServiceImpl<DryOpcDeviceMapper, DryOpcDevice> implements IDryOpcDeviceService {
}
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/service/impl/DryRealTimeDataServiceImpl.java
@@ -8,14 +8,21 @@
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.service.IoHandler;
import org.apache.mina.core.session.IoSession;
import org.jeecg.common.api.CommonAPI;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.config.TenantContext;
import org.jeecg.common.config.mqtoken.UserTokenContext;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.modules.dry.api.JeecgSystemApi;
import org.jeecg.modules.dry.common.CacheConstants;
import org.jeecg.modules.dry.entity.*;
import org.jeecg.modules.dry.service.*;
import org.jeecg.modules.dry.socket.ServerHandler;
import org.jeecg.modules.dry.socket.SocketServerConfig;
import org.jeecg.modules.dry.util.DryUtil;
import org.jeecg.modules.dry.vo.*;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
@@ -52,13 +59,31 @@
    private IDryProdRecordService prodRecordService;
    @Autowired
    private IoAcceptor ioAcceptor;
    private CommonAPI commonAPI;
    private String token;
    public String getTemporaryToken() {
        if (token == null) {
            RedisUtil redisUtil = SpringContextUtils.getBean(RedisUtil.class);
            // æ¨¡æ‹Ÿç™»å½•生成Token
            token = JwtUtil.sign("admin", "b668043e3ea4bc2d");
            // è®¾ç½®Token缓存有效时间为 5 åˆ†é’Ÿ
            redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
            redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, 5 * 60 * 1000);
        }
        return token;
    }
    @Override
    @Transactional
    public Result<?> realTimeDataHandle(RealTimeDataVo realTimeDataVo) {
        TenantContext.setTenant(realTimeDataVo.getTenantid()+"");
        log.info("实时数据:"+realTimeDataVo.toString());
        // 1 æŸ¥è¯¢æˆ–创建工单
        // 1.1 ä»Žredis取出工单缓存
@@ -99,12 +124,12 @@
        orderVo.setState_windbox(realTimeDataVo.getState_windbox());
        orderVo.setState_valve(realTimeDataVo.getState_valve());
        orderVo.setOrderStatus(realTimeDataVo.getWorkorder_status());
        orderVo.setEqp_status(realTimeDataVo.getEqp_status());
        orderVo.setEqp_state(realTimeDataVo.getEqp_state());
        orderVo.setWarning(realTimeDataVo.getEqp_warning());
        orderVo.setFault(realTimeDataVo.getEqp_fault());
        orderVo.setLevel(realTimeDataVo.getLevel());
        String fault = realTimeDataVo.getFault();
//        String[] split = fault.split(",");
//        for (String s : split) {
//
//        }
        DryOrderTrendVo trendVo = new DryOrderTrendVo(realTimeDataVo);
        // 2.2 ä¿å­˜å·¥å•含水率变化 æˆ– é‡é‡å˜åŒ–
@@ -293,6 +318,7 @@
        LambdaQueryWrapper<DryEquipment> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DryEquipment::getTenantId, realTimeDataVo.getTenantid());
        queryWrapper.eq(DryEquipment::getEnable, "Y");
jeecg-module-dry/jeecg-module-dry-biz/src/main/java/org/jeecg/modules/dry/websocket/DrySocket.java
@@ -1,214 +1,146 @@
package org.jeecg.modules.dry.websocket;
import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.DrySocketConst;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.modules.dry.service.IDryRealTimeDataService;
import org.jeecg.modules.dry.vo.RealTimeDataVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.jeecg.common.constant.WebsocketConst;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
/**
 * @Author scott
 * @Date 2019/11/29 9:41
 * @Description: æ­¤æ³¨è§£ç›¸å½“于设置访问URL
 */
@Component
@ServerEndpoint("/drySocket/{tenantId}/{machineId}")
@Slf4j
@ServerEndpoint("/drySocket/{tenantId}")
public class DrySocket {
    /**
     * å½“前 session
     */
    private Session session;
    /**
     * å½“前租户id
     */
    private String tenantId;
    /**
     * è®¾å¤‡id,用于标识同一租户,不同设备的数据
     */
    private String machineId;
    /**
     * å½“前socket唯一id
     */
    private String socketId;
    /**线程安全Map*/
    private static ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<>();
    /**
     * ç§Ÿæˆ·è¿žæŽ¥æ± ï¼ŒåŒ…含单个租户的所有socket连接;
     * å› ä¸ºä¸€ä¸ªç§Ÿæˆ·å¯èƒ½æ‰“开多个设备,多个设备就会有多个连接;
     * key是tenantId,value是Map对象;子Map的key是machineId,value是drySocket对象
     */
    private static Map<String, Map<String, DrySocket>> tenantPool = new HashMap<>();
    /**
     * è¿žæŽ¥æ± ï¼ŒåŒ…含所有WebSocket连接;
     * key是socketId,value是drySocket对象
     */
    private static Map<String, DrySocket> machinePool = new HashMap<>();
    /**
     * èŽ·å–æŸä¸ªç§Ÿæˆ·æ‰€æœ‰çš„è®¾å¤‡
     */
    public static Map<String, DrySocket> getTenantPool(String tenantId) {
        return tenantPool.computeIfAbsent(tenantId, k -> new HashMap<>(5));
    }
    /**
     * å‘当前租户发送消息
     *
     * @param message æ¶ˆæ¯å†…容
     */
    public void sendMessage(String message) {
        try {
            this.session.getAsyncRemote().sendText(message);
        } catch (Exception e) {
            log.error("【drySocket】消息发送失败:" + e.getMessage());
        }
    }
    /**
     * å°è£…消息json
     *
     * @param data æ¶ˆæ¯å†…容
     */
    public static String packageMessage(String type, Object data) {
        JSONObject message = new JSONObject();
        message.put(DrySocketConst.TYPE, type);
        message.put(DrySocketConst.DATA, data);
        return message.toJSONString();
    }
    /**
     * å‘指定租户的所有设备发送消息
     *
     * @param tenantId  æŽ¥æ”¶æ¶ˆæ¯çš„租户ID
     * @param message æ¶ˆæ¯å†…容
     */
    public static void sendMessageTo(String tenantId, String message) {
        Collection<DrySocket> values = getTenantPool(tenantId).values();
        if (values.size() > 0) {
            for (DrySocket socketItem : values) {
                socketItem.sendMessage(message);
            }
        } else {
            log.warn("【drySocket】消息发送失败:tenantId\"" + tenantId + "\"不存在或未在线!");
        }
    }
    /**
     * å‘指定租户的指定设备发送消息
     *
     * @param tenantId  æŽ¥æ”¶æ¶ˆæ¯çš„租户ID
     * @param message æ¶ˆæ¯å†…容
     */
    public static void sendMessageTo(String tenantId, String machineId, String message) {
        DrySocket socketItem = getTenantPool(tenantId).get(machineId);
        if (socketItem != null) {
            socketItem.sendMessage(message);
        } else {
            log.warn("【drySocket】消息发送失败:tenantId\"" + tenantId + "\"的machineId\"" + machineId + "\"不存在或未在线!");
        }
    }
    /**
     * å‘多个租户的所有设备发送消息
     *
     * @param tenantIds æŽ¥æ”¶æ¶ˆæ¯çš„租户ID数组
     * @param message æ¶ˆæ¯å†…容
     */
    public static void sendMessageTo(String[] tenantIds, String message) {
        for (String tenantId : tenantIds) {
            DrySocket.sendMessageTo(tenantId, message);
        }
    }
    /**
     * å‘所有租户的所有设备发送消息
     *
     * @param message æ¶ˆæ¯å†…容
     */
    public static void sendMessageToAll(String message) {
        for (DrySocket socketItem : machinePool.values()) {
            socketItem.sendMessage(message);
        }
    }
    /**
     * websocket å¼€å¯è¿žæŽ¥
     */
    //==========【websocket接受、推送消息等方法 â€”— å…·ä½“服务节点推送ws消息】========================================================================================
    @OnOpen
    public void onOpen(Session session, @PathParam("tenantId") String tenantId, @PathParam("machineId") String machineId) {
    public void onOpen(Session session, @PathParam(value = "tenantId") String tenantId) {
        try {
            this.tenantId = tenantId;
            this.machineId = machineId;
            this.socketId = tenantId + machineId;
            this.session = session;
            machinePool.put(this.socketId, this);
            getTenantPool(tenantId).put(this.machineId, this);
            log.info("【drySocket】有新的连接,总数为:" + machinePool.size());
        } catch (Exception ignored) {
            sessionPool.put(tenantId, session);
            log.info("【系统 WebSocket】有新的连接,总数为:" + sessionPool.size());
        } catch (Exception e) {
        }
    }
    /**
     * websocket æ–­å¼€è¿žæŽ¥
     */
    @OnClose
    public void onClose() {
    public void onClose(@PathParam("tenantId") String tenantId) {
        try {
            machinePool.remove(this.socketId);
            getTenantPool(this.tenantId).remove(this.machineId);
            log.info("【drySocket】连接断开,总数为:" + machinePool.size());
        } catch (Exception ignored) {
            sessionPool.remove(tenantId);
            log.info("【系统 WebSocket】连接断开,总数为:" + sessionPool.size());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * websocket æ”¶åˆ°æ¶ˆæ¯
     * ws推送消息
     *
     * @param tenantId
     * @param message
     */
    public void pushMessage(String tenantId, String message) {
        for (Map.Entry<String, Session> item : sessionPool.entrySet()) {
            //userId key值= {用户id + "_"+ ç™»å½•token的md5串}
            //TODO vue2未改key新规则,暂时不影响逻辑
            if (item.getKey().contains(tenantId)) {
                Session session = item.getValue();
                try {
                    //update-begin-author:taoyan date:20211012 for: websocket报错 https://gitee.com/jeecg/jeecg-boot/issues/I4C0MU
                    synchronized (session){
                        log.info("【系统 WebSocket】推送单人消息:" + message);
                        session.getBasicRemote().sendText(message);
                    }
                    //update-end-author:taoyan date:20211012 for: websocket报错 https://gitee.com/jeecg/jeecg-boot/issues/I4C0MU
                } catch (Exception e) {
                    log.error(e.getMessage(),e);
                }
            }
        }
    }
    /**
     * ws遍历群发消息
     */
    public void pushMessage(String message) {
        try {
            for (Map.Entry<String, Session> item : sessionPool.entrySet()) {
                try {
                    item.getValue().getAsyncRemote().sendText(message);
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
            }
            log.info("【系统 WebSocket】群发消息:" + message);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }
    /**
     * ws接受客户端消息
     */
    @OnMessage
    public void onMessage(String message) {
        log.info("【drySocket】onMessage:" + message);
        IDryRealTimeDataService realTimeDataService = SpringContextUtils.getBean(IDryRealTimeDataService.class);
        JSONObject json;
        try {
            json = JSON.parseObject(message);
        } catch (Exception e) {
            log.warn("【drySocket】收到不合法的消息:" + message);
            return;
        }
        String type = json.getString(DrySocketConst.TYPE);
        switch (type) {
            // å¿ƒè·³æ£€æµ‹
            case DrySocketConst.TYPE_HB:
                this.sendMessage(DrySocket.packageMessage(type, true));
                break;
            // å®žæ—¶æ•°æ®å¤„理
            case DrySocketConst.TYPE_RDT:
                Object o = json.get(DrySocketConst.DATA);
                RealTimeDataVo realTimeDataVo = BeanUtil.toBean(o, RealTimeDataVo.class);
                realTimeDataService.realTimeDataHandle(realTimeDataVo);
                break;
            default:
                log.warn("【drySocket】收到不识别的消息类型:" + type);
                break;
    public void onMessage(String message, @PathParam(value = "tenantId") String tenantId) {
        if(!"ping".equals(message) && !WebsocketConst.CMD_CHECK.equals(message)){
            log.info("【系统 WebSocket】收到客户端消息:" + message);
        }else{
            log.debug("【系统 WebSocket】收到客户端消息:" + message);
        }
//        //------------------------------------------------------------------------------
//        JSONObject obj = new JSONObject();
//        //业务类型
//        obj.put(WebsocketConst.MSG_CMD, WebsocketConst.CMD_CHECK);
//        //消息内容
//        obj.put(WebsocketConst.MSG_TXT, "心跳响应");
//        this.pushMessage(userId, obj.toJSONString());
//        //------------------------------------------------------------------------------
    }
    /**
     * é…ç½®é”™è¯¯ä¿¡æ¯å¤„理
     *
     * @param session
     * @param t
     */
    @OnError
    public void onError(Session session, Throwable t) {
        log.warn("【系统 WebSocket】消息出现错误");
        t.printStackTrace();
    }
    //==========【系统 WebSocket接受、推送消息等方法 â€”— å…·ä½“服务节点推送ws消息】========================================================================================
    //==========【采用redis发布订阅模式——推送消息】========================================================================================
    /**
     * æ­¤ä¸ºå•点消息(多人) redis
     *
     * @param userIds
     * @param message
     */
//    public void sendMessage(String[] userIds, String message) {
//        for (String userId : userIds) {
//            sendMessage(userId, message);
//        }
//    }
    //=======【采用redis发布订阅模式——推送消息】==========================================================================================
}
jeecg-module-dry/jeecg-module-dry-start/src/main/resources/application.yml
jeecg-module-system/jeecg-system-start/src/main/resources/application.yml
@@ -3,3 +3,6 @@
    name: jeecg-system
  profiles:
    active: '@profile.name@'
  cloud:
    inetutils:
      preferred-networks: 192.168