36#include <wx/datetime.h>
40#include "o_sound/o_sound.h"
48#include "ais_info_gui.h"
58wxDEFINE_EVENT(EVT_AIS_DEL_TRACK, wxCommandEvent);
60wxDEFINE_EVENT(EVT_AIS_NEW_TRACK, wxCommandEvent);
61wxDEFINE_EVENT(EVT_AIS_TOUCH, wxCommandEvent);
62wxDEFINE_EVENT(EVT_AIS_WP, wxCommandEvent);
63wxDEFINE_EVENT(SOUND_PLAYED_EVTYPE, wxCommandEvent);
66extern bool g_bquiting;
67extern int g_iSoundDeviceIndex;
70extern wxString g_CmdSoundString;
77static void onSoundFinished(
void *ptr) {
79 wxCommandEvent ev(SOUND_PLAYED_EVTYPE);
80 wxPostEvent(g_pAISGUI, ev);
85 NavObj_dB::GetInstance().InsertRoutePoint(pWP);
87 if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
88 pRouteManagerDialog->UpdateWptListCtrl();
89 if (gFrame->GetPrimaryCanvas()) {
90 gFrame->GetPrimaryCanvas()->undo->BeforeUndoableAction(
91 Undo_CreateWaypoint, pWP, Undo_HasParent, NULL);
92 gFrame->GetPrimaryCanvas()->undo->AfterUndoableAction(NULL);
93 gFrame->RefreshAllCanvas(
false);
94 gFrame->InvalidateAllGL();
98const static char *
const kDeleteTrackPrompt = _(R
"(
99This AIS target has Persistent Tracking selected by MMSI properties
100A Persistent track recording will therefore be restarted for this target.
102Do you instead want to stop Persistent Tracking for this target?
106 if (wxID_NO == OCPNMessageBox(NULL, kDeleteTrackPrompt, _(
"OpenCPN Info"),
107 wxYES_NO | wxCENTER, 60)) {
108 props->m_bPersistentTrack =
true;
112AisInfoGui::AisInfoGui() {
117 auto palert_target = std::static_pointer_cast<const AisTargetData>(ptr);
118 ShowAisInfo(palert_target);
122 Bind(EVT_AIS_TOUCH, [&](wxCommandEvent ev) { gFrame->TouchAISActive(); });
125 Bind(EVT_AIS_WP, [&](wxCommandEvent ev) {
126 auto pWP =
static_cast<RoutePoint *
>(ev.GetClientData());
127 OnNewAisWaypoint(pWP);
131 Bind(EVT_AIS_NEW_TRACK, [&](wxCommandEvent ev) {
132 auto t =
static_cast<Track *
>(ev.GetClientData());
133 NavObj_dB::GetInstance().InsertTrack(t);
137 Bind(EVT_AIS_DEL_TRACK, [&](wxCommandEvent ev) {
142 Bind(SOUND_PLAYED_EVTYPE,
143 [&](wxCommandEvent ev) { OnSoundFinishedAISAudio(ev); });
146 m_bAIS_Audio_Alert_On =
false;
147 m_bAIS_AlertPlaying =
false;
148 m_alarm_defer_count = -1;
149 m_lastMMSItime = wxDateTime::Now();
152void AisInfoGui::OnSoundFinishedAISAudio(wxCommandEvent &event) {
157 m_AIS_Sound->UnLoad();
159 m_bAIS_AlertPlaying =
false;
162void AisInfoGui::ShowAisInfo(
163 std::shared_ptr<const AisTargetData> palert_target) {
164 if (!palert_target)
return;
166 int audioType = AISAUDIO_NONE;
168 switch (palert_target->Class) {
170 audioType = AISAUDIO_DSC;
173 audioType = AISAUDIO_SART;
176 audioType = AISAUDIO_CPA;
182 int last_alert_MMSI = m_lastMMSI;
183 wxDateTime last_alert_time = m_lastMMSItime;
185 if (palert_target->MMSI != last_alert_MMSI) {
186 wxTimeSpan dt = wxDateTime::Now() - last_alert_time;
187 if (dt.GetSeconds() > 5) {
188 m_alarm_defer_count = -1;
192 m_lastMMSI = palert_target->MMSI;
193 m_lastMMSItime = wxDateTime::Now();
198 if (!g_pais_alert_dialog_active) {
200 if (m_alarm_defer_count < 0) {
201 m_alarm_defer_count = g_AIS_alert_delay;
203 if (m_alarm_defer_count >= 1) {
204 m_alarm_defer_count--;
207 if (m_alarm_defer_count == 0) {
208 bool b_jumpto = (palert_target->Class == AIS_SART) ||
209 (palert_target->Class == AIS_DSC);
210 bool b_createWP = palert_target->Class == AIS_DSC;
211 bool b_ack = palert_target->Class != AIS_DSC;
228 if (!gFrame->IsIconized()) {
230 pAISAlertDialog->Create(palert_target->MMSI, gFrame,
g_pAIS, b_jumpto,
231 b_createWP, b_ack, -1, _(
"AIS Alert"));
233 g_pais_alert_dialog_active = pAISAlertDialog;
235 wxTimeSpan alertLifeTime(0, 1, 0,
237 auto alert_dlg_active =
239 alert_dlg_active->dtAlertExpireTime = wxDateTime::Now() + alertLifeTime;
240 g_Platform->PositionAISAlert(pAISAlertDialog);
242 pAISAlertDialog->Show();
246 m_bAIS_Audio_Alert_On =
true;
254 double tcpa_min = 1e6;
256 const auto ¤t_targets =
g_pAIS->GetTargetList();
257 for (
auto &it : current_targets) {
260 if ((td->Class != AIS_SART) && (td->Class != AIS_DSC)) {
261 if (g_bAIS_CPA_Alert && td->b_active) {
262 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
263 if (td->TCPA < tcpa_min) {
265 palert_target_lowestcpa = td.get();
274 auto alert_dlg_active =
277 g_pAIS->Get_Target_Data_From_MMSI(alert_dlg_active->Get_Dialog_MMSI());
283 if (AIS_NO_ALERT == palert_target->n_alert_state) {
284 if (palert_target_lowestcpa) {
285 palert_target = NULL;
287 m_alarm_defer_count = -1;
292 wxDateTime now = wxDateTime::Now();
293 if (((AIS_ALERT_SET == palert_target->n_alert_state) &&
294 !palert_target->b_in_ack_timeout) ||
295 (palert_target->Class == AIS_SART)) {
296 alert_dlg_active->UpdateText();
298 wxTimeSpan alertLifeTime(0, 1, 0,
300 alert_dlg_active->dtAlertExpireTime = wxDateTime::Now() + alertLifeTime;
303 else if (!palert_target->b_in_ack_timeout &&
304 (now.IsEarlierThan(alert_dlg_active->dtAlertExpireTime))) {
305 alert_dlg_active->UpdateText();
307 alert_dlg_active->Close();
308 m_bAIS_Audio_Alert_On =
false;
309 m_alarm_defer_count = -1;
312 if (
true == palert_target->b_suppress_audio)
313 m_bAIS_Audio_Alert_On =
false;
315 m_bAIS_Audio_Alert_On =
true;
317 alert_dlg_active->Close();
318 m_bAIS_Audio_Alert_On =
false;
319 m_alarm_defer_count = -1;
325 if (!g_bAIS_CPA_Alert_Audio) m_bAIS_Audio_Alert_On =
false;
328 auto alert_dlg_active_audio_check =
330 if (alert_dlg_active_audio_check && alert_dlg_active_audio_check->IsShown()) {
331 if (m_bAIS_Audio_Alert_On) {
333 m_AIS_Sound = o_sound::Factory();
335 if (!AIS_AlertPlaying()) {
336 m_bAIS_AlertPlaying =
true;
340 if (g_bAIS_DSC_Alert_Audio) soundFile = g_DSC_sound_file;
343 if (g_bAIS_SART_Alert_Audio) soundFile = g_SART_sound_file;
347 if (g_bAIS_GCPA_Alert_Audio) soundFile = g_AIS_sound_file;
351 m_AIS_Sound->Load(soundFile, g_iSoundDeviceIndex);
352 if (m_AIS_Sound->IsOk()) {
353 m_AIS_Sound->SetFinishedCallback(onSoundFinished,
this);
354 if (!m_AIS_Sound->Play()) {
357 m_bAIS_AlertPlaying =
false;
362 m_bAIS_AlertPlaying =
false;
370 if (palert_target && (palert_target->Class == AIS_SART)) {
374 gFrame->UpdateAISMOBRoute(palert_target.get());
376 gFrame->ActivateAISMOBRoute(palert_target.get());
ArrayOfMmsiProperties g_MMSI_Props_Array
Global instance.
AisDecoder * g_pAIS
Global instance.
Class AisDecoder and helpers.
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 navigational route in the navigation system.
Represents a track, which is a series of connected track points.
MySQL based storage for routes, tracks, etc.
Waypoint or mark abstraction.
Route * pAISMOBRoute
Global instance.
Framework for Undo features.