Author SHA1 Message Date
Kyle Schwarz c056e8dc2e Driver: Servd: Toggle closing when done 2025-07-02 14:28:59 -04:00
Yasser YassineandKyle Schwarz d5ae1cdb03 Device: RED2: Update settings 2025-07-02 18:10:51 +00:00
Kyle Schwarz 5795791eac Driver: Servd: Fix Address creation 2025-06-19 16:48:15 -04:00
Ben KleinhekselandKyle Schwarz c91db6355c Device: Add setValueLiveData()
* Adds the ability to set a CoreMini signal value via the live data interface
* Adds definition for the Manual Trigger and DAQ Enable signals
2025-06-18 21:51:32 +00:00
14 changed files with 258 additions and 6 deletions
+6
View File
@@ -74,6 +74,8 @@ static constexpr const char* TIMEOUT = "The timeout was reached.";
static constexpr const char* WIVI_NOT_SUPPORTED = "Wireless neoVI functions are not supported on this device."; static constexpr const char* WIVI_NOT_SUPPORTED = "Wireless neoVI functions are not supported on this device.";
static constexpr const char* RESTRICTED_ENTRY_FLAG = "Attempted to set a restricted flag in a Root Directory entry."; static constexpr const char* RESTRICTED_ENTRY_FLAG = "Attempted to set a restricted flag in a Root Directory entry.";
static constexpr const char* NOT_SUPPORTED = "The requested feature is not supported."; static constexpr const char* NOT_SUPPORTED = "The requested feature is not supported.";
static constexpr const char* FIXED_POINT_OVERFLOW = "Value is too large to convert to fixed point.";
static constexpr const char* FIXED_POINT_PRECISION = "Value is too small for fixed point precision.";
// Device Errors // Device Errors
static constexpr const char* POLLING_MESSAGE_OVERFLOW = "Too many messages have been recieved for the polling message buffer, some have been lost!"; static constexpr const char* POLLING_MESSAGE_OVERFLOW = "Too many messages have been recieved for the polling message buffer, some have been lost!";
@@ -240,6 +242,10 @@ const char* APIEvent::DescriptionForType(Type type) {
return RESTRICTED_ENTRY_FLAG; return RESTRICTED_ENTRY_FLAG;
case Type::NotSupported: case Type::NotSupported:
return NOT_SUPPORTED; return NOT_SUPPORTED;
case Type::FixedPointOverflow:
return FIXED_POINT_OVERFLOW;
case Type::FixedPointPrecision:
return FIXED_POINT_PRECISION;
// Device Errors // Device Errors
case Type::PollingMessageOverflow: case Type::PollingMessageOverflow:
+3 -1
View File
@@ -142,7 +142,9 @@ void init_event(pybind11::module_& m) {
.value("VSAOtherError", APIEvent::Type::VSAOtherError) .value("VSAOtherError", APIEvent::Type::VSAOtherError)
.value("NoErrorFound", APIEvent::Type::NoErrorFound) .value("NoErrorFound", APIEvent::Type::NoErrorFound)
.value("TooManyEvents", APIEvent::Type::TooManyEvents) .value("TooManyEvents", APIEvent::Type::TooManyEvents)
.value("Unknown", APIEvent::Type::Unknown); .value("Unknown", APIEvent::Type::Unknown)
.value("FixedPointOverflow", APIEvent::Type::FixedPointOverflow)
.value("FixedPointPrecision", APIEvent::Type::FixedPointPrecision);
pybind11::enum_<APIEvent::Severity>(apiEvent, "Severity") pybind11::enum_<APIEvent::Severity>(apiEvent, "Severity")
.value("Any", APIEvent::Severity::Any) .value("Any", APIEvent::Severity::Any)
+57
View File
@@ -1,4 +1,5 @@
#include "icsneo/communication/livedata.h" #include "icsneo/communication/livedata.h"
#include <cmath>
namespace icsneo { namespace icsneo {
namespace LiveDataUtil { namespace LiveDataUtil {
@@ -18,5 +19,61 @@ double liveDataValueToDouble(const LiveDataValue& val) {
return val.value * liveDataFixedPointToDouble; return val.value * liveDataFixedPointToDouble;
} }
bool liveDataDoubleToValue(const double& dFloat, LiveDataValue& value) {
union {
struct
{
uint32_t ValueFractionPart;
int32_t ValueInt32;
} parts;
int64_t ValueLarge;
} CminiFixedPt;
constexpr double CM_FIXED_POINT_TO_DOUBLEVALUE = (1.0 / (double)(1ULL << 32)); // 2^-32
constexpr double CM_DOUBLEVALUE_TO_FIXED_POINT = ((double)(1ULL << 32)); // 2^32
// Use const for limits (C++98 compatible)
const double INT32_MAX_DOUBLE =
static_cast<double>(std::numeric_limits<int32_t>::max()) + (1.0 - std::numeric_limits<double>::epsilon());
const double INT32_MIN_DOUBLE = static_cast<double>(std::numeric_limits<int32_t>::min());
const double MIN_FIXED_POINT_DOUBLE = (double)(1ull * CM_FIXED_POINT_TO_DOUBLEVALUE);
// This needs to be assigned separately, otherwise, for dFloat >= 2^31,
// long double dBigFloat = dFloat * CM_DOUBLEVALUE_TO_FIXED_POINT overflows
// long long (value is >= 2^63) and so the assignment ValueLarge = dBigFloat is undefined
int32_t intPart; //creating temp variable due to static analysis warning about writing and reading to different union members
if(dFloat < 0.0)
intPart = (int32_t)std::floor(dFloat);
else
intPart = (int32_t)dFloat;
//using temp varialbes to avoid static analysis warning about read/write to different union members
double frac = dFloat - (double)(intPart);
uint32_t fracPart = (uint32_t)std::floor((frac * CM_DOUBLEVALUE_TO_FIXED_POINT) + 0.5);
//write temp vars back into the union
CminiFixedPt.parts.ValueInt32 = intPart;
CminiFixedPt.parts.ValueFractionPart = fracPart;
value.value = CminiFixedPt.ValueLarge;
if(dFloat == (double)0.0)
return true;
//check if double can be stored as 32.32
// 0x1 0000 0000 0000 0000 * CM_FIXED_POINT_TO_DOUBLEVALUE = 0x1 0000 0000
if(dFloat > INT32_MAX_DOUBLE || dFloat < INT32_MIN_DOUBLE) {
EventManager::GetInstance().add(APIEvent::Type::FixedPointOverflow, APIEvent::Severity::Error);
return false;
}
// Use absolute value for minimum fixed point check
double absFloat = (dFloat < 0.0) ? -dFloat : dFloat;
if(absFloat < MIN_FIXED_POINT_DOUBLE) {
EventManager::GetInstance().add(APIEvent::Type::FixedPointPrecision, APIEvent::Severity::Error);
return false;
}
return true;
}
} // namespace LiveDataUtil } // namespace LiveDataUtil
} // namespace icsneo } // namespace icsneo
+10
View File
@@ -12,4 +12,14 @@ void LiveDataCommandMessage::appendSignalArg(LiveDataValueType valueType) {
arg->valueType = valueType; arg->valueType = valueType;
} }
void LiveDataSetValueMessage::appendSetValue(LiveDataValueType valueType, const LiveDataValue& value) {
auto& arg = args.emplace_back(std::make_shared<LiveDataArgument>());
arg->objectType = LiveDataObjectType::MISC;
arg->objectIndex = 0u;
arg->signalIndex = 0u;
arg->valueType = valueType;
values.push_back(std::make_shared<LiveDataValue>(value));
}
} // namespace icsneo } // namespace icsneo
+27
View File
@@ -99,6 +99,33 @@ bool HardwareLiveDataPacket::EncodeFromMessage(LiveDataMessage& message, std::ve
clearMsg->cmd = static_cast<uint32_t>(message.cmd); clearMsg->cmd = static_cast<uint32_t>(message.cmd);
break; break;
} }
case LiveDataCommand::SET_VALUE: {
auto setValMsg = reinterpret_cast<LiveDataSetValueMessage*>(&message);
const auto numArgs = setValMsg->args.size();
if(numArgs) {
payloadSize = static_cast<uint16_t>(sizeof(LiveDataSetValue) + (sizeof(LiveDataSetValueEntry) * (numArgs-1)));
bytestream.resize((payloadSize + sizeof(ExtendedCommandHeader)),0);
LiveDataSetValue* out = reinterpret_cast<LiveDataSetValue*>(bytestream.data() + sizeof(ExtendedCommandHeader));
out->version = icsneo::LiveDataUtil::LiveDataVersion;
out->cmd = static_cast<uint32_t>(setValMsg->cmd);
if(!setValMsg->handle)
setValMsg->handle = LiveDataUtil::getNewHandle();
out->handle = setValMsg->handle;
out->numSetValues = (uint32_t)numArgs;
for(size_t i = 0; i < numArgs; ++i) {
out->values[i].arg.objectType = setValMsg->args[i]->objectType;
out->values[i].arg.objectIndex = setValMsg->args[i]->objectIndex;
out->values[i].arg.signalIndex = setValMsg->args[i]->signalIndex;
out->values[i].arg.valueType = setValMsg->args[i]->valueType;
out->values[i].value.value = setValMsg->values[i]->value;
out->values[i].value.header.length = sizeof(LiveDataValue::value);
}
} else {
report(APIEvent::Type::LiveDataInvalidArgument, APIEvent::Severity::Error);
return false;
}
break;
}
default: { default: {
report(APIEvent::Type::LiveDataInvalidCommand, APIEvent::Severity::Error); report(APIEvent::Type::LiveDataInvalidCommand, APIEvent::Severity::Error);
return false; return false;
+55
View File
@@ -2235,6 +2235,61 @@ bool Device::clearAllLiveData() {
return true; return true;
} }
bool Device::setValueLiveData(std::shared_ptr<LiveDataSetValueMessage> message) {
if(!supportsLiveData()) {
report(APIEvent::Type::LiveDataNotSupported, APIEvent::Severity::Error);
return false;
}
if(!isOpen()) {
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
return false;
}
if((message->args.size() != message->values.size()) || message->args.empty()) {
report(APIEvent::Type::LiveDataInvalidArgument, APIEvent::Severity::Error);
return false;
}
std::vector<uint8_t> bytes;
if(!com->encoder->encode(*com->packetizer, bytes, message)) {
report(APIEvent::Type::LiveDataEncoderError, APIEvent::Severity::Error);
return false;
}
std::shared_ptr<Message> response = com->waitForMessageSync(
[this, &bytes](){ return com->sendPacket(bytes); },
std::make_shared<MessageFilter>(Message::Type::LiveData));
if(response) {
auto statusMsg = std::dynamic_pointer_cast<LiveDataStatusMessage>(response);
if(statusMsg && statusMsg->requestedCommand == message->cmd) {
switch(statusMsg->status) {
case LiveDataStatus::SUCCESS:
return true;
case LiveDataStatus::ERR_DUPLICATE:
case LiveDataStatus::ERR_HANDLE:
{
report(APIEvent::Type::LiveDataInvalidHandle, APIEvent::Severity::Error);
return false;
}
case LiveDataStatus::ERR_FULL:
{
report(APIEvent::Type::LiveDataMaxSignalsReached, APIEvent::Severity::Error);
return false;
}
case LiveDataStatus::ERR_UNKNOWN_COMMAND:
{
report(APIEvent::Type::LiveDataCommandFailed, APIEvent::Severity::Error);
return false;
}
default:
break;
}
}
}
report(APIEvent::Type::LiveDataNoDeviceResponse, APIEvent::Severity::Error);
return false;
}
bool Device::readVSA(const VSAExtractionSettings& extractionSettings) { bool Device::readVSA(const VSAExtractionSettings& extractionSettings) {
if(isOnline()) { if(isOnline()) {
goOffline(); goOffline();
@@ -35,6 +35,8 @@ int main() {
msg->appendSignalArg(icsneo::LiveDataValueType::GPS_LATITUDE); msg->appendSignalArg(icsneo::LiveDataValueType::GPS_LATITUDE);
msg->appendSignalArg(icsneo::LiveDataValueType::GPS_LONGITUDE); msg->appendSignalArg(icsneo::LiveDataValueType::GPS_LONGITUDE);
msg->appendSignalArg(icsneo::LiveDataValueType::GPS_ACCURACY); msg->appendSignalArg(icsneo::LiveDataValueType::GPS_ACCURACY);
msg->appendSignalArg(icsneo::LiveDataValueType::DAQ_ENABLE);
msg->appendSignalArg(icsneo::LiveDataValueType::MANUAL_TRIGGER);
msg->cmd = icsneo::LiveDataCommand::SUBSCRIBE; msg->cmd = icsneo::LiveDataCommand::SUBSCRIBE;
msg->handle = icsneo::LiveDataUtil::getNewHandle(); msg->handle = icsneo::LiveDataUtil::getNewHandle();
msg->updatePeriod = std::chrono::milliseconds(100); msg->updatePeriod = std::chrono::milliseconds(100);
@@ -77,6 +79,26 @@ int main() {
})); }));
// Run handler for three seconds to observe the signal data // Run handler for three seconds to observe the signal data
std::this_thread::sleep_for(std::chrono::seconds(3)); std::this_thread::sleep_for(std::chrono::seconds(3));
double val = 0;
for (unsigned int i = 0; i < 10; ++i) {
// Set the values of signals we're watching so we can see them change live
auto setValMsg = std::make_shared<icsneo::LiveDataSetValueMessage>();
setValMsg->cmd = icsneo::LiveDataCommand::SET_VALUE;
setValMsg->handle = msg->handle;
// Convert the value format
icsneo::LiveDataValue ldValueDAQEnable;
icsneo::LiveDataValue ldValueManTrig;
if (!icsneo::LiveDataUtil::liveDataDoubleToValue(val / 3, ldValueDAQEnable) ||
!icsneo::LiveDataUtil::liveDataDoubleToValue(val, ldValueManTrig)) {
break;
}
setValMsg->appendSetValue(icsneo::LiveDataValueType::DAQ_ENABLE, ldValueDAQEnable);
setValMsg->appendSetValue(icsneo::LiveDataValueType::MANUAL_TRIGGER, ldValueManTrig);
device->setValueLiveData(setValMsg);
++val;
// Run handler for three seconds to observe the signal data
std::this_thread::sleep_for(std::chrono::seconds(3));
}
// Unsubscribe from the GPS signals and run handler for one more second // Unsubscribe from the GPS signals and run handler for one more second
// Unsubscription only requires a valid in-use handle, in this case from our previous subscription // Unsubscription only requires a valid in-use handle, in this case from our previous subscription
ret = device->unsubscribeLiveData(msg->handle); ret = device->unsubscribeLiveData(msg->handle);
+2
View File
@@ -51,6 +51,8 @@ public:
WiVINotSupported = 0x1015, WiVINotSupported = 0x1015,
RestrictedEntryFlag = 0x1016, RestrictedEntryFlag = 0x1016,
NotSupported = 0x1017, NotSupported = 0x1017,
FixedPointOverflow = 0x1018,
FixedPointPrecision = 0x1019,
// Device Events // Device Events
PollingMessageOverflow = 0x2000, PollingMessageOverflow = 0x2000,
+20
View File
@@ -19,6 +19,7 @@ enum class LiveDataCommand : uint32_t {
UNSUBSCRIBE, UNSUBSCRIBE,
RESPONSE, RESPONSE,
CLEAR_ALL, CLEAR_ALL,
SET_VALUE,
}; };
enum class LiveDataStatus : uint32_t { enum class LiveDataStatus : uint32_t {
@@ -41,10 +42,13 @@ enum class LiveDataValueType : uint32_t {
GPS_SPEED, GPS_SPEED,
GPS_VALID, GPS_VALID,
GPS_ENABLE = 62, GPS_ENABLE = 62,
MANUAL_TRIGGER = 108,
TIME_SINCE_MSG = 111,
GPS_ACCURACY = 120, GPS_ACCURACY = 120,
GPS_BEARING = 121, GPS_BEARING = 121,
GPS_TIME = 122, GPS_TIME = 122,
GPS_TIME_VALID = 123, GPS_TIME_VALID = 123,
DAQ_ENABLE = 124,
}; };
inline std::ostream& operator<<(std::ostream& os, const LiveDataCommand cmd) { inline std::ostream& operator<<(std::ostream& os, const LiveDataCommand cmd) {
@@ -54,6 +58,7 @@ inline std::ostream& operator<<(std::ostream& os, const LiveDataCommand cmd) {
case LiveDataCommand::UNSUBSCRIBE: return os << "Unsubscribe"; case LiveDataCommand::UNSUBSCRIBE: return os << "Unsubscribe";
case LiveDataCommand::RESPONSE: return os << "Response"; case LiveDataCommand::RESPONSE: return os << "Response";
case LiveDataCommand::CLEAR_ALL: return os << "Clear All"; case LiveDataCommand::CLEAR_ALL: return os << "Clear All";
case LiveDataCommand::SET_VALUE: return os << "Set Value";
} }
return os; return os;
} }
@@ -81,6 +86,9 @@ inline std::ostream& operator<<(std::ostream& os, const LiveDataValueType cmd) {
case LiveDataValueType::GPS_BEARING: return os << "GPS Bearing"; case LiveDataValueType::GPS_BEARING: return os << "GPS Bearing";
case LiveDataValueType::GPS_TIME: return os << "GPS Time"; case LiveDataValueType::GPS_TIME: return os << "GPS Time";
case LiveDataValueType::GPS_TIME_VALID: return os << "GPS Time Valid"; case LiveDataValueType::GPS_TIME_VALID: return os << "GPS Time Valid";
case LiveDataValueType::DAQ_ENABLE: return os << "DAQ Enable";
case LiveDataValueType::MANUAL_TRIGGER: return os << "Manual Trigger";
case LiveDataValueType::TIME_SINCE_MSG: return os << "Time Since Msg";
} }
return os; return os;
} }
@@ -127,6 +135,17 @@ struct LiveDataSubscribe : public LiveDataHeader {
LiveDataArgument args[1]; LiveDataArgument args[1];
}; };
struct LiveDataSetValueEntry
{
LiveDataArgument arg;
LiveDataValue value;
};
struct LiveDataSetValue : public LiveDataHeader {
uint32_t numSetValues;
LiveDataSetValueEntry values[1];
};
struct ExtResponseHeader { struct ExtResponseHeader {
ExtendedCommand command; ExtendedCommand command;
uint16_t length; uint16_t length;
@@ -138,6 +157,7 @@ namespace LiveDataUtil
LiveDataHandle getNewHandle(); LiveDataHandle getNewHandle();
double liveDataValueToDouble(const LiveDataValue& val); double liveDataValueToDouble(const LiveDataValue& val);
bool liveDataDoubleToValue(const double& dFloat, LiveDataValue& value);
static constexpr uint32_t LiveDataVersion = 1; static constexpr uint32_t LiveDataVersion = 1;
} // namespace LiveDataUtil } // namespace LiveDataUtil
@@ -38,6 +38,14 @@ public:
LiveDataStatus status; LiveDataStatus status;
}; };
class LiveDataSetValueMessage : public LiveDataMessage {
public:
LiveDataSetValueMessage() {}
std::vector<std::shared_ptr<LiveDataArgument>> args;
std::vector<std::shared_ptr<LiveDataValue>> values;
void appendSetValue(LiveDataValueType valueType, const LiveDataValue& value);
};
} // namespace icsneo } // namespace icsneo
#endif // __cplusplus #endif // __cplusplus
+1
View File
@@ -636,6 +636,7 @@ public:
bool subscribeLiveData(std::shared_ptr<LiveDataCommandMessage> message); bool subscribeLiveData(std::shared_ptr<LiveDataCommandMessage> message);
bool unsubscribeLiveData(const LiveDataHandle& handle); bool unsubscribeLiveData(const LiveDataHandle& handle);
bool clearAllLiveData(); bool clearAllLiveData();
bool setValueLiveData(std::shared_ptr<LiveDataSetValueMessage> message);
// VSA Read functions // VSA Read functions
+31
View File
@@ -668,6 +668,37 @@ typedef struct
} Fire3LinuxSettings; } Fire3LinuxSettings;
#define FIRE3LINUXSETTINGS_SIZE 8 #define FIRE3LINUXSETTINGS_SIZE 8
static_assert(sizeof(Fire3LinuxSettings) == FIRE3LINUXSETTINGS_SIZE, "Fire3LinuxSettings is the wrong size!"); static_assert(sizeof(Fire3LinuxSettings) == FIRE3LINUXSETTINGS_SIZE, "Fire3LinuxSettings is the wrong size!");
/* Define number of CMP streams per device*/
#define CMP_STREAMS_FIRE3 (10)
#define CMP_STREAMS_FIRE3FR (10)
#define CMP_STREAMS_RED2 (10)
#define CMP_STREAMS_A2B (3)
#define CMP_STREAMS_GIGASTAR (10)
/* CMP Network Enables */
typedef struct
{
uint8_t bStreamEnabled : 1;
uint8_t EthModule : 2;
uint8_t bControlEnabled : 1;
uint8_t spare : 4;
uint8_t streamId;
uint8_t dstMac[6];
uint64_t network_enables_1;
uint64_t network_enables_2;
} CMP_NETWORK_DATA;
/* Global CMP Data */
typedef struct
{
uint8_t cmp_enabled : 1;
uint8_t sparebits : 7;
uint8_t spare;
uint16_t cmp_device_id;
} CMP_GLOBAL_DATA;
#pragma pack(pop) #pragma pack(pop)
#ifdef __cplusplus #ifdef __cplusplus
@@ -79,7 +79,15 @@ typedef struct {
uint16_t digitalIoThresholdEnable; uint16_t digitalIoThresholdEnable;
uint16_t misc_io_initial_ddr; uint16_t misc_io_initial_ddr;
uint16_t misc_io_initial_latch; uint16_t misc_io_initial_latch;
ETHERNET_SETTINGS2 ethernet2; ETHERNET_SETTINGS2 ethernet2_1;
ETHERNET_SETTINGS ethernet_2;
ETHERNET_SETTINGS2 ethernet2_2;
Fire3LinuxSettings os_settings;
RAD_GPTP_SETTINGS gPTP;
uint16_t iso_tester_pullup_enable;
CMP_GLOBAL_DATA cmp_global_data;
CMP_NETWORK_DATA cmp_stream_data[CMP_STREAMS_RED2];
uint32_t networkTimeSync;
} neovired2_settings_t; } neovired2_settings_t;
typedef struct { typedef struct {
+7 -4
View File
@@ -1,5 +1,7 @@
#include "icsneo/platform/servd.h" #include "icsneo/platform/servd.h"
#include <string_view>
using namespace icsneo; using namespace icsneo;
#define SERVD_VERSION 1 #define SERVD_VERSION 1
@@ -12,9 +14,9 @@ bool Servd::Enabled() {
return enabled ? enabled[0] == '1' : false; return enabled ? enabled[0] == '1' : false;
} }
std::vector<std::string_view> split(const std::string_view& str, char delim = ' ') std::vector<std::string> split(const std::string_view& str, char delim = ' ')
{ {
std::vector<std::string_view> ret; std::vector<std::string> ret;
size_t tail = 0; size_t tail = 0;
size_t head = 0; size_t head = 0;
while (head < str.size()) { while (head < str.size()) {
@@ -114,8 +116,8 @@ bool Servd::open() {
return false; return false;
} }
aliveThread = std::thread(&Servd::alive, this); aliveThread = std::thread(&Servd::alive, this);
readThread = std::thread(&Servd::read, this, Address{tokens[2].data(), (uint16_t)std::stol(tokens[3].data())}); readThread = std::thread(&Servd::read, this, Address{tokens[2].c_str(), (uint16_t)std::stol(tokens[3].c_str())});
writeThread = std::thread(&Servd::write, this, Address{tokens[0].data(), (uint16_t)std::stol(tokens[1].data())}); writeThread = std::thread(&Servd::write, this, Address{tokens[0].c_str(), (uint16_t)std::stol(tokens[1].c_str())});
opened = true; opened = true;
return true; return true;
} }
@@ -140,6 +142,7 @@ bool Servd::close() {
messageSocket.sendto(request.data(), request.size(), SERVD_ADDRESS); messageSocket.sendto(request.data(), request.size(), SERVD_ADDRESS);
} }
opened = false; opened = false;
setIsClosing(false);
return true; return true;
} }