r577: 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.7 2001/02/23 21:58:32 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(MSG_BACKGROUND_SUPERVISOR_CANCEL, ReconstructorSupervisor::OnCancel)
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_pDialogProgress(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 = 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   m_pProjView = pProjView;
87   m_pProjDoc = pProjView->GetDocument();
88 }
89
90 // Static function
91 void 
92 BackgroundSupervisor::cancelSupervisor (BackgroundSupervisor* pSupervisor)
93 {
94   wxCommandEvent cancelEvent (wxEVT_COMMAND_MENU_SELECTED, MSG_BACKGROUND_SUPERVISOR_CANCEL);
95   wxPostEvent (pSupervisor, cancelEvent);
96 }
97
98
99 bool
100 ReconstructorSupervisor::start()
101 {
102   if (m_bFail)
103     return false;
104   
105   if (! theApp->getUseBackgroundTasks())
106     m_pDialogProgress = new wxProgressDialog (_T("Filtered Backprojection"), _T("Reconstruction Progress"), m_iTotalViews, m_pProjView->getFrame(), wxPD_CAN_ABORT | wxPD_AUTO_HIDE);
107   else {
108     std::string strLabel ("Reconstructing ");
109     strLabel += m_pProjView->GetFrame()->GetTitle();
110     wxCommandEvent addTaskEvent (wxEVT_COMMAND_MENU_SELECTED, MSG_BACKGROUND_SUPERVISOR_ADD);
111     addTaskEvent.SetString (strLabel.c_str());
112     addTaskEvent.SetInt (m_iTotalViews);
113     addTaskEvent.SetClientData (this);
114     wxPostEvent (theApp->getBackgroundManager(), addTaskEvent);
115     wxPostEvent (m_pProjDoc, addTaskEvent);
116   }
117   
118   m_iRunning = m_iNumThreads;
119   m_iViewsDone = 0;
120   m_pTimer = new Timer;
121   
122   // starting all threads
123   for (int i = 0; i < m_iNumThreads; i++)
124     m_vecpThread[i]->Run();
125     
126   return true;
127 }
128
129 void
130 ReconstructorSupervisor::OnCancel(wxCommandEvent& event)
131 {
132   if (isDone() || m_bCancelled)
133     return;
134   m_bCancelled = true;
135   m_bCancelling = false;
136   cleanUp();
137 }
138
139 void
140 ReconstructorSupervisor::cleanUp()
141 {
142   wxCriticalSection cleanSection;
143   cleanSection.Enter();
144   
145   m_critsectThreadContainer.Enter();
146   for (int i = 0; i < m_iNumThreads; i++) 
147     if (m_vecpThread[i] && m_vecpThread[i]->IsRunning()) {
148       m_vecpThread[i]->Pause();
149       m_vecpThread[i]->Delete();
150     }
151     
152   for (i = 0; i < m_iNumThreads; i++) {
153       delete m_vecpChildImageFile[i];
154       m_vecpChildImageFile[i] = NULL;
155     }    
156   m_critsectThreadContainer.Leave();
157     
158     m_iNumThreads = 0;
159     m_iRunning = 0;
160     delete m_pDialogProgress;
161     delete m_pTimer;
162     m_pDialogProgress = NULL;
163     setDone();
164
165     wxCommandEvent doneEvent (wxEVT_COMMAND_MENU_SELECTED, MSG_BACKGROUND_SUPERVISOR_REMOVE);
166     doneEvent.SetClientData (this);
167     wxPostEvent (theApp->getBackgroundManager(), doneEvent);
168     wxPostEvent (m_pProjDoc, doneEvent);
169
170     cleanSection.Leave();
171 }
172
173 void
174 ReconstructorSupervisor::onDone()
175 {
176   wxCriticalSection doneSection;
177   doneSection.Enter();
178   
179
180   ImageFileDocument* pReconDoc = theApp->newImageDoc();
181   if (! pReconDoc) {
182     sys_error (ERR_SEVERE, "Unable to create image file");
183     doneSection.Leave();
184     return;
185   }
186     
187   ImageFile* pImageFile = getImageFile();
188   pReconDoc->setImageFile (pImageFile);
189   if (theApp->getAskDeleteNewDocs())
190     pReconDoc->Modify (true);
191   pReconDoc->UpdateAllViews (m_pProjView);
192   if (ImageFileView* rasterView = pReconDoc->getView()) {
193     rasterView->OnUpdate (rasterView, NULL);
194     rasterView->getFrame()->SetFocus();
195     rasterView->getFrame()->Show(true);
196   }
197   *theApp->getLog() << m_strLabel << "\n";
198   pImageFile->labelAdd (m_pProjView->GetDocument()->getProjections().getLabel());
199   pImageFile->labelAdd (m_strLabel.c_str(), m_pTimer->timerEnd());
200   
201   doneSection.Leave();
202   cleanUp();
203 }
204
205 void
206 ReconstructorSupervisor::OnWorkerUnitTick (wxCommandEvent& event)
207 {
208     ++m_iViewsDone;
209     
210 #ifdef DEBUG
211     if (theApp->getVerboseLogging())
212       *theApp->getLog() << "Views done: " << static_cast<int>(m_iViewsDone) <<"\n";
213 #endif
214     
215     if (m_pDialogProgress)
216       m_bCancelling = ! m_pDialogProgress->Update (m_iViewsDone - 1);
217     else {
218       wxCommandEvent addTaskEvent (wxEVT_COMMAND_MENU_SELECTED, MSG_BACKGROUND_SUPERVISOR_UNIT_TICK);
219       addTaskEvent.SetInt (m_iViewsDone - 1);
220       addTaskEvent.SetClientData (this);
221       wxPostEvent (theApp->getBackgroundManager(), addTaskEvent);
222     }
223 }
224
225 void
226 ReconstructorSupervisor::OnWorkerDone (wxCommandEvent& event)
227 {
228   m_iRunning--;
229   wxASSERT (m_iRunning >= 0);
230   m_critsectThreadContainer.Enter();
231   m_vecpThread[event.GetInt()] = NULL;
232   m_critsectThreadContainer.Leave();
233
234 #ifdef DEBUG
235   if (theApp->getVerboseLogging()) {
236     wxString msg;
237     msg.Printf("Reconstructor Supervisor: Thread finished. Remaining threads: %d\n", m_iRunning);  
238     wxCommandEvent eventLog (wxEVT_COMMAND_MENU_SELECTED, MAINMENU_LOG_EVENT );
239     eventLog.SetString( msg );
240     wxPostEvent( theApp->getMainFrame(), eventLog ); // send log event
241   }
242 #endif
243   if (m_iRunning <= 0) {
244     onDone();
245     return;
246   }
247 }
248
249 void
250 ReconstructorSupervisor::OnWorkerFail (wxCommandEvent& event)
251 {
252   if (isDone())
253     return;
254
255   m_iRunning--;
256   m_critsectThreadContainer.Enter();
257   m_vecpThread[event.GetInt()] = NULL;
258   m_critsectThreadContainer.Leave();
259   wxCommandEvent eventLog( wxEVT_COMMAND_MENU_SELECTED, MAINMENU_LOG_EVENT );
260   eventLog.SetString( event.GetString() );
261   wxPostEvent( theApp->getMainFrame(), eventLog ); // send log event
262
263   onDone();
264 }
265
266
267
268 ImageFile*
269 ReconstructorSupervisor::getImageFile()
270 {
271   ImageFile* pImageFile = new ImageFile (m_iImageNX, m_iImageNY);
272   pImageFile->arrayDataClear();
273   ImageFileArray pArray = pImageFile->getArray();
274   
275   int i;
276   for (i = 0; i < m_iNumThreads; i++) {
277     ImageFileArrayConst pChildArray = m_vecpChildImageFile[i]->getArray();
278     for (int ix = 0; ix < m_iImageNX; ix++)
279       for (int iy = 0; iy < m_iImageNY; iy++)
280         pArray[ix][iy] += pChildArray[ix][iy];
281   }
282   
283   return (pImageFile);
284 }
285
286 bool
287 ReconstructorSupervisor::testDone()
288 {
289   return (m_iRunning <= 0 ? true : false);
290 }
291
292
293 ReconstructorSupervisor::~ReconstructorSupervisor()
294 {
295 }
296
297
298 ReconstructorWorker::ReconstructorWorker 
299 (ReconstructorSupervisor* pSupervisor, ProjectionFileView* pProjView, ImageFile* pImageFile, int iThread, int iStartView, int iNumViews, 
300                                             const char* pszFilterName, double dFilterParam, const char* pszFilterMethod, 
301                                             int iZeropad, const char* pszFilterGenerationName, const char* pszInterpName, int iInterpParam,
302                                             const char* pszBackprojectName)
303     : m_pSupervisor(pSupervisor), m_iStartView(iStartView), m_iNumViews(iNumViews), m_iThread(iThread), 
304     m_pProjView(pProjView), m_pImageFile(pImageFile), m_strFilterName(pszFilterName), m_dFilterParam(dFilterParam), m_strFilterMethod(pszFilterMethod),
305     m_iZeropad(iZeropad), m_strFilterGenerationName(pszFilterGenerationName), m_strInterpName(pszInterpName),
306     m_iInterpParam(iInterpParam), m_strBackprojectName(pszBackprojectName),
307     wxThread(wxTHREAD_DETACHED)
308 {
309 }
310
311 wxThread::ExitCode
312 ReconstructorWorker::Entry ()
313 {
314   Reconstructor* pReconstructor = new Reconstructor (m_pProjView->GetDocument()->getProjections(), 
315     *m_pImageFile, m_strFilterName.c_str(), m_dFilterParam, m_strFilterMethod.c_str(), m_iZeropad, 
316     m_strFilterGenerationName.c_str(), m_strInterpName.c_str(), m_iInterpParam, m_strBackprojectName.c_str(), Trace::TRACE_NONE);
317   if (pReconstructor->fail()) {
318       wxString msg;
319       msg.Printf("Unable to make reconstructor: %s", pReconstructor->failMessage());  
320       wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, BackgroundSupervisor::MSG_WORKER_THREAD_FAIL );
321       event.SetString( msg );
322       wxPostEvent( theApp->getMainFrame(), event ); // send in a thread-safe way
323       return reinterpret_cast<wxThread::ExitCode>(-1);
324   }
325
326   wxCommandEvent eventProgress (wxEVT_COMMAND_MENU_SELECTED, BackgroundSupervisor::MSG_WORKER_THREAD_UNIT_TICK);
327
328   for (int iView = 0; iView < m_iNumViews; iView++) {
329     if (TestDestroy()) {
330 #ifdef DEBUG
331       wxString msg;
332       msg.Printf("Received destroy message view #%d\n", iView);  
333       wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, MAINMENU_LOG_EVENT );
334       event.SetString( msg );
335       wxPostEvent( theApp->getMainFrame(), event ); // send in a thread-safe way
336 #endif
337       delete pReconstructor;
338       return reinterpret_cast<wxThread::ExitCode>(-1);
339     }
340     pReconstructor->reconstructView (iView + m_iStartView, 1);
341     wxPostEvent (m_pSupervisor, eventProgress);
342   }
343   pReconstructor->postProcessing();
344   
345   wxCommandEvent eventDone (wxEVT_COMMAND_MENU_SELECTED, BackgroundSupervisor::MSG_WORKER_THREAD_DONE);
346   eventProgress.SetInt (m_iThread); // Send back thread# that has finished
347   wxPostEvent (m_pSupervisor, eventDone);
348   
349   delete pReconstructor;
350   return reinterpret_cast<wxThread::ExitCode>(0);
351 }
352
353 void
354 ReconstructorWorker::OnExit ()
355 {
356 }