Reader review branch - #1
Conversation
JakaMohorko
left a comment
There was a problem hiding this comment.
Will continue from the queue reader onwards in a separate review.
| struct DomainInfo | ||
| { | ||
| std::chrono::system_clock::time_point epoch; | ||
| RatioPtr resolution; |
There was a problem hiding this comment.
I wonder if we could use std::ratio internally. The openDAQ ratio object isn't really the most ideal for calculations.
There was a problem hiding this comment.
We could but this would mean that we duplicate some functionality that already exist and overload it for std::ratio (seemed like it's not ideal so I haven't commited to that yet).
| using SysPeriod = std::chrono::system_clock::period; | ||
| Int scaleNumerator = SysPeriod::num * commonDomain.resolution.getDenominator(); | ||
| Int scaleDenominator = SysPeriod::den * commonDomain.resolution.getNumerator(); | ||
| Int offsetFromCommon = epochOffset * scaleNumerator / scaleDenominator; |
There was a problem hiding this comment.
This calculation should be done somewhat carefully to not result in integer overflow. Also, when dividing by scaleDenominator, can this result in a non-integer number?
There was a problem hiding this comment.
Very unlikely. I think it is reasonable to expect that the later epoch is representible in ticks of common resolution. For example, epochs that are whole seconds satisfy this for all resolutions that are divisors of a unit.
| using SysPeriod = std::chrono::system_clock::period; | ||
| Int scaleNumerator = SysPeriod::num * commonDomain.resolution.getDenominator(); | ||
| Int scaleDenominator = SysPeriod::den * commonDomain.resolution.getNumerator(); | ||
| Int offsetFromCommon = epochOffset * scaleNumerator / scaleDenominator; | ||
| Type valueScaledToCommon = valueInCommon - offsetFromCommon; | ||
|
|
||
| Int multiplierNumerator = regularDomain.resolution.getNumerator() * commonDomain.resolution.getDenominator(); | ||
| Int multiplierDenominator = regularDomain.resolution.getDenominator() * commonDomain.resolution.getNumerator(); |
There was a problem hiding this comment.
This calculation is duplicated from above if I'm not mistaken. Could be merged into a helper method. It seems like the bottom code is the same.
There was a problem hiding this comment.
Yes, we could make a helper out of this
| if (den % num != 0) // 1 = k * num/den, the resolution is a fractional divider of a unit | ||
| DAQ_THROW_EXCEPTION(NotSupportedException, "Resolution must be aligned on full unit of domain"); |
There was a problem hiding this comment.
The exception here seems like it'll be hard to catch. It might be prudent to at least define new error codes here. If the user receives this error, it'll be hard to debug, especially as this is not thrown in case of bugs, but in case of incompatible signal connections.
It might be worth re-thinking a system for error handling in case of incompatibilities. Throwing exceptions feels a bit overkill.
| // Offset of current domain in common domain ticks | ||
| Int epochOffset = domain.epoch.time_since_epoch().count() - commonDomain.epoch.time_since_epoch().count(); | ||
|
|
||
| using SysPeriod = std::chrono::system_clock::period; | ||
| Int scaleNumerator = SysPeriod::num * commonDomain.resolution.getDenominator(); | ||
| Int scaleDenominator = SysPeriod::den * commonDomain.resolution.getNumerator(); | ||
| RangeValue offsetFromCommon = static_cast<RangeValue>(epochOffset * scaleNumerator / scaleDenominator); | ||
|
|
||
| // tick_common = tick * multiplier | ||
| Int multiplierNumerator = domain.resolution.getNumerator() * commonDomain.resolution.getDenominator(); | ||
| Int multiplierDenominator = domain.resolution.getDenominator() * commonDomain.resolution.getNumerator(); |
There was a problem hiding this comment.
Feels like a really similar calculation as above again.
|
|
||
| BEGIN_NAMESPACE_OPENDAQ | ||
|
|
||
| SignalEvent::SignalEvent(const EventPacketPtr& packet) |
There was a problem hiding this comment.
I think merging events is a good approach. I would potentially revisit the reader status API to see if there's a better way of portraying the events instead of forwarding event packets.
There was a problem hiding this comment.
Would be cool, but with the object having to be an API interface implementation and event packets being canonical, the reason for change would have to be strong.
| typeCtx.domainIn = SampleType::Undefined; | ||
| typeCtx.domainOut = domainReadType; | ||
| typeCtx.valueIn = SampleType::Undefined; | ||
| typeCtx.valueOut = mode == ReadMode::RawValue ? SampleType::Undefined : valueReadType; |
There was a problem hiding this comment.
As mentioned above, we need to clarify how the read mode and value read type interact (and that Unscaled actually does nothing).
| // TODO: Add first timestamp mechanism for sync tolerance checking | ||
| checkConnection(); | ||
| drainConnection(); |
There was a problem hiding this comment.
Could this be a separate step that's potentially called externally? It seems that this needs to be called before any read operation.
The flow will likely be:
- User API call
- Check "are readers ready?"
- Read data with queue readers
Between 2 and 3, we could update the reader state, then read whatever's there. It might even be better that the connection state is not updated within a "read" or "getAvailable" call.
| void QueueReader::checkConnection() const | ||
| { | ||
| if (!connection.assigned()) | ||
| DAQ_THROW_EXCEPTION(InvalidOperationException, "Connection must be assigned for this operation."); | ||
| } |
There was a problem hiding this comment.
This is probably a placeholder, but the design should be done so that this can not be triggered.
| SizeT QueueReader::getAvailableSamples() | ||
| { | ||
| return getAvailableSamplesNative() * sampleRateDivider; | ||
| } |
There was a problem hiding this comment.
As mentioned above, I think we're somewhat mixing these calculations and doing them both internally in the queue reader and externally.
There was a problem hiding this comment.
The user has to allocate buffers in "native" domain. To do this, they will have to use dividers either provided by the reader or calculated manually - this is inconvenient. The reader is only capable of communicating the common domain and a single available count. The only choice here is whether QueueReader does this or the ReadCoordinator will do this for all signals. Being aware of a single divider seems less messy than bookkeeping all dividers to me.
1b6c21b to
267a93a
Compare
267a93a to
353d63e
Compare
Review PR