using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; namespace FLY.Modbus.WithThread { /// /// 使用线程版本 /// 支持自定义逻辑 /// public class ModbusMapper_Client : ModbusMapper { /// /// 更新周期,单位ms, /// public int UpdateInterval = 100; /// /// 实际更新间隔 /// public TimeSpan ActUpdateInterval { get; private set; } /// /// 工作中 /// public bool IsRunning { get; private set; } /// /// 异常次数 /// public int ErrorCnt { get; private set; } class RegWrite { public DataToRegs dr; public object val; public RegWrite(DataToRegs dr, object val) { this.dr = dr; this.val = val; } } List rws = new List(); /// /// tcp client /// public ClientTCP Client { get; set; } CancellationTokenSource cts; /// /// /// /// 服务器ep public ModbusMapper_Client(IPEndPoint ep) { Client = new ClientTCP(ep); } /// /// /// /// public override string ToString() { return Client.ToString(); } /// /// 启动 /// public void Start() { lock (this) { if (IsRunning) return; IsRunning = true; cts = new CancellationTokenSource(); Task.Factory.StartNew(OnPoll_update, cts.Token); } } /// /// 停止 /// public void Stop() { lock (this) { if (!IsRunning) return; cts.Cancel(); Client.Close(); } } void OnPoll_update() { Stopwatch stopwatch = new Stopwatch(); Stopwatch stopwatch_act = new Stopwatch(); bool isTimeForRead = true; _connect: while (true) { if (cts.IsCancellationRequested) goto _end; bool isconnected = Client.Connect(); if (cts.IsCancellationRequested) goto _end; if (isconnected) break; else Thread.Sleep(1000); } //刚连接成功。 //从本地PC 属性获取value 更新到 本地PLC 寄存器, //目的: 当从PLC读取寄存器值时,能比较不同,产生变化事件 foreach (var areaManager in mAreaManager) areaManager.SetAllIsPlcValueChanged(); //清空 写缓存 lock (rws) { rws.Clear(); } stopwatch.Restart(); isTimeForRead = true; while (true) { if (cts.IsCancellationRequested) goto _end; if (stopwatch.ElapsedMilliseconds >= UpdateInterval) { isTimeForRead = true; } if (isTimeForRead) { isTimeForRead = false; stopwatch.Restart(); if (!stopwatch_act.IsRunning) { stopwatch_act.Restart(); } else { ActUpdateInterval = stopwatch_act.Elapsed; stopwatch_act.Restart(); } if (!UpdateReadData()) { if (cts.IsCancellationRequested)//可能是外部强制关闭 goto _end; goto _error;//连接断开,终止更新线程 } } //输出写入数据 if (!UpdateWriteData()) { if (cts.IsCancellationRequested)//可能是外部强制关闭 goto _end; goto _error;//连接断开,终止更新线程 } Thread.Sleep(30); } _error: ErrorCnt++; goto _connect; _end: Client.Close(); IsRunning = false; } /// /// 更新一次读全部数据 /// /// bool UpdateReadData() { foreach (var areaManager in mAreaManager) { areaManager.MakePlanReal(); } //int curr_area_idx = 0; foreach (var areaManager in mAreaManager) { foreach(Plan p_doing in areaManager.plan) { switch (p_doing.area) { case PLCAddressArea.Coil: { if (!Client.Do_01(p_doing.addr, p_doing.num, out IEnumerable values)) return false; SetModbusData(PLCAddressArea.Coil, p_doing.addr, values); } break; case PLCAddressArea.Register: { if (!Client.Do_03(p_doing.addr, p_doing.num, out IEnumerable values)) return false; SetModbusData(PLCAddressArea.Register, p_doing.addr, values); } break; } } } return true; } /// /// PC -> PLC /// 为命名数据赋值,会改变Modbus的寄存器值,并产生通知 /// /// 数据映射 /// 数据值,不能为空 /// public override void SetNameData(DataToRegs dr, object val) { RegWrite rw = new RegWrite(dr, val); lock (rws) { rws.Add(rw); } } /// /// 提取全部写命令缓冲区 输出 /// bool UpdateWriteData() { while (true) { RegWrite rw; lock (rws) { if (rws.Count() == 0) break; rw = rws.First(); rws.RemoveAt(0); } //强制令读时,推送数据 rw.dr.isPlcValueChanged = true; object plc_value = rw.dr.ToPLCObj(rw.val); switch (rw.dr.dataArea) { case PLCAddressArea.Coil: if (!Client.Do_0F((UInt16)rw.dr.addr, new bool[] {(bool)plc_value })) return false; break; default: //case PLCAddressArea.Register: if (!Client.Do_10((UInt16)rw.dr.addr, (UInt16[])plc_value)) return false; break; } } return true; } } }