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