DeviceDescriptor: eliminate obsolete NMEAOut kludge
[xcsoar.git] / src / Device / Descriptor.cpp
blob89392d570dc0ac0f40f132d727bb225239fbf9b9
1 /*
2 Copyright_License {
4 XCSoar Glide Computer - http://www.xcsoar.org/
5 Copyright (C) 2000-2013 The XCSoar Project
6 A detailed list of copyright holders can be found in the file "AUTHORS".
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #include "Device/Descriptor.hpp"
25 #include "Device/Driver.hpp"
26 #include "Device/Parser.hpp"
27 #include "Driver/FLARM/Device.hpp"
28 #include "Driver/LX/Internal.hpp"
29 #include "Device/Internal.hpp"
30 #include "Device/Register.hpp"
31 #include "Blackboard/DeviceBlackboard.hpp"
32 #include "Components.hpp"
33 #include "Port/ConfiguredPort.hpp"
34 #include "NMEA/Info.hpp"
35 #include "Thread/Mutex.hpp"
36 #include "Util/StringUtil.hpp"
37 #include "Logger/NMEALogger.hpp"
38 #include "Language/Language.hpp"
39 #include "Operation/Operation.hpp"
40 #include "OS/Clock.hpp"
41 #include "../Simulator.hpp"
42 #include "Asset.hpp"
43 #include "Input/InputQueue.hpp"
44 #include "LogFile.hpp"
45 #include "Job/Job.hpp"
47 #ifdef ANDROID
48 #include "Java/Object.hpp"
49 #include "Java/Global.hpp"
50 #include "Android/InternalSensors.hpp"
51 #include "Android/Main.hpp"
52 #include "Android/Product.hpp"
53 #endif
55 #ifdef IOIOLIB
56 #include "Android/IOIOHelper.hpp"
57 #include "Android/BMP085Device.hpp"
58 #include "Android/I2CbaroDevice.hpp"
59 #include "Android/NunchuckDevice.hpp"
60 #include "Android/VoltageDevice.hpp"
61 #endif
63 #include <assert.h>
65 /**
66 * This scope class calls DeviceDescriptor::Return() and
67 * DeviceDescriptor::EnableNMEA() when the caller leaves the current
68 * scope. The caller must have called DeviceDescriptor::Borrow()
69 * successfully before constructing this class.
71 struct ScopeReturnDevice {
72 DeviceDescriptor &device;
73 OperationEnvironment &env;
75 ScopeReturnDevice(DeviceDescriptor &_device, OperationEnvironment &_env)
76 :device(_device), env(_env) {
79 ~ScopeReturnDevice() {
80 device.EnableNMEA(env);
81 device.Return();
85 class OpenDeviceJob final : public Job {
86 DeviceDescriptor &device;
88 public:
89 OpenDeviceJob(DeviceDescriptor &_device):device(_device) {}
91 /* virtual methods from class Job */
92 virtual void Run(OperationEnvironment &env) {
93 device.DoOpen(env);
97 DeviceDescriptor::DeviceDescriptor(unsigned _index)
98 :index(_index),
99 open_job(NULL),
100 port(NULL), monitor(NULL), dispatcher(NULL),
101 driver(NULL), device(NULL),
102 #ifdef ANDROID
103 internal_sensors(NULL),
104 #ifdef IOIOLIB
105 droidsoar_v2(nullptr),
106 nunchuck(nullptr),
107 voltage(nullptr),
108 #endif
109 #endif
110 ticker(false), borrowed(false)
112 config.Clear();
113 #ifdef IOIOLIB
114 for (unsigned i=0; i<sizeof i2cbaro/sizeof i2cbaro[0]; i++)
115 i2cbaro[i] = nullptr;
116 #endif
119 void
120 DeviceDescriptor::SetConfig(const DeviceConfig &_config)
122 config = _config;
124 if (config.UsesDriver()) {
125 driver = FindDriverByName(config.driver_name);
126 assert(driver != NULL);
127 } else
128 driver = NULL;
131 void
132 DeviceDescriptor::ClearConfig()
134 config.Clear();
137 PortState
138 DeviceDescriptor::GetState() const
140 if (open_job != nullptr)
141 return PortState::LIMBO;
143 if (port != nullptr)
144 return port->GetState();
146 #ifdef ANDROID
147 if (internal_sensors != nullptr)
148 return PortState::READY;
150 #ifdef IOIOLIB
151 if (droidsoar_v2 != nullptr)
152 return PortState::READY;
154 if (i2cbaro[0] != nullptr)
155 return PortState::READY;
157 if (nunchuck != nullptr)
158 return PortState::READY;
160 if (voltage != nullptr)
161 return PortState::READY;
162 #endif
163 #endif
165 return PortState::FAILED;
168 bool
169 DeviceDescriptor::ShouldReopenDriverOnTimeout() const
171 return driver == NULL || driver->HasTimeout();
174 void
175 DeviceDescriptor::CancelAsync()
177 assert(InMainThread());
179 if (!async.IsBusy())
180 return;
182 assert(open_job != NULL);
184 async.Cancel();
185 async.Wait();
187 delete open_job;
188 open_job = NULL;
191 bool
192 DeviceDescriptor::Open(Port &_port, OperationEnvironment &env)
194 assert(port == NULL);
195 assert(device == NULL);
196 assert(driver != NULL);
197 assert(!ticker);
198 assert(!IsBorrowed());
200 reopen_clock.Update();
202 device_blackboard->mutex.Lock();
203 device_blackboard->SetRealState(index).Reset();
204 device_blackboard->ScheduleMerge();
205 device_blackboard->mutex.Unlock();
207 settings_sent.Clear();
208 settings_received.Clear();
209 was_alive = false;
211 port = &_port;
213 parser.Reset();
214 parser.SetReal(_tcscmp(driver->name, _T("Condor")) != 0);
215 parser.SetIgnoreChecksum(config.ignore_checksum);
216 if (config.IsDriver(_T("Condor")))
217 parser.DisableGeoid();
219 if (driver->CreateOnPort != nullptr) {
220 Device *new_device = driver->CreateOnPort(config, *port);
222 const ScopeLock protect(mutex);
223 device = new_device;
224 } else
225 port->StartRxThread();
227 EnableNMEA(env);
228 return true;
231 bool
232 DeviceDescriptor::OpenInternalSensors()
234 #ifdef ANDROID
235 if (is_simulator())
236 return true;
238 if (IsNookSimpleTouch())
239 /* avoid a crash on startup b/c nook has no internal sensors */
240 return false;
242 internal_sensors =
243 InternalSensors::create(Java::GetEnv(), context, GetIndex());
244 if (internal_sensors) {
245 // TODO: Allow user to specify whether they want certain sensors.
246 internal_sensors->subscribeToSensor(InternalSensors::TYPE_PRESSURE);
247 return true;
249 #endif
250 return false;
253 bool
254 DeviceDescriptor::OpenDroidSoarV2()
256 #ifdef IOIOLIB
257 if (is_simulator())
258 return true;
260 if (ioio_helper == nullptr)
261 return false;
263 if (i2cbaro[0] == NULL) {
264 i2cbaro[0] = new I2CbaroDevice(GetIndex(), Java::GetEnv(),
265 ioio_helper->GetHolder(),
266 DeviceConfig::PressureUse::STATIC_WITH_VARIO,
267 config.sensor_offset,
268 2 + (0x77 << 8) + (27 << 16), 0, // bus, address
269 5, // update freq.
270 0); // flags
272 i2cbaro[1] = new I2CbaroDevice(GetIndex(), Java::GetEnv(),
273 ioio_helper->GetHolder(),
274 // needs calibration ?
275 (config.sensor_factor == fixed(0)) ? DeviceConfig::PressureUse::PITOT_ZERO :
276 DeviceConfig::PressureUse::PITOT,
277 config.sensor_offset, 1 + (0x77 << 8) + (46 << 16), 0 ,
280 return true;
282 #endif
283 return false;
286 bool
287 DeviceDescriptor::OpenI2Cbaro()
289 #ifdef IOIOLIB
290 if (is_simulator())
291 return true;
293 if (ioio_helper == nullptr)
294 return false;
296 for (unsigned i=0; i<sizeof i2cbaro/sizeof i2cbaro[0]; i++) {
297 if (i2cbaro[i] == NULL) {
298 i2cbaro[i] = new I2CbaroDevice(GetIndex(), Java::GetEnv(),
299 ioio_helper->GetHolder(),
300 // needs calibration ?
301 (config.sensor_factor == fixed(0) && config.press_use == DeviceConfig::PressureUse::PITOT) ?
302 DeviceConfig::PressureUse::PITOT_ZERO :
303 config.press_use,
304 config.sensor_offset,
305 config.i2c_bus, config.i2c_addr,
306 config.press_use == DeviceConfig::PressureUse::TEK_PRESSURE ? 20 : 5,
307 0); // called flags, actually reserved for future use.
308 return true;
311 #endif
312 return false;
315 bool
316 DeviceDescriptor::OpenNunchuck()
318 #ifdef IOIOLIB
319 if (is_simulator())
320 return true;
322 if (ioio_helper == nullptr)
323 return false;
325 nunchuck = new NunchuckDevice(GetIndex(), Java::GetEnv(),
326 ioio_helper->GetHolder(),
327 config.i2c_bus, 5); // twi, sample_rate
328 return true;
329 #else
330 return false;
331 #endif
334 bool
335 DeviceDescriptor::OpenVoltage()
337 #ifdef IOIOLIB
338 if (is_simulator())
339 return true;
341 if (ioio_helper == nullptr)
342 return false;
344 voltage = new VoltageDevice(GetIndex(), Java::GetEnv(),
345 ioio_helper->GetHolder(),
346 config.sensor_offset, config.sensor_factor,
347 60); // sample_rate per minute
348 return true;
349 #else
350 return false;
351 #endif
354 bool
355 DeviceDescriptor::DoOpen(OperationEnvironment &env)
357 assert(config.IsAvailable());
359 if (config.port_type == DeviceConfig::PortType::INTERNAL)
360 return OpenInternalSensors();
362 if (config.port_type == DeviceConfig::PortType::DROIDSOAR_V2)
363 return OpenDroidSoarV2();
365 if (config.port_type == DeviceConfig::PortType::I2CPRESSURESENSOR)
366 return OpenI2Cbaro();
368 if (config.port_type == DeviceConfig::PortType::NUNCHUCK)
369 return OpenNunchuck();
371 if (config.port_type == DeviceConfig::PortType::IOIOVOLTAGE)
372 return OpenVoltage();
374 reopen_clock.Update();
376 Port *port = OpenPort(config, *this);
377 if (port == NULL) {
378 TCHAR name_buffer[64];
379 const TCHAR *name = config.GetPortName(name_buffer, 64);
381 StaticString<256> msg;
382 msg.Format(_T("%s: %s."), _("Unable to open port"), name);
383 env.SetErrorMessage(msg);
384 return false;
387 if (!port->WaitConnected(env) || !Open(*port, env)) {
388 delete port;
389 return false;
392 return true;
395 void
396 DeviceDescriptor::Open(OperationEnvironment &env)
398 assert(InMainThread());
399 assert(port == NULL);
400 assert(device == NULL);
401 assert(!ticker);
402 assert(!IsBorrowed());
404 if (is_simulator() || !config.IsAvailable())
405 return;
407 CancelAsync();
409 assert(!IsOccupied());
410 assert(open_job == NULL);
412 TCHAR buffer[64];
413 LogFormat(_T("Opening device %s"), config.GetPortName(buffer, 64));
415 open_job = new OpenDeviceJob(*this);
416 async.Start(open_job, env, this);
419 void
420 DeviceDescriptor::Close()
422 assert(InMainThread());
423 assert(!IsBorrowed());
425 CancelAsync();
427 #ifdef ANDROID
428 delete internal_sensors;
429 internal_sensors = NULL;
431 #ifdef IOIOLIB
432 delete droidsoar_v2;
433 droidsoar_v2 = nullptr;
435 for (unsigned i=0; i<sizeof i2cbaro/sizeof i2cbaro[0]; i++) {
436 delete i2cbaro[i];
437 i2cbaro[i] = nullptr;
439 delete nunchuck;
440 nunchuck = nullptr;
442 delete voltage;
443 voltage = nullptr;
445 #endif
447 #endif
449 /* safely delete the Device object */
450 Device *old_device = device;
453 const ScopeLock protect(mutex);
454 device = nullptr;
455 /* after leaving this scope, no other thread may use the old
456 object; to avoid locking the mutex for too long, the "delete"
457 is called after the scope */
460 delete old_device;
462 Port *old_port = port;
463 port = NULL;
464 delete old_port;
466 ticker = false;
468 device_blackboard->mutex.Lock();
469 device_blackboard->SetRealState(index).Reset();
470 device_blackboard->ScheduleMerge();
471 device_blackboard->mutex.Unlock();
473 settings_sent.Clear();
474 settings_received.Clear();
477 void
478 DeviceDescriptor::Reopen(OperationEnvironment &env)
480 assert(InMainThread());
481 assert(!IsBorrowed());
483 Close();
484 Open(env);
487 void
488 DeviceDescriptor::AutoReopen(OperationEnvironment &env)
490 assert(InMainThread());
492 if (/* don't reopen a device that is occupied */
493 IsOccupied() ||
494 !config.IsAvailable() ||
495 !ShouldReopen() ||
496 /* attempt to reopen a failed device every 30 seconds */
497 !reopen_clock.CheckUpdate(30000))
498 return;
500 TCHAR buffer[64];
501 LogFormat(_T("Reconnecting to device %s"), config.GetPortName(buffer, 64));
503 InputEvents::processGlideComputer(GCE_COMMPORT_RESTART);
504 Reopen(env);
507 bool
508 DeviceDescriptor::EnableNMEA(OperationEnvironment &env)
510 if (device == NULL)
511 return true;
513 bool success = device->EnableNMEA(env);
515 if (port != NULL)
516 /* re-enable the NMEA handler if it has been disabled by the
517 driver */
518 port->StartRxThread();
520 return success;
523 const TCHAR *
524 DeviceDescriptor::GetDisplayName() const
526 return driver != NULL ? driver->display_name : NULL;
529 bool
530 DeviceDescriptor::IsDriver(const TCHAR *name) const
532 return driver != NULL
533 ? _tcscmp(driver->name, name) == 0
534 : false;
537 bool
538 DeviceDescriptor::CanDeclare() const
540 return driver != NULL &&
541 (driver->CanDeclare() ||
542 device_blackboard->IsFLARM(index));
545 bool
546 DeviceDescriptor::IsLogger() const
548 return driver != NULL && driver->IsLogger();
551 bool
552 DeviceDescriptor::IsNMEAOut() const
554 return driver != NULL && driver->IsNMEAOut();
557 bool
558 DeviceDescriptor::IsManageable() const
560 if (driver != NULL) {
561 if (driver->IsManageable())
562 return true;
564 if (_tcscmp(driver->name, _T("LX")) == 0 && device != NULL) {
565 const LXDevice &lx = *(const LXDevice *)device;
566 return lx.IsV7() || lx.IsNano() || lx.IsLX16xx();
570 return false;
573 bool
574 DeviceDescriptor::Borrow()
576 assert(InMainThread());
578 if (!CanBorrow())
579 return false;
581 borrowed = true;
582 return true;
585 void
586 DeviceDescriptor::Return()
588 assert(InMainThread());
589 assert(IsBorrowed());
591 borrowed = false;
592 assert(!IsOccupied());
594 /* if the caller has disabled the NMEA while the device was
595 borrowed, we may not have received new values for some time, but
596 that doesn't mean the device has failed; give it some time to
597 recover, and don't reopen right away */
598 reopen_clock.Update();
601 bool
602 DeviceDescriptor::IsAlive() const
604 ScopeLock protect(device_blackboard->mutex);
605 return device_blackboard->RealState(index).alive;
608 bool
609 DeviceDescriptor::ParseNMEA(const char *line, NMEAInfo &info)
611 assert(line != NULL);
613 /* restore the driver's ExternalSettings */
614 const ExternalSettings old_settings = info.settings;
615 info.settings = settings_received;
617 if (device != NULL && device->ParseNMEA(line, info)) {
618 info.alive.Update(info.clock);
620 if (!config.sync_from_device)
621 info.settings = old_settings;
623 /* clear the settings when the values are the same that we already
624 sent to the device */
625 const ExternalSettings old_received = settings_received;
626 settings_received = info.settings;
627 info.settings.EliminateRedundant(settings_sent, old_received);
629 return true;
632 /* no change - restore the ExternalSettings that we returned last
633 time */
634 info.settings = old_settings;
636 // Additional "if" to find GPS strings
637 if (parser.ParseLine(line, info)) {
638 info.alive.Update(fixed(MonotonicClockMS()) / 1000);
639 return true;
642 return false;
645 void
646 DeviceDescriptor::ForwardLine(const char *line)
648 /* XXX make this method thread-safe; this method can be called from
649 any thread, and if the Port gets closed, bad things happen */
651 if (IsNMEAOut() && port != NULL) {
652 port->Write(line);
653 port->Write("\r\n");
657 bool
658 DeviceDescriptor::WriteNMEA(const char *line, OperationEnvironment &env)
660 assert(line != NULL);
662 return port != NULL && PortWriteNMEA(*port, line, env);
665 #ifdef _UNICODE
666 bool
667 DeviceDescriptor::WriteNMEA(const TCHAR *line, OperationEnvironment &env)
669 assert(line != NULL);
671 if (port == NULL)
672 return false;
674 char buffer[_tcslen(line) * 4 + 1];
675 if (::WideCharToMultiByte(CP_ACP, 0, line, -1, buffer, sizeof(buffer),
676 NULL, NULL) <= 0)
677 return false;
679 return WriteNMEA(buffer, env);
681 #endif
683 bool
684 DeviceDescriptor::PutMacCready(fixed value, OperationEnvironment &env)
686 assert(InMainThread());
688 if (device == NULL || settings_sent.CompareMacCready(value) ||
689 !config.sync_to_device)
690 return true;
692 if (!Borrow())
693 /* TODO: postpone until the borrowed device has been returned */
694 return false;
696 ScopeReturnDevice restore(*this, env);
697 if (!device->PutMacCready(value, env))
698 return false;
700 ScopeLock protect(device_blackboard->mutex);
701 NMEAInfo &basic = device_blackboard->SetRealState(index);
702 settings_sent.mac_cready = value;
703 settings_sent.mac_cready_available.Update(basic.clock);
705 return true;
708 bool
709 DeviceDescriptor::PutBugs(fixed value, OperationEnvironment &env)
711 assert(InMainThread());
713 if (device == NULL || settings_sent.CompareBugs(value) ||
714 !config.sync_to_device)
715 return true;
717 if (!Borrow())
718 /* TODO: postpone until the borrowed device has been returned */
719 return false;
721 ScopeReturnDevice restore(*this, env);
722 if (!device->PutBugs(value, env))
723 return false;
725 ScopeLock protect(device_blackboard->mutex);
726 NMEAInfo &basic = device_blackboard->SetRealState(index);
727 settings_sent.bugs = value;
728 settings_sent.bugs_available.Update(basic.clock);
730 return true;
733 bool
734 DeviceDescriptor::PutBallast(fixed fraction, fixed overload,
735 OperationEnvironment &env)
737 assert(InMainThread());
739 if (device == NULL || !config.sync_to_device ||
740 (settings_sent.CompareBallastFraction(fraction) &&
741 settings_sent.CompareBallastOverload(overload)))
742 return true;
744 if (!Borrow())
745 /* TODO: postpone until the borrowed device has been returned */
746 return false;
748 ScopeReturnDevice restore(*this, env);
749 if (!device->PutBallast(fraction, overload, env))
750 return false;
752 ScopeLock protect(device_blackboard->mutex);
753 NMEAInfo &basic = device_blackboard->SetRealState(index);
754 settings_sent.ballast_fraction = fraction;
755 settings_sent.ballast_fraction_available.Update(basic.clock);
756 settings_sent.ballast_overload = overload;
757 settings_sent.ballast_overload_available.Update(basic.clock);
759 return true;
762 bool
763 DeviceDescriptor::PutVolume(unsigned volume, OperationEnvironment &env)
765 assert(InMainThread());
767 if (device == NULL || !config.sync_to_device)
768 return true;
770 if (!Borrow())
771 /* TODO: postpone until the borrowed device has been returned */
772 return false;
774 ScopeReturnDevice restore(*this, env);
775 return device->PutVolume(volume, env);
778 bool
779 DeviceDescriptor::PutActiveFrequency(RadioFrequency frequency,
780 OperationEnvironment &env)
782 assert(InMainThread());
784 if (device == NULL || !config.sync_to_device)
785 return true;
787 if (!Borrow())
788 /* TODO: postpone until the borrowed device has been returned */
789 return false;
791 ScopeReturnDevice restore(*this, env);
792 return device->PutActiveFrequency(frequency, env);
795 bool
796 DeviceDescriptor::PutStandbyFrequency(RadioFrequency frequency,
797 OperationEnvironment &env)
799 assert(InMainThread());
801 if (device == NULL || !config.sync_to_device)
802 return true;
804 if (!Borrow())
805 /* TODO: postpone until the borrowed device has been returned */
806 return false;
808 ScopeReturnDevice restore(*this, env);
809 return device->PutStandbyFrequency(frequency, env);
812 bool
813 DeviceDescriptor::PutQNH(const AtmosphericPressure &value,
814 OperationEnvironment &env)
816 assert(InMainThread());
818 if (device == NULL || settings_sent.CompareQNH(value) ||
819 !config.sync_to_device)
820 return true;
822 if (!Borrow())
823 /* TODO: postpone until the borrowed device has been returned */
824 return false;
826 ScopeReturnDevice restore(*this, env);
827 if (!device->PutQNH(value, env))
828 return false;
830 ScopeLock protect(device_blackboard->mutex);
831 NMEAInfo &basic = device_blackboard->SetRealState(index);
832 settings_sent.qnh = value;
833 settings_sent.qnh_available.Update(basic.clock);
835 return true;
838 static bool
839 DeclareToFLARM(const struct Declaration &declaration, Port &port,
840 const Waypoint *home, OperationEnvironment &env)
842 return FlarmDevice(port).Declare(declaration, home, env);
845 static bool
846 DeclareToFLARM(const struct Declaration &declaration,
847 Port &port, const DeviceRegister &driver, Device *device,
848 const Waypoint *home,
849 OperationEnvironment &env)
851 /* enable pass-through mode in the "front" device */
852 if (driver.HasPassThrough() && device != NULL &&
853 !device->EnablePassThrough(env))
854 return false;
856 return DeclareToFLARM(declaration, port, home, env);
859 static bool
860 DoDeclare(const struct Declaration &declaration,
861 Port &port, const DeviceRegister &driver, Device *device,
862 bool flarm, const Waypoint *home,
863 OperationEnvironment &env)
865 StaticString<60> text;
866 text.Format(_T("%s: %s."), _("Sending declaration"), driver.display_name);
867 env.SetText(text);
869 bool result = device != NULL && device->Declare(declaration, home, env);
871 if (flarm) {
872 text.Format(_T("%s: FLARM."), _("Sending declaration"));
873 env.SetText(text);
875 result |= DeclareToFLARM(declaration, port, driver, device, home, env);
878 return result;
881 bool
882 DeviceDescriptor::Declare(const struct Declaration &declaration,
883 const Waypoint *home,
884 OperationEnvironment &env)
886 assert(borrowed);
887 assert(port != NULL);
888 assert(driver != NULL);
889 assert(device != NULL);
891 /* enable the "muxed FLARM" hack? */
892 const bool flarm = device_blackboard->IsFLARM(index) &&
893 !IsDriver(_T("FLARM"));
895 return DoDeclare(declaration, *port, *driver, device, flarm,
896 home, env);
899 bool
900 DeviceDescriptor::ReadFlightList(RecordedFlightList &flight_list,
901 OperationEnvironment &env)
903 assert(borrowed);
904 assert(port != NULL);
905 assert(driver != NULL);
906 assert(device != NULL);
908 StaticString<60> text;
909 text.Format(_T("%s: %s."), _("Reading flight list"), driver->display_name);
910 env.SetText(text);
912 return device->ReadFlightList(flight_list, env);
915 bool
916 DeviceDescriptor::DownloadFlight(const RecordedFlightInfo &flight,
917 const TCHAR *path,
918 OperationEnvironment &env)
920 assert(borrowed);
921 assert(port != NULL);
922 assert(driver != NULL);
923 assert(device != NULL);
925 if (port == NULL || driver == NULL || device == NULL)
926 return false;
928 StaticString<60> text;
929 text.Format(_T("%s: %s."), _("Downloading flight log"), driver->display_name);
930 env.SetText(text);
932 return device->DownloadFlight(flight, path, env);
935 void
936 DeviceDescriptor::OnSysTicker()
938 assert(InMainThread());
940 if (port != NULL && port->GetState() == PortState::FAILED && !IsOccupied())
941 Close();
943 if (device == NULL)
944 return;
946 const bool now_alive = IsAlive();
947 if (!now_alive && was_alive && !IsOccupied()) {
948 /* connection was just lost */
949 device->LinkTimeout();
951 NullOperationEnvironment env;
952 EnableNMEA(env);
955 was_alive = now_alive;
957 if (now_alive || IsBorrowed()) {
958 ticker = !ticker;
959 if (ticker)
960 // write settings to vario every second
961 device->OnSysTicker();
965 void
966 DeviceDescriptor::OnSensorUpdate(const MoreData &basic)
968 /* must hold the mutex because this method may run in any thread,
969 just in case the main thread deletes the Device while this method
970 still runs */
971 const ScopeLock protect(mutex);
973 if (device != nullptr)
974 device->OnSensorUpdate(basic);
977 void
978 DeviceDescriptor::OnCalculatedUpdate(const MoreData &basic,
979 const DerivedInfo &calculated)
981 assert(InMainThread());
983 if (device != nullptr)
984 device->OnCalculatedUpdate(basic, calculated);
987 bool
988 DeviceDescriptor::ParseLine(const char *line)
990 ScopeLock protect(device_blackboard->mutex);
991 NMEAInfo &basic = device_blackboard->SetRealState(index);
992 basic.UpdateClock();
993 return ParseNMEA(line, basic);
996 void
997 DeviceDescriptor::OnNotification()
999 /* notification from AsyncJobRunner, the Job was finished */
1001 assert(InMainThread());
1002 assert(open_job != NULL);
1004 async.Wait();
1006 delete open_job;
1007 open_job = NULL;
1010 void
1011 DeviceDescriptor::DataReceived(const void *data, size_t length)
1013 if (monitor != NULL)
1014 monitor->DataReceived(data, length);
1016 // Pass data directly to drivers that use binary data protocols
1017 if (driver != NULL && device != NULL && driver->UsesRawData()) {
1018 ScopeLock protect(device_blackboard->mutex);
1019 NMEAInfo &basic = device_blackboard->SetRealState(index);
1020 basic.UpdateClock();
1022 const ExternalSettings old_settings = basic.settings;
1024 if (device->DataReceived(data, length, basic)) {
1025 if (!config.sync_from_device)
1026 basic.settings = old_settings;
1028 device_blackboard->ScheduleMerge();
1031 return;
1034 if (!IsNMEAOut())
1035 PortLineSplitter::DataReceived(data, length);
1038 void
1039 DeviceDescriptor::LineReceived(const char *line)
1041 NMEALogger::Log(line);
1043 if (dispatcher != NULL)
1044 dispatcher->LineReceived(line);
1046 if (ParseLine(line))
1047 device_blackboard->ScheduleMerge();