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