Device/Disk: Add VSA read and parse functionality

Implement ability to extract network traffic (CAN, LIN, Ethernet, etc.) from VSA message records on disk. Add a method to Device class that uses the VSAParser and the individual record types to extract messages from the VSA message records and pass them back to the communication system. This routes messages such that it appears as if they were discovered live instead of read from disk. The parse process (in Device) requires determination of metadata about the VSA file system on a device before it can begin extracting messages. This currently only handles data captured from the current coremini script on a device.
pull/62/head
Max Brombach 2023-11-15 16:02:47 +00:00
parent 4248c1a538
commit 02f1b4592e
43 changed files with 4122 additions and 20 deletions

View File

@ -275,6 +275,22 @@ set(SRC_FILES
disk/plasiondiskreaddriver.cpp
disk/extextractordiskreaddriver.cpp
disk/fat.cpp
disk/vsa/vsa.cpp
disk/vsa/vsa02.cpp
disk/vsa/vsa03.cpp
disk/vsa/vsa04.cpp
disk/vsa/vsa05.cpp
disk/vsa/vsa06.cpp
disk/vsa/vsa07.cpp
disk/vsa/vsa08.cpp
disk/vsa/vsa09.cpp
disk/vsa/vsa0b.cpp
disk/vsa/vsa0c.cpp
disk/vsa/vsa0d.cpp
disk/vsa/vsa0e.cpp
disk/vsa/vsa0f.cpp
disk/vsa/vsa6a.cpp
disk/vsa/vsaparser.cpp
${PLATFORM_SRC}
)

View File

@ -169,6 +169,15 @@ static constexpr const char* FT_DEVICE_NOT_CONNECTED = "FTD3XX device not connec
static constexpr const char* FT_INCORRECT_DEVICE_PATH = "Incorrect FTD3XX device path.";
static constexpr const char* FT_OTHER_ERROR = "Other FTD3XX error.";
// VSA
static constexpr const char* VSA_BUFFER_CORRUPTED = "VSA data in record buffer is corrupted.";
static constexpr const char* VSA_TIMESTAMP_NOT_FOUND = "Unable to find a VSA record with a valid timestamp.";
static constexpr const char* VSA_BUFFER_FORMAT_ERROR = "VSA record buffer is formatted incorrectly.";
static constexpr const char* VSA_MAX_READ_ATTEMPTS_REACHED = "Reached max attempts to read VSA records before exit.";
static constexpr const char* VSA_BYTE_PARSE_FAILURE = "Failure to parse record bytes from VSA buffer.";
static constexpr const char* VSA_EXTENDED_MESSAGE_ERROR = "Failure to parse extended message record sequence";
static constexpr const char* VSA_OTHER_ERROR = "Unknown error in VSA read API.";
static constexpr const char* TOO_MANY_EVENTS = "Too many events have occurred. The list has been truncated.";
static constexpr const char* UNKNOWN = "An unknown internal error occurred.";
static constexpr const char* INVALID = "An invalid internal error occurred.";
@ -393,6 +402,22 @@ const char* APIEvent::DescriptionForType(Type type) {
case Type::FTOtherError:
return FT_OTHER_ERROR;
// VSA
case Type::VSABufferCorrupted:
return VSA_BUFFER_CORRUPTED;
case Type::VSATimestampNotFound:
return VSA_TIMESTAMP_NOT_FOUND;
case Type::VSABufferFormatError:
return VSA_BUFFER_FORMAT_ERROR;
case Type::VSAMaxReadAttemptsReached:
return VSA_MAX_READ_ATTEMPTS_REACHED;
case Type::VSAByteParseFailure:
return VSA_BYTE_PARSE_FAILURE;
case Type::VSAExtendedMessageError:
return VSA_EXTENDED_MESSAGE_ERROR;
case Type::VSAOtherError:
return VSA_OTHER_ERROR;
// Other Errors
case Type::TooManyEvents:
return TOO_MANY_EVENTS;

File diff suppressed because it is too large Load Diff

39
disk/vsa/vsa.cpp 100644
View File

@ -0,0 +1,39 @@
#include "icsneo/disk/vsa/vsa.h"
#include "icsneo/communication/packet/ethernetpacket.h"
using namespace icsneo;
// VSA Base Class Functions
// VSAMessage Class Functions
std::shared_ptr<Packet> VSAMessage::getPacket() const
{
auto packet = std::make_shared<Packet>();
packet->network = network;
reservePacketData(packet);
packet->data.insert(packet->data.end(), payload.begin(), payload.end());
return packet;
}
// VSAExtendedMessage Class Functions
void VSAExtendedMessage::appendPacket(std::shared_ptr<Packet> packet) const
{
packet->data.insert(packet->data.end(), payload.begin(), payload.end());
// Set the network if not already set (Happens in AA0F records)
if(packet->network.getNetID() == Network::NetID::Invalid) {
packet->network = network;
}
}
void VSAExtendedMessage::truncatePacket(std::shared_ptr<Packet> packet)
{
static constexpr auto EthernetLengthOffset = 26u;
switch(packet->network.getType()) {
case Network::Type::Ethernet:
const auto& packetLength = *reinterpret_cast<uint16_t*>(packet->data.data() + EthernetLengthOffset);
const size_t ethernetFrameSize = packetLength - (sizeof(uint16_t) * 2);
const size_t bytestreamExpectedSize = sizeof(HardwareEthernetPacket) + ethernetFrameSize;
packet->data.resize(bytestreamExpectedSize);
}
}

26
disk/vsa/vsa02.cpp 100644
View File

@ -0,0 +1,26 @@
#include "icsneo/disk/vsa/vsa02.h"
using namespace icsneo;
VSA02::VSA02(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA02);
constantIndex = *reinterpret_cast<uint16_t*>(recordBytes + 2);
flags = *reinterpret_cast<Flags*>(recordBytes + 4);
pieceCount = recordBytes[5];
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 6) & UINT63_MAX;
samples.insert(samples.end(), recordBytes + 14, recordBytes + 30);
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
}
void VSA02::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}

24
disk/vsa/vsa03.cpp 100644
View File

@ -0,0 +1,24 @@
#include "icsneo/disk/vsa/vsa03.h"
using namespace icsneo;
VSA03::VSA03(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA03);
eventType = static_cast<EventType>(*reinterpret_cast<uint16_t*>(recordBytes + 2));
eventData = *reinterpret_cast<uint16_t*>(recordBytes + 4);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 6) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 14);
doChecksum(recordBytes);
}
void VSA03::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 7; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}

24
disk/vsa/vsa04.cpp 100644
View File

@ -0,0 +1,24 @@
#include "icsneo/disk/vsa/vsa04.h"
using namespace icsneo;
VSA04::VSA04(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA04);
flags = *reinterpret_cast<Flags*>(recordBytes + 2);
partitionIndex = *reinterpret_cast<uint16_t*>(recordBytes + 4);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 6) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 14);
doChecksum(recordBytes);
}
void VSA04::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 7; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}

23
disk/vsa/vsa05.cpp 100644
View File

@ -0,0 +1,23 @@
#include "icsneo/disk/vsa/vsa05.h"
using namespace icsneo;
VSA05::VSA05(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA05);
errorType = static_cast<ErrorType>(*reinterpret_cast<uint16_t*>(recordBytes + 2));
errorNetwork = *reinterpret_cast<uint16_t*>(recordBytes + 4);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 6) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 14);
}
void VSA05::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 7; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}

25
disk/vsa/vsa06.cpp 100644
View File

@ -0,0 +1,25 @@
#include "icsneo/disk/vsa/vsa06.h"
using namespace icsneo;
VSA06::VSA06(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA06);
savedSectors.insert(savedSectors.end(), reinterpret_cast<uint32_t*>(recordBytes + 2), reinterpret_cast<uint32_t*>(recordBytes + 18));
error = *reinterpret_cast<uint16_t*>(recordBytes + 18);
savedSectorsHigh = *reinterpret_cast<uint16_t*>(recordBytes + 20);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 22) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
}
void VSA06::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}

25
disk/vsa/vsa07.cpp 100644
View File

@ -0,0 +1,25 @@
#include "icsneo/disk/vsa/vsa07.h"
using namespace icsneo;
VSA07::VSA07(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA07);
lastSector = *reinterpret_cast<uint32_t*>(recordBytes + 2);
currentSector = *reinterpret_cast<uint32_t*>(recordBytes + 6);
reserved.insert(reserved.end(), recordBytes + 10, recordBytes + 22);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 22) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
}
void VSA07::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}

24
disk/vsa/vsa08.cpp 100644
View File

@ -0,0 +1,24 @@
#include "icsneo/disk/vsa/vsa08.h"
using namespace icsneo;
VSA08::VSA08(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA08);
troubleSramCount.insert(troubleSramCount.end(), recordBytes + 2, recordBytes + 6);
troubleSectors.insert(troubleSectors.end(), reinterpret_cast<uint32_t*>(recordBytes + 6), reinterpret_cast<uint32_t*>(recordBytes + 20));
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 22) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
}
void VSA08::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}

32
disk/vsa/vsa09.cpp 100644
View File

@ -0,0 +1,32 @@
#include "icsneo/disk/vsa/vsa09.h"
using namespace icsneo;
VSA09::VSA09(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA09);
serialNumber = *reinterpret_cast<uint32_t*>(recordBytes + 2);
firmwareMajorVersion = recordBytes[6];
firmwareMinorVersion = recordBytes[7];
manufactureMajorRevision = recordBytes[8];
manufactureMinorRevision = recordBytes[9];
bootloaderMajorVersion = recordBytes[10];
bootloaderMinorVersion = recordBytes[11];
reserved0.insert(reserved0.end(), recordBytes + 12, recordBytes + 18);
hardwareID = static_cast<HardwareID>(recordBytes[18]);
reserved1.insert(reserved1.end(), recordBytes + 19, recordBytes + 22);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 22) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
}
void VSA09::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}

38
disk/vsa/vsa0b.cpp 100644
View File

@ -0,0 +1,38 @@
#include "icsneo/disk/vsa/vsa0b.h"
#include <algorithm>
using namespace icsneo;
static constexpr auto PayloadOffset = 4;
VSA0B::VSA0B(uint8_t* const recordBytes)
: VSAMessage(recordBytes + PayloadOffset, CoreMiniPayloadSize, static_cast<Network::CoreMini>(recordBytes[29]))
{
setType(VSA::Type::AA0B);
captureBitfield = reinterpret_cast<uint16_t*>(recordBytes)[1];
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 20) & UINT63_MAX;
reserved = recordBytes[28];
checksum = reinterpret_cast<uint16_t*>(recordBytes)[15];
doChecksum(recordBytes);
}
void VSA0B::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
bool VSA0B::filter(const std::shared_ptr<VSAMessageReadFilter> filter)
{
if((filter->captureBitfield != captureBitfield && filter->captureBitfield != UINT16_MAX) ||
getICSTimestampFromTimepoint(filter->readRange.first) > timestamp ||
getICSTimestampFromTimepoint(filter->readRange.second) < timestamp) {
return false;
}
return true;
}

27
disk/vsa/vsa0c.cpp 100644
View File

@ -0,0 +1,27 @@
#include "icsneo/disk/vsa/vsa0c.h"
using namespace icsneo;
VSA0C::VSA0C(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA0C);
captureBitfield = *reinterpret_cast<uint16_t*>(recordBytes + 2);
audioPreamble = recordBytes[4];
audioHeader = recordBytes[5];
pcmData.insert(pcmData.end(), recordBytes + 6, recordBytes + 20);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 20) & UINT63_MAX;
vNetBitfield = *reinterpret_cast<VSA0C::VNet*>(recordBytes + 28);
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
}
void VSA0C::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}

104
disk/vsa/vsa0d.cpp 100644
View File

@ -0,0 +1,104 @@
#include "icsneo/disk/vsa/vsa0d.h"
#include <algorithm>
using namespace icsneo;
static constexpr auto FirstPayloadOffset = 8;
static constexpr auto FirstPayloadSize = 12;
static constexpr auto ConsecutivePayloadOffset = 4;
static constexpr auto LastPayloadSize = 24;
static constexpr auto OtherPayloadSize = 28;
// Parent class functions
VSA0D::VSA0D(uint8_t* const recordBytes, uint8_t* const messageBytes, size_t numBytes, uint32_t& runningChecksum, Network::CoreMini networkId)
: VSAExtendedMessage(messageBytes, numBytes, networkId)
{
static constexpr auto DWordSize = 4;
setType(VSA::Type::AA0D);
setIndex(*reinterpret_cast<uint16_t*>(recordBytes + 2) & 0x01FFu);
setSequenceNum((*reinterpret_cast<uint16_t*>(recordBytes + 2) & 0xFE00u) >> 9);
uint32_t* dwords = reinterpret_cast<uint32_t*>(payload.data());
for(size_t i = 0; i < payload.size() / DWordSize; i++) {
runningChecksum += dwords[i];
}
}
// First Record Functions
VSA0DFirst::VSA0DFirst(uint8_t* const recordBytes, uint32_t& runningChecksum)
: VSA0D(recordBytes, recordBytes + FirstPayloadOffset, FirstPayloadSize, runningChecksum, static_cast<Network::CoreMini>(recordBytes[29]))
{
captureBitfield = *reinterpret_cast<uint16_t*>(recordBytes + 4);
setRecordCount(*reinterpret_cast<uint16_t*>(recordBytes + 6));
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 20) & UINT63_MAX;
vNetInfo = *reinterpret_cast<VNet*>(recordBytes + 28);
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
uint32_t* const timestampDWords = reinterpret_cast<uint32_t*>(timestamp);
runningChecksum += timestampDWords[0];
runningChecksum += timestampDWords[1];
}
void VSA0DFirst::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
void VSA0DFirst::reservePacketData(std::shared_ptr<Packet>& packet) const
{
uint32_t numMessageBytes = (getRecordCount() - 2) * OtherPayloadSize + FirstPayloadSize + LastPayloadSize;
packet->data.reserve(numMessageBytes);
}
void VSA0DFirst::reorderPayload(std::vector<uint8_t>& secondPayload)
{
std::vector<uint8_t> tempPayload;
tempPayload.insert(tempPayload.end(), secondPayload.begin(), secondPayload.begin() + 4);
uint8_t* timestampBytes = reinterpret_cast<uint8_t*>(&timestamp);
tempPayload.insert(tempPayload.end(), timestampBytes, timestampBytes + 8);
tempPayload.insert(tempPayload.end(), secondPayload.begin() + 4, secondPayload.end());
payload.clear();
secondPayload.clear();
payload.insert(payload.end(), tempPayload.begin(), tempPayload.begin() + 12); // This is done because the capacity of payload is already 12
secondPayload.insert(secondPayload.end(), tempPayload.begin() + 12, tempPayload.end());
}
bool VSA0DFirst::filter(const std::shared_ptr<VSAMessageReadFilter> filter)
{
if((filter->captureBitfield != captureBitfield && filter->captureBitfield != UINT16_MAX) ||
getICSTimestampFromTimepoint(filter->readRange.first) > timestamp ||
getICSTimestampFromTimepoint(filter->readRange.second) < timestamp) {
return false;
}
return true;
}
// Consecutive Record Functions
VSA0DConsecutive::VSA0DConsecutive(uint8_t* const recordBytes, uint32_t& runningChecksum, std::shared_ptr<VSA0DFirst> first, bool isLastRecord)
: VSA0D(recordBytes, recordBytes + ConsecutivePayloadOffset, isLastRecord ? LastPayloadSize : OtherPayloadSize, runningChecksum)
{
this->first = first;
calculatedChecksum = runningChecksum;
if(getIndex() == 1) {
first->reorderPayload(payload);
} else if(isLastRecord) {
recordChecksum = *reinterpret_cast<uint32_t*>(recordBytes + 28);
doChecksum(recordBytes);
} else {
setChecksumFailed(first->getChecksumFailed());
}
setRecordCount(first->getRecordCount());
}
void VSA0DConsecutive::doChecksum(uint8_t* recordBytes)
{
setChecksumFailed(recordBytes && calculatedChecksum != recordChecksum);
}

111
disk/vsa/vsa0e.cpp 100644
View File

@ -0,0 +1,111 @@
#include "icsneo/disk/vsa/vsa0e.h"
#include <algorithm>
using namespace icsneo;
static constexpr auto FirstPayloadOffset = 10;
static constexpr auto FirstPayloadSize = 10;
static constexpr auto ConsecutivePayloadOffset = 4;
static constexpr auto LastPayloadSize = 24;
static constexpr auto OtherPayloadSize = 28;
// Parent class functions
VSA0E::VSA0E(uint8_t* const recordBytes, uint8_t* const messageBytes, size_t numBytes, uint32_t& runningChecksum, Network::CoreMini networkId)
: VSAExtendedMessage(messageBytes, numBytes, networkId)
{
static constexpr auto DWordSize = 4;
setType(VSA::Type::AA0E);
setIndex(static_cast<uint16_t>(recordBytes[2]));
setSequenceNum(static_cast<uint16_t>(recordBytes[3]));
if(getIndex() == 0) {
runningChecksum = (static_cast<uint32_t>(payload[0]) << 16) | (static_cast<uint32_t>(payload[1]) << 24);
uint32_t* dwords = reinterpret_cast<uint32_t*>(payload.data() + 2);
for(size_t i = 0; i < (payload.size() - 2) / DWordSize; i++) {
runningChecksum += dwords[i];
}
} else {
uint32_t* dwords = reinterpret_cast<uint32_t*>(payload.data());
for(size_t i = 0; i < payload.size() / DWordSize; i++) {
runningChecksum += dwords[i];
}
}
}
// First Record Functions
VSA0EFirst::VSA0EFirst(uint8_t* const recordBytes, uint32_t& runningChecksum)
: VSA0E(recordBytes, recordBytes + FirstPayloadOffset, FirstPayloadSize, runningChecksum,
static_cast<Network::CoreMini>(*reinterpret_cast<uint16_t*>(recordBytes + 28)))
{
captureBitfield = *reinterpret_cast<uint16_t*>(recordBytes + 4);
setRecordCount(*reinterpret_cast<uint32_t*>(recordBytes + 6));
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 20) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
}
void VSA0EFirst::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
void VSA0EFirst::reservePacketData(std::shared_ptr<Packet>& packet) const
{
uint32_t numMessageBytes = (getRecordCount() - 2) * OtherPayloadSize + FirstPayloadSize + LastPayloadSize;
packet->data.reserve(numMessageBytes);
}
bool VSA0EFirst::filter(const std::shared_ptr<VSAMessageReadFilter> filter)
{
if((filter->captureBitfield != captureBitfield && filter->captureBitfield != UINT16_MAX) ||
getICSTimestampFromTimepoint(filter->readRange.first) > timestamp ||
getICSTimestampFromTimepoint(filter->readRange.second) < timestamp) {
return false;
}
return true;
}
void VSA0EFirst::reorderPayload(std::vector<uint8_t>& secondPayload)
{
std::vector<uint8_t> tempPayload;
tempPayload.insert(tempPayload.end(), { 0, 0 }); // Leaving this in here temporarily to figure out checksum stuff
tempPayload.insert(tempPayload.end(), payload.begin(), payload.end());
tempPayload.insert(tempPayload.end(), secondPayload.begin(), secondPayload.begin() + 6);
uint8_t* timestampBytes = reinterpret_cast<uint8_t*>(&timestamp);
tempPayload.insert(tempPayload.end(), timestampBytes, timestampBytes + 8);
tempPayload.insert(tempPayload.end(), secondPayload.begin() + 6, secondPayload.end());
payload.clear();
secondPayload.clear();
payload.insert(payload.end(), tempPayload.begin(), tempPayload.begin() + 10); // This is done because the capacity of payload is already 10
secondPayload.insert(secondPayload.end(), tempPayload.begin() + 12, tempPayload.end());
}
// Consecutive Record Functions
VSA0EConsecutive::VSA0EConsecutive(uint8_t* const recordBytes, uint32_t& runningChecksum, std::shared_ptr<VSA0EFirst> first, bool isLastRecord)
: VSA0E(recordBytes, recordBytes + ConsecutivePayloadOffset, isLastRecord ? LastPayloadSize : OtherPayloadSize, runningChecksum)
{
this->first = first;
calculatedChecksum = runningChecksum;
if(getIndex() == 1) {
first->reorderPayload(payload);
} else if(isLastRecord) {
recordChecksum = *reinterpret_cast<uint32_t*>(recordBytes + 28);
doChecksum(recordBytes);
} else {
setChecksumFailed(first->getChecksumFailed());
}
setRecordCount(first->getRecordCount());
}
void VSA0EConsecutive::doChecksum(uint8_t* recordBytes)
{
setChecksumFailed(recordBytes && calculatedChecksum != recordChecksum);
}

106
disk/vsa/vsa0f.cpp 100644
View File

@ -0,0 +1,106 @@
#include "icsneo/disk/vsa/vsa0f.h"
#include <algorithm>
using namespace icsneo;
static constexpr auto FirstPayloadOffset = 18;
static constexpr auto FirstPayloadSize = 14;
static constexpr auto LastPayloadSize = 24;
static constexpr auto OtherPayloadSize = 28;
// Parent class functions
VSA0F::VSA0F(uint8_t* const recordBytes, uint8_t* const messageBytes, size_t numBytes, uint32_t& runningChecksum, Network::CoreMini networkId)
: VSAExtendedMessage(messageBytes, numBytes, networkId)
{
static constexpr auto DWordSize = 4;
setType(VSA::Type::AA0F);
setIndex(*reinterpret_cast<uint16_t*>(recordBytes + 2) & 0x01FFu);
setSequenceNum((*reinterpret_cast<uint16_t*>(recordBytes + 2) & 0xFE00u) >> 9);
if(getIndex() == 0) {
runningChecksum = (static_cast<uint32_t>(payload[0]) << 16) | (static_cast<uint32_t>(payload[1]) << 24);
uint32_t* dwords = reinterpret_cast<uint32_t*>(payload.data() + 2);
for (size_t i = 0; i < (payload.size() - 2) / DWordSize; i++) {
runningChecksum += dwords[i];
}
} else {
uint32_t* dwords = reinterpret_cast<uint32_t*>(recordBytes);
for (size_t i = 0; i < 8; i++) {
runningChecksum += dwords[i];
}
}
}
// First Record Functions
VSA0FFirst::VSA0FFirst(uint8_t* const recordBytes, uint32_t& runningChecksum)
: VSA0F(recordBytes, recordBytes + FirstPayloadOffset, FirstPayloadSize, runningChecksum)
{
captureBitfield = *reinterpret_cast<uint16_t*>(recordBytes + 4);
uint16_t byteCount = *reinterpret_cast<uint16_t*>(recordBytes + 6);
uint16_t recordCount;
if(byteCount <= FirstPayloadSize) {
recordCount = 1;
} else if(byteCount <= FirstPayloadSize + LastPayloadSize) {
recordCount = 2;
} else {
byteCount -= FirstPayloadSize + LastPayloadSize;
recordCount = 2 + byteCount / OtherPayloadSize;
if (byteCount % OtherPayloadSize != 0) {
recordCount += 1;
}
}
setRecordCount(recordCount);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 8) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 16);
doChecksum(recordBytes);
// Network ID is not found in first record for AA0F
// Only the subsequent records have the Network ID in the payload
}
void VSA0FFirst::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for (size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
void VSA0FFirst::reservePacketData(std::shared_ptr<Packet>& packet) const
{
uint32_t numMessageBytes = (getRecordCount() - 2) * OtherPayloadSize + FirstPayloadSize + LastPayloadSize;
packet->data.reserve(numMessageBytes);
}
bool VSA0FFirst::filter(const std::shared_ptr<VSAMessageReadFilter> filter)
{
if(filter->captureBitfield != captureBitfield ||
getICSTimestampFromTimepoint(filter->readRange.first) > timestamp ||
getICSTimestampFromTimepoint(filter->readRange.second) < timestamp) {
return false;
}
return true;
}
// Consecutive Record Functions
VSA0FConsecutive::VSA0FConsecutive(uint8_t* const recordBytes, uint32_t& runningChecksum, std::shared_ptr<VSA0FFirst> first, bool isLastRecord)
: VSA0F(recordBytes, recordBytes + 4, isLastRecord ? LastPayloadSize : OtherPayloadSize, runningChecksum)
{
this->first = first;
calculatedChecksum = runningChecksum;
if(isLastRecord) {
doChecksum(recordBytes);
} else {
network = Network(static_cast<Network::CoreMini>(*reinterpret_cast<uint16_t*>(recordBytes + 28))); // Network ID is stored in 25th and 26th recordBytes of payload
}
setRecordCount(first->getRecordCount());
}
void VSA0FConsecutive::doChecksum(uint8_t* recordBytes)
{
setChecksumFailed(recordBytes && calculatedChecksum != 0);
}

37
disk/vsa/vsa6a.cpp 100644
View File

@ -0,0 +1,37 @@
#include "icsneo/disk/vsa/vsa6a.h"
#include "icsneo/disk/diskdriver.h"
using namespace icsneo;
static constexpr auto PayloadOffset = 56;
static constexpr auto PayloadSize = 452;
static constexpr auto TimestampOffset = 48;
static constexpr auto TimestampSize = 8;
VSA6A::VSA6A(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA6A);
sequenceNum = *reinterpret_cast<uint32_t*>(recordBytes + 34);
totalSectors = *reinterpret_cast<uint32_t*>(recordBytes + 38);
reserved = *reinterpret_cast<uint32_t*>(recordBytes + 42);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 46) & UINT63_MAX;
timestampSum = *reinterpret_cast<uint16_t*>(recordBytes + 54);
data.insert(data.end(), recordBytes + 56, recordBytes + 508);
checksum = *reinterpret_cast<uint32_t*>(recordBytes + 508);
doChecksum(recordBytes);
}
void VSA6A::doChecksum(uint8_t* recordBytes)
{
uint32_t sum = 0;
for(size_t i = PayloadOffset; i < PayloadOffset+ PayloadSize; i++) {
sum += recordBytes[i];
}
uint16_t tSum = 0;
for(size_t i = TimestampOffset; i < TimestampOffset + TimestampSize; i++) {
tSum += recordBytes[i];
}
setChecksumFailed(sum != checksum || tSum != timestampSum);
}

View File

@ -0,0 +1,460 @@
#include "icsneo/disk/vsa/vsaparser.h"
#include "icsneo/disk/vsa/vsa02.h"
#include "icsneo/disk/vsa/vsa03.h"
#include "icsneo/disk/vsa/vsa04.h"
#include "icsneo/disk/vsa/vsa05.h"
#include "icsneo/disk/vsa/vsa06.h"
#include "icsneo/disk/vsa/vsa07.h"
#include "icsneo/disk/vsa/vsa08.h"
#include "icsneo/disk/vsa/vsa09.h"
#include "icsneo/disk/vsa/vsa0b.h"
#include "icsneo/disk/vsa/vsa0c.h"
#include "icsneo/disk/vsa/vsa0d.h"
#include "icsneo/disk/vsa/vsa0e.h"
#include "icsneo/disk/vsa/vsa0f.h"
#include "icsneo/disk/vsa/vsa6a.h"
#include "icsneo/disk/diskdriver.h"
#include <iostream>
using namespace icsneo;
bool VSAParser::parseBytes(uint8_t* const bytes, uint64_t arrLen)
{
uint64_t bytesOffset = 0;
while(bytesOffset + VSA::StandardRecordSize <= arrLen) { // Enough bytes to read for Standard Record
if(bytes[bytesOffset] != 0xAAu) {
// Invalid Input
return false;
}
switch(bytes[bytesOffset + 1]) {
case 0x00u: // Pad Record
bytesOffset += VSA::StandardRecordSize;
break;
case 0x01u: // Message Data (Deprecated)
hasDeprecatedRecords = true;
bytesOffset += VSA::StandardRecordSize;
break;
case 0x02u: // Logdata Record
if(settings.extractAA02) {
vsaRecords.push_back(std::make_shared<VSA02>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x03u: // Event Record
if(settings.extractAA03) {
vsaRecords.push_back(std::make_shared<VSA03>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x04u: // Partition Info Record
if(settings.extractAA04) {
vsaRecords.push_back(std::make_shared<VSA04>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x05u: // Application Error Record
if(settings.extractAA05) {
vsaRecords.push_back(std::make_shared<VSA05>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x06u: // Debug/Internal
if(settings.extractAA06) {
vsaRecords.push_back(std::make_shared<VSA06>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x07u: // Debug/Internal
if(settings.extractAA07) {
vsaRecords.push_back(std::make_shared<VSA07>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x08u: // Buffer Info Record
if(settings.extractAA08) {
vsaRecords.push_back(std::make_shared<VSA08>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x09u: // Device Info Record
if(settings.extractAA09) {
vsaRecords.push_back(std::make_shared<VSA09>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x0Au: // Logger Info Configuration (Deprecated)
hasDeprecatedRecords = true;
bytesOffset += VSA::StandardRecordSize;
break;
case 0x0Bu: // Message Data
if(settings.extractAA0B) {
auto record = std::make_shared<VSA0B>(bytes + bytesOffset);
vsaRecords.push_back(record);
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x0Cu: // PCM Audio Data
if(settings.extractAA0C) {
vsaRecords.push_back(std::make_shared<VSA0C>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x0Du: // Message Data (Extended)
if(settings.extractAA0D) {
if(!handleExtendedRecord(bytes, bytesOffset, VSA::Type::AA0D)) {
return false;
}
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x0Eu: // Message Data (Extended)
if(settings.extractAA0E) {
if(!handleExtendedRecord(bytes, bytesOffset, VSA::Type::AA0E)) {
return false;
}
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x0Fu: // Message Data (Extended)
if(settings.extractAA0F) {
if(!handleExtendedRecord(bytes, bytesOffset, VSA::Type::AA0F)) {
return false;
}
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x6Au: // Logger Configuration Backup
if(bytesOffset + Disk::SectorSize <= arrLen) {
if(settings.extractAA6A) {
vsaRecords.push_back(std::make_shared<VSA6A>(bytes + bytesOffset));
}
}
bytesOffset += Disk::SectorSize;
break;
default:
// Unhandled VSA Record Type
return false;
break;
}
}
return true;
}
bool VSAParser::handleExtendedRecord(uint8_t* const bytes, uint64_t& bytesOffset, VSA::Type type)
{
// Gather info about the extended record sequence of the record contained in bytes
std::shared_ptr<VSAExtendedMessage> first;
uint16_t seqNum;
ExtendedMessageState::ExtendedRecordSeqInfo* seqInfo;
uint32_t runningChecksum = 0;
switch(type) {
case VSA::Type::AA0D:
first = std::make_shared<VSA0DFirst>(bytes + bytesOffset, runningChecksum);
seqNum = first->getSequenceNum();
seqInfo = &state.vsa0DSeqInfo[seqNum];
break;
case VSA::Type::AA0E:
first = std::make_shared<VSA0EFirst>(bytes + bytesOffset, runningChecksum);
seqNum = first->getSequenceNum();
seqInfo = &state.vsa0ESeqInfo[seqNum];
break;
case VSA::Type::AA0F:
first = std::make_shared<VSA0FFirst>(bytes + bytesOffset, runningChecksum);
seqNum = first->getSequenceNum();
seqInfo = &state.vsa0FSeqInfo[seqNum];
break;
default:
return false; // Invalid type was passed
}
if(seqInfo->nextIndex == 0 && seqInfo->records.size() == 0) { // This is the first record in the sequence
if(first->getIndex() != 0) {
seqInfo->clear();
report(APIEvent::Type::VSAExtendedMessageError, APIEvent::Severity::EventWarning);
return true; // This is not actually the first record
}
seqInfo->records.push_back(first);
seqInfo->totalRecordCount = first->getRecordCount();
seqInfo->nextIndex++;
seqInfo->runningChecksum = runningChecksum;
} else if(seqInfo->nextIndex < seqInfo->totalRecordCount && seqInfo->records.size() > 0) { // Consecutive Record
std::shared_ptr<VSAExtendedMessage> consecutive;
bool isLast = seqInfo->nextIndex == seqInfo->totalRecordCount - 1;
// Construct the consecutive record from bytes
switch(type) {
case VSA::Type::AA0D:
consecutive = std::make_shared<VSA0DConsecutive>(
bytes + bytesOffset,
seqInfo->runningChecksum,
std::dynamic_pointer_cast<VSA0DFirst>(seqInfo->records[0]),
isLast
);
break;
case VSA::Type::AA0E:
consecutive = std::make_shared<VSA0EConsecutive>(
bytes + bytesOffset,
seqInfo->runningChecksum,
std::dynamic_pointer_cast<VSA0EFirst>(seqInfo->records[0]),
isLast
);
break;
case VSA::Type::AA0F:
consecutive = std::make_shared<VSA0FConsecutive>(
bytes + bytesOffset,
seqInfo->runningChecksum,
std::dynamic_pointer_cast<VSA0FFirst>(seqInfo->records[0]),
isLast
);
break;
default:
return false;
}
if(consecutive->getIndex() == seqInfo->nextIndex && consecutive->getSequenceNum() == seqNum) { // This record is valid in the sequence
seqInfo->records.push_back(consecutive);
seqInfo->nextIndex++;
} else { // Sequence is out of order/invalid
// Throw away incomplete sequence and report warning
seqInfo->clear();
report(APIEvent::Type::VSAExtendedMessageError, APIEvent::Severity::EventWarning);
// Save data for new sequence
if(first->getIndex() == 0) {
seqInfo->records.push_back(first);
seqInfo->totalRecordCount = first->getRecordCount();
seqInfo->nextIndex++;
seqInfo->runningChecksum = runningChecksum;
}
return true;
}
if(seqInfo->nextIndex == seqInfo->totalRecordCount) { // This is the last record in the sequence
if(consecutive->getChecksumFailed()) {
// Fail out if checksum fails
seqInfo->clear();
return false;
}
vsaRecords.insert(vsaRecords.end(), seqInfo->records.begin(), seqInfo->records.end());
seqInfo->clear();
}
} else {
return false; // Undefined behavior
}
return true;
}
VSAParser::RecordParseStatus VSAParser::getRecordFromBytes(uint8_t* const bytes, size_t arrLen, std::shared_ptr<VSA>& record)
{
record = nullptr;
if(arrLen < VSA::StandardRecordSize) {
// Not enough bytes
return VSAParser::RecordParseStatus::InsufficientData;
} else if(bytes[0] != 0xAAu) {
return VSAParser::RecordParseStatus::NotARecordStart;
} else {
switch(bytes[1]) {
case 0x00u: // Pad Record
return VSAParser::RecordParseStatus::Pad;
case 0x01u: // Message Data (Deprecated)
return VSAParser::RecordParseStatus::Deprecated;
case 0x02u: // Logdata Record
if(settings.extractAA02) {
record = std::make_shared<VSA02>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x03u: // Event Record
if(settings.extractAA03) {
record = std::make_shared<VSA03>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x04u: // Partition Info Record
if(settings.extractAA04) {
record = std::make_shared<VSA04>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x05u: // Application Error Record
if(settings.extractAA05) {
record = std::make_shared<VSA05>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x06u: // Debug/Internal
if(settings.extractAA06) {
record = std::make_shared<VSA06>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x07u: // Debug/Internal
if(settings.extractAA07) {
record = std::make_shared<VSA07>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x08u: // Buffer Info Record
if(settings.extractAA08) {
record = std::make_shared<VSA08>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x09u: // Device Info Record
if(settings.extractAA09) {
record = std::make_shared<VSA09>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x0Au:
return VSAParser::RecordParseStatus::Deprecated;
case 0x0Bu: // Message Data
if(settings.extractAA0B) {
record = std::make_shared<VSA0B>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x0Cu: // PCM Audio Data
if(settings.extractAA0C) {
record = std::make_shared<VSA0C>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x0Du: // Message Data (Extended)
if(settings.extractAA0D) {
uint32_t payloadChecksum = 0;
const auto& vsa = std::make_shared<VSA0DFirst>(bytes, payloadChecksum);
record = vsa;
if(vsa->getIndex() == 0) {
return VSAParser::RecordParseStatus::Success;
}
// This returns the consecutive record as a first record
return VSAParser::RecordParseStatus::ConsecutiveExtended;
}
break;
case 0x0Eu: // Message Data (Extended)
if(settings.extractAA0E) {
uint32_t payloadChecksum = 0;
const auto& vsa = std::make_shared<VSA0EFirst>(bytes, payloadChecksum);
record = vsa;
if(vsa->getIndex() == 0) {
return VSAParser::RecordParseStatus::Success;
}
// This returns the consecutive record as a first record
return VSAParser::RecordParseStatus::ConsecutiveExtended;
}
break;
case 0x0Fu: // Message Data (Extended)
if(settings.extractAA0F) {
uint32_t payloadChecksum = 0;
const auto& vsa = std::make_shared<VSA0FFirst>(bytes, payloadChecksum);
record = vsa;
if(vsa->getIndex() == 0) {
return VSAParser::RecordParseStatus::Success;
}
// This returns the consecutive record as a first record
return VSAParser::RecordParseStatus::ConsecutiveExtended;
}
break;
case 0x6Au: // Logger Configuration Backup
if(settings.extractAA6A) {
if(arrLen < Disk::SectorSize) {
return VSAParser::RecordParseStatus::InsufficientData;
}
record = std::make_shared<VSA6A>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
default:
// Unhandled VSA Record Type
return VSAParser::RecordParseStatus::UnknownRecordType;
break;
}
}
return VSAParser::RecordParseStatus::FilteredOut;
}
void VSAParser::clearParseState()
{
for(size_t i = 0; i < state.vsa0DSeqInfo.size(); i++) {
state.vsa0DSeqInfo[i].clear();
}
for(size_t i = 0; i < state.vsa0ESeqInfo.size(); i++) {
state.vsa0ESeqInfo[i].clear();
}
for(size_t i = 0; i < state.vsa0DSeqInfo.size(); i++) {
state.vsa0ESeqInfo[i].clear();
}
}
bool VSAParser::extractMessagePackets(std::vector<std::shared_ptr<Packet>>& packets)
{
if(settings != Settings::messageRecords()) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false; // We do not have exclusively message records
}
std::shared_ptr<Packet> packet;
bool activeExtendedMessage = false;
VSA::Type previousRecordType = VSA::Type::Invalid;
for(const auto& record : vsaRecords) {
VSA::Type activeRecordType = record->getType();
switch(activeRecordType) {
// Handle standard message records
case VSA::Type::AA0B: {
if(activeExtendedMessage) {
// Non-terminated extended message record
// There was a failure/unexpected behavior in the parsing process
report(APIEvent::Type::VSAExtendedMessageError, APIEvent::Severity::Error);
return false;
}
std::shared_ptr<VSAMessage> messageRecord = std::dynamic_pointer_cast<VSAMessage>(record);
if(!settings.messageFilter || messageRecord->filter(settings.messageFilter)) {
packet = messageRecord->getPacket();
packets.push_back(packet);
}
packet = nullptr;
break;
}
// Handle extended message records
case VSA::Type::AA0D:
case VSA::Type::AA0E:
case VSA::Type::AA0F: {
std::shared_ptr<VSAExtendedMessage> extendedMessageRecord = std::dynamic_pointer_cast<VSAExtendedMessage>(record);
if(!activeExtendedMessage) { // Start new extended message packet
packet = extendedMessageRecord->getPacket();
activeExtendedMessage = true;
previousRecordType = extendedMessageRecord->getType();
} else if(previousRecordType == activeRecordType) { // Continue existing extended message packet
extendedMessageRecord->appendPacket(packet);
if(extendedMessageRecord->getRecordCount() == static_cast<uint32_t>(extendedMessageRecord->getIndex() + 1)) { // Last record in sequence
if(!settings.messageFilter || extendedMessageRecord->filter(settings.messageFilter)) {
VSAExtendedMessage::truncatePacket(packet);
packets.push_back(packet);
}
activeExtendedMessage = false;
packet = nullptr;
previousRecordType = activeRecordType;
}
} else {
// Non-terminated extended message record
// There was a failure/unexpected behavior in the parsing process
activeExtendedMessage = false;
packet = nullptr;
previousRecordType = VSA::Type::Invalid;
report(APIEvent::Type::VSAOtherError, APIEvent::Severity::Error);
return false;
}
break;
}
default:
// Non-message record discovered
report(APIEvent::Type::VSAOtherError, APIEvent::Severity::Error);
return false;
}
}
vsaRecords.clear();
return true;
}

View File

@ -8,6 +8,7 @@ option(LIBICSNEO_BUILD_CPP_LIN_EXAMPLE "Build the LIN example." ON)
option(LIBICSNEO_BUILD_CPP_LIVEDATA_EXAMPLE "Build the Live Data example." ON)
option(LIBICSNEO_BUILD_CPP_COREMINI_EXAMPLE "Build the Coremini example." ON)
option(LIBICSNEO_BUILD_CPP_MDIO_EXAMPLE "Build the MDIO example." ON)
option(LIBICSNEO_BUILD_CPP_VSA_EXAMPLE "Build the VSA example." ON)
# Disabled until we properly build these in-tree
# option(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE "Build the command-line interactive C# example." OFF)
@ -53,6 +54,10 @@ if(LIBICSNEO_BUILD_CPP_MDIO_EXAMPLE)
add_subdirectory(cpp/mdio)
endif()
if(LIBICSNEO_BUILD_CPP_VSA_EXAMPLE)
add_subdirectory(cpp/vsa)
endif()
# if(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE)
# add_subdirectory(csharp)
# endif()

View File

@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneocpp-vsa-example VERSION 0.1.0)
set(CMAKE_CXX_STANDARD_REQUIRED 11)
include(GNUInstallDirs)
# Add an include directory like so if desired
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# Enable Warnings
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
# Add libicsneo, usually a git submodule within your project works well
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
add_executable(libicsneocpp-vsa-example src/VSAExample.cpp)
target_link_libraries(libicsneocpp-vsa-example icsneocpp)

View File

@ -0,0 +1,94 @@
#include <iostream>
#include <random>
#include "icsneo/icsneocpp.h"
void onEvent(std::shared_ptr<icsneo::APIEvent> event) {
std::cout << event->describe() << std::endl;
}
int main(int argc, char* argv[]) {
if(argc != 2) {
std::cout << "Usage: libicsneocpp-vsa-example <device serial>" << std::endl;
return -1;
}
std::string deviceSerial = argv[1];
// register an event callback so we can see all logged events
icsneo::EventManager::GetInstance().addEventCallback(icsneo::EventCallback(onEvent));
auto devices = icsneo::FindAllDevices();
if(devices.empty()) {
std::cout << "error: no devices found" << std::endl;
return -1;
}
std::shared_ptr<icsneo::Device> device;
for(auto&& d : devices) {
if(deviceSerial == d->getSerial()) {
device = d;
}
}
if(!device) {
std::cout << "error: failed to find a device with serial number: " << deviceSerial << std::endl;
return -1;
}
std::cout << "info: found " << device->describe() << std::endl;
if(!device->open()) {
std::cout << "error: unable to open device" << std::endl;
}
device->stopScript();
uint64_t firstOffset;
std::shared_ptr<icsneo::VSA> firstRecord;
if(!device->findFirstVSARecord(firstOffset, firstRecord)) {
std::cout << "error: unable to find first VSA record" << std::endl;
return -1;
}
std::cout << "info: found first VSA record at " << firstOffset << std::endl;
uint64_t origLastOffset;
std::shared_ptr<icsneo::VSA> origLastRecord;
if(!device->findLastVSARecord(origLastOffset, origLastRecord)) {
std::cout << "error: unable to find last VSA record" << std::endl;
return -1;
}
std::cout << "info: found last VSA record at " << origLastOffset << std::endl;
uint64_t canFrameCount = 0;
uint64_t ethFrameCount = 0;
device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> msg) {
if(msg->type != icsneo::Message::Type::Frame) {
return;
}
const auto frame = std::static_pointer_cast<icsneo::Frame>(msg);
if(frame->network.getType() == icsneo::Network::Type::CAN) {
++canFrameCount;
} else if(frame->network.getType() == icsneo::Network::Type::Ethernet) {
++ethFrameCount;
}
}));
icsneo::VSAExtractionSettings settings;
{
auto& filter = settings.filters.emplace_back();
filter.readRange.first = origLastRecord->getTimestampICSClock() - std::chrono::seconds(20);
filter.readRange.second = origLastRecord->getTimestampICSClock() - std::chrono::seconds(10);
}
{
auto& filter = settings.filters.emplace_back();
filter.readRange.first = firstRecord->getTimestampICSClock() + std::chrono::seconds(0);
filter.readRange.second = firstRecord->getTimestampICSClock() + std::chrono::seconds(10);
}
std::cout << "info: reading two blocks of VSA, 10s from the start and 10s from the end..." << std::endl;
if(!device->readVSA(settings)) {
std::cout << "error: unable to read VSA" << std::endl;
return -1;
}
std::cout << "info: processed " << canFrameCount << " CAN frames and " << ethFrameCount << " Ethernet frames" << std::endl;
return 0;
}

View File

@ -158,6 +158,15 @@ public:
FTIncorrectDevicePath = FTOK + 31,
FTOtherError = FTOK + 32,
// VSA
VSABufferCorrupted = 0x5000,
VSATimestampNotFound = VSABufferCorrupted + 1,
VSABufferFormatError = VSABufferCorrupted + 2,
VSAMaxReadAttemptsReached = VSABufferCorrupted + 3,
VSAByteParseFailure = VSABufferCorrupted + 4,
VSAExtendedMessageError = VSABufferCorrupted + 5,
VSAOtherError = VSABufferCorrupted + 6,
NoErrorFound = 0xFFFFFFFD,
TooManyEvents = 0xFFFFFFFE,
Unknown = 0xFFFFFFFF

View File

@ -76,6 +76,8 @@ public:
const std::shared_ptr<MessageFilter>& f = {},
std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
void dispatchMessage(const std::shared_ptr<Message>& msg);
std::function<std::unique_ptr<Packetizer>()> makeConfiguredPacketizer;
std::unique_ptr<Packetizer> packetizer;
std::unique_ptr<Encoder> encoder;
@ -93,7 +95,6 @@ protected:
std::mutex redirectingReadMutex; // Don't allow read to be disabled while in the redirectionFn
std::mutex syncMessageMutex;
void dispatchMessage(const std::shared_ptr<Message>& msg);
void handleInput(Packetizer& p, std::vector<uint8_t>& readBytes);
private:

View File

@ -29,10 +29,10 @@ public:
if(message->type == Message::Type::Frame || message->type == Message::Type::Main51 ||
message->type == Message::Type::RawMessage || message->type == Message::Type::ReadSettings) {
RawMessage& frame = *static_cast<RawMessage*>(message.get());
if(!matchNetworkType(frame.network.getType()))
const auto frame = std::dynamic_pointer_cast<RawMessage>(message);
if(!matchNetworkType(frame->network.getType()))
return false;
if(!matchNetID(frame.network.getNetID()))
if(!matchNetID(frame->network.getNetID()))
return false;
} else if (netid != Network::NetID::Any || networkType != Network::Type::Any) {
return false; // Filtering on a NetID or Type, but this message doesn't have one

View File

@ -43,6 +43,8 @@
#include "icsneo/communication/message/ethphymessage.h"
#include "icsneo/third-party/concurrentqueue/concurrentqueue.h"
#include "icsneo/platform/nodiscard.h"
#include "icsneo/disk/vsa/vsa.h"
#include "icsneo/disk/vsa/vsaparser.h"
#define ICSNEO_FINDABLE_DEVICE_BASE(className, type) \
static constexpr DeviceType::Enum DEVICE_TYPE = type; \
@ -581,6 +583,101 @@ public:
bool unsubscribeLiveData(const LiveDataHandle& handle);
bool clearAllLiveData();
// VSA Read functions
/**
* Read VSA message records from disk and dispatch the messages via Communication object. Default behavior excludes
* records older than the current CoreMini script and performs a full disk dump of other records. The CoreMini script is
* also stopped by default.
*
* @param extractionSettings Contains filters and other advanced settings for extraction process
*
* @return Returns false if there were failures during the read or parse processes or issues with record formatting, else true
*/
bool readVSA(const VSAExtractionSettings& extractionSettings = VSAExtractionSettings());
/**
* Determines important metadata about VSA record storage on the disk. Terminates at first failed attempt to retrieve information
*
* @param metadata The metadata object to store the probed information into
* @param extractionSettings The settings for this extraction of VSA data
*
* @return True if all metadata information was successfully found
*/
bool probeVSA(VSAMetadata& metadata, const VSAExtractionSettings& extractionSettings);
/**
* Find the first VSA record chronologically from ring buffer in the VSA log file on disk
*
* @param firstOffset Variable used to pass out offset of first record on the disk
* @param firstRecord Variable used to pass out the first record in the buffer
* @param extractionSettings The settings for this extraction of VSA data
* @param optMetadata Metadata about the current state of the VSA log file
* (Must include valid CoreMini timestamp, disk size, and isOverlapped values)
*
* @return True if the first record was found successfully
*/
bool findFirstVSARecord(uint64_t& firstOffset, std::shared_ptr<VSA>& firstRecord,
const VSAExtractionSettings& extractionSettings = VSAExtractionSettings(), std::optional<VSAMetadata> optMetadata = std::nullopt);
/**
* Find the last record chronologically from ring buffer in the VSA log file with a valid timestamp
*
* @param lastOffset Variable used to pass out the offset of the last record on the disk
* @param lastRecord Variable used to pass out the last record with a valid timestamp
* @param extractionSettings The settings for this extraction of VSA data
* @param optMetadata Metadata about the current state of the VSA log file
* (Must include valid CoreMini timestamp, disk size, and isOverlapped values)
*
* @return True if the last record was found successfully
*/
bool findLastVSARecord(uint64_t& lastOffset, std::shared_ptr<VSA>& lastRecord,
const VSAExtractionSettings& extractionSettings = VSAExtractionSettings(), std::optional<VSAMetadata> optMetadata = std::nullopt);
/**
* Find the closest VSA record to the desired time_point
*
* @param point The desired time_point of the record
* @param vsaOffset Variable used to pass out offset of record closest to the desired time_point
* @param record Variable used to pass out the record closest to the desired time_point
* @param extractionSettings Settings for this extraction of VSA data
* @param optMetadata Optional param to include metadata about the VSA log file on disk
*
* @return Pair containing the location of the record closest to the desired time_point (in bytes from the beginning of VSA log file) and the record itself
*/
bool findVSAOffsetFromTimepoint(
ICSClock::time_point point, uint64_t& vsaOffset, std::shared_ptr<VSA>& record, const VSAExtractionSettings& extractionSettings = VSAExtractionSettings(),
std::optional<VSAMetadata> optMetadata = std::nullopt);
/**
* Parse VSA message records with the given filter and dispatch them with this device's com channel
*
* @param metadata Important information about the VSA logfile (including first record location)
* @param extractionSettings Settings for this extraction of VSA data
* @param filter Struct used to determine which bytes to read and to filter out undesired VSA records
*
* @return True if there were no failures reading from disk, parsing VSA records, or dispatching VSA records
*/
bool parseVSA(
VSAMetadata& metadata, const VSAExtractionSettings& extractionSettings = VSAExtractionSettings(),
const VSAMessageReadFilter& filter = VSAMessageReadFilter());
/**
* Wrapper function for Device::readLogicalDisk(pos, into, amount, ...) that handles the VSA record ring buffer.
* Handles pos that is before the VSA::RecordStartOffset or is larger than the diskSize.
* Sets amount to maximum size of ring buffer if given amount is too large.
*
* @param pos Position to start read from in relation to VSA file start
* @param into The buffer to read bytes into from the disk
* @param amount The number of bytes to read into the buffer
* @param metadata Optional metadata param (used to determine disk size and if disk is overlapped)
*
* @return Returns value of return from readLogicalDisk with the given inputs
*/
std::optional<uint64_t> vsaReadLogicalDisk(
uint64_t pos, uint8_t* into, uint64_t amount, std::optional<VSAMetadata> metadata = std::nullopt
);
protected:
bool online = false;
int messagePollingCallbackID = 0;
@ -758,6 +855,96 @@ private:
void scriptStatusThreadBody();
void stopScriptStatusThreadIfNecessary(std::unique_lock<std::mutex> lk);
// VSA Read functions
/**
* Read the timestamp from disk of the VSA record stored at pos. If the timestamp is unparsable, attempt to read from
* previous records up to minPos
*
* @param parser The parser that is used to create a VSA record from the given buffer
* @param buffer Vector of bytes that stores a sector from the disk
* @param pos The location that the buffer was read from
* @param minPos The leftmost (minimum) offset from the beginning of the VSA log file to attempt to read from
* @param optMetadata Optional param to include metadata about the VSA log file on disk
*
* @return The timestamp of the first valid record found at or before the given position
*/
std::optional<uint64_t> getVSATimestampOrBefore(VSAParser& parser, std::vector<uint8_t>& buffer, uint64_t pos, uint64_t minPos,
std::optional<VSAMetadata> optMetadata = std::nullopt);
/**
* Read the timestamp from disk of the VSA record stored at pos. If the timestamp is unparsable, attempt to read from
* previous records up to maxPos
*
* @param parser The parser that is used to create a VSA record from the given buffer
* @param buffer Vector of bytes that stores a sector that was previously read from the disk
* @param pos The location that data in the buffer was read from
* @param maxPos The rightmost (maximum) offset from the beginning of the VSA log file to attempt to read from
* @param optMetadata Optional param to include metadata about the VSA log file on disk
*
* @return The timestamp of the first valid record found at or after the given position
*/
std::optional<uint64_t> getVSATimestampOrAfter(VSAParser& parser, std::vector<uint8_t>& buffer, uint64_t pos, uint64_t maxPos,
std::optional<VSAMetadata> optMetadata = std::nullopt);
/**
* Iterate over VSA records and dispatch the messages contained within them. For extended message records, we concatenate the payloads of all of
* the records together before dispatching. Dispatch is performed by Communication::dispatchMessage(...).
*
* @param parser The parser that holds the VSAMessage records to be dispatched
*
* @return True if dispatching of records is successful without unhandled issues from record parse, else false
*/
bool dispatchVSAMessages(VSAParser& parser);
/**
* Determine if the ring buffer for VSA records has filled entirely and looped to the beginning.
*
* @param optMetadata Optional metadata param with partial information about current state of VSA log file
* (Must contain valid disk size and CoreMini timestamp)
*
* @return True if the buffer has looped; Returns std::nullopt if unable to determine
*/
std::optional<bool> isVSAOverlapped(std::optional<VSAMetadata> optMetadata = std::nullopt);
/**
* Find the first extended message record in the sequence of the given extended message record by backtracking in the disk.
* Results are returned by reference (not through the return value)
*
* @param record The extended message record whose sequence for which to find the first record
* @param pos The position of the given record in the VSA log file (in bytes)
* @param parser Used to parse indices and sequence numbers from the extended message records
* @param metadata Optional param to include metadata about the VSA log file on disk
*
* @return True if the first extended message record in the sequence was successfully found
*/
bool findFirstExtendedVSAFromConsecutive(std::shared_ptr<VSAExtendedMessage>& record, uint64_t& pos,
VSAParser& parser, std::optional<VSAMetadata> metadata = std::nullopt);
/**
* Find the first record before the given position that contains a valid timestamp. Results are returned by reference.
*
* @param record The record from which to backtrack in the VSA buffer
* @param pos The position of the given record in the VSA log file (in bytes)
* @param parser Used to parse records from the VSA buffer
*
* @return True if a record with valid timestamp is found within a set number of reads
*/
bool findPreviousRecordWithTimestamp(std::shared_ptr<VSA>& record, uint64_t& pos, VSAParser& parser);
/**
* Get the creation timestamp of the CoreMini script from VSA log file
*
* @return Timestamp in 25 nanosecond ticks since January 1, 2007
*/
std::optional<uint64_t> getCoreMiniScriptTimestamp();
/**
* Get the size of the VSA file storage system from the CoreMini script
*
* @return The size of the vsa log files on the disk
*/
std::optional<uint64_t> getVSADiskSize();
};
}

View File

@ -24,7 +24,7 @@ public:
protected:
RADGigastar(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) {
initialize<RADGigastarSettings, Disk::ExtExtractorDiskReadDriver, Disk::NeoMemoryDiskDriver>(makeDriver);
initialize<RADGigastarSettings>(makeDriver);
}
void setupPacketizer(Packetizer& packetizer) override {

View File

@ -0,0 +1,312 @@
#ifndef __VSA_H__
#define __VSA_H__
#ifdef __cplusplus
#include "icsneo/communication/network.h"
#include "icsneo/communication/packet.h"
#include <vector>
#include <memory>
#include <chrono>
#include "stdint.h"
namespace icsneo {
using CaptureBitfield = uint16_t;
static constexpr uint64_t ICSEpochHoursSinceUnix = 13514 * 24; // Number of hours between the start of the Unix epoch and the start of the ICS epoch
static constexpr uint64_t UINT63_MAX = 0x7FFFFFFFFFFFFFFFu;
/**
* Struct that meets Clock format requirements from STL Chrono library.
* Indicates time for for the ICS Epoch (January 1, 2007) in 25 nanosecond ticks.
*/
struct ICSClock {
using rep = uint64_t; // Type for tick count
using period = std::ratio_multiply<std::ratio<25>, std::nano>; // Ratio of tick length to seconds (25 nanoseconds)
using duration = std::chrono::duration<rep, period>; // Type for duration in 25 nanosecond ticks
using time_point = std::chrono::time_point<ICSClock>; // Type for a point in time with respect to ICSClock
static constexpr bool is_steady = true; // This clock does not move backwards
/**
* Get the time_point at the current time with respect to ICSClock
*
* @return Time point at the current time with respect to ICSClock
*/
static time_point now() noexcept
{
return time_point { std::chrono::duration_cast<duration>(std::chrono::system_clock::now().time_since_epoch()) -
std::chrono::hours(ICSEpochHoursSinceUnix) };
}
};
using Timestamp = ICSClock::time_point; // Point in time to start or stop read
class VSA;
/**
* Holds metadata for the VSA log file
*/
struct VSAMetadata {
uint64_t firstRecordLocation = UINT64_MAX; // Location of the record with lowest timestamp in ring buffer
std::shared_ptr<VSA> firstRecord = nullptr; // The record with lowest timestamp
uint64_t lastRecordLocation = UINT64_MAX; // Location of the record with the highest timestamp in ring buffer
std::shared_ptr<VSA> lastRecord = nullptr; // The record with the highest timestamp
uint64_t bufferEnd = UINT64_MAX; // One byte beyond the last byte of the sequence started from lastRecordLocation
uint64_t diskSize = 0; // The size of the vsa log file on the disk
bool isOverlapped = false; // Determines if VSA ring buffer has looped to beginning
uint64_t coreMiniTimestamp = UINT64_MAX; // Timestamp of the CoreMini message in 25 nanosecond ticks since January 1, 2007
};
/**
* Struct used to exclude VSA message records from parse
*/
struct VSAMessageReadFilter {
CaptureBitfield captureBitfield = UINT16_MAX; // The capture from which to gather VSA message records. UINT16_MAX indicates 'all captures'
// The range of timestamps to collect record data from
std::pair<Timestamp, Timestamp> readRange = std::make_pair(Timestamp(ICSClock::duration(0x0ull)), Timestamp(ICSClock::duration(UINT64_MAX)));
static constexpr Timestamp MinTimestamp = Timestamp(ICSClock::duration(0x0ull));
static constexpr Timestamp MaxTimestamp = Timestamp(ICSClock::duration(UINT64_MAX));
};
struct VSAExtractionSettings {
bool parseOldRecords = false;
bool stopCoreMini = true;
std::vector<VSAMessageReadFilter> filters;
};
/**
* Abstract VSA base class to store VSA record data read from VSA log file on disk
*/
class VSA {
public:
static constexpr size_t StandardRecordSize = 32; // Size of most VSA records
static constexpr uint64_t RecordStartOffset = 0x06000000u; // Offset of VSA record ring buffer from start of VSA log file
/**
* Convert the given time_point object to a timestamp in 25 nanosecond ticks since January 1, 2007
*
* @return Timestamp of the given time_point in 25 nanosecond ticks since January 1, 2007
*/
static uint64_t getICSTimestampFromTimepoint(const Timestamp& point) noexcept { return point.time_since_epoch().count(); }
/**
* Enum to determine what type of record is underlying VSA parent class
*/
enum class Type : uint16_t {
AA00 = 0xAA00u, // Pad
AA01 = 0xAA01u, // Message Data (Deprecated)
AA02 = 0xAA02u, // 'Logdata'
AA03 = 0xAA03u, // Event
AA04 = 0xAA04u, // Partition Info
AA05 = 0xAA05u, // Application Error
AA06 = 0xAA06u, // Internal/Debug
AA07 = 0xAA07u, // Internal/Debug
AA08 = 0xAA08u, // Buffer Info
AA09 = 0xAA09u, // Device Info
AA0A = 0xAA0Au, // Logger Configuration Info (Deprecated)
AA0B = 0xAA0Bu, // Message Data
AA0C = 0xAA0Cu, // PCM Audio Data
AA0D = 0XAA0Du, // Message Data (Extended)
AA0E = 0xAA0Eu, // Message Data (Extended)
AA0F = 0xAA0Fu, // Message Data (Extended)
AA6A = 0xAA6Au, // Logger Configuration Backup (512 Bytes)
Invalid = UINT16_MAX // Used to indicate unset or unhandled VSA record types
};
/**
* Get the record type
*
* @return Type of record
*/
Type getType() const { return type; }
/**
* Get the timestamp stored in this record
*
* @return The record's timestamp in 25 nanosecond ticks since January 1, 2007
*/
virtual uint64_t getTimestamp() = 0;
/**
* Determine whether this record has a valid timestamp. All invalid timestamps are set to the maximum value for a uint64_t.
*
* @return True if the timestamp is set to a valid number
*/
bool isTimestampValid() { return getTimestamp() != UINT64_MAX && !checksumFailed; }
/**
* Determine if the checksum for this record failed
*
* @return True if the checksum does not pass
*/
bool getChecksumFailed() { return checksumFailed; }
/**
* Get the timestamp of this record in C++ native std::chrono::time_point
*
* @return Timestamp of record as an std::chrono::time_point
*/
Timestamp getTimestampICSClock()
{
return Timestamp(std::chrono::duration_cast<ICSClock::duration>(std::chrono::nanoseconds(getTimestamp() * 25)));
}
protected:
/**
* Used to construct a VSA record from child class
*/
VSA() {}
/**
* Used to set the type of this record in child class constructors
*
* @param recordType The type of this record
*/
void setType(Type recordType) { this->type = recordType; }
/**
* Set whether the checksum was passed for this record. This is called in each child class constructor.
*
* @param fail True if checksum did not pass, else false
*/
void setChecksumFailed(bool fail) { checksumFailed = fail; }
private:
/**
* Performs checksum on data in specific record type. Calls VSA::setChecksumFailed(...) from child class.
*
* @param recordBytes Bytestream of record to perform checksum with
*/
virtual void doChecksum(uint8_t* recordBytes) = 0;
Type type = Type::Invalid; // The type of this record
bool checksumFailed = false; // Determines if checksum failed
};
/**
* Interface class for handling common functionality of VSAMessage record types (AA0B, AA0D, AA0E, AA0F)
*/
class VSAMessage : public VSA {
public:
static constexpr size_t CoreMiniPayloadSize = 24; // Size of CoreMini message (payload)
/**
* Construct a packet from the message payload and network
*
* @return Packet constructed from payload and network
*/
std::shared_ptr<Packet> getPacket() const;
/**
* Reserve enough memory to store a CoreMini message in the given packet
*
* @param packet The packet in which we are reserving memory for a message
*/
virtual void reservePacketData(std::shared_ptr<Packet>& packet) const { packet->data.reserve(CoreMiniPayloadSize); }
/**
* Determine whether to filter out this VSAMessage record during parsing
*
* @param filter The filter struct to check this message record against
*
* @return True if this message passes the given filter
*/
virtual bool filter(const std::shared_ptr<VSAMessageReadFilter> filter) = 0;
protected:
/**
* Constructor for normal instance of VSAMessage class
*
* @param messageBytes Bytestream that begins at the start of the message payload
* @param numBytes The number of bytes that the message payload contains
* @param networkId The CoreMini ID of the network for this message
*/
VSAMessage(uint8_t* const messageBytes, size_t numBytes, Network::CoreMini networkId = static_cast<Network::CoreMini>(UINT16_MAX))
: VSA(), payload(messageBytes, messageBytes + numBytes), network(networkId) {}
std::vector<uint8_t> payload; // CoreMini message/payload of VSA record containing message data
Network network; // CoreMini network of this message
};
/**
* Interface class for handling common functionality of VSA Extended Message records (AA0D, AA0E, AA0F)
*/
class VSAExtendedMessage : public VSAMessage {
public:
static void truncatePacket(std::shared_ptr<Packet> packet);
/**
* Appends the payload for this message to the given packet.
* Also sets the network of the packet if unset (used primarily for AA0F records which do not contain the network in the first extended message record).
*
* @param packet The packet to append this record's payload to
*/
void appendPacket(std::shared_ptr<Packet> packet) const;
/**
* Get the total number of records for this extended message
*
* @return Total number of records that this message spans
*/
uint32_t getRecordCount() const { return totalRecordCount; }
/**
* Get the index of this record in the extended message sequence
*
* @return The index of this record
*/
uint16_t getIndex() { return index; };
/**
* Get the numerical id of the sequence of extended message records this record is a part of
*
* @return The sequence number of this extended message record
*/
uint16_t getSequenceNum() { return sequenceNum; }
protected:
/**
* Constructor for normal instance of VSAExtendedMessage
*
* @param messageBytes Bytestream that begins at the start of the message payload
* @param numBytes The length of the message payload in bytes
* @param networkId The CoreMini ID of the network for this message
*/
VSAExtendedMessage(uint8_t* const messageBytes, size_t numBytes, Network::CoreMini networkId = static_cast<Network::CoreMini>(UINT16_MAX))
: VSAMessage(messageBytes, numBytes, networkId) {}
/**
* Set the total number of records for this message
*
* @param recordCount Total number of records for this message
*/
void setRecordCount(uint32_t recordCount) { totalRecordCount = recordCount; }
/**
* Set the index of this record
*
* @param recordIndex The index of this record in its extended message sequence
*/
void setIndex(uint16_t recordIndex) { this->index = recordIndex; }
/**
* Set the sequence number of this record
*
* @param seq The id for the extended message sequence this record is a part of
*/
void setSequenceNum(uint16_t seq) { sequenceNum = seq; }
private:
uint32_t totalRecordCount; // The total number of records for the extended message
uint16_t index; // The index of this record in its extended message sequence
uint16_t sequenceNum; // The id of the sequence of records this record is a part of
};
} // namespace icsneo
#endif // __cplusplus
#endif // __VSA_H__

View File

@ -0,0 +1,55 @@
#ifndef __VSA02_H__
#define __VSA02_H__
#ifdef __cplusplus
#include "icsneo/disk/vsa/vsa.h"
namespace icsneo {
/**
* Class that contains data for Logdata records
*/
class VSA02 : public VSA {
public:
/**
* Constructor that parses the given bytestream
*
* @param bytes Bystream that contains data for Logdata VSA records
*/
VSA02(uint8_t* const bytes);
/**
* Get the timestamp for this record in 25 nanosecond ticks since January 1, 2007
*
* @return The timestamp for this record in 25 nanosecond ticks since January 1, 2007
*/
uint64_t getTimestamp() override { return timestamp; }
private:
/**
* Perform the checksum for this record
*
* @param bytes Bystream to test against the checksum
*/
void doChecksum(uint8_t* bytes) override;
uint16_t constantIndex; // Index into CoreMini binary where constant data for this record can be found
struct Flags {
bool hasMoreData : 1; // Set to true if there are further Logdata records expected to terminate this "chain"
uint8_t numSamples : 3; // Number of valid samples in samples
bool isAscii : 1; // Set to true if the processing code should treat samples as an ASCII string
bool prefixTime : 1; // Set to true if the function block step that created this record requested that the timestamp be prepended on the output
bool sample0IsHex : 1; // Set to true if the value in sample 0 should be written as hex
bool sample1IsHex : 1; // Set to true if the value in sample 1 shoudl be written as hex
} flags; // Series of flags for this record
uint8_t pieceCount; // Value of the rolling counter for this "chain" of logdata records
uint64_t timestamp; // Timestamp in 25 nanosecond ticks since January 1, 2007
std::vector<uint8_t> samples; // Data for this record that varies based on the above flags. Either 2 32.32 fixed point values or 16 byte ASCII string
uint16_t checksum; // The sum of the previous 15 words
};
} // namespace icsneo
#endif // __cplusplus
#endif // __VSA02_H__

View File

@ -0,0 +1,54 @@
#ifndef __VSA03_H__
#define __VSA03_H__
#ifdef __cplusplus
#include "icsneo/disk/vsa/vsa.h"
namespace icsneo {
/**
* Class used to store event
*/
class VSA03 : public VSA {
public:
/**
* Constructor that extracts data from the given bytestream
*
* @param bytes Bytestream to extract VSA record data from
*/
VSA03(uint8_t* const bytes);
/**
* Get the timestamp for this record in 25 nanosecond ticks since January 1, 2007
*
* @return The timestamp for this record in 25 nanosecond ticks since January 1, 2007
*/
uint64_t getTimestamp() override { return timestamp; }
private:
/**
* Perform checksum for this record
*
* @param bytes Bytestream to test against the checksum
*/
void doChecksum(uint8_t* bytes) override;
enum class EventType : uint16_t {
CaptureStarted = 0,
StorageReconnected = 3,
FileSystemBufferOverflow = 4,
LoggerWentToSleep = 5,
Internal = 7,
CaptureStopped = 8,
LoggerPowerEvent = 9
} eventType; // Enumerated value indicating which type of event occurred
uint16_t eventData; // Information about the event that is dependent on eventType
uint64_t timestamp; // Timestamp of this record in 25 nanosecond ticks since January 1, 2007
uint16_t checksum; // The sum of the previous 7 words
};
} // namespace icsneo
#endif // __cplusplus
#endif // __VSA03_H__

View File

@ -0,0 +1,49 @@
#ifndef __VSA04_H__
#define __VSA04_H__
#ifdef __cplusplus
#include "icsneo/disk/vsa/vsa.h"
namespace icsneo {
/**
* Class that contains a partition info record
*/
class VSA04 : public VSA {
public:
/**
* Constructor that created partition info record from bytestream
*
* @param bytes Bytestream to create this record from
*/
VSA04(uint8_t* const bytes);
/**
* Get the timestamp of this record in 25 nanosecond ticks since January 1, 2007
*
* @return Timestamp of this record in 25 nanosecond ticks since January 1, 2007
*/
uint64_t getTimestamp() override { return timestamp; }
private:
/**
* Perform the checksum for this record
*
* @param bytes Bytestream to check against the checksum
*/
void doChecksum(uint8_t* bytes) override;
struct Flags {
bool invalidRequestDetected : 1; // Indicates if an invalid request was detected
uint16_t reserved : 15; // Empty flag bits
} flags; // Mostly empty field for flags
uint16_t partitionIndex; // The index of the partition containing this record
uint64_t timestamp; // Timestamp of this record in 25 nanosecond ticks since January 1, 2007
uint16_t checksum; // Sum of the previous 7 words
};
} // namespace icsneo
#endif // __cplusplus
#endif // __VSA04_H__

View File

@ -0,0 +1,79 @@
#ifndef __VSA05_H__
#define __VSA05_H__
#ifdef __cplusplus
#include "icsneo/disk/vsa/vsa.h"
namespace icsneo {
/**
* Class that holds data for application error records
*/
class VSA05 : public VSA {
public:
/**
* Constructor to convert bytestream into application error record
*
* @param bytes The bytestream containing the record data
*/
VSA05(uint8_t* const bytes);
/**
* Get the timestamp for this record in 25 nanosecond ticks since January 1, 2007
*
* @return The timestamp for this record in 25 nanosecond ticks since January 1, 2007
*/
uint64_t getTimestamp() override { return timestamp; }
private:
/**
* Perform the checksum on this record
*
* @param bytes Bytestream to test against the checksum
*/
void doChecksum(uint8_t* bytes) override;
enum class ErrorType : uint16_t {
NetworkReceiveBufferOverflow = 0,
NetworkTransmitBufferOverflow = 1,
NetworkTransmitReportBufferOverflow = 2,
PeripheralProcessorCommunicationError = 3,
NetworkPeripheralOverflow = 4,
CommunicationPacketChecksumError = 6,
CommunicationPacketDetectedMissingByte = 7,
FailedToApplySettingsToNetwork = 9,
EnabledNetworkCountExceedsLicenseCapability = 10,
NetworkNotEnabled = 11,
DetectedInvalidTimestamp = 12,
LoadedDefaultSettings = 13,
DeviceAttemptedUnsupportedOperation = 14,
TrasmitBufferFillExceededThreshold = 17,
TransmitRequestedOnInvalidNetwork = 18,
TransmitRequestedOnTransmitIncapableNetwork = 19,
TransmitRequestedWhileControllersInactive = 20,
FilterMatchesExceedLimit = 21,
EthernetPreemptionError = 22,
TransmitWhileControllerModeInvalid = 23,
FragmentedEthernetIPFrame = 25,
TransmitBufferUnderrun = 26,
ActiveCoolingFailureDetected = 27,
OvertemperatureConditionDetected = 28,
UndersizedEthernetFrame = 30,
OversizedEthernetFrame = 31,
SystemWatchdogEventOcurred = 32,
SystemClockFailureDetected = 33,
RecoveredFromSystemClockFailure = 34,
SystemResetFailedPeripheralComponent = 35,
FailedToInitializeLoggerDisk = 41,
AttemptedToApplyInvalidSettingsToNetwork = 42
} errorType; // Enumerated value indicating the type of error that occurred
uint16_t errorNetwork; // When applicable, the enumerated network index that the error occurred on
uint64_t timestamp; // Timestamp of this record in 25 nanosecond ticks since January 1, 2007
uint16_t checksum; // Sum of the previous 7 words
};
} // namespace icsneo
#endif // __cplusplus
#endif // __VSA05_H__

View File

@ -0,0 +1,49 @@
#ifndef __VSA06_H__
#define __VSA06_H__
#ifdef __cplusplus
#include "icsneo/disk/vsa/vsa.h"
#include <vector>
namespace icsneo {
/**
* Class that holds data for an internal/debug VSA record
*/
class VSA06 : public VSA {
public:
/**
* Constructor to convert bytestream into internal/debug record
*
* @param bytes Bytestream to parse into internal/debug record
*/
VSA06(uint8_t* const bytes);
/**
* Get the timestamp for this record in 25 nanosecond ticks since January 1, 2007
*
* @return The timestamp for this record in 25 nanosecond ticks since January 1, 2007
*/
uint64_t getTimestamp() override { return timestamp; }
private:
/**
* Perform the checksum on this record
*
* @param bytes Bytestream to test against the checksum
*/
void doChecksum(uint8_t* bytes) override;
std::vector<uint32_t> savedSectors; // Unknown
uint16_t error; // Unknown
uint16_t savedSectorsHigh; // Unknown
uint64_t timestamp; // Timestamp for this record in 25 nanosecond ticks since January 1, 2007
uint16_t checksum; // Sum of the previous 15 words
};
} // namespace icsneo
#endif // __cplusplus
#endif // __VSA06_H__

View File

@ -0,0 +1,49 @@
#ifndef __VSA07_H__
#define __VSA07_H__
#ifdef __cplusplus
#include "icsneo/disk/vsa/vsa.h"
#include <vector>
namespace icsneo {
/**
* Class that holds data for an internal/debug VSA record
*/
class VSA07 : public VSA {
public:
/**
* Constructor to convert bytestream into internal/debug record
*
* @param bytes Bytestream to parse into internal/debug record
*/
VSA07(uint8_t* const bytes);
/**
* Get the timestamp for this record in 25 nanosecond ticks since January 1, 2007
*
* @return The timestamp for this record in 25 nanosecond ticks since January 1, 2007
*/
uint64_t getTimestamp() override { return timestamp; }
private:
/**
* Perform the checksum on this record
*
* @param bytes Bytestream to test against the checksum
*/
void doChecksum(uint8_t* bytes) override;
uint32_t lastSector; // Unknown
uint32_t currentSector; // Unknown
std::vector<uint8_t> reserved; // Unused bytes (12 bytes)
uint64_t timestamp; // Timestamp for this record in 25 nanosecond ticks since January 1, 2007
uint16_t checksum; // Sum of the previous 15 words
};
} // namespace icsneo
#endif // __cplusplus
#endif // __VSA07_H__

View File

@ -0,0 +1,46 @@
#ifndef __VSA08_H__
#define __VSA08_H__
#ifdef __cplusplus
#include "icsneo/disk/vsa/vsa.h"
namespace icsneo {
/**
* Class to store data for a buffer information record
*/
class VSA08 : public VSA {
public:
/**
* Constructor to convert a bytestream to a buffer info record
*
* @param bytes Bytestream to convert into a buffer record
*/
VSA08(uint8_t* const bytes);
/**
* Get the timestamp for this record in 25 nanosecond ticks since January 1, 2007
*
* @return The timestamp for this record in 25 nanosecond ticks since January 1, 2007
*/
uint64_t getTimestamp() override { return timestamp; }
private:
/**
* Perform the checksum on this record
*
* @param bytes Bytestream to test against the checksum
*/
void doChecksum(uint8_t* bytes) override;
std::vector<uint8_t> troubleSramCount; // Unknown (4 bytes)
std::vector<uint32_t> troubleSectors; // Unknown (16 bytes)
uint64_t timestamp; // Timestamp for this record in 25 nanosecond ticks since January 1, 2007
uint16_t checksum; // Sum of the previous 15 words
};
} // namespace icsneo
#endif // __cplusplus
#endif // __VSA08_H__

View File

@ -0,0 +1,63 @@
#ifndef __VSA09_H__
#define __VSA09_H__
#ifdef __cplusplus
#include "icsneo/disk/vsa/vsa.h"
namespace icsneo {
/**
* Class used to store data for a Device Information VSA Record
*/
class VSA09 : public VSA {
public:
/**
* Constructor to convert bytestream to Device Information VSA Record
*
* @param bytes Bytestream to convert into Device Information Record
*/
VSA09(uint8_t* const bytes);
/**
* Get the timestamp for this record in 25 nanosecond ticks since January 1, 2007
*
* @return The timestamp for this record in 25 nanosecond ticks since January 1, 2007
*/
uint64_t getTimestamp() override { return timestamp; }
private:
/**
* Perform the checksum on this record
*
* @param bytes Bytestream to test against the checksum
*/
void doChecksum(uint8_t* bytes) override;
uint32_t serialNumber; // Decimal representation of Base-36 serial number
uint8_t firmwareMajorVersion; // Major version of firmware (A for A.B version)
uint8_t firmwareMinorVersion; // Minor version of firmware (B for A.B version)
uint8_t manufactureMajorRevision; // Major version of manufacture revision (A for A.B revision)
uint8_t manufactureMinorRevision; // Minor version of manufacture revision (B for A.B revision)
uint8_t bootloaderMajorVersion; // Major version of bootloader (A for A.B version)
uint8_t bootloaderMinorVersion; // Minor version of bootloader (B for A.B version)
std::vector<uint8_t> reserved0; // Unused bytes (6 bytes)
enum class HardwareID : uint8_t {
NeoVIRED = 0,
NeoVIFIRE = 1,
NeoVIION = 11,
RADGalaxy = 19,
RADMars = 29,
ValueLOG = 31,
NeoVIRED2FIRE3 = 33,
RADGigastar = 36
} hardwareID; // Identifier for specific hardware device type
std::vector<uint8_t> reserved1; // Unused bytes (3 bytes)
uint64_t timestamp; // Timestamp for this record in 25 nanosecond ticks since January 1, 2007
uint16_t checksum; // Sum of the previous 15 words
};
} // namespace icsneo
#endif // __cplusplus
#endif // __VSA09_H__

View File

@ -0,0 +1,57 @@
#ifndef __VSA0B_H__
#define __VSA0B_H__
#ifdef __cplusplus
#include <vector>
#include "icsneo/disk/vsa/vsa.h"
namespace icsneo {
/**
* Class that holds single-record message records
*/
class VSA0B : public VSAMessage {
public:
/**
* Constructor that reads message record data from bytestream
*
* @param bytes Bytestream to read message record data from
*/
VSA0B(uint8_t* const bytes);
/**
* Determine whether to filter out this message record
*
* @param filter The filter to check this record against
*
* @return True if this record has passed the filter (i.e., is not being filtered out)
*/
bool filter(const std::shared_ptr<VSAMessageReadFilter> filter) override;
/**
* Get the timestamp of this record in 25 nanosecond ticks since January 1, 2007
*
* @return Timestamp of this record in 25 nanosecond ticks since January 1, 2007
*/
uint64_t getTimestamp() override { return timestamp; }
private:
/**
* Perform a checksum on this record
*
* @param bytes Bytestream to test against the checksum
*/
void doChecksum(uint8_t* bytes) override;
uint16_t captureBitfield; // The capture that this record is a part of
uint8_t reserved; // Unused bytes
uint16_t checksum; // Sum of the previous 15 half words
uint64_t timestamp; // Timestamp of this record in 25 nanosecond ticks since January 1, 2007 (extracted from CoreMini message payload)
};
} // namespace icsneo
#endif // __cplusplus
#endif // __VSA0B_H__

View File

@ -0,0 +1,54 @@
#ifndef __VSA0C_H__
#define __VSA0C_H__
#ifdef __cplusplus
#include "icsneo/disk/vsa/vsa.h"
#include <vector>
namespace icsneo {
/**
* Class used to hold data for a PCM Audio VSA Record
*/
class VSA0C : public VSA {
public:
/**
* Constructor to convert bytestream to PCM Audio Record
*
* @param bytes Bytestream to parse
*/
VSA0C(uint8_t* const bytes);
/**
* Get the timestamp for this record in 25 nanosecond ticks since January 1, 2007
*
* @return The timestamp for this record in 25 nanosecond ticks since January 1, 2007
*/
uint64_t getTimestamp() override { return timestamp; }
private:
/**
* Perform the checksum on this record
*
* @param bytes Bytestream to test against the checksum
*/
void doChecksum(uint8_t* bytes) override;
uint16_t captureBitfield; // Capture this record is a member of (Unused)
uint8_t audioPreamble; // Unknown
uint8_t audioHeader; // Unknown
std::vector<uint8_t> pcmData; // Audio data payload (14 bytes)
uint64_t timestamp; // Timestamp of this record in 25 nanosecond ticks since January 1, 2007
struct VNet {
uint16_t vNetSlot : 2; // Bits to identify VNet slot of this record
uint16_t reserved : 14; // Unused bits
} vNetBitfield; // Struct to ensure VNetSlot is only 2 bits
uint16_t checksum; // Sum of the previous 15 words
};
} // namespace icsneo
#endif // __cplusplus
#endif // __VSA0C_H__

View File

@ -0,0 +1,134 @@
#ifndef __VSA0D_H__
#define __VSA0D_H__
#ifdef __cplusplus
#include "icsneo/disk/vsa/vsa.h"
namespace icsneo {
/**
* Base class for VSA0D extended message record types
*/
class VSA0D : public VSAExtendedMessage {
public:
/**
* Constructor for VSA0D parent class. Passes most information to VSAExtendedMessage constructor.
*
* @param bytes Bytestream to read record data from
* @param messageBytes Bytestream starting at the payload of this message record
* @param numBytes The length of the payload of this message record
* @param runningChecksum Checksum for the payload bytes of this sequence of extended message records
* @param networkId The CoreMini Network ID for this record
*/
VSA0D(uint8_t* const bytes, uint8_t* const messageBytes, size_t numBytes, uint32_t& runningChecksum, Network::CoreMini networkId = static_cast<Network::CoreMini>(0xFFFFu));
};
/**
* Class holding data for the first record in a series of VSA0D extended message records
*/
class VSA0DFirst : public VSA0D {
public:
/**
* Constructor that parses first 32 bytes of bytestream into readable data.
*
* @param bytes Bytestream to parse VSA record from
*/
VSA0DFirst(uint8_t* const bytes, uint32_t& runningChecksum);
/**
* Reserve memory in the packet data vector to store message data from this record and subsequent consecutive records.
*
* @param packet The packet to reserve memory in
*/
void reservePacketData(std::shared_ptr<Packet>& packet) const override;
/**
* Determine whether to filter out this message record
*
* @param filter The filter to check this record against
*
* @return True if the record passes the filter
*/
bool filter(const std::shared_ptr<VSAMessageReadFilter> filter) override;
/**
* Get the timestamp of this record. Timestamp indicates number of 25 nanosecond ticks since January 1, 2007.
*
* @return The timestamp of this record
*/
uint64_t getTimestamp() override { return timestamp; }
/**
* Reorder bytes in the payload between this record and the second record in the sequence. The bytes are reordered to allow
* simple concatenation of payload bytes from records before creating and dispatching a packet.
*
* @param secondPayload Reference to the payload from the second record in the sequence
*/
void reorderPayload(std::vector<uint8_t>& secondPayload);
private:
/**
* Perform checksum on this record with the given bytestream
*
* @param bytes The bytestream to test against the checksum
*/
void doChecksum(uint8_t* bytes) override;
uint16_t captureBitfield; // The data capture this record is a part of
uint64_t timestamp; // The timestamp of this record in 25 nanosecond ticks since January 1, 2007
struct VNet {
uint8_t vNetSlot : 2; // VNet bytes
uint8_t reserved : 6; // Unused bytes
} vNetInfo; // Struct used to indicate which bytes are actually used for VNetSlot
uint16_t checksum; // The sum of the previous 15 words
};
/**
* Class holding data for subsequent records in series of VSA0D extended message records
*/
class VSA0DConsecutive : public VSA0D {
public:
/**
* Constructor that parses first 32 bytes of VSA0D record
* @param bytes Bytestream to read VSA record data from
* @param first The first record in this series of VSA0D records
* @param isLastRecord Determines if this record is the last record in this series of extended message records
*/
VSA0DConsecutive(uint8_t* const bytes, uint32_t& runningChecksum, std::shared_ptr<VSA0DFirst> first, bool isLastRecord = false);
/**
* Determine whether to filter out this message record. Utilizes the filter from the first record.
*
* @param filter The filter to check this record against
*
* @return True if this record passes the filter
*/
bool filter(const std::shared_ptr<VSAMessageReadFilter> filter) override { return first ? first->filter(filter) : false; }
/**
* Get the timestamp of this record in 25 nanosecond ticks since January 1, 2007.
*
* @return Timestamp in 25 nanosecond ticks since January 1, 2007.
*/
uint64_t getTimestamp() override { return first ? first->getTimestamp() : UINT64_MAX; }
private:
/**
* Perform checksum on this record with the given bytestream
*
* @param bytes The bytestream to test the checksum against
*/
void doChecksum(uint8_t* bytes) override;
uint32_t recordChecksum; // The checksum for this extended record sequence found in the last record (not used if not last record)
uint32_t calculatedChecksum = 0; // Running checksum total for the extended record sequence of this record
std::shared_ptr<VSA0DFirst> first = nullptr; // The first record in this extended message record series
};
} // namespace icsneo
#endif //__cplusplus
#endif // __VSA0D_H__

View File

@ -0,0 +1,130 @@
#ifndef __VSA0E_H__
#define __VSA0E_H__
#ifdef __cplusplus
#include "icsneo/disk/vsa/vsa.h"
namespace icsneo {
/**
* Base class for VSA0E extended message record types
*/
class VSA0E : public VSAExtendedMessage {
public:
/**
* Constructor for VSA0E parent class. Passes most information to VSAExtendedMessage constructor.
*
* @param bytes Bytestream to read record data from
* @param messageBytes Bytestream starting at the payload of this message record
* @param numBytes The length of the payload of this message record
* @param runningChecksum Checksum for the payload bytes of this sequence of extended message records
* @param networkId The CoreMini Network ID for this record
*/
VSA0E(uint8_t* const bytes, uint8_t* const messageBytes, size_t numBytes, uint32_t& runningChecksum, Network::CoreMini networkId = static_cast<Network::CoreMini>(0xFFFFu));
};
/**
* Class holding data for the first record in a series of VSA0E extended message records
*/
class VSA0EFirst : public VSA0E {
public:
/**
* Constructor that parses first 32 bytes of bytestream into readable data.
*
* @param bytes Bytestream to parse VSA record from
*/
VSA0EFirst(uint8_t* const bytes, uint32_t& runningChecksum);
/**
* Reserve memory in the packet data vector to store message data from this record and subsequent consecutive records.
*
* @param packet The packet to reserve memory in
*/
void reservePacketData(std::shared_ptr<Packet>& packet) const override;
/**
* Determine whether to filter out this message record
*
* @param filter The filter to check this record against
*
* @return True if the record passes the filter
*/
bool filter(const std::shared_ptr<VSAMessageReadFilter> filter) override;
/**
* Get the timestamp of this record. Timestamp indicates number of 25 nanosecond ticks since January 1, 2007.
*
* @return The timestamp of this record
*/
uint64_t getTimestamp() override { return timestamp; }
/**
* Reorder bytes in the payload between this record and the second record in the sequence. The bytes are reordered to allow
* simple concatenation of payload bytes from records before creating and dispatching a packet.
*
* @param secondPayload Reference to the payload from the second record in the sequence
*/
void reorderPayload(std::vector<uint8_t>& secondPayload);
private:
/**
* Perform the checksum on this record
*
* @param bytes Bytestream to test against the checksum
*/
void doChecksum(uint8_t* bytes) override;
uint16_t captureBitfield; // The data capture this record is a part of
uint64_t timestamp; // Timestamp of this record in 25 nanosecond ticks since January 1, 2007
uint16_t checksum; // Sum of the previous 15 words
};
/**
* Class holding data for subsequent records in series of VSA0E extended message records
*/
class VSA0EConsecutive : public VSA0E {
public:
/**
* Constructor that parses first 32 bytes of VSA0E record
* @param bytes Bytestream to read VSA record data from
* @param first The first record in this series of VSA0E records
* @param isLastRecord Determines if this record is the last record in this series of extended message records
*/
VSA0EConsecutive(uint8_t* const bytes, uint32_t& runningChecksum, std::shared_ptr<VSA0EFirst> first, bool isLastRecord = false);
/**
* Determine whether to filter out this message record. Utilizes the filter from the first record.
*
* @param filter The filter to check this record against
*
* @return True if this record passes the filter
*/
bool filter(const std::shared_ptr<VSAMessageReadFilter> filter) override { return first ? first->filter(filter) : false; }
/**
* Get the timestamp of this record in 25 nanosecond ticks since January 1, 2007.
*
* @return Timestamp in 25 nanosecond ticks since January 1, 2007.
*/
uint64_t getTimestamp() override { return first ? first->getTimestamp() : UINT64_MAX; }
private:
/**
* Perform checksum on this record with the given bytestream
*
* @param bytes The bytestream to test the checksum against
*/
void doChecksum(uint8_t* bytes) override;
uint32_t recordChecksum; // The checksum for this extended record sequence found in the last record (not used if not last record)
uint32_t calculatedChecksum; // Running checksum total for the extended record sequence of this record
std::shared_ptr<VSA0EFirst> first = nullptr; // The first record in this series of extended message records
};
} // namespace icsneo
#endif //__cplusplus
#endif // __VSA0E_H__

View File

@ -0,0 +1,119 @@
#ifndef __VSA0F_H__
#define __VSA0F_H__
#ifdef __cplusplus
#include "icsneo/disk/vsa/vsa.h"
namespace icsneo {
/**
* Base class for VSA0F extended message record types
*/
class VSA0F : public VSAExtendedMessage {
public:
/**
* Standard constructor for VSA0F parent class. Passes most information to VSAExtendedMessage constructor.
*
* @param bytes Bytestream to read record data from
* @param messageBytes Bytestream starting at the payload of this message record
* @param numBytes The length of the payload of this message record
* @param runningChecksum Checksum for the payload bytes of this sequence of extended message records
* @param networkId The CoreMini Network ID for this record
*/
VSA0F(uint8_t* const bytes, uint8_t* const messageBytes, size_t numBytes, uint32_t& runningChecksum, Network::CoreMini networkId = static_cast<Network::CoreMini>(0xFFFFu));
};
/**
* Class holding data for the first record in a series of VSA0F extended message records
*/
class VSA0FFirst : public VSA0F {
public:
/**
* Constructor that parses first 32 bytes of bytestream into readable data.
*
* @param bytes Bytestream to parse VSA record from
*/
VSA0FFirst(uint8_t* const bytes, uint32_t& runningChecksum);
/**
* Reserve memory in the packet data vector to store message data from this record and subsequent consecutive records.
*
* @param packet The packet to reserve memory in
*/
void reservePacketData(std::shared_ptr<Packet>& packet) const override;
/**
* Determine whether to filter out this message record
*
* @param filter The filter to check this record against
*
* @return True if the record passes the filter
*/
bool filter(const std::shared_ptr<VSAMessageReadFilter> filter) override;
/**
* Get the timestamp of this record. Timestamp indicates number of 25 nanosecond ticks since January 1, 2007.
*
* @return The timestamp of this record
*/
uint64_t getTimestamp() override { return timestamp; }
private:
/**
* Perform checksum on this record with the given bytestream
*
* @param bytes The bytestream to test against the checksum
*/
void doChecksum(uint8_t* bytes) override;
uint16_t captureBitfield; // The data capture this record is a part of
uint64_t timestamp; // The timestamp of this record in 25 nanosecond ticks since January 1, 2007
uint16_t checksum; // The sum of the previous 9 words (Does not include message payload since payload bits follow the checksum)
};
/**
* Class holding data for subsequent records in series of VSA0D extended message records
*/
class VSA0FConsecutive : public VSA0F {
public:
/**
* Constructor that parses first 32 bytes of VSA0F record
* @param bytes Bytestream to read VSA record data from
* @param first The first record in this series of VSA0F records
* @param isLastRecord Determines if this record is the last record in this series of extended message records
*/
VSA0FConsecutive(uint8_t* const bytes, uint32_t& runningChecksum, std::shared_ptr<VSA0FFirst> first, bool isLastRecord = false);
/**
* Determine whether to filter out this message record. Utilizes the filter from the first record.
*
* @param filter The filter to check this record against
*
* @return True if this record passes the filter
*/
bool filter(const std::shared_ptr<VSAMessageReadFilter> filter) override { return first ? first->filter(filter) : false; }
/**
* Get the timestamp of this record in 25 nanosecond ticks since January 1, 2007.
*
* @return Timestamp in 25 nanosecond ticks since January 1, 2007.
*/
uint64_t getTimestamp() override { return first ? first->getTimestamp() : UINT64_MAX; }
private:
/**
* Perform checksum on this record with the given bytestream
*
* @param bytes The bytestream to test the checksum against
*/
void doChecksum(uint8_t* bytes) override;
uint32_t calculatedChecksum; // Running checksum total for the extended record sequence of this record
std::shared_ptr<VSA0FFirst> first = nullptr; // The first record in this series of extended message records
};
} // namespace icsneo
#endif //__cplusplus
#endif // __VSA0F_H__

View File

@ -0,0 +1,49 @@
#ifndef __VSA6A_H__
#define __VSA6A_H__
#ifdef __cplusplus
#include "icsneo/disk/vsa/vsa.h"
namespace icsneo {
/**
* Class that contains data for a Logger Configuration Backup Record
*/
class VSA6A : public VSA {
public:
/**
* Constructor to convert a bytestream to a Logger Configuration Backup Record
*
* @param bytes Bytestream to convert to Logger Configuration Backup Record
*/
VSA6A(uint8_t* const bytes);
/**
* Get the timestamp for this record in 25 nanosecond ticks since January 1, 2007
*
* @return The timestamp for this record in 25 nanosecond ticks since January 1, 2007
*/
uint64_t getTimestamp() override { return timestamp; }
private:
/**
* Perform the checksum on this record
*
* @param bytes Bytestream to test against the checksum
*/
void doChecksum(uint8_t* bytes) override;
uint32_t sequenceNum; // Unknown
uint32_t totalSectors; // Unknown
uint32_t reserved; // Unused bytes
uint64_t timestamp; // Timestamp of this record in 25 nanosecond ticks since January 1, 2007
uint16_t timestampSum; // Sum of the bytes in this record's timestamp (previous 8 bytes)
std::vector<uint8_t> data; // Payload data for this record (452 bytes)
uint32_t checksum; // Sum of the previous 452 bytes (bytes from data)
};
} // namespace icsneo
#endif // __cplusplus
#endif // __VSA6A_H__

View File

@ -0,0 +1,234 @@
#ifndef __VSAPARSER_H__
#define __VSAPARSER_H__
#ifdef __cplusplus
#include "icsneo/disk/vsa/vsa.h"
#include "icsneo/disk/vsa/vsa0d.h"
#include "icsneo/disk/vsa/vsa0e.h"
#include "icsneo/disk/vsa/vsa0f.h"
#include "icsneo/communication/message/message.h"
#include "icsneo/api/eventmanager.h"
#include <vector>
#include <array>
namespace icsneo {
/**
* Class used to parse VSA records from bytestreams
*/
class VSAParser {
public:
/**
* Struct that determines which types of VSA records to extract from disk
*/
struct Settings {
bool extractAA02 = true; // Extract Logdata Records
bool extractAA03 = true; // Extract Event Records
bool extractAA04 = true; // Extract Partition Info Records
bool extractAA05 = true; // Extract Application Error Records
bool extractAA06 = true; // Extract Debug/Internal 1 Records
bool extractAA07 = true; // Extract Debug/Internal 2 Records
bool extractAA08 = true; // Extract Buffer Info Records
bool extractAA09 = true; // Extract Device Info Records
bool extractAA0B = true; // Extract Message Records
bool extractAA0C = true; // Extract PCM Audio Records
bool extractAA0D = true; // Extract Extended Message 1 Records
bool extractAA0E = true; // Extract Extended Message 2 Records
bool extractAA0F = true; // Extract Extended Message 3 Records
bool extractAA6A = true; // Extract Logger Configuration Backup Records
std::shared_ptr<VSAMessageReadFilter> messageFilter = nullptr; // Used for post-read filtering of message records
/**
* Static constructor for VSAParser::Settings that only extracts message records (AA0B, AA0D, AA0E, AA0F)
*/
static Settings messageRecords() { return { false, false, false, false, false, false, false, false, true, false, true, true, true, false }; }
/**
* Operator overload for equivalency of VSAParser::Settings struct
*
* @param s The settings object to test against this settings object
*
* @return True if the extraction settings are the same. Does not check the filter
*/
bool operator==(const Settings& s)
{
return s.extractAA02 == this->extractAA02 && s.extractAA03 == this->extractAA03 && s.extractAA04 == this->extractAA04 &&
s.extractAA05 == this->extractAA05 && s.extractAA06 == this->extractAA06 && s.extractAA07 == this->extractAA07 &&
s.extractAA08 == this->extractAA08 && s.extractAA09 == this->extractAA09 && s.extractAA0B == this->extractAA0B &&
s.extractAA0C == this->extractAA0C && s.extractAA0D == this->extractAA0D && s.extractAA0E == this->extractAA0E &&
s.extractAA0F == this->extractAA0F && s.extractAA6A == this->extractAA6A;
}
/**
* Operator overload for non-equivalency of VSAParser::Settings struct
*
* @param s The settings object to test against this settings object
*
* @return True if the extraction settings are not the same. Does not check the filter
*/
bool operator!=(const Settings& s)
{
return !(*this == s);
}
};
/**
* Enumerated values to determine status of attempt to parse out-of-context record from bytestream
*/
enum class RecordParseStatus : uint8_t {
NotARecordStart, // Indicates first byte was not of format required for VSA records
Pad, // This record is a pad record
Deprecated, // This record is deprecated
ConsecutiveExtended, // This is a consecutive extended message record (i.e., not the first record in an extended message sequence)
FilteredOut, // This record was filtered out due to the current Settings of the VSAParser
UnknownRecordType, // The second byte indicates a record type that is unknown/not handled
InsufficientData, // There were not enough bytes given to the parse call
Success // The record was successfully parsed
};
/**
* Constructor with default settings
*
* @param report Handler to report APIEvents
*/
VSAParser(const device_eventhandler_t& report) { this->report = report; }
/**
* Constructor with non-default settings
*
* @param report Handler to report APIEvents
* @param settings The settings to use for this parser
*/
VSAParser(const device_eventhandler_t& report, const Settings& settings)
: settings(settings) { this->report = report; }
/**
* Parse the given bytestream into VSA records and store them in vsaRecords.
* Non-terminated extended message record sequences are stored in a temporary buffer until they terminate.
*
* @param bytes Bytestream to parse VSA records from
* @param arrLen The number of bytes in the bytestream
*
* @return True if there was no failure or unhandled behavior during parse, else false
*/
bool parseBytes(uint8_t* const bytes, uint64_t arrLen);
/**
* Get the last fully-parsed record in the parser
*/
std::shared_ptr<VSA>& back() { return vsaRecords.back(); }
/**
* Get the number of records contained within the parser
*
* @return Size of the vector of VSA records
*/
size_t size() { return vsaRecords.size(); }
/**
* Determine if number of records contained within the parser is 0
*
* @return True if the parser record container is empty
*/
bool empty() { return vsaRecords.empty(); }
/**
* Clear all fully-parsed records from the parser. Does not affect non-terminated extended message records stored in buffers.
*/
void clearRecords() { vsaRecords.clear(); }
/**
* Parse first record from the given bytestream.
*
* @param bytes The bytestream to read from
* @param arrLen Length of the bytestream
* @param record Variable to pass out the record if able to parse
*
* @return The status of the record parse
*/
RecordParseStatus getRecordFromBytes(uint8_t* const bytes, size_t arrLen, std::shared_ptr<VSA>& record);
/**
* Set a message filter for the Settings for this VSAParser
*
* @param filter The message filter to set for this VSAParser
*/
void setMessageFilter(const VSAMessageReadFilter& filter) { settings.messageFilter = std::make_shared<VSAMessageReadFilter>(filter); }
/**
* Remove the message filter for the Settings from this parser
*/
void clearMessageFilter() { settings.messageFilter = nullptr; }
/**
* Clear all extended message buffers and parse states
*/
void clearParseState();
/**
* Extract all packets from fully-parsed VSA records and store them in the given buffer
*
* @param packets The vector in which to store the packets from fully-parsed records
*
* @return True if packets were successfully extracted
*/
bool extractMessagePackets(std::vector<std::shared_ptr<Packet>>& packets);
private:
/**
* Holds the state of all possible extended message record sequences (most will be empty/null)
*/
struct ExtendedMessageState {
/**
* Holds the state of a single extended message record sequence
*/
struct ExtendedRecordSeqInfo {
/**
* Reset the state of this record sequence
*/
void clear()
{
nextIndex = 0;
totalRecordCount = 0;
runningChecksum = 0;
records.clear();
records.shrink_to_fit();
}
uint16_t nextIndex = 0; // The next index to be parsed in this sequence
uint32_t totalRecordCount = 0; // The total number of records that are in this sequence
uint32_t runningChecksum = 0; // The running calculated checksum for this sequence
std::vector<std::shared_ptr<VSAExtendedMessage>> records; // All of the records in this sequence
};
std::array<ExtendedRecordSeqInfo, 128> vsa0DSeqInfo; // Holds state for each possible sequence ID for VSA0D
std::array<ExtendedRecordSeqInfo, 256> vsa0ESeqInfo; // Holds state for each possible sequence ID for VSA0E
std::array<ExtendedRecordSeqInfo, 128> vsa0FSeqInfo; // Holds state for each possible sequence ID for VSA0F
};
/**
* Handle parsing of extended message records
*
* @param bytes The bytestream to parse the extended message record from
* @param bytesOffset The offset in the bytestream to read the record from
* @param type The type of VSA extended message record that we are parsing (AA0D, AA0E, AA0F)
*
* @return True if no unhandled failures to parse occurred
*/
bool handleExtendedRecord(uint8_t* const bytes, uint64_t& bytesOffset, VSA::Type type);
std::vector<std::shared_ptr<VSA>> vsaRecords; // The vector of records that this parser has parsed
bool hasDeprecatedRecords = false; // Indicates whether records of deprecated types are present in the disk
Settings settings; // The settings used to determine which records to save to records vector
ExtendedMessageState state; // The parse state of all possible extended message sequences
device_eventhandler_t report; // Event handler to report APIEvents
};
} // namespace icsneo
#endif // __cplusplus
#endif // __VSAPARSER_H__