1 // ----------------------------------------------------------------------------
2 // Copyright (C) 2014
3 // David Freese, W1HKJ
4 //
5 // This file is part of fldigi
6 //
7 // fldigi is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // fldigi is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 // ----------------------------------------------------------------------------
20
21 #include <config.h>
22
23 #include <sys/time.h>
24
25 #include <iostream>
26
27 #include "arq.h"
28
29 using namespace std;
30
31 //=============================================================================
32 // status messages
33 //=============================================================================
34 string TXPOLL = "TX: Send Blocks Report";
35 string STIMEDOUT = "Timed out";
36 string ABORTXFR = "ABORT transfer";
37 string RXIDENT = "RX: Link Still Active";
38 string RXCONREQ = "RX: Connect Request";
39 string RXCONACK = "RX: Connect OK";
40 string RXDISCONN = "RX: Disconnect Request";
41 string RXDISCONACK = "RX: Disconnect OK";
42 string RXSTATUS = "RX: Status Report";
43 string RXPOLL = "RX: Send Blocks Report";
44 string TXSTATUS = "TX: Blocks Received OK";
45 string TXDISCONN = "TX: Disconnect Request";
46 string TXDISACK = "TX: Disconnect OK";
47 string TXBEACON = "TX: Beacon";
48 string TXCONNECT = "TX: Connect";
49 string TXCONNACK = "TX: Connect OK";
50 string TXIDENT = "TX: Watchdog %d";
51
52 //bool bPoll = false;
53
upcase(string s)54 string arq::upcase(string s)
55 {
56 for (size_t i = 0; i < s.length(); i++)
57 s[i] = toupper(s[i]);
58 return s;
59 }
60
arq()61 arq::arq()
62 {
63 sendfnc = NULL;
64 rcvfnc = NULL;
65 printRX = NULL;
66 abortfnc = NULL;
67 disconnectfnc = NULL;
68 qualityfnc = NULL;
69 printRX_DEBUG = NULL;
70 printTX_DEBUG = NULL;
71 rxUrCall = NULL;
72
73 Header.erase();
74
75 MyStreamID = '0';
76 UrStreamID = '0';
77
78 UrCall.erase();
79 MyCall.erase();
80
81 logfile = "server.log";
82
83 // queues //
84 TxTextQueue.clear();//erase(); // Text out to mail engine
85 RxTextQueue.clear();//erase(); // Text in from mail engine
86 TxPlainTextQueue.clear();//erase();
87 RxFrameQueue.clear();//erase();
88 lastRxChar = 0;
89 TXflag = false;
90
91 SessionNumber = 0;
92
93 exponent = EXPONENT;
94 maxheaders = MAXHEADERS;
95 RetryTime = RETRYTIME;
96 Retries = RETRIES;
97 Timeout = TIMEOUT;
98 TxDelay = TXDELAY;
99 immediate = false;
100 primary = false;
101
102 setBufferlength();
103
104
105 // status variables //
106 // totalRx = 0;
107 // nbrbadRx = 0;
108 totalTx = 0;
109 nbrbadTx = 0;
110 payloadlength = 32; // Average length of payload received
111
112 // static status
113 Firstsent = MAXCOUNT - 1; // First Header I sent last turn
114 LastHeader = MAXCOUNT - 1; // Last Header I sent last turn
115 Lastqueued = MAXCOUNT - 1; // Last Header in static send queue
116
117 EndHeader = MAXCOUNT - 1; // Last I received o.k.
118 GoodHeader = MAXCOUNT - 1; // Last Header received consecutive
119
120 // Ur status
121 UrGoodHeader = MAXCOUNT - 1; // Other station's Good Header
122 UrLastHeader = MAXCOUNT - 1; // Other station's Header last sent
123 UrEndHeader = MAXCOUNT - 1; // Other station's last received Header
124 blkcount = -1;
125
126 TXflag = false; // TX on
127 LinkState = DOWN; // ARQ link is initially down
128 Sending = 0;
129 PollOK = false;
130 // bABORT = false;
131
132 MyMissing.clear();
133 MissingRxBlocks = "";
134
135 TxBlocks.clear();
136 TxMissing.clear();
137 TxPending.clear();
138
139 RxPending.clear();
140
141 arqstop = false;
142
143 retries = baseRetries = Retries;
144 baseRetryTime = RetryTime;
145 baseTimeout = Timeout;
146
147 retrytime = RetryTime / ARQLOOPTIME;
148 timeout = Timeout / ARQLOOPTIME;
149 loopcount = 0;
150 set_idtimer();
151
152 tx2txdelay = 0;//TxDelay / ARQLOOPTIME;
153
154 // srand(time(NULL));
155 }
156
setBufferlength()157 void arq::setBufferlength()
158 {
159 Bufferlength = 1;
160 for (int i = 0; i < exponent; i++) Bufferlength *= 2;
161 MyBlockLengthChar = '0' + exponent;
162 }
163
resetTx()164 void arq::resetTx()
165 {
166 Firstsent = MAXCOUNT - 1; // First Header I sent last turn
167 LastHeader = MAXCOUNT - 1; // Last Header I sent last turn
168 Lastqueued = MAXCOUNT - 1; // Last Header in static send queue
169 TxMissing.clear();
170 TxBlocks.clear();
171 TxPending.clear();
172 TxTextQueue.clear();
173 // UrMissing.clear();
174 }
175
resetRx()176 void arq::resetRx()
177 {
178 RxTextQueue.clear();//erase(); // Text in from mail engine
179 RxFrameQueue.clear();//erase();
180 lastRxChar = 0;
181 EndHeader = MAXCOUNT - 1; // Last I received o.k.
182 GoodHeader = MAXCOUNT - 1; // Last Header I received conseq. o.k, 1st in send queue
183 RxPending.clear();
184 MissingRxBlocks = "";
185 }
186
reset()187 void arq::reset()
188 {
189 resetTx();
190 resetRx();
191 immediate = false;
192 primary = false;
193 blkcount = -1;
194 // bABORT = false;
195 }
196
197 // new session number
198 // unknown stream id = 0
199 // known id's from 1 to 63
newsession()200 void arq::newsession()
201 {
202 if (++SessionNumber == 64) SessionNumber = 1;
203 MyStreamID = SessionNumber + '0';
204 }
205
206 // get new blocknumber
newblocknumber()207 void arq::newblocknumber()
208 {
209 Lastqueued++;
210 Lastqueued %= MAXCOUNT;
211 }
212
213 // Checksum of header + Header
checksum(string & s)214 string arq::checksum(string &s)
215 {
216 framecrc.reset();
217 return framecrc.scrc16(s);
218 }
219
220 // Start header when MyStreamID has been assigned
newHeader()221 void arq::newHeader()
222 {
223 Header.erase();
224 Header += SOH;
225 Header += '0'; // protocol version;
226 }
227
IdHeader()228 void arq::IdHeader()
229 {
230 newHeader();
231 Header += UrStreamID;
232 }
233
UnkHeader()234 void arq::UnkHeader()
235 {
236 newHeader();
237 Header += '0';
238 }
239
240 char crlf[3] = " ";
241
addToTxQue(string s)242 void arq::addToTxQue(string s)
243 {
244 // TxTextQueue += "\r\n";
245 crlf[0] = 0x0D;
246 crlf[1] = 0x0A;
247 TxTextQueue.append(s);
248 TxTextQueue.append(crlf);
249 totalTx++;
250 }
251
252 // Connect (client:port, server:port)
253 // c Header = Client:port Server:port <streamnr. (0)> <max. Headerlen>
254 // e.g.: '00cW1HKJ:1025 KH6TY:24 4'
255 //
connectFrame()256 void arq::connectFrame()
257 {
258 char szGlobals[24];
259 reset();
260 UnkHeader();
261 Header += CONREQ;
262
263 Payload.erase();
264 Payload.append(MyCall);
265 Payload.append(":1025");
266 Payload += ' ';
267 Payload.append(UrCall);
268 Payload.append(":24");
269 Payload += ' ';
270 Payload += MyStreamID;
271 Payload += ' ';
272 Payload += MyBlockLengthChar;
273
274 snprintf(szGlobals, 23, " T%dR%dW%d", Timeout/1000, Retries, RetryTime/1000);
275 Payload.append(szGlobals);
276
277 Frame = Header + Payload;
278
279 Frame = Frame + checksum(Frame);
280 Frame += EOT;
281
282 addToTxQue(Frame);
283
284 LinkState = ARQ_CONNECTING;
285 printSTATUS(TXCONNECT, 5.0);
286
287 set_idtimer();
288 }
289
290
291 // Connect acknowledge (server:port, client:port)
292 // k Header = Server:port Client:port <streamnr.> <max. Headerlen>
293 // e.g: '00kKH6TY:24 W1HKJ:1024 8 6'
294 //
ackFrame()295 void arq::ackFrame ()
296 {
297 reset();
298 IdHeader();
299 Header += CONACK;
300
301 Payload.erase();
302 Payload.append(MyCall);
303 Payload.append(":24");
304 Payload += ' ';
305 Payload.append(UrCall);
306 Payload += ' ';
307 Payload += MyStreamID;
308 Payload += ' ';
309 Payload += MyBlockLengthChar;
310
311 Frame = Header + Payload;
312 Frame = Frame + checksum(Frame);
313 Frame += EOT;
314
315 addToTxQue(Frame);
316 printSTATUS(TXCONNACK, 5.0);
317 set_idtimer();
318 }
319
320 // Connect (caller:port, static:port)
321 // c Header = Caller:port static:port <streamnr. (0)> <max. Headerlen>
322 // e.g.: '00cW1HKJ:87 KH6TY:87 4'
323 //
ttyconnectFrame()324 void arq::ttyconnectFrame()
325 {
326 UnkHeader();
327 Header += CONREQ;
328
329 Payload.erase();
330 Payload.append(MyCall);
331 Payload.append(":87");
332 Payload += ' ';
333 Payload.append(UrCall);
334 Payload.append(":87");
335 Payload += ' ';
336 Payload += MyStreamID;
337 Payload += ' ';
338 Payload += MyBlockLengthChar;
339
340 Frame = Header + Payload;
341 Frame = Frame + checksum(Frame);
342 Frame += EOT;
343
344 addToTxQue(Frame);
345 set_idtimer();
346 }
347
348 // Connect acknowledge (server:port, client:port)
349 // k Header = Server:port Client:port <streamnr.> <max. Headerlen>
350 // e.g: '00kKH6TY:87 W1HKJ 4'
351 // Service id # 87 is keyboard-to-keyboard
352 //
ttyackFrame()353 void arq::ttyackFrame()
354 {
355 IdHeader();
356 Header += CONACK;
357
358 Payload.erase();
359 Payload.append(MyCall);
360 Payload.append(":87");
361 Payload += ' ';
362 Payload.append(UrCall);
363 Payload += ' ';
364 Payload += MyBlockLengthChar;
365
366 Frame = Header + Payload;
367 Frame = Frame + checksum(Frame);
368 Frame += EOT;
369
370 addToTxQue(Frame);
371 set_idtimer();
372 }
373
374 // Identify
375 //i frame = '00iKH6TY de W1HKJ'
identFrame()376 void arq::identFrame()
377 {
378 IdHeader();
379 Header += IDENT;
380
381 Payload.erase();
382 Payload.append(UrCall);
383 Payload.append(" de ");
384 Payload.append(MyCall);
385
386 Frame = Header + Payload;
387 Frame = Frame + checksum(Frame);
388 Frame += EOT;
389
390 addToTxQue(Frame);
391
392 char szIDENT[80];
393 snprintf(szIDENT,sizeof(szIDENT), TXIDENT.c_str(), retries);
394 printSTATUS(szIDENT, 5.0);
395
396 set_idtimer();
397 }
398
399 // e.g. Ping frame
400 // u Header = From:port
401 // e.g: '00uKH6TY:7 '
pingFrame()402 void arq::pingFrame()
403 {
404 IdHeader();
405 Header += _UNPROTO;
406
407 Payload.erase();
408 Payload.append(MyCall);
409 Payload.append(":7");
410 Payload += ' ';
411
412 Frame = Header + Payload;
413 Frame = Frame + checksum(Frame);
414 Frame += EOT;
415
416 addToTxQue(Frame);
417 set_idtimer();
418 }
419
420 // talk frame
421 // similar to UNPROTO frame
422 // but only sent if ARQ_CONNECTED
talkFrame(string txt)423 void arq::talkFrame(string txt)
424 {
425 IdHeader();
426 Header += _TALK;
427
428 Payload.erase();
429 Payload.append(MyCall);
430 Payload.append(":73");
431 Payload += ' ';
432 if (txt.length() > (size_t)Bufferlength)
433 Payload.append(txt.substr(0, Bufferlength));
434 else
435 Payload.append(txt);
436 Frame = Header + Payload;
437 Frame = Frame + checksum(Frame);
438 Frame += EOT;
439
440 addToTxQue(Frame);
441 set_idtimer();
442 }
443
ackAbortFrame()444 void arq::ackAbortFrame()
445 {
446 IdHeader();
447 Header += _ACKABORT;
448
449 Payload.erase();
450 Payload += (LastHeader + 0x20);
451 Payload += (GoodHeader + 0x20);
452 Payload += (EndHeader + 0x20);
453 Payload.append(MissingRxBlocks);
454
455 Frame = Header + Payload;
456 Frame = Frame + checksum(Frame);
457 Frame += EOT;
458
459 addToTxQue(Frame);
460 printSTATUS(TXSTATUS, 5.0);
461 }
462
463 // Status report (End, Good, Lastrx, Missing)
464 //p frame = <last Header tx><last Header rx ok><last Header rx> <missing Headers>
465 //e.g.: '00sXHCAB'
466 //
statFrame()467 void arq::statFrame()
468 {
469 if (idtimer && _idtimer <= 0) talkFrame("auto ID");
470
471 IdHeader();
472 Header += STATUS;
473
474 Payload.erase();
475 Payload += (LastHeader + 0x20);
476 Payload += (GoodHeader + 0x20);
477 Payload += (EndHeader + 0x20);
478 Payload.append(MissingRxBlocks);
479
480 Frame = Header + Payload;
481 Frame = Frame + checksum(Frame);
482 Frame += EOT;
483
484 addToTxQue(Frame);
485 printSTATUS(TXSTATUS, 5.0);
486 }
487
488 // Disconnect session
489 //d frame = ""
490 //e.g.: '00d'
491
disconnectFrame()492 void arq::disconnectFrame()
493 {
494 IdHeader();
495 Header += DISREQ;
496
497 Payload.erase();
498 Payload.append(MyCall);
499 Payload.append(":90");
500 Frame = Header + Payload;
501 Frame = Frame + checksum(Frame);
502 Frame += EOT;
503
504 addToTxQue(Frame);
505 printSTATUS(TXDISCONN, 5.0);
506 set_idtimer();
507 }
508
disackFrame()509 void arq::disackFrame()
510 {
511 IdHeader();
512 Header += _DISACK;
513
514 Payload.erase();
515 Payload.append(MyCall);
516 Payload.append(":91");
517 Frame = Header + Payload;
518 Frame = Frame + checksum(Frame);
519 Frame += EOT;
520 addToTxQue(Frame);
521 printSTATUS(TXDISACK, 5.0);
522 set_idtimer();
523 }
524
525 // ABORT session
526 //a frame = ""
527 //e.g.: '00a'
abortFrame()528 void arq::abortFrame()
529 {
530 IdHeader();
531 Header += _ABORT;
532
533 Payload.erase();
534 Payload.append(MyCall);
535 Payload.append(":92");
536 Frame = Header + Payload;
537 Frame = Frame + checksum(Frame);
538 Frame += EOT;
539
540 addToTxQue(Frame);
541 set_idtimer();
542 }
543
544 // Beacon frame
545 // u Header = From:port data
546 // e.g: '00uKH6TY:72 Beacon text '
547 //
beaconFrame(string txt)548 void arq::beaconFrame(string txt)
549 {
550 UnkHeader();
551 Header += _UNPROTO;
552
553 Payload.erase();
554 Payload.append(MyCall);
555 Payload.append(":72");
556 Payload += ' ';
557 if (txt.length() > (size_t)Bufferlength)
558 Payload.append(txt.substr(0, Bufferlength));
559 else
560 Payload.append(txt);
561 Frame = Header + Payload;
562 Frame = Frame + checksum(Frame);
563 Frame += EOT;
564
565 addToTxQue(Frame);
566 printSTATUS(TXBEACON, 5.0);
567 set_idtimer();
568 }
569
570 // poll
571 //p frame = <last Header tx><last Header rx ok><last Header rx> <missing Headers>
572 //e.g.: '00pXHCAB'
pollFrame()573 void arq::pollFrame()
574 {
575
576 IdHeader();
577 Frame = Header;
578 Frame += POLL;
579 Frame.append(MyCall);
580 Frame += SUB;
581 Frame += (LastHeader + 0x20);
582 Frame += (GoodHeader + 0x20);
583 Frame += (EndHeader + 0x20);
584 Frame.append(MissingRxBlocks);
585 Frame.append(checksum(Frame));
586 Frame += EOT;
587
588 addToTxQue(Frame);
589 printSTATUS(TXPOLL, 5.0);
590 set_idtimer();
591 }
592
593 // Text frame
textFrame(cTxtBlk block)594 void arq::textFrame(cTxtBlk block)
595 {
596 IdHeader();
597 Frame = Header;
598 Frame += (block.nbr() + 0x20);
599 Frame.append(block.text());
600 Frame.append(checksum(Frame));
601 Frame += SOH;
602
603 addToTxQue(Frame);
604 }
605
606 //=====================================================================
607
parseIDENT()608 void arq::parseIDENT()
609 {
610 timeout = Timeout / ARQLOOPTIME;
611 statFrame();
612 immediate = true;
613 printSTATUS(RXIDENT, 5.0);
614 }
615
parseCONREQ()616 void arq::parseCONREQ()
617 {
618
619 size_t p1 = 0, p2 = rcvPayload.find(':');
620 if (p2 == string::npos)
621 return;
622 // if (LinkState == ARQ_CONNECTED || LinkState == WAITFORACK) return; // disallow multiple connects
623
624 // requesting stations callsign
625 UrCall = upcase(rcvPayload.substr(p1, p2 - p1));
626 p1 = rcvPayload.find(' ', p2+1);
627 if (p1 == string::npos) {
628 UrCall.erase();
629 return;
630 }
631
632 p1++;
633 p2 = rcvPayload.find(":", p1);
634 string testcall = upcase(rcvPayload.substr(p1, p2 - p1));
635 if (testcall != MyCall) {
636 UrCall.erase();
637 return;
638 }
639
640 p1 = rcvPayload.find(' ', p2 +1);
641 if (p1 == string::npos) {
642 UrCall.erase();
643 return;
644 }
645
646 p1++; // *p1 ==> StreamID for requesting station
647 UrStreamID = rcvPayload[p1];
648 p1++; // *p1 ==> requested block size
649 UrBlockLengthChar = rcvPayload[p1];
650
651 p1 += 3; // *p1 ==>" TnnnRnnnWnnn"
652 if (p1 < rcvPayload.length()) {
653 char num[7];
654 if (rcvPayload[p1] == 'T') {
655 int n = 0;
656 while (rcvPayload[++p1] != 'R' && n < 6) num[n++] = rcvPayload[p1];
657 num[n] = 0;
658 sscanf(num, "%d", &Timeout);
659 Timeout *= 1000;
660 if (p1 < rcvPayload.length() && rcvPayload[p1] == 'R') {
661 int n = 0;
662 while (rcvPayload[++p1] != 'W' && n < 6) num[n++] = rcvPayload[p1];
663 num[n] = 0;
664 sscanf(num, "%d", &Retries);
665 if (p1 < rcvPayload.length() && rcvPayload[p1] == 'W') {
666 int n = 0;
667 while (++p1 < rcvPayload.length() && n < 6) num[n++] = rcvPayload[p1];
668 num[n] = 0;
669 sscanf(num, "%d", &RetryTime);
670 RetryTime *= 1000;
671 Timeout += Retries * RetryTime;
672 }
673 }
674 /*
675 char line[80];
676 string NewValues = "Temporary control parameters set to\n";
677 snprintf(line, 79, " Retries = %d\n", Retries);
678 NewValues.append(line);
679 snprintf(line, 79, " Wait time = %d secs\n", RetryTime / 1000);
680 NewValues.append(line);
681 snprintf(line, 79, " Timeout = %d secs\n", Timeout / 1000);
682 NewValues.append(line);
683 printRX(NewValues);
684 */
685 }
686 }
687
688 reset();
689
690 LinkState = WAITFORACK;
691 newsession();
692
693 if (rxUrCall) rxUrCall(UrCall);
694
695 TxTextQueue.clear();
696 ackFrame();
697 immediate = true;
698 printSTATUS(RXCONREQ, 5.0);
699
700 }
701
parseCONACK()702 void arq::parseCONACK()
703 {
704 if (LinkState < ARQ_CONNECTING ) { //!= ARQ_CONNECTING) {
705 return; // Connect Acknowledge only valid during a connect
706 }
707
708 size_t p1 = 0, p2 = rcvPayload.find(':');
709 // LinkState = DOWN;
710 if (p2 == string::npos)
711 return;
712 // requesting stations callsign
713 UrCall = upcase(rcvPayload.substr(p1, p2 - p1));
714 p1 = rcvPayload.find(' ', p2+1);
715 if (p1 == string::npos) {
716 UrCall.erase();
717 return;
718 }
719
720 p1++;
721 p2 = rcvPayload.find(" ", p1);
722 string testcall = upcase(rcvPayload.substr(p1, p2 - p1));
723 if (testcall != MyCall) {
724 UrCall.erase();
725 return;
726 }
727
728 p1++; // *p1 ==> StreamID for requesting station
729 UrStreamID = rcvPayload[p1];
730 p1++; // *p1 ==> requested block size
731 UrBlockLengthChar = rcvPayload[p1];
732
733 RxTextQueue.clear();//erase();
734
735 LinkState = ARQ_CONNECTED;
736 timeout = Timeout / ARQLOOPTIME;
737
738 statFrame();
739 immediate = true;
740 primary = true;
741 printSTATUS(RXCONACK, 5.0);
742 }
743
parseDISREQ()744 void arq::parseDISREQ()
745 {
746 if (LinkState == DOWN) return;
747 TxTextQueue.clear();//erase();
748 TxMissing.clear();
749 TxBlocks.clear();
750 TxPlainTextQueue.clear();
751 disackFrame();
752 immediate = true;
753 LinkState = DOWN;
754 if (rxUrCall) rxUrCall("");
755 if (disconnectfnc) disconnectfnc();
756 printSTATUS(RXDISCONN, 5.0);
757 }
758
parseDISACK()759 void arq::parseDISACK()
760 {
761 if (rxUrCall) rxUrCall("");
762 LinkState = DOWN;
763 printSTATUS(RXDISCONACK, 5.0);
764 }
765
parseABORT()766 void arq::parseABORT()
767 {
768 reset();
769 if (abortfnc) abortfnc();
770 ackAbortFrame();
771 immediate = true;
772 LinkState = ARQ_CONNECTED;
773 }
774
parseACKABORT()775 void arq::parseACKABORT()
776 {
777 reset();
778 if (abortfnc) abortfnc();
779 LinkState = ARQ_CONNECTED;
780 }
781
parseUNPROTO()782 void arq::parseUNPROTO()
783 {
784 size_t p1 = 0, p2 = rcvPayload.find(':');
785 if (p2 == string::npos)
786 return;
787 // requesting stations callsign
788 UrCall = upcase(rcvPayload.substr(p1, p2 - p1));
789 if (rxUrCall) rxUrCall(UrCall);
790 if (printRX) printRX(rcvPayload + "\n");
791 }
792
parseTALK()793 void arq::parseTALK()
794 {
795 size_t p1 = rcvPayload.find(":73");
796 if (p1 == string::npos)
797 return;
798 string talktext = rcvPayload.substr(p1 + 4);
799 if (printTALK) printTALK(talktext);
800 }
801
parseSTATUS()802 void arq::parseSTATUS()
803 {
804 // create the missing list
805 // all reported missing blocks
806 if (LinkState >= ARQ_CONNECTED) {
807 UrLastHeader = rcvPayload[0] - 0x20; // Other station's Header last sent
808 UrGoodHeader = rcvPayload[1] - 0x20; // Other station's Good Header
809 UrEndHeader = rcvPayload[2] - 0x20; // Other station's last received Header
810
811 size_t nummissing = rcvPayload.length() - 3;
812 vector<int> missing;
813 // append those reported missing
814 if (nummissing > 0)
815 for (size_t i = 0; i < nummissing; i++)
816 missing.push_back(rcvPayload[i+3] - 0x20);
817 // append those not reported missing from UrEndHeader to LastHeader
818 if (UrEndHeader != LastHeader) {
819 int m = UrEndHeader + 1;
820 if (m > MAXCOUNT) m -= MAXCOUNT;
821 while (m != LastHeader) {
822 missing.push_back(m);
823 m++;
824 if (m > MAXCOUNT) m -= MAXCOUNT;
825 }
826 missing.push_back(LastHeader);
827 }
828
829 if (missing.empty())
830 TxMissing.clear();
831
832 if (TxMissing.empty() == false) {
833 list<cTxtBlk> keep;
834 list<cTxtBlk>::iterator p = TxMissing.begin();
835 while (p != TxMissing.end()) {
836 for (size_t n = 0; n < missing.size(); n++) {
837 if (p->nbr() == missing[n]) {
838 keep.push_back(*p);
839 break;
840 }
841 }
842 p++;
843 }
844 TxMissing = keep;
845 }
846 }
847
848 // print any txpending blocks up to and including UrGoodHeader
849 list <cTxtBlk>::iterator p1 = TxPending.begin();
850
851 p1 = TxPending.begin();
852 while (p1 != TxPending.end()) {
853 if(p1->nbr() == UrGoodHeader) {
854 if (printTX) printTX(p1->text());
855 TxPending.erase(p1);
856 break;
857 } else
858 if (printTX) printTX(p1->text());
859 TxPending.erase(p1);
860 p1 = TxPending.begin();
861 }
862
863 switch (LinkState) {
864 case WAITFORACK :
865 LinkState = ARQ_CONNECTED;
866 break;
867 case DISCONNECTING :
868 if (rxUrCall) rxUrCall("");
869 LinkState = DOWN;
870 break;
871 case WAITING :
872 LinkState = ARQ_CONNECTED;
873 break;
874 // case ABORTING :
875 // reset();
876 // if (abortfnc) abortfnc();
877 // LinkState = ARQ_CONNECTED;
878 // break;
879 // case ABORT :
880 // break;
881 default: break;
882 }
883
884 printSTATUS(RXSTATUS, 5.0);
885 }
886
parsePOLL()887 void arq::parsePOLL()
888 {
889 if (LinkState == DISCONNECTING || LinkState == DOWN ||
890 LinkState == TIMEDOUT || LinkState == ABORT )
891 return;
892
893 statFrame();
894 immediate = true;
895 LinkState = ARQ_CONNECTED;
896 printSTATUS(RXPOLL, 5.0);
897 }
898
parseDATA()899 void arq::parseDATA()
900 {
901 vector<cTxtBlk>::iterator p1, p2;
902 int n1, n2;
903
904 if (LinkState < ARQ_CONNECTED) return; // do not respond if DOWN or TIMEDOUT
905
906 for (p1 = RxPending.begin(); p1 < RxPending.end(); p1++)
907 if (blknbr == p1->nbr()) {
908 return;
909 }
910
911 char szStatus[80];
912 snprintf(szStatus, sizeof(szStatus),"RX: data block %d", blknbr);
913 printSTATUS(szStatus, 5.0);
914
915 cTxtBlk tempblk(blknbr, rcvPayload);
916 RxPending.push_back (tempblk);
917
918 for (p1 = RxPending.begin(); p1 < RxPending.end() - 1; p1++) {
919 n1 = p1->nbr();
920 if (n1 < GoodHeader) n1 += MAXCOUNT;
921 for (p2 = p1 + 1; p2 < RxPending.end(); p2++) {
922 n2 = p2->nbr();
923 if (n2 < GoodHeader) n2 += MAXCOUNT;
924 if (n2 < n1) {
925 tempblk = *p1;
926 *p1 = *p2;
927 *p2 = tempblk;
928 }
929 }
930 }
931 // compute new EndHeader
932 EndHeader = GoodHeader;
933 if (!RxPending.empty()) {
934 p1 = RxPending.end() - 1;
935 EndHeader = p1->nbr();
936 }
937
938 // add RxPending blocks that are consecutive to GoodHeader
939 p1 = RxPending.begin();
940 while (!RxPending.empty()) {
941 if ((p1->nbr() != (GoodHeader +1) % MAXCOUNT))
942 break;
943 RxTextQueue.append(p1->text());
944 GoodHeader = p1->nbr();
945 if (printRX) printRX(p1->text());
946 RxPending.erase(p1);
947 p1 = RxPending.begin();
948 }
949
950 MissingRxBlocks = "";
951
952 if (RxPending.empty())
953 return;
954
955 int start = (GoodHeader + 1)%MAXCOUNT;
956 int end = (EndHeader + 1)%MAXCOUNT;
957 int test;
958 bool ok;
959 if (end < start) end += MAXCOUNT;
960 for (int i = start; i < end; i++) {
961 test = (i % MAXCOUNT);
962 ok = false;
963 for (p1 = RxPending.begin(); p1 < RxPending.end(); p1++) {
964 if (test == p1->nbr()) {
965 ok = true;
966 break;
967 }
968 }
969 if (!ok)
970 MissingRxBlocks += test + 0x20;
971 }
972 }
973
isUrcall()974 bool arq::isUrcall()
975 {
976 if (UrCall.empty())
977 return false;
978 if (rcvPayload.find(UrCall) != string::npos)
979 return true;
980 return false;
981 }
982
983
984 // expects to receive a full frame
985 // txt[0] == SOH
986 // txt[len - 3] ... txt[len] == CRC
987 // returns
988 // -1 invalid frame
989 // -n failed CRC for text type n
990 // n valid frame
991 // rcvPayload will contain the valid payload
992 //
parseFrame(string txt)993 int arq::parseFrame(string txt)
994 {
995 if ( txt.length() < 8 ) {
996 return -1; // not a valid frame
997 }
998 Ccrc16 testcrc;
999 size_t len = txt.length();
1000
1001 rcvPayload = txt.substr(4, len - 8);
1002 fID = txt[3];
1003
1004 // treat unproto TALK as a special case
1005 // no effort made to confirm the data by the CRC value
1006 if (fID == _TALK) {
1007 if (LinkState >= ARQ_CONNECTED) {
1008 timeout = Timeout / ARQLOOPTIME;
1009 parseTALK();
1010 retries = Retries;
1011 }
1012 return -1;
1013 }
1014
1015 string sRcvdCRC = testcrc.scrc16( txt.substr(0, len - 4));
1016
1017 if (sRcvdCRC != txt.substr(len - 4) ) {
1018 if (printRX_DEBUG)
1019 printRX_DEBUG("CRC failed\n");
1020 return -1; // failed CRC test
1021 }
1022
1023 retries = Retries;
1024
1025 switch (fID) {
1026 case IDENT :
1027 if (!isUrcall())
1028 break;
1029 blknbr = fID - 0x20;
1030 parseIDENT();
1031 if (printRX_DEBUG) {
1032 printRX_DEBUG("IDENT:");
1033 }
1034 break;
1035 case CONREQ :
1036 if (LinkState > TIMEDOUT)
1037 break; // disallow multiple connects
1038 blknbr = fID - 0x20;
1039 parseCONREQ();
1040 if (printRX_DEBUG) {
1041 printRX_DEBUG("CONREQ:");
1042 }
1043 break;
1044 case CONACK :
1045 if (!isUrcall())
1046 break;
1047 blknbr = fID - 0x20;
1048 parseCONACK();
1049 if (printRX_DEBUG) {
1050 printRX_DEBUG("CONACK:");
1051 }
1052 break;
1053 case DISREQ :
1054 if (!isUrcall())
1055 break;
1056 blknbr = fID - 0x20;
1057 parseDISREQ();
1058 if (printRX_DEBUG) {
1059 printRX_DEBUG("DISREQ:");
1060 }
1061 break;
1062 case _DISACK :
1063 if (!isUrcall())
1064 break;
1065 blknbr = fID - 0x20;
1066 parseDISACK();
1067 if (printRX_DEBUG) {
1068 printRX_DEBUG("DISACK: ");
1069 }
1070 break;
1071 case STATUS :
1072 if (LinkState == DOWN || LinkState == TIMEOUT)
1073 break;
1074 blknbr = fID - 0x20;
1075 parseSTATUS();
1076 if (printRX_DEBUG) {
1077 printRX_DEBUG("STATUS:");
1078 }
1079 break;
1080 case POLL :
1081 if (!isUrcall()) {
1082 break;
1083 }
1084 blknbr = fID - 0x20;
1085 parsePOLL();
1086 if (printRX_DEBUG) {
1087 printRX_DEBUG("POLL:");
1088 }
1089 break;
1090 case _ABORT :
1091 if (!isUrcall())
1092 break;
1093 blknbr = fID - 0x20;
1094 parseABORT();
1095 if (printRX_DEBUG) {
1096 printRX_DEBUG("RCVD ABORT:");
1097 }
1098 break;
1099 case _ACKABORT :
1100 blknbr = fID - 0x20;
1101 parseACKABORT();
1102 if (printRX_DEBUG) {
1103 printRX_DEBUG("RCVD ACK_ABORT:");
1104 }
1105 break;
1106 case _UNPROTO :
1107 if (LinkState >TIMEDOUT && !isUrcall())
1108 break; // disallow interruption
1109 blknbr = fID - 0x20;
1110 parseUNPROTO();
1111 if (printRX_DEBUG) {
1112 printRX_DEBUG("UNPROTO:");
1113 }
1114 break;
1115 default :
1116 blknbr = fID - 0x20;
1117 parseDATA();
1118 if (printRX_DEBUG) {
1119 printRX_DEBUG("DATA:");
1120 }
1121 }
1122 if (printRX_DEBUG) {
1123 printRX_DEBUG(txt); printRX_DEBUG("\n");
1124 }
1125
1126
1127 if (LinkState == ARQ_CONNECTED)
1128 timeout = Timeout / ARQLOOPTIME;
1129
1130 return fID;
1131 }
1132
1133
rcvChar(char c)1134 void arq::rcvChar( char c )
1135 {
1136 if ( c == 0x06 ) {
1137 Sending = 0;
1138 retrytime = rtry();
1139 timeout = Timeout / ARQLOOPTIME;
1140 tx2txdelay = TxDelay / ARQLOOPTIME;
1141 return;
1142 }
1143
1144 if (lastRxChar == SOH && c == SOH) // consecutive <SOH> characters
1145 return;
1146
1147 if (lastRxChar == EOT && c == EOT) // consecutive <EOT> characters
1148 return;
1149
1150 if (RxFrameQueue.empty()) {
1151 if (c == SOH)
1152 RxFrameQueue += c;
1153 } else {
1154 if (c == SOH || c == EOT) {
1155 parseFrame(RxFrameQueue);
1156 RxFrameQueue.clear();//erase();
1157 if (c == SOH) RxFrameQueue += c;
1158 } else
1159 RxFrameQueue += c;
1160 }
1161
1162 lastRxChar = c;
1163 }
1164
1165 //=====================================================================
1166
sendText(string txt)1167 void arq::sendText (string txt)
1168 {
1169 size_t offset = 0;
1170 cTxtBlk tempblk;
1171 if (LinkState < ARQ_CONNECTED) return;
1172
1173 Blocks2Send = 0;
1174 while (offset < txt.length()) {
1175 newblocknumber();
1176 tempblk.nbr(Lastqueued);
1177 tempblk.text(txt.substr(offset, Bufferlength));
1178 offset += Bufferlength;
1179 TxBlocks.push_back(tempblk);
1180 Blocks2Send++;
1181 }
1182 }
1183
sendblocks()1184 void arq::sendblocks()
1185 {
1186 char szStatus[80];
1187 int missedblks = 0, newblks = 0;
1188 int framecount = 0;
1189 cTxtBlk tempblk;
1190
1191 if (TxMissing.empty() == false) {
1192 list<cTxtBlk>::iterator p = TxMissing.begin();
1193 while (p != TxMissing.end()) {
1194 textFrame(*p);
1195 p++;
1196 framecount++;
1197 }
1198 }
1199 missedblks = framecount;
1200 if (!TxBlocks.empty()) {
1201 while (TxBlocks.empty() == false && framecount < maxheaders) {
1202 tempblk = TxBlocks.front();
1203 if ((tempblk.nbr() + 2)%MAXCOUNT == UrGoodHeader)
1204 break;
1205 TxBlocks.pop_front();
1206 TxMissing.push_back(tempblk);
1207 TxPending.push_back(tempblk);
1208 textFrame(tempblk);
1209 LastHeader = tempblk.nbr();
1210 framecount++;
1211 }
1212 }
1213 newblks = framecount - missedblks;
1214 snprintf(szStatus, sizeof(szStatus),"TX: repeat %d new %d",
1215 missedblks, newblks);
1216 printSTATUS(szStatus, 0.0);
1217
1218 if (!TxMissing.empty() || !TxBlocks.empty())
1219 pollFrame();
1220
1221 if (LinkState != ABORT && LinkState != ABORTING)
1222 LinkState = WAITING;
1223 }
1224
connect(string callsign)1225 void arq::connect(string callsign)
1226 {
1227 UrCall = callsign;
1228 for (size_t i = 0; i < UrCall.length(); i++)
1229 UrCall[i] = toupper(UrCall[i]);
1230
1231 if (rxUrCall) rxUrCall(UrCall);
1232 TxTextQueue.clear();
1233 connectFrame();
1234 LinkState = ARQ_CONNECTING;
1235 immediate = true;
1236 }
1237
disconnect()1238 void arq::disconnect()
1239 {
1240 Retries = baseRetries;
1241 Timeout = baseTimeout;
1242 RetryTime = baseRetryTime;
1243 totalTx = 0;
1244 nbrbadTx = 0;
1245
1246 LinkState = DISCONNECT;
1247 }
1248
abort()1249 void arq::abort()
1250 {
1251 // bABORT = true;
1252 LinkState = ABORT;
1253 }
1254
sendBeacon(string txt)1255 void arq::sendBeacon (string txt)
1256 {
1257 string deText = "<<< FLARQ Beacon >>> de ";
1258 deText.append(MyCall);
1259 TxTextQueue.clear();//erase();
1260 addToTxQue(deText);
1261 beaconFrame(txt);
1262 immediate = true;
1263 LinkState = DOWN;
1264 }
1265
sendPlainText(string txt)1266 void arq::sendPlainText( string txt )
1267 {
1268 size_t p = 0;
1269 while (p < txt.length()) {
1270 talkFrame(txt.substr(p, Bufferlength));
1271 p += Bufferlength;
1272 }
1273 }
1274
transmitdata()1275 void arq::transmitdata()
1276 {
1277 if (TxTextQueue.empty() == false) {
1278 sendfnc(TxTextQueue);
1279 Sending = 0x80;
1280 if (printTX_DEBUG)
1281 printTX_DEBUG(TxTextQueue);
1282 TxTextQueue.clear();
1283 tx2txdelay = TxDelay / ARQLOOPTIME;
1284 return;
1285 }
1286 if (TxPlainTextQueue.empty() == false) {
1287 sendfnc(TxPlainTextQueue);
1288 Sending = 0x80;
1289 if (printTX_DEBUG)
1290 printTX_DEBUG(TxPlainTextQueue);
1291 TxPlainTextQueue.clear();
1292 tx2txdelay = TxDelay / ARQLOOPTIME;
1293 }
1294 }
1295
rtry()1296 int arq::rtry()
1297 {
1298 return RetryTime / ARQLOOPTIME;
1299 // return (RetryTime + rand() * 5000 / RAND_MAX) / ARQLOOPTIME;
1300 }
1301
1302 //---------------------------------------------------------------------
1303
arqloop(void * who)1304 void arqloop(void *who)
1305 {
1306 arq *me = (arq *)who;
1307 char c;
1308
1309 if (idtimer) me->_idtimer--;
1310
1311 // check for received chars including 0x06 for Sending = 0
1312 if (me->getc1(c) == true) {
1313 me->rcvChar(c);
1314 while (me->getc1(c) == true)
1315 me->rcvChar(c);
1316 if (me->tx2txdelay < me->TxDelay / ARQLOOPTIME)
1317 me->tx2txdelay = me->TxDelay / ARQLOOPTIME;
1318 }
1319 if (me->Sending == 0) { // not transmitting
1320 // wait period between transmits
1321 if (me->tx2txdelay > 0) {
1322 me->tx2txdelay--;
1323 } else {
1324 if (me->immediate == true) {
1325 me->transmitdata();
1326 me->retrytime = me->rtry();
1327 me->retries = me->Retries;
1328 me->immediate = false;
1329 } else {
1330 switch (me->LinkState) {
1331 case ARQ_CONNECTING :
1332 break;
1333 case DISCONNECT :
1334 me->LinkState = DISCONNECTING;
1335 me->TxTextQueue.clear();
1336 me->TxMissing.clear();
1337 me->TxBlocks.clear();
1338 me->TxPlainTextQueue.clear();
1339 me->disconnectFrame();
1340 me->immediate = true;
1341 break;
1342 case DISCONNECTING :
1343 if (me->retrytime-- == 0) {
1344 me->retrytime = me->rtry();
1345 if (--me->retries) {
1346 me->TxTextQueue.clear();
1347 me->TxMissing.clear();
1348 me->TxBlocks.clear();
1349 me->TxPlainTextQueue.clear();
1350 me->disconnectFrame();
1351 me->transmitdata();
1352 me->timeout = me->Timeout / ARQLOOPTIME;
1353 }
1354 }
1355 break;
1356 case ABORT :
1357 me->LinkState = ABORTING;
1358 me->TxTextQueue.clear();
1359 me->TxMissing.clear();
1360 me->TxBlocks.clear();
1361 me->TxPlainTextQueue.clear();
1362 me->tx2txdelay = 5000/ ARQLOOPTIME; // 5 sec delay for abort
1363 me->abortFrame();
1364 me->immediate = true;
1365 break;
1366 case ABORTING :
1367 if (--me->retrytime == 0) {
1368 me->retrytime = me->rtry();
1369 if (me->retries--) {
1370 me->TxTextQueue.clear();
1371 me->TxMissing.clear();
1372 me->TxBlocks.clear();
1373 me->TxPlainTextQueue.clear();
1374 me->abortFrame();
1375 me->transmitdata();
1376 me->timeout = me->Timeout / ARQLOOPTIME;
1377 }
1378 }
1379 break;
1380 case WAITING :
1381 if (me->retrytime-- == 0) {
1382 me->retrytime = me->rtry();
1383 if (--me->retries) {
1384 me->TxTextQueue.clear();
1385 me->pollFrame();
1386 me->transmitdata();
1387 me->nbrbadTx++;
1388 me->timeout = me->Timeout / ARQLOOPTIME;
1389 }
1390 }
1391 break;
1392 case WAITFORACK :
1393 if (me->retrytime-- == 0) {
1394 me->retrytime = me->rtry();
1395 if (--me->retries) {
1396 me->TxTextQueue.clear();
1397 me->ackFrame();
1398 me->transmitdata();
1399 me->nbrbadTx++;
1400 me->timeout = me->Timeout / ARQLOOPTIME;
1401 }
1402 }
1403 break;
1404
1405 case ARQ_CONNECTED :
1406 default:
1407 if (me->TxTextQueue.empty() == false) {
1408 me->transmitdata();
1409 } else if ( (me->TxMissing.empty() == false) || (me->TxBlocks.empty() == false)) {
1410 me->nbrbadTx += me->TxMissing.size();
1411 me->sendblocks();
1412 me->transmitdata();
1413 } else if ( me->TxPlainTextQueue.empty() == false ) {
1414 me->transmitdata();
1415 }
1416 break;
1417 }
1418 me->timeout--;
1419 if (me->timeout == 0 // 10000 / ARQLOOPTIME // 10 seconds remaining
1420 && me->LinkState == ARQ_CONNECTED // link is connected
1421 && me->primary == true ) { // this is the connecting station
1422 if (--me->retries) { // repeat Retries and then allow timeout
1423 me->TxTextQueue.clear();
1424 me->identFrame(); // send an identity frame to try to keep
1425 me->transmitdata(); // the link up
1426 me->timeout = me->rtry(); //10000 / ARQLOOPTIME + 1;
1427 }
1428 }
1429 if (me->timeout == 0) {
1430 if (me->LinkState == ARQ_CONNECTED)
1431 me->LinkState = TIMEDOUT;
1432 else
1433 me->LinkState = DOWN;
1434 me->Retries = me->baseRetries;
1435 me->Timeout = me->baseTimeout;
1436 me->RetryTime = me->baseRetryTime;
1437
1438 me->retries = me->Retries;
1439 me->retrytime = me->rtry();
1440 me->TxMissing.clear();
1441 me->TxBlocks.clear();
1442 me->TxTextQueue.clear();
1443 me->TxPlainTextQueue.clear();
1444 me->timeout = me->Timeout / ARQLOOPTIME;
1445 if (me->rxUrCall) me->rxUrCall("");
1446 }
1447 if (me->retries == 0) {
1448 me->LinkState = DOWN;
1449 me->Retries = me->baseRetries;
1450 me->Timeout = me->baseTimeout;
1451 me->RetryTime = me->baseRetryTime;
1452
1453 me->retries = me->Retries;
1454 me->retrytime = me->rtry();
1455 me->TxMissing.clear();
1456 me->TxBlocks.clear();
1457 me->TxTextQueue.clear();
1458 me->TxPlainTextQueue.clear();
1459 me->timeout = me->Timeout / ARQLOOPTIME;
1460 if (me->rxUrCall) me->rxUrCall("");
1461 me->printSTATUS(STIMEDOUT, 10.0);
1462 }
1463 }
1464 }
1465 }
1466
1467 if (me->arqstop) {
1468 me->LinkState = STOPPED;
1469 me->arqstop = false;
1470
1471 me->LinkState = DOWN;
1472 me->Retries = me->baseRetries;
1473 me->Timeout = me->baseTimeout;
1474 me->RetryTime = me->baseRetryTime;
1475 me->retries = me->Retries;
1476 me->retrytime = me->rtry();
1477
1478 me->TxMissing.clear();
1479 me->TxBlocks.clear();
1480 me->TxTextQueue.clear();
1481 me->TxPlainTextQueue.clear();
1482 me->timeout = me->Timeout / ARQLOOPTIME;
1483
1484 if (me->rxUrCall) me->rxUrCall("");
1485
1486 me->printSTATUS(STIMEDOUT, 10.0);
1487 Fl::repeat_timeout( 1.0, arqloop, me);
1488 return;
1489 }
1490
1491 Fl::repeat_timeout( ARQLOOPTIME/1000.0, arqloop, me);
1492 }
1493
1494
start_arq()1495 void arq::start_arq()
1496 {
1497 Fl::add_timeout(1.0, arqloop, this);
1498 }
1499
restart_arq()1500 void arq::restart_arq() {
1501 arqstop = true;
1502 }
1503
1504 //---------------------------------------------------------------------
1505