DynExp
Highly flexible laboratory automation for dynamically changing experiments.
Loading...
Searching...
No Matches
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
7namespace DynExp
8{
12
16
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 {
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 {
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 {
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
118
131
135
139
141 : HardwareAdapterBase(OwnerThreadID, std::move(Params))
142 {
143 Init();
144 }
145
147 {
149
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
172
173 auto Text = ReadBuffer.str().substr(ReadBuffer.tellg());
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
201
209
211 {
212 OutStream << ReadAll();
213
214 return *this;
215 }
216
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
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
348
352
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
455
462
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
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.
static constexpr 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.
static constexpr auto GetMaxBufferSize() noexcept
Defines the maximal size of the hardware adapter's (read) buffer.
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.
void Flush() const
Flushes the underlying hardware interface and calls ReadIntoBuffer().
static constexpr 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
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.