r574: 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.4 2001/02/23 02:06:02 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(ThreadedReconstructor, BackgroundTask)
47 BEGIN_EVENT_TABLE(ThreadedReconstructor, BackgroundTask)
48 EVT_MENU(RECONSTRUCTION_THREAD_EVENT, ThreadedReconstructor::OnThreadEvent)
49 END_EVENT_TABLE()
50
51
52 enum {
53   RTHREAD_UNIT_COMPLETE = -1,
54     RTHREAD_THREAD_DONE = -2,
55     RTHREAD_THREAD_CANCELLED = -3,
56 };
57
58 ThreadedReconstructor::ThreadedReconstructor (ProjectionFileView* pProjView, 
59                                               int iImageNX, int iImageNY, const char* pszFilterName, double dFilterParam, const char* pszFilterMethod, 
60                                               int iZeropad, const char* pszFilterGenerationName, const char* pszInterpName, int iInterpParam,
61                                               const char* pszBackprojectName, const char* const pszLabel)
62                                               : m_pProjView(pProjView), m_pDialogProgress(NULL), m_pGauge(NULL), m_bFail(false), m_iNumThreads(0), m_iImageNX(iImageNX), 
63                                               m_iImageNY(iImageNY), m_strLabel(pszLabel), m_pTimer(NULL), m_bCancelled(false), m_bCancelling(false), 
64                                               BackgroundTask()
65 {
66   m_iNumThreads = theApp->getNumberCPU();
67   //  ++m_iNumThreads;
68   m_iTotalViews = m_pProjView->GetDocument()->getProjections().nView();
69   int iBaseViews = m_iTotalViews / m_iNumThreads;
70   int iExtraViews = m_iTotalViews % m_iNumThreads;
71   
72   m_vecpChildImageFile.reserve (m_iNumThreads);
73   m_vecpReconstructor.reserve (m_iNumThreads);
74   m_vecpThread.reserve (m_iNumThreads);
75   
76   for (unsigned int iProc = 0; iProc < m_iNumThreads; iProc++) {
77     m_vecpChildImageFile[iProc] = new ImageFile (iImageNX, iImageNY);
78     m_vecpReconstructor[iProc] = new Reconstructor (m_pProjView->GetDocument()->getProjections(), *m_vecpChildImageFile[iProc],
79       pszFilterName, dFilterParam, pszFilterMethod, iZeropad, pszFilterGenerationName, 
80       pszInterpName, iInterpParam, pszBackprojectName, Trace::TRACE_NONE);
81     
82     int iStartView = iProc * iBaseViews;
83     int iNumViews = iBaseViews;
84     if (iProc < iExtraViews)
85       ++iNumViews;
86     m_vecpThread[iProc] = new ReconstructionThread (this, m_vecpReconstructor[iProc], iProc, iStartView, iNumViews);
87     if (m_vecpThread[iProc]->Create () != wxTHREAD_NO_ERROR) {
88       m_bFail = true;
89       break;
90     }
91   }
92   
93 }
94
95
96 bool
97 ThreadedReconstructor::start()
98 {
99   if (m_bFail)
100     return false;
101   
102   m_pProjView->GetDocument()->addReconstructor (this);
103   if (! theApp->getUseBackgroundTasks())
104     m_pDialogProgress = new wxProgressDialog (_T("Filtered Backprojection"), _T("Reconstruction Progress"), m_iTotalViews, m_pProjView->getFrame(), wxPD_CAN_ABORT | wxPD_AUTO_HIDE);
105   else {
106     std::string strLabel ("Reconstructing ");
107     strLabel += m_pProjView->GetFrame()->GetTitle();
108     m_pGauge = theApp->getBackgroundManager()->addTask (this, m_iTotalViews, strLabel.c_str());
109   }
110   
111   m_iRunning = m_iNumThreads;
112   m_iViewsDone = 0;
113   m_pTimer = new Timer;
114   
115   // starting all threads
116   for (int i = 0; i < m_iNumThreads; i++)
117     m_vecpThread[i]->Run();
118   
119   if (m_bCancelled)
120     return false;
121   
122   return true;
123 }
124
125 void
126 ThreadedReconstructor::cancel()
127 {
128   if (isDone() || m_bCancelled)
129     return;
130   m_bCancelled = true;
131   m_bCancelling = false;
132   cleanUp();
133 }
134
135 void
136 ThreadedReconstructor::cleanUp()
137 {
138   wxCriticalSection cleanSection;
139   cleanSection.Enter();
140   
141   for (int i = 0; i < m_iNumThreads; i++) 
142     if (m_vecpThread[i] && m_vecpThread[i]->IsRunning()) {
143       m_vecpThread[i]->Pause();
144       m_vecpThread[i]->Delete();
145     }
146     
147     for (i = 0; i < m_iNumThreads; i++) {
148       delete m_vecpChildImageFile[i];
149       m_vecpChildImageFile[i] = NULL;
150     }
151     for (i = 0; i < m_iNumThreads; i++) {
152       delete m_vecpReconstructor[i];
153       m_vecpReconstructor[i] = NULL;
154     }
155     
156     
157     m_iNumThreads = 0;
158     m_iRunning = 0;
159     delete m_pDialogProgress;
160     delete m_pTimer;
161     m_pDialogProgress = NULL;
162     m_pGauge = NULL;
163     setDone();
164     m_pProjView->GetDocument()->removeReconstructor (this);
165     theApp->getBackgroundManager()->taskDone (this);
166     
167     cleanSection.Leave();
168 }
169
170 void
171 ThreadedReconstructor::onDone()
172 {
173   wxCriticalSection doneSection;
174   doneSection.Enter();
175
176   m_pProjView->GetDocument()->removeReconstructor (this);
177   ImageFileDocument* pReconDoc = theApp->newImageDoc();
178   if (! pReconDoc) {
179     sys_error (ERR_SEVERE, "Unable to create image file");
180     doneSection.Leave();
181     return;
182   }
183   
184   for (int i = 0; i < m_iNumThreads; i++) {
185     delete m_vecpReconstructor[i];
186     m_vecpReconstructor[i] = NULL;
187   }
188   
189   ImageFile* pImageFile = getImageFile();
190   pReconDoc->setImageFile (pImageFile);
191   if (theApp->getAskDeleteNewDocs())
192     pReconDoc->Modify (true);
193   pReconDoc->UpdateAllViews (m_pProjView);
194   if (ImageFileView* rasterView = pReconDoc->getView()) {
195     rasterView->OnUpdate (rasterView, NULL);
196     rasterView->getFrame()->SetFocus();
197     rasterView->getFrame()->Show(true);
198   }
199   *theApp->getLog() << m_strLabel << "\n";
200   pImageFile->labelAdd (m_pProjView->GetDocument()->getProjections().getLabel());
201   pImageFile->labelAdd (m_strLabel.c_str(), m_pTimer->timerEnd());
202   
203   doneSection.Leave();
204   cleanUp();
205 }
206
207
208 void
209 ThreadedReconstructor::OnThreadEvent (wxCommandEvent& event)
210 {
211   if (isDone())
212     return;
213   wxCriticalSection eventSection;
214   eventSection.Enter();
215   if (isDone()) {
216     eventSection.Leave();
217     return;
218   }
219   
220   int iEventId = event.GetInt();
221   if (iEventId == RTHREAD_UNIT_COMPLETE) {
222     ++m_iViewsDone;
223     
224 #ifdef DEBUG
225     if (theApp->getVerboseLogging())
226       *theApp->getLog() << "Views done: " << static_cast<int>(m_iViewsDone) <<"\n";
227 #endif
228     
229     if (m_pDialogProgress)
230       m_bCancelling = ! m_pDialogProgress->Update (m_iViewsDone - 1);
231     else if (m_pGauge) {
232       m_pGauge->SetValue (m_iViewsDone - 1); 
233       eventSection.Leave();
234       m_bCancelling = theApp->getBackgroundManager()->isCancelling(this);
235     }
236     if (! isDone() && m_bCancelling) {
237       eventSection.Leave();
238       cancel();
239       return;
240     }
241   } 
242   else if (event.GetInt() >= 0) {
243     m_iRunning--;
244     m_vecpThread[event.GetInt()] = NULL;
245     *theApp->getLog() << "Thread finished. Remaining threads: " << m_iRunning << "\n";
246     if (m_iRunning <= 0) {
247       eventSection.Leave();
248       onDone();
249       return;
250     }
251   }
252   else
253     *theApp->getLog() << "Got event #" << iEventId << "\n";
254   
255   eventSection.Leave();
256 }
257
258 ImageFile*
259 ThreadedReconstructor::getImageFile()
260 {
261   ImageFile* pImageFile = new ImageFile (m_iImageNX, m_iImageNY);
262   pImageFile->arrayDataClear();
263   ImageFileArray pArray = pImageFile->getArray();
264   
265   int i;
266   for (i = 0; i < m_iNumThreads; i++) {
267     ImageFileArrayConst pChildArray = m_vecpChildImageFile[i]->getArray();
268     for (int ix = 0; ix < m_iImageNX; ix++)
269       for (int iy = 0; iy < m_iImageNY; iy++)
270         pArray[ix][iy] += pChildArray[ix][iy];
271   }
272   
273   return (pImageFile);
274 }
275
276 bool
277 ThreadedReconstructor::testDone()
278 {
279   return (m_iRunning <= 0 ? true : false);
280 }
281
282
283 ThreadedReconstructor::~ThreadedReconstructor()
284 {
285 }
286
287
288 ReconstructionThread::ReconstructionThread (ThreadedReconstructor* pSupervisor, 
289                                             Reconstructor* pReconstructor, int iThread, int iStartView, int iNumViews)
290                                             : m_pSupervisor(pSupervisor), m_pReconstructor(pReconstructor), 
291                                             m_iStartView(iStartView), m_iNumViews(iNumViews), m_iThread(iThread), 
292                                             wxThread(wxTHREAD_DETACHED)
293 {
294 }
295
296 wxThread::ExitCode
297 ReconstructionThread::Entry ()
298 {
299   wxCommandEvent eventProgress (wxEVT_COMMAND_MENU_SELECTED, RECONSTRUCTION_THREAD_EVENT);
300   for (int iView = 0; iView < m_iNumViews; iView++) {
301     if (TestDestroy()) {
302       wxString msg;
303       msg.Printf("TestDestroy TRUE at view #%d\n", iView);  
304       wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, MAINMENU_LOG_EVENT );
305       event.SetString( msg );
306       wxPostEvent( theApp->getMainFrame(), event ); // send in a thread-safe way
307       return reinterpret_cast<wxThread::ExitCode>(RTHREAD_THREAD_CANCELLED);
308     }
309     m_pReconstructor->reconstructView (iView + m_iStartView, 1);
310     eventProgress.SetInt (RTHREAD_UNIT_COMPLETE);
311     wxPostEvent (m_pSupervisor, eventProgress);
312   }
313   m_pReconstructor->postProcessing();
314   
315   eventProgress.SetInt (m_iThread); // Send back thread# that has finished
316   wxPostEvent (m_pSupervisor, eventProgress);
317   
318   return reinterpret_cast<wxThread::ExitCode>(0);
319 }
320
321 void
322 ReconstructionThread::OnExit ()
323 {
324 }