Scope
StorageController_p.h
1 #pragma once
2 
3 #include "StorageController.h"
4 #include "BaseController_p.h"
5 
6 #include "helpers/helpers.h"
7 #include "helpers/SyncQueues.h"
8 #include "parameters/Scope.h"
9 #include "ScopeDatatypes.h"
10 #include "helpers/ScopeMultiImage.h"
11 #include "helpers/ScopeMultiImageResonanceSW.h"
12 #include "helpers/hresult_exception.h"
13 #include "helpers/ScopeMultiImageEncoder.h"
14 #include "helpers/ScopeException.h"
15 #include "ScopeLogger.h"
16 
17 namespace scope {
18 
21  : public BaseController<1>::Impl {
22 
23 protected:
26 
28  uint32_t runcounter;
29 
31  std::array<std::vector<std::wstring>, SCOPE_NAREAS> filenames;
32 
34  std::array<std::unique_ptr<ScopeMultiImageEncoder>, SCOPE_NAREAS> encoders;
35 
38 
39 protected:
41  Impl(const Impl& i);
42 
44  Impl operator=(const Impl& i);
45 
47  ControllerReturnStatus Run(StopCondition* const sc, const uint32_t& _area) override {
48  DBOUT(L"StorageController::Impl::Run beginning\n");
49  ControllerReturnStatus returnstatus(ControllerReturnStatus::none);
50  uint32_t framearea = 0;
51  std::wstring foldername(L"");
52  std::array<ScopeMultiImagePtr, SCOPE_NAREAS> current_frames;
53  const DaqMode requested_mode = parameters.requested_mode();
54  std::array<uint32_t, SCOPE_NAREAS> requested_frames;
55  uint32_t a = 0;
56  std::generate(std::begin(requested_frames), std::end(requested_frames),
57  [&]() { return parameters.areas[a++]->daq.requested_frames(); } );
58  // Checks if we should really do saving
59  bool dosave(parameters.storage.autosave()
60  && ( (requested_mode == DaqModeHelper::nframes)
61  || ((requested_mode == DaqModeHelper::continuous) && parameters.storage.savelive()) ) );
62 
63  // If saving desired create folder and encoders
64  try {
65  if ( dosave ) {
66  foldername = CreateFolder();
67  // Save ScopeParameters in xml if desired
69  parameters.Save(foldername + L"parameters.xml");
70  ++runcounter;
71  }
72  } catch (...) {
73  ScopeExceptionHandler(__FUNCTION__, true, true);
74  dosave = false;
75  }
76 
77  bool reqEqual, framecountEqual;
78  std::array<uint32_t, SCOPE_NAREAS> totalframecount;
79  totalframecount.assign(0);
80  InitializeEncoders(dosave, foldername);
81 
82  // dequeue and save loop
83  while ( !sc->IsSet() ) {
84  // Dequeue
85  ScopeMessage<SCOPE_MULTIIMAGEPTR_T> msg(input_queue->Dequeue());
86 
87  // If message has abort tag, break from while loop
88  if ( msg.tag == ScopeMessageTag::abort ) {
89  returnstatus = ControllerReturnStatus::stopped;
90  break;
91  }
92  DBOUT(L"StorageController::impl::Run dequeued\n");
93 
94  framearea = msg.cargo->Area();
95  current_frames[framearea] = msg.cargo;
96 
97  // otherwise something is seriously wrong:
98  assert(parameters.areas[framearea]->daq.inputs->channels() == current_frames[framearea]->Channels());
99 
100  // Create new frame on disk (Frames are only actually saved if encoder was created with dosave=true)
101  encoders[framearea]->NewFrame();
102  // and write into it
103  encoders[framearea]->WriteFrame(current_frames[framearea]);
104 
105  reqEqual = false;
106  framecountEqual = false;
107  const uint32_t maxframes = 3000000000 / ( current_frames[framearea]->Linewidth()*current_frames[framearea]->Lines()*16/8 );
108  // Preparation for the conditions below
109  for ( uint32_t i = 0; i < SCOPE_NAREAS; i++ ){
110  reqEqual = reqEqual || ( requested_frames.at(i) == encoders.at(i)->Framecount() + totalframecount.at(i) );
111  framecountEqual = framecountEqual || ( encoders.at(i)->Framecount() == maxframes );
112  }
113 
114  // Check if in nframes mode if we have already stored all requested frames for all areas
115  if ( (requested_mode == DaqModeHelper::nframes) && reqEqual ) {
116  // If yes we want to stop
117  sc->Set(true);
118  returnstatus = ControllerReturnStatus::finished;
119  DBOUT(L"StorageController::Impl::Run - all requested frames from all areas saved\n");
120  }
121 
122  // Check if the old file has to be closed and a new one opened
123  if ( framecountEqual ) {
124  for ( uint32_t i = 0; i < SCOPE_NAREAS; i++ ) {
125  totalframecount.at(i)+= encoders.at(i)->Framecount();
126 
127  // This calls encoders destructors, thus all files are closed and can be TIFF-fixed
128  for ( auto& e : encoders )
129  e.reset(nullptr);
130 
131  // Fix the tiff tags if wanted
132  if ( dosave && parameters.storage.usetifftags() )
133  FixTIFFTags();
134 
135  runcounter++;
136  InitializeEncoders(dosave, foldername);
137  }
138  }
139  }
140 
141  // This calls encoders destructors, thus all files are closed and can be TIFF-fixed
142  for ( auto& e : encoders )
143  e.reset(nullptr);
144 
145  // Fix the tiff tags if wanted
146  if ( dosave && parameters.storage.usetifftags() )
147  FixTIFFTags();
148 
149  if ( sc->IsSet() )
150  returnstatus = (ControllerReturnStatus)(returnstatus || ControllerReturnStatus::stopped);
151 
152  ATLTRACE(L"StorageController::Impl::Run end\n");
153  return returnstatus;
154  }
155 
157  std::wstring CreateFolder() {
158  // Make suffic depending on run state
159  std::wstring runmode(L"");
160  switch ( parameters.run_state().t ) {
161  case RunStateHelper::RunningContinuous:
162  runmode = L"_Live";
163  break;
164  case RunStateHelper::RunningSingle:
165  runmode = L"_Single";
166  break;
167  case RunStateHelper::RunningStack:
168  runmode = L"_Stack";
169  break;
170  case RunStateHelper::RunningTimeseries:
171  runmode = L"_Timeseries";
172  break;
173  }
174 
175  std::wstringstream foldername;
176  foldername << parameters.storage.folder() << parameters.date() << L"\\" << parameters.time() << runmode << L"\\";
177 
178  std::wstring msg = L"Saving into " + foldername.str();
179  scope_logger.Log(msg, log_info);
180 
181  // Create directory
182  if ( SHCreateDirectoryEx(NULL, foldername.str().c_str(), NULL) == ERROR_SUCCESS )
183  return foldername.str();
184  else
185  throw ScopeException(__FUNCTION__);
186 
187  return L"";
188  }
189 
191  void InitializeEncoders(const bool& _dosave, const std::wstring& _foldername) {
192  for ( uint32_t a = 0 ; a < SCOPE_NAREAS ; a++ ) {
193  // Make a new multi image encoder for that area
194  encoders[a] = std::unique_ptr<ScopeMultiImageEncoder>(new ScopeMultiImageEncoder(_dosave, parameters.areas[a]->daq.inputs->channels(), parameters.storage.compresstiff()));
195  filenames[a].resize(parameters.areas[a]->daq.inputs->channels());
196  // Construct the filenames for all channels
197  for ( uint32_t c = 0 ; c < parameters.areas[a]->daq.inputs->channels() ; c++ ) {
198  std::wstringstream stream;
199  stream << _foldername << parameters.storage.basename() << L"_A" << a << L"_Ch" << c << L"_ " << std::setfill(L'0') << std::setw(4) << runcounter << L".tif";
200  filenames[a][c] = stream.str();
201  }
202  // Give the filenames to the encoder
203  encoders[a]->Initialize(filenames[a]);
204  }
205  }
206 
207 public:
209  explicit Impl(SynchronizedQueue<ScopeMessage<SCOPE_MULTIIMAGEPTR_T>>* const _iqueue, const parameters::Scope& _parameters)
210  : BaseController::Impl(_parameters)
211  , input_queue(_iqueue)
212  , runcounter(0) {
213  }
214 
216  ~Impl() {
217  StopAll();
218  WaitForAll(-1);
219  }
220 
224  void StopOne(const uint32_t& _a) override {
226  ScopeMessage<SCOPE_MULTIIMAGEPTR_T> stopmsg(ScopeMessageTag::abort, nullptr);
227  input_queue->Enqueue(stopmsg);
228  }
229 
233  void FixTIFFTags() {
234  STARTUPINFO si;
235  PROCESS_INFORMATION pi;
236  // Prepare process
237  ZeroMemory( &si, sizeof(si) );
238  si.cb = sizeof(si);
239  ZeroMemory( &pi, sizeof(pi) );
240  WCHAR buff[255];
241  GetCurrentDirectory(255, buff);
242 
243  // Build command line string for exiftool.exe
244  std::wstringstream cmdbase;
245  cmdbase << L"-delete_original -overwrite_original_in_place -ResolutionUnit=None -Software=\"Proudly recorded with Scope\" ";
246  switch ( parameters.run_state().t ) {
247  case RunStateHelper::RunningStack:
248  cmdbase << L"-ImageDescription=\"ImageJ=1.47m\nunit=um\nspacing=" << parameters.stack.spacing() << L"\" ";
249  break;
250  case RunStateHelper::RunningTimeseries:
251  break;
252  default:
253  cmdbase << L"-ImageDescription=\"ImageJ=1.47m\nunit=um\" ";
254  }
255 
256  // Go through all areas (run exiftool.exe once per area)
257  for ( uint32_t a = 0 ; a < SCOPE_NAREAS ; a++ ) {
258  std::wstringstream cmd;
259  cmd << cmdbase.str();
260  // Add timeseries stuff
261  if ( parameters.run_state() == RunStateHelper::RunningTimeseries ) {
262  cmd << L"-ImageDescription=\"ImageJ=1.47m\nunit=um\nfinterval=" << parameters.timeseries.totaltimes[a]()/parameters.timeseries.frames[a]();
263  cmd << L"\nframes=" << parameters.timeseries.frames[a]() << L"\" ";
264  }
265 
266  // Add µm resolution
267  cmd << L"-XResolution=" << 1/parameters.areas[a]->micronperpixelx() << L" -YResolution=" << 1/parameters.areas[a]->micronperpixely() << L" ";
268 
269  // Add the filenames
270  for ( uint32_t c = 0 ; c < parameters.areas[a]->daq.inputs->channels() ; c++ )
271  cmd << L"\"" << filenames[a][c] << L"\" ";
272 
273  // Run exiftool.exe
274  DBOUT(L"Cmd: " << cmd.str());
275  std::wstring cmdstr(cmd.str());
276  std::vector<wchar_t> wincmd(cmdstr.begin(), cmdstr.end());
277  if ( 0 == CreateProcess(L"tools/exiftool.exe", wincmd.data(), NULL, NULL, FALSE, BELOW_NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, NULL, NULL, &si, &pi) ) {
278  std::wstringstream errstr;
279  errstr << L"exiftool.exe did not start with error " << GetLastError();
280  scope_logger.Log(errstr.str(), log_error);
281  return;
282  }
283 
284  // Wait 1 second for exiftool.exe to finish
285  DWORD procret = WaitForSingleObject(pi.hProcess, 1000);
286  if ( WAIT_OBJECT_0 != procret ) {
287  std::wstringstream errstr;
288  errstr << L"Waiting for exiftool failed with error " << procret;
289  scope_logger.Log(errstr.str(), log_error);
290  return;
291  }
292 
293  // Close process handle
294  CloseHandle(pi.hProcess);
295  if (0 == CloseHandle(pi.hThread) ) {
296  std::wstringstream errstr;
297  errstr << L"exiftool.exe did not finish correctly, error " << GetLastError();
298  scope_logger.Log(errstr.str(), log_error);
299  return;
300  }
301  }
302 
303  }
304 };
305 
306 }
parameters::Scope parameters
the Controller's own set of ScopeParameters
ScopeString basename
basename for image files
Definition: Storage.h:28
Thread-safe lock-free bool to signal a requested stop to the worker function currently executed in th...
Definition: helpers.h:87
ScopeNumber< bool > autosave
autosave all acquisitions (except live scan)
Definition: Storage.h:31
The master parameters class.
Definition: Scope.h:204
Simple exception class for Scope.
Definition: ScopeException.h:9
ScopeString date
current date
Definition: Scope.h:216
std::array< std::vector< std::wstring >, SCOPE_NAREAS > filenames
Keep track of filenames.
Stack stack
the StackParameters
Definition: Scope.h:237
std::array< std::unique_ptr< Area >, SCOPE_NAREAS > areas
holds AreaParameters for all areas.
Definition: Scope.h:231
ScopeNumber< bool > savelive
autosave live scan acquisitions
Definition: Storage.h:34
ScopeLogger scope_logger
the logger kept handy
std::wstring CreateFolder()
Create folder.Fformat is: "folder/date/time_runmode/".
std::array< ScopeNumber< uint32_t >, SCOPE_NAREAS > frames
number of frames in timeseries for each area
Definition: Runstates.h:72
ScopeNumber< bool > compresstiff
write compressed TIFF
Definition: Storage.h:43
Base class for all Scope datatypes here, provides a uniform interface (and saves typing...).
void Enqueue(const T &elem)
Enqueues an element and notifies one waiting operation that queue is not empty.
Definition: SyncQueues.h:43
Timeseries timeseries
the TimeseriesParameters
Definition: Scope.h:240
ScopeNumber< bool > usetifftags
write tiff tags for ImageJ etc.
Definition: Storage.h:40
Storage storage
the StorageParameters
Definition: Scope.h:234
A logger class to log various messages and user comments.
Definition: ScopeLogger.h:24
ControllerReturnStatus Run(StopCondition *const sc, const uint32_t &_area) override
Main function for running data conversion to TIFF and storage.
ScopeString folder
folder to Save into
Definition: Storage.h:25
ScopeNumber< double > spacing
Spacing between planes in microns.
Definition: Runstates.h:32
ScopeValue< DaqMode > requested_mode
requested acquisition mode (see DaqModeHelper)
Definition: Scope.h:279
void Log(const std::wstring &message, const log_message_type &msgtype)
Logs a message.
Definition: ScopeLogger.cpp:26
In here all declarations for all kinds of datatypes Scope needs.
Impl(SynchronizedQueue< ScopeMessage< SCOPE_MULTIIMAGEPTR_T >> *const _iqueue, const parameters::Scope &_parameters)
Connect the input queue and take parameters.
virtual ControllerReturnStatus WaitForAll(const int32_t &_wait_time)
Wait for the futures of all async worker functions.
virtual void StopAll()
Request all async worker function to stop.
virtual void StopOne(const uint32_t &_a)
Request one async worker function to stop by settings its StopCondition to true.
#define DBOUT(s)
A debug output to the debug console.
Definition: helpers.h:153
void Save(const std::wstring &filename) const
Save all to file.
Definition: Scope.cpp:513
The implementation class of the StorageController.
void ScopeExceptionHandler(const std::string &_origin, const bool &_log, const bool &_showmessagebox, const bool &_trace, const bool &_rethrow)
Handles all exceptions and does nice logging.
Impl operator=(const Impl &i)
disable assignment
~Impl()
Stop the controller and interrupt thread if necessary.
A synchronized, thread-safe queue was modeled after ringbuffer example from boost?! and/or a Herb Sutter column?!
Definition: DaqController.h:7
std::array< ScopeNumber< double >, SCOPE_NAREAS > totaltimes
total acquisition time for each area
Definition: Runstates.h:75
ScopeString time
current time
Definition: Scope.h:219
ScopeValue< RunState > run_state
current RunState
Definition: Scope.h:276
void Set(const bool &_a=true)
Definition: helpers.h:108
void StopOne(const uint32_t &_a) override
We need to override here.
uint32_t runcounter
for continuous file numbering
Encodes multi images to TIFF using Windows Imaging Components.
Various helper functions and classes for Scope.
SynchronizedQueue< ScopeMessage< SCOPE_MULTIIMAGEPTR_T > > *const input_queue
Input queue with multi images from the PipelineController.
Base class for all controllers.
void InitializeEncoders(const bool &_dosave, const std::wstring &_foldername)
Creates and initializes the encoders.
T Dequeue()
Dequeues front element, waits indefinitely if queue is empty.
Definition: SyncQueues.h:61
ScopeNumber< bool > saveparameters
Save ScopeParameters in xml file.
Definition: Storage.h:37
Impl(const Impl &i)
disable copy
std::array< std::unique_ptr< ScopeMultiImageEncoder >, SCOPE_NAREAS > encoders
the encoders (encapsulating the WIC stuff)
void FixTIFFTags()
Write correct Tiff flags into files (fix the ones that are by default written by the WIC...
bool IsSet() const
Definition: helpers.h:105