本来是想直接实战的,但是写着写着发现:
如果对PLC的基础知识,尤其存储区、数据结构不先了解的话,在对PLC的地址进行读写操作时,会觉得莫名其妙,所以,就先补写了一片PLC的基础介绍:
PLC基础知识 + 西门子S7-200Smart型号存储区、数据类型
一、什么是HslCommunication?
官网:http://www.hslcommunication.cn/
官网Doc文档:http://www.hsltechnology.cn/Doc/HslCommunication
官网API文档:http://api.hslcommunication.cn/html/c136d3de-eab7-9b0f-4bdf-d891297c8018.htm
1、HSL的介绍:
这是一个基于工业物联网,计算机通讯的架构实现,集成了工业软件开发的大部分的基础功能实现,比如三菱PLC通讯,西门子PLC通讯,欧姆龙PLC通讯,modbus通讯,AB PLC,基恩士PLC,台达PLC,松下PLC,GE PLC通讯等等,这些通讯全部进行了多语言的实现,当然,主打的 .net 库的功能集成还更加的强大,除此之外,还实现了跨程序,跨语言,跨平台的通讯,让你不再纠结于使用windows还是 linux系统,实现了日志功能,流水号生成功能,邮件发送功能,傅立叶变换功能,等等,将来会集成更多的工业环境常见功能的实现。
2、HSL的版权问题:
-
个人用户可免费用于学术研究,测试。
-
如果,个人所有依赖组件的项目年总值小于3000元以下的,只需要一次性付费240rmb,加入普通vip即可获得永久使用的权利,永久发放激活码。
-
个人商业使用及企业商业使用需要进行授权,授权费用参考官网:http://www.hslcommunication.cn/
所以:作为个人入门、学习、调试等使用,还是很不错的选择,但是如果有商业用途,建议获取官方授权,或者另寻它法!
二、使用Java通过HSLCommunication快速实现对S7-200Smart的读写操作
1、引入HSLCommunication的依赖:
<!-- https://mvnrepository.com/artifact/com.github.dathlin/HslCommunication --> <dependency> <groupId>com.github.dathlin</groupId> <artifactId>HslCommunication</artifactId> <version>3.3.1</version> </dependency>
2、以短连接的方式对本地PLC进行读写操作:
// 测试了多种数据类型,以及中文 public class SiemensShortConnect { public static void main(String[] args) { SiemensS7Net siemensS7 = new SiemensS7Net(SiemensPLCS.S200Smart, "127.0.0.1"); siemensS7.setPort(102); siemensS7.Write("M10.0", Boolean.TRUE); // BOOL型占 1 Bit,可以存在小空间的M区(位存储区) siemensS7.Write("V100",100); // 整形 int = 4Byte siemensS7.Write("V104",(long) 10000); // 整形 long = 4Byte siemensS7.Write("V112", (float) 4.44); // 浮点型(实数), float = 4Byte siemensS7.Write("V116", (double) 8.88); // 浮点型(实数), double = 8Byte siemensS7.Write("V124", "zidan"); // UTF-8编码下,一个英文字母为一个字符,一个英文字符 = 1Byte;但是VW起始位必须要偶数 siemensS7.Write("VW130", "jiguiquan"); // UTF-8编码下,一个英文字母为一个字符,一个英文字符 = 1Byte;一个中文汉字 = 3字节;但是VW起始位必须要偶数 siemensS7.Write("VW140", "吉桂权", Charset.forName("UTF-8")); // UTF-8编码下,一个英文字母为一个字符,一个英文字符 = 1Byte;一个中文汉字 = 3字节;但是VW起始位必须要偶数 Boolean m10 = siemensS7.ReadBool("M10.0").Content; Integer v100 = siemensS7.ReadInt32( "VB100" ).Content; // V100 = VB100 Long v104 = siemensS7.ReadInt64( "VB104" ).Content; // V104 = VB104 Float v112 = siemensS7.ReadFloat("V112").Content; Double v116 = siemensS7.ReadDouble("V116").Content; String vw124 = siemensS7.ReadString("VW124").Content; // V124 = VW124 String vw130 = siemensS7.ReadString("V130").Content; // VW130 = V130 String vw140 = siemensS7.ReadString("V140", Charset.forName("UTF-8")).Content; // 底层会自己判断,我们只需要根据JAVA中的数据类型,区判断字节数即可; System.out.println("M10.0: " + m10); System.out.println("V100: " + v100); System.out.println("V104: " + v104); System.out.println("V112: " + v112); System.out.println("V116: " + v116); System.out.println("VW124: " + vw124); System.out.println("VW130: " + vw130); System.out.println("VW140: " + vw140); } }
运行结果如下:
M10.0: true V100: 100 V104: 10000 V112: 4.44 V116: 8.88 VW124: zidan VW130: jiguiquan VW140: 吉桂权
问:何为短连接?
答:如下图,每一次读或写操作,都会伴随一次 连接~断开,这种显然是很不友好且耗资源的;如果有很多操作时,我们肯定希望能够通过长连接完成;
3、通过长连接的方式对本地PLC进行读写操作:
public class SiemensLongConnect { public static void main(String[] args) { SiemensS7Net siemensS7 = new SiemensS7Net( SiemensPLCS.S200Smart, "127.0.0.1" ); siemensS7.setPort(102); OperateResult connect = siemensS7.ConnectServer(); // 建立长连接 if (!connect.IsSuccess) { System.out.println("connect failed:" + connect.Message); return; } siemensS7.Write("M10.0", Boolean.TRUE); // BOOL型占 1 Bit,可以存在小空间的M区(位存储区) siemensS7.Write("V100",100); // 整形 int = 4Byte siemensS7.Write("V104",(long) 10000); // 整形 long = 4Byte siemensS7.Write("V112", (float) 4.44); // 浮点型(实数), float = 4Byte siemensS7.Write("V116", (double) 8.88); // 浮点型(实数), double = 8Byte siemensS7.Write("V124", "zidan"); // UTF-8编码下,一个英文字母为一个字符,一个英文字符 = 1Byte;但是VW起始位必须要偶数 siemensS7.Write("VW130", "jiguiquan"); // UTF-8编码下,一个英文字母为一个字符,一个英文字符 = 1Byte;一个中文汉字 = 3字节;但是VW起始位必须要偶数 siemensS7.Write("VW140", "吉桂权", Charset.forName("UTF-8")); // UTF-8编码下,一个英文字母为一个字符,一个英文字符 = 1Byte;一个中文汉字 = 3字节;但是VW起始位必须要偶数 Boolean m10 = siemensS7.ReadBool("M10.0").Content; Integer v100 = siemensS7.ReadInt32( "VB100" ).Content; // V100 = VB100 Long v104 = siemensS7.ReadInt64( "VB104" ).Content; // V104 = VB104 Float v112 = siemensS7.ReadFloat("V112").Content; Double v116 = siemensS7.ReadDouble("V116").Content; String vw124 = siemensS7.ReadString("VW124").Content; // V124 = VW124 String vw130 = siemensS7.ReadString("V130").Content; // VW130 = V130 String vw140 = siemensS7.ReadString("V140", Charset.forName("UTF-8")).Content; // 底层会自己判断,我们只需要根据JAVA中的数据类型,区判断字节数即可; System.out.println("M10.0: " + m10); System.out.println("V100: " + v100); System.out.println("V104: " + v104); System.out.println("V112: " + v112); System.out.println("V116: " + v116); System.out.println("VW124: " + vw124); System.out.println("VW130: " + vw130); System.out.println("VW140: " + vw140); siemensS7.ConnectClose( ); // 关闭长连接 } }
输出结果与短连接相同:
M10.0: true V100: 100 V104: 10000 V112: 4.44 V116: 8.88 VW124: zidan VW130: jiguiquan VW140: 吉桂权
但是只建立了一次连接:
显然,我们工作中,应该更多的使用长连接的方式对PLC进行读写操作!
4、正确且更优雅的方式对PLC进行操作:
// 实际上所有的读写都是返回是否成功的标记的,在实际的开发中,需要严格的判定,怎么判定呢?如下方代码: OperateResultExOne<Boolean> readM20 = siemensS7.ReadBool("M20"); if (readM20.IsSuccess) { // 读取成功,这时候获取Content才是正确的值 Boolean m20 = readM20.Content; } else { String message = readM20.Message; }