Register.cs
13.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
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
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using DL.Com;
using DL.Utils;
namespace DL.Com.Protocol.Modbus
{
/// <summary>
/// 寄存器
/// </summary>
public abstract class Register
{
public delegate void StatusChangedEventHandler(StatusChangedEventArgs statusChangedEventArgs);
public event StatusChangedEventHandler StatusChanged;
protected static log4net.ILog log;
protected static ushort _serialnum = 0;
public Register(string logname = "logModbus")
{
log = log4net.LogManager.GetLogger(logname);
FunctionCodes = new Dictionary<RegisterFunction, byte>();
FrameStruct = new DataFrame();
}
/// <summary>
/// 功能码
/// </summary>
public Dictionary<RegisterFunction, byte> FunctionCodes { get; set; }
/// <summary>
/// 帧结构
/// </summary>
public DataFrame FrameStruct { get; protected set; }
/// <summary>
/// 数据帧--字节数据
/// </summary>
public byte[] DataFrame { get { return FrameStruct.ToBytes(); } }
/// <summary>
/// PLC地址
/// </summary>
public string PLCAddress { get; }
/// <summary>
/// 根据命令设置PDU参数
/// </summary>
/// <param name="cmdByte"></param>
/// <returns></returns>
protected abstract void SetParamPDU(CmdByte cmdByte);
/// <summary>
/// 设置MBAP报文头
/// </summary>
/// <param name="serialnum">事务处理标识,即报文序列号</param>
/// <param name="equipAddr">单元标识符,即设备地址</param>
protected void SetMBAP(ushort serialnum, byte equipAddr)
{
FrameStruct.SetMBAP(serialnum, equipAddr);
_serialnum++;
if (_serialnum >= ushort.MaxValue)
{
_serialnum = 0;
}
}
/// <summary>
/// 设置帧结构
/// </summary>
/// <param name="funcStr">功能</param>
/// <param name="addr">开始地址</param>
/// <param name="value">数量/值</param>
/// <param name="bytelenght">字节长度,写多个线圈/寄存器使用</param>
/// <param name="valueofmulti">写入的值,写多个线圈/寄存器使用</param>
protected void SetPDU(RegisterFunction func, Byte2 addr, Byte2 value, byte bytelenght = 0x00, byte[] valueofmulti = null)
{
FrameStruct.SetPDU(FunctionCodes[func], addr, value, bytelenght, valueofmulti);
}
/// <summary>
/// 检查响应的MBAP+功能码是否与请求一致
/// </summary>
/// <param name="data">响应的数据</param>
/// <returns>true:请求与返回对应</returns>
private bool CheckResponse(byte[] data, bool ignoreSerialNum = false)
{
if (data != null && data.Length > 8)
{
if (FrameStruct.MBAP.SerialNum.ByteH.Equals(data[0]) &&
FrameStruct.MBAP.SerialNum.ByteL.Equals(data[1]) &&
FrameStruct.MBAP.ProtocolId.ByteH.Equals(data[2]) &&
FrameStruct.MBAP.ProtocolId.ByteL.Equals(data[3]) &&
FrameStruct.MBAP.EquipAddr.Equals(data[6]) &&
FrameStruct.PDU.FuncCode.Equals(data[7])
)
{
return true;
}
if (ignoreSerialNum)
{
if (
FrameStruct.MBAP.ProtocolId.ByteH.Equals(data[2]) &&
FrameStruct.MBAP.ProtocolId.ByteL.Equals(data[3]) &&
FrameStruct.MBAP.EquipAddr.Equals(data[6]) &&
FrameStruct.PDU.FuncCode.Equals(data[7])
)
{
return true;
}
}
}
return false;
}
/// <summary>
/// 获取响应数据的结果,并返回响应的
/// </summary>
/// <param name="data">响应的数据</param>
/// <returns>true:响应成功</returns>
protected bool GetResponseResult(byte[] response, out byte[] data, bool ignoreSerial = false)
{
if (CheckResponse(response, ignoreSerial))
{
data = new byte[response.Length - 8];
Array.Copy(response, 8, data, 0, response.Length - 8);
return true;
}
if (response != null && response.Length >= 8)
{
data = new byte[] { response[7] };
if ((data[0] & 0x80) == 0x80)
{
LogUtil.Error($"从站异常响应[{StringHelper.ToHexString(data[0])}],响应数据为[{StringHelper.ToHexString(response)}]", log);
}
return false;
}
data = new byte[] { 0x00 };
return false;
}
/// <summary>
/// 解析读操作的响应数据
/// </summary>
/// <param name="response">响应的字节数据</param>
/// <param name="data"></param>
/// <param name="ignoreserial">忽略请求编号</param>
/// <returns>true:读取成功</returns>
protected virtual bool ParseRead(byte[] response, out byte[] data, bool ignoreserial = false)
{
data = null;
if (GetResponseResult(response, out byte[] data1, ignoreserial))
{
int len = data1[0];
data = new byte[len];
Array.Copy(data1, 1, data, 0, len);
return true;
}
return false;
}
/// <summary>
/// 解析写单个操作的响应数据
/// </summary>
/// <param name="response">响应的字节数据</param>
/// <returns>true:写入单个操作成功</returns>
protected abstract bool ParseWriteSingle(byte[] response);
/// <summary>
/// 解析写多个操作的响应数据
/// </summary>
/// <param name="response">响应的字节数据</param>
/// <returns>true:写多单个操作成功</returns>
protected abstract bool ParseWriteMultiple(byte[] response);
/// <summary>
/// 获取响应的结果
/// </summary>
/// <param name="function"></param>
/// <param name="response"></param>
/// <param name="data"></param>
/// <returns>解析</returns>
public bool GetResult(RegisterFunction function, byte[] response, out byte[] data)
{
bool rtn = false;
data = null;
switch (function)
{
case RegisterFunction.Read:
rtn = ParseRead(response, out data);
break;
case RegisterFunction.WriteSingle:
rtn = ParseWriteSingle(response);
break;
case RegisterFunction.WriteMultiple:
rtn = ParseWriteMultiple(response);
break;
default:
rtn = ParseRead(response, out data);
break;
}
return rtn;
}
public bool GetReadReault(byte[] response, out byte[] data)
{
bool rtn = ParseRead(response, out data, true);
return rtn;
}
/// <summary>
/// 分离多个反馈字节
/// </summary>
/// <param name="data"></param>
/// <returns>《功能码,响应的数据》</returns>
public static List<byte[]> SplitResponse(byte[] data)
{
List<byte[]> rtn = new List<byte[]>();
List<byte> tmpdata = new List<byte>();
tmpdata.AddRange(data);
while (tmpdata.Count >= 8)
{
if (ParseHeader(tmpdata.ToArray(), out byte func, out int len))
{
byte[] tmp = new byte[len + 6];
Array.Copy(tmpdata.ToArray(), 0, tmp, 0, len + 6);
rtn.Add(tmp);
tmpdata.RemoveRange(0, tmp.Length);
}
}
return rtn;
}
/// <summary>
/// 解析头
/// </summary>
/// <param name="header"></param>
/// <param name="func">功能码</param>
/// <param name="len">长度</param>
/// <returns>解析成功</returns>
public static bool ParseHeader(byte[] header, out byte func, out int len)
{
func = 0;
len = 0;
if (header != null && header.Length >= 8)
{
func = header[7];
len = Byte2.GetInt32(header[4], header[5]);
return true;
}
return false;
}
/// <summary>
/// 写入多个值时,将写入的值转换为发送的数组
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static int[] GetValuesWithMultiMode(int[] data)
{
int mod = data.Length % 8;
List<int> tmp = new List<int>();
int t = 0;
for (int i = 0; i < data.Length; i++)
{
if ((i + 1) % 8 == 0)
{
t += data[i] * (int)Math.Pow(2, data[i]);
tmp.Add(t);
t = 0;
}
else
{
t += data[i] * (int)Math.Pow(2, data[i]);
}
}
if (mod != 0)
tmp.Add(t);
return tmp.ToArray();
}
/// <summary>
/// 设置并获取请求字节数组
/// </summary>
/// <param name="func"></param>
/// <param name="startAddr"></param>
/// <param name="countOrValue"></param>
/// <param name="ValuesOfMultiple"></param>
/// <returns></returns>
public virtual byte[] GetRequestBytes(RegisterFunction func, ushort startAddr, ushort countOrValue, byte equipAddr = 1, ushort[] ValuesOfMultiple = null)
{
CmdByte cmdByte = new CmdByte();
cmdByte.StartAddr = Byte2.GetStru2Byte(startAddr);
cmdByte.Function = func;
cmdByte.Count = Byte2.GetStru2Byte(countOrValue);
if (func.Equals(RegisterFunction.WriteMultiple))
{
if (ValuesOfMultiple == null)
return null;
List<byte> values = new List<byte>();
foreach (var item in ValuesOfMultiple)
{
values.Add((byte)item);
}
cmdByte.ByteLength = (byte)(countOrValue % 8 == 0 ? countOrValue / 8 : countOrValue / 8 + 1);
cmdByte.ValuesOfMultiple = values.ToArray();
}
SetMBAP(_serialnum, equipAddr);
SetParamPDU(cmdByte);
return DataFrame;
}
}
public class StatusChangedEventArgs : EventArgs
{
public Dictionary<int, byte> ChangedIdxAndValue { get; set; }
public byte[] Data;
public bool Init { get; set; }
public StatusChangedEventArgs()
{
ChangedIdxAndValue = new Dictionary<int, byte>();
}
}
public class CmdByte
{
public RegisterFunction Function { get; set; }
public Byte2 StartAddr { get; set; }
public Byte2 Count { get; set; }
public byte ByteLength { get; set; }
public byte[] ValuesOfMultiple { get; set; }
string ToHexString(byte[] data)
{
if (data == null)
return "";
StringBuilder stringBuilder = new StringBuilder();
foreach (var item in data)
{
stringBuilder.Append(item.ToString("x2"));
stringBuilder.Append(" ");
}
return stringBuilder.ToString().Trim();
}
public override string ToString()
{
return $"[Function={Function.ToString()},StartAddr={StartAddr},Count/Value={Count},ByteLength={ByteLength},ValuesOfMultiple={ToHexString(ValuesOfMultiple)}]";
}
}
/// <summary>
/// 寄存器功能
/// </summary>
public enum RegisterFunction
{
/// <summary>
/// 读
/// </summary>
Read,
/// <summary>
/// 写单个
/// </summary>
WriteSingle,
/// <summary>
/// 写多个
/// </summary>
WriteMultiple,
}
/// <summary>
/// 寄存器类型
/// </summary>
public enum RegisterType
{
/// <summary>
/// 线圈,支持的功能码为 0x01,0x05,0x0f
/// </summary>
CoilSatus,
/// <summary>
/// 离散输入,支持的功能码为 0x02
/// </summary>
InputStatus,
/// <summary>
/// 输入寄存器,支持的功能码为 0x04
/// </summary>
InputRegister,
/// <summary>
/// 保持寄存器,支持的功能码为 0x03,0x06,0x10
/// </summary>
HoldingRegister,
}
}