OpenCPN Partial API docs
Loading...
Searching...
No Matches
notification_manager.cpp
1/***************************************************************************
2 *
3 * Project: OpenCPN
4 * Purpose: Notification Manager
5 * Author: David Register
6 *
7 ***************************************************************************
8 * Copyright (C) 2025 by David S. Register *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the *
22 * Free Software Foundation, Inc., *
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24 **************************************************************************/
25#include <cmath>
26#include <memory>
27#include <vector>
28#include <fstream>
29#include <sstream>
30#include <wx/dir.h>
31
32#include "model/base_platform.h"
33#include "model/navutil_base.h"
34#include "model/notification.h"
36#include "wx/filename.h"
37#include "model/datetime.h"
38#include "model/comm_appmsg_bus.h"
39
40extern BasePlatform* g_BasePlatform;
41extern std::shared_ptr<ObservableListener> ack_listener;
42
43NotificationManager& NotificationManager::GetInstance() {
44 static NotificationManager instance;
45 return instance;
46}
47
48NotificationManager::NotificationManager() {
49 m_timeout_timer.Bind(wxEVT_TIMER, &NotificationManager::OnTimer, this,
50 m_timeout_timer.GetId());
51 m_timeout_timer.Start(1000, wxTIMER_CONTINUOUS);
52}
53
54void NotificationManager::OnTimer(wxTimerEvent& event) {
55 for (auto note : active_notifications) {
56 if (note->GetTimeoutLeft() > 0) {
57 note->DecrementTimoutCount();
58 }
59 }
60 for (auto note : active_notifications) {
61 if (note->GetTimeoutLeft() == 0) {
62 AcknowledgeNotification(note->GetGuid());
63 note->DecrementTimoutCount();
64 break;
65 }
66 }
67}
68
69void NotificationManager::ScrubNotificationDirectory(int days_to_retain) {
70 wxString note_directory = g_BasePlatform->GetPrivateDataDir() +
71 wxFileName::GetPathSeparator() + "notifications" +
72 wxFileName::GetPathSeparator();
73 if (!wxDirExists(note_directory)) return;
74
75 wxDateTime now = wxDateTime::Now();
76 wxArrayString file_list;
77 wxDir::GetAllFiles(note_directory, &file_list);
78 for (size_t i = 0; i < file_list.GetCount(); i++) {
79 wxFileName fn(file_list[i]);
80 wxTimeSpan age = now.Subtract(fn.GetModificationTime());
81 if (age.IsLongerThan(wxTimeSpan(days_to_retain * 24))) {
82 wxRemoveFile(file_list[i]);
83 }
84 }
85}
86
87void NotificationManager::PersistNotificationAsFile(
88 const std::shared_ptr<Notification> _notification) {
89 wxString note_directory = g_BasePlatform->GetPrivateDataDir() +
90 wxFileName::GetPathSeparator() + "notifications" +
91 wxFileName::GetPathSeparator();
92 if (!wxDirExists(note_directory)) wxMkdir(note_directory);
93 wxString severity_prefix = "Info_";
94 NotificationSeverity severity = _notification->GetSeverity();
95 if (severity == NotificationSeverity::kWarning)
96 severity_prefix = "Warning_";
97 else if (severity == NotificationSeverity::kCritical)
98 severity_prefix = "Critical_";
99 wxString file_name = wxString(_notification.get()->GetGuid().c_str());
100 file_name.Prepend(severity_prefix);
101 file_name.Prepend(note_directory);
102 file_name += ".txt";
103
104 wxDateTime act_time = wxDateTime(_notification->GetActivateTime());
105 wxString stime = wxString::Format(
106 "%s", ocpn::toUsrDateTimeFormat(
107 act_time, DateTimeFormatOptions().SetFormatString(
108 "$short_date $24_hour_minutes_seconds")));
109
110 std::stringstream ss;
111 ss << stime.ToStdString() << std::endl;
112 ss << _notification->GetMessage() << std::endl;
113
114 std::ofstream outputFile(file_name.ToStdString().c_str(), std::ios::out);
115 if (outputFile.is_open()) {
116 outputFile << ss.str();
117 }
118}
119
121 int rv = 0;
122 for (auto note : active_notifications) {
123 int severity = static_cast<int>(note->GetSeverity());
124 if (severity > rv) rv = severity;
125 }
126 return static_cast<NotificationSeverity>(rv);
127}
128
129std::string NotificationManager::AddNotification(
130 std::shared_ptr<Notification> _notification) {
131 active_notifications.push_back(_notification);
132 PersistNotificationAsFile(_notification);
134
135 // Send notification to listeners
136 auto msg = std::make_shared<NotificationMsg>("POST", _notification);
137 AppMsgBus::GetInstance().Notify(std::move(msg));
138
139 return _notification->GetGuid();
140}
141
142std::string NotificationManager::AddNotification(NotificationSeverity _severity,
143 const std::string& _message,
144 int _timeout_secs) {
145 auto notification =
146 std::make_shared<Notification>(_severity, _message, _timeout_secs);
147 return AddNotification(notification);
148}
149
150bool NotificationManager::AcknowledgeNotification(const std::string& GUID) {
151 if (!GUID.length()) return false;
152
153 size_t target_message_hash = 0;
154 for (auto it = active_notifications.begin();
155 it != active_notifications.end();) {
156 if ((*it)->GetGuid() == GUID) {
157 target_message_hash = (*it)->GetStringHash();
158 break;
159 } else
160 ++it;
161 }
162 if (!target_message_hash) return false;
163
164 // erase multiple notifications with identical message_hash
165 bool rv = false;
166 bool done = false;
167 while (!done && active_notifications.size()) {
168 for (auto it = active_notifications.begin();
169 it != active_notifications.end();) {
170 if ((*it)->GetStringHash() == target_message_hash) {
171 // Send notification to listeners
172 auto msg = std::make_shared<NotificationMsg>("ACK", *it);
173 AppMsgBus::GetInstance().Notify(std::move(msg));
174
175 // Drop the notification
176 active_notifications.erase(it);
177
178 rv = true;
179 break;
180 } else
181 ++it;
182 if (it == active_notifications.end()) done = true;
183 }
184 }
185
186 if (rv) {
188 }
189
190 return rv;
191}
192
193bool NotificationManager::AcknowledgeAllNotifications() {
194 bool rv = false;
195
196 while (active_notifications.size()) {
197 AcknowledgeNotification(active_notifications[0]->GetGuid());
198 rv = true;
199 }
200
201 return rv;
202}
wxString & GetPrivateDataDir()
Return dir path for opencpn.log, etc., respecting -c cli option.
void Notify(const std::shared_ptr< const AppMsg > &msg)
Send message to everyone listening to given message type.
const void Notify()
Notify all listeners, no data supplied.
The global list of user notifications, a singleton.
bool AcknowledgeNotification(const std::string &guid)
User ack on a notification which eventually will remove it.
EventVar evt_notificationlist_change
Notified without data when a notification is added or removed.
NotificationSeverity GetMaxSeverity()
Return max severity among current active notifications.
Class Notification.
Class NotificationManager.
Configuration options for date and time formatting.