r576: no message
[ctsim.git] / src / threadrecon.cpp
1 /*****************************************************************************
2 ** FILE IDENTIFICATION
3 **
4 **   Name:          threadrecon.cpp
5 **   Purpose:       Threaded reconstruction class
6 **   Programmer:    Kevin Rosenberg
7 **   Date Started:  February 2001
8 **
9 **  This is part of the CTSim program
10 **  Copyright (C) 1983-2001 Kevin Rosenberg
11 **
12 **  $Id: threadrecon.cpp,v 1.6 2001/02/23 18:56:56 kevin Exp $
13 **
14 **  This program is free software; you can redistribute it and/or modify
15 **  it under the terms of the GNU General Public License (version 2) as
16 **  published by the Free Software Foundation.
17 **
18 **  This program is distributed in the hope that it will be useful,
19 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
20 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 **  GNU General Public License for more details.
22 **
23 **  You should have received a copy of the GNU General Public License
24 **  along with this program; if not, write to the Free Software
25 **  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26 ******************************************************************************/
27
28 #include "wx/wxprec.h"
29
30 #ifndef WX_PRECOMP
31 #include "wx/wx.h"
32 #endif
33
34 #include "ct.h"
35 #include "ctsim.h"
36 #include "docs.h"
37 #include "views.h"
38 #include "threadrecon.h"
39 #include "backgroundmgr.h"
40
41 #if defined(HAVE_CONFIG_H)
42 #include "config.h"
43 #endif
44
45
46 IMPLEMENT_DYNAMIC_CLASS(ReconstructorSupervisor, BackgroundSupervisor)
47 BEGIN_EVENT_TABLE(ReconstructorSupervisor, BackgroundSupervisor)
48 EVT_MENU(RECONSTRUCTION_THREAD_EVENT, ReconstructorSupervisor::OnThreadEvent)
49 EVT_MENU(MSG_WORKER_THREAD_FAIL, ReconstructorSupervisor::OnWorkerFail)
50 EVT_MENU(MSG_WORKER_THREAD_DONE, ReconstructorSupervisor::OnWorkerDone)
51 EVT_MENU(MSG_WORKER_THREAD_UNIT_TICK, ReconstructorSupervisor::OnWorkerUnitTick)
52 END_EVENT_TABLE()
53
54
55 ReconstructorSupervisor::ReconstructorSupervisor (ProjectionFileView* pProjView, 
56                                               int iImageNX, int iImageNY, const char* pszFilterName, double dFilterParam, const char* pszFilterMethod, 
57                                               int iZeropad, const char* pszFilterGenerationName, const char* pszInterpName, int iInterpParam,
58                                               const char* pszBackprojectName, const char* const pszLabel)
59                                               : m_pProjView(pProjView), m_pDialogProgress(NULL), m_pGauge(NULL), m_bFail(false), m_iNumThreads(0), m_iImageNX(iImageNX), 
60                                               m_iImageNY(iImageNY), m_strLabel(pszLabel), m_pTimer(NULL), m_bCancelled(false), m_bCancelling(false), 
61                                               BackgroundSupervisor()
62 {
63   m_iNumThreads = theApp->getNumberCPU();
64   //  ++m_iNumThreads;
65   m_iTotalViews = m_pProjView->GetDocument()->getProjections().nView();
66   int iBaseViews = m_iTotalViews / m_iNumThreads;
67   int iExtraViews = m_iTotalViews % m_iNumThreads;
68   
69   m_vecpChildImageFile.reserve (m_iNumThreads);
70   m_vecpThread.reserve (m_iNumThreads);
71   
72   for (unsigned int iProc = 0; iProc < m_iNumThreads; iProc++) {
73     m_vecpChildImageFile[iProc] = new ImageFile (iImageNX, iImageNY);
74     
75     int iStartView = iProc * iBaseViews;
76     int iNumViews = iBaseViews;
77     if (iProc < iExtraViews)
78       ++iNumViews;
79       m_vecpThread[iProc] = new ReconstructorWorker (this, pProjView, m_vecpChildImageFile[iProc], iProc, iStartView, iNumViews,
80       pszFilterName, dFilterParam, pszFilterMethod, iZeropad, pszFilterGenerationName, pszInterpName, iInterpParam, pszBackprojectName);
81     if (m_vecpThread[iProc]->Create () != wxTHREAD_NO_ERROR) {
82       m_bFail = true;
83       break;
84     }
85   }
86   
87 }
88
89
90 bool
91 ReconstructorSupervisor::start()
92 {
93   if (m_bFail)
94     return false;
95   
96   m_pProjView->GetDocument()->addReconstructor (this);
97   if (! theApp->getUseBackgroundTasks())
98     m_pDialogProgress = new wxProgressDialog (_T("Filtered Backprojection"), _T("Reconstruction Progress"), m_iTotalViews, m_pProjView->getFrame(), wxPD_CAN_ABORT | wxPD_AUTO_HIDE);
99   else {
100     std::string strLabel ("Reconstructing ");
101     strLabel += m_pProjView->GetFrame()->GetTitle();
102     m_pGauge = theApp->getBackgroundManager()->addTask (this, m_iTotalViews, strLabel.c_str());
103   }
104   
105   m_iRunning = m_iNumThreads;
106   m_iViewsDone = 0;
107   m_pTimer = new Timer;
108   
109   // starting all threads
110   for (int i = 0; i < m_iNumThreads; i++)
111     m_vecpThread[i]->Run();
112   
113   if (m_bCancelled)
114     return false;
115   
116   return true;
117 }
118
119 void
120 ReconstructorSupervisor::cancel()
121 {
122   if (isDone() || m_bCancelled)
123     return;
124   m_bCancelled = true;
125   m_bCancelling = false;
126   cleanUp();
127 }
128
129 void
130 ReconstructorSupervisor::cleanUp()
131 {
132   wxCriticalSection cleanSection;
133   cleanSection.Enter();
134   
135   m_critsectThreadContainer.Enter();
136   for (int i = 0; i < m_iNumThreads; i++) 
137     if (m_vecpThread[i] && m_vecpThread[i]->IsRunning()) {
138       m_vecpThread[i]->Pause();
139       m_vecpThread[i]->Delete();
140     }
141   m_critsectThreadContainer.Leave();
142     
143   for (i = 0; i < m_iNumThreads; i++) {
144       delete m_vecpChildImageFile[i];
145       m_vecpChildImageFile[i] = NULL;
146     }    
147     
148     m_iNumThreads = 0;
149     m_iRunning = 0;
150     delete m_pDialogProgress;
151     delete m_pTimer;
152     m_pDialogProgress = NULL;
153     m_pGauge = NULL;
154     setDone();
155     m_pProjView->GetDocument()->removeReconstructor (this);
156     theApp->getBackgroundManager()->taskDone (this);
157     
158     cleanSection.Leave();
159 }
160
161 void
162 ReconstructorSupervisor::onDone()
163 {
164   wxCriticalSection doneSection;
165   doneSection.Enter();
166   
167   m_pProjView->GetDocument()->removeReconstructor (this);
168   ImageFileDocument* pReconDoc = theApp->newImageDoc();
169   if (! pReconDoc) {
170     sys_error (ERR_SEVERE, "Unable to create image file");
171     doneSection.Leave();
172     return;
173   }
174     
175   ImageFile* pImageFile = getImageFile();
176   pReconDoc->setImageFile (pImageFile);
177   if (theApp->getAskDeleteNewDocs())
178     pReconDoc->Modify (true);
179   pReconDoc->UpdateAllViews (m_pProjView);
180   if (ImageFileView* rasterView = pReconDoc->getView()) {
181     rasterView->OnUpdate (rasterView, NULL);
182     rasterView->getFrame()->SetFocus();
183     rasterView->getFrame()->Show(true);
184   }
185   *theApp->getLog() << m_strLabel << "\n";
186   pImageFile->labelAdd (m_pProjView->GetDocument()->getProjections().getLabel());
187   pImageFile->labelAdd (m_strLabel.c_str(), m_pTimer->timerEnd());
188   
189   doneSection.Leave();
190   cleanUp();
191 }
192
193 void
194 ReconstructorSupervisor::OnWorkerUnitTick (wxCommandEvent& event)
195 {
196     ++m_iViewsDone;
197     
198 #ifdef DEBUG
199     if (theApp->getVerboseLogging())
200       *theApp->getLog() << "Views done: " << static_cast<int>(m_iViewsDone) <<"\n";
201 #endif
202     
203     if (m_pDialogProgress)
204       m_bCancelling = ! m_pDialogProgress->Update (m_iViewsDone - 1);
205     else if (m_pGauge) {
206       m_pGauge->SetValue (m_iViewsDone - 1); 
207       m_bCancelling = theApp->getBackgroundManager()->isCancelling(this);
208     }
209     if (! isDone() && m_bCancelling) {
210       cancel();
211       return;
212     }
213 }
214
215 void
216 ReconstructorSupervisor::OnWorkerDone (wxCommandEvent& event)
217 {
218   if (isDone())
219     return;
220
221   m_iRunning--;
222   m_critsectThreadContainer.Enter();
223   m_vecpThread[event.GetInt()] = NULL;
224   m_critsectThreadContainer.Leave();
225   wxString msg;
226   msg.Printf("Reconstructor Supervisor: Thread finished. Remaining threads: %d\n", m_iRunning);  
227   wxCommandEvent eventLog (wxEVT_COMMAND_MENU_SELECTED, MAINMENU_LOG_EVENT );
228   eventLog.SetString( msg );
229   wxPostEvent( theApp->getMainFrame(), eventLog ); // send log event
230
231   if (m_iRunning <= 0) {
232     onDone();
233     return;
234   }
235 }
236
237 void
238 ReconstructorSupervisor::OnWorkerFail (wxCommandEvent& event)
239 {
240   if (isDone())
241     return;
242
243   m_iRunning--;
244   m_critsectThreadContainer.Enter();
245   m_vecpThread[event.GetInt()] = NULL;
246   m_critsectThreadContainer.Leave();
247   wxCommandEvent eventLog( wxEVT_COMMAND_MENU_SELECTED, MAINMENU_LOG_EVENT );
248   eventLog.SetString( event.GetString() );
249   wxPostEvent( theApp->getMainFrame(), eventLog ); // send log event
250
251   onDone();
252 }
253
254
255 void
256 ReconstructorSupervisor::OnThreadEvent (wxCommandEvent& event)
257 {
258       wxString msg;
259       msg.Printf("Reconstructor Supervisor: Received event #%d\n", event.GetInt());  
260       wxCommandEvent eventLog( wxEVT_COMMAND_MENU_SELECTED, MAINMENU_LOG_EVENT );
261       eventLog.SetString( msg );
262       wxPostEvent( theApp->getMainFrame(), eventLog ); // send log event
263 }
264
265 ImageFile*
266 ReconstructorSupervisor::getImageFile()
267 {
268   ImageFile* pImageFile = new ImageFile (m_iImageNX, m_iImageNY);
269   pImageFile->arrayDataClear();
270   ImageFileArray pArray = pImageFile->getArray();
271   
272   int i;
273   for (i = 0; i < m_iNumThreads; i++) {
274     ImageFileArrayConst pChildArray = m_vecpChildImageFile[i]->getArray();
275     for (int ix = 0; ix < m_iImageNX; ix++)
276       for (int iy = 0; iy < m_iImageNY; iy++)
277         pArray[ix][iy] += pChildArray[ix][iy];
278   }
279   
280   return (pImageFile);
281 }
282
283 bool
284 ReconstructorSupervisor::testDone()
285 {
286   return (m_iRunning <= 0 ? true : false);
287 }
288
289
290 ReconstructorSupervisor::~ReconstructorSupervisor()
291 {
292 }
293
294
295 ReconstructorWorker::ReconstructorWorker 
296 (ReconstructorSupervisor* pSupervisor, ProjectionFileView* pProjView, ImageFile* pImageFile, int iThread, int iStartView, int iNumViews, 
297                                             const char* pszFilterName, double dFilterParam, const char* pszFilterMethod, 
298                                             int iZeropad, const char* pszFilterGenerationName, const char* pszInterpName, int iInterpParam,
299                                             const char* pszBackprojectName)
300     : m_pSupervisor(pSupervisor), m_iStartView(iStartView), m_iNumViews(iNumViews), m_iThread(iThread), 
301     m_pProjView(pProjView), m_pImageFile(pImageFile), m_strFilterName(pszFilterName), m_dFilterParam(dFilterParam), m_strFilterMethod(pszFilterMethod),
302     m_iZeropad(iZeropad), m_strFilterGenerationName(pszFilterGenerationName), m_strInterpName(pszInterpName),
303     m_iInterpParam(iInterpParam), m_strBackprojectName(pszBackprojectName),
304     wxThread(wxTHREAD_DETACHED)
305 {
306 }
307
308 wxThread::ExitCode
309 ReconstructorWorker::Entry ()
310 {
311   Reconstructor* pReconstructor = new Reconstructor (m_pProjView->GetDocument()->getProjections(), 
312     *m_pImageFile, m_strFilterName.c_str(), m_dFilterParam, m_strFilterMethod.c_str(), m_iZeropad, 
313     m_strFilterGenerationName.c_str(), m_strInterpName.c_str(), m_iInterpParam, m_strBackprojectName.c_str(), Trace::TRACE_NONE);
314   if (pReconstructor->fail()) {
315       wxString msg;
316       msg.Printf("Unable to make reconstructor: %s", pReconstructor->failMessage());  
317       wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, BackgroundSupervisor::MSG_WORKER_THREAD_FAIL );
318       event.SetString( msg );
319       wxPostEvent( theApp->getMainFrame(), event ); // send in a thread-safe way
320       return reinterpret_cast<wxThread::ExitCode>(-1);
321   }
322
323   wxCommandEvent eventProgress (wxEVT_COMMAND_MENU_SELECTED, BackgroundSupervisor::MSG_WORKER_THREAD_UNIT_TICK);
324
325   for (int iView = 0; iView < m_iNumViews; iView++) {
326     if (TestDestroy()) {
327 #ifdef DEBUG
328       wxString msg;
329       msg.Printf("Received destroy message view #%d\n", iView);  
330       wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, MAINMENU_LOG_EVENT );
331       event.SetString( msg );
332       wxPostEvent( theApp->getMainFrame(), event ); // send in a thread-safe way
333 #endif
334       delete pReconstructor;
335       return reinterpret_cast<wxThread::ExitCode>(-1);
336     }
337     pReconstructor->reconstructView (iView + m_iStartView, 1);
338     wxPostEvent (m_pSupervisor, eventProgress);
339   }
340   pReconstructor->postProcessing();
341   
342   wxCommandEvent eventDone (wxEVT_COMMAND_MENU_SELECTED, BackgroundSupervisor::MSG_WORKER_THREAD_DONE);
343   eventProgress.SetInt (m_iThread); // Send back thread# that has finished
344   wxPostEvent (m_pSupervisor, eventDone);
345   
346   delete pReconstructor;
347   return reinterpret_cast<wxThread::ExitCode>(0);
348 }
349
350 void
351 ReconstructorWorker::OnExit ()
352 {
353 }