using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using NModbus; using System.Net.Sockets; using System.Threading; using System.Configuration; using System.Net.NetworkInformation; namespace CarLocalMeter { public class PlcCls { private Log lg = Log.GetInstance(); //写日志 Dictionary locationInfo = new Dictionary(); private static ModbusFactory modbusFactory; private static IModbusMaster master; ushort[] registerBuffer; //参数(分别为站号,起始地址,长度) byte slaveAddress = 0; ushort startAddress; ushort numberOfPoints = 1; bool blThreadFlag;//数据采集线程开关 Thread DataCollectThread = null;//采集进程 #region 重连接 int tryReconnNetTimeSpan = AppConfigCache.rfidConnTime; private void ReConn() { bool isSuc = false; using (Ping ping = new Ping()) { PingReply reply = null; for (int i = 0; i < tryReconnNetTimeSpan * 60; i++) { try { reply = ping.Send(AppConfigCache.plcIp, 1000); if (reply.Status == IPStatus.Success) { isSuc = true; break; } else { lg.WriteLog(LogType.RfidLoc, "Ping不通"); continue; } } catch (Exception ex) { lg.WriteLog(LogType.RfidLoc, $"尝试自动恢复连接失败:{ex.Message}"); return; } } } //建立连接 if (isSuc) { Start(); } } #endregion /// /// 这里需尝试不停的连接,若没有成功,则继续连,一直到成功或者连续几次不成功,给出提示 /// private void ConnectionPlc() { //初始化modbusmaster modbusFactory = new ModbusFactory(); //在本地测试 所以使用回环地址,modbus协议规定端口号 502 master = modbusFactory.CreateMaster(new TcpClient(AppConfigCache.plcIp, AppConfigCache.plcPort)); //设置读取超时时间 master.Transport.ReadTimeout = 2000; master.Transport.Retries = 2000; blThreadFlag = true; DataCollectThread = new Thread(new ThreadStart(DataCollect)); string[] strLocation = AppConfigCache.plcLocation.Split(','); string[] strLocationName = AppConfigCache.plcLocationName.Split(','); for (int i = 0; i < strLocation.Length; i++) { locationInfo.Add(strLocationName[i], Convert.ToInt32(strLocation[i])); } } public void Start() { try { blThreadFlag = true; ConnectionPlc(); CacleCls.plcState = 0; DataCollectThread.Start(); } catch { CacleCls.plcMsgInfo = "PLC连接失败,请检查配置信息及PLC网络"; CacleCls.plcState = 1; } } public void Stop() { try { blThreadFlag = false; if (master != null) master.Dispose(); } catch { } } bool bFlag = false; #region 数据采集 private List lWeight = new List(); /// /// 数据采集线程 /// private void DataCollect() { if (!blThreadFlag) return; while (blThreadFlag) { try { //不停的给plc写值,我这写1234,他那会改为4321 DataCollectWrite(0, 1234); //将重量信息写入到plc重量位置 DataCollectWrite2(4, (ushort)CacleCls.weight); foreach (KeyValuePair kvp in locationInfo) { startAddress = (ushort)kvp.Value; registerBuffer = master.ReadHoldingRegisters(slaveAddress, startAddress, numberOfPoints); string db = ""; for (int i = 0; i < registerBuffer.Length; i++) { db += registerBuffer[i]; } switch (kvp.Key) { case "plcState": { if (db != "4321") CacleCls.plcState = 1; else CacleCls.plcState = 0; }; break; case "leftGs": //左光栅 CacleCls.leftGs = db; break; case "rightGs": //右光栅 CacleCls.rightGs = db; break; case "topHw": //前红外 CacleCls.topJg = db; break; case "bottomHw": //后红外 CacleCls.bottomJg = db; break; case "rgLight": //红绿灯 CacleCls.rgLight = db; break; } } #region /* try { if (CacleCls.weight < 500) { if (!bFlag) { //实际上红绿灯是自动控制的,所以没影响 //DataCollectWrite(); } bFlag = true; } else { if (bFlag) { } bFlag = false; } } catch(Exception ex) { } //*/ #endregion Thread.Sleep(AppConfigCache.sleepTime); //读取到db之后,则需要将这个db值写入到缓存中 } catch (Exception ex) { try { master.Dispose(); } catch { } CacleCls.plcState = 1; blThreadFlag = false; lg.WriteLog(LogType.PlcLog, $"获取PLC数据异常:{ex.Message}"); ReConn(); } } } #region 读取2个寄存器的情况,例如数据是int32 public void getDb() { registerBuffer = master.ReadHoldingRegisters(slaveAddress, 4, 2); int[] i = new int[1]; Buffer.BlockCopy(registerBuffer, 0, i, 0, 4); Console.WriteLine(i[0]); /* //将short转换为int,由于我们在插入到Modbus寄存器中一个int32会拆分为2个int16进行插入, //因此我们在获取寄存器数据类型为int32的数据时,返回的是2个short,此时需要转换为1个int //也就是用Buffer.BlockCopy即可。当然类似flat的搞法也是一样的,见下面的例子 short[] s = { 1, 2 }; int[] i = new int[1]; float[] f = new float[1]; Buffer.BlockCopy(s, 0, i, 0, 4); Buffer.BlockCopy(s, 0, f, 0, 4); Console.WriteLine("{0} {1}", i[0], f[0]); */ } #endregion /// /// 传入需要写入的地址 /// /// /// public async void DataCollectWrite(ushort startAddr, ushort uValue) { try { await master.WriteSingleRegisterAsync(slaveAddress, startAddr, uValue); } catch(Exception ex) { //这里写个日志,记录下未成功 lg.WriteLog(LogType.PlcLog, $"写入位置:[{startAddr}]的值:[{uValue}]异常。错误信息:{ex.Message}"); } } /// /// 写入占位2个的情况,因为一个寄存器只能写入到int16,所以数据类型为int32需要写入2个寄存器 /// /// /// public async void DataCollectWrite2(ushort startAddr, uint uValue) { try { ushort[] uSValue = new ushort[2]; uSValue[0] = BitConverter.ToUInt16(BitConverter.GetBytes(uValue), 0); uSValue[1] = BitConverter.ToUInt16(BitConverter.GetBytes(uValue), 2); await master.WriteMultipleRegistersAsync(slaveAddress, startAddr, uSValue); } catch (Exception ex) { //这里写个日志,记录下未成功 lg.WriteLog(LogType.PlcLog, $"写入位置:[{startAddr}]的值:[{uValue}]异常。错误信息:{ex.Message}"); } } #endregion } }