OpenCPN Partial API docs
Loading...
Searching...
No Matches
senc_manager.cpp
Go to the documentation of this file.
1/**************************************************************************
2 * Copyright (C) 2010 by David S. Register *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
16 **************************************************************************/
17
24// For compilers that support precompilation, includes "wx.h".
25#include "wx/wxprec.h"
26#ifndef WX_PRECOMP
27#include "wx/wx.h"
28#endif
29
30#include "senc_manager.h"
31
32#include "model/config_vars.h"
33
34#include "s57chart.h"
35#include "o_senc.h"
36#include "chartbase.h"
37#include "chcanv.h"
38#include "s57class_registrar.h"
39#include "top_frame.h"
40
42
43namespace {
44constexpr int kThreadWaitSeconds = 5;
45}
46
47//----------------------------------------------------------------------------------
48// SENCJobTicket Implementation
49//----------------------------------------------------------------------------------
50SENCJobTicket::SENCJobTicket() {
51 m_chart = nullptr;
52 m_SENCResult = SENC_BUILD_INACTIVE;
53 m_status = THREAD_INACTIVE;
54 m_completion_posted = false;
55}
56
57const wxEventType wxEVT_OCPN_BUILDSENCTHREAD = wxNewEventType();
58
59//----------------------------------------------------------------------------------
60// OCPN_BUILDSENC_ThreadEvent Implementation
61//----------------------------------------------------------------------------------
62OCPN_BUILDSENC_ThreadEvent::OCPN_BUILDSENC_ThreadEvent(wxEventType commandType,
63 int id)
64 : wxEvent(id, commandType) {
65 stat = 0;
66 m_ticket = nullptr;
67}
68
69OCPN_BUILDSENC_ThreadEvent::~OCPN_BUILDSENC_ThreadEvent() {}
70
71wxEvent *OCPN_BUILDSENC_ThreadEvent::Clone() const {
73 newevent->stat = this->stat;
74 newevent->type = this->type;
75 newevent->m_ticket = this->m_ticket;
76
77 return newevent;
78}
79
80//----------------------------------------------------------------------------------
81// SENCThreadManager Implementation
82//----------------------------------------------------------------------------------
83SENCThreadManager::SENCThreadManager() : m_shutting_down(false) {
84 int nCPU = wxMax(1, wxThread::GetCPUCount());
85 if (g_nCPUCount > 0) nCPU = g_nCPUCount;
86
87 if (nCPU < 1) nCPU = 1;
88
89 m_max_jobs = wxMax(nCPU - 1, 1);
90
91 wxLogDebug("SENC: nCPU: %d m_max_jobs :%d\n", nCPU, m_max_jobs);
92
93 Connect(
95 (wxObjectEventFunction)(wxEventFunction)&SENCThreadManager::OnEvtThread);
96}
97
98SENCThreadManager::~SENCThreadManager() { ClearJobList(); }
99
100bool SENCThreadManager::IsShuttingDown() const { return m_shutting_down; }
101
102int SENCThreadManager::GetRunningJobCount() {
103 wxCriticalSectionLocker lock(m_list_mutex);
104 int nRunning = 0;
105 for (SENCJobTicket *ticket : ticket_list) {
106 if (ticket->m_status == THREAD_STARTED) nRunning++;
107 }
108 return nRunning;
109}
110
111void SENCThreadManager::UpdateAlertString() {
112 int nRunning = 0;
113 size_t jobCount = 0;
114 {
115 wxCriticalSectionLocker lock(m_list_mutex);
116 jobCount = ticket_list.size();
117 for (SENCJobTicket *ticket : ticket_list) {
118 if (ticket->m_status == THREAD_STARTED) nRunning++;
119 }
120 }
121
122 if (auto tfPtr = top_frame::Get()) {
123 if (nRunning) {
124 wxString count;
125 count.Printf(" %ld", static_cast<long>(jobCount));
126 tfPtr->SetAlertString(_("Preparing vector chart ") + count);
127 } else {
128 tfPtr->SetAlertString("");
129 }
130 }
131}
132
133void SENCThreadManager::ClearJobList() {
134 m_shutting_down = true;
135
136 wxDateTime end = wxDateTime::Now();
137 end.Add(wxTimeSpan::Seconds(kThreadWaitSeconds));
138
139 while (wxDateTime::Now() < end && GetRunningJobCount() > 0) {
140 wxSleep(1);
141 wxYield();
142 }
143
144 std::vector<SENCJobTicket *> tickets_to_delete;
145 {
146 wxCriticalSectionLocker lock(m_list_mutex);
147 auto collect = [&](std::vector<SENCJobTicket *> &list) {
148 for (SENCJobTicket *ticket : list) tickets_to_delete.push_back(ticket);
149 list.clear();
150 };
151 collect(ticket_list);
152 collect(completing_list);
153 }
154
155 for (SENCJobTicket *ticket : tickets_to_delete) delete ticket;
156}
157
158SENCThreadStatus SENCThreadManager::ScheduleJob(SENCJobTicket *ticket) {
159 if (m_shutting_down) return THREAD_INACTIVE;
160
161 {
162 wxCriticalSectionLocker lock(m_list_mutex);
163
164 for (SENCJobTicket *queued : ticket_list) {
165 if (queued->m_FullPath000 == ticket->m_FullPath000) return THREAD_PENDING;
166 }
167
168 ticket->m_status = THREAD_PENDING;
169 ticket->m_completion_posted = false;
170 ticket_list.push_back(ticket);
171 }
172
173 StartTopJob();
174 return THREAD_PENDING;
175}
176
177void SENCThreadManager::StartTopJob() {
178 if (m_shutting_down) return;
179
180 bool started_job = false;
181 do {
182 started_job = false;
183
184 {
185 wxCriticalSectionLocker lock(m_list_mutex);
186
187 int nRunning = 0;
188 for (SENCJobTicket *ticket : ticket_list) {
189 if (ticket->m_status == THREAD_STARTED) nRunning++;
190 }
191
192 if (nRunning >= m_max_jobs) break;
193
194 SENCJobTicket *startCandidate = nullptr;
195 for (SENCJobTicket *ticket : ticket_list) {
196 if (ticket->m_status == THREAD_PENDING) {
197 startCandidate = ticket;
198 break;
199 }
200 }
201
202 if (!startCandidate) break;
203
204 SENCBuildThread *thread = new SENCBuildThread(startCandidate, this);
205 startCandidate->m_status = THREAD_STARTED;
206 thread->SetPriority(20);
207 if (thread->Run() != wxTHREAD_NO_ERROR) {
208 startCandidate->m_status = THREAD_PENDING;
209 delete thread;
210 break;
211 }
212
213 started_job = true;
214 }
215 } while (started_job);
216
217 UpdateAlertString();
218}
219
220void SENCThreadManager::FinishJob(SENCJobTicket *ticket) {
221 {
222 wxCriticalSectionLocker lock(m_list_mutex);
223
224 for (size_t i = 0; i < ticket_list.size(); i++) {
225 if (ticket_list[i] == ticket) {
226 ticket->m_status = THREAD_FINISHED;
227 completing_list.push_back(ticket);
228 ticket_list.erase(ticket_list.begin() + i);
229 break;
230 }
231 }
232 }
233
234 UpdateAlertString();
235}
236
237int SENCThreadManager::GetJobCount() {
238 wxCriticalSectionLocker lock(m_list_mutex);
239 return static_cast<int>(ticket_list.size());
240}
241
242bool SENCThreadManager::IsChartInTicketlist(s57chart *chart) {
243 wxCriticalSectionLocker lock(m_list_mutex);
244 for (SENCJobTicket *ticket : ticket_list) {
245 if (ticket->m_chart == chart) return true;
246 }
247 for (SENCJobTicket *ticket : completing_list) {
248 if (ticket->m_chart == chart) return true;
249 }
250 return false;
251}
252
253bool SENCThreadManager::SetChartPointer(s57chart *chart, void *new_ptr) {
254 wxCriticalSectionLocker lock(m_list_mutex);
255 auto update = [&](std::vector<SENCJobTicket *> &list) {
256 for (SENCJobTicket *ticket : list) {
257 if (ticket->m_chart == chart) {
258 ticket->m_chart = static_cast<s57chart *>(new_ptr);
259 return true;
260 }
261 }
262 return false;
263 };
264
265 if (update(ticket_list)) return true;
266 return update(completing_list);
267}
268
269void SENCThreadManager::InvalidateChartPointer(s57chart *chart) {
270 SetChartPointer(chart, nullptr);
271}
272
273void SENCThreadManager::ReleaseCompletedTicket(SENCJobTicket *ticket) {
274 wxCriticalSectionLocker lock(m_list_mutex);
275 for (size_t i = 0; i < completing_list.size(); i++) {
276 if (completing_list[i] == ticket) {
277 completing_list.erase(completing_list.begin() + i);
278 break;
279 }
280 }
281}
282
283void SENCThreadManager::NotifyFrame(OCPN_BUILDSENC_ThreadEvent &event) {
284 if (!top_frame::Get()) return;
285
287 frame_event.stat = event.stat;
288 frame_event.type = event.type;
289 frame_event.m_ticket = event.m_ticket;
290 top_frame::Get()->GetEventHandler()->AddPendingEvent(frame_event);
291}
292
293void SENCThreadManager::OnEvtThread(OCPN_BUILDSENC_ThreadEvent &event) {
294 switch (event.type) {
295 case SENC_BUILD_DONE_NOERROR:
296 case SENC_BUILD_DONE_ERROR:
297 FinishJob(event.m_ticket);
298 StartTopJob();
299 NotifyFrame(event);
300 break;
301 default:
302 break;
303 }
304}
305
306//----------------------------------------------------------------------------------
307// SENCBuildThread Implementation
308//----------------------------------------------------------------------------------
309
310SENCBuildThread::SENCBuildThread(SENCJobTicket *ticket,
311 SENCThreadManager *manager)
312 : wxThread(wxTHREAD_DETACHED), m_manager(manager), m_ticket(ticket) {
313 m_FullPath000 = ticket->m_FullPath000;
314 m_SENCFileName = ticket->m_SENCFileName;
315 Create();
316}
317
318void *SENCBuildThread::Entry() {
319 EVENTSENCResult result = SENC_BUILD_DONE_ERROR;
320 int stat = -1;
321
322 if (!m_manager || m_manager->IsShuttingDown()) return nullptr;
323
324 try {
325 Osenc senc;
326
327 senc.setRegistrar(g_poRegistrar);
328 senc.setRefLocn(m_ticket->ref_lat, m_ticket->ref_lon);
329 senc.SetLODMeters(m_ticket->m_LOD_meters);
330 senc.setNoErrDialog(true);
331
332 m_ticket->m_SENCResult = SENC_BUILD_STARTED;
333
334 stat = senc.createSenc200(m_FullPath000, m_SENCFileName, false);
335 result = (stat == ERROR_INGESTING000) ? SENC_BUILD_DONE_ERROR
336 : SENC_BUILD_DONE_NOERROR;
337 } catch (const std::exception &e) {
338 wxLogMessage("SENC build thread exception: %s", e.what());
339 result = SENC_BUILD_DONE_ERROR;
340 stat = -1;
341 }
342
343 // Post exactly one completion event to the manager.
344 if (!m_manager || m_manager->IsShuttingDown() ||
345 m_ticket->m_completion_posted)
346 return nullptr;
347
348 m_ticket->m_completion_posted = true;
349 m_ticket->m_SENCResult = result;
350
352 event.stat = stat;
353 event.type = result;
354 event.m_ticket = m_ticket;
355 m_manager->QueueEvent(event.Clone());
356
357 return nullptr;
358}
General chart base definitions.
Generic Chart canvas base.
Manager for S57 chart SENC creation threads.
Represents an S57 format electronic navigational chart in OpenCPN.
Definition s57chart.h:90
Global variables stored in configuration file.
S57 SENC File Object.
S57 Chart Object.
const wxEventType wxEVT_OCPN_BUILDSENCTHREAD
Global instance.
SENCThreadManager * g_SencThreadManager
Global instance.
S57 Chart Object.
const wxEventType wxEVT_OCPN_BUILDSENCTHREAD
Global instance.
Abstract gFrame/MyFrame interface.