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