34#include <wx/datetime.h>
38#include "o_sound/o_sound.h"
48#include "ais_info_gui.h"
57wxDEFINE_EVENT(EVT_AIS_DEL_TRACK, wxCommandEvent);
59wxDEFINE_EVENT(EVT_AIS_NEW_TRACK, wxCommandEvent);
60wxDEFINE_EVENT(EVT_AIS_TOUCH, wxCommandEvent);
61wxDEFINE_EVENT(EVT_AIS_WP, wxCommandEvent);
62wxDEFINE_EVENT(SOUND_PLAYED_EVTYPE, wxCommandEvent);
66static void onSoundFinished(
void *ptr) {
68 wxCommandEvent ev(SOUND_PLAYED_EVTYPE);
74 NavObj_dB::GetInstance().InsertRoutePoint(pWP);
78 if (top_frame::Get()->GetAbstractPrimaryCanvas()) {
79 top_frame::Get()->BeforeUndoableAction(Undo_CreateWaypoint, pWP,
80 Undo_HasParent, NULL);
81 top_frame::Get()->AfterUndoableAction(NULL);
82 top_frame::Get()->RefreshAllCanvas(
false);
83 top_frame::Get()->InvalidateAllGL();
87const static char *
const kDeleteTrackPrompt = _(R
"(
88This AIS target has Persistent Tracking selected by MMSI properties
89A Persistent track recording will therefore be restarted for this target.
91Do you instead want to stop Persistent Tracking for this target?
95 if (wxID_NO == OCPNMessageBox(NULL, kDeleteTrackPrompt, _(
"OpenCPN Info"),
96 wxYES_NO | wxCENTER, 60)) {
97 props->m_bPersistentTrack =
true;
101AisInfoGui::AisInfoGui() {
106 auto palert_target = std::static_pointer_cast<const AisTargetData>(ptr);
107 ShowAisInfo(palert_target);
112 [&](wxCommandEvent ev) { top_frame::Get()->TouchAISActive(); });
115 Bind(EVT_AIS_WP, [&](wxCommandEvent ev) {
116 auto pWP =
static_cast<RoutePoint *
>(ev.GetClientData());
117 OnNewAisWaypoint(pWP);
121 Bind(EVT_AIS_NEW_TRACK, [&](wxCommandEvent ev) {
122 auto t =
static_cast<Track *
>(ev.GetClientData());
123 NavObj_dB::GetInstance().InsertTrack(t);
127 Bind(EVT_AIS_DEL_TRACK, [&](wxCommandEvent ev) {
132 Bind(SOUND_PLAYED_EVTYPE,
133 [&](wxCommandEvent ev) { OnSoundFinishedAISAudio(ev); });
136 m_bAIS_Audio_Alert_On =
false;
137 m_bAIS_AlertPlaying =
false;
138 m_alarm_defer_count = -1;
139 m_lastMMSItime = wxDateTime::Now();
142void AisInfoGui::OnSoundFinishedAISAudio(wxCommandEvent &event) {
147 m_AIS_Sound->UnLoad();
149 m_bAIS_AlertPlaying =
false;
152void AisInfoGui::ShowAisInfo(
153 std::shared_ptr<const AisTargetData> palert_target) {
154 if (!palert_target)
return;
156 int audioType = AISAUDIO_NONE;
158 switch (palert_target->Class) {
160 audioType = AISAUDIO_DSC;
163 audioType = AISAUDIO_SART;
166 audioType = AISAUDIO_CPA;
172 int last_alert_MMSI = m_lastMMSI;
173 wxDateTime last_alert_time = m_lastMMSItime;
175 if (palert_target->MMSI != last_alert_MMSI) {
176 wxTimeSpan dt = wxDateTime::Now() - last_alert_time;
177 if (dt.GetSeconds() > 5) {
178 m_alarm_defer_count = -1;
182 m_lastMMSI = palert_target->MMSI;
183 m_lastMMSItime = wxDateTime::Now();
188 if (!g_pais_alert_dialog_active) {
190 if (m_alarm_defer_count < 0) {
191 m_alarm_defer_count = g_AIS_alert_delay;
193 if (m_alarm_defer_count >= 1) {
194 m_alarm_defer_count--;
197 if (m_alarm_defer_count == 0) {
198 bool b_jumpto = (palert_target->Class == AIS_SART) ||
199 (palert_target->Class == AIS_DSC);
200 bool b_createWP = palert_target->Class == AIS_DSC;
201 bool b_ack = palert_target->Class != AIS_DSC;
218 if (!top_frame::Get()->IsIconized()) {
220 pAISAlertDialog->Create(palert_target->MMSI, wxTheApp->GetTopWindow(),
221 g_pAIS, b_jumpto, b_createWP, b_ack, -1,
224 g_pais_alert_dialog_active = pAISAlertDialog;
226 wxTimeSpan alertLifeTime(0, 1, 0,
228 auto alert_dlg_active =
230 alert_dlg_active->dtAlertExpireTime = wxDateTime::Now() + alertLifeTime;
231 g_Platform->PositionAISAlert(pAISAlertDialog);
233 pAISAlertDialog->Show();
237 m_bAIS_Audio_Alert_On =
true;
245 double tcpa_min = 1e6;
247 const auto ¤t_targets =
g_pAIS->GetTargetList();
248 for (
auto &it : current_targets) {
251 if ((td->Class != AIS_SART) && (td->Class != AIS_DSC)) {
252 if (g_bAIS_CPA_Alert && td->b_active) {
253 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
254 if (td->TCPA < tcpa_min) {
256 palert_target_lowestcpa = td.get();
265 auto alert_dlg_active =
268 g_pAIS->Get_Target_Data_From_MMSI(alert_dlg_active->Get_Dialog_MMSI());
274 if (AIS_NO_ALERT == palert_target->n_alert_state) {
275 if (palert_target_lowestcpa) {
276 palert_target = NULL;
278 m_alarm_defer_count = -1;
283 wxDateTime now = wxDateTime::Now();
284 if (((AIS_ALERT_SET == palert_target->n_alert_state) &&
285 !palert_target->b_in_ack_timeout) ||
286 (palert_target->Class == AIS_SART)) {
287 alert_dlg_active->UpdateText();
289 wxTimeSpan alertLifeTime(0, 1, 0,
291 alert_dlg_active->dtAlertExpireTime = wxDateTime::Now() + alertLifeTime;
294 else if (!palert_target->b_in_ack_timeout &&
295 (now.IsEarlierThan(alert_dlg_active->dtAlertExpireTime))) {
296 alert_dlg_active->UpdateText();
298 alert_dlg_active->Close();
299 m_bAIS_Audio_Alert_On =
false;
300 m_alarm_defer_count = -1;
303 if (
true == palert_target->b_suppress_audio)
304 m_bAIS_Audio_Alert_On =
false;
306 m_bAIS_Audio_Alert_On =
true;
308 alert_dlg_active->Close();
309 m_bAIS_Audio_Alert_On =
false;
310 m_alarm_defer_count = -1;
316 if (!g_bAIS_CPA_Alert_Audio) m_bAIS_Audio_Alert_On =
false;
319 auto alert_dlg_active_audio_check =
321 if (alert_dlg_active_audio_check && alert_dlg_active_audio_check->IsShown()) {
322 if (m_bAIS_Audio_Alert_On) {
324 m_AIS_Sound = o_sound::Factory();
326 if (!AIS_AlertPlaying()) {
327 m_bAIS_AlertPlaying =
true;
331 if (g_bAIS_DSC_Alert_Audio) soundFile = g_DSC_sound_file;
334 if (g_bAIS_SART_Alert_Audio) soundFile = g_SART_sound_file;
338 if (g_bAIS_GCPA_Alert_Audio) soundFile = g_AIS_sound_file;
342 m_AIS_Sound->Load(soundFile, g_iSoundDeviceIndex);
343 if (m_AIS_Sound->IsOk()) {
344 m_AIS_Sound->SetFinishedCallback(onSoundFinished,
this);
345 if (!m_AIS_Sound->Play()) {
348 m_bAIS_AlertPlaying =
false;
353 m_bAIS_AlertPlaying =
false;
361 if (palert_target && (palert_target->Class == AIS_SART)) {
365 top_frame::Get()->UpdateAISMOBRoute(palert_target.get());
367 top_frame::Get()->ActivateAISMOBRoute(palert_target.get());
ArrayOfMmsiProperties g_MMSI_Props_Array
Global instance.
AisDecoder * g_pAIS
Global instance.
Class AisDecoder and helpers.
AisInfoGui * g_pAISGUI
Global instance.
Global state for AIS decoder.
Class AISTargetAlertDialog and helpers.
Generic Chart canvas base.
Dialog for displaying AIS target alerts.
EventVar new_ais_wp
Notified when new AIS wp is created.
EventVar info_update
Notified when AIS user dialogs should update.
EventVar touch_state
Notified when gFrame->TouchAISActive() should be invoked.
Handles the AIS information GUI and sound alerts.
Process incoming AIS messages.
void Listen(const std::string &key, wxEvtHandler *listener, wxEventType evt)
Set object to send wxEventType ev to listener on changes in key.
Custom event class for OpenCPN's notification system.
std::shared_ptr< const void > GetSharedPtr() const
Gets the event's payload data.
Represents a waypoint or mark within the navigation system.
Represents a track, which is a series of connected track points.
Miscellaneous globals primarely used by gui layer, not persisted in configuration file.
MySQL based storage for routes, tracks, etc.
Waypoint or mark abstraction.
Route * pAISMOBRoute
Global instance.
RouteManagerDialog * pRouteManagerDialog
Global instance.
Abstract gFrame/MyFrame interface.
Framework for Undo features.