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