DynExp
Highly flexible laboratory automation for dynamically changing experiments.
HardwareAdapter.cpp
Go to the documentation of this file.
1 // This file is part of DynExp.
2 
3 #include "stdafx.h"
4 #include "moc_HardwareAdapter.cpp"
5 #include "HardwareAdapter.h"
6 
7 namespace DynExp
8 {
10  {
11  }
12 
14  {
15  }
16 
18  {
19  }
20 
22  {
23  // Short (default) timeout only since main thread should not block.
24  auto lock = AcquireLock();
25 
26  LastException = std::exception_ptr();
27  }
28 
29  void HardwareAdapterBase::ThrowException(std::exception_ptr Exception) const
30  {
31  // If AcquireLock() throws here, Exception does not get stored and is replaced
32  // by the one thrown by AcquireLock(). This could in principal cause unintended
33  // effects. On the other hand, next calls to hardware adapter's functions can
34  // cause another exception which is then stored.
36 
37  ThrowExceptionUnsafe(Exception);
38  }
39 
40  void HardwareAdapterBase::ThrowExceptionUnsafe(std::exception_ptr Exception) const
41  {
42  if (!Exception)
43  return;
44 
45  try
46  {
47  std::rethrow_exception(Exception);
48  }
49 // Object making use of this hardware adapter handles exception or fails which causes the exception to be logged.
50 #ifdef DYNEXP_DEBUG
51  catch (const Util::Exception& e)
52  {
53  Util::EventLog().Log(e);
54  LastException = std::current_exception();
55 
57  }
58 #endif // DYNEXP_DEBUG
59  catch (...)
60  {
61  LastException = std::current_exception();
62 
64  }
65  }
66 
67  void HardwareAdapterBase::SetExceptionUnsafe(std::exception_ptr Exception) const
68  {
69  if (!Exception)
70  return;
71 
72  try
73  {
74  std::rethrow_exception(Exception);
75  }
76  catch (const Util::Exception& e)
77  {
78  Util::EventLog().Log(e);
79  LastException = std::current_exception();
80  }
81  catch (...)
82  {
83  LastException = std::current_exception();
84  }
85  }
86 
88  {
89  // auto lock = AcquireLock(); not necessary here, since DynExp ensures that Object::Reset() can only
90  // be called if respective object is not in use.
91 
92  LastException = nullptr;
94 
95  EnsureReadyState(false);
96  }
97 
98  void HardwareAdapterBase::EnsureReadyStateChild(bool IsAutomaticStartup)
99  {
100  if (GetException(IsAutomaticStartup ? HardwareOperationTimeout : ShortTimeoutDefault))
102  "A hardware adapter is in an error state. It requires to be reset in order to transition into a ready state.");
103 
105  }
106 
107  std::exception_ptr HardwareAdapterBase::GetExceptionChild(const std::chrono::milliseconds Timeout) const
108  {
109  // Short (default) timeout only since main thread should not block.
110  auto lock = AcquireLock(Timeout);
111 
112  return GetExceptionUnsafe();
113  }
114 
116  {
117  }
118 
120  {
122  { "None", LineEndingType::None },
123  { "Zero", LineEndingType::Zero },
124  { "LF", LineEndingType::LF },
125  { "CRLF", LineEndingType::CRLF },
126  { "CR", LineEndingType::CR }
127  };
128 
129  return List;
130  }
131 
133  {
134  }
135 
137  {
138  }
139 
141  : HardwareAdapterBase(OwnerThreadID, std::move(Params))
142  {
143  Init();
144  }
145 
147  {
149 
150  ReadIntoBuffer();
151 
152  // TODO: replace by C++20 view() method as soon as available
153  auto Contents = ReadBuffer.str();
154  auto Pos = Contents.find(LineEndingString, ReadBuffer.tellg());
155 
156  if (Pos == std::string::npos)
157  return "";
158 
159  auto Line = Contents.substr(ReadBuffer.tellg(), Pos - ReadBuffer.tellg());
160 
161  ReadBuffer.str(Contents.substr(Pos + LineEndingString.length()));
162  ReadBuffer.clear();
163 
164  return Line;
165  }
166 
168  {
170 
171  ReadIntoBuffer();
172 
173  auto Text = ReadBuffer.str().substr(ReadBuffer.tellg());
174  ClearReadBuffer();
175 
176  return Text;
177  }
178 
179  std::string SerialCommunicationHardwareAdapter::WaitForLine(unsigned int NumTries, std::chrono::milliseconds DelayBetweenTries) const
180  {
181  for (; NumTries > 0; --NumTries)
182  {
183  auto Line = ReadLine();
184 
185  if (!Line.empty())
186  return Line;
187 
188  std::this_thread::sleep_for(DelayBetweenTries);
189  }
190 
191  return "";
192  }
193 
195  {
197 
198  ClearChild();
199  ClearReadBuffer();
200  }
201 
203  {
205 
206  FlushChild();
207  ReadIntoBuffer();
208  }
209 
211  {
212  OutStream << ReadAll();
213 
214  return *this;
215  }
216 
218  {
220 
221  Write_endl();
222 
223  return *this;
224  }
225 
227  {
229 
230  Write(InStream.str());
231  Write_endl();
232 
233  return *this;
234  }
235 
237  {
239 
240  Write(Text);
241  Write_endl();
242 
243  return *this;
244  }
245 
247  {
249 
250  Write(Text.data());
251  Write_endl();
252 
253  return *this;
254  }
255 
257  {
259 
260  Write(Text);
261  Write_endl();
262 
263  return *this;
264  }
265 
267  {
269 
270  Write(std::string(1, Char));
271  Write_endl();
272 
273  return *this;
274  }
275 
276  void SerialCommunicationHardwareAdapter::InsertIntoBuffer(const std::string& String) const
277  {
278  if (String.empty())
279  return;
280 
282  if (!CheckOverflow())
283  return;
284 
285  ReadBuffer.write(String.c_str(), String.length());
286 
287  if (ReadBuffer.fail())
288  throw Util::InvalidStateException("Writing data into read buffer failed.");
289  }
290 
292  {
293  auto DerivedParams = dynamic_Params_cast<SerialCommunicationHardwareAdapter>(GetParams());
294 
295  LineEnding = DerivedParams->LineEnding;
297  }
298 
300  {
301  Init();
302  ClearReadBuffer();
303 
305  }
306 
308  {
309  if (!CheckOverflow())
310  return;
311 
312  auto DataRead = Read();
313 
314  if (!DataRead.empty())
315  ReadBuffer.write(DataRead.c_str(), DataRead.length());
316 
317  if (ReadBuffer.fail())
318  throw Util::InvalidStateException("Writing data into read buffer failed.");
319  }
320 
322  {
323  ReadBuffer.str("");
324  ReadBuffer.clear();
325  }
326 
328  {
329  auto initial_pos = ReadBuffer.tellg();
330  ReadBuffer.seekg(0, std::ios::end);
331  auto size = ReadBuffer.tellg();
332  ReadBuffer.seekg(initial_pos, std::ios::beg);
333 
334  // Limit buffer length
335  if (size > GetMaxBufferSize())
336  {
337  SetWarning("Read buffer exceeds 100 MB. No further data is read from the hardware device.", Util::DynExpErrorCodes::Overflow);
338 
339  return false;
340  }
341 
342  return true;
343  }
344 
346  {
347  }
348 
350  {
351  }
352 
354  {
355  }
356 
358  {
359  auto Owner = std::dynamic_pointer_cast<const QSerialCommunicationHardwareAdapter>(GetOwner());
360  if (!Owner)
361  return;
362 
363  Owner->QSerialCommunicationHardwareAdapterWorkerOnly.SetCommunicationChannelOpened();
364  }
365 
367  {
368  auto Owner = std::dynamic_pointer_cast<const QSerialCommunicationHardwareAdapter>(GetOwner());
369  if (!Owner)
370  return;
371 
372  Owner->QSerialCommunicationHardwareAdapterWorkerOnly.SetCommunicationChannelClosed();
373  }
374 
375  void QSerialCommunicationHardwareAdapterWorker::DataRead(const std::string& String) const
376  {
377  auto Owner = std::dynamic_pointer_cast<const QSerialCommunicationHardwareAdapter>(GetOwner());
378  if (!Owner)
379  return;
380 
381  Owner->QSerialCommunicationHardwareAdapterWorkerOnly.DataRead(String);
382  }
383 
385  : SerialCommunicationHardwareAdapter(OwnerThreadID, std::move(Params)),
386  QSerialCommunicationHardwareAdapterWorkerOnly(*this), Worker(nullptr), CommunicationChannelOpened(false)
387  {
388  }
389 
391  {
392  if (Worker)
393  {
394  emit CloseSig();
395 
396  Worker->deleteLater();
397  }
398  }
399 
400  void QSerialCommunicationHardwareAdapter::DataRead(const std::string& String) const
401  {
402  InsertIntoBuffer(String);
403  }
404 
406  {
407  PendingException = std::exception_ptr();
408 
409  // Derived classes have to push new parameters to the Worker and emit the ResetSig() to reset the worker.
411  }
412 
414  {
415  if (!Worker)
416  {
417  auto WorkerUniquePtr = MakeWorker();
418  if (!WorkerUniquePtr)
419  throw Util::InvalidArgException("Worker cannot be nullptr.");
420  WorkerUniquePtr->MoveToWorkerThread(GetID());
421 
422  // Worker destroyed in DynExpCore's worker thread by calling QObject::deleteLater() on it.
423  Worker = WorkerUniquePtr.release();
424 
425  // Connect Worker's slots to signals which are emitted here.
434 
435  InitWorker();
436  }
437 
438  emit OpenSig();
439  }
440 
442  {
444 
445  auto Exception = GetExceptionUnsafe();
446  Util::ForwardException(Exception);
447 
449  }
450 
452  {
454  }
455 
457  {
459 
460  emit ClearSig();
461  }
462 
464  {
466 
467  emit FlushSig();
468  }
469 
471  {
473 
474  emit ReadSig();
475 
476  // See SerialCommunicationHardwareAdapter::InsertIntoBuffer() declaration.
477  return "";
478  }
479 
480  void QSerialCommunicationHardwareAdapter::Write(const std::string& String) const
481  {
483 
484  emit WriteSig(QString::fromStdString(String));
485  }
486 
488  {
490 
491  emit Write_endl_Sig();
492  }
493 
495  {
496  if (!PendingException)
497  return;
498 
499  std::exception_ptr e;
500  std::swap(PendingException, e);
501 
502  // AcquireLock() has already been called by an (in)direct caller of this function.
504  }
505 }
Implementation of DynExp hardware adapter objects.
Defines the base class for a hardware adapter object. Hardware adapters describe interfaces/connectio...
void ThrowException(std::exception_ptr Exception) const
Stores Exception in LastException, wraps it in a Util::ForwardedException and throws the wrapped exce...
std::exception_ptr GetExceptionChild(const std::chrono::milliseconds Timeout) const override final
Returns a pointer to the exception which has caused this Object instance to fail.
void SetExceptionUnsafe(std::exception_ptr Exception) const
Stores Exception in LastException.
static constexpr auto ShortTimeoutDefault
Default timeout e.g. used as a default for calls to Object::GetException().
virtual void EnsureReadyStateChild()=0
Ensures that this Object instance is ready by possibly starting its worker thread or by opening conne...
std::exception_ptr LastException
Stores the most recent exception caused by a hardware operation.
static constexpr auto HardwareOperationTimeout
Default timeout used to lock the mutex provided by the base class Util::ILockable to synchronize acce...
void ThrowExceptionUnsafe(std::exception_ptr Exception) const
Stores Exception in LastException, wraps it in a Util::ForwardedException and throws the wrapped exce...
auto GetExceptionUnsafe() const
Getter for LastException.
void ResetImpl(dispatch_tag< Object >) override final
Refer to DynExp::Object::Reset(). Using tag dispatch mechanism to ensure that ResetImpl() of every de...
void ResetException() const
Sets LastException to nullptr in a thread-safe way.
void SetWarning(std::string Description, int ErrorCode) const
Setter for Object::Warning. Sets the warning by a description and an error code.
Definition: Object.cpp:454
ParamsConstTypeSyncPtrType GetParams(const std::chrono::milliseconds Timeout=GetParamsTimeoutDefault) const
Locks the mutex of the parameter class instance Params assigned to this Object instance and returns a...
Definition: Object.cpp:436
ItemIDType GetID() const noexcept
Returns the ID of this Object instance. Thread-safe since ID is const.
Definition: Object.h:2143
void EnsureReadyState(bool IsAutomaticStartup)
Ensures that this Object instance is ready by possibly starting its worker thread or by opening conne...
Definition: Object.cpp:446
std::exception_ptr GetException(const std::chrono::milliseconds Timeout=Util::ILockable::DefaultTimeout) const
Returns a pointer to the exception which has caused this Object instance to fail.
Definition: Object.h:2204
Refer to ParamsBase::dispatch_tag.
Definition: Object.h:2018
void DataRead(const std::string &String) const
Calls QSerialCommunicationHardwareAdapter::DataRead on QWorker::Owner. Does nothing if QWorker::Owner...
void Read()
Reads from the communication connection's hardware interface.
void Clear()
Clears the communication connection's buffers and state.
void Write_endl()
Writes end of the line character(s) to the communication connection's hardware interface.
void Flush()
Flushes the communication connection's buffers.
void Close()
Closes the communication connection.
void Write(const QString String)
Writes String to the communication connection's hardware interface.
void Open()
Opens the communication connection.
void Reset()
Resets the worker and the communication connection.
void SetCommunicationChannelClosed() const noexcept
Calls QSerialCommunicationHardwareAdapter::SetCommunicationChannelClosed on QWorker::Owner....
void SetCommunicationChannelOpened() const noexcept
Calls QSerialCommunicationHardwareAdapter::SetCommunicationChannelOpened on QWorker::Owner....
virtual void InitWorker()
Tells the worker instance to perform initialization steps, e.g. by emitting a Qt signal which is rece...
void ResetSig()
Resets the worker and the communication connection.
std::exception_ptr PendingException
Stores exceptions which have occurred in the worker instance thread.
void ClearSig() const
Clears the communication connection's buffers and state.
bool IsConnectedChild() const noexcept override final
Determines the connection status of the hardware interface.
void OpenSig()
Opens the communication connection.
QSerialCommunicationHardwareAdapterWorker * Worker
Worker instance running in its own thread to handle the actual communication there.
void Write_endl_Sig() const
Writes end of the line character(s) to the communication connection's hardware interface.
std::atomic< bool > CommunicationChannelOpened
Indicates whether the worker instance has opened the communication channel.
void ClearChild() const override final
Clears internal buffers of the underlying hardware interface.
std::string Read() const override final
Reads a string from the underlying hardware interface.
void FlushSig() const
Flushes the communication connection's buffers.
void ThrowPendingException() const
If PendingException is not nullptr, sets it to nullptr and throws the exception calling Util::Forward...
bool IsReadyChild() const override final
Returns wheter this Object instance is ready (e.g. it is running or connected to a hardware device) a...
void Write(const std::string &String) const override final
Writes a string to the underlying hardware interface.
QSerialCommunicationHardwareAdapter(const std::thread::id OwnerThreadID, ParamsBasePtrType &&Params)
Constructs a hardware adapter instance.
void Write_endl() const override final
Writes end of the line characters to the underlying hardware interface.
void WriteSig(const QString String) const
Writes String to the communication connection's hardware interface.
void ResetImpl(dispatch_tag< SerialCommunicationHardwareAdapter >) override final
Refer to DynExp::Object::Reset(). Using tag dispatch mechanism to ensure that ResetImpl() of every de...
void EnsureReadyStateChild() override final
Ensures that this Object instance is ready by possibly starting its worker thread or by opening conne...
void CloseSig()
Closes the communication connection.
void DataRead(const std::string &String) const
Passes data to the hardware adapter which has been received by the worker instance.
void ReadSig() const
Reads from the communication connection's hardware interface.
void FlushChild() const override final
Flushes the underlying hardware interface.
virtual QWorkerPtrType MakeWorker()=0
Abstract factory function for a worker instance derived from QSerialCommunicationHardwareAdapterWorke...
static Util::TextValueListType< LineEndingType > AvlblLineEndingsStrList()
Assigns labels to the entries of LineEndingType.
Defines a hardware adapter for serial communication. Logical const-ness: see declaration of class Dyn...
void Init()
Initializes the instance at construction or in case Object::Reset() is called.
constexpr static unsigned int GetLineEndingLength(SerialCommunicationHardwareAdapterParams::LineEndingType LineEnding) noexcept
Determines the amount of characters required to express a line ending type defined by SerialCommunica...
virtual void Write_endl() const =0
Writes end of the line characters to the underlying hardware interface.
virtual void FlushChild() const
Flushes the underlying hardware interface.
virtual void Write(const std::string &String) const =0
Writes a string to the underlying hardware interface.
bool CheckOverflow() const
Checks whether the length of the read buffer (ReadBuffer) exceeds the maximal allowed length given by...
std::string LineEndingString
String corresponding to LineEnding. Refer to LineEndingToChar()
virtual void ClearChild() const
Clears internal buffers of the underlying hardware interface.
constexpr static auto GetMaxBufferSize() noexcept
Defines the maximal size of the hardware adapter's (read) buffer.
void Flush() const
Flushes the underlying hardware interface and calls ReadIntoBuffer().
constexpr static std::array< char, 2 > LineEndingToChar(SerialCommunicationHardwareAdapterParams::LineEndingType LineEnding) noexcept
Converts SerialCommunicationHardwareAdapterParams::LineEndingType to two characters being used as the...
const SerialCommunicationHardwareAdapter & operator<<(endl) const
Writes a new line calling Write_endl().
void InsertIntoBuffer(const std::string &String) const
This method can be called from derived classes to manually insert data into the read buffer....
std::string ReadLine() const
Calls ReadIntoBuffer() before it extracts the first line from the read buffer.
std::string ReadAll() const
Calls ReadIntoBuffer() before it extracts the entire content from the read buffer.
virtual std::string Read() const =0
Reads a string from the underlying hardware interface.
std::atomic< SerialCommunicationHardwareAdapterParams::LineEndingType > LineEnding
Copy of SerialCommunicationHardwareAdapterParams::LineEnding to avoid locking the corresponding Seria...
void ResetImpl(dispatch_tag< HardwareAdapterBase >) override final
Refer to DynExp::Object::Reset(). Using tag dispatch mechanism to ensure that ResetImpl() of every de...
void ReadIntoBuffer() const
Calls Read() to retrieve data from the underlying hardware interface. Writes the retrieved data to th...
std::string WaitForLine(unsigned int NumTries=10, std::chrono::milliseconds DelayBetweenTries=std::chrono::milliseconds(10)) const
Calls ReadLine() for NumTries times until a line is received. Makes the calling thread sleep for Dela...
void Clear() const
Clears the content and state flags of the read buffer (ReadBuffer) as well as possible internal buffe...
const SerialCommunicationHardwareAdapter & operator>>(std::stringstream &OutStream) const
Writes the read buffer's content into OutStream calling ReadAll().
std::stringstream ReadBuffer
Buffer storing data read from the underlying physical hardware.
void ClearReadBuffer() const
Clears the content and state flags of the read buffer (ReadBuffer)
SerialCommunicationHardwareAdapter(const std::thread::id OwnerThreadID, ParamsBasePtrType &&Params)
Constructs a hardware adapter instance.
Type denoting the end of a line when piped to operator<<(endl)
void Log(const std::string &Message, const ErrorType Type=ErrorType::Info, const size_t Line=0, const std::string &Function="", const std::string &File="", const int ErrorCode=0, const std::stacktrace &Trace={}) noexcept
Logs an event from information specified manually.
Definition: Util.cpp:309
DynExp exceptions are derived from this class. It contains basic information about the cause of the e...
Definition: Exception.h:51
LockType AcquireLock(const std::chrono::milliseconds Timeout=DefaultTimeout) const
Locks the internal mutex. Blocks until the mutex is locked or until the timeout duration is exceeded.
Definition: Util.cpp:8
An invalid argument like a null pointer has been passed to a function.
Definition: Exception.h:137
An operation cannot be performed currently since the related object is in an invalid state like an er...
Definition: Exception.h:150
auto GetOwner() const noexcept
Returns this worker instance's owner.
Definition: QtUtil.h:349
OwnerPtrType Owner
Pointer to the hardware adapter owning this instance.
Definition: QtUtil.h:371
@ None
Unspecified/arbitrary waveform.
DynExp's main namespace contains the implementation of DynExp including classes to manage resources (...
std::unique_ptr< ParamsBase > ParamsBasePtrType
Alias for a pointer to the parameter system base class ParamsBase.
Definition: Object.h:1807
void ForwardException(std::exception_ptr e)
Wraps the exception passed to the function in a ForwardedException and throws the ForwardedException....
Definition: Exception.cpp:30
EventLogger & EventLog()
This function holds a static EventLogger instance and returns a reference to it. DynExp uses only one...
Definition: Util.cpp:509
std::vector< std::pair< TextType, ValueType > > TextValueListType
Type of a list containing key-value pairs where key is a text of type Util::TextType.
Definition: QtUtil.h:37
Accumulates include statements to provide a precompiled header.