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;
224 if (gFrame->IsIconized() || !gFrame->IsActive())
225 gFrame->RequestUserAttention();
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;
327 if (m_bAIS_Audio_Alert_On) {
329 m_AIS_Sound = o_sound::Factory();
331 if (!AIS_AlertPlaying()) {
332 m_bAIS_AlertPlaying =
true;
336 if (g_bAIS_DSC_Alert_Audio) soundFile = g_DSC_sound_file;
339 if (g_bAIS_SART_Alert_Audio) soundFile = g_SART_sound_file;
343 if (g_bAIS_GCPA_Alert_Audio) soundFile = g_AIS_sound_file;
347 m_AIS_Sound->Load(soundFile, g_iSoundDeviceIndex);
348 if (m_AIS_Sound->IsOk()) {
349 m_AIS_Sound->SetFinishedCallback(onSoundFinished,
this);
350 if (!m_AIS_Sound->Play()) {
353 m_bAIS_AlertPlaying =
false;
358 m_bAIS_AlertPlaying =
false;
365 if (palert_target && (palert_target->Class == AIS_SART)) {
369 gFrame->UpdateAISMOBRoute(palert_target.get());
371 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.