1 // ----------------------------------------------------------------------------
2 // arq_io.cxx
3 //
4 // support for ARQ server/client system such as pskmail and fl_arq
5 //
6 // Copyright (C) 2006-2017
7 // Dave Freese, W1HKJ
8 // Copyright (C) 2008-2013
9 // Stelios Bounanos, M0GLD
10 // Copyright (C) 2009-2013
11 // John Douyere, VK2ETA
12 // Copyright (c) 2013
13 // Remi Chateauneu, F4ECW
14 //
15 // This file is part of fldigi.
16 //
17 // Fldigi is free software: you can redistribute it and/or modify
18 // it under the terms of the GNU General Public License as published by
19 // the Free Software Foundation, either version 3 of the License, or
20 // (at your option) any later version.
21 //
22 // Fldigi is distributed in the hope that it will be useful,
23 // but WITHOUT ANY WARRANTY; without even the implied warranty of
24 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 // GNU General Public License for more details.
26 //
27 // You should have received a copy of the GNU General Public License
28 // along with fldigi. If not, see <http://www.gnu.org/licenses/>.
29 // ----------------------------------------------------------------------------
30
31
32 #include <config.h>
33
34 #ifdef __MINGW32__
35 # include "compat.h"
36 #endif
37
38 #include <fstream>
39 #include <sstream>
40 #include <string>
41 #include <cstdlib>
42 #include <ctime>
43 #include <errno.h>
44
45 #include <sys/types.h>
46 #if !defined(__WIN32__) && !defined(__APPLE__)
47 # include <sys/ipc.h>
48 # include <sys/msg.h>
49 #endif
50
51 #include <signal.h>
52
53 #include "main.h"
54 #include "configuration.h"
55 #include "fl_digi.h"
56 #include "trx.h"
57 #include "arq_io.h"
58
59 #include "threads.h"
60 #include "socket.h"
61 #include "debug.h"
62 #include "qrunner.h"
63
64 #include <FL/Fl.H>
65 #include <FL/fl_ask.H>
66
67 LOG_FILE_SOURCE(debug::LOG_ARQCONTROL);
68
69 using namespace std;
70
71 // =====================================================================
72 static pthread_t arq_thread;
73 static pthread_mutex_t arq_mutex = PTHREAD_MUTEX_INITIALIZER;
74 static pthread_mutex_t arq_rx_mutex = PTHREAD_MUTEX_INITIALIZER;
75 static pthread_mutex_t tosend_mutex = PTHREAD_MUTEX_INITIALIZER;
76
77 static void *arq_loop(void *args);
78
79 static bool arq_exit = false;
80 static bool arq_enabled;
81 static bool abort_flag = false;
82
83 /// Any access to shared variables must be protected.
84 static string tosend = ""; // Protected by tosend_mutex
85 //static string enroute = ""; // Protected by tosend_mutex
86
87 static string arqtext = ""; // Protected by arq_rx_mutex
88 static string txstring = ""; // Protected by arq_rx_mutex
89 bool arq_text_available = false; // Protected by arq_rx_mutex
90 // Beware 'arq_text_available' is accessed by other modules.
91
92 // =====================================================================
93
94 static const char *asc[128] = {
95 "<NUL>", "<SOH>", "<STX>", "<ETX>",
96 "<EOT>", "<ENQ>", "<ACK>", "<BEL>",
97 "<BS>", "<TAB>", "\n", "<VT>",
98 "<FF>", "", "<SO>", "<SI>",
99 "<DLE>", "<DC1>", "<DC2>", "<DC3>",
100 "<DC4>", "<NAK>", "<SYN>", "<ETB>",
101 "<CAN>", "<EM>", "<SUB>", "<ESC>",
102 "<FS>", "<GS>", "<RS>", "<US>",
103 " ", "!", "\"", "#",
104 "$", "%", "&", "\'",
105 "(", ")", "*", "+",
106 ",", "-", ".", "/",
107 "0", "1", "2", "3",
108 "4", "5", "6", "7",
109 "8", "9", ":", ";",
110 "<", "=", ">", "?",
111 "@", "A", "B", "C",
112 "D", "E", "F", "G",
113 "H", "I", "J", "K",
114 "L", "M", "N", "O",
115 "P", "Q", "R", "S",
116 "T", "U", "V", "W",
117 "X", "Y", "Z", "[",
118 "\\", "]", "^", "_",
119 "`", "a", "b", "c",
120 "d", "e", "f", "g",
121 "h", "i", "j", "k",
122 "l", "m", "n", "o",
123 "p", "q", "r", "s",
124 "t", "u", "v", "w",
125 "x", "y", "z", "{",
126 "|", "}", "~", "<DEL>"
127 };
128
noctrl(string src)129 string noctrl(string src)
130 {
131 static string retstr;
132 retstr.clear();
133 char hexstr[10];
134 int c;
135 for (size_t i = 0; i < src.length(); i++) {
136 c = src[i];
137 if ( c > 0 && c < 128)
138 retstr.append(asc[c]);
139 else {
140 snprintf(hexstr, sizeof(hexstr), "<%0X>", c & 0xFF);
141 retstr.append(hexstr);
142 }
143 }
144 return retstr;
145 }
146
147 //======================================================================
148
149 extern void parse_arqtext(string &toparse);
150
set_button(Fl_Button * button,bool value)151 static void set_button(Fl_Button* button, bool value)
152 {
153 button->value(value);
154 button->do_callback();
155 }
156
ParseMode(string src)157 void ParseMode(string src)
158 {
159 LOG_INFO("%s", src.c_str());
160 if ((src.find("XMTTUNE") != string::npos) ||
161 (src.find("PTTTUNE") != string::npos)) {
162 int msecs = 100;
163 if (src.length() > 7) {
164 int ret = sscanf( src.substr(7, src.length() - 7).c_str(), "%d", &msecs);
165 if (ret != 1 || msecs < 10 || msecs > 20000) msecs = 100;
166 }
167 // if (debug_pskmail)
168 LOG_INFO("%s %5.2f sec", "ARQ set ptt-tune on", msecs/1000.0);
169 REQ_SYNC(&waterfall::set_XmtRcvBtn, wf, true);
170 REQ_SYNC(trx_tune);
171 MilliSleep(msecs);
172 // if (debug_pskmail)
173 LOG_INFO("%s", "ARQ set ptt-tune off");
174 REQ_SYNC(&waterfall::set_XmtRcvBtn, wf, false);
175 REQ_SYNC(trx_receive);
176 }
177 else for (size_t i = 0; i < NUM_MODES; ++i) {
178 if (strlen(mode_info[i].pskmail_name) > 0) {
179 if (src == mode_info[i].pskmail_name) {
180 if (active_modem->get_mode() == mode_info[i].mode) {
181 LOG_INFO("Active modem already set to %s", src.c_str());
182 } else {
183 REQ_SYNC(init_modem_sync, mode_info[i].mode, 0);
184 MilliSleep(100);
185 // AbortARQ();
186 // if (debug_pskmail)
187 LOG_INFO("Modem set to %s", mode_info[i].pskmail_name);
188 }
189 break;
190 }
191 }
192 }
193 WriteARQ('\002');
194 }
195
ParseRSID(string src)196 void ParseRSID(string src)
197 {
198 if (src == "ON") {
199 // if (debug_pskmail)
200 LOG_INFO("%s", "RsID turned ON");
201 REQ(set_button, btnRSID, 1);
202 }
203 if (src == "OFF") {
204 // if (debug_pskmail)
205 LOG_INFO("%s", "RsID turned OFF");
206 REQ(set_button, btnRSID, 0);
207 }
208 }
209
210
ParseTxRSID(string src)211 void ParseTxRSID(string src)
212 {
213 if (src == "ON") {
214 // if (debug_pskmail)
215 LOG_INFO("%s", "TxRsID turned ON");
216 REQ(set_button, btnTxRSID, 1);
217 }
218 if (src == "OFF") {
219 // if (debug_pskmail)
220 LOG_INFO("%s", "TxRsID turned OFF");
221 REQ(set_button, btnTxRSID, 0);
222 }
223 }
224
parse_arqtext(string & toparse)225 void parse_arqtext(string &toparse)
226 {
227 static string strCmdText;
228 static string strSubCmd;
229 unsigned long int idxCmd, idxCmdEnd, idxSubCmd, idxSubCmdEnd;
230
231 if (toparse.empty()) return;
232
233 LOG_VERBOSE("parsing: %s", noctrl(toparse).c_str());
234
235
236 idxCmd = toparse.find("<cmd>");
237 idxCmdEnd = toparse.find("</cmd>");
238
239 while ( idxCmd != string::npos && idxCmdEnd != string::npos && idxCmdEnd > idxCmd ) {
240 LOG_VERBOSE("Parsing: %s", noctrl(toparse.substr(idxCmd, idxCmdEnd - idxCmd + 6)).c_str());
241
242 strCmdText = toparse.substr(idxCmd + 5, idxCmdEnd - idxCmd - 5);
243 if (strCmdText == "server" && mailserver == false && mailclient == false) {
244 mailserver = true;
245 mailclient = false;
246 string PskMailLogName;
247 PskMailLogName.assign(PskMailDir);
248 PskMailLogName.append("gMFSK.log");
249 Maillogfile = new cLogfile(PskMailLogName.c_str());
250 Maillogfile->log_to_file_start();
251 REQ(set_button, wf->xmtlock, 1);
252 if (progdefaults.PSKmailSweetSpot)
253 active_modem->set_freq(progdefaults.PSKsweetspot);
254 active_modem->set_freqlock(true);
255 LOG_INFO("%s", "ARQ is set to pskmail server");
256 } else if (strCmdText == "client" && mailclient == false && mailserver == false) {
257 mailclient = true;
258 mailserver = false;
259 string PskMailLogName;
260 PskMailLogName.assign(PskMailDir);
261 PskMailLogName.append("gMFSK.log");
262 Maillogfile = new cLogfile(PskMailLogName.c_str());
263 Maillogfile->log_to_file_start();
264 REQ(set_button, wf->xmtlock, 0);
265 active_modem->set_freqlock(false);
266 LOG_INFO("%s", "ARQ is set to pskmail client");
267 } else if (strCmdText == "normal") {
268 mailserver = false;
269 mailclient = false;
270 if (Maillogfile) {
271 delete Maillogfile;
272 Maillogfile = 0;
273 }
274 REQ(set_button, wf->xmtlock, 0);
275 active_modem->set_freqlock(false);
276 LOG_INFO("%s", "ARQ is reset to normal ops");
277 } else if ((idxSubCmd = strCmdText.find("<mode>")) != string::npos) {
278 idxSubCmdEnd = strCmdText.find("</mode>");
279 if ( idxSubCmdEnd != string::npos &&
280 idxSubCmdEnd > idxSubCmd ) {
281 strSubCmd = strCmdText.substr(idxSubCmd + 6, idxSubCmdEnd - idxSubCmd - 6);
282 LOG_INFO("%s %s", "ARQ mode ", strSubCmd.c_str());
283 ParseMode(strSubCmd);
284 }
285 } else if ((idxSubCmd = strCmdText.find("<rsid>")) != string::npos) {
286 idxSubCmdEnd = strCmdText.find("</rsid>");
287 if ( idxSubCmdEnd != string::npos &&
288 idxSubCmdEnd > idxSubCmd ) {
289 strSubCmd = strCmdText.substr(idxSubCmd + 6, idxSubCmdEnd - idxSubCmd - 6);
290 ParseRSID(strSubCmd);
291 LOG_INFO("%s %s", "ARQ rsid ", strSubCmd.c_str());
292 }
293 } else if ((idxSubCmd = strCmdText.find("<txrsid>")) != string::npos) {
294 idxSubCmdEnd = strCmdText.find("</txrsid>");
295 if ( idxSubCmdEnd != string::npos &&
296 idxSubCmdEnd > idxSubCmd ) {
297 strSubCmd = strCmdText.substr(idxSubCmd + 8, idxSubCmdEnd - idxSubCmd - 8);
298 ParseTxRSID(strSubCmd);
299 LOG_INFO("%s %s", "ARQ txrsid ", strSubCmd.c_str());
300 }
301 } else if (strCmdText == "abort") {
302 LOG_INFO("%s", "Abort current ARQ ops");
303 abort_flag = true;
304 }
305
306 toparse.erase(idxCmd, idxCmdEnd - idxCmd + 6);
307 while (toparse[0] == '\n' || toparse[0] == '\r') toparse.erase(0, 1);
308
309 idxCmd = toparse.find("<cmd>");
310 idxCmdEnd = toparse.find("</cmd>");
311 }
312 if (!toparse.empty())
313 LOG_VERBOSE("Remaining text: %s", noctrl(toparse).c_str());
314 }
315
316 #define TIMEOUT 180 // 3 minutes
317
318 //======================================================================
319 // Gmfsk ARQ file i/o used only on Linux
320 //======================================================================
321 // checkTLF
322 // look for files named
323 // TLFfldigi ==> tlfio is true and
324 // ==> mailclient is true
325 // in $HOME
326
checkTLF()327 void checkTLF() {
328 static string TLFfile;
329 static string TLFlogname;
330 ifstream testFile;
331
332 tlfio = mailserver = mailclient = false;
333
334 TLFfile.assign(PskMailDir);
335 TLFfile.append("TLFfldigi");
336
337 testFile.open(TLFfile.c_str());
338 if (testFile.is_open()) {
339 testFile.close();
340 mailclient = true;
341 tlfio = true;
342 TLFlogname.assign(PskMailDir);
343 TLFlogname.append("gMFSK.log");
344 Maillogfile = new cLogfile(TLFlogname.c_str());
345 Maillogfile->log_to_file_start();
346 }
347 }
348
TLF_arqRx()349 static bool TLF_arqRx()
350 {
351 /// The mutex is automatically unlocked when returning.
352 #if defined(__WIN32__) || defined(__APPLE__)
353 return false;
354 #else
355 time_t start_time, prog_time;
356 static char mailline[1000];
357 static string sAutoFile("");
358 sAutoFile.assign(PskMailDir);
359 sAutoFile.append("gmfsk_autofile");
360
361 ifstream autofile(sAutoFile.c_str());
362 if(autofile) {
363 time(&start_time);
364 while (!autofile.eof()) {
365 memset(mailline, 0, sizeof(mailline));
366 autofile.getline(mailline, 998); // leave space for "\n" and null byte
367 txstring.append(mailline);
368 txstring.append("\n");
369 time(&prog_time);
370 if (prog_time - start_time > TIMEOUT) {
371 LOG_ERROR("TLF file I/O failure");
372 autofile.close();
373 std::remove (sAutoFile.c_str());
374 return false;
375 }
376 }
377 autofile.close();
378 std::remove (sAutoFile.c_str());
379
380 parse_arqtext(txstring);
381
382 if (abort_flag) {
383 AbortARQ();
384 abort_flag = false;
385 return true;
386 }
387
388 if (!txstring.empty()) {
389 guard_lock arq_rx_lock(&arq_rx_mutex);
390 if (arqtext.empty()) {
391 arqtext = txstring;
392 if (mailserver && progdefaults.PSKmailSweetSpot)
393 active_modem->set_freq(progdefaults.PSKsweetspot);
394 arq_text_available = true;
395 active_modem->set_stopflag(false);
396 start_tx();
397 } else {
398 arqtext.append(txstring);
399 active_modem->set_stopflag(false);
400 }
401 txstring.clear();
402 }
403 }
404 return true;
405 #endif
406 }
407
408 //======================================================================
409 // Auto transmit of file contained in WRAP_auto_dir
410 //======================================================================
WRAP_auto_arqRx()411 bool WRAP_auto_arqRx()
412 {
413 time_t start_time, prog_time;
414 static char mailline[1000];
415 static string sAutoFile("");
416
417 ifstream autofile;
418
419 if (sAutoFile.empty()) {
420 sAutoFile.assign(FLMSG_WRAP_auto_dir);
421 sAutoFile.append("wrap_auto_file");
422 autofile.open(sAutoFile.c_str());
423 if (!autofile) {
424 sAutoFile.assign(WRAP_auto_dir);
425 sAutoFile.append("wrap_auto_file");
426 autofile.open(sAutoFile.c_str());
427 }
428 } else
429 autofile.open(sAutoFile.c_str());
430
431 if(autofile) {
432 /// Mutex is unlocked when leaving the block.
433 guard_lock arq_rx_lock(&arq_rx_mutex);
434 txstring.clear();
435 time(&start_time);
436 while (!autofile.eof()) {
437 memset(mailline,0,1000);
438 autofile.getline(mailline, 998); // leave space for "\n" and null byte
439 txstring.append(mailline);
440 txstring.append("\n");
441 time(&prog_time);
442 if (prog_time - start_time > TIMEOUT) {
443 LOG_ERROR("autowrap file I/O failure");
444 autofile.close();
445 std::remove (sAutoFile.c_str());
446 return false;
447 }
448 }
449 autofile.close();
450 std::remove (sAutoFile.c_str());
451
452 if (!txstring.empty()) {
453 arqtext.assign("\n....start\n");
454 arqtext.append(txstring);
455 arqtext.append("\n......end\n");
456 arq_text_available = true;
457 LOG_DEBUG("%s", arqtext.c_str());
458 start_tx();
459 txstring.clear();
460 return true;
461 }
462 }
463 return false;
464 }
465
466 //======================================================================
467 // Socket ARQ i/o used on all platforms
468 //======================================================================
469
470 #define ARQLOOP_TIMING 50 // 100 // msec
471 #define CLIENT_TIMEOUT 5 // timeout after 5 secs
472
473 struct ARQCLIENT { Socket sock; time_t keep_alive; };
474 static string errstring;
475
476 static pthread_t* arq_socket_thread = 0;
477 ARQ_SOCKET_Server* ARQ_SOCKET_Server::inst = 0;
478 static std::vector<ARQCLIENT *> arqclient; // Protected by arq_mutex
479
480 void arq_run(Socket);
481
ARQ_SOCKET_Server()482 ARQ_SOCKET_Server::ARQ_SOCKET_Server()
483 {
484 server_socket = new Socket;
485 arq_socket_thread = new pthread_t;
486 run = true;
487 }
488
~ARQ_SOCKET_Server()489 ARQ_SOCKET_Server::~ARQ_SOCKET_Server()
490 {
491 run = false;
492 if (arq_socket_thread) {
493 CANCEL_THREAD(*arq_socket_thread);
494 pthread_join(*arq_socket_thread, NULL);
495 delete arq_socket_thread;
496 arq_socket_thread = 0;
497 }
498 delete server_socket;
499 }
500
start(const char * node,const char * service)501 bool ARQ_SOCKET_Server::start(const char* node, const char* service)
502 {
503 if (inst) return false;
504
505 inst = new ARQ_SOCKET_Server;
506
507 try {
508 inst->server_socket->open(Address(node, service));
509 inst->server_socket->bind();
510 #ifdef __WIN32__
511 inst->server_socket->listen();
512 inst->server_socket->set_timeout(0.1);
513 #endif
514 }
515 catch (const SocketException& e) {
516 errstring.assign("Could not start ARQ server (");
517 errstring.append(e.what()).append(")");
518 if (e.error() == EADDRINUSE)
519 errstring.append("\nMultiple instances of fldigi??");
520 LOG_ERROR("%s", errstring.c_str());
521
522 delete arq_socket_thread;
523 arq_socket_thread = 0;
524 delete inst;
525 inst = 0;
526 return false;
527 }
528
529 return !pthread_create(arq_socket_thread, NULL, thread_func, NULL);
530 }
531
532 bool server_stopped = false;
533
stop(void)534 void ARQ_SOCKET_Server::stop(void)
535 {
536 if (!inst)
537 return;
538 inst->run = false;
539 SET_THREAD_CANCEL();
540 MilliSleep(50);
541
542 #if !defined(__WOE32__) && !defined(__APPLE__)
543 int timeout = 10;
544 while(!server_stopped) {
545 MilliSleep(100);
546 Fl::awake();
547 if (--timeout == 0) break;
548 }
549 #endif
550
551 delete inst;
552 inst = 0;
553
554 }
555
thread_func(void *)556 void* ARQ_SOCKET_Server::thread_func(void*)
557 {
558 SET_THREAD_ID(ARQSOCKET_TID);
559
560 SET_THREAD_CANCEL();
561
562 // On POSIX we block indefinitely and are interrupted by a signal.
563 // On WIN32 we block for a short time and test for cancellation.
564 while (inst->run) {
565 try {
566 #ifdef __WIN32__
567 if (inst->server_socket->wait(0))
568 arq_run(inst->server_socket->accept());
569 #else
570 arq_run(inst->server_socket->accept());
571 TEST_THREAD_CANCEL();
572 #endif
573 }
574 catch (const SocketException& e) {
575 if (e.error() != EINTR) {
576 errstring = e.what();
577 LOG_ERROR("%s", errstring.c_str());
578 break;
579 }
580 }
581 catch (...) {
582 break;
583 }
584 }
585
586 {
587 /// Mutex is unlocked when leaving the block.
588 guard_lock arq_lock(&arq_mutex);
589
590 if (!arqclient.empty()) {
591 for (vector<ARQCLIENT *>::iterator p = arqclient.begin();
592 p < arqclient.end();
593 p++) {
594 (*p)->sock.close();
595 arqclient.erase(p);
596 }
597 }
598 }
599
600 inst->server_socket->close();
601 server_stopped = true;
602 return NULL;
603 }
604
arq_reset()605 void arq_reset()
606 {
607 /// Mutex is unlocked when returning from function
608 guard_lock arq_rx_lock(&arq_rx_mutex);
609 arqmode = mailserver = mailclient = false;
610 // txstring.clear();
611 // arqtext.clear();
612 }
613
arq_run(Socket s)614 void arq_run(Socket s)
615 {
616 /// Mutex is unlocked when returning from function
617 guard_lock arq_lock(&arq_mutex);
618 struct timeval t = { 0, 20000 };
619 s.set_timeout(t);
620 s.set_nonblocking();
621 ARQCLIENT *client = new ARQCLIENT;
622 client->sock = s;
623 client->keep_alive = time(0);
624 arqclient.push_back(client);
625 arqmode = true;
626 vector<ARQCLIENT *>::iterator p = arqclient.begin();
627 ostringstream outs;
628 outs << "Clients: ";
629 while (p != arqclient.end()) {
630 outs << (*p)->sock.fd() << " ";
631 p++;
632 }
633 LOG_INFO("%s", outs.str().c_str());
634 }
635
WriteARQsocket(unsigned char * data,size_t len)636 void WriteARQsocket(unsigned char* data, size_t len)
637 {
638 /// Mutex is unlocked when returning from function
639 guard_lock arq_lock(&arq_mutex);
640 if (arqclient.empty()) return;
641 static string instr;
642 instr.clear();
643
644 string outs = "";
645 for (unsigned int i = 0; i < len; i++)
646 outs += asc[data[i] & 0x7F];
647 LOG_INFO("%s", outs.c_str());
648
649 vector<ARQCLIENT *>::iterator p;
650 for (p = arqclient.begin(); p < arqclient.end(); p++) {
651 try {
652 (*p)->sock.wait(1);
653 (*p)->sock.send(data, len);
654 (*p)->keep_alive = time(0);
655 p++;
656 }
657 catch (const SocketException& e) {
658 LOG_INFO("closing socket fd %d %s", (*p)->sock.fd(), e.what());
659 try {
660 (*p)->sock.close();
661 } catch (const SocketException& e) {
662 LOG_ERROR("Socket error on # %d, %d: %s", (*p)->sock.fd(), e.error(), e.what());
663 }
664 arqclient.erase(p);
665 }
666 }
667
668 if (arqclient.empty()) arq_reset();
669 }
670
test_arq_clients()671 void test_arq_clients()
672 {
673 /// Mutex is unlocked when returning from function
674 guard_lock arq_lock(&arq_mutex);
675 if (arqclient.empty()) return;
676 static string instr;
677 instr.clear();
678 vector<ARQCLIENT *>::iterator p;
679 p = arqclient.begin();
680 time_t now;
681 size_t ret;
682 while (p < arqclient.end()) {
683 if (difftime(now = time(0), (*p)->keep_alive) > CLIENT_TIMEOUT) {
684 try {
685 (*p)->sock.wait(1);
686 ret = (*p)->sock.send("\0", 1);
687 if (ret <= 0) {
688 LOG_INFO("closing inactive socket %d", (int)((*p)->sock.fd()));
689 (*p)->sock.close();
690 arqclient.erase(p); // sets p to next iterator
691 } else {
692 (*p)->keep_alive = now;
693 p++;
694 }
695 }
696 catch (const SocketException& e) {
697 LOG_INFO("socket %d timed out, error %d, %s", (*p)->sock.fd(), e.error(), e.what());
698 try {
699 (*p)->sock.close();
700 } catch (const SocketException& e) {
701 LOG_ERROR("Socket error on # %d, %d: %s", (*p)->sock.fd(), e.error(), e.what());
702 }
703 arqclient.erase(p);
704 }
705 } else {
706 p++;
707 }
708 }
709 if (arqclient.empty()) arq_reset();
710 }
711
Socket_arqRx()712 bool Socket_arqRx()
713 {
714 {
715 /// Mutex is unlocked when leaving block
716 guard_lock arq_lock(&arq_mutex);
717 if (arqclient.empty()) return false;
718
719 static string instr;
720 vector<ARQCLIENT *>::iterator p = arqclient.begin();
721 size_t n = 0;
722 instr.clear();
723 while (p != arqclient.end()) {
724 try {
725 (*p)->sock.wait(0);
726 while ( (n = (*p)->sock.recv(instr)) > 0) {
727 txstring.append(instr);
728 LOG_VERBOSE("%s", txstring.c_str());
729 }
730 p++;
731 }
732 catch (const SocketException& e) {
733 txstring.clear();
734 LOG_INFO("closing socket fd %d, %d: %s", (*p)->sock.fd(), e.error(), e.what());
735 try {
736 (*p)->sock.close();
737 } catch (const SocketException& e) {
738 LOG_ERROR("socket error on # %d, %d: %s", (*p)->sock.fd(), e.error(), e.what());
739 }
740 arqclient.erase(p);
741 }
742 }
743
744 if (arqclient.empty()) arq_reset();
745
746 }
747
748 if (!txstring.empty()) parse_arqtext(txstring);
749
750 if (abort_flag) {
751 AbortARQ();
752 abort_flag = false;
753 return true;
754 }
755
756 {
757 /// Mutex is unlocked when leaving block
758 guard_lock arq_rx_lock(&arq_rx_mutex);
759
760 if (txstring.empty()) return false;
761
762 arqtext.append(txstring);
763
764 if (mailserver && progdefaults.PSKmailSweetSpot)
765 active_modem->set_freq(progdefaults.PSKsweetspot);
766
767 if (trx_state != STATE_TX)
768 start_tx();
769
770 txstring.clear();
771
772 arq_text_available = true;
773 active_modem->set_stopflag(false);
774
775 }
776 return true;
777 }
778
779 //======================================================================
780 // Implementation using thread vice the fldigi timeout facility
781 //======================================================================
782
WriteARQ(unsigned char data)783 void WriteARQ(unsigned char data)
784 {
785 if (active_modem->get_mode() == MODE_FSQ) return;
786 guard_lock tosend_lock(&tosend_mutex);
787 tosend += data;
788 }
789
WriteARQ(const char * data)790 void WriteARQ(const char *data)
791 {
792 if (active_modem->get_mode() == MODE_FSQ) return;
793 guard_lock tosend_lock(&tosend_mutex);
794 tosend.append(data);
795 }
796
arq_loop(void * args)797 static void *arq_loop(void *args)
798 {
799 SET_THREAD_ID(ARQ_TID);
800
801 for (;;) {
802 /* see if we are being canceled */
803 if (arq_exit)
804 break;
805
806 test_arq_clients();
807
808 {
809 /// Mutex is unlocked when exiting block
810 guard_lock tosend_lock(&tosend_mutex);
811 // enroute.clear();
812 if (!tosend.empty()) {
813 // enroute = tosend;
814 WriteARQsocket((unsigned char*)tosend.c_str(), tosend.length());
815 tosend.clear();
816 }
817
818 // if (!enroute.empty()) {
819 // WriteARQsocket((unsigned char*)enroute.c_str(), enroute.length());
820 // }
821 }
822 if (arq_exit) break;
823
824 // order of precedence; Socket, Wrap autofile, TLF autofile
825 if (!Socket_arqRx())
826 if (!WRAP_auto_arqRx())
827 TLF_arqRx();
828
829 MilliSleep(ARQLOOP_TIMING);
830
831 }
832 // exit the arq thread
833 return NULL;
834 }
835
arq_state(void)836 bool arq_state(void)
837 {
838 return arq_enabled;
839 }
840
arq_init()841 void arq_init()
842 {
843 arq_enabled = false;
844
845 txstring.clear();
846 arqclient.clear();
847
848 if (!ARQ_SOCKET_Server::start( progdefaults.arq_address.c_str(), progdefaults.arq_port.c_str() )) {
849 arq_enabled = false;
850 return;
851 }
852
853 if (pthread_create(&arq_thread, NULL, arq_loop, NULL) < 0) {
854 LOG_ERROR("arq init: pthread_create failed");
855 arq_enabled = false;
856 return;
857 }
858
859 arq_enabled = true;
860 }
861
arq_close(void)862 void arq_close(void)
863 {
864 if (!arq_enabled) return;
865
866 ARQ_SOCKET_Server::stop();
867
868 // tell the arq thread to kill it self
869 {
870 guard_lock arqclose(&tosend_mutex);
871 arq_exit = true;
872 }
873 // and then wait for it to die
874 pthread_join(arq_thread, NULL);
875
876 arq_enabled = false;
877 LOG_INFO("ARQ closed");
878 if(data_io_enabled == ARQ_IO)
879 data_io_enabled = DISABLED_IO ;
880 arq_exit = false;
881
882 }
883
arq_get_char()884 int arq_get_char()
885 {
886 /// Mutex is unlocked when returning from function
887 guard_lock arq_rx_lock(&arq_rx_mutex);
888 int c = 0;
889 if (arq_text_available) {
890 if (!arqtext.empty()) {
891 c = arqtext[0] & 0xFF;
892 arqtext.erase(0,1);
893 } else {
894 arq_text_available = false;
895 c = GET_TX_CHAR_ETX;
896 }
897 }
898 return c;
899 }
900
flush_arq_tx_buffer(void)901 void flush_arq_tx_buffer(void)
902 {
903 guard_lock arq_rx_lock(&arq_rx_mutex);
904 arq_text_available = false;
905 // arqtext.clear();
906 }
907
908 //======================================================================
909 // following function used if the T/R button is pressed to stop a transmission
910 // that is servicing the ARQ text buffer. It allows the ARQ client to reset
911 // itself properly
912 //======================================================================
AbortARQ()913 void AbortARQ() {
914 /// Mutex is unlocked when returning from function
915 guard_lock arq_lock(&arq_rx_mutex);
916 arqtext.clear();
917 txstring.clear();
918 arq_text_available = false;
919 }
920
921 //======================================================================
922 // Special notification for PSKMAIL: new mode marked only, in following
923 // format: "<DC2><Mode:newmode>", with <DC2> = '0x12'.
924 //======================================================================
pskmail_notify_rsid(trx_mode mode)925 void pskmail_notify_rsid(trx_mode mode)
926 {
927 static char buf[64];
928 memset(buf, 0, sizeof(buf));
929 int n = snprintf(buf, sizeof(buf),
930 "\x12<Mode:%s>\n",
931 mode_info[mode].name);
932 if (n > 0 && n < (int)sizeof(buf)) {
933 WriteARQ((const char *)buf);
934 REQ(&FTextBase::addstr, ReceiveText, buf, FTextBase::CTRL);
935 LOG_INFO("%s", buf);
936 }
937 }
938
939 //======================================================================
940 // Special notification for PSKMAIL: signal to noise measured by decoder
941 // format "<DC2><s2n: CC, A.a, D.d>"
942 // where CC = count, A.a = average s/n, D.d = Std dev of s/n
943 //======================================================================
pskmail_notify_s2n(double s2n_ncount,double s2n_avg,double s2n_stddev)944 void pskmail_notify_s2n(double s2n_ncount, double s2n_avg, double s2n_stddev)
945 {
946 static char buf[64];
947 memset(buf, 0, sizeof(buf));
948 int n = snprintf(buf, sizeof(buf),
949 "\x12<s2n: %1.0f, %1.1f, %1.1f>\n",
950 s2n_ncount, s2n_avg, s2n_stddev);
951 if (n > 0 && n < (int)sizeof(buf)) {
952 WriteARQ((const char *)buf);
953 REQ(&FTextBase::addstr, ReceiveText, buf, FTextBase::CTRL);
954 LOG_INFO("%s", buf);
955 }
956 }
957
958