313 lines
11 KiB
C++
313 lines
11 KiB
C++
#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:
|
|
virtual ~VSA() = default;
|
|
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__
|