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();
186 if (palert_target->Class == AIS_SART) m_alarm_defer_count = 1;
189 if (!g_pais_alert_dialog_active) {
191 if (m_alarm_defer_count < 0) {
192 m_alarm_defer_count = g_AIS_alert_delay;
194 if (m_alarm_defer_count >= 1) {
195 m_alarm_defer_count--;
198 if (m_alarm_defer_count == 0) {
199 bool b_jumpto = (palert_target->Class == AIS_SART) ||
200 (palert_target->Class == AIS_DSC);
201 bool b_createWP = palert_target->Class == AIS_DSC;
202 bool b_ack = palert_target->Class != AIS_DSC;
219 if (!top_frame::Get()->IsIconized()) {
221 pAISAlertDialog->Create(palert_target->MMSI, wxTheApp->GetTopWindow(),
222 g_pAIS, b_jumpto, b_createWP, b_ack, -1,
225 g_pais_alert_dialog_active = pAISAlertDialog;
227 wxTimeSpan alertLifeTime(0, 1, 0,
229 auto alert_dlg_active =
231 alert_dlg_active->dtAlertExpireTime = wxDateTime::Now() + alertLifeTime;
232 g_Platform->PositionAISAlert(pAISAlertDialog);
234 pAISAlertDialog->Show();
238 m_bAIS_Audio_Alert_On =
true;
246 double tcpa_min = 1e6;
248 const auto ¤t_targets =
g_pAIS->GetTargetList();
249 for (
auto &it : current_targets) {
252 if ((td->Class != AIS_SART) && (td->Class != AIS_DSC)) {
253 if (g_bAIS_CPA_Alert && td->b_active) {
254 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
255 if (td->TCPA < tcpa_min) {
257 palert_target_lowestcpa = td.get();
266 auto alert_dlg_active =
269 g_pAIS->Get_Target_Data_From_MMSI(alert_dlg_active->Get_Dialog_MMSI());
275 if (AIS_NO_ALERT == palert_target->n_alert_state) {
276 if (palert_target_lowestcpa) {
277 palert_target = NULL;
279 m_alarm_defer_count = -1;
284 wxDateTime now = wxDateTime::Now();
285 if (((AIS_ALERT_SET == palert_target->n_alert_state) &&
286 !palert_target->b_in_ack_timeout) ||
287 (palert_target->Class == AIS_SART)) {
288 alert_dlg_active->UpdateText();
290 wxTimeSpan alertLifeTime(0, 1, 0,
292 alert_dlg_active->dtAlertExpireTime = wxDateTime::Now() + alertLifeTime;
295 else if (!palert_target->b_in_ack_timeout &&
296 (now.IsEarlierThan(alert_dlg_active->dtAlertExpireTime))) {
297 alert_dlg_active->UpdateText();
299 alert_dlg_active->Close();
300 m_bAIS_Audio_Alert_On =
false;
301 m_alarm_defer_count = -1;
304 if (
true == palert_target->b_suppress_audio)
305 m_bAIS_Audio_Alert_On =
false;
307 m_bAIS_Audio_Alert_On =
true;
309 alert_dlg_active->Close();
310 m_bAIS_Audio_Alert_On =
false;
311 m_alarm_defer_count = -1;
317 if (!g_bAIS_CPA_Alert_Audio) m_bAIS_Audio_Alert_On =
false;
320 auto alert_dlg_active_audio_check =
322 if (alert_dlg_active_audio_check && alert_dlg_active_audio_check->IsShown()) {
323 if (m_bAIS_Audio_Alert_On) {
325 m_AIS_Sound = o_sound::Factory();
327 if (!AIS_AlertPlaying()) {
328 m_bAIS_AlertPlaying =
true;
332 if (g_bAIS_DSC_Alert_Audio) soundFile = g_DSC_sound_file;
335 if (g_bAIS_SART_Alert_Audio) soundFile = g_SART_sound_file;
339 if (g_bAIS_GCPA_Alert_Audio) soundFile = g_AIS_sound_file;
343 m_AIS_Sound->Load(soundFile, g_iSoundDeviceIndex);
344 if (m_AIS_Sound->IsOk()) {
345 m_AIS_Sound->SetFinishedCallback(onSoundFinished,
this);
346 if (!m_AIS_Sound->Play()) {
349 m_bAIS_AlertPlaying =
false;
354 m_bAIS_AlertPlaying =
false;
362 if (palert_target && (palert_target->Class == AIS_SART)) {
366 top_frame::Get()->UpdateAISMOBRoute(palert_target.get());
368 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.