A2B: Add A2BMessage transmit support
parent
ed1632c652
commit
78465e0f20
|
|
@ -394,6 +394,7 @@ if(LIBICSNEO_BUILD_TESTS)
|
||||||
test/eventmanagertest.cpp
|
test/eventmanagertest.cpp
|
||||||
test/ethernetpacketizertest.cpp
|
test/ethernetpacketizertest.cpp
|
||||||
test/i2cencoderdecodertest.cpp
|
test/i2cencoderdecodertest.cpp
|
||||||
|
test/a2bencoderdecodertest.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(libicsneo-tests gtest gtest_main)
|
target_link_libraries(libicsneo-tests gtest gtest_main)
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,7 @@ static constexpr const char* ATOMIC_OPERATION_RETRIED = "An operation failed to
|
||||||
static constexpr const char* ATOMIC_OPERATION_COMPLETED_NONATOMICALLY = "An ideally-atomic operation was completed nonatomically.";
|
static constexpr const char* ATOMIC_OPERATION_COMPLETED_NONATOMICALLY = "An ideally-atomic operation was completed nonatomically.";
|
||||||
static constexpr const char* WIVI_STACK_REFRESH_FAILED = "The Wireless neoVI stack encountered a communication error.";
|
static constexpr const char* WIVI_STACK_REFRESH_FAILED = "The Wireless neoVI stack encountered a communication error.";
|
||||||
static constexpr const char* WIVI_UPLOAD_STACK_OVERFLOW = "The Wireless neoVI upload stack has encountered an overflow condition.";
|
static constexpr const char* WIVI_UPLOAD_STACK_OVERFLOW = "The Wireless neoVI upload stack has encountered an overflow condition.";
|
||||||
|
static constexpr const char* A2B_MESSAGE_INCOMPLETE_FRAME = "At least one of the frames of the A2B message does not contain samples for each channel and stream.";
|
||||||
|
|
||||||
// Transport Errors
|
// Transport Errors
|
||||||
static constexpr const char* FAILED_TO_READ = "A read operation failed.";
|
static constexpr const char* FAILED_TO_READ = "A read operation failed.";
|
||||||
|
|
@ -241,6 +242,8 @@ const char* APIEvent::DescriptionForType(Type type) {
|
||||||
return WIVI_STACK_REFRESH_FAILED;
|
return WIVI_STACK_REFRESH_FAILED;
|
||||||
case Type::WiVIUploadStackOverflow:
|
case Type::WiVIUploadStackOverflow:
|
||||||
return WIVI_UPLOAD_STACK_OVERFLOW;
|
return WIVI_UPLOAD_STACK_OVERFLOW;
|
||||||
|
case Type::A2BMessageIncompleteFrame:
|
||||||
|
return A2B_MESSAGE_INCOMPLETE_FRAME;
|
||||||
|
|
||||||
// Transport Errors
|
// Transport Errors
|
||||||
case Type::FailedToRead:
|
case Type::FailedToRead:
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
#include "icsneo/communication/message/ethphymessage.h"
|
#include "icsneo/communication/message/ethphymessage.h"
|
||||||
#include "icsneo/communication/packet/i2cpacket.h"
|
#include "icsneo/communication/packet/i2cpacket.h"
|
||||||
#include "icsneo/communication/message/i2cmessage.h"
|
#include "icsneo/communication/message/i2cmessage.h"
|
||||||
|
#include "icsneo/communication/packet/a2bpacket.h"
|
||||||
|
|
||||||
|
|
||||||
using namespace icsneo;
|
using namespace icsneo;
|
||||||
|
|
||||||
|
|
@ -70,6 +72,18 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
|
||||||
// packets to the device. This function just encodes them back to back into `result`
|
// packets to the device. This function just encodes them back to back into `result`
|
||||||
return HardwareISO9141Packet::EncodeFromMessage(*isomsg, result, report, packetizer);
|
return HardwareISO9141Packet::EncodeFromMessage(*isomsg, result, report, packetizer);
|
||||||
} // End of Network::Type::ISO9141
|
} // End of Network::Type::ISO9141
|
||||||
|
case Network::Type::A2B: {
|
||||||
|
auto a2bmsg = std::dynamic_pointer_cast<A2BMessage>(message);
|
||||||
|
if(!a2bmsg) {
|
||||||
|
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
buffer = &result;
|
||||||
|
if(!HardwareA2BPacket::EncodeFromMessage(*a2bmsg, result, report)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} // End of Network::Type::A2B
|
||||||
case Network::Type::I2C: {
|
case Network::Type::I2C: {
|
||||||
auto i2cmsg = std::dynamic_pointer_cast<I2CMessage>(message);
|
auto i2cmsg = std::dynamic_pointer_cast<I2CMessage>(message);
|
||||||
if(!i2cmsg) {
|
if(!i2cmsg) {
|
||||||
|
|
@ -81,7 +95,7 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
} // End of Network::Type::I2C
|
||||||
default:
|
default:
|
||||||
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -70,11 +70,11 @@ bool A2BWAVOutput::writeSamples(const std::shared_ptr<A2BMessage>& msg, A2BMessa
|
||||||
uint8_t numChannels = msg->getNumChannels();
|
uint8_t numChannels = msg->getNumChannels();
|
||||||
|
|
||||||
uint8_t channel = 0;
|
uint8_t channel = 0;
|
||||||
uint32_t sampleIndex = 0;
|
uint32_t frame = 0;
|
||||||
uint8_t bitDepth = msg->getBitDepth();
|
uint8_t bitDepth = msg->getBitDepth();
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
auto sample = msg->getSample(dir, channel, sampleIndex);
|
auto sample = msg->getSample(dir, channel, frame);
|
||||||
|
|
||||||
if(!sample) {
|
if(!sample) {
|
||||||
if(channel == 0) {
|
if(channel == 0) {
|
||||||
|
|
@ -90,7 +90,7 @@ bool A2BWAVOutput::writeSamples(const std::shared_ptr<A2BMessage>& msg, A2BMessa
|
||||||
|
|
||||||
channel = (channel + 1) % numChannels;
|
channel = (channel + 1) % numChannels;
|
||||||
if(channel == 0) {
|
if(channel == 0) {
|
||||||
sampleIndex++;
|
frame++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
#include "icsneo/communication/packet/a2bpacket.h"
|
#include "icsneo/communication/packet/a2bpacket.h"
|
||||||
#include "icsneo/communication/message/a2bmessage.h"
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
using namespace icsneo;
|
|
||||||
|
|
||||||
std::shared_ptr<Message> HardwareA2BPacket::DecodeToMessage(const std::vector<uint8_t> &bytestream) {
|
namespace icsneo {
|
||||||
|
|
||||||
constexpr uint8_t coreMiniMessageHeaderSize = 28;
|
const size_t HardwareA2BPacket::coreMiniMessageHeaderSize = 28;
|
||||||
|
const size_t HardwareA2BPacket::a2bMessageMaxLength = (size_t)HardwareA2BPacket::coreMiniMessageHeaderSize + 1024;
|
||||||
|
const size_t HardwareA2BPacket::a2bHeaderSize = 6;
|
||||||
|
|
||||||
|
std::shared_ptr<Message> HardwareA2BPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
|
||||||
|
|
||||||
if(bytestream.size() < coreMiniMessageHeaderSize)
|
if(bytestream.size() < coreMiniMessageHeaderSize)
|
||||||
{
|
{
|
||||||
|
|
@ -31,9 +33,14 @@ std::shared_ptr<Message> HardwareA2BPacket::DecodeToMessage(const std::vector<ui
|
||||||
uint8_t bytesPerChannel = data->header.channelSize16 ? 2 : 4;
|
uint8_t bytesPerChannel = data->header.channelSize16 ? 2 : 4;
|
||||||
uint8_t numChannels = data->header.channelNum;
|
uint8_t numChannels = data->header.channelNum;
|
||||||
uint8_t bitDepth = data->header.channelSize16 ? A2BPCM_L16 : A2BPCM_L24;
|
uint8_t bitDepth = data->header.channelSize16 ? A2BPCM_L16 : A2BPCM_L24;
|
||||||
bool monitor = data->header.monitor;
|
|
||||||
|
|
||||||
std::shared_ptr<A2BMessage> msg = std::make_shared<A2BMessage>(bitDepth, bytesPerChannel, numChannels, monitor);
|
std::shared_ptr<A2BMessage> msg = std::make_shared<A2BMessage>(bitDepth, bytesPerChannel, numChannels);
|
||||||
|
msg->channelSize16 = data->header.channelSize16;
|
||||||
|
msg->monitor = data->header.monitor;
|
||||||
|
msg->txmsg = data->header.txmsg;
|
||||||
|
msg->errIndicator = data->header.errIndicator;
|
||||||
|
msg->syncFrame = data->header.syncFrame;
|
||||||
|
msg->rfu2 = data->header.rfu2;
|
||||||
|
|
||||||
const uint8_t *bytes = bytestream.data();
|
const uint8_t *bytes = bytestream.data();
|
||||||
bytes+=coreMiniMessageHeaderSize;
|
bytes+=coreMiniMessageHeaderSize;
|
||||||
|
|
@ -58,3 +65,99 @@ std::shared_ptr<Message> HardwareA2BPacket::DecodeToMessage(const std::vector<ui
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HardwareA2BPacket::EncodeFromMessage(const A2BMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report) {
|
||||||
|
|
||||||
|
if(message.getBytesPerSample() != 2 && message.getBytesPerSample() != 4) {
|
||||||
|
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t sampleBytes = message.getNumSamples() * static_cast<size_t>(message.getBytesPerSample());
|
||||||
|
size_t totalSize = coreMiniMessageHeaderSize + sampleBytes;
|
||||||
|
|
||||||
|
if(totalSize > a2bMessageMaxLength) {
|
||||||
|
report(APIEvent::Type::MessageMaxLengthExceeded, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytestream.reserve(totalSize);
|
||||||
|
|
||||||
|
bytestream.push_back(message.getNumChannels());
|
||||||
|
bytestream.push_back(message.channelSize16 ? 1 : 0);
|
||||||
|
|
||||||
|
uint8_t a2b2Bits = 0;
|
||||||
|
|
||||||
|
if(message.monitor) {
|
||||||
|
a2b2Bits = a2b2Bits | 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(message.txmsg) {
|
||||||
|
a2b2Bits = a2b2Bits | (1 << 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(message.errIndicator) {
|
||||||
|
a2b2Bits = a2b2Bits | (1 << 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(message.syncFrame) {
|
||||||
|
a2b2Bits = a2b2Bits | (1 << 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
bytestream.push_back(a2b2Bits);
|
||||||
|
bytestream.push_back(0);
|
||||||
|
bytestream.push_back(static_cast<uint8_t>(message.rfu2));
|
||||||
|
bytestream.push_back(static_cast<uint8_t>(message.rfu2 >> 8));
|
||||||
|
|
||||||
|
for(size_t i = 0; i < (coreMiniMessageHeaderSize - a2bHeaderSize); i++)
|
||||||
|
bytestream.push_back(0);
|
||||||
|
|
||||||
|
uint8_t numChannels = message.getNumChannels();
|
||||||
|
|
||||||
|
uint8_t channel = 0;
|
||||||
|
uint32_t frame = 0;
|
||||||
|
|
||||||
|
auto writeSample = [&](A2BPCMSample&& sample) {
|
||||||
|
for(uint32_t i = 0; i < static_cast<uint32_t>(message.getBytesPerSample()); i++) {
|
||||||
|
bytestream.push_back(static_cast<uint8_t>((sample >> (i*8))));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
auto dsSample = message.getSample(A2BMessage::A2BDirection::DownStream, channel, frame);
|
||||||
|
auto usSample = message.getSample(A2BMessage::A2BDirection::UpStream, channel, frame);
|
||||||
|
|
||||||
|
|
||||||
|
// Check if getSample failed for both downstream and upstream
|
||||||
|
if(!dsSample && !usSample) {
|
||||||
|
|
||||||
|
if(channel != 0) {
|
||||||
|
//Incomplete frame, the frame we are currently on does not contain all channel samples
|
||||||
|
report(APIEvent::Type::A2BMessageIncompleteFrame, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Since no samples have been written for the current frame yet and there are no more
|
||||||
|
// samples in both upstream and downstream, we can break and end parsing.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Since the first case failed, at least one of the streams still has samples.
|
||||||
|
// This case checks to see if the other stream does not have a sample.
|
||||||
|
else if(!dsSample || !usSample) {
|
||||||
|
// Report an error since we must have a one to one correspondence between upstream
|
||||||
|
// and downstream.
|
||||||
|
report(APIEvent::Type::A2BMessageIncompleteFrame, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSample(std::move(dsSample.value()));
|
||||||
|
writeSample(std::move(usSample.value()));
|
||||||
|
|
||||||
|
channel = (channel + 1) % numChannels;
|
||||||
|
if(channel == 0)
|
||||||
|
frame++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -90,6 +90,7 @@ public:
|
||||||
WiVIStackRefreshFailed = 0x2036,
|
WiVIStackRefreshFailed = 0x2036,
|
||||||
WiVIUploadStackOverflow = 0x2037,
|
WiVIUploadStackOverflow = 0x2037,
|
||||||
I2CMessageExceedsMaxLength = 0x2038,
|
I2CMessageExceedsMaxLength = 0x2038,
|
||||||
|
A2BMessageIncompleteFrame = 0x2039,
|
||||||
|
|
||||||
// Transport Events
|
// Transport Events
|
||||||
FailedToRead = 0x3000,
|
FailedToRead = 0x3000,
|
||||||
|
|
|
||||||
|
|
@ -31,16 +31,15 @@ public:
|
||||||
|
|
||||||
A2BMessage() = delete;
|
A2BMessage() = delete;
|
||||||
|
|
||||||
A2BMessage(uint8_t bitDepth, uint8_t bytesPerSample, uint8_t numChannels, bool monitor) :
|
A2BMessage(uint8_t bitDepth, uint8_t bytesPerSample, uint8_t numChannels) :
|
||||||
mBitDepth(bitDepth),
|
bDepth(bitDepth),
|
||||||
mBytesPerSample(bytesPerSample),
|
bps(bytesPerSample)
|
||||||
mMonitor(monitor)
|
|
||||||
{
|
{
|
||||||
downstream.resize(numChannels);
|
downstream.resize(numChannels);
|
||||||
upstream.resize(numChannels);
|
upstream.resize(numChannels);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addSample(A2BPCMSample &&sample, A2BDirection dir, uint8_t channel) {
|
void addSample(A2BPCMSample&& sample, A2BDirection dir, uint8_t channel) {
|
||||||
if(dir == A2BDirection::DownStream) {
|
if(dir == A2BDirection::DownStream) {
|
||||||
downstream[channel].push_back(std::move(sample));
|
downstream[channel].push_back(std::move(sample));
|
||||||
}
|
}
|
||||||
|
|
@ -61,18 +60,18 @@ public:
|
||||||
return upstream[channel].data();
|
return upstream[channel].data();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<A2BPCMSample> getSample(A2BDirection dir, uint8_t channel, uint32_t sampleIndex) const {
|
std::optional<A2BPCMSample> getSample(A2BDirection dir, uint8_t channel, uint32_t frame) const {
|
||||||
const A2BPCMSample* samples = getSamples(dir, channel);
|
const A2BPCMSample* samples = getSamples(dir, channel);
|
||||||
auto numSamplesInChannel = getNumSamplesInChannel(dir, channel);
|
auto numSamplesInChannel = getNumSamplesInChannel(dir, channel);
|
||||||
|
|
||||||
if(
|
if(
|
||||||
samples == nullptr ||
|
samples == nullptr ||
|
||||||
sampleIndex >= numSamplesInChannel.value_or(0)
|
frame >= numSamplesInChannel.value_or(0)
|
||||||
) {
|
) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return samples[sampleIndex];
|
return samples[frame];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<size_t> getNumSamplesInChannel(A2BDirection dir, uint8_t channel) const {
|
std::optional<size_t> getNumSamplesInChannel(A2BDirection dir, uint8_t channel) const {
|
||||||
|
|
@ -104,25 +103,41 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t getBitDepth() const {
|
uint8_t getBitDepth() const {
|
||||||
return mBitDepth;
|
return bDepth;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t getBytesPerSample() const {
|
uint8_t getBytesPerSample() const {
|
||||||
return mBytesPerSample;
|
return bps;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isMonitor() const {
|
// Equals operation for testing.
|
||||||
return mMonitor;
|
bool operator==(const A2BMessage& rhs) const {
|
||||||
|
return channelSize16 == rhs.channelSize16 &&
|
||||||
|
monitor == rhs.monitor &&
|
||||||
|
txmsg == rhs.txmsg &&
|
||||||
|
errIndicator == rhs.errIndicator &&
|
||||||
|
syncFrame == rhs.syncFrame &&
|
||||||
|
rfu2 == rhs.rfu2 &&
|
||||||
|
downstream == rhs.downstream &&
|
||||||
|
upstream == rhs.upstream &&
|
||||||
|
totalSamples == rhs.totalSamples &&
|
||||||
|
bDepth == rhs.bDepth &&
|
||||||
|
bps == rhs.bps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool channelSize16;
|
||||||
|
bool monitor;
|
||||||
|
bool txmsg;
|
||||||
|
bool errIndicator;
|
||||||
|
bool syncFrame;
|
||||||
|
uint16_t rfu2;
|
||||||
private:
|
private:
|
||||||
std::vector<ChannelBuffer> downstream;
|
std::vector<ChannelBuffer> downstream;
|
||||||
std::vector<ChannelBuffer> upstream;
|
std::vector<ChannelBuffer> upstream;
|
||||||
size_t totalSamples = 0;
|
size_t totalSamples = 0;
|
||||||
|
|
||||||
uint8_t mBitDepth;
|
uint8_t bDepth;
|
||||||
uint8_t mBytesPerSample;
|
uint8_t bps;
|
||||||
bool mMonitor;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,7 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include "icsneo/communication/message/message.h"
|
#include "icsneo/communication/message/a2bmessage.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace icsneo {
|
namespace icsneo {
|
||||||
|
|
||||||
|
|
@ -18,6 +16,7 @@ typedef uint16_t icscm_bitfield;
|
||||||
struct HardwareA2BPacket {
|
struct HardwareA2BPacket {
|
||||||
|
|
||||||
static std::shared_ptr<Message> DecodeToMessage(const std::vector<uint8_t>& bytestream);
|
static std::shared_ptr<Message> DecodeToMessage(const std::vector<uint8_t>& bytestream);
|
||||||
|
static bool EncodeFromMessage(const A2BMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report);
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
// CxA2B
|
// CxA2B
|
||||||
|
|
@ -34,6 +33,10 @@ struct HardwareA2BPacket {
|
||||||
icscm_bitfield : 11;
|
icscm_bitfield : 11;
|
||||||
icscm_bitfield rfu2;
|
icscm_bitfield rfu2;
|
||||||
} header;
|
} header;
|
||||||
|
|
||||||
|
static const size_t coreMiniMessageHeaderSize;
|
||||||
|
static const size_t a2bMessageMaxLength;
|
||||||
|
static const size_t a2bHeaderSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,235 @@
|
||||||
|
#include "icsneo/icsneocpp.h"
|
||||||
|
#include "icsneo/communication/encoder.h"
|
||||||
|
#include "icsneo/communication/packet/a2bpacket.h"
|
||||||
|
#include "icsneo/communication/packetizer.h"
|
||||||
|
#include "icsneo/api/eventmanager.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
class A2BEncoderDecoderTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
void SetUp() override {
|
||||||
|
|
||||||
|
report = [](APIEvent::Type, APIEvent::Severity) {
|
||||||
|
// Unless caught by the test, the packetizer should not throw errors
|
||||||
|
EXPECT_TRUE(false);
|
||||||
|
};
|
||||||
|
packetizer.emplace([this](APIEvent::Type t, APIEvent::Severity s) {
|
||||||
|
report(t, s);
|
||||||
|
});
|
||||||
|
packetEncoder.emplace([this](APIEvent::Type t, APIEvent::Severity s) {
|
||||||
|
report(t, s);
|
||||||
|
});
|
||||||
|
packetDecoder.emplace([this](APIEvent::Type t, APIEvent::Severity s) {
|
||||||
|
report(t, s);
|
||||||
|
});
|
||||||
|
|
||||||
|
A2BPCMSample initialSample = 10 << 8;
|
||||||
|
constructTest(
|
||||||
|
msg1,
|
||||||
|
msg1Encoded,
|
||||||
|
{1,1,0,0,0,0},
|
||||||
|
1,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
3,
|
||||||
|
initialSample
|
||||||
|
);
|
||||||
|
|
||||||
|
initialSample = (29 << 16) | (10 << 8);
|
||||||
|
constructTest(
|
||||||
|
msg2,
|
||||||
|
msg2Encoded,
|
||||||
|
{1,0,9,0,0,0},
|
||||||
|
1,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
initialSample
|
||||||
|
);
|
||||||
|
|
||||||
|
initialSample = (29 << 16) | (10 << 8);
|
||||||
|
constructTest(
|
||||||
|
msg3,
|
||||||
|
msg3Encoded,
|
||||||
|
{2,0,6,0,0xAA,0xFF},
|
||||||
|
2,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
0xFFAA,
|
||||||
|
3,
|
||||||
|
initialSample
|
||||||
|
);
|
||||||
|
|
||||||
|
constructTest(
|
||||||
|
msg4,
|
||||||
|
msg4Encoded,
|
||||||
|
{4,0,6,0,0xAA,0xFF},
|
||||||
|
4,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
0xFFAA,
|
||||||
|
0,
|
||||||
|
initialSample
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void constructTest(
|
||||||
|
std::shared_ptr<A2BMessage>& testMsg,
|
||||||
|
std::vector<uint8_t>& testMsgEncoded,
|
||||||
|
std::array<uint8_t, 6> encodedHeader,
|
||||||
|
uint8_t numChannels,
|
||||||
|
bool channelSize16,
|
||||||
|
bool monitor,
|
||||||
|
bool txmsg,
|
||||||
|
bool errIndicator,
|
||||||
|
bool syncFrame,
|
||||||
|
uint16_t rfu2,
|
||||||
|
uint32_t numFrames,
|
||||||
|
A2BPCMSample initialSample
|
||||||
|
)
|
||||||
|
{
|
||||||
|
|
||||||
|
testMsg = std::make_shared<A2BMessage>(
|
||||||
|
(uint8_t)(channelSize16 ? 16 : 24),
|
||||||
|
(uint8_t)(channelSize16 ? 2 : 4),
|
||||||
|
numChannels
|
||||||
|
);
|
||||||
|
|
||||||
|
auto addSample = [&](
|
||||||
|
uint8_t channel,
|
||||||
|
A2BMessage::A2BDirection dir
|
||||||
|
) {
|
||||||
|
testMsg->addSample(std::move(initialSample), dir, channel);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < static_cast<size_t>(testMsg->getBytesPerSample()); i++) {
|
||||||
|
testMsgEncoded.push_back(static_cast<uint8_t>(initialSample >> (i * 8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
initialSample++;
|
||||||
|
};
|
||||||
|
|
||||||
|
testMsg->network = Network(Network::NetID::A2B1);
|
||||||
|
testMsg->channelSize16 = channelSize16;
|
||||||
|
testMsg->monitor = monitor;
|
||||||
|
testMsg->txmsg = txmsg;
|
||||||
|
testMsg->errIndicator = errIndicator;
|
||||||
|
testMsg->syncFrame = syncFrame;
|
||||||
|
testMsg->rfu2 = rfu2;
|
||||||
|
|
||||||
|
testMsgEncoded.reserve(
|
||||||
|
HardwareA2BPacket::coreMiniMessageHeaderSize + numFrames * static_cast<size_t>(testMsg->getBytesPerSample())
|
||||||
|
);
|
||||||
|
|
||||||
|
testMsgEncoded.insert(
|
||||||
|
testMsgEncoded.end(),
|
||||||
|
encodedHeader.begin(),
|
||||||
|
encodedHeader.end()
|
||||||
|
); // Insert header
|
||||||
|
appendCoreMiniHeaderOffset(testMsgEncoded);
|
||||||
|
|
||||||
|
for(unsigned int frame = 0; frame < numFrames; frame++) {
|
||||||
|
for(uint8_t channel = 0; channel < testMsg->getNumChannels(); channel++) {
|
||||||
|
|
||||||
|
addSample(channel, A2BMessage::A2BDirection::DownStream);
|
||||||
|
addSample(channel, A2BMessage::A2BDirection::UpStream);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void appendCoreMiniHeaderOffset(std::vector<uint8_t>& buf) {
|
||||||
|
for(
|
||||||
|
size_t i = 0;
|
||||||
|
i < (HardwareA2BPacket::coreMiniMessageHeaderSize - HardwareA2BPacket::a2bHeaderSize);
|
||||||
|
i++
|
||||||
|
) {
|
||||||
|
buf.push_back(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void decrementEncodedMessageSize(std::vector<uint8_t>& bytestream) {
|
||||||
|
if(bytestream.size() < 6)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint16_t size = (bytestream[3] << 8) | bytestream[2];
|
||||||
|
|
||||||
|
if(size == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
size--;
|
||||||
|
|
||||||
|
bytestream[2] = (uint8_t)size;
|
||||||
|
bytestream[3] = (uint8_t)(size >> 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testMessage(std::shared_ptr<A2BMessage>& msg, std::vector<uint8_t>& msgEncoded) {
|
||||||
|
std::vector<uint8_t> bytestream;
|
||||||
|
std::vector<uint8_t> rawPacketBytes;
|
||||||
|
std::shared_ptr<Message> decodeMsg;
|
||||||
|
std::shared_ptr<A2BMessage> decodeA2BMsg;
|
||||||
|
|
||||||
|
EXPECT_TRUE(packetEncoder->encode(*packetizer, bytestream, msg));
|
||||||
|
rawPacketBytes = std::vector<uint8_t>(bytestream.begin() + 6, bytestream.end());
|
||||||
|
|
||||||
|
EXPECT_EQ(rawPacketBytes, msgEncoded);
|
||||||
|
decrementEncodedMessageSize(bytestream);
|
||||||
|
|
||||||
|
EXPECT_TRUE(packetizer->input(bytestream));
|
||||||
|
auto packets = packetizer->output();
|
||||||
|
if(packets.empty()) { EXPECT_TRUE(false);}
|
||||||
|
EXPECT_TRUE(packetDecoder->decode(decodeMsg, packets.back()));
|
||||||
|
decodeA2BMsg = std::static_pointer_cast<A2BMessage>(decodeMsg);
|
||||||
|
|
||||||
|
EXPECT_EQ(*msg, *decodeA2BMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
device_eventhandler_t report;
|
||||||
|
std::optional<Encoder> packetEncoder;
|
||||||
|
std::optional<Packetizer> packetizer;
|
||||||
|
std::optional<Decoder> packetDecoder;
|
||||||
|
|
||||||
|
std::shared_ptr<A2BMessage> msg1;
|
||||||
|
std::vector<uint8_t> msg1Encoded;
|
||||||
|
|
||||||
|
std::shared_ptr<A2BMessage> msg2;
|
||||||
|
std::vector<uint8_t> msg2Encoded;
|
||||||
|
|
||||||
|
std::shared_ptr<A2BMessage> msg3;
|
||||||
|
std::vector<uint8_t> msg3Encoded;
|
||||||
|
|
||||||
|
std::shared_ptr<A2BMessage> msg4;
|
||||||
|
std::vector<uint8_t> msg4Encoded;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(A2BEncoderDecoderTest, ChannelSize16SingleChannel) {
|
||||||
|
testMessage(msg1, msg1Encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(A2BEncoderDecoderTest, ChannelSize24MultiChannel) {
|
||||||
|
testMessage(msg2, msg2Encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(A2BEncoderDecoderTest, ChannelSize24MultiChannelMultiFrame) {
|
||||||
|
testMessage(msg3, msg3Encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(A2BEncoderDecoderTest, NoFrames) {
|
||||||
|
testMessage(msg4, msg4Encoded);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue