1 // ----------------------------------------------------------------------------
2 // Copyright (C) 2014
3 //              David Freese, W1HKJ
4 //
5 // This file is part of flrig.
6 //
7 // flrig 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 // flrig 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 <math.h>
22 #include <string>
23 
24 #include <fstream>
25 
26 #include <FL/Fl.H>
27 
28 #include "rig.h"
29 #include "support.h"
30 #include "util.h"
31 #include "debug.h"
32 #include "status.h"
33 #include "rigbase.h"
34 #include "rig_io.h"
35 #include "icons.h"
36 #include "tod_clock.h"
37 
38 #include "socket_io.h"
39 
40 using namespace std;
41 
42 extern bool test;
43 
44 const char *nuline = "\n";
45 
46 static int iBaudRates[] = { 300, 600, 1200, 2400, 4800, 9600,
47 	19200, 38400, 57600, 115200, 230400, 460800 };
48 const char *szBaudRates[] = { "300", "600", "1200", "2400", "4800", "9600",
49 	"19200", "38400", "57600", "115200", "230400", "460800", NULL };
50 
BaudRate(int n)51 int BaudRate(int n)
52 {
53 	if (n > (int)sizeof(iBaudRates)) return 1200;
54 	return (iBaudRates[n]);
55 }
56 
57 
startXcvrSerial()58 bool startXcvrSerial()
59 {
60 	debug::level_e level = debug::level;
61 	debug::level = debug::DEBUG_LEVEL;
62 
63 	bypass_serial_thread_loop = true;
64 // setup commands for serial port
65 	if (progStatus.xcvr_serial_port == "NONE") {
66 		bypass_serial_thread_loop = false;
67 		return false;
68 	}
69 
70 	RigSerial->Device(progStatus.xcvr_serial_port);
71 	RigSerial->Baud(BaudRate(progStatus.comm_baudrate));
72 	RigSerial->Stopbits(progStatus.stopbits);
73 	RigSerial->Retries(progStatus.comm_retries);
74 	RigSerial->Timeout(progStatus.comm_timeout);
75 	RigSerial->RTSptt(progStatus.comm_rtsptt);
76 	RigSerial->DTRptt(progStatus.comm_dtrptt);
77 	RigSerial->RTSCTS(progStatus.comm_rtscts);
78 	RigSerial->RTS(progStatus.comm_rtsplus);
79 	RigSerial->DTR(progStatus.comm_dtrplus);
80 
81 	if (!RigSerial->OpenPort()) {
82 		LOG_ERROR("Cannot access %s", progStatus.xcvr_serial_port.c_str());
83 		return false;
84 	} else {
85 		LOG_DEBUG("\n\
86 Serial port:\n\
87     Port     : %s\n\
88     Baud     : %d\n\
89     Stopbits : %d\n\
90     Retries  : %d\n\
91     Timeout  : %d\n\
92     Loop     : %d\n\
93     RTSCTS   : %d\n\
94     CATptt   : %d\n\
95     RTSptt   : %d\n\
96     DTRptt   : %d\n\
97     RTS+     : %d\n\
98     DTR+     : %d\n",
99 			progStatus.xcvr_serial_port.c_str(),
100 			progStatus.comm_baudrate,
101 			progStatus.stopbits,
102 			progStatus.comm_retries,
103 			progStatus.comm_timeout,
104 			progStatus.serloop_timing,
105 			progStatus.comm_rtscts,
106 			progStatus.comm_catptt,
107 			progStatus.comm_rtsptt,
108 			progStatus.comm_dtrptt,
109 			progStatus.comm_rtsplus,
110 			progStatus.comm_dtrplus );
111 	}
112 
113 	RigSerial->FlushBuffer();
114 
115 	debug::level = level;
116 
117 	return true;
118 }
119 
startAuxSerial()120 bool startAuxSerial()
121 {
122 	if (progStatus.aux_serial_port == "NONE") return false;
123 
124 	AuxSerial->Device(progStatus.aux_serial_port);
125 	AuxSerial->Baud(BaudRate(progStatus.comm_baudrate));
126 	AuxSerial->Stopbits(progStatus.stopbits);
127 	AuxSerial->Retries(progStatus.comm_retries);
128 	AuxSerial->Timeout(progStatus.comm_timeout);
129 
130 	if (!AuxSerial->OpenPort()) {
131 		LOG_ERROR("Cannot access %s", progStatus.aux_serial_port.c_str());
132 		return false;
133 	}
134 	return true;
135 }
136 
startSepSerial()137 bool startSepSerial()
138 {
139 	if (progStatus.sep_serial_port == "NONE") return false;
140 
141 	SepSerial->Device(progStatus.sep_serial_port);
142 	SepSerial->Baud(BaudRate(progStatus.comm_baudrate));
143 
144 	SepSerial->RTSCTS(false);
145 	SepSerial->RTS(progStatus.sep_rtsplus);
146 	SepSerial->RTSptt(progStatus.sep_rtsptt);
147 
148 	SepSerial->DTR(progStatus.sep_dtrplus);
149 	SepSerial->DTRptt(progStatus.sep_dtrptt);
150 
151 	if (!SepSerial->OpenPort()) {
152 		LOG_ERROR("Cannot access %s", progStatus.sep_serial_port.c_str());
153 		return false;
154 	}
155 	return true;
156 }
157 
158 // TODO: Review for thread safety.
159 //  Tried adding mutex, but deadlocks startup
160 // progress dialog:
161 // guard_lock reply_lock(&mutex_replystr);
162 //
assignReplyStr(string val)163 void assignReplyStr(string val)
164 {
165 	selrig->replystr = val;
166 }
167 
168 char replybuff[RXBUFFSIZE+1];
169 string respstr;
170 
readResponse()171 int readResponse()
172 {
173 	int numread = 0;
174 	respstr.clear();
175 	memset(replybuff, 0, RXBUFFSIZE + 1);
176 	if (progStatus.use_tcpip)
177 		numread = read_from_remote(respstr);
178 	else {
179 		numread = RigSerial->ReadBuffer(replybuff, RXBUFFSIZE);
180 		for (int i = 0; i < numread; respstr += replybuff[i++]);
181 	}
182 	if (numread)
183 		LOG_DEBUG("rsp:%3d, %s", numread, str2hex(respstr.c_str(), respstr.length()));
184 	return numread;
185 }
186 
sendCommand(string s,int nread,int wait)187 int sendCommand (string s, int nread, int wait)
188 {
189 	int numwrite = (int)s.size();
190 
191 	// TODO: Review for thread safety
192 	//
193 	// Clear command before sending, to keep the logs sensical.  Otherwise it looks like
194 	// reply was from this command, when it really was from a previous command.
195 	assignReplyStr("");
196 
197 	if (progStatus.use_tcpip) {
198 		readResponse();
199 		send_to_remote(s, progStatus.byte_interval);
200 		int timeout =
201 			progStatus.comm_wait + progStatus.tcpip_ping_delay +
202 			(int)((nread + progStatus.comm_echo ? numwrite : 0)*11000.0/RigSerial->Baud() );
203 		while (timeout > 0) {
204 			if (timeout > 10) MilliSleep(10);
205 			else MilliSleep(timeout);
206 			timeout -= 10;
207 			Fl::awake();
208 		}
209 		if (nread == 0) return 0;
210 		return readResponse();
211 	}
212 
213 	LOG_DEBUG("cmd:%3d, %s", (int)s.length(), str2hex(s.data(), s.length()));
214 
215 	if (RigSerial->IsOpen() == false) {
216 		return 0;
217 	}
218 
219 	RigSerial->FlushBuffer();
220 	RigSerial->WriteBuffer(s.c_str(), numwrite);
221 
222 	int timeout = progStatus.comm_wait +
223 		(int)((nread + progStatus.comm_echo ? numwrite : 0)*11000.0/RigSerial->Baud());
224 	timeout += wait;
225 	while (timeout > 0) {
226 		if (timeout > 10) MilliSleep(10);
227 		else MilliSleep(timeout);
228 		timeout -= 10;
229 		Fl::awake();
230 	}
231 	if (nread == 0) return 0;
232 	return readResponse();
233 }
234 
235 static int waitcount = 0;
236 static bool timeout_alert = false;
237 static char sztimeout_alert[200];
238 
show_timeout(void *)239 static void show_timeout(void *)
240 {
241 	fl_alert2("%s",sztimeout_alert);
242 }
243 
waitCommand(string command,int nread,string info,int msec,char term,int how,int level)244 bool waitCommand(
245 				string command,
246 				int nread,
247 				string info,
248 				int msec,
249 				char term,
250 				int how,
251 				int level )
252 {
253 	guard_lock reply_lock(&mutex_replystr);
254 
255 	int numwrite = (int)command.length();
256 	if (nread == 0)
257 		LOG_DEBUG("cmd:%3d, %s", numwrite, how == ASC ? command.c_str() : str2hex(command.data(), numwrite));
258 
259 	if (progStatus.use_tcpip) {
260 		send_to_remote(command, progStatus.byte_interval);
261 		if (nread == 0) return 0;
262 	} else {
263 		if (RigSerial->IsOpen() == false) {
264 			LOG_DEBUG("cmd: %s", how == ASC ? command.c_str() : str2hex(command.data(), command.length()));
265 			waitcount = 0;
266 			return 0;
267 		}
268 
269 		RigSerial->FlushBuffer();
270 //		replystr.clear();
271 
272 		RigSerial->WriteBuffer(command.c_str(), numwrite);
273 		if (nread == 0) {
274 			waitcount = 0;
275 			return 0;
276 		}
277 	}
278 
279 	int tod_start = zmsec();
280 
281 // minimimum time to wait for a response
282 	int timeout = (int)((nread + progStatus.comm_echo ? numwrite : 0)*11000.0/RigSerial->Baud()
283 		+ progStatus.use_tcpip ? progStatus.tcpip_ping_delay : 0);
284 	while (timeout > 0) {
285 		if (timeout > 10) MilliSleep(10);
286 		else MilliSleep(timeout);
287 		timeout -= 10;
288 		Fl::awake();
289 	}
290 // additional wait for xcvr processing
291 	string returned = "";
292 	static char sztemp[100];
293 	int waited = 0;
294 	while (waited < msec) {
295 		if (readResponse())
296 			returned.append(respstr);
297 		if (	((int)returned.length() >= nread) ||
298 				(returned.find(term) != string::npos) ) {
299 			assignReplyStr(returned);
300 			waited = zmsec() - tod_start;
301 			snprintf(sztemp, sizeof(sztemp), "%s rcvd in %d msec", info.c_str(), waited);
302 			showresp(level, how, sztemp, command, returned);
303 			waitcount = 0;
304 			return true;
305 		}
306 		waited += 10;
307 		MilliSleep(10);
308 		Fl::awake();
309 	}
310 	waitcount++;
311 	assignReplyStr(returned);
312 	waited = zmsec() - tod_start;
313 	snprintf(sztemp, sizeof(sztemp), "%s TIMED OUT in %d ms", command.c_str(), waited);
314 	showresp(ERR, HEX, sztemp, command, returned);
315 	if (waitcount > 4 && !timeout_alert) {
316 		timeout_alert = true;
317 		snprintf(sztimeout_alert, sizeof(sztimeout_alert),
318 			"Serial i/o failure\n%s TIME OUT in %d ms",
319 			command.c_str(), waited);
320 			Fl::awake(show_timeout);
321 	}
322 	return false;
323 }
324 
waitResponse(int timeout)325 int waitResponse(int timeout)
326 {
327 	int n = 0;
328 	if (!progStatus.use_tcpip && RigSerial->IsOpen() == false)
329 		return 0;
330 
331 	MilliSleep(10);
332 	if (!(n = readResponse()))
333 		while (timeout > 0) {
334 			if (timeout > 10) MilliSleep(10);
335 			else MilliSleep(timeout);
336 			timeout -= 10;
337 			Fl::awake();
338 		}
339 	return n;
340 }
341 
showresp(int level,int how,string s,string tx,string rx)342 void showresp(int level, int how, string s, string tx, string rx)
343 {
344 	time_t now;
345 	time(&now);
346 	struct tm *local = localtime(&now);
347 	char sztm[20];
348 	strftime(sztm, sizeof(sztm), "%H:%M:%S", local);
349 
350 	string s1 = how == HEX ? str2hex(tx.c_str(), tx.length()) : tx;
351 	string s2 = how == HEX ? str2hex(rx.c_str(), rx.length()) : rx;
352 	if (how == ASC) {
353 		size_t p;
354 		while((p = s1.find('\r')) != string::npos)
355 			s1.replace(p, 1, "<cr>");
356 		while((p = s1.find('\n')) != string::npos)
357 			s1.replace(p, 1, "<lf>");
358 		while((p = s2.find('\r')) != string::npos)
359 			s2.replace(p, 1, "<cr>");
360 		while((p = s2.find('\n')) != string::npos)
361 			s2.replace(p, 1, "<lf>");
362 	}
363 
364 	switch (level) {
365 	case QUIET:
366 		SLOG_QUIET("%s: %10s\ncmd %s\nans %s", sztm, s.c_str(), s1.c_str(), s2.c_str());
367 		break;
368 	case ERR:
369 		SLOG_ERROR("%s: %10s\ncmd %s\nans %s", sztm, s.c_str(), s1.c_str(), s2.c_str());
370 		break;
371 	case INFO:
372 		SLOG_INFO("%s: %10s\ncmd %s\nans %s", sztm, s.c_str(), s1.c_str(), s2.c_str());
373 		break;
374 	case DEBUG:
375 		SLOG_DEBUG("%s: %10s\ncmd %s\nans %s", sztm, s.c_str(), s1.c_str(), s2.c_str());
376 		break;
377 	case WARN:
378 	default:
379 		SLOG_WARN("%s: %10s\ncmd %s\nans %s", sztm, s.c_str(), s1.c_str(), s2.c_str());
380 		break;
381 	}
382 }
383