【2023】JAVA和PLC实现通讯读取写入数据,以三菱PLC举例

1、创建maven工程引入依赖

        <!-- https://mvnrepository.com/artifact/com.github.dathlin/HslCommunication -->
        <dependency>
            <groupId>com.github.dathlin</groupId>
            <artifactId>HslCommunication</artifactId>
            <version>3.3.1</version>
        </dependency>

进行PLC连接需要了解两个概念:长连接和短连接。为了更好的说明当前的通信情况,我把所有的通信拆分为四个部分,连接,发,收,断开。

  • 短连接:连接,发,收,断开,连接,发,收,断开,连接,发,收,断开,连接,发,收,断开…无限循环
  • 长连接:连接,发,收,发,收,发,收,发,收,发,收,发,收,发,收,发,收,发,收…断开

然后我们来看看异常的情况,短连接的异常比较好处理,反正每次请求都是先连接,关键来看长连接的异常
长连接:连接,发,收,发,收…异常,连接,发,收,发,收,异常,连接,连接,连接…收,发,收,发,收,发,收,发,收…断开
这里第一个异常发生后,第二次读写立即连接上去并且成功,第二个异常触发后,一直读写失败,说明就是一直连接不上去。
对于HSL组件来说,不需要重复连接服务器或是plc,无论是短连接还是长连接,都只需要一直读写就OK了,对读写的结果进行判定,即使发生异常了,读写失败了,也要一直坚持,网络好的时候,读写会恢复成功的。

2、通过代码进行连接通讯

  • 实现短连接
// 实例化对象,指定PLC的ip地址和端口号
MelsecMcNet melsecMc = new MelsecMcNet( "192.168.1.110", 6000 );
// 举例读取D100的值
short D100 = melsecMc.ReadInt16( "D100" ).Content;
  • 实现长连接
// 实例化对象,指定PLC的ip地址和端口号
MelsecMcNet melsecMc = new MelsecMcNet( "192.168.1.110", 6000 );
// 如果网络不太理想,配置了两个端口,一个有问题,立即切换另一个的话,可以配置如下的代码
// melsecMc.GetPipeSocket( ).SetMultiPorts( new int[] { 6000, 6001 } );

// 连接对象
OperateResult connect = melsecMc.ConnectServer( );
if (!connect.IsSuccess)    //用于判断是否连接成功,返回true/false
{
    Console.WriteLine( "connect failed:" + connect.Message );
    return;
}

// 举例读取D100的值
short D100 = melsecMc.ReadInt16( "D100" ).Content;

// 实际上所有的读写都是返回是否成功的标记的,在实际的开发中,需要严格的判定,怎么判定呢?如下的代码
OperateResult<short> readD100 = melsecMc.ReadInt16( "D100" );
if (readD100.IsSuccess)
{
    // 读取成功,这时候获取Content才是正确的值
    short value = readD100.Content;
}
else
{
    // 读取失败,如果仍然坚持去获取Content的值,就为0
}

// 读写是否成功的情况,应用于几乎所有的读写代码,只要看清楚返回的数据类型即可
melsecMc.ConnectClose( );

3、基本的读取示例

MelsecMcNet melsec_net = new MelsecMcNet( "192.168.0.100", 6000 );
melsec_net.SetPersistentConnection( );                                           // 设置长连接的操作

// 此处以D寄存器作为示例,此处没有判断是否读写成功,所以是有风险的,如果通讯失败,所有的值都不是正确的
short short_D1000 = melsec_net.ReadInt16( "D1000" ).Content;                     // 读取D1000的short值 
ushort ushort_D1000 = melsec_net.ReadUInt16( "D1000" ).Content;                  // 读取D1000的ushort值
int int_D1000 = melsec_net.ReadInt32( "D1000" ).Content;                         // 读取D1000-D1001组成的int数据
uint uint_D1000 = melsec_net.ReadUInt32( "D1000" ).Content;                      // 读取D1000-D1001组成的uint数据
float float_D1000 = melsec_net.ReadFloat( "D1000" ).Content;                     // 读取D1000-D1001组成的float数据
long long_D1000 = melsec_net.ReadInt64( "D1000" ).Content;                       // 读取D1000-D1003组成的long数据
ulong ulong_D1000 = melsec_net.ReadUInt64( "D1000" ).Content;                    // 读取D1000-D1003组成的long数据
double double_D1000 = melsec_net.ReadDouble( "D1000" ).Content;                  // 读取D1000-D1003组成的double数据
string str_D1000 = melsec_net.ReadString( "D1000", 10 ).Content;                 // 读取D1000-D1009组成的条码数据

// 读取数组
short[] short_D1000_array = melsec_net.ReadInt16( "D1000", 10 ).Content;         // 读取D1000的short值 
ushort[] ushort_D1000_array = melsec_net.ReadUInt16( "D1000", 10 ).Content;      // 读取D1000的ushort值
int[] int_D1000_array = melsec_net.ReadInt32( "D1000", 10 ).Content;             // 读取D1000-D1001组成的int数据
uint[] uint_D1000_array = melsec_net.ReadUInt32( "D1000", 10 ).Content;          // 读取D1000-D1001组成的uint数据
float[] float_D1000_array = melsec_net.ReadFloat( "D1000", 10 ).Content;         // 读取D1000-D1001组成的float数据
long[] long_D1000_array = melsec_net.ReadInt64( "D1000", 10 ).Content;           // 读取D1000-D1003组成的long数据
ulong[] ulong_D1000_array = melsec_net.ReadUInt64( "D1000", 10 ).Content;        // 读取D1000-D1003组成的long数据
double[] double_D1000_array = melsec_net.ReadDouble( "D1000", 10 ).Content;      // 读取D1000-D1003组成的double数据

4、具体的详细读取demo

常用方法

  • setNetworkNumber() :设置网络号
  • ConnectServer():把连接由短连接切换为长连接,需要断开重新连接
  • SetPersistentConnection():设置长连接的操作,不需要断开重新连接
  • IsSuccess :判断是否连接成功
  • read 开头的方法都为读取方法,
  • write 开头的方法都为写入的方法
public class MelsenMcClent {

    private MelsecMcNet clent;
    private String ip;
    private int port;

    /**
     * 初始化连接对象
     * @author zhengfuping
     * @date 2023/1/9 8:07
     * @version 1.0
     */
    public boolean iniConnet(String ip,int port){
        clent = new MelsecMcNet(ip, port);
        this.ip = ip;
        this.port = port;
        clent.setNetworkStationNumber((byte) 0x00);
        clent.setSleepTime(10);
        OperateResult connet = clent.ConnectServer();
        return connet.IsSuccess;
    }
    /**
     * @description: TODO 重试连接
     * @author zhengfuping
     * @date 2023/1/9 8:07
     * @version 1.0
     */
    public boolean restartConnet(){
        OperateResult connet = clent.ConnectServer();
        if(connet != null && connet.IsSuccess){
            return true;
        }else{
            clent = new MelsecMcNet(this.ip, this.port);
            clent.setNetworkStationNumber((byte) 0x00);
            clent.setSleepTime(10);
            connet = clent.ConnectServer();
            return connet.IsSuccess;
        }
    }

    /**
     * 读取字符串
     * */
    public String read(String address,int length){
        OperateResultExOne<String> result = clent.ReadString(address,(short)length);
        return result.Content.trim();
    }
    /**
     * 读取32位数字
     * */
    public Integer readInt32(String address){
        OperateResultExOne<Integer> result = clent.ReadInt32(address);
        return result.Content;
    }
    /**
     * 读取16位数字
     * */
    public Short readInt16(String address){
        OperateResultExOne<Short> result = clent.ReadInt16(address);
        return result.Content;
    }
    /**
     * 读取浮点数
     * */
    public Float readFloat(String address){
        OperateResultExOne<Float> result = clent.ReadFloat(address);
        return result.Content;
    }

    /**
     * 写入字符串
     * */
    public boolean write(String address,String dataVal){
        OperateResult result = clent.Write(address, dataVal);
        return result.IsSuccess;
    }
    /**
     * 写入数字
     * */
    public boolean write(String address,short dataVal){
        OperateResult result = clent.Write(address, dataVal);
        return result.IsSuccess;
    }

//下面的或者工具类对象可以自己根据业务情况设置,一般三菱PLC是一个端口只能有一个客户端去连接
  /**
     * 创建单例
     * */
    private static class Interior{
        private static MelsenMcClent CLENT = new MelsenMcClent();

        public static MelsenMcClent getConnect(String state){
            MelsenMcClent writeClent;
            if (state.equals("Write")){
                writeClent = getWriteClent();
            }else if (state.equals("Read")){
                writeClent = getReadClent();
            }else {
                writeClent = null;
            }
            return writeClent;
        }

        /**
         * 写入对象
         * */
        private static MelsenMcClent getWriteClent(){
            MelsenMcClent writeClent=Interior.CLENT;
            writeClent.iniConnet("192.168.120.158", 4000);
            return writeClent;
        }
        /**读取对象*/
        private static MelsenMcClent getReadClent(){
            MelsenMcClent readClent=Interior.CLENT;
            readClent.iniConnet("192.168.120.158", 4001);
            return readClent;
        }
    }

    /**
     * 返回连接工具对象(因为我这边需要读取写入分开,所以设置为两个,可以不设置)
     * @param state 读取/写入状态
     * @return MelsenMcClent
     */
    public static MelsenMcClent getConnect(String state){
        return Interior.getConnect(state);
    }

###具体调用读取和写入的代码如下
因为我本地没有连接PLC就不粘贴日志的输出结果了,可以使用下面的软件进行配合测试

使用软件进行测试

1、写入值:

2、读取值

刚刚写入的值

  • 软件名字叫HslCommunication,可以直接他家官网下载,具体页面如下,选择好PLC型号,然后输入IP和端口就可以连接测试了

🔗一些同学说依赖有问题;我把我的发百度网盘上了,需要的可以自己拿

链接: https://pan.baidu.com/s/1NcpAVuzSqURhWs7wfUhMQg?pwd=rg12
提取码: rg12

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
xiaoxingxing的头像xiaoxingxing管理团队
上一篇 2023年12月5日
下一篇 2023年12月5日

相关推荐