-
Notifications
You must be signed in to change notification settings - Fork 0
Reader review branch #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| /* | ||
| * Copyright 2022-2026 openDAQ d.o.o. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| #pragma once | ||
|
|
||
| template <typename Enum> | ||
| class EnumFlags | ||
| { | ||
| static_assert(std::is_enum_v<Enum>, "EnumFlags requires an enum type"); | ||
|
|
||
| public: | ||
| using Underlying = std::underlying_type_t<Enum>; | ||
|
|
||
| constexpr EnumFlags() = default; | ||
|
|
||
| constexpr EnumFlags(Enum value) | ||
| : value(static_cast<Underlying>(value)) | ||
| { | ||
| } | ||
|
|
||
| constexpr bool empty() const | ||
| { | ||
| return value == 0; | ||
| } | ||
|
|
||
| constexpr bool contains(Enum flag) const | ||
| { | ||
| return (value & static_cast<Underlying>(flag)) != 0; | ||
| } | ||
|
|
||
| constexpr void add(Enum flag) | ||
| { | ||
| value |= static_cast<Underlying>(flag); | ||
| } | ||
|
|
||
| constexpr void remove(Enum flag) | ||
| { | ||
| value &= ~static_cast<Underlying>(flag); | ||
| } | ||
|
|
||
| constexpr void set(Enum flag, bool active) | ||
| { | ||
| if (active) | ||
| add(flag); | ||
| else | ||
| remove(flag); | ||
| } | ||
|
|
||
| constexpr void clear() | ||
| { | ||
| value = 0; | ||
| } | ||
|
|
||
| constexpr Underlying raw() const | ||
| { | ||
| return value; | ||
| } | ||
|
|
||
| private: | ||
| Underlying value = 0; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,199 @@ | ||
| /* | ||
| * Copyright 2022-2026 openDAQ d.o.o. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| #pragma once | ||
| #include <opendaq/data_descriptor_ptr.h> | ||
| #include <opendaq/event_packet_ptr.h> | ||
| #include <opendaq/enum_flags.h> | ||
| #include <opendaq/input_port_config_ptr.h> | ||
| #include <opendaq/logger_component_ptr.h> | ||
| #include <opendaq/sample_type.h> | ||
| #include <opendaq/sample_reader.h> | ||
| #include <opendaq/typed_reading_utils.h> | ||
|
|
||
| #include <deque> | ||
|
|
||
| BEGIN_NAMESPACE_OPENDAQ | ||
|
|
||
| enum class SignalEventType | ||
| { | ||
| NoChange = 0, | ||
| ValueChanged, | ||
| DomainChanged, | ||
| DomainAndValueChanged, | ||
| Gap | ||
| }; | ||
|
|
||
| class SignalEvent | ||
| { | ||
| public: | ||
| SignalEvent(const EventPacketPtr& packet); | ||
|
|
||
| static SignalEvent syncGapEvent(Int gapDiff); | ||
|
|
||
| bool merge(const SignalEvent& otherEvent); | ||
| SignalEventType getType() const; | ||
| const DataDescriptorPtr& getDomainDescriptor() const; | ||
| const DataDescriptorPtr& getValueDescriptor() const; | ||
|
|
||
| EventPacketPtr toEventPacket() const; | ||
|
|
||
| private: | ||
| explicit SignalEvent(Int gapDiff); | ||
|
|
||
| void updateType(); | ||
| private: | ||
| SignalEventType eventType; | ||
| DataDescriptorPtr domainDescriptor; | ||
| DataDescriptorPtr valueDescriptor; | ||
| Int gapDiff; | ||
| }; | ||
|
|
||
|
|
||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Whitespace |
||
| enum class AdvanceResult | ||
| { | ||
| Success = 0, | ||
| NeedMoreData, | ||
| DomainChanged, | ||
| OvershotError, | ||
| Error | ||
| }; | ||
|
|
||
| enum class QueueReaderIssue : uint32_t | ||
| { | ||
| None = 0, | ||
| ValueTypesNotConvertible = 1 << 0, | ||
| DomainTypesNotConvertible = 1 << 1, | ||
| UnsupportedDomainRule = 1 << 2, | ||
| OriginParsingFailed = 1 << 3, | ||
| DomainUnitInvalid = 1 << 4 | ||
| }; | ||
|
|
||
| class QueueReader | ||
| { | ||
| public: | ||
| explicit QueueReader(const InputPortConfigPtr& port, // Consider using Connection instead | ||
| SampleType valueReadType, | ||
| SampleType domainReadType, | ||
| ReadMode mode, | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this ReadMode needs some clarification and how it interacts with the value/domain read types. |
||
| const LoggerComponentPtr& logger, | ||
| bool globalIdFromSignal); | ||
|
|
||
| public: | ||
| DomainInfo getDomainInfo(); | ||
| std::unique_ptr<DomainValue> getFirstSampleDomainValue(); | ||
|
Comment on lines
+95
to
+96
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This feels a bit odd, since DomainInfo is actually a part of DomainValue. It seems that two things are being mixed together. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The signal has a domain but the domain value also carries a copy of its domain info just to be a meaningful time point by itself. Is the copy problematic? |
||
| AdvanceResult advanceToDomainValue(const DomainValue* domainValue); | ||
| Int getSampleRate(); | ||
|
|
||
| void dropOutdatedPacketSegments(); | ||
|
|
||
| /** | ||
| * @brief Get the Available Samples in common rate equivalent | ||
| * | ||
| * @return SizeT Available samples multiplied by the sample rate divider. | ||
| */ | ||
| SizeT getAvailableSamples(); | ||
|
|
||
| bool hasPendingEvents(); | ||
| EventPacketPtr popFrontEvent(); | ||
|
|
||
| bool isValid(); | ||
|
|
||
| void domainChangeHandled(); | ||
| void updateConnection(); | ||
|
|
||
| void setSampleRateDivider(SizeT divider); | ||
| SizeT getSampleRateDivider() const; | ||
|
|
||
| /** | ||
| * @brief Read common rate equivalent samples into the buffer. There will be nativeSamples = count / sampleRateDivider | ||
| * samples read from the packets into the buffer. | ||
| * | ||
| * @param buffer Buffer that has capacity of at least count / sampleRateDivider | ||
| * @param count Desired sample count in common rate equivalent. | ||
| * @return AdvanceResult | ||
| */ | ||
| AdvanceResult read(void* valueBuffer, void* domainBuffer, SizeT* count); | ||
|
Comment on lines
+120
to
+128
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With this design, it feels like both the caller and queue reader must do the calculations to the common rate. Or rather, that the buffer size will be in the target count size, whereas the count variable will be in the common one. It seems like it'd make more sense to have the Additionally, does |
||
| AdvanceResult skip(SizeT* count); | ||
|
|
||
| /** | ||
| * @brief Drop the current data segment, if there are fewer than samplesInBlock samples available (common rate equivalent). | ||
| * | ||
| * For example: If there are 3 samples available before next event, samplesInBlock=10 (this is dividerLCM in terms of multireading) | ||
| * and divider for the queue reader is 2, then 5 native samples are required as minimum aligned read. Since 3 < 5, we drop the 3 samples | ||
| * and create a synchronization GAP event in the event queue. | ||
| * | ||
| * @param samplesInBlock Number of samples (common rate equivalent). | ||
| * @return true If samples were dropped. | ||
| * @return false If samples were not dropped - data segment is long enough or there is no event in the queue to end the segment. | ||
| */ | ||
| bool dropLeftoverSegment(SizeT samplesInBlock); | ||
|
|
||
| private: | ||
| void drainConnection(); | ||
| void adoptPackets(); | ||
| void consumeLeadingEventPackets(); | ||
|
|
||
| SizeT getAvailableSamplesNative(); | ||
| AdvanceResult readNative(void* valueBuffer, void* domainBuffer, SizeT* count); | ||
|
|
||
| void checkConnection() const; | ||
|
|
||
| SignalEventType addEncounteredEvent(const EventPacketPtr& packet); | ||
| void addToEventQueue(SignalEvent&& event); | ||
| void parseDomainDescriptor(); | ||
| void parseValueDescriptor(); | ||
| void parseCachedDescriptors(); | ||
| size_t getNumberOfEventPacketsInQueue(); | ||
| bool dropUntilEvent(); | ||
|
|
||
| private: | ||
| std::deque<PacketPtr> packets; | ||
| std::deque<SignalEvent> events; | ||
|
|
||
| SizeT readingPosition = 0; | ||
|
|
||
| InputPortConfigPtr port; | ||
| ConnectionPtr connection; | ||
|
|
||
| LoggerComponentPtr loggerComponent; | ||
|
|
||
| EnumFlags<QueueReaderIssue> issues; | ||
|
|
||
| ReadMode readMode; | ||
|
|
||
| struct TypedReadingContext | ||
| { | ||
| SampleType domainIn; | ||
| SampleType domainOut; | ||
| ReadLayout domainLayout; | ||
| DomainInfo domainInfo; | ||
| FunctionPtr domainTransform = nullptr; | ||
|
|
||
| SampleType valueIn; | ||
| SampleType valueOut; | ||
| ReadLayout valueLayout; | ||
| FunctionPtr valueTransform = nullptr; | ||
| }; | ||
| TypedReadingContext typeCtx; | ||
|
|
||
| Int sampleRate = -1; | ||
| Int packetDelta{0}; | ||
| bool domainChanged = false; | ||
|
|
||
| SizeT sampleRateDivider = 1; | ||
| }; | ||
|
|
||
| END_NAMESPACE_OPENDAQ | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a possibility that a descriptor is "un-set". Does this fall into the
XChangedevents?