1 /***************************************************************************
2 *
3 * Project: OpenCPN
4 * Purpose: NMEA Data Object
5 * Author: David Register
6 *
7 ***************************************************************************
8 * Copyright (C) 2010 by David S. Register *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the *
22 * Free Software Foundation, Inc., *
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24 **************************************************************************/
25
26 #include "wx/wxprec.h"
27
28 #ifndef WX_PRECOMP
29 #include "wx/wx.h"
30 #endif //precompiled headers
31
32 #include "dychart.h"
33
34 #include <stdlib.h>
35 #include <math.h>
36 #include <time.h>
37
38 #include "dychart.h"
39
40 #include "wificlient.h"
41 #include "chart1.h"
42 #include "statwin.h"
43
44 static int wifi_s_dns_test_flag;
45
46
47 //------------------------------------------------------------------------------
48 // WIFI Window Implementation
49 //------------------------------------------------------------------------------
BEGIN_EVENT_TABLE(WIFIWindow,wxWindow)50 BEGIN_EVENT_TABLE(WIFIWindow, wxWindow)
51 EVT_PAINT(WIFIWindow::OnPaint)
52 EVT_ACTIVATE(WIFIWindow::OnActivate)
53 EVT_CLOSE(WIFIWindow::OnCloseWindow)
54
55 EVT_SOCKET(WIFI_SOCKET_ID, WIFIWindow::OnSocketEvent)
56 EVT_TIMER(TIMER_WIFI1, WIFIWindow::OnTimer1)
57
58 END_EVENT_TABLE()
59
60 // A constructor
61 WIFIWindow::WIFIWindow(wxFrame *frame, const wxString& WiFiServerName):
62 wxWindow(frame, wxID_ANY, wxPoint(20,20), wxSize(5,5), wxSIMPLE_BORDER)
63
64 {
65 parent_frame = (MyFrame *)frame;
66 m_sock = NULL;
67
68 m_pdata_server_string = new wxString(WiFiServerName);
69
70 m_watchtick = 0;
71 m_timer_active = false;
72
73 // Decide upon Server source
74 wxString msg(_T("WiFi Server is...."));
75 msg.Append(*m_pdata_server_string);
76 wxLogMessage(msg);
77
78 if(m_pdata_server_string->Contains(_T("TCP/IP")))
79 {
80 wxString WIFI_data_ip;
81 WIFI_data_ip = m_pdata_server_string->Mid(7); // extract the IP
82
83 if(!WIFI_data_ip.IsEmpty())
84 {
85 // Create the socket
86 m_sock = new wxSocketClient();
87
88 // Setup the event handler and subscribe to most events
89 m_sock->SetEventHandler(*this, WIFI_SOCKET_ID);
90
91 m_sock->SetNotify(wxSOCKET_CONNECTION_FLAG |
92 wxSOCKET_INPUT_FLAG |
93 wxSOCKET_LOST_FLAG);
94 m_sock->Notify(TRUE);
95
96 m_busy = FALSE;
97
98 // Build the target address
99
100 // n.b. Win98
101 // wxIPV4address::Hostname() uses sockets function gethostbyname() for address resolution
102 // Implications...Either name target must exist in c:\windows\hosts, or
103 // a DNS server must be active on the network.
104 // If neither true, then wxIPV4address::Hostname() will block (forever?)....
105 //
106 // Workaround....
107 // Use a thread to try the name lookup, in case it hangs
108
109 WIFIDNSTestThread *ptest_thread = NULL;
110 ptest_thread = new WIFIDNSTestThread(WIFI_data_ip);
111
112 ptest_thread->Run(); // Run the thread from ::Entry()
113
114
115 // Sleep and loop for N seconds
116 #define SLEEP_TEST_SEC 2
117
118 for(int is=0 ; is<SLEEP_TEST_SEC * 10 ; is++)
119 {
120 wxMilliSleep(100);
121 if(wifi_s_dns_test_flag)
122 break;
123 }
124
125 if(!wifi_s_dns_test_flag)
126 {
127
128 wxString msg(WIFI_data_ip);
129 msg.Prepend(_T("Could not resolve TCP/IP host '"));
130 msg.Append(_T("'\n Suggestion: Try 'xxx.xxx.xxx.xxx' notation"));
131 OCPNMessageDialog md(this, msg, _T("OpenCPN Message"), wxICON_ERROR );
132 md.ShowModal();
133
134 m_sock->Notify(FALSE);
135 m_sock->Destroy();
136
137 return;
138 }
139
140 addr.Hostname(WIFI_data_ip);
141 addr.Service(SERVER_PORT);
142
143 // It is considered safe to block GUI during socket IO, since WIFI data activity is infrequent
144 m_sock->SetFlags(wxSOCKET_WAITALL | wxSOCKET_BLOCK );
145 m_sock->Connect(addr, FALSE); // Non-blocking connect
146
147 // Initialize local data stores
148 for(int ilocal = 0 ; ilocal < NLOCALSTORE ; ilocal++)
149 {
150 station_data[ilocal].bisvalid = false;
151 }
152
153 Timer1.SetOwner(this, TIMER_WIFI1);
154 m_scan_interval_msec = 10000;
155 Timer1.Start(m_scan_interval_msec,wxTIMER_CONTINUOUS);
156 m_timer_active = true;
157 } // !Isempty()
158 }
159
160 Hide();
161 }
162
163
~WIFIWindow()164 WIFIWindow::~WIFIWindow()
165 {
166 delete m_pdata_server_string;
167 }
168
OnCloseWindow(wxCloseEvent & event)169 void WIFIWindow::OnCloseWindow(wxCloseEvent& event)
170 {
171 // Kill off the WIFI Client Socket if alive
172 if(m_sock)
173 {
174 m_sock->Notify(FALSE);
175 m_sock->Destroy();
176 Timer1.Stop();
177 }
178 }
179
180
GetSource(wxString & source)181 void WIFIWindow::GetSource(wxString& source)
182 {
183 source = *m_pdata_server_string;
184 }
185
186
187
OnActivate(wxActivateEvent & event)188 void WIFIWindow::OnActivate(wxActivateEvent& event)
189 {
190 }
191
OnPaint(wxPaintEvent & event)192 void WIFIWindow::OnPaint(wxPaintEvent& event)
193 {
194 wxPaintDC dc(this);
195 }
196
Pause(void)197 void WIFIWindow::Pause(void)
198 {
199 if(m_timer_active)
200 Timer1.Stop();
201
202 if(m_sock)
203 m_sock->Notify(FALSE);
204 }
205
UnPause(void)206 void WIFIWindow::UnPause(void)
207 {
208 if(m_timer_active)
209 Timer1.Start(m_scan_interval_msec,wxTIMER_CONTINUOUS);
210
211 if(m_sock)
212 m_sock->Notify(TRUE);
213 }
214
215 ///////////////////////////////
OnSocketEvent(wxSocketEvent & event)216 void WIFIWindow::OnSocketEvent(wxSocketEvent& event)
217 {
218
219 wifi_scan_data *pt;
220 unsigned char response_type;
221
222 int i, ilocal;
223 unsigned char *pbuffer;
224 int *pcnt;
225 int cnt;
226 unsigned char buf[100];
227 int pt_eaten[64];
228
229 if(event.GetSocketEvent() == wxSOCKET_INPUT)
230 {
231
232 // Read the first 5 bytes of the reply, getting its total type and total length
233 m_sock->Read(buf, 5);
234
235 // Read the rest
236 response_type = buf[0];
237 int *pint =(int *)(&buf[1]);
238 int total_length = *pint;
239
240 // get some memory to read the rest
241 pbuffer = (unsigned char*) malloc(total_length * sizeof(unsigned char));
242
243 m_sock->Read(pbuffer, total_length-5);
244
245 switch(response_type - 0x80)
246 {
247 case 'D' :
248 m_bRX = true; // reset watchdog
249 m_watchtick = 0;
250
251 // Get the scan results station count
252 pcnt = (int *)&pbuffer[0];
253 cnt = *pcnt;
254
255 if(cnt > 64)
256 cnt = 64; // be safe
257
258 // Manage the data input
259 // Some setup
260 for(i=0 ; i < cnt ; i++)
261 pt_eaten[i] = false;
262
263 // First, check to see if any input station data is already present in local store
264 // If it is (ESSID matches), then simply update the signal quality, and refresh the age.
265 // Also, flag the fact that the input data has been eaten.
266
267 for(i=0 ; i < cnt ; i++)
268 {
269 pt = (wifi_scan_data *)(&pbuffer[(sizeof(int) + i * 256)]); // skipping the first int
270 if(strlen(pt->ESSID))
271 {
272 for(int ilocal = 0 ; ilocal < NLOCALSTORE ; ilocal++)
273 {
274 if((!strcmp(pt->ESSID, station_data[ilocal].ESSID)) && (station_data[ilocal].bisvalid))
275 {
276 station_data[ilocal].sig_quality = pt->sig_quality;
277 station_data[ilocal].age = -1;
278 pt_eaten[i] = true;
279 }
280 }
281 }
282 }
283
284 // Now, age the local store by one
285 for(ilocal = 0 ; ilocal < NLOCALSTORE ; ilocal++)
286 if(station_data[ilocal].bisvalid)
287 station_data[ilocal].age ++;
288
289 // and free any entries that are over the specified age
290 for(ilocal = 0 ; ilocal < NLOCALSTORE ; ilocal++)
291 {
292 if((station_data[ilocal].bisvalid) && (station_data[ilocal].age >= N_AGEDEATH))
293 {
294 station_data[ilocal].bisvalid = false;
295 station_data[ilocal].ESSID[0] = 0;
296 }
297 }
298
299
300 // Now, check to see if any input data is un-eaten
301 // If found, then try to allocate to a local store item
302 for(i=0 ; i < cnt ; i++)
303 {
304 if(pt_eaten[i] == false)
305 {
306 pt = (wifi_scan_data *)(&pbuffer[(sizeof(int) + i * 256)]);
307 if(strlen(pt->ESSID))
308 {
309 for(ilocal = 0 ; ilocal < NLOCALSTORE ; ilocal++)
310 {
311 if(station_data[ilocal].bisvalid == false)
312 {
313 strcpy(station_data[ilocal].ESSID, pt->ESSID);
314 station_data[ilocal].sig_quality = pt->sig_quality;
315 station_data[ilocal].secure = pt->secure;
316 station_data[ilocal].bisvalid = true;
317 station_data[ilocal].age = 0;
318 pt_eaten[i] = true;
319 break;
320 }
321 }
322 }
323 }
324 }
325
326 // There may still be un-eaten input data at this point......
327 // For now, ignore it. If it is real, it will appear as soon as something else dies
328
329 // Finally, send the data to the display window
330 for(ilocal = 0 ; ilocal < NLOCALSTORE ; ilocal++)
331 {
332 if(station_data[ilocal].bisvalid)
333 {
334 // g_ChartBarWin->pWiFi->SetStationQuality(ilocal, station_data[ilocal].sig_quality);
335 // g_ChartBarWin->pWiFi->SetStationSecureFlag(ilocal, station_data[ilocal].secure);
336 // g_ChartBarWin->pWiFi->SetStationAge(ilocal, station_data[ilocal].age);
337 }
338 // else
339 // g_ChartBarWin->pWiFi->SetStationQuality(ilocal, 0);
340 }
341 g_ChartBarWin->Refresh(true);
342
343 break;
344
345
346 case 'S' :
347 {
348 /*
349 StatusString = wxString(&buf[5]);
350
351 // This may be useful later....
352 fi_status_data *status = (wifi_status_data *)&buf[5];
353
354 memcpy(&connected_ap_mac_addr, &status->currently_connected_ap, sizeof(struct sockaddr));
355
356 // Check for re-connect, if needed
357 if(StatusString.StartsWith("Not"))
358 {
359 if(s_do_reconnect)
360 {
361 time_t tnow = wxDateTime::GetTimeNow();
362 last_connect_seconds = tnow - last_connect_time;
363
364 do_reconnect();
365 }
366 }
367
368 m_statWindow->Refresh();
369 */
370 break;
371 }
372
373 case 'R' :
374 {
375 /*
376 wxString wr(&buf[5]);
377 m_logWindow->WriteText(wr);
378 long ac_compass, ac_brg_commanded, ac_brg_current, ac_motor_dir;
379
380 // Parse the Antenna Controller string
381 if(!strncmp((const char *)&buf[5], "ANTC", 4)) // valid string
382 {
383 wxStringTokenizer tk(wr, wxT(":"));
384
385 wxString token = tk.GetNextToken(); // skip ANTC
386
387 token = tk.GetNextToken();
388 token.ToLong(&ac_compass); // compass heading
389
390 token = tk.GetNextToken();
391 token.ToLong(&ac_brg_commanded); // last commanded antenna bearing
392
393 token = tk.GetNextToken();
394 token.ToLong(&ac_brg_current); // current antenna brg
395
396 token = tk.GetNextToken();
397 token.ToLong(&ac_motor_dir); // current motor state
398
399 s_ac_compass = ac_compass;
400 s_ac_brg_commanded = ac_brg_commanded;
401 s_ac_brg_current = ac_brg_current;
402 s_ac_motor_dir = ac_motor_dir;
403
404
405 m_antWindow->Refresh();
406 }
407 */
408 break;
409 }
410
411 case 'K' :
412 {
413
414 break;
415 }
416
417
418 default:
419 break;
420 } //switch
421
422 free(pbuffer);
423
424 } // if
425
426
427 event.Skip();
428 }
429
430
OnTimer1(wxTimerEvent & event)431 void WIFIWindow::OnTimer1(wxTimerEvent& event)
432 {
433 Timer1.Stop();
434
435 if(m_sock->IsConnected())
436 {
437 // Keep a watchdog on received data
438 if(g_ChartBarWin)
439 {
440 if(m_watchtick++ > WIFI_DOG_TIMEOUT) // nothing received recently
441 {
442 // g_ChartBarWin->pWiFi->SetServerStatus(false);
443 g_ChartBarWin->Refresh(true);
444
445 // Try to totally reset the socket
446 m_sock->Destroy();
447
448 m_sock = new wxSocketClient();
449 m_sock->SetEventHandler(*this, WIFI_SOCKET_ID);
450
451 m_sock->SetNotify(wxSOCKET_CONNECTION_FLAG |
452 wxSOCKET_INPUT_FLAG |
453 wxSOCKET_LOST_FLAG);
454 m_sock->Notify(TRUE);
455 m_sock->SetFlags(wxSOCKET_WAITALL | wxSOCKET_BLOCK );
456
457 m_watchtick = 0;
458 }
459 // else
460 // g_ChartBarWin->pWiFi->SetServerStatus(true);
461 }
462
463 unsigned char c = WIFI_TRANSMIT_DATA_EXT; // and call for more data
464 m_sock->Write(&c, 1);
465 }
466 else // try to connect
467 {
468 if(g_ChartBarWin)
469 {
470 // g_ChartBarWin->pWiFi->SetServerStatus(false);
471 g_ChartBarWin->Refresh(true);
472 }
473 m_sock->Connect(addr, FALSE); // Non-blocking connect
474 }
475
476 m_bRX = false;
477 Timer1.Start(m_scan_interval_msec,wxTIMER_CONTINUOUS);
478 }
479
480
481 //-------------------------------------------------------------------------------------------------------------
482 //
483 // A simple thread to test host name resolution without blocking the main thread
484 //
485 //-------------------------------------------------------------------------------------------------------------
WIFIDNSTestThread(const wxString & name_or_ip)486 WIFIDNSTestThread::WIFIDNSTestThread(const wxString &name_or_ip)
487 {
488 m_pip = new wxString(name_or_ip);
489
490 Create();
491 }
492
~WIFIDNSTestThread()493 WIFIDNSTestThread::~WIFIDNSTestThread()
494 {
495 delete m_pip;
496 }
497
498
Entry()499 void *WIFIDNSTestThread::Entry()
500 {
501 wifi_s_dns_test_flag = 0;
502
503 wxIPV4address addr;
504 addr.Hostname(*m_pip); // this may block forever if DNS is not active
505
506 wifi_s_dns_test_flag = 1; // came back OK
507 return NULL;
508 }
509
510