• springboot 连接西门子plc,读取对应的值,并修改到数据库


    springboot 连接西门子plc,读取对应的值,并修改到数据库

    需求:服务器连接plc,读取数据,之后写入到数据库,但是要求速度很快,而且plc中命令对应的值是不断变化的,这个变化,服务器要实时的看到;本地测试,可以通过博途
    一、代码实现
    1. maven依赖
    <dependency>
       <groupId>com.github.xingshuangs</groupId>
        <artifactId>iot-communication</artifactId>
        <version>1.4.4</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    2. 入口函数
    @SpringBootApplication
    @MapperScan("com.gotion.modules.dao")
    public class FamenYaokongApplication implements ApplicationRunner {
    	private static Logger logger = LoggerFactory.getLogger(FamenYaokongApplication.class);
    	public static void main(String[] args) {
            SpringApplication.run(FamenYaokongApplication.class, args);
        }
    
        @Autowired
        private OtherProgram otherProgram;
        
    	@Override
        public void run(ApplicationArguments args) throws Exception {
            // 创建子线程
            Thread thread = new Thread(otherProgram);
            // 这句话保证主线程停掉的时候,子线程持续运行
            thread.setDaemon(true);
            // 启动子线程
            thread.start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    3. 子线程类
    package com.gotion.modules.task;
    
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.github.xingshuangs.iot.protocol.common.serializer.ByteArraySerializer;
    import com.github.xingshuangs.iot.protocol.s7.enums.EPlcType;
    import com.github.xingshuangs.iot.protocol.s7.service.S7PLC;
    import com.gotion.modules.dao.SysPlcDao;
    import com.gotion.modules.dao.SysPlcMlDao;
    import com.gotion.modules.entity.SysPlcEntity;
    import com.gotion.modules.entity.SysPlcMlEntity;
    import com.gotion.modules.plc.PlcReadDataUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    
    /**
     * @Author: Administrator
     * @Date: 2023/11/5
     * @Description:
     */
    @Service
    public class OtherProgram implements Runnable {
        private static Logger logger = LoggerFactory.getLogger(OtherProgram.class);
    
    
        @Autowired
        private SysPlcDao plcIpDao;
        @Autowired
        private SysPlcMlDao plcMlDao;
    
    
        @Override
        public void run() {
            // 在这里编写子线程的执行逻辑
            // 可以调用其他程序的入口方法或执行其他操作
            logger.info("plc startTime:" + System.currentTimeMillis());
            QueryWrapper<SysPlcEntity> queryWrapper = new QueryWrapper<>();
            queryWrapper.select("id", "ip_address").eq("is_flag", 1);
            List<SysPlcEntity> ipList = plcIpDao.selectList(queryWrapper);
            if (!ipList.isEmpty()) {
                ipList.stream().forEach(plcIp -> {
                    // 建立连接
                    S7PLC s7PLC = new S7PLC(EPlcType.S1200, plcIp.getIpAddress()); // 使用plcIp的IP地址
                    while (true) {
                        // 构建序列化对象
                        ByteArraySerializer serializer = ByteArraySerializer.newInstance();
                        // 获取对应plc下db1的命令最大字节的数字
                        SysPlcMlEntity db1PlcMl = plcMlDao.getOneOrderByIdDescLimitOneAndIpIdAndContentLike(plcIp.getId(), "DB1");
                        SysPlcMlEntity db3PlcMl = plcMlDao.getOneOrderByIdDescLimitOneAndIpIdAndContentLike(plcIp.getId(), "DB3");
                        SysPlcMlEntity db4PlcMl = plcMlDao.getOneOrderByIdDescLimitOneAndIpIdAndContentLike(plcIp.getId(), "DB4");
                        SysPlcMlEntity db5PlcMl = plcMlDao.getOneOrderByIdDescLimitOneAndIpIdAndContentLike(plcIp.getId(), "DB5");
                        SysPlcMlEntity db6PlcMl = plcMlDao.getOneOrderByIdDescLimitOneAndIpIdAndContentLike(plcIp.getId(), "DB6");
                        SysPlcMlEntity db7PlcMl = plcMlDao.getOneOrderByIdDescLimitOneAndIpIdAndContentLike(plcIp.getId(), "DB7");
                        //-----------------读取plc1的字节   DBW两个字节,DBD4个字节  DBX/B一个字节 看最后一个以什么结尾,就在对应的字节数上添加2或者4或者1
                        // DB1地址范围是0~220+4   224
                        if (db1PlcMl != null) {
                            byte[] db1Datas = s7PLC.readByte("DB1.DBD0", PlcReadDataUtils.getMaxByteOffsite(db1PlcMl));
                            if (db1Datas != null && db1Datas.length > 0) {
                                //DB1数据块
                                PlcReadDataUtils.readData(plcIp.getId(), "DB1", serializer, db1Datas, plcMlDao);
                            }
                        }
                        // DB3地址范围是0~284+4   288  DBW两个字节,DBD4个字节  B一个字节
                        if (db3PlcMl != null) {
                            byte[] db3Datas = s7PLC.readByte("DB3.DBW0", PlcReadDataUtils.getMaxByteOffsite(db3PlcMl));
                            if (db3Datas != null && db3Datas.length > 0) {
                                //DB3数据块
                                PlcReadDataUtils.readData(plcIp.getId(), "DB3", serializer, db3Datas, plcMlDao);
                            }
                        }
                        if (db4PlcMl != null) {
                            // DB4地址范围是0~286     287
                            byte[] db4Datas = s7PLC.readByte("DB4.DBD0", PlcReadDataUtils.getMaxByteOffsite(db4PlcMl));
                            if (db4Datas != null && db4Datas.length>0) {
                                // DB4数据块
                                PlcReadDataUtils.readData(plcIp.getId(), "DB4", serializer, db4Datas, plcMlDao);
                            }
                        }
                        if (db5PlcMl != null) {
                            // DB5地址范围是0~374+2   376
                            byte[] db5Datas = s7PLC.readByte("DB5.DBX0.0", PlcReadDataUtils.getMaxByteOffsite(db5PlcMl));
                            if (db5Datas != null && db5Datas.length > 0) {
                                // DB5数据块
                                PlcReadDataUtils.readData(plcIp.getId(), "DB5", serializer, db5Datas, plcMlDao);
                            }
                        }
                        if (db6PlcMl != null) {
                            byte[] db6Datas = s7PLC.readByte("DB6.DBX0.0", PlcReadDataUtils.getMaxByteOffsite(db6PlcMl));
                            if (db6Datas != null && db6Datas.length >0) {
                                PlcReadDataUtils.readData(plcIp.getId(),"DB6", serializer, db6Datas, plcMlDao);
                            }
                        }
                        if (db7PlcMl != null) {
                            // DB7
                            byte[] db7Datas = s7PLC.readByte("DB7.DBX0.0", PlcReadDataUtils.getMaxByteOffsite(db7PlcMl));
                            if (db7Datas != null && db7Datas.length > 0) {
                                // DB7数据块
                                PlcReadDataUtils.readData(plcIp.getId(),"DB7", serializer, db7Datas, plcMlDao);
                            }
                        }
                        logger.info("plc endTime:" + System.currentTimeMillis());
                    }
                });
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    1. dao层和sql语句
    /**
    * 根据id和db块的命令查询最新的一条数据,目的:获取最大的命令值
    * @param ipId
    * @param mlContent
    * @return
    */
    SysPlcMlEntity getOneOrderByIdDescLimitOneAndIpIdAndContentLike(@Param("ipId") Long ipId, @Param("mlContent") String mlContent);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    <select id="getOneOrderByIdDescLimitOneAndIpIdAndContentLike" resultType="com.gotion.modules.entity.SysPlcMlEntity">
        SELECT id,ip_id,type_id,name,ml_name,ml_content,ml_value FROM `sys_plc_ml`
        <where>
            <if test="ipId != null">
                and ip_id = #{ipId}
            if>
            <if test="mlContent != null and mlContent.trim() != ''">
                and ml_content like concat(#{mlContent},'%')
            if>
        where>
        ORDER BY id DESC LIMIT 1
    select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 工具类
    package com.gotion.modules.plc;
    
    import com.github.xingshuangs.iot.protocol.common.enums.EDataType;
    import com.github.xingshuangs.iot.protocol.common.serializer.ByteArrayParameter;
    import com.github.xingshuangs.iot.protocol.common.serializer.ByteArraySerializer;
    import com.gotion.modules.dao.SysPlcMlDao;
    import com.gotion.modules.entity.SysPlcMlEntity;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @Author: Administrator
     * @Date: 2023/11/5
     * @Description:
     */
    public class PlcReadDataUtils {
    
        public static Integer getMaxByteOffsite(SysPlcMlEntity plcMl) {
            int count = 0;
            String[] mlContents =  plcMl.getMlContent().split("\\.");
            String byteOffset = mlContents[1].replaceAll("[^0-9]", "");
            if (plcMl.getMlContent().contains("DBD")) {
                count = Integer.valueOf(byteOffset) + 4;
            } else if (plcMl.getMlContent().contains("DBW")) {
                count = Integer.valueOf(byteOffset) + 2;
            } else if (plcMl.getMlContent().contains("DBX")) {
                count = Integer.valueOf(byteOffset) + 1;
            }
            return count;
        }
    
        public static List<ByteArrayParameter> readData(Long ipId, String dbNumber, ByteArraySerializer serializer, byte[] datas, SysPlcMlDao plcMlDao) {
            // 查询plc1对应的db1的数据集合
            List<SysPlcMlEntity> plcMlList = plcMlDao.getListByIpIdAndMlContentLike(ipId, dbNumber);
            List<ByteArrayParameter> db1ParameterList = new ArrayList<>();
            plcMlList.stream().forEach(plcMl-> {
                String[] mlContents =  plcMl.getMlContent().split("\\.");
                String byteOffset = mlContents[1].replaceAll("[^0-9]", "");
                // 先判断DB1.DBX0.0 第二个点后面有没有小数,没有就设置为0
                String bitOffset = "0";
                if (mlContents.length > 2) {
                    bitOffset = mlContents[2];
                }
                if (plcMl.getMlContent().contains("DBD")) {
                    db1ParameterList.add(new ByteArrayParameter(Integer.valueOf(byteOffset), Integer.valueOf(bitOffset), 1, EDataType.FLOAT32));
                }
                if (plcMl.getMlContent().contains("DBW")) {
                    db1ParameterList.add(new ByteArrayParameter(Integer.valueOf(byteOffset), Integer.valueOf(bitOffset), 1, EDataType.INT16));
                }
                if (plcMl.getMlContent().contains("DBX")) {
                    db1ParameterList.add(new ByteArrayParameter(Integer.valueOf(byteOffset), Integer.valueOf(bitOffset),1, EDataType.BOOL));
                }
            });
            // 读取db1的 字节数据
            List<ByteArrayParameter> byteList = serializer.extractParameter(db1ParameterList, datas);
            for (int i = 0; i < plcMlList.size(); i++) {
                plcMlList.get(i).setMlValue(byteList.get(i).getValue().toString());
            }
            plcMlDao.batchUpdateMlList(plcMlList);
            return byteList;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    1. 数据库结构
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
    二、解释:

    思路:有多个plc,先查出plc的数量,根据plc地址创建连接,然后根据命令读取值
    DB5.DBW2.1:数据块5,字节索引:2,位索引:1
    DB4.DBW3:数据块4,字节索引:3,位索引:0
    DB5.DBD2.3:数据块5,字节索引:2,位索引:3
    DB5.DBD2:数据块:5,字节索引:2,位索引:0
    DB4.DBX1.1:数据块:4,字节索引:1,位索引:1
    DB4.DBX1:数据块:4,字节索引:1,位索引:0

    每个命令后面的数据分别代表字节索引和位索引

    我代码的逻辑:
    (1)获取最大的字节数:先查询出每个plc中,每个db块的最大的字节索引,命令是连续的,我就从数据库中倒叙查询每个plc中每个db块,然后取一条数据;
    SysPlcMlEntity db1PlcMl = plcMlDao.getOneOrderByIdDescLimitOneAndIpIdAndContentLike(plcIp.getId(), "DB1");
    (2)因为每个db块的初始字节索引和位索引都是0,所以代码中直接写死初始值,根据初始值和每个db块的数量读取plc字节数组;
    byte[] db1Datas = s7PLC.readByte("DB1.DBD0", PlcReadDataUtils.getMaxByteOffsite(db1PlcMl));
    (3)解析读取到的字节数组:
    ① 由于要读取字节数组,同时把获取到的值写进数据库,所以我需要根据ip和db块查询数据库对应的集合数据:
    List plcMlList = plcMlDao.getListByIpIdAndMlContentLike(ipId, dbNumber);
    ②循环集合plcMlList ,然后获取单个命令,截取后边的字节索引和位索引,然后根据字节索引和位索引读取每个命令得到对象ByteArrayParameter,添加到集合List中
    ③读取数据:根据字节数组和对象集合读取数据
    List byteList = serializer.extractParameter(db1ParameterList, datas);
    ④赋值,修改数据库
    通过fori循环,将获取的值赋值给每个对象,最后修改数据。由于我们解析字节获取的值和我们查询数据库的值的长度是一样的,所以循环一个集合的长度就可以的。

  • 相关阅读:
    编程狂人|妙用Java 8中的 Function接口 消灭if...else...
    高数总结(6
    连连看核心算法与基本思想(附全部项目代码链接与代码详细注释)
    信号频谱分析与功率谱密度
    SQL优化——如何写出高效率SQL
    当你离开现在的公司,你的百万业务量还能保持吗?
    交通物流模型 | 基于时空注意力融合网络的城市轨道交通假期短时客流预测
    kali安装dvwa
    ControlNet原理及应用
    【3dmax】怎么将点删除而面保留
  • 原文地址:https://blog.csdn.net/weixin_44021888/article/details/134232175