diff --git a/api/python/arrus/ops/us4r.py b/api/python/arrus/ops/us4r.py index 76d42f7a6..338cf1610 100644 --- a/api/python/arrus/ops/us4r.py +++ b/api/python/arrus/ops/us4r.py @@ -143,7 +143,7 @@ class Scheme: :param rx_buffer_size: number of elements the rx buffer (allocated on \ us4r ddr internal memory) should consists of :param output_buffer: specification of the output buffer - :param work_mode: determines the system work mode, available values: 'ASYNC', 'HOST' + :param work_mode: determines the system work mode, available values: 'ASYNC', 'HOST', 'MANUAL' :param processing: data processing to perform on the raw channel RF data \ currently only arrus.utils.imaging is supported """ diff --git a/api/python/arrus/session.py b/api/python/arrus/session.py index 4ff092fec..08cc403aa 100644 --- a/api/python/arrus/session.py +++ b/api/python/arrus/session.py @@ -201,6 +201,17 @@ def stop_scheme(self): if self._current_processing is not None: self._current_processing.stop() + def run(self): + """ + Runs the uploaded scheme. + + The behaviour of this method depends on the work mode: + - MANUAL: triggers execution of batch of sequences only ONCE, + - HOST, ASYNC: triggers execution of batch of sequences IN A LOOP (Host: trigger is on buffer element release). + The run function can be called only once (before the scheme is stopped). + """ + self._session_handle.run() + def get_device(self, path: str): """ Returns a device identified by a given id. diff --git a/api/python/arrus/utils/core.py b/api/python/arrus/utils/core.py index b16638154..00d79f670 100644 --- a/api/python/arrus/utils/core.py +++ b/api/python/arrus/utils/core.py @@ -109,7 +109,8 @@ def convert_to_core_scheme(scheme): core_work_mode = { "ASYNC": arrus.core.Scheme.WorkMode_ASYNC, - "HOST": arrus.core.Scheme.WorkMode_HOST + "HOST": arrus.core.Scheme.WorkMode_HOST, + "MANUAL": arrus.core.Scheme.WorkMode_MANUAL }[scheme.work_mode] return arrus.core.Scheme(core_seq, rx_buffer_size, data_buffer_spec, diff --git a/arrus/core/api/ops/us4r/Scheme.h b/arrus/core/api/ops/us4r/Scheme.h index 37321d491..dd44c5a93 100644 --- a/arrus/core/api/ops/us4r/Scheme.h +++ b/arrus/core/api/ops/us4r/Scheme.h @@ -25,7 +25,9 @@ class Scheme { /** Trigger generated by us4r, error on overflow. */ ASYNC, /** Trigger generated by host, no error on overflow. */ - HOST + HOST, + /** New data acquisition and processing is manually triggered by user. */ + MANUAL }; /** diff --git a/arrus/core/api/session/Session.h b/arrus/core/api/session/Session.h index 714118817..6507d2240 100644 --- a/arrus/core/api/session/Session.h +++ b/arrus/core/api/session/Session.h @@ -57,6 +57,16 @@ class Session { */ virtual void stopScheme() = 0; + /** + * Runs the uploaded scheme. + * + * The behaviour of this method depends on the work mode: + * - MANUAL: triggers execution of batch of sequences only ONCE, + * - HOST, ASYNC: triggers execution of batch of sequences IN A LOOP (Host: trigger is on buffer element release). + * The run function can be called only once (before the scheme is stopped). + */ + virtual void run() = 0; + virtual ~Session() = default; }; diff --git a/arrus/core/devices/probe/ProbeImpl.cpp b/arrus/core/devices/probe/ProbeImpl.cpp index 982c2aafe..51bda1e61 100644 --- a/arrus/core/devices/probe/ProbeImpl.cpp +++ b/arrus/core/devices/probe/ProbeImpl.cpp @@ -139,8 +139,8 @@ void ProbeImpl::syncTrigger() { void ProbeImpl::registerOutputBuffer(Us4ROutputBuffer *buffer, const Us4RBuffer::Handle &us4rBuffer, - bool isTriggerSync) { - adapter->registerOutputBuffer(buffer, us4rBuffer, isTriggerSync); + ::arrus::ops::us4r::Scheme::WorkMode workMode) { + adapter->registerOutputBuffer(buffer, us4rBuffer, workMode); } // Remaps FCM according to given rx aperture active channels mappings. diff --git a/arrus/core/devices/probe/ProbeImpl.h b/arrus/core/devices/probe/ProbeImpl.h index bae565554..67e8fd579 100644 --- a/arrus/core/devices/probe/ProbeImpl.h +++ b/arrus/core/devices/probe/ProbeImpl.h @@ -36,7 +36,8 @@ class ProbeImpl : public ProbeImplBase { void syncTrigger() override; - void registerOutputBuffer(Us4ROutputBuffer *buffer, const Us4RBuffer::Handle &us4rBuffer, bool isTriggerSync) override; + void registerOutputBuffer(Us4ROutputBuffer *buffer, const Us4RBuffer::Handle &us4rBuffer, + ::arrus::ops::us4r::Scheme::WorkMode workMode) override; static FrameChannelMapping::Handle remapFcm(const FrameChannelMapping::Handle &adapterFcm, const std::vector> &adapterActiveChannels, diff --git a/arrus/core/devices/probe/ProbeImplBase.h b/arrus/core/devices/probe/ProbeImplBase.h index 4e2a9914d..3187d3587 100644 --- a/arrus/core/devices/probe/ProbeImplBase.h +++ b/arrus/core/devices/probe/ProbeImplBase.h @@ -2,6 +2,7 @@ #define ARRUS_CORE_DEVICES_PROBE_PROBEIMPLBASE_H #include "arrus/core/api/devices/probe/Probe.h" +#include "arrus/core/api/ops/us4r/Scheme.h" #include "arrus/core/devices/us4r/Us4RBuffer.h" #include "arrus/core/devices/UltrasoundDevice.h" @@ -18,7 +19,8 @@ class ProbeImplBase : public Probe, public UltrasoundDevice { setTxRxSequence(const std::vector &seq, const ops::us4r::TGCCurve &tgcSamples, uint16 rxBufferSize, uint16 rxBatchSize, std::optional sri, bool triggerSync) = 0; - virtual void registerOutputBuffer(Us4ROutputBuffer *, const Us4RBuffer::Handle &, bool isTriggerSync) = 0; + virtual void registerOutputBuffer(Us4ROutputBuffer *, const Us4RBuffer::Handle &, + ::arrus::ops::us4r::Scheme::WorkMode workMode) = 0; }; } diff --git a/arrus/core/devices/us4r/Us4RImpl.cpp b/arrus/core/devices/us4r/Us4RImpl.cpp index 89a320796..1c1fc8629 100644 --- a/arrus/core/devices/us4r/Us4RImpl.cpp +++ b/arrus/core/devices/us4r/Us4RImpl.cpp @@ -13,6 +13,7 @@ namespace arrus::devices { using ::arrus::ops::us4r::TxRxSequence; +using ::arrus::ops::us4r::Scheme; using ::arrus::ops::us4r::Tx; using ::arrus::ops::us4r::Rx; using ::arrus::ops::us4r::Pulse; @@ -95,8 +96,8 @@ Us4RImpl::upload(const ops::us4r::TxRxSequence &seq, } // Upload and register buffers. - bool isTriggerSync = workMode == ::arrus::ops::us4r::Scheme::WorkMode::HOST; - auto[rxBuffer, fcm] = uploadSequence(seq, rxBufferNElements, 1, isTriggerSync); + bool useTriggerSync = workMode == Scheme::WorkMode::HOST || workMode == Scheme::WorkMode::MANUAL; + auto[rxBuffer, fcm] = uploadSequence(seq, rxBufferNElements, 1, useTriggerSync); ARRUS_REQUIRES_TRUE(!rxBuffer->empty(), "Us4R Rx buffer cannot be empty."); // Calculate how much of the data each Us4OEM produces. @@ -115,7 +116,7 @@ Us4RImpl::upload(const ops::us4r::TxRxSequence &seq, } // Create output buffer. this->buffer = std::make_shared(us4oemComponentSize, shape, dataType, hostBufferNElements); - getProbeImpl()->registerOutputBuffer(this->buffer.get(), rxBuffer, isTriggerSync); + getProbeImpl()->registerOutputBuffer(this->buffer.get(), rxBuffer, workMode); return {this->buffer, std::move(fcm)}; } @@ -184,7 +185,7 @@ Us4RImpl::uploadSequence(const ops::us4r::TxRxSequence &seq, uint16_t rxBufferSi triggerSync); } -void Us4RImpl::syncTrigger() { +void Us4RImpl::trigger() { this->getDefaultComponent()->syncTrigger(); } diff --git a/arrus/core/devices/us4r/Us4RImpl.h b/arrus/core/devices/us4r/Us4RImpl.h index 48ff74599..0aaadb7d9 100644 --- a/arrus/core/devices/us4r/Us4RImpl.h +++ b/arrus/core/devices/us4r/Us4RImpl.h @@ -106,6 +106,8 @@ class Us4RImpl : public Us4R { void stop() override; + void trigger(); + void setVoltage(Voltage voltage) override; void disableHV() override; @@ -127,8 +129,6 @@ class Us4RImpl : public Us4R { void stopDevice(); - void syncTrigger(); - std::tuple uploadSequence(const ops::us4r::TxRxSequence &seq, uint16_t rxBufferSize, uint16_t rxBatchSize, bool triggerSync); diff --git a/arrus/core/devices/us4r/probeadapter/ProbeAdapterImpl.cpp b/arrus/core/devices/us4r/probeadapter/ProbeAdapterImpl.cpp index 8b6d7392c..6bbcb7663 100644 --- a/arrus/core/devices/us4r/probeadapter/ProbeAdapterImpl.cpp +++ b/arrus/core/devices/us4r/probeadapter/ProbeAdapterImpl.cpp @@ -12,6 +12,7 @@ namespace arrus::devices { using namespace ::arrus::ops::us4r; +using ::arrus::ops::us4r::Scheme; ProbeAdapterImpl::ProbeAdapterImpl(DeviceId deviceId, ProbeAdapterModelId modelId, @@ -292,18 +293,18 @@ void ProbeAdapterImpl::syncTrigger() { } void ProbeAdapterImpl::registerOutputBuffer(Us4ROutputBuffer *buffer, const Us4RBuffer::Handle &us4rBuffer, - bool isTriggerSync) { + ::arrus::ops::us4r::Scheme::WorkMode workMode) { Ordinal us4oemOrdinal = 0; for (auto &us4oem: us4oems) { auto us4oemBuffer = us4rBuffer->getUs4oemBuffer(us4oemOrdinal); - registerOutputBuffer(buffer, us4oemBuffer, us4oem, - isTriggerSync); + registerOutputBuffer(buffer, us4oemBuffer, us4oem, workMode); ++us4oemOrdinal; } } void ProbeAdapterImpl::registerOutputBuffer(Us4ROutputBuffer *outputBuffer, const Us4OEMBuffer &us4oemBuffer, - Us4OEMImplBase::RawHandle us4oem, bool isTriggerSync) { + Us4OEMImplBase::RawHandle us4oem, + ::arrus::ops::us4r::Scheme::WorkMode workMode) { // Each transfer should have the same size. std::unordered_set sizes; for (auto &element: us4oemBuffer.getElements()) { @@ -398,7 +399,8 @@ void ProbeAdapterImpl::registerOutputBuffer(Us4ROutputBuffer *outputBuffer, cons for (size_t i = 0; i < nRepeats; ++i) { std::function releaseFunc; - if (!isTriggerSync) { + if (workMode == Scheme::WorkMode::ASYNC || workMode == Scheme::WorkMode::MANUAL) { + // The TX/RX is triggered by us4OEM or user. releaseFunc = [this, nUs4OEM, startFiring, endFiring]() { for (auto &us4oem: this->us4oems) { us4oem->getIUs4oem()->MarkEntriesAsReadyForTransfer(startFiring, endFiring); diff --git a/arrus/core/devices/us4r/probeadapter/ProbeAdapterImpl.h b/arrus/core/devices/us4r/probeadapter/ProbeAdapterImpl.h index 42c32c4bc..2afdb021a 100644 --- a/arrus/core/devices/us4r/probeadapter/ProbeAdapterImpl.h +++ b/arrus/core/devices/us4r/probeadapter/ProbeAdapterImpl.h @@ -45,10 +45,8 @@ class ProbeAdapterImpl : public ProbeAdapterImplBase { void syncTrigger() override; - void - registerOutputBuffer(Us4ROutputBuffer *buffer, - const Us4RBuffer::Handle &us4rBuffer, - bool isTriggerSync); + void registerOutputBuffer(Us4ROutputBuffer *buffer, const Us4RBuffer::Handle &us4rBuffer, + ::arrus::ops::us4r::Scheme::WorkMode workMode) override; private: Logger::Handle logger; @@ -57,10 +55,8 @@ class ProbeAdapterImpl : public ProbeAdapterImplBase { ChannelIdx numberOfChannels; ChannelMapping channelMapping; - void registerOutputBuffer(Us4ROutputBuffer *outputBuffer, - const Us4OEMBuffer &us4oemBuffer, - Us4OEMImplBase::RawHandle us4oem, - bool isTriggerSync); + void registerOutputBuffer(Us4ROutputBuffer *outputBuffer, const Us4OEMBuffer &us4oemBuffer, + Us4OEMImplBase::RawHandle us4oem, ::arrus::ops::us4r::Scheme::WorkMode workMode); Us4OEMImplBase::RawHandle getMasterUs4oem() const { return this->us4oems[0]; diff --git a/arrus/core/devices/us4r/probeadapter/ProbeAdapterImplBase.h b/arrus/core/devices/us4r/probeadapter/ProbeAdapterImplBase.h index 4ea6509cb..ff619c0c8 100644 --- a/arrus/core/devices/us4r/probeadapter/ProbeAdapterImplBase.h +++ b/arrus/core/devices/us4r/probeadapter/ProbeAdapterImplBase.h @@ -4,6 +4,7 @@ #include "arrus/core/api/devices/us4r/ProbeAdapter.h" #include "arrus/core/api/devices/us4r/FrameChannelMapping.h" #include "arrus/core/api/ops/us4r/tgc.h" +#include "arrus/core/api/ops/us4r/Scheme.h" #include "arrus/core/devices/TxRxParameters.h" #include "arrus/core/devices/us4r/DataTransfer.h" #include "arrus/core/devices/us4r/Us4ROutputBuffer.h" @@ -26,9 +27,8 @@ class ProbeAdapterImplBase : public ProbeAdapter { bool triggerSync) = 0; virtual - void registerOutputBuffer(Us4ROutputBuffer *buffer, - const Us4RBuffer::Handle &transfers, - bool isTriggerSync) = 0; + void registerOutputBuffer(Us4ROutputBuffer *buffer, const Us4RBuffer::Handle &transfers, + ::arrus::ops::us4r::Scheme::WorkMode workMode) = 0; virtual Ordinal getNumberOfUs4OEMs() = 0; diff --git a/arrus/core/session/SessionImpl.cpp b/arrus/core/session/SessionImpl.cpp index ccfa8a5d0..59ea5e5c9 100644 --- a/arrus/core/session/SessionImpl.cpp +++ b/arrus/core/session/SessionImpl.cpp @@ -106,6 +106,10 @@ SessionImpl::~SessionImpl() { } UploadResult SessionImpl::upload(const ops::us4r::Scheme &scheme) { + std::lock_guard guard(stateMutex); + if(state == State::STARTED) { + throw IllegalStateException("Stop the scheme first, before uploading new scheme"); + } auto us4r = (::arrus::devices::Us4R *) getDevice(DeviceId(DeviceType::Us4R, 0)); auto &outputBufferSpec = scheme.getOutputBuffer(); auto[buffer, fcm] = us4r->upload(scheme.getTxRxSequence(), scheme.getRxBufferSize(), @@ -114,18 +118,40 @@ UploadResult SessionImpl::upload(const ops::us4r::Scheme &scheme) { std::unordered_map> metadataMap; metadataMap.emplace("frameChannelMapping", std::move(fcm)); auto constMetadata = std::make_shared(metadataMap); + currentScheme = scheme; return UploadResult(buffer, constMetadata); } void SessionImpl::startScheme() { + std::lock_guard guard(stateMutex); auto us4r = (::arrus::devices::Us4R *) getDevice(DeviceId(DeviceType::Us4R, 0)); us4r->start(); + state = State::STARTED; } void SessionImpl::stopScheme() { + std::lock_guard guard(stateMutex); auto us4r = (::arrus::devices::Us4R *) getDevice(DeviceId(DeviceType::Us4R, 0)); us4r->stop(); + state = State::STOPPED; } +void SessionImpl::run() { + std::lock_guard guard(stateMutex); + if(!currentScheme.has_value()) { + throw IllegalStateException("Upload scheme before running."); + } + if(state == State::STOPPED) { + startScheme(); + } else { + if(currentScheme.value().getWorkMode() == ops::us4r::Scheme::WorkMode::MANUAL) { + auto us4r = (::arrus::devices::Us4RImpl *)getDevice(DeviceId(DeviceType::Us4R, 0)); + us4r->trigger(); + } + else { + throw IllegalStateException("Scheme already started."); + } + } +} } \ No newline at end of file diff --git a/arrus/core/session/SessionImpl.h b/arrus/core/session/SessionImpl.h index 6f9294ea2..02cbfc860 100644 --- a/arrus/core/session/SessionImpl.h +++ b/arrus/core/session/SessionImpl.h @@ -2,8 +2,9 @@ #define ARRUS_CORE_SESSION_SESSIONIMPL_H #include -#include +#include +#include #include "arrus/core/api/session/Session.h" #include "arrus/core/common/hash.h" #include "arrus/core/devices/DeviceId.h" @@ -30,6 +31,8 @@ class SessionImpl : public Session { void stopScheme() override; + void run() override; + SessionImpl(SessionImpl const &) = delete; void operator=(SessionImpl const &) = delete; @@ -39,6 +42,11 @@ class SessionImpl : public Session { void operator=(SessionImpl const &&) = delete; private: + enum class State { + STARTED, + STOPPED + }; + using DeviceMap = std::unordered_map< arrus::devices::DeviceId, arrus::devices::Device::Handle, @@ -49,6 +57,9 @@ class SessionImpl : public Session { DeviceMap devices; arrus::devices::Us4RFactory::Handle us4rFactory; + std::recursive_mutex stateMutex; + std::optional currentScheme; + State state{State::STOPPED}; };