using FLY.Modbus; using PropertyChanged; using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; namespace FLY.FeedbackRenZiJia.Server { public class PLCLink : IPLCLink { /// <summary> /// 一次读取的数据量 /// </summary> const int MaxRegsOfOneRead = 50; const int MaxCoilsOfOneRead = 100; //D200 4x201 通道数量 //D201 4x202 设置值 更新 //D202 4x203 当前值 更新 //不用 //D400 4x401 设置值 160个 //D600 4x601 当前值 160个 //不用 //M3 0x4 风环开关 //M4 0x5 检测电流 public FLY.Modbus.WithThread.ClientTCP mclient; /// <summary> /// 更新周期,单位ms, /// </summary> public int UpdateInterval = 100; /// <summary> /// 实际更新间隔 /// </summary> public TimeSpan ActUpdateInterval { get; private set; } /// <summary> /// 加热通道数 /// </summary> [DoNotCheckEquality] public UInt16 ChannelCnt { get; set; } /// <summary> /// 加热量更新 /// </summary> [DoNotCheckEquality] public UInt16 HeatUpdate { get; set; } /// <summary> /// 当前电流 有没? /// </summary> public bool HasElectricity { get; private set; } /// <summary> /// 风机是否启动? /// </summary> public bool HasFan { get; private set; } /// <summary> /// 加热量更新 读 /// </summary> public UInt16 HeatUpdate_R { get; private set; } public int Errno { get; set; } = -1; public event PropertyChangedEventHandler PropertyChanged; /// <summary> /// 工作中 /// </summary> public bool IsRunning { get; private set; } /// <summary> /// 异常次数 /// </summary> public int ErrorCnt { get; private set; } CancellationTokenSource cts; public PLCLink(IPEndPoint ep) { mclient = new Modbus.WithThread.ClientTCP(ep); this.PropertyChanged += PLCLink_PropertyChanged; } class RegWrite { public PLCAddressArea dataArea; public int addr; public object val; public RegWrite(PLCAddressArea dataArea, int addr, object val) { this.dataArea = dataArea; this.addr = addr; this.val = val; } } List<RegWrite> rws = new List<RegWrite>(); private void PLCLink_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "ChannelCnt") { //D200 4x201 通道数量 RegWrite regWrite = new RegWrite(PLCAddressArea.Register, 200, new UInt16[] { ChannelCnt }); lock (rws) { rws.Add(regWrite); } } else if (e.PropertyName == "HeatUpdate") { //D201 4x202 设置值 更新 RegWrite regWrite = new RegWrite(PLCAddressArea.Register, 201, new UInt16[] { HeatUpdate }); lock (rws) { rws.Add(regWrite); } } } 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(); mclient.Close(); } } void OnPoll_update() { Stopwatch stopwatch = new Stopwatch(); _connect: while (true) { if (cts.IsCancellationRequested) goto _end; bool isconnected = mclient.Connect(); if (cts.IsCancellationRequested) goto _end; if (isconnected) break; else Thread.Sleep(1000); } lock (rws) { rws.Clear(); } stopwatch.Restart(); while (true) { if (cts.IsCancellationRequested) goto _end; if (stopwatch.ElapsedMilliseconds >= UpdateInterval) { //检查上次更新周期是否完成 ActUpdateInterval = stopwatch.Elapsed; stopwatch.Restart(); if (!UpdateReadData()) { if (cts.IsCancellationRequested)//可能是外部强制关闭 goto _end; goto _error;//连接断开,终止更新线程 } } //输出写入数据 if (!UpdateWriteData()) { if (cts.IsCancellationRequested)//可能是外部强制关闭 goto _end; goto _error;//连接断开,终止更新线程 } Errno = 0; Thread.Sleep(30); } _error: Errno = -1; ErrorCnt++; goto _connect; _end: mclient.Close(); Errno = -1; IsRunning = false; } bool UpdateReadData() { //M3 0x4 风环开关 //M4 0x5 检测电流 if (!mclient.Do_01(3, 2, out IEnumerable<bool> values)) return false; HasFan = values.ElementAt(0); HasElectricity = values.ElementAt(1); //D201 4x202 设置值 更新 if (!mclient.Do_03(201, 1, out IEnumerable<UInt16> values2)) return false; HeatUpdate_R = values2.ElementAt(0); return true; } bool UpdateWriteData() { while (true) { RegWrite rw; lock (rws) { if (rws.Count() == 0) break; rw = rws.First(); rws.RemoveAt(0); } switch (rw.dataArea) { case PLCAddressArea.Register: if (!mclient.Do_10((UInt16)rw.addr, (IEnumerable<UInt16>)rw.val)) return false; break; } } return true; } /// <summary> /// 设置加热量 /// </summary> /// <param name="values"></param> public void SetHeat(IEnumerable<UInt16> values) { UInt16[] buf = values.ToArray(); List<RegWrite> writes = new List<RegWrite>(); //D400 4x401 设置值 160个 int addr = 400; int cnt = buf.Count(); int offset = 0; //每次最多 MaxRegsOfOneRead while (true) { int cnt1 = cnt; if (cnt1 > MaxRegsOfOneRead) cnt1 = MaxRegsOfOneRead; UInt16[] buf1 = new UInt16[cnt1]; Array.Copy(buf, offset, buf1, 0, cnt1); RegWrite rw = new RegWrite(PLCAddressArea.Register, addr+offset, buf1); writes.Add(rw); offset += cnt1; cnt -= cnt1; if (cnt == 0) { break; } } lock (rws) { rws.AddRange(writes); } } } }