DynExp
Highly flexible laboratory automation for dynamically changing experiments.
HardwareAdapter.h
Go to the documentation of this file.
1 // This file is part of DynExp.
2 
8 #pragma once
9 
10 #include "stdafx.h"
11 #include "Object.h"
12 
13 namespace DynExp
14 {
15  class HardwareAdapterBase;
16  class SerialCommunicationHardwareAdapter;
17  class QSerialCommunicationHardwareAdapter;
18 
22  using HardwareAdapterPtrType = std::shared_ptr<HardwareAdapterBase>;
23 
29  template <typename HardwareAdapterT>
31  {
32  return std::make_shared<typename HardwareAdapterT::ConfigType>();
33  }
34 
42  template <typename HardwareAdapterT>
43  HardwareAdapterPtrType MakeHardwareAdapter(const std::thread::id OwnerThreadID, ParamsBasePtrType&& Params)
44  {
45  return std::make_shared<HardwareAdapterT>(OwnerThreadID, std::move(Params));
46  }
47 
52  {
53  public:
59 
60  virtual ~HardwareAdapterParamsBase() = 0;
61 
62  virtual const char* GetParamClassTag() const noexcept override { return "HardwareAdapterParamsBase"; }
63 
64  private:
67 
68  DummyParam Dummy = { *this };
69  };
70 
75  {
76  public:
79 
82  };
83 
89  {
90  public:
93 
97  static constexpr auto ShortTimeoutDefault = std::chrono::milliseconds(10);
98 
103  static constexpr auto HardwareOperationTimeout = std::chrono::milliseconds(100);
104 
109  constexpr static auto Category() noexcept { return ""; }
110 
116  : Object(OwnerThreadID, std::move(Params)) {}
117 
118  virtual ~HardwareAdapterBase() = 0;
119 
120  virtual std::string GetCategory() const override { return Category(); }
121 
125  void ResetException() const;
126 
132  bool IsConnected() const noexcept { return IsConnectedChild(); }
133 
134  protected:
140  void ThrowException(std::exception_ptr Exception) const;
141 
146 
149  void ThrowExceptionUnsafe(std::exception_ptr Exception) const;
150 
155  void SetExceptionUnsafe(std::exception_ptr Exception) const;
156 
161  auto GetExceptionUnsafe() const { return LastException; }
163 
164  private:
165  void ResetImpl(dispatch_tag<Object>) override final;
167 
172  void EnsureReadyStateChild([[maybe_unused]] bool IsAutomaticStartup) override final;
173 
174  virtual void EnsureReadyStateChild() = 0;
175  std::exception_ptr GetExceptionChild(const std::chrono::milliseconds Timeout) const override final;
176 
181  virtual bool IsConnectedChild() const noexcept = 0;
183 
184  mutable std::exception_ptr LastException;
185  };
186 
190  class SerialCommunicationException : public Util::Exception
191  {
192  public:
199  SerialCommunicationException(std::string Description, const int ErrorCode,
200  const std::source_location Location = std::source_location::current()) noexcept
201  : Exception(std::move(Description), Util::ErrorType::Error, ErrorCode, Location)
202  {}
203  };
204 
209  {
210  public:
215  enum LineEndingType { None, Zero, LF, CRLF, CR };
216 
222 
224 
225  virtual const char* GetParamClassTag() const noexcept override { return "SerialCommunicationHardwareAdapterParams"; }
226 
231  static Util::TextValueListType<LineEndingType> AvlblLineEndingsStrList();
232 
236  Param<LineEndingType> LineEnding = { *this, AvlblLineEndingsStrList(), "LineEnding", "Line ending",
237  "Characters which terminate a single line", true, LineEndingType::LF };
238 
239  private:
242  };
243 
248  {
249  public:
252 
255  };
256 
262  {
263  public:
266 
270  struct endl {};
271 
276  constexpr static auto Name() noexcept { return "< any >"; }
277 
278  constexpr static auto Category() noexcept { return "Communication"; }
279 
287  constexpr static std::array<char, 2> LineEndingToChar(SerialCommunicationHardwareAdapterParams::LineEndingType LineEnding) noexcept;
288 
295  constexpr static unsigned int GetLineEndingLength(SerialCommunicationHardwareAdapterParams::LineEndingType LineEnding) noexcept;
296 
301  constexpr static auto GetMaxBufferSize() noexcept { return 104857600; }
302 
307 
308  virtual ~SerialCommunicationHardwareAdapter() = 0;
309 
310  virtual std::string GetName() const override { return Name(); }
311  virtual std::string GetCategory() const override { return Category(); }
312 
321  auto GetLineEnding() const { return LineEnding.load(); }
322 
328  std::string ReadLine() const;
329 
334  std::string ReadAll() const;
335 
344  std::string WaitForLine(unsigned int NumTries = 10, std::chrono::milliseconds DelayBetweenTries = std::chrono::milliseconds(10)) const;
345 
350  void Clear() const;
351 
355  void Flush() const;
356 
362  const SerialCommunicationHardwareAdapter& operator>>(std::stringstream& OutStream) const;
363 
369 
375  const SerialCommunicationHardwareAdapter& operator<<(const std::stringstream& InStream) const;
376 
382  const SerialCommunicationHardwareAdapter& operator<<(const std::string& Text) const;
383 
387  const SerialCommunicationHardwareAdapter& operator<<(const std::string_view Text) const;
388 
392  const SerialCommunicationHardwareAdapter& operator<<(const char* Text) const;
393 
399  const SerialCommunicationHardwareAdapter& operator<<(const char Char) const;
400 
407  template <typename T>
408  auto& operator<<(const T& Value) const
409  {
411 
412  Write(Util::ToStr(Value));
413  Write_endl();
414 
415  return *this;
416  }
417 
418  protected:
429  void InsertIntoBuffer(const std::string& String) const;
430 
431  private:
432  void Init();
433 
434  void ResetImpl(dispatch_tag<HardwareAdapterBase>) override final;
436 
441 
447  void ReadIntoBuffer() const;
448 
452  void ClearReadBuffer() const;
453 
459  bool CheckOverflow() const;
461 
467  virtual void ClearChild() const {}
468  virtual void FlushChild() const {}
469 
474  virtual std::string Read() const = 0;
475 
480  virtual void Write(const std::string& String) const = 0;
481 
485  virtual void Write_endl() const = 0;
487 
493  std::atomic<SerialCommunicationHardwareAdapterParams::LineEndingType> LineEnding = SerialCommunicationHardwareAdapterParams::LineEndingType::LF;
494 
495  std::string LineEndingString;
496  mutable std::stringstream ReadBuffer;
497  };
498 
500  {
501  switch (LineEnding)
502  {
503  case SerialCommunicationHardwareAdapterParams::LineEndingType::LF: return { '\n', '\0' };
504  case SerialCommunicationHardwareAdapterParams::LineEndingType::CRLF: return { '\r', '\n' };
505  case SerialCommunicationHardwareAdapterParams::LineEndingType::CR: return { '\r', '\0' };
506  default: return { '\0', '\0' };
507  }
508  }
509 
511  {
513  return 0;
514  if (LineEnding == SerialCommunicationHardwareAdapterParams::LineEndingType::CRLF)
515  return 2;
516 
517  return 1;
518  }
519 
524  {
525  public:
531 
533 
534  virtual const char* GetParamClassTag() const noexcept override { return "QSerialCommunicationHardwareAdapterParams"; }
535 
536  private:
539 
540  DummyParam Dummy = { *this };
541  };
542 
547  {
548  public:
551 
554  };
555 
564  {
565  Q_OBJECT
566 
567  public:
570 
571  public slots:
572  void Open() { OpenChild(); }
573  void Close() { CloseChild(); }
574  void Reset() { ResetChild(); }
575  void Clear() { ClearChild(); }
576  void Flush() { FlushChild(); }
577  void Read() { ReadChild(); }
578  void Write(const QString String) { WriteChild(String); }
579  void Write_endl() { Write_endl_Child(); }
580 
581  protected:
586  void SetCommunicationChannelOpened() const noexcept;
587 
592  void SetCommunicationChannelClosed() const noexcept;
593 
598  void DataRead(const std::string& String) const;
599 
606  template <typename ExceptionType>
607  void SetException(const ExceptionType& Exception) const;
608 
609  private:
610  virtual void OpenChild() = 0;
611  virtual void CloseChild() = 0;
612  virtual void ResetChild() = 0;
613  virtual void ClearChild() = 0;
614  virtual void FlushChild() = 0;
615  virtual void ReadChild() = 0;
616  virtual void WriteChild(const QString& String) = 0;
617  virtual void Write_endl_Child() = 0;
618  };
619 
629  {
630  Q_OBJECT
631 
632  private:
638  {
641 
647 
648  void SetCommunicationChannelOpened() const noexcept { Parent.SetCommunicationChannelOpened(); }
649  void SetCommunicationChannelClosed() const noexcept { Parent.SetCommunicationChannelClosed(); }
650  void DataRead(const std::string& String) const { Parent.DataRead(String); }
651 
655  template <typename ExceptionType>
656  void SetException(const ExceptionType& Exception) const { Parent.SetException(Exception); }
657 
659  };
660 
661  public:
664  using QWorkerPtrType = std::unique_ptr<QSerialCommunicationHardwareAdapterWorker>;
665 
668 
670 
671  private:
676 
683  virtual QWorkerPtrType MakeWorker() = 0;
684 
689  virtual void InitWorker() {}
691 
692  void ResetImpl(dispatch_tag<SerialCommunicationHardwareAdapter>) override final;
694 
699  void EnsureReadyStateChild() override final;
700 
701  bool IsReadyChild() const override final;
702  bool IsConnectedChild() const noexcept override final;
703 
709  void SetCommunicationChannelOpened() const noexcept { CommunicationChannelOpened = true; }
710  void SetCommunicationChannelClosed() const noexcept { CommunicationChannelOpened = false; }
711 
716  void DataRead(const std::string& String) const;
717 
724  template <typename ExceptionType>
725  void SetException(const ExceptionType& Exception) const noexcept
726  {
727  try
728  {
730 
731  auto ExceptionPtr = std::make_exception_ptr(Exception);
732  SetExceptionUnsafe(ExceptionPtr);
733  PendingException = ExceptionPtr;
734  }
735  catch (...)
736  {
737  // Swallow exception if an error occurs setting it. This is not optimal but there
738  // is no other way since exceptions are not allowed to leave Qt's slots.
739  }
740  }
742 
747  void ClearChild() const override final;
748  void FlushChild() const override final;
749  std::string Read() const override final;
750  void Write(const std::string& String) const override final;
751  void Write_endl() const override final;
752 
759  void ThrowPendingException() const;
761 
762  signals:
768  void OpenSig();
769  void CloseSig();
770  void ResetSig();
771  void ClearSig() const;
772  void FlushSig() const;
773  void ReadSig() const;
774  void WriteSig(const QString String) const;
775  void Write_endl_Sig() const;
777 
778  private:
780  mutable std::atomic<bool> CommunicationChannelOpened;
781  mutable std::exception_ptr PendingException;
782  };
783 
784  // Needs to be defined after declaration of class QSerialCommunicationHardwareAdapter.
785  template <typename ExceptionType>
786  void QSerialCommunicationHardwareAdapterWorker::SetException(const ExceptionType& Exception) const
787  {
788  auto Owner = std::dynamic_pointer_cast<const QSerialCommunicationHardwareAdapter>(GetOwner());
789  if (!Owner)
790  return;
791 
792  Owner->QSerialCommunicationHardwareAdapterWorkerOnly.SetException(Exception);
793  }
794 }
795 
800 namespace DynExpHardware {};
Implementation of DynExp objects as the base for derived resources and implementation of the object p...
The configurator classes have the task to generate parameter objects (refer to DynExp::ParamsBase) of...
Definition: Object.h:1879
DynExp's core class acts as the interface between the user interface and DynExp's internal data like ...
Definition: DynExpCore.h:127
Defines the base class for a hardware adapter object. Hardware adapters describe interfaces/connectio...
HardwareAdapterBase(const std::thread::id OwnerThreadID, ParamsBasePtrType &&Params)
Constructs a hardware adapter instance.
void EnsureReadyStateChild([[maybe_unused]] bool IsAutomaticStartup) override final
Ensures that this Object instance is ready by possibly starting its worker thread or by opening conne...
void ThrowException(std::exception_ptr Exception) const
Stores Exception in LastException, wraps it in a Util::ForwardedException and throws the wrapped exce...
constexpr static auto Category() noexcept
Every derived class has to redefine this function.
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.
virtual bool IsConnectedChild() const noexcept=0
Determines the connection status of the hardware interface.
bool IsConnected() const noexcept
Determines the connection status of the hardware interface.
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 std::string GetCategory() const override
Returns the category of this Object type.
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.
virtual void ResetImpl(dispatch_tag< HardwareAdapterBase >)=0
Refer to DynExp::Object::Reset(). Using tag dispatch mechanism to ensure that ResetImpl() of every de...
Configurator class for HardwareAdapterBase.
Parameter class for HardwareAdapterBase.
void ConfigureParamsImpl(dispatch_tag< ParamsBase >) override final
Called by DynExp::ParamsBase::ConfigureParams() as a starting point for the tag dispatch mechanism to...
DummyParam Dummy
Dummy parameter which is to be owned once by parameter classes that do not contain any other paramete...
virtual const char * GetParamClassTag() const noexcept override
This function is intended to be overridden once in each derived class returning the name of the respe...
HardwareAdapterParamsBase(ItemIDType ID, const DynExpCore &Core)
Constructs the parameters for a HardwareAdapterBase instance.
virtual void ConfigureParamsImpl(dispatch_tag< HardwareAdapterParamsBase >)
Called by DynExp::ParamsBase::ConfigureParams() as a starting point for the tag dispatch mechanism to...
Base class for all DynExp Objects like hardware adapters (DynExp::HardwareAdapterBase),...
Definition: Object.h:1971
virtual bool IsReadyChild() const =0
Returns wheter this Object instance is ready (e.g. it is running or connected to a hardware device) a...
const std::thread::id OwnerThreadID
Thread id of the thread which has constructed (and owns) this Object instance.
Definition: Object.h:2302
const ParamsBasePtrType Params
Pointer to the parameter class instance belonging to this Object instance.
Definition: Object.h:2303
Refer to ParamsBase::dispatch_tag.
Definition: Object.h:2018
Dummy parameter which is to be owned once by parameter classes that do not contain any other paramete...
Definition: Object.h:522
Abstract base class for object parameter classes. Each class derived from class Object must be accomp...
Definition: Object.h:326
const ItemIDType ID
ID of the Object this parameter class instance belongs to.
Definition: Object.h:1779
const DynExpCore & Core
Reference to DynExp's core.
Definition: Object.h:1780
Tag for function dispatching mechanism within this class used when derived classes are not intended t...
Definition: Object.h:349
Configurator class for QSerialCommunicationHardwareAdapter.
Parameter class for QSerialCommunicationHardwareAdapter.
virtual void ConfigureParamsImpl(dispatch_tag< QSerialCommunicationHardwareAdapterParams >)
Called by DynExp::ParamsBase::ConfigureParams() as a starting point for the tag dispatch mechanism to...
virtual const char * GetParamClassTag() const noexcept override
This function is intended to be overridden once in each derived class returning the name of the respe...
void ConfigureParamsImpl(dispatch_tag< SerialCommunicationHardwareAdapterParams >) override final
Called by DynExp::ParamsBase::ConfigureParams() as a starting point for the tag dispatch mechanism to...
QSerialCommunicationHardwareAdapterParams(ItemIDType ID, const DynExpCore &Core)
Constructs the parameters for a QSerialCommunicationHardwareAdapter instance.
Qt worker which performs actual serial communication hardware operations in a separate thread in orde...
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.
Allow exclusive access to some of QSerialCommunicationHardwareAdapter's private methods to QSerialCom...
void SetException(const ExceptionType &Exception) const
Sets Exception as PendingException and stores it by calling HardwareAdapterBase::SetExceptionUnsafe()...
QSerialCommunicationHardwareAdapter & Parent
Owning QSerialCommunicationHardwareAdapter instance.
void DataRead(const std::string &String) const
Passes data to the hardware adapter which has been received by the worker instance.
void SetCommunicationChannelClosed() const noexcept
Sets CommunicationChannelOpened to false.
constexpr QSerialCommunicationHardwareAdapterWorkerOnlyType(QSerialCommunicationHardwareAdapter &Parent) noexcept
Construcs an instance - one for each QSerialCommunicationHardwareAdapter instance.
void SetCommunicationChannelOpened() const noexcept
Sets CommunicationChannelOpened to true.
SerialCommunicationHardwareAdapter is based on a Qt communication object (wrapped by QSerialCommunica...
virtual void InitWorker()
Tells the worker instance to perform initialization steps, e.g. by emitting a Qt signal which is rece...
void SetException(const ExceptionType &Exception) const noexcept
Sets Exception as PendingException and stores it by calling HardwareAdapterBase::SetExceptionUnsafe()...
QSerialCommunicationHardwareAdapterWorkerOnlyType QSerialCommunicationHardwareAdapterWorkerOnly
Allow exclusive access to some of QSerialCommunicationHardwareAdapter's private methods to QSerialCom...
std::unique_ptr< QSerialCommunicationHardwareAdapterWorker > QWorkerPtrType
Pointer-type owning the related QSerialCommunicationHardwareAdapterWorker instance.
virtual void ResetImpl(dispatch_tag< QSerialCommunicationHardwareAdapter >)
Refer to DynExp::Object::Reset(). Using tag dispatch mechanism to ensure that ResetImpl() of every de...
void SetCommunicationChannelClosed() const noexcept
Sets CommunicationChannelOpened to false.
virtual QWorkerPtrType MakeWorker()=0
Abstract factory function for a worker instance derived from QSerialCommunicationHardwareAdapterWorke...
Defines an exception caused by a serial communication operation of a hardware adapter.
SerialCommunicationException(std::string Description, const int ErrorCode, const std::source_location Location=std::source_location::current()) noexcept
Constructs a SerialCommunicationException instance.
Configurator class for SerialCommunicationHardwareAdapter.
Parameter class for SerialCommunicationHardwareAdapter.
LineEndingType
Possible line endings sent after writing a line and used to determine the end of a line while reading...
void ConfigureParamsImpl(dispatch_tag< HardwareAdapterParamsBase >) override final
Called by DynExp::ParamsBase::ConfigureParams() as a starting point for the tag dispatch mechanism to...
virtual void ConfigureParamsImpl(dispatch_tag< SerialCommunicationHardwareAdapterParams >)
Called by DynExp::ParamsBase::ConfigureParams() as a starting point for the tag dispatch mechanism to...
virtual const char * GetParamClassTag() const noexcept override
This function is intended to be overridden once in each derived class returning the name of the respe...
SerialCommunicationHardwareAdapterParams(ItemIDType ID, const DynExpCore &Core)
Constructs the parameters for a SerialCommunicationHardwareAdapter instance.
Defines a hardware adapter for serial communication. Logical const-ness: see declaration of class Dyn...
constexpr static unsigned int GetLineEndingLength(SerialCommunicationHardwareAdapterParams::LineEndingType LineEnding) noexcept
Determines the amount of characters required to express a line ending type defined by SerialCommunica...
auto & operator<<(const T &Value) const
Writes the input parameter calling Write(). Also writes endl at the end automatically.
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.
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.
constexpr static std::array< char, 2 > LineEndingToChar(SerialCommunicationHardwareAdapterParams::LineEndingType LineEnding) noexcept
Converts SerialCommunicationHardwareAdapterParams::LineEndingType to two characters being used as the...
virtual void ResetImpl(dispatch_tag< SerialCommunicationHardwareAdapter >)
Refer to DynExp::Object::Reset(). Using tag dispatch mechanism to ensure that ResetImpl() of every de...
virtual std::string Read() const =0
Reads a string from the underlying hardware interface.
virtual std::string GetName() const override
Returns the name of this Object type.
virtual std::string GetCategory() const override
Returns the category of this Object type.
auto GetLineEnding() const
Getter for LineEnding. Thread-safe since LineEnding only changes when Object::Reset() is called (itse...
constexpr static auto Name() noexcept
Every derived class has to redefine this function.
std::stringstream ReadBuffer
Buffer storing data read from the underlying physical hardware.
constexpr static auto Category() noexcept
Every derived class has to redefine this function.
Type denoting the end of a line when piped to operator<<(endl)
Interface to allow synchronizing the access to derived classes between different threads by providing...
Definition: Util.h:56
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
Implements a QObject belonging to a hardware adapter (derived from DynExp::HardwareAdapterBase) that ...
Definition: QtUtil.h:327
DynExp's hardware namespace contains the implementation of DynExp hardware adapters which extend DynE...
@ None
Unspecified/arbitrary waveform.
DynExp's main namespace contains the implementation of DynExp including classes to manage resources (...
HardwareAdapterPtrType MakeHardwareAdapter(const std::thread::id OwnerThreadID, ParamsBasePtrType &&Params)
Factory function to generate a hardware adapter of a specific type.
std::unique_ptr< ParamsBase > ParamsBasePtrType
Alias for a pointer to the parameter system base class ParamsBase.
Definition: Object.h:1807
size_t ItemIDType
ID type of objects/items managed by DynExp.
ConfiguratorBasePtrType MakeHardwareAdapterConfig()
Factory function to generate a configurator for a specific hardware adapter type.
std::shared_ptr< ConfiguratorBase > ConfiguratorBasePtrType
Alias for a pointer to the configurator base class ConfiguratorBase.
Definition: Object.h:1959
std::shared_ptr< HardwareAdapterBase > HardwareAdapterPtrType
Pointer type to store a hardware adapter (DynExp::HardwareAdapterBase) with.
DynExp's Util namespace contains commonly used functions and templates as well as extensions to Qt an...
Definition: circularbuf.cpp:7
std::string ToStr(const T &Value, int Precision=-1)
Converts a (numeric) value of type T to a std::string using operator<< of std::stringstream.
Definition: Util.h:625
std::ostream & operator<<(std::ostream &stream, const Exception &e)
Writes a DynExp exception in a user-readable way to a stream.
Definition: Exception.cpp:17
ErrorType
DynExp's error types
Definition: Exception.h:15
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.