<dependency>
<groupId>com.github.dathlingroupId>
<artifactId>HslCommunicationartifactId>
<version>3.3.1version>
dependency>
进行PLC连接需要了解两个概念:长连接和短连接。为了更好的说明当前的通信情况,我把所有的通信拆分为四个部分,连接,发,收,断开。
然后我们来看看异常的情况,短连接的异常比较好处理,反正每次请求都是先连接,关键来看长连接的异常
长连接:连接,发,收,发,收…异常,连接,发,收,发,收,异常,连接,连接,连接…收,发,收,发,收,发,收,发,收…断开
这里第一个异常发生后,第二次读写立即连接上去并且成功,第二个异常触发后,一直读写失败,说明就是一直连接不上去。
对于HSL组件来说,不需要重复连接服务器或是plc,无论是短连接还是长连接,都只需要一直读写就OK了,对读写的结果进行判定,即使发生异常了,读写失败了,也要一直坚持,网络好的时候,读写会恢复成功的。
// 实例化对象,指定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( );
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数据
常用方法
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就不粘贴日志的输出结果了,可以使用下面的软件进行配合测试