PLCLink.cs 8.12 KB
Newer Older
潘栩锋's avatar
潘栩锋 committed
1
using FLY.Modbus;
2
using PropertyChanged;
潘栩锋's avatar
潘栩锋 committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
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
    {

18 19 20 21 22 23
        /// <summary>
        /// 一次读取的数据量
        /// </summary>
        const int MaxRegsOfOneRead = 50;
        const int MaxCoilsOfOneRead = 100;

潘栩锋's avatar
潘栩锋 committed
24 25
        //D200	4x201 通道数量
        //D201	4x202 设置值 更新
26
        //D202	4x203 当前值 更新 //不用
潘栩锋's avatar
潘栩锋 committed
27
        //D400	4x401 设置值 160个
28
        //D600	4x601 当前值 160个 //不用
潘栩锋's avatar
潘栩锋 committed
29 30 31
        //M3	0x4	风环开关
        //M4	0x5	检测电流

32
        public FLY.Modbus.WithThread.ClientTCP mclient;
潘栩锋's avatar
潘栩锋 committed
33 34 35 36 37 38 39 40 41 42
        /// <summary>
        /// 更新周期,单位ms,
        /// </summary>
        public int UpdateInterval = 100;

        /// <summary>
        /// 实际更新间隔
        /// </summary>
        public TimeSpan ActUpdateInterval { get; private set; }

43
 
潘栩锋's avatar
潘栩锋 committed
44 45 46
        /// <summary>
        /// 加热通道数
        /// </summary>
47 48
        [DoNotCheckEquality]
        public UInt16 ChannelCnt { get; set; }
潘栩锋's avatar
潘栩锋 committed
49 50 51
        /// <summary>
        /// 加热量更新
        /// </summary>
52 53
        [DoNotCheckEquality]
        public UInt16 HeatUpdate { get; set; }
潘栩锋's avatar
潘栩锋 committed
54 55 56 57 58 59 60 61 62 63

        /// <summary>
        /// 当前电流 有没?
        /// </summary>
        public bool HasElectricity { get; private set; }
        /// <summary>
        /// 风机是否启动?
        /// </summary>
        public bool HasFan { get; private set; }
        /// <summary>
64
        /// 加热量更新 读
潘栩锋's avatar
潘栩锋 committed
65
        /// </summary>
66
        public UInt16 HeatUpdate_R { get; private set; }
潘栩锋's avatar
潘栩锋 committed
67

68
        public int Errno { get; set; } = -1;
潘栩锋's avatar
潘栩锋 committed
69

70 71 72 73 74 75 76 77 78 79 80 81 82

        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// 工作中
        /// </summary>
        public bool IsRunning { get; private set; }
        /// <summary>
        /// 异常次数
        /// </summary>
        public int ErrorCnt { get; private set; }

        CancellationTokenSource cts;
83

潘栩锋's avatar
潘栩锋 committed
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
        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>();
104

潘栩锋's avatar
潘栩锋 committed
105 106 107 108 109

        private void PLCLink_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "ChannelCnt")
            {
110
                //D200	4x201	通道数量
潘栩锋's avatar
潘栩锋 committed
111 112 113 114 115 116 117 118
                RegWrite regWrite = new RegWrite(PLCAddressArea.Register, 200, new UInt16[] { ChannelCnt });
                lock (rws)
                {
                    rws.Add(regWrite);
                }
            }
            else if (e.PropertyName == "HeatUpdate")
            {
119
                //D201	4x202	设置值 更新
潘栩锋's avatar
潘栩锋 committed
120 121 122 123 124 125 126 127
                RegWrite regWrite = new RegWrite(PLCAddressArea.Register, 201, new UInt16[] { HeatUpdate });
                lock (rws)
                {
                    rws.Add(regWrite);
                }
            }
        }

128 129

        public void Start()
潘栩锋's avatar
潘栩锋 committed
130
        {
131
            lock (this)
潘栩锋's avatar
潘栩锋 committed
132
            {
133 134 135 136 137
                if (IsRunning)
                    return;
                IsRunning = true;
                cts = new CancellationTokenSource();
                Task.Factory.StartNew(OnPoll_update, cts.Token);
潘栩锋's avatar
潘栩锋 committed
138 139
            }

140 141
        }
        public void Stop()
潘栩锋's avatar
潘栩锋 committed
142
        {
143 144 145 146 147 148 149
            lock (this)
            {
                if (!IsRunning)
                    return;
                cts.Cancel();
                mclient.Close();
            }
潘栩锋's avatar
潘栩锋 committed
150 151
        }

152

潘栩锋's avatar
潘栩锋 committed
153 154 155 156
        void OnPoll_update()
        {
            Stopwatch stopwatch = new Stopwatch();

157 158 159

        _connect:
            while (true)
潘栩锋's avatar
潘栩锋 committed
160
            {
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
                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;

潘栩锋's avatar
潘栩锋 committed
186 187 188 189 190 191 192
                if (stopwatch.ElapsedMilliseconds >= UpdateInterval)
                {
                    //检查上次更新周期是否完成
                    ActUpdateInterval = stopwatch.Elapsed;
                    stopwatch.Restart();

                    if (!UpdateReadData())
193 194 195 196 197 198 199
                    {
                        if (cts.IsCancellationRequested)//可能是外部强制关闭
                            goto _end;

                        goto _error;//连接断开,终止更新线程
                    }
                    
潘栩锋's avatar
潘栩锋 committed
200
                }
201
                
潘栩锋's avatar
潘栩锋 committed
202 203
                //输出写入数据
                if (!UpdateWriteData())
204 205 206 207 208 209 210 211
                {
                    if (cts.IsCancellationRequested)//可能是外部强制关闭
                        goto _end;

                    goto _error;//连接断开,终止更新线程
                }
                Errno = 0;
                Thread.Sleep(30);
潘栩锋's avatar
潘栩锋 committed
212
            }
213 214 215 216 217 218 219 220

        _error:
            Errno = -1;
            ErrorCnt++;
            goto _connect;

        _end:
            mclient.Close();
221
            Errno = -1;
潘栩锋's avatar
潘栩锋 committed
222 223 224 225 226 227 228 229 230 231
            IsRunning = false;
        }

        bool UpdateReadData()
        {
            //M3	0x4	风环开关
            //M4	0x5	检测电流
            if (!mclient.Do_01(3, 2, out IEnumerable<bool> values))
                return false;
            HasFan = values.ElementAt(0);
232 233 234 235 236 237 238 239
            HasElectricity = values.ElementAt(1);

            //D201	4x202	设置值 更新
            if (!mclient.Do_03(201, 1, out IEnumerable<UInt16> values2))
                return false;

            HeatUpdate_R = values2.ElementAt(0);
            
潘栩锋's avatar
潘栩锋 committed
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
            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();
274 275 276 277 278 279 280 281
            List<RegWrite> writes = new List<RegWrite>();
            //D400	4x401	设置值 160个
            int addr = 400;

            int cnt = buf.Count();
            int offset = 0;
            //每次最多 MaxRegsOfOneRead
            while (true)
潘栩锋's avatar
潘栩锋 committed
282
            {
283 284 285 286 287 288
                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);
289
                writes.Add(rw);
290 291
                offset += cnt1;
                cnt -= cnt1;
潘栩锋's avatar
潘栩锋 committed
292

293 294 295 296 297 298
                if (cnt == 0)
                {
                    break;
                }
            }
            lock (rws)
299
            {
300
                rws.AddRange(writes);
301
            }
潘栩锋's avatar
潘栩锋 committed
302
        }
303

潘栩锋's avatar
潘栩锋 committed
304 305
    }
}