JAVA与西门子S7 PLC通信,方式一:S7connector

背景

        在公司项目中,需要用到和PLC进行通讯,经过搜索后查询到使用JAVA与PLC通信两种方式,测试后达到正常读写的目的,于是记录下学习过程。

环境

Spring+SpringMVC+MybatisPlus  / SpringBoot

PLC: 西门子 S7-1500/S7-1200(1214C)

PLC设置

 第一步: 使用  TIA Portal 创建DB数据块,设置地址为:12 (地址可设置为1-59999 任意一个数字);

 第二步:在DB块中插入新行,此处设置了8个Bool类型,1个Byte、3个String(参考)

        首次添加时不会显示“偏移量”,需要右键数据块,选择属性,在‘常规-属性’中,取消‘优化的块访问’后将程序下载到设备后即可以看到偏移量。

 正在准备设备模拟文章中...

JAVA使用S7Connector读写数据

读取/写入单个数据

第一步: 在pom中增加依赖:添加s7Connector依赖。

  <!-- https://mvnrepository.com/artifact/com.github.s7connector/s7connector -->
    <dependency>
      <groupId>com.github.s7connector</groupId>
      <artifactId>s7connector</artifactId>
      <version>2.1</version>
    </dependency>

        当前项目中需要和PLC通信实现读取PLC地址中的数据、向PLC地址中写入数据,所以下面通过三个方法来实现 连接-读取-写入 这三个目标。
第二步: 初始化PLC链接


    /**
     * 初始化PLC连接
     */
    public S7Connector initConnect(){
        //PLC地址
        String ipAddress = "192.168.1.2";
        //默认端口
        String port = "102";
         S7Connector s7Connector =  S7ConnectorFactory
                .buildTCPConnector()
                .withHost(ipAddress)
                .withPort(port)
                .withTimeout(10000) //连接超时时间
                .withRack(0) 
                .withSlot(1) 
                .build();
        S7Serializer  s7Serializer2L = S7SerializerFactory.buildSerializer(s7Connector);
    return s7Connector;
        
    }

第三步:读取PLC地址中的数据

         PLC中待读取的数据地址为DB1000,偏移量为2,数据类型word (2位的16进制数据);

         (稍后补充PLC截图)

        读取PLC中数据很简单,调用s7connect.read 即可,但是需要根据PLC中存储的数据类型将二进制数组解析为Java数据类型;

/**
* 读取PLC中的数据,字符串类型
*
**/
public void readPlcData() {

        S7Connector s7Connector = initConnect(); 
        //第一个参数:DaveArea.DB 表示读取PLC的地址区域为DB
        //第二个参数:DB地址,若plc中是DB1000,则填1000   
        //第三个参数:数据长度, <=plc中两个偏移量的间隔,当前偏移量为1000,下一个地址偏移量为1100,则长度可填 0-1000;
        //第三个参数:偏移量
        byte[] barcodeByte = s7Connector.read(DaveArea.DB, 1000, 2, 0);
        //由于当前PLC地址中保存的数据类型是字符串类型,所以直接将byte[] 转成string即可;
        String barcode = new String(barcodeByte);
        System.out.println(barcode);
        try {
            s7Connector.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

读取PLC 中word类型的数据如下:

/**
* 读取PLC中的数据
*
**/
public void readPlcData() {

        S7Connector s7Connector = initConnect(); 
        //第一个参数:DaveArea.DB 表示读取PLC的地址区域为DB
        //第二个参数:DB地址,若plc中是DB1000,则填1000   
        //第三个参数:数据长度, <=plc中两个偏移量的间隔,当前偏移量为1000,下一个地址偏移量为1100,则长度可填 0-1000;
        //第四个参数:偏移量
        byte[] barcodeByte = s7Connector.read(DaveArea.DB, 1000, 2, 0);
        //由于当前PLC地址中保存的数据类型是字符串类型,所以直接将byte[] 转成string即可;
        String barcode =byteToHex(barcodeByte);
        System.out.println(barcode);
        try {
            s7Connector.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

 /**
     * byte数组转hex
     * @param bytes
     * @return
     */
    public static String byteToHex(byte[] bytes){
        String strHex = "";
        StringBuilder sb = new StringBuilder("");
        for (int n = 0; n < bytes.length; n++) {
            strHex = Integer.toHexString(bytes[n] & 0xFF);
            sb.append((strHex.length() == 1) ? "0" + strHex : strHex); // 每个字节由两个字符表示,位数不够,高位补0
        }
        return sb.toString().trim();
    }

第四步:向PLC地址中写入数据

 写入数据与读取数据类似,调用write方法即可;但是注意写数据时数据类型需要转成二进制数组;

/**
* 向PLC中写数据
*
**/
public void writePlcData() {

        S7Connector s7Connector = initConnect(); 
        //第一个参数:DaveArea.DB 表示读取PLC的地址区域为DB
        //第二个参数:DB地址,若plc中是DB1000,则填1000   
        //第三个参数:偏移量
        //第四个参数:写入的数据 二进制数组byte[],由于plc中地址的数据类型是word,所以写入的数据必须是4位的16进制数据
        connector2L.write(DaveArea.DB,1000, 4,hexStringToBytes("0001"));
       
        try {
            s7Connector.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

 /**
     * 将16进制字符串转成二进制数组
     * @param hexString
     * @return
     */
    public static byte[] hexStringToBytes(String hexString) {
        if (hexString == null || hexString.equals("")) {
            return null;
        }
        hexString = hexString.toUpperCase();
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return d;
    }

        以上方式为读取/写入数据块单个偏移量的数据,下面的方式为批量读取/写入数据块数据。

批量读取/写入数据        

与单个读取/写入数据相同,第一步也是需要创建连接,然后再进行读取/写入:

第一步:创建连接


    /**
     * 初始化PLC连接
     */
    public S7Connector initConnect(){
        //PLC地址
        String ipAddress = "192.168.1.2";
        //默认端口
        String port = "102";
         S7Connector s7Connector =  S7ConnectorFactory
                .buildTCPConnector()
                .withHost(ipAddress)
                .withPort(port)
                .withTimeout(10000) //连接超时时间
                .withRack(0) 
                .withSlot(1) 
                .build();
        S7Serializer  s7Serializer2L = S7SerializerFactory.buildSerializer(s7Connector);
    return s7Connector;
        
    }

第二步:根据数据块以及偏移量创建对象


import com.github.s7connector.api.annotation.S7Variable;
import com.github.s7connector.impl.utils.S7Type;
import lombok.Data;

@Data
public class PLCData {

    /**
     * type是这个点位在PLC中设置的类型,源码会解析其长度;
     * byteOffset对应PLC偏移量中的整数部分;
     * bitOffset指偏移量的小数部分,bitOffset指第几个bit.
     * byteOffset和bitOffset 也可理解为返回的byte[]中第byteOffset到bitOffset
     */
    @S7Variable(type= S7Type.BOOL,byteOffset = 0,bitOffset = 0)
    public Boolean data10;//bool型的值不要用private

    @S7Variable(type=S7Type.BOOL,byteOffset = 0,bitOffset = 1)
    public Boolean data11;

    @S7Variable(type=S7Type.BOOL,byteOffset = 0,bitOffset = 2)
    public Boolean data12;

    @S7Variable(type=S7Type.BOOL,byteOffset = 0,bitOffset = 3)
    public Boolean data13;

    @S7Variable(type=S7Type.BOOL,byteOffset = 0,bitOffset = 4)
    public Boolean data14;

    @S7Variable(type=S7Type.BOOL,byteOffset = 0,bitOffset = 5)
    public Boolean data15;

    @S7Variable(type=S7Type.BOOL,byteOffset = 0,bitOffset = 6)
    public Boolean data16;

    @S7Variable(type=S7Type.BOOL,byteOffset = 0,bitOffset = 7)
    public Boolean data17;

    @S7Variable(type=S7Type.BYTE,byteOffset = 1,bitOffset = 0)
    public Byte dataB1;

    @S7Variable(type=S7Type.STRING,byteOffset = 2,bitOffset = 0)
    public String dataS1;

    @S7Variable(type=S7Type.STRING,byteOffset = 258,bitOffset = 40)
    public String dataS2;

    @S7Variable(type=S7Type.STRING,byteOffset = 514,bitOffset = 40)
    public String dataS3;

    @S7Variable(type=S7Type.STRING,byteOffset = 770)
    public String dataS4;

}

第三步:读取数据

       通过dispense 即可读取在对象中设置的所有数据(根据偏移量自动填充到对象的属性中。)

//参数1:根据偏移量和数据类型创建的对象
//参数2:DB数据块地址
//参数3:数据偏移量-返回的byte向后延几位
PlcDb plcDb = S7ConnectUti.getS7Serializer().dispense(PlcDb.class,12,0);

第四步:写入数据

/**
* address: DB数据块地址
* offset: 偏移量
* plcDb :  根据DB块的偏移量和数据类型创建的对象-需要修改的数据对应的属性需要赋值
*/
 public static void writeBooleanPlcData(Integer address,Integer offset,PlcDb plcDb) {
        getS7Serializer().store(plcDb,address, offset);
 }

 //示例
PlcDb plcDbWrite = new PlcDb();
plcDbWrite.setData10(true);
S7ConnectUti.writeBooleanPlcData(12,0,plcDbWrite);

源码地址:

PLCConnectDemo: JAVA连接PLC,S7connect、OPCUA(Milo)

方法在utils下面的   S7ConnectUti 中。

问题记录:

1. 提示错误:Result: the CPU does not support reading a bit block of length<>1

检查查询的实体类中字段是否为public,若为private则会出现错误。

2. 提示错误: the desired address is beyond limit for this PLC

JAVA数据类型和PLC中的数据类型不匹配,我的错误为PLC中类型为Array[0..9] of char,不能使用String直接接收,需要使用上文中【读取PLC地址中的数据】单独读取并进行解析。

  

猜你喜欢

转载自blog.csdn.net/qq_31451767/article/details/119122231