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
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
using NLog;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
namespace GeneralGommunication
{
/// <summary>
/// 独立线程运行。 固需要做线程同步。
/// 支持Push, Call
/// 其中Call, 一“问”,必须一“答”。 长时间没答,重发“问”,连续3次都没 "答" 触发通讯断开。
/// 正常模式,多个"问" 会储存在缓存区, 等上一条 "答" ,才能发下一条"问"。
/// 打包模式, 多个"问" 没有先后关系,把它们打包, 不等 "答" 就同时发送"问"。 但 还是必须等 全部 "答" 都回复,才能执行一个动作。
/// </summary>
public abstract class Dev7E:IDev7E
{
public Logger logger = NLog.LogManager.GetCurrentClassLogger();
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// 包出错次数
/// </summary>
public int ErrCnt { get; protected set; }
/// <summary>
/// 连接成功;
/// 当命令多次发送失败,IsConnected = false
/// </summary>
public bool IsConnected { get; private set; }
/// <summary>
/// 通讯速度测量模块
/// </summary>
public CommSpeedMeasuring Csm => csm;
/// <summary>
/// 有数据需要发送
/// </summary>
public event SendDataEventHandler SendMsgEvent;
public event TimeOutEventHandler TimeOutEvent;
List<byte> currPack = new List<byte>();
/// <summary>
/// 指令队列,必须等上1条指令回复了,才能发下条指令
/// </summary>
List<COMMREQ_TransactionBase> Transactions;
/// <summary>
/// 当前正在等待回复的指令
/// </summary>
COMMREQ_TransactionBase currTran;
/// <summary>
/// currTran 发送后,开始计时
/// </summary>
Stopwatch stopwatch_timeOut;
/// <summary>
/// currTran 重发次数
/// </summary>
int retryCnt = 0;
CommSpeedMeasuring csm = new CommSpeedMeasuring();
/// <summary>
/// 指令前序 序号
/// </summary>
protected int PrefixIndex { get; set; } = 1;
/// <summary>
/// 对于全部 有返回的函数调用,都使用Dispatcher,使线程同步
/// </summary>
Dispatcher dispatcher;
DispatcherTimer timerTimeOut;//等待回复超时
public Dev7E()
{
Transactions = new List<COMMREQ_TransactionBase>();
stopwatch_timeOut = new Stopwatch();
timerTimeOut = new DispatcherTimer();
timerTimeOut.Interval = TimeSpan.FromSeconds(0.5);
timerTimeOut.Tick += TimerTimeOut_Tick;
timerTimeOut.Start();
}
#region 测量
#endregion
private void TimerTimeOut_Tick(object sender, EventArgs e)
{
//TODO 要处理 TimeOut / ParseFuncPack / GetSendMsg 线性同步问题
if (!IsConnected)
return;
if (currTran == null)
return;//没有指令
if (!stopwatch_timeOut.IsRunning)
return;//还没开始发送
if (stopwatch_timeOut.Elapsed < TimeSpan.FromSeconds(1))
return;//发送到现在,还没到1秒,继续等
//大于1秒也没回复,异常
//重试3次
retryCnt++;
stopwatch_timeOut.Stop();//停止,等下次发送
if (retryCnt >= 3)
{
//已经重试了3次,放弃
ResetMsg();
//连接断开
TimeOutEvent?.Invoke(this);
return;
}
else
{
//再发一次指令
byte[] msg = GetSendMsg();
SendMsgEvent?.Invoke(this,msg);
}
}
public void RecMsg(byte[] recBuf)
{
//IsConnected = true;
csm.IncRec(recBuf.Count());
List<byte[]> packs = new List<byte[]>();
lock (currPack)
{
for (int i = 0; i < recBuf.Count(); i++)
{
if (recBuf[i] == 0x7e)
{
//找到头了
//结束之前的包
if (currPack.Count > 0)
{
var pack = currPack.ToArray();
packs.Add(pack);
currPack.Clear();
csm.IncPack(1);
}
}
currPack.Add(recBuf[i]);
}
}
for (int i = 0; i < packs.Count(); i++) {
ParsePack(packs[i]);
}
}
/// <summary>
/// 获取 发送队列 第1条msg
/// </summary>
/// <returns></returns>
public byte[] GetSendMsg()
{
//TODO 要处理 TimeOut / ParseFuncPack / GetSendMsg 线性同步问题
if (currTran == null)
{
//当前没有指令正在发送
if (Transactions.Count() == 0)//队列没有需要发送的指令
return null;
currTran = Transactions.First();
retryCnt = 0;
Transactions.RemoveAt(0);
}
else
{
//发送出去中,等待回复
if (stopwatch_timeOut.IsRunning)
return null;//已经发送了,计时器都启动了
}
if (currTran is COMMREQ_Transaction)
{
var tran = currTran as COMMREQ_Transaction;
//找出 COMMREQ
var commReq = tran.commReq;
List<byte> pack = new List<byte>();
//添加前序
pack.AddRange(commReq.Prefix);
//添加数据
if (tran.datas != null)
pack.AddRange(tran.datas);
//调试: 打印发送信息
if (tran.datasObj == null)
logger.Debug($"REQ {commReq.PrefixString}");
else
logger.Debug($"REQ {commReq.PrefixString} {Newtonsoft.Json.JsonConvert.SerializeObject(tran.datasObj)}");
//转为 7E格式
var buf = GetSendPack(pack);
//开始计时
stopwatch_timeOut.Restart();
return buf.ToArray();
}
else
{
//集群交易
List<byte> bufs = new List<byte>();
var _tran = currTran as COMMREQ_TransactionMulti;
//获取还没接收到数据的交易,不能没有交易
var trans = _tran.transactions.FindAll(t => !t.hasRet);
if (trans.Count() == 0)
throw new Exception("没有 [没接收到数据的交易],程序写错");
foreach (var tran in trans)
{
//找出 COMMREQ
var commReq = tran.commReq;
List<byte> pack = new List<byte>();
//添加前序
pack.AddRange(commReq.Prefix);
//添加数据
if (tran.datas != null)
pack.AddRange(tran.datas);
//转为 7E格式
var buf = GetSendPack(pack);
bufs.AddRange(buf);
//调试: 打印发送信息
if (tran.datasObj == null)
logger.Debug($"REQ multi {commReq.PrefixString}");
else
logger.Debug($"REQ multi {commReq.PrefixString} {Newtonsoft.Json.JsonConvert.SerializeObject(tran.datasObj)}");
}
//开始计时
stopwatch_timeOut.Restart();
return bufs.ToArray();
}
}
/// <summary>
/// 复位全部状态,通常由于通讯模块检测到连接断开导致的
/// </summary>
public void ResetMsg()
{
currTran = null;
stopwatch_timeOut.Stop();
csm.Reset();
IsConnected = false;
Transactions.Clear();
}
/// 包解析
/// </summary>
void ParsePack(byte[] pack)
{
//第0个肯定是7E
if (pack.Count() == 1)
{
//只有一个 7E
return;
}
//转义数据
if (!Protocol7ECommon.Pdu2Data(pack, out List<byte> datas))
{
//异常
return;
}
//解析后的 datas 没有了 帧包装 7E
pack = datas.ToArray();
if (pack.Count() == 1)
{
//只有一个字节,异常!!!
ErrCnt++;
return;
}
//解析后的 datas 没有了 帧包装 7E
byte crc8 = Misc.CRC.CRC8(pack, 0, pack.Count() - 1);
if (pack.Last() != crc8)
{
//CRC8 出错
ErrCnt++;
return;
}
IsConnected = true;
ParsePackAfterCheckCRC8(pack);
}
protected abstract void ParsePackAfterCheckCRC8(byte[] buf);
/// <summary>
/// 功能包解析
/// </summary>
/// <param name="datas"></param>
protected virtual void ParseFuncPack(byte[] pack)
{
if (currTran == null)
{
//没有请求。。。
return;
}
if (currTran is COMMREQ_Transaction)
{
ParseFuncPack_Single(pack);
}
else
{
ParseFuncPack_Multi(pack);
}
}
protected bool ParseFuncPack_Single(byte[] pack)
{
object retData = null;
var tran = currTran as COMMREQ_Transaction;
var commReq = tran.commReq;
//还有B0
if (commReq.ResponseTotalLen + PrefixIndex > pack.Count())
{
//失败,指令长度不对!!
logger.Error($"ACK expect:{commReq.PrefixString} len={commReq.ResponseTotalLen + 1}, but reponse len ={pack.Count()} reponse: {pack.ToString_Bytes2HexAndAscii()}");
ErrCnt++;
return false;
}
if (commReq.IsMatch != null)
{
if (!commReq.IsMatch(commReq, pack, PrefixIndex, commReq.IsMatchContext, ref retData))
{
//回复对不上请求
logger.Error($"ACK expect:{commReq.PrefixString} len={commReq.ResponseTotalLen + 1}, but reponse len ={pack.Count()} reponse: {pack.ToString_Bytes2HexAndAscii()}");
ErrCnt++;
return false;
}
}
else
{
if (!IsMatch(commReq, pack))
{
//回复对不上请求
logger.Error($"ACK expect:{commReq.PrefixString} len={commReq.ResponseTotalLen + 1}, but reponse len ={pack.Count()} reponse: {pack.ToString_Bytes2HexAndAscii()}");
ErrCnt++;
return false;
}
//处理指令
//还有B0
if (commReq.ParseFuncPack != null)//解析回复数据
retData = commReq.ParseFuncPack(pack, commReq.Prefix.Count() + PrefixIndex);
}
tran.hasRet = true;
tran.retData = retData;
if (retData == null)
logger.Debug($"ACK {commReq.PrefixString}");
else
logger.Debug($"ACK {commReq.PrefixString} {Newtonsoft.Json.JsonConvert.SerializeObject(retData)}");
//有很多指令是没有回复数据的, 回调只是通知 指令已经执行了而已
//调用回调
if (tran.asyncDelegate != null)
{
if (dispatcher != null)//线程同步执行
{
dispatcher.BeginInvoke(tran.asyncDelegate, tran.asyncContext, retData);
}
else
{
tran.asyncDelegate.Invoke(tran.asyncContext, retData);
}
}
//停止超时检测
stopwatch_timeOut.Stop();
//空出当前交易位置
currTran = null;
if (Transactions.Count() > 0)
{
//队列还有需要发送的指令,通知外部获取指令发送
byte[] msg = GetSendMsg();
SendMsgEvent?.Invoke(this,msg);
}
return true;
}
bool IsMatch2(COMMREQ commReq, byte[] pack, ref object retData)
{
//还有B0
if (commReq.ResponseTotalLen + PrefixIndex > pack.Count())
{
//失败,指令长度不对!!
//logger.Error($"ACK expect:{commReq.PrefixString} len={commReq.ReponseTotalLen + 1}, but reponse len ={pack.Count()} reponse: {bytes2hex(pack)}");
//ErrCnt++;
return false;
}
if (commReq.IsMatch != null)
{
//自定义IsMatch规则
if (!commReq.IsMatch(commReq, pack, PrefixIndex, commReq.IsMatchContext, ref retData))
{
//回复对不上请求
//logger.Error($"ACK expect:{commReq.PrefixString} ,but reponse: {bytes2hex(pack)}");
return false;
}
}
else
{
//通用IsMatch规则
if (!IsMatch(commReq, pack))
{
//回复对不上请求
//logger.Error($"ACK expect:{commReq.PrefixString} ,but reponse: {bytes2hex(pack)}");
return false;
}
//处理指令
//还有B0
if (commReq.ParseFuncPack != null)//解析回复数据
retData = commReq.ParseFuncPack(pack, commReq.Prefix.Count() + PrefixIndex);
}
return true;
}
protected bool ParseFuncPack_Multi(byte[] pack)
{
object retData = null;
var _tran = currTran as COMMREQ_TransactionMulti;
//寻找合适的commReq
var trans = _tran.transactions.FindAll(t => !t.hasRet);
COMMREQ commReq = null;
COMMREQ_Transaction tran = null;
foreach (var t in trans)
{
tran = t;
commReq = tran.commReq;
if (IsMatch2(commReq, pack, ref retData))//匹配正确,就是这个tran的回复
break;
commReq = null;
tran = null;
}
if (commReq == null) {
//不能解析
logger.Error($"ACK multi reponse len ={pack.Count()} reponse: {pack.ToString_Bytes2HexAndAscii()}");
ErrCnt++;
return false;
}
tran.hasRet = true;
tran.retData = retData;
//解析成功
if (retData == null)
logger.Debug($"ACK multi {commReq.PrefixString}");
else
logger.Debug($"ACK multi {commReq.PrefixString} {Newtonsoft.Json.JsonConvert.SerializeObject(retData)}");
//有很多指令是没有回复数据的, 回调只是通知 指令已经执行了而已
//调用回调
if (tran.asyncDelegate != null)
{
if (dispatcher != null)//线程同步执行
{
dispatcher.BeginInvoke(tran.asyncDelegate, tran.asyncContext, retData);
}
else
{
tran.asyncDelegate.Invoke(tran.asyncContext, retData);
}
}
if (_tran.transactions.All(t => t.hasRet))
{
//完成, 全部回复
if (_tran.asyncDelegate != null)
{
if (dispatcher != null)//线程同步执行
{
dispatcher.BeginInvoke(_tran.asyncDelegate, _tran.asyncContext, _tran);
}
else
{
tran.asyncDelegate.Invoke(_tran.asyncContext, _tran);
}
}
//停止超时检测
stopwatch_timeOut.Stop();
//空出当前交易位置
currTran = null;
if (Transactions.Count() > 0)
{
//队列还有需要发送的指令,通知外部获取指令发送
byte[] msg = GetSendMsg();
SendMsgEvent?.Invoke(this,msg);
}
return true;
}
return false;
}
/// <summary>
/// 与指令前序比较,一定要完全一样
/// </summary>
/// <param name="commreq"></param>
/// <param name="datas"></param>
/// <returns></returns>
protected bool IsMatch(COMMREQ commreq, byte[] datas)
{
int prefix_index = PrefixIndex;
//datas[0] 是B0, 不应该用于判断
//与指令前序一个个比较
for (int i = 0; i < commreq.Prefix.Length; i++)
{
if (commreq.Prefix[i] != datas[prefix_index + i])
{
return false;
}
}
return true;
}
/// <summary>
/// 数据发送,输入不需要包含开头的7E; 输出会添加CRC8 和 7E
/// </summary>
/// <param name="datas"></param>
protected virtual List<byte> GetSendPack(List<byte> datas)
{
//需要在后面添加CRC8
byte crc8 = Misc.CRC.CRC8(datas, 0, datas.Count());
datas.Add(crc8);
Protocol7ECommon.Data2Pdu(datas, out List<byte> pack);
pack.Add(0x7E);//一定要加0x7E
return pack;
}
protected void AddTran(COMMREQ_Transaction tran)
{
if (transactionMulti!=null)
{
transactionMulti.transactions.Add(tran);
}
else
{
//放入 交易队列
Transactions.Add(tran);
if (currTran == null)
{
//当前没有指令正在发送
byte[] msg = GetSendMsg();
SendMsgEvent?.Invoke(this,msg);
}
}
}
COMMREQ_TransactionMulti transactionMulti;
void BuildMultiTransAsync()
{
if (transactionMulti!=null) {
throw new Exception("已经是 BuildMultiTrans, 不能再次执行, 必须调用 AddMultiTrans");
}
transactionMulti = new COMMREQ_TransactionMulti();
}
public void BuildMultiTrans(Action action)
{
BuildMultiTrans(action, null, null);
}
public void BuildMultiTrans(Action action, CallBackHandler asyncDelegate, object asyncContext)
{
BuildMultiTransAsync();
action();
AddMultiTransAsync(asyncDelegate, asyncContext);
}
void AddMultiTransAsync(CallBackHandler asyncDelegate, object asyncContext)
{
if (transactionMulti.transactions.Count() == 0) {
throw new Exception("MultiTrans 为空");
}
transactionMulti.asyncDelegate = asyncDelegate;
transactionMulti.asyncContext = asyncContext;
//放入 交易队列
Transactions.Add(transactionMulti);
transactionMulti = null;
if (currTran == null)
{
//当前没有指令正在发送
byte[] msg = GetSendMsg();
SendMsgEvent?.Invoke(this, msg);
}
}
}
}