1 // =====================================================================
2 //
3 // FD_logger.cxx
4 //
5 // interface to tcpip application fdserver.tcl
6 //   fdserver is a multiple client tcpip server
7 //
8 // Copyright (C) 2016
9 //		Dave Freese, W1HKJ
10 //
11 // This file is part of fldigi.
12 //
13 // Fldigi is free software: you can redistribute it and/or modify
14 // it under the terms of the GNU General Public License as published by
15 // the Free Software Foundation, either version 3 of the License, or
16 // (at your option) any later version.
17 //
18 // Fldigi is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 // GNU General Public License for more details.
22 //
23 // You should have received a copy of the GNU General Public License
24 // along with fldigi.  If not, see <http://www.gnu.org/licenses/>.
25 // =====================================================================
26 
27 #include <iostream>
28 #include <cmath>
29 #include <cstring>
30 #include <vector>
31 #include <list>
32 #include <stdlib.h>
33 
34 #include <FL/Fl_Text_Display.H>
35 #include <FL/Fl_Text_Buffer.H>
36 
37 #include "fl_digi.h"
38 #include "rigsupport.h"
39 #include "modem.h"
40 #include "trx.h"
41 #include "configuration.h"
42 #include "main.h"
43 #include "waterfall.h"
44 #include "macros.h"
45 #include "qrunner.h"
46 #include "debug.h"
47 #include "status.h"
48 #include "icons.h"
49 
50 #include "logsupport.h"
51 #include "fd_logger.h"
52 #include "fd_view.h"
53 #include "flmisc.h"
54 
55 #include "confdialog.h"
56 
57 LOG_FILE_SOURCE(debug::LOG_FD);
58 
59 using namespace std;
60 
61 //forward declarations of local functions
62 void FD_start();
63 
64 //======================================================================
65 // Socket FD i/o used on all platforms
66 //======================================================================
67 
68 pthread_t FD_thread;
69 pthread_t FD_rx_socket_thread;
70 pthread_mutex_t FD_mutex     = PTHREAD_MUTEX_INITIALIZER;
71 
72 Socket *FD_socket = 0;
73 
74 bool FD_connected = false;
75 bool FD_logged_on = false;
76 bool FD_enabled = false;
77 bool FD_exit = false;
78 
79 string FD_ip_addr = "";
80 string FD_ip_port = "";
81 
82 string FD_rxbuffer;
83 
84 //======================================================================
85 // data report from fdserver.tcl
86 // LOGON w1hkj 40,DIG
87 // LOGON_OK W1HKJ 1A 5
88 // SCORE 35
89 // WORKED ... all on a single line
90 // {160 CW 1} {160 DIG 0} {160 PHONE 0}
91 // {80 CW 0} {80 DIG 0} {80 PHONE 0}
92 // {40 CW 1} {40 DIG 0} {40 PHONE 0}
93 // {20 CW 0} {20 DIG 1} {20 PHONE 0}
94 // {17 CW 0} {17 DIG 0} {17 PHONE 1}
95 // {15 CW 0} {15 DIG 0} {15 PHONE 1}
96 // {10 CW 0} {10 DIG 0} {10 PHONE 0}
97 // {2 CW 0} {2 DIG 0} {2 PHONE 0}
98 // {440 CW 0} {440 DIG 0} {440 PHONE 0}
99 //======================================================================
100 
101 //======================================================================
102 //
103 //======================================================================
post(Fl_Input2 * w,const char * s)104 void post(Fl_Input2 *w, const char * s)
105 {
106 	w->value(s);
107 }
108 
109 //======================================================================
110 //
111 //======================================================================
view(Fl_Output * w,const char * s)112 void view(Fl_Output *w, const char * s)
113 {
114 	w->value(s);
115 }
116 
117 //======================================================================
118 //
119 //======================================================================
120 static string toparse;
121 
parse_logon_ok(string s)122 void parse_logon_ok(string s)
123 {
124 	size_t p = 0;
125 	static string call, clss, mult, sect;
126 	call.clear(); clss.clear(); sect.clear();
127 	s.erase(0, 9);
128 	call = s;
129 	call.erase(call.find(" "));
130 	p = s.find(" ");
131 	if (p != string::npos) {
132 		s.erase(0, p+1);
133 		clss = s;
134 		p = clss.find(" ");
135 		clss.erase(p);
136 		p = s.find(" ");
137 		if (p != string::npos) {
138 			s.erase(0,p+1);
139 			mult = s;
140 			p = mult.find(" ");
141 			mult.erase(p);
142 			p = s.find(" ");
143 			if (p != string::npos) {
144 				s.erase(0,p+1);
145 				sect = s;
146 				p = sect.find("\r");
147 				if (p != string::npos) sect.erase(p);
148 				p = sect.find("\n");
149 				if (p != string::npos) sect.erase(p);
150 			}
151 		}
152 	}
153 	progdefaults.my_FD_call = call;
154 	REQ(&post, inp_my_FD_call, call.c_str());
155 	REQ(&view, view_FD_call, call.c_str());
156 	progdefaults.my_FD_class = clss;
157 	REQ(&post, inp_my_FD_class, clss.c_str());
158 	REQ(&view, view_FD_class, clss.c_str());
159 	progdefaults.my_FD_section = sect;
160 	REQ(&post, inp_my_FD_section, sect.c_str());
161 	REQ(&view, view_FD_section, sect.c_str());
162 	progdefaults.my_FD_mult = mult;
163 	REQ(&view, view_FD_mult, mult.c_str());
164 }
165 
166 //======================================================================
167 //
168 //======================================================================
parse_score(string s)169 void parse_score(string s)
170 {
171 	static string sscore;
172 	size_t p = s.find("\r");
173 	if (p != string::npos) s.erase(p);
174 	p = s.find("\n");
175 	if (p != string::npos) s.erase(p);
176 	p = s.find(" ");
177 	if (p != string::npos) s.erase(0, p+1);
178 	sscore = s;
179 	REQ(&view, view_FD_score, sscore.c_str());
180 }
181 
182 //======================================================================
183 //
184 //======================================================================
parse_entry(string needle,Fl_Output * view1,Fl_Output * view2)185 void parse_entry( string needle, Fl_Output *view1, Fl_Output *view2 )
186 {
187 	size_t p1 = toparse.find(needle);
188 	if (p1 == string::npos) return;
189 	p1 += needle.length();
190 	size_t p2 = toparse.find(" ", p1);
191 	size_t p3 = toparse.find("}", p1);
192 	if (p3 == string::npos) return;
193 	string num = "", op = "";
194 	num = toparse.substr(p1, p2 - p1);
195 	op = toparse.substr(p2+1, p3 - (p2+1));
196 
197 	view1->value(num.c_str());
198 	view2->value(op.c_str());
199 }
200 
201 //======================================================================
202 //
203 //======================================================================
parse_worked()204 void parse_worked()
205 {
206 // CW contacts
207 	parse_entry("{160 CW ", view_FD_CW[0],  view_FD_CW_OP[0]);
208 	parse_entry("{80 CW ",  view_FD_CW[1],  view_FD_CW_OP[1]);
209 	parse_entry("{40 CW ",  view_FD_CW[2],  view_FD_CW_OP[2]);
210 	parse_entry("{20 CW ",  view_FD_CW[3],  view_FD_CW_OP[3]);
211 	parse_entry("{17 CW ",  view_FD_CW[4],  view_FD_CW_OP[4]);
212 	parse_entry("{15 CW ",  view_FD_CW[5],  view_FD_CW_OP[5]);
213 	parse_entry("{12 CW ",  view_FD_CW[6],  view_FD_CW_OP[6]);
214 	parse_entry("{10 CW ",  view_FD_CW[7],  view_FD_CW_OP[7]);
215 	parse_entry("{6 CW ",   view_FD_CW[8],  view_FD_CW_OP[8]);
216 	parse_entry("{2 CW ",   view_FD_CW[9],  view_FD_CW_OP[9]);
217 	parse_entry("{220 CW ", view_FD_CW[10], view_FD_CW_OP[10]);
218 	parse_entry("{440 CW ", view_FD_CW[11], view_FD_CW_OP[11]);
219 
220 // DIG contacts
221 	parse_entry("{160 DIG ", view_FD_DIG[0],  view_FD_DIG_OP[0]);
222 	parse_entry("{80 DIG ",  view_FD_DIG[1],  view_FD_DIG_OP[1]);
223 	parse_entry("{40 DIG ",  view_FD_DIG[2],  view_FD_DIG_OP[2]);
224 	parse_entry("{20 DIG ",  view_FD_DIG[3],  view_FD_DIG_OP[3]);
225 	parse_entry("{17 DIG ",  view_FD_DIG[4],  view_FD_DIG_OP[4]);
226 	parse_entry("{15 DIG ",  view_FD_DIG[5],  view_FD_DIG_OP[5]);
227 	parse_entry("{12 DIG ",  view_FD_DIG[6],  view_FD_DIG_OP[6]);
228 	parse_entry("{10 DIG ",  view_FD_DIG[7],  view_FD_DIG_OP[7]);
229 	parse_entry("{6 DIG ",   view_FD_DIG[8],  view_FD_DIG_OP[8]);
230 	parse_entry("{2 DIG ",   view_FD_DIG[9],  view_FD_DIG_OP[9]);
231 	parse_entry("{220 DIG ", view_FD_DIG[10], view_FD_DIG_OP[10]);
232 	parse_entry("{440 DIG ", view_FD_DIG[11], view_FD_DIG_OP[11]);
233 
234 // PHONE contacts
235 	parse_entry("{160 PHONE ", view_FD_PHONE[0],  view_FD_PHONE_OP[0]);
236 	parse_entry("{80 PHONE ",  view_FD_PHONE[1],  view_FD_PHONE_OP[1]);
237 	parse_entry("{40 PHONE ",  view_FD_PHONE[2],  view_FD_PHONE_OP[2]);
238 	parse_entry("{20 PHONE ",  view_FD_PHONE[3],  view_FD_PHONE_OP[3]);
239 	parse_entry("{17 PHONE ",  view_FD_PHONE[4],  view_FD_PHONE_OP[4]);
240 	parse_entry("{15 PHONE ",  view_FD_PHONE[5],  view_FD_PHONE_OP[5]);
241 	parse_entry("{12 PHONE ",  view_FD_PHONE[6],  view_FD_PHONE_OP[6]);
242 	parse_entry("{10 PHONE ",  view_FD_PHONE[7],  view_FD_PHONE_OP[7]);
243 	parse_entry("{6 PHONE ",   view_FD_PHONE[8],  view_FD_PHONE_OP[8]);
244 	parse_entry("{2 PHONE ",   view_FD_PHONE[9],  view_FD_PHONE_OP[9]);
245 	parse_entry("{220 PHONE ", view_FD_PHONE[10], view_FD_PHONE_OP[10]);
246 	parse_entry("{440 PHONE ", view_FD_PHONE[11], view_FD_PHONE_OP[11]);
247 
248 }
249 
clear_fd_viewer()250 void clear_fd_viewer() {
251 	for (int i = 0; i < 12; i++) {
252 		view_FD_CW[i]->value("");
253 		view_FD_CW_OP[i]->value("");
254 		view_FD_DIG[i]->value("");
255 		view_FD_DIG_OP[i]->value("");
256 		view_FD_PHONE[i]->value("");
257 		view_FD_PHONE_OP[i]->value("");
258 	}
259 	view_FD_call->value("");
260 	view_FD_class->value("");
261 	view_FD_section->value("");
262 	view_FD_mult->value("");
263 	box_fdserver_connected->color((Fl_Color)31);
264 	box_fdserver_connected->redraw();
265 }
266 
267 //======================================================================
268 //
269 //======================================================================
parse_FD_stream(string data)270 void parse_FD_stream(string data)
271 {
272 	size_t p = 0;
273 	if (data.empty()) return;
274 
275 //std::cout << "RX Stream:\n" << data << std::endl;
276 
277 	if (data.find("QUIT") != string::npos) {
278 //std::cout << "Quit\n";
279 		FD_disconnect();
280 		btn_fd_connect->value(0);
281 		return;
282 	}
283 	if (data.find("LOGON_DENIED") != string::npos) {
284 //std::cout << "Logon denied\n";
285 		FD_logged_on = false;
286 		LOG_ERROR("FD logon DENIED");
287 		btn_fd_connect->value(0);
288 		return;
289 	}
290 	if (data.find("LOGOFF_OK") != string::npos) {
291 //std::cout << "Log off OK\n";
292 		FD_logged_on = false;
293 		return;
294 	}
295 	if (data.find("LOGON_OK") != string::npos) {
296 //std::cout << "Logon OK\n";
297 		parse_logon_ok(data.substr(p));
298 		FD_logged_on = true;
299 		box_fdserver_connected->color((Fl_Color)2);
300 		box_fdserver_connected->redraw();
301 	}
302 	if ( (p = data.find("SCORE") ) != string::npos) {
303 //std::cout << "SCORE\n";
304 		parse_score(data.substr(p));
305 	}
306 	if ( (p = data.find("WORKED"))!= string::npos) {
307 //std::cout << "WORKED\n";
308 		toparse = data.substr(p);
309 		REQ(parse_worked);
310 		return;
311 	}
312 	if ( data.find("NODUP") != string::npos) {
313 //std::cout << "Not a duplicate\n";
314 		return;
315 	}
316 	else if (data.find("DUP") != string::npos) {
317 //std::cout << "Duplicate\n";
318 	}
319 	else if (data.find("REJECT") != string::npos) {
320 //std::cout << "Reject\n";
321 	}
322 	else if (data.find("ACCEPT") != string::npos) {
323 //std::cout << "Accept\n";
324 	}
325 }
326 
327 //======================================================================
328 //
329 //======================================================================
FD_write(string s)330 void FD_write(string s)
331 {
332 	FD_socket->send(s.append("\n"));
333 }
334 
335 //======================================================================
336 //
337 //======================================================================
FD_get_record(string call)338 void FD_get_record(string call)
339 {
340 	if(!FD_socket) return;
341 	if (!FD_connected) return;
342 }
343 
344 //======================================================================
345 //
346 //======================================================================
FD_add_record()347 void FD_add_record()
348 {
349 	if(!FD_socket) return;
350 	if (!FD_connected) return;
351 	guard_lock send_lock(&FD_mutex);
352 	string cmd = "ADD ";
353 	cmd.append(inpCall->value()).append(" ");
354 	cmd.append(inpSection->value()).append(" ");
355 	cmd.append(inpClass->value());
356 	FD_write(cmd);
357 }
358 
359 //======================================================================
360 //
361 //======================================================================
362 static string fd_band;
363 static string fd_mode;
364 
FD_opmode()365 static string FD_opmode()
366 {
367 	if (!active_modem) {
368 		return "DIG";
369 	}
370 	if (active_modem->get_mode() == MODE_CW)
371 		return "CW";
372 	else if (active_modem->get_mode() < MODE_SSB)
373 		return "DIG";
374 	else if (active_modem->get_mode() == MODE_SSB)
375 		return "PHONE";
376 	return "";
377 }
378 
379 //======================================================================
380 //
381 //======================================================================
FD_opband()382 static string FD_opband()
383 {
384 	if (!active_modem) return "40";
385 
386 	float freq = qsoFreqDisp->value();
387 	freq /= 1e6;
388 
389 	if (freq >= 1.8 && freq < 3.5) return "160";
390 	if (freq >= 3.5 && freq <= 7.0) return "80";
391 	if (freq >= 7.0 && freq <= 7.5) return "40";
392 	if (freq >= 14.0 && freq < 18.0) return "20";
393 	if (freq >= 18.0 && freq < 21.0) return "17";
394 	if (freq >= 21.0 && freq < 24.0) return "15";
395 	if (freq >= 24.0 && freq < 28.0) return "12";
396 	if (freq >= 28.0 && freq < 50.0) return "10";
397 	if (freq >= 50.0 && freq < 70.0) return "6";
398 	if (freq >= 144.0 && freq < 222.0) return "2";
399 	if (freq >= 222.0 && freq < 420.0) return "222";
400 	if (freq >= 420.0 && freq < 444.0) return "440";
401 	return "";
402 }
403 
404 //======================================================================
405 //
406 //======================================================================
FD_dupcheck()407 int FD_dupcheck()
408 {
409 	if(!FD_socket) return 0;
410 	if (!FD_connected) return 0;
411 
412 	string response;
413 	string cmd = "DUPCHECK ";
414 	cmd.append(inpCall->value());
415 	try {
416 		guard_lock send_lock(&FD_mutex);
417 		FD_write(cmd);
418 		MilliSleep(50);
419 		FD_socket->recv(response);
420 		if (response.empty())
421 			return 0;
422 		if (response.find("NODUP") != string::npos)
423 			return 0;
424 		if (response.find("DUP") != string::npos)
425 			return 1;
426 		return 0;
427 	} catch (const SocketException& e) {
428 		LOG_ERROR("Error %d, %s", e.error(), e.what());
429 		FD_exit = true;
430 	}
431 	return 0;
432 }
433 
434 //======================================================================
435 //
436 //======================================================================
FD_logon()437 void FD_logon()
438 {
439 	string ucasecall = progdefaults.fd_op_call;
440 	string buffer;
441 	string cmd = "LOGON ";
442 
443 	if (ucasecall.empty()) return;
444 	for (size_t n = 0; n < ucasecall.length(); n++)
445 		ucasecall[n] = toupper(ucasecall[n]);
446 
447 	fd_band = FD_opband();
448 	fd_mode = FD_opmode();
449 	if (fd_band.empty() || fd_mode.empty()) return;
450 
451 	cmd.append(ucasecall).append(" ");
452 	cmd.append(fd_band).append(",");
453 	cmd.append(fd_mode);
454 
455 	try {
456 		guard_lock send_lock(&FD_mutex);
457 //std::cout << "Log On: " << cmd << std::endl;
458 		FD_write(cmd);
459 		FD_socket->recv(buffer);
460 
461 		parse_FD_stream(buffer);
462 
463 		LOG_INFO("Logged on to fdserver");
464 		FD_logged_on = true;
465 
466 	} catch (const SocketException& e) {
467 		LOG_ERROR("Error %d, %s", e.error(), e.what());
468 	}
469 }
470 
471 //======================================================================
472 //
473 //======================================================================
FD_logoff()474 void FD_logoff()
475 {
476 	if (!FD_socket) return;
477 
478 	guard_lock send_lock(&FD_mutex);
479 	try {
480 		string buffer;
481 		FD_write("LOGOFF");
482 		MilliSleep(100);
483 		FD_socket->recv(buffer);
484 		parse_FD_stream(buffer);
485 
486 		FD_disconnect();
487 		LOG_INFO("Logged off FD server");
488 	} catch (const SocketException& e) {
489 		LOG_ERROR("Error %d, %s", e.error(), e.what());
490 	}
491 }
492 
493 //======================================================================
494 //
495 //======================================================================
FD_band_check()496 void FD_band_check() {
497 	if (fd_band != FD_opband()) {
498 		FD_logoff();
499 		MilliSleep(50);
500 		FD_start();
501 		FD_logon();
502 	}
503 }
504 
505 //======================================================================
506 //
507 //======================================================================
FD_mode_check()508 void FD_mode_check() {
509 	if (fd_mode != FD_opmode()) {
510 		FD_logoff();
511 		MilliSleep(50);
512 		FD_start();
513 		FD_logon();
514 	}
515 }
516 
517 //======================================================================
518 //
519 //======================================================================
FD_rcv_data()520 void FD_rcv_data()
521 {
522 	string tempbuff;
523 	try {
524 		guard_lock send_lock(&FD_mutex);
525 		FD_socket->recv(tempbuff);
526 		if (tempbuff.empty())
527 			return;
528 		parse_FD_stream(tempbuff);
529 	} catch (const SocketException& e) {
530 		LOG_ERROR("Error %d, %s", e.error(), e.what());
531 		FD_exit = true;
532 	}
533 }
534 
535 //======================================================================
536 //
537 //======================================================================
538 static int fd_looptime = 1000; // in milliseconds
FD_start()539 void FD_start()
540 {
541 	guard_lock send_lock(&FD_mutex);
542 
543 	FD_ip_addr =  progdefaults.fd_tcpip_addr;
544 	FD_ip_port = progdefaults.fd_tcpip_port;
545 
546 	try {
547 		FD_socket = new Socket(
548 				Address( FD_ip_addr.c_str(),
549 						 FD_ip_port.c_str(),
550 						 "tcp") );
551 		FD_socket->set_timeout(0.01);
552 		FD_socket->connect();
553 		FD_socket->set_nonblocking(true);
554 
555 		LOG_INFO( "Connected to fdserver %s:%s",
556 			FD_ip_addr.c_str(), FD_ip_port.c_str() );
557 
558 		fd_looptime = 100;
559 		FD_connected = true;
560 	}
561 	catch (const SocketException& e) {
562 //		LOG_ERROR("%s", e.what() );
563 		delete FD_socket;
564 		FD_socket = 0;
565 		FD_connected = false;
566 	}
567 }
568 
569 //======================================================================
570 //
571 //======================================================================
FD_restart()572 void FD_restart()
573 {
574 	guard_lock send_lock(&FD_mutex);
575 
576 	FD_ip_addr =  progdefaults.fd_tcpip_addr;
577 	FD_ip_port = progdefaults.fd_tcpip_port;
578 
579 	try {
580 		FD_socket->shut_down();
581 		FD_socket->close();
582 		delete FD_socket;
583 		FD_connected = false;
584 		FD_socket = new Socket(
585 				Address( FD_ip_addr.c_str(),
586 						 FD_ip_port.c_str(),
587 						 "tcp") );
588 		FD_socket->set_timeout(0.01);
589 		FD_socket->connect();
590 		FD_socket->set_nonblocking(true);
591 		fd_looptime = 100;
592 
593 		LOG_INFO( "Connected to fdserver %s:%s",
594 			FD_ip_addr.c_str(), FD_ip_port.c_str() );
595 	}
596 	catch (const SocketException& e) {
597 //		LOG_ERROR("%s", e.what() );
598 		delete FD_socket;
599 		FD_socket = 0;
600 		FD_connected = false;
601 	}
602 }
603 
604 //======================================================================
605 // Disconnect from FD tcpip server
606 //======================================================================
FD_disconnect()607 void FD_disconnect()
608 {
609 	if (!FD_socket) return;
610 
611 // send disconnect string
612 	FD_socket->shut_down();
613 	FD_socket->close();
614 	delete FD_socket;
615 	FD_socket = 0;
616 	FD_connected = false;
617 	FD_logged_on = false;
618 	REQ(clear_fd_viewer);
619 	LOG_INFO("Disconnected from fdserver");
620 }
621 
622 ////////////////////////////////////////////////
623 // NEED TO DETECT WHEN SERVER IS CLOSED OR FAILS
624 ////////////////////////////////////////////////
625 
626 //======================================================================
627 // Thread loop
628 //======================================================================
629 
630 static notify_dialog *fd_alert_window = 0;
631 
ui_feedback()632 void ui_feedback()
633 {
634 	btn_fd_connect->value(0);
635 
636 	if (!fd_alert_window) fd_alert_window = new notify_dialog;
637 
638 	fd_alert_window->notify(_("Check FD server, connect failed"), 15.0);
639 	show_notifier(fd_alert_window);
640 }
641 
FD_loop(void * args)642 void *FD_loop(void *args)
643 {
644 	SET_THREAD_ID(FD_TID);
645 
646 	int try_count = 10;
647 	while(1) {
648 		for (int i = 0; i < fd_looptime/50; i++) {
649 			MilliSleep(50);
650 			if (FD_exit) break;
651 		}
652 
653 		if (!FD_connected && progdefaults.connect_to_fdserver) {
654 			if (try_count--)
655 				FD_start();
656 			else {
657 				progdefaults.connect_to_fdserver = false;
658 				REQ(ui_feedback);
659 				try_count = 10;
660 			}
661 		}
662 
663 		if ( FD_connected &&
664 			 ((FD_ip_addr != progdefaults.fd_tcpip_addr) ||
665 			  (FD_ip_port != progdefaults.fd_tcpip_port)) )
666 			FD_restart();
667 
668 		if (FD_exit) break;
669 
670 		if (FD_connected && !progdefaults.connect_to_fdserver) {
671 			if (FD_logged_on) FD_logoff();
672 			FD_disconnect();
673 		} else if (	FD_connected &&
674 					!FD_logged_on &&
675 					progdefaults.connect_to_fdserver) {
676 			FD_logon();
677 		} else if (FD_connected && FD_logged_on) {
678 			FD_rcv_data();
679 			FD_mode_check();
680 			FD_band_check();
681 		}
682 	}
683 
684 	if (FD_logged_on && FD_socket)
685 		FD_logoff(); // calls FD_disconnect()
686 	else
687 		FD_disconnect();
688 
689 	// exit the FD thread
690 	SET_THREAD_CANCEL();
691 	return NULL;
692 }
693 
694 //======================================================================
695 //
696 //======================================================================
FD_init(void)697 void FD_init(void)
698 {
699 	FD_enabled = false;
700 	FD_exit = false;
701 
702 	if (pthread_create(&FD_thread, NULL, FD_loop, NULL) < 0) {
703 		LOG_ERROR("%s", "pthread_create failed");
704 		return;
705 	}
706 
707 	LOG_INFO("%s", "fdserver thread started");
708 
709 	FD_enabled = true;
710 }
711 
712 //======================================================================
713 //
714 //======================================================================
FD_close(void)715 void FD_close(void)
716 {
717 	if (!FD_enabled) return;
718 
719 	FD_exit = true;
720 	pthread_join(FD_thread, NULL);
721 	FD_enabled = false;
722 
723 	LOG_INFO("%s", "fdserver thread terminated. ");
724 
725 	if (FD_socket) {
726 		FD_socket->shut_down();
727 		FD_socket->close();
728 	}
729 
730 }
731 
732