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

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

需求:服务器连接plc,读取数据,之后写入到数据库,但是要求速度很快,而且plc中命令对应的值是不断变化的,这个变化,服务器要实时的看到;本地测试,可以通过博途
一、代码实现
1. maven依赖
<dependency><groupId>com.github.xingshuangs</groupId><artifactId>iot-communication</artifactId><version>1.4.4</version>
</dependency>
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);}@Autowiredprivate OtherProgram otherProgram;@Overridepublic void run(ApplicationArguments args) throws Exception {// 创建子线程Thread thread = new Thread(otherProgram);// 这句话保证主线程停掉的时候,子线程持续运行thread.setDaemon(true);// 启动子线程thread.start();}
}
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);@Autowiredprivate SysPlcDao plcIpDao;@Autowiredprivate SysPlcMlDao plcMlDao;@Overridepublic 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   224if (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     287byte[] 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   376byte[] 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) {// DB7byte[] 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. dao层和sql语句
/**
* 根据id和db块的命令查询最新的一条数据,目的:获取最大的命令值
* @param ipId
* @param mlContent
* @return
*/
SysPlcMlEntity getOneOrderByIdDescLimitOneAndIpIdAndContentLike(@Param("ipId") Long ipId, @Param("mlContent") String mlContent);
<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. 工具类
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 第二个点后面有没有小数,没有就设置为0String 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. 数据库结构
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
二、解释:

思路:有多个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<SysPlcMlEntity> plcMlList = plcMlDao.getListByIpIdAndMlContentLike(ipId, dbNumber);
②循环集合plcMlList ,然后获取单个命令,截取后边的字节索引和位索引,然后根据字节索引和位索引读取每个命令得到对象ByteArrayParameter,添加到集合List中
③读取数据:根据字节数组和对象集合读取数据
List<ByteArrayParameter> byteList = serializer.extractParameter(db1ParameterList, datas);
④赋值,修改数据库
通过fori循环,将获取的值赋值给每个对象,最后修改数据。由于我们解析字节获取的值和我们查询数据库的值的长度是一样的,所以循环一个集合的长度就可以的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/131949.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android Framework学习之Activity启动原理

Android Activity启动原理 Android 13.0 Activity启动原理逻辑流程图如下&#xff1a;

每天五分钟计算机视觉:搭建手写字体识别的卷积神经网络

本文重点 我们学习了卷积神经网络中的卷积层和池化层,这二者都是卷积神经网络中不可缺少的元素,本例中我们将搭建一个卷积神经网络完成手写字体识别。 卷积和池化的直观体现 手写字体识别 手写字体的图片大小是32*32*3的,它是一张 RGB 模式的图片,现在我们想识别它是从 …

【快速解决】Android Studio ERROR: Read timed out

目录 前言 回顾我查到过的解决方案&#xff08;这里是我自己解决时候的经历&#xff0c;赶时间的可以直接跳过看文章最后&#xff0c;快速进行解决&#xff09; 快速解决方案如下 总结 前言 当我们新建一个安卓项目出现Read timed out时候不要慌&#xff0c;这篇文章会打开…

前端的几种网络请求方式

网络请求 node编写接口 这里用到的几个包的作用 express&#xff1a;基于 Node.js 平台&#xff0c;快速、开放、极简的 Web 开发框架&#xff0c;官网&#xff1a;https://www.expressjs.com.cn/cors&#xff1a;用来解决跨域问题body-parser&#xff1a;可以通过 req.body…

AFL入门教学

1、AFL简介 AFL&#xff08;American Fuzzy Lop&#xff09;是一个面向安全的模糊测试工具&#xff0c;它使用了一个新的编译时插桩技术和遗传算法&#xff0c;可以自动发现触发目标二进程程序的测试用例&#xff0c;从而大大提高测试代码的功能覆盖率。 AFL官网&#xff1a;…

mac装不了python3.7.6

今天发现一个很奇怪的问题 但是我一换成 conda create -n DCA python3.8.12就是成功的 这个就很奇怪

c++中httplib使用

httplib文件链接:百度网盘 请输入提取码 提取码:kgnq json解析库:百度网盘 请输入提取码 提取码:oug0 一、获取token 打开postman, 在body这个参数中点击raw,输入用户名和密码 然后需要获取到域名和地址。 c++代码如下: #include "httplib.h" #in…

Linux环境基础开发工具使用(二)

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、Linux项目自动化构建工具-make/Makefile1、背景2、实例代码3、依赖关系4、依赖方法5、原理…

pytorch3D Windows下安装经验总结

一、说明及准备工作 最近在安装pytorch3D的时候遇到了很多问题&#xff0c;查了很多博客&#xff0c;但发现讲的都不太全&#xff0c;所以特将自己的及收集到的安装过程经验总结如下。我是在Anaconda中虚拟环境下安装的。 1.1准备工作 官方安装教程如下&#xff1a;https://…

java入门-JDK下载与安装

1、下载jdk Java 的产品叫JDK&#xff08;Java Development Kit: Java开发者工具包&#xff09;&#xff0c;必须安装JDK才能使用java 1、官网地址 https://www.oracle.com/java/ https://www.oracle.com/java/technologies/downloads/ 目前比较稳定的版本为 JDK17. 我们就安…

亚马逊云科技大语言模型下的六大创新应用功能

目录 前言 亚马逊云科技的AI创新应用 ​编辑 Amazon CodeWhisperer Amazon CodeWhisperer产品的优势 更快地完成更多工作 自信地进行编码 增强代码安全性 使用收藏夹工具 自定义 CodeWhisperer 以获得更好的建议 如何使用Amazon CodeWhisperer 步骤 1 步骤 2 具体…

【Vue基础-实践】数据可视化大屏设计(林月明螺蛳粉文化公司单量数据大屏)

目录 一、知识整理 1、页面自适应 2、下载插件px to rem & rpx 3、关于padding与margin 4、下载echarts 5、下载axios 6、experss官网接口创建 7、创建路由 8、api接口创建 9、设置基准路径 10、跨域设置 11、图表设置 12、地图数据引用 13、设置地图效果 二、…

3D高斯泼溅(Splatting)简明教程

在线工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 3D场景编辑器 3D 高斯泼溅&#xff08;Splatting&#xff09;是用于实时辐射场渲染的 3D 高斯分布描述的一种光栅化技术&#xff0c;它允许实时渲染从小图像样…

wandb 安装本地部署使用教程

1、官网注册 wandb.ai是一个为机器学习开发者提供的开发工具平台&#xff0c;可以帮助用户跟踪实验&#xff0c;管理和版本数据&#xff0c;以及与团队协作&#xff0c;从而更专注于构建最佳模型。 wandb官网&#xff1a; https://wandb.ai 首先我们打开官网注册号自己的账号并…

[自动化运维工具] Ansible的简单介绍与常用模块详解

文章目录 1. Ansible概述1.1 简介1.2 Ansible的特性1.3 Ansible的组件构成1.4 Ansible的工作原理 2. Ansible环境部署2.1 前置准备2.2 安装ansible2.3 查看基本信息2.4 配置远程主机清单 3. Ansible的常用模块3.1 ansible的基础命令格式3.2 Command模块3.2.1 基本格式和常用参数…

通过51单片机控制28byj48步进电机按角度正反转旋转

一、前言 本项目基于STC89C52单片机&#xff0c;通过控制28BYJ-48步进电机实现按角度正反转旋转的功能。28BYJ-48步进电机是一种常用的电机&#xff0c;精准定位和高扭矩输出&#xff0c;适用于许多小型的自动化系统和机械装置。 在这个项目中&#xff0c;使用STC89C52单片机…

跟着Nature Communications学作图:纹理柱状图+添加显著性标签!

&#x1f4cb;文章目录 复现图片设置工作路径和加载相关R包读取数据集数据可视化计算均值和标准差方差分析组间t-test 图a可视化过程图b可视化过程合并图ab 跟着「Nature Communications」学作图&#xff0c;今天主要通过复刻NC文章中的一张主图来巩固先前分享过的知识点&#…

Git同时配置Gitee和GitHub

Git同时配置Gitee和GitHub 一、删除原先ssh密钥二、生成密钥 这里的同时配置是针对于之前配置过单个gitee或者github而言的&#xff0c;如果需要看git从安装开始的配置&#xff0c;则可以看这一篇文章 git安装配置教程 一、删除原先ssh密钥 在C盘下用户/用户名/.ssh文件下找到…

ESP32S3入手体验测试

ESP32S3入手体验测试 &#x1f516;所入手的型号是YD-ESP32-S3 N16R8,该款和乐鑫官方推出的ESP32-S3-DevKitC-1配置差不多。 &#x1f388;乐鑫官方介绍&#xff1a;ESP32-S3-DevKitC-1 v1.1 &#x1f530;两者采用的模组&#xff1a;ESP32-S3-WROOM-1 和ESP32-S3-WROOM-1U模组…

“Java与Redis的默契舞曲:优雅地连接与存储数据“

文章目录 引言1. Java连接上Redis2. Java对Redis进行存储数据2.1 存储set类型数据2.2 存储hash类型数据2.3 存储list类型数据 总结 引言 在现代软件开发中&#xff0c;数据存储和处理是至关重要的一环。Java作为一门强大的编程语言&#xff0c;与Redis这个高性能的内存数据库相…