Bindings: Add LiveData and LiveDataMessage support in Python bindings
parent
000036f745
commit
977677e3af
|
|
@ -22,6 +22,7 @@ pybind11_add_module(icsneopy
|
||||||
icsneopy/device/devicetype.cpp
|
icsneopy/device/devicetype.cpp
|
||||||
icsneopy/communication/network.cpp
|
icsneopy/communication/network.cpp
|
||||||
icsneopy/communication/io.cpp
|
icsneopy/communication/io.cpp
|
||||||
|
icsneopy/communication/livedata.cpp
|
||||||
icsneopy/communication/message/message.cpp
|
icsneopy/communication/message/message.cpp
|
||||||
icsneopy/communication/message/canmessage.cpp
|
icsneopy/communication/message/canmessage.cpp
|
||||||
icsneopy/communication/message/canerrormessage.cpp
|
icsneopy/communication/message/canerrormessage.cpp
|
||||||
|
|
@ -34,6 +35,7 @@ pybind11_add_module(icsneopy
|
||||||
icsneopy/communication/message/spimessage.cpp
|
icsneopy/communication/message/spimessage.cpp
|
||||||
icsneopy/communication/message/scriptstatusmessage.cpp
|
icsneopy/communication/message/scriptstatusmessage.cpp
|
||||||
icsneopy/communication/message/ethphymessage.cpp
|
icsneopy/communication/message/ethphymessage.cpp
|
||||||
|
icsneopy/communication/message/livedatamessage.cpp
|
||||||
icsneopy/communication/message/callback/messagecallback.cpp
|
icsneopy/communication/message/callback/messagecallback.cpp
|
||||||
icsneopy/communication/message/filter/messagefilter.cpp
|
icsneopy/communication/message/filter/messagefilter.cpp
|
||||||
icsneopy/core/macseccfg.cpp
|
icsneopy/core/macseccfg.cpp
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
#include <pybind11/pybind11.h>
|
||||||
|
#include <pybind11/stl.h>
|
||||||
|
#include <pybind11/functional.h>
|
||||||
|
#include <pybind11/native_enum.h>
|
||||||
|
|
||||||
|
#include "icsneo/communication/livedata.h"
|
||||||
|
|
||||||
|
namespace icsneo {
|
||||||
|
|
||||||
|
void init_livedata(pybind11::module_& m) {
|
||||||
|
// LiveDataValue struct
|
||||||
|
pybind11::classh<LiveDataValue>(m, "LiveDataValue")
|
||||||
|
.def(pybind11::init<>())
|
||||||
|
.def_readwrite("value", &LiveDataValue::value);
|
||||||
|
|
||||||
|
// LiveDataArgument struct
|
||||||
|
pybind11::classh<LiveDataArgument>(m, "LiveDataArgument")
|
||||||
|
.def(pybind11::init<>())
|
||||||
|
.def_readwrite("object_type", &LiveDataArgument::objectType)
|
||||||
|
.def_readwrite("object_index", &LiveDataArgument::objectIndex)
|
||||||
|
.def_readwrite("signal_index", &LiveDataArgument::signalIndex)
|
||||||
|
.def_readwrite("value_type", &LiveDataArgument::valueType);
|
||||||
|
|
||||||
|
// LiveDataCommand enum
|
||||||
|
pybind11::native_enum<LiveDataCommand>(m, "LiveDataCommand", "enum.IntEnum")
|
||||||
|
.value("STATUS", LiveDataCommand::STATUS)
|
||||||
|
.value("SUBSCRIBE", LiveDataCommand::SUBSCRIBE)
|
||||||
|
.value("UNSUBSCRIBE", LiveDataCommand::UNSUBSCRIBE)
|
||||||
|
.value("RESPONSE", LiveDataCommand::RESPONSE)
|
||||||
|
.value("CLEAR_ALL", LiveDataCommand::CLEAR_ALL)
|
||||||
|
.value("SET_VALUE", LiveDataCommand::SET_VALUE)
|
||||||
|
.finalize();
|
||||||
|
|
||||||
|
// LiveDataStatus enum
|
||||||
|
pybind11::native_enum<LiveDataStatus>(m, "LiveDataStatus", "enum.IntEnum")
|
||||||
|
.value("SUCCESS", LiveDataStatus::SUCCESS)
|
||||||
|
.value("ERR_UNKNOWN_COMMAND", LiveDataStatus::ERR_UNKNOWN_COMMAND)
|
||||||
|
.value("ERR_HANDLE", LiveDataStatus::ERR_HANDLE)
|
||||||
|
.value("ERR_DUPLICATE", LiveDataStatus::ERR_DUPLICATE)
|
||||||
|
.value("ERR_FULL", LiveDataStatus::ERR_FULL)
|
||||||
|
.finalize();
|
||||||
|
|
||||||
|
// LiveDataObjectType enum
|
||||||
|
pybind11::enum_<LiveDataObjectType>(m, "LiveDataObjectType")
|
||||||
|
.value("MISC", LiveDataObjectType::MISC)
|
||||||
|
.value("SNA", LiveDataObjectType::SNA)
|
||||||
|
.export_values();
|
||||||
|
|
||||||
|
// LiveDataValueType enum
|
||||||
|
pybind11::native_enum<LiveDataValueType>(m, "LiveDataValueType", "enum.IntEnum")
|
||||||
|
.value("GPS_LATITUDE", LiveDataValueType::GPS_LATITUDE)
|
||||||
|
.value("GPS_LONGITUDE", LiveDataValueType::GPS_LONGITUDE)
|
||||||
|
.value("GPS_ALTITUDE", LiveDataValueType::GPS_ALTITUDE)
|
||||||
|
.value("GPS_SPEED", LiveDataValueType::GPS_SPEED)
|
||||||
|
.value("GPS_VALID", LiveDataValueType::GPS_VALID)
|
||||||
|
.value("GPS_ENABLE", LiveDataValueType::GPS_ENABLE)
|
||||||
|
.value("MANUAL_TRIGGER", LiveDataValueType::MANUAL_TRIGGER)
|
||||||
|
.value("TIME_SINCE_MSG", LiveDataValueType::TIME_SINCE_MSG)
|
||||||
|
.value("GPS_ACCURACY", LiveDataValueType::GPS_ACCURACY)
|
||||||
|
.value("GPS_BEARING", LiveDataValueType::GPS_BEARING)
|
||||||
|
.value("GPS_TIME", LiveDataValueType::GPS_TIME)
|
||||||
|
.value("GPS_TIME_VALID", LiveDataValueType::GPS_TIME_VALID)
|
||||||
|
.value("DAQ_ENABLE", LiveDataValueType::DAQ_ENABLE)
|
||||||
|
.finalize();
|
||||||
|
|
||||||
|
// LiveDataUtil namespace functions
|
||||||
|
m.def("get_new_handle", &LiveDataUtil::getNewHandle,
|
||||||
|
"Generate a new unique LiveData handle");
|
||||||
|
|
||||||
|
m.def("livedata_value_to_double", &LiveDataUtil::liveDataValueToDouble,
|
||||||
|
pybind11::arg("val"),
|
||||||
|
"Convert LiveDataValue to double (32.32 fixed-point to floating-point)");
|
||||||
|
|
||||||
|
m.def("livedata_double_to_value", &LiveDataUtil::liveDataDoubleToValue,
|
||||||
|
pybind11::arg("d"),
|
||||||
|
"Convert double to LiveDataValue (32.32 fixed-point format). Returns LiveDataValue or None on failure.");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace icsneo
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
#include <pybind11/pybind11.h>
|
||||||
|
#include <pybind11/stl.h>
|
||||||
|
#include <pybind11/functional.h>
|
||||||
|
#include <pybind11/chrono.h>
|
||||||
|
|
||||||
|
#include "icsneo/communication/message/livedatamessage.h"
|
||||||
|
|
||||||
|
namespace icsneo {
|
||||||
|
|
||||||
|
void init_livedatamessage(pybind11::module_& m) {
|
||||||
|
// LiveDataMessage base class
|
||||||
|
pybind11::classh<LiveDataMessage, RawMessage>(m, "LiveDataMessage")
|
||||||
|
.def(pybind11::init<>())
|
||||||
|
.def_readwrite("handle", &LiveDataMessage::handle)
|
||||||
|
.def_readwrite("cmd", &LiveDataMessage::cmd);
|
||||||
|
|
||||||
|
// LiveDataCommandMessage (for subscribe/unsubscribe)
|
||||||
|
pybind11::classh<LiveDataCommandMessage, LiveDataMessage>(m, "LiveDataCommandMessage")
|
||||||
|
.def(pybind11::init<>())
|
||||||
|
.def_readwrite("update_period", &LiveDataCommandMessage::updatePeriod)
|
||||||
|
.def_readwrite("expiration_time", &LiveDataCommandMessage::expirationTime)
|
||||||
|
.def_readwrite("args", &LiveDataCommandMessage::args)
|
||||||
|
.def("append_signal_arg", &LiveDataCommandMessage::appendSignalArg,
|
||||||
|
pybind11::arg("value_type"),
|
||||||
|
"Append a signal argument to the command message");
|
||||||
|
|
||||||
|
// LiveDataValueMessage (received values)
|
||||||
|
pybind11::classh<LiveDataValueMessage, LiveDataMessage>(m, "LiveDataValueMessage")
|
||||||
|
.def(pybind11::init<>())
|
||||||
|
.def_readwrite("num_args", &LiveDataValueMessage::numArgs)
|
||||||
|
.def_readwrite("values", &LiveDataValueMessage::values);
|
||||||
|
|
||||||
|
// LiveDataStatusMessage (status responses)
|
||||||
|
pybind11::classh<LiveDataStatusMessage, LiveDataMessage>(m, "LiveDataStatusMessage")
|
||||||
|
.def(pybind11::init<>())
|
||||||
|
.def_readwrite("requested_command", &LiveDataStatusMessage::requestedCommand)
|
||||||
|
.def_readwrite("status", &LiveDataStatusMessage::status);
|
||||||
|
|
||||||
|
// LiveDataSetValueMessage (for setting values)
|
||||||
|
pybind11::classh<LiveDataSetValueMessage, LiveDataMessage>(m, "LiveDataSetValueMessage")
|
||||||
|
.def(pybind11::init<>())
|
||||||
|
.def_readwrite("args", &LiveDataSetValueMessage::args)
|
||||||
|
.def_readwrite("values", &LiveDataSetValueMessage::values)
|
||||||
|
.def("append_set_value", &LiveDataSetValueMessage::appendSetValue,
|
||||||
|
pybind11::arg("value_type"),
|
||||||
|
pybind11::arg("value"),
|
||||||
|
"Append a value to set in the message");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace icsneo
|
||||||
|
|
@ -52,6 +52,11 @@ void init_device(pybind11::module_& m) {
|
||||||
.def("start_script", &Device::startScript, pybind11::call_guard<pybind11::gil_scoped_release>())
|
.def("start_script", &Device::startScript, pybind11::call_guard<pybind11::gil_scoped_release>())
|
||||||
.def("stop_script", &Device::stopScript, pybind11::call_guard<pybind11::gil_scoped_release>())
|
.def("stop_script", &Device::stopScript, pybind11::call_guard<pybind11::gil_scoped_release>())
|
||||||
.def("supports_tc10", &Device::supportsTC10)
|
.def("supports_tc10", &Device::supportsTC10)
|
||||||
|
.def("supports_live_data", &Device::supportsLiveData)
|
||||||
|
.def("subscribe_live_data", &Device::subscribeLiveData, pybind11::arg("message"), pybind11::call_guard<pybind11::gil_scoped_release>())
|
||||||
|
.def("unsubscribe_live_data", &Device::unsubscribeLiveData, pybind11::arg("handle"), pybind11::call_guard<pybind11::gil_scoped_release>())
|
||||||
|
.def("clear_all_live_data", &Device::clearAllLiveData, pybind11::call_guard<pybind11::gil_scoped_release>())
|
||||||
|
.def("set_value_live_data", &Device::setValueLiveData, pybind11::arg("message"), pybind11::call_guard<pybind11::gil_scoped_release>())
|
||||||
.def("transmit", pybind11::overload_cast<std::shared_ptr<Frame>>(&Device::transmit), pybind11::call_guard<pybind11::gil_scoped_release>())
|
.def("transmit", pybind11::overload_cast<std::shared_ptr<Frame>>(&Device::transmit), pybind11::call_guard<pybind11::gil_scoped_release>())
|
||||||
.def("upload_coremini", [](Device& device, std::string& path, Disk::MemoryType memType) { std::ifstream ifs(path, std::ios::binary); return device.uploadCoremini(ifs, memType); }, pybind11::call_guard<pybind11::gil_scoped_release>())
|
.def("upload_coremini", [](Device& device, std::string& path, Disk::MemoryType memType) { std::ifstream ifs(path, std::ios::binary); return device.uploadCoremini(ifs, memType); }, pybind11::call_guard<pybind11::gil_scoped_release>())
|
||||||
.def("write_macsec_config", &Device::writeMACsecConfig, pybind11::call_guard<pybind11::gil_scoped_release>())
|
.def("write_macsec_config", &Device::writeMACsecConfig, pybind11::call_guard<pybind11::gil_scoped_release>())
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,8 @@ void init_version(pybind11::module_&);
|
||||||
void init_flexray(pybind11::module_& m);
|
void init_flexray(pybind11::module_& m);
|
||||||
void init_idevicesettings(pybind11::module_&);
|
void init_idevicesettings(pybind11::module_&);
|
||||||
void init_ethphymessage(pybind11::module_&);
|
void init_ethphymessage(pybind11::module_&);
|
||||||
|
void init_livedata(pybind11::module_&);
|
||||||
|
void init_livedatamessage(pybind11::module_&);
|
||||||
|
|
||||||
PYBIND11_MODULE(icsneopy, m) {
|
PYBIND11_MODULE(icsneopy, m) {
|
||||||
pybind11::options options;
|
pybind11::options options;
|
||||||
|
|
@ -48,6 +50,7 @@ PYBIND11_MODULE(icsneopy, m) {
|
||||||
init_devicetype(m);
|
init_devicetype(m);
|
||||||
init_network(m);
|
init_network(m);
|
||||||
init_io(m);
|
init_io(m);
|
||||||
|
init_livedata(m);
|
||||||
init_message(m);
|
init_message(m);
|
||||||
init_canmessage(m);
|
init_canmessage(m);
|
||||||
init_canerrormessage(m);
|
init_canerrormessage(m);
|
||||||
|
|
@ -60,6 +63,7 @@ PYBIND11_MODULE(icsneopy, m) {
|
||||||
init_macsecconfig(m);
|
init_macsecconfig(m);
|
||||||
init_scriptstatusmessage(m);
|
init_scriptstatusmessage(m);
|
||||||
init_spimessage(m);
|
init_spimessage(m);
|
||||||
|
init_livedatamessage(m);
|
||||||
init_messagefilter(m);
|
init_messagefilter(m);
|
||||||
init_messagecallback(m);
|
init_messagecallback(m);
|
||||||
init_diskdriver(m);
|
init_diskdriver(m);
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,8 @@ double liveDataValueToDouble(const LiveDataValue& val) {
|
||||||
return val.value * liveDataFixedPointToDouble;
|
return val.value * liveDataFixedPointToDouble;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool liveDataDoubleToValue(const double& dFloat, LiveDataValue& value) {
|
std::optional<LiveDataValue> liveDataDoubleToValue(const double& dFloat) {
|
||||||
|
LiveDataValue value;
|
||||||
union {
|
union {
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
|
|
@ -56,23 +57,23 @@ bool liveDataDoubleToValue(const double& dFloat, LiveDataValue& value) {
|
||||||
value.value = CminiFixedPt.ValueLarge;
|
value.value = CminiFixedPt.ValueLarge;
|
||||||
|
|
||||||
if(dFloat == (double)0.0)
|
if(dFloat == (double)0.0)
|
||||||
return true;
|
return value;
|
||||||
|
|
||||||
//check if double can be stored as 32.32
|
//check if double can be stored as 32.32
|
||||||
// 0x1 0000 0000 0000 0000 * CM_FIXED_POINT_TO_DOUBLEVALUE = 0x1 0000 0000
|
// 0x1 0000 0000 0000 0000 * CM_FIXED_POINT_TO_DOUBLEVALUE = 0x1 0000 0000
|
||||||
if(dFloat > INT32_MAX_DOUBLE || dFloat < INT32_MIN_DOUBLE) {
|
if(dFloat > INT32_MAX_DOUBLE || dFloat < INT32_MIN_DOUBLE) {
|
||||||
EventManager::GetInstance().add(APIEvent::Type::FixedPointOverflow, APIEvent::Severity::Error);
|
EventManager::GetInstance().add(APIEvent::Type::FixedPointOverflow, APIEvent::Severity::Error);
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use absolute value for minimum fixed point check
|
// Use absolute value for minimum fixed point check
|
||||||
double absFloat = (dFloat < 0.0) ? -dFloat : dFloat;
|
double absFloat = (dFloat < 0.0) ? -dFloat : dFloat;
|
||||||
if(absFloat < MIN_FIXED_POINT_DOUBLE) {
|
if(absFloat < MIN_FIXED_POINT_DOUBLE) {
|
||||||
EventManager::GetInstance().add(APIEvent::Type::FixedPointPrecision, APIEvent::Severity::Error);
|
EventManager::GetInstance().add(APIEvent::Type::FixedPointPrecision, APIEvent::Severity::Error);
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace LiveDataUtil
|
} // namespace LiveDataUtil
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,15 @@ Complete CAN Example
|
||||||
:language: python
|
:language: python
|
||||||
|
|
||||||
|
|
||||||
|
LiveData Subscription and Monitoring
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
:download:`Download example <../../examples/python/livedata/livedata_example.py>`
|
||||||
|
|
||||||
|
.. literalinclude:: ../../examples/python/livedata/livedata_example.py
|
||||||
|
:language: python
|
||||||
|
|
||||||
|
|
||||||
Transmit Ethernet frames on Ethernet 01
|
Transmit Ethernet frames on Ethernet 01
|
||||||
========================================
|
========================================
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ int main() {
|
||||||
}
|
}
|
||||||
std::cout << "OK" << std::endl;
|
std::cout << "OK" << std::endl;
|
||||||
|
|
||||||
// Create a subscription message for the GPS signals
|
// Create a subscription message for the GPS signals and TIME_SINCE_MSG
|
||||||
std::cout << "\tSending a live data subscribe command... ";
|
std::cout << "\tSending a live data subscribe command... ";
|
||||||
auto msg = std::make_shared<icsneo::LiveDataCommandMessage>();
|
auto msg = std::make_shared<icsneo::LiveDataCommandMessage>();
|
||||||
msg->appendSignalArg(icsneo::LiveDataValueType::GPS_LATITUDE);
|
msg->appendSignalArg(icsneo::LiveDataValueType::GPS_LATITUDE);
|
||||||
|
|
@ -37,6 +37,7 @@ int main() {
|
||||||
msg->appendSignalArg(icsneo::LiveDataValueType::GPS_ACCURACY);
|
msg->appendSignalArg(icsneo::LiveDataValueType::GPS_ACCURACY);
|
||||||
msg->appendSignalArg(icsneo::LiveDataValueType::DAQ_ENABLE);
|
msg->appendSignalArg(icsneo::LiveDataValueType::DAQ_ENABLE);
|
||||||
msg->appendSignalArg(icsneo::LiveDataValueType::MANUAL_TRIGGER);
|
msg->appendSignalArg(icsneo::LiveDataValueType::MANUAL_TRIGGER);
|
||||||
|
msg->appendSignalArg(icsneo::LiveDataValueType::TIME_SINCE_MSG);
|
||||||
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);
|
||||||
|
|
@ -44,6 +45,9 @@ int main() {
|
||||||
// Transmit the subscription message
|
// Transmit the subscription message
|
||||||
ret = device->subscribeLiveData(msg);
|
ret = device->subscribeLiveData(msg);
|
||||||
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||||
|
if (!ret) {
|
||||||
|
std::cout << "\t\tError: " << icsneo::GetLastError() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
// Register a handler that uses the data after it arrives every ~100ms
|
// Register a handler that uses the data after it arrives every ~100ms
|
||||||
std::cout << "\tStreaming messages for 3 seconds... " << std::endl << std::endl;
|
std::cout << "\tStreaming messages for 3 seconds... " << std::endl << std::endl;
|
||||||
|
|
@ -53,19 +57,21 @@ int main() {
|
||||||
switch(ldMsg->cmd) {
|
switch(ldMsg->cmd) {
|
||||||
case icsneo::LiveDataCommand::STATUS: {
|
case icsneo::LiveDataCommand::STATUS: {
|
||||||
auto msg2 = std::dynamic_pointer_cast<icsneo::LiveDataStatusMessage>(message);
|
auto msg2 = std::dynamic_pointer_cast<icsneo::LiveDataStatusMessage>(message);
|
||||||
std::cout << "[Handle] " << ldMsg->handle << std::endl;
|
std::cout << "[STATUS Message]" << std::endl;
|
||||||
std::cout << "[Requested Command] " << msg2->requestedCommand << std::endl;
|
std::cout << " Handle: " << ldMsg->handle << std::endl;
|
||||||
std::cout << "[Status] " << msg2->status << std::endl << std::endl;
|
std::cout << " Requested Command: " << msg2->requestedCommand << std::endl;
|
||||||
|
std::cout << " Status: " << msg2->status << std::endl << std::endl;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case icsneo::LiveDataCommand::RESPONSE: {
|
case icsneo::LiveDataCommand::RESPONSE: {
|
||||||
auto valueMsg = std::dynamic_pointer_cast<icsneo::LiveDataValueMessage>(message);
|
auto valueMsg = std::dynamic_pointer_cast<icsneo::LiveDataValueMessage>(message);
|
||||||
if((valueMsg->handle == msg->handle) && (valueMsg->values.size() == msg->args.size()))
|
if((valueMsg->handle == msg->handle) && (valueMsg->values.size() == msg->args.size()))
|
||||||
{
|
{
|
||||||
std::cout << "[Handle] " << msg->handle << std::endl;
|
std::cout << "[Response Message]" << std::endl;
|
||||||
std::cout << "[Values] " << valueMsg->numArgs << std::endl;
|
std::cout << " Handle: " << msg->handle << std::endl;
|
||||||
|
std::cout << " Number of Values: " << valueMsg->numArgs << std::endl;
|
||||||
for(uint32_t i = 0; i < valueMsg->numArgs; ++i) {
|
for(uint32_t i = 0; i < valueMsg->numArgs; ++i) {
|
||||||
std::cout << "[" << msg->args[i]->valueType << "] ";
|
std::cout << " [" << msg->args[i]->valueType << "] ";
|
||||||
auto scaledValue = icsneo::LiveDataUtil::liveDataValueToDouble(*valueMsg->values[i]);
|
auto scaledValue = icsneo::LiveDataUtil::liveDataValueToDouble(*valueMsg->values[i]);
|
||||||
std::cout << scaledValue << std::endl;
|
std::cout << scaledValue << std::endl;
|
||||||
}
|
}
|
||||||
|
|
@ -86,22 +92,33 @@ int main() {
|
||||||
setValMsg->cmd = icsneo::LiveDataCommand::SET_VALUE;
|
setValMsg->cmd = icsneo::LiveDataCommand::SET_VALUE;
|
||||||
setValMsg->handle = msg->handle;
|
setValMsg->handle = msg->handle;
|
||||||
// Convert the value format
|
// Convert the value format
|
||||||
icsneo::LiveDataValue ldValueDAQEnable;
|
auto ldValueDAQEnable = icsneo::LiveDataUtil::liveDataDoubleToValue(val / 3);
|
||||||
icsneo::LiveDataValue ldValueManTrig;
|
auto ldValueManTrig = icsneo::LiveDataUtil::liveDataDoubleToValue(val);
|
||||||
if (!icsneo::LiveDataUtil::liveDataDoubleToValue(val / 3, ldValueDAQEnable) ||
|
auto ldValueTimeSinceMsg = icsneo::LiveDataUtil::liveDataDoubleToValue(val);
|
||||||
!icsneo::LiveDataUtil::liveDataDoubleToValue(val, ldValueManTrig)) {
|
if (!ldValueDAQEnable || !ldValueManTrig || !ldValueTimeSinceMsg) {
|
||||||
|
std::cout << "\tError: Failed to convert values" << std::endl;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
setValMsg->appendSetValue(icsneo::LiveDataValueType::DAQ_ENABLE, ldValueDAQEnable);
|
setValMsg->appendSetValue(icsneo::LiveDataValueType::DAQ_ENABLE, *ldValueDAQEnable);
|
||||||
setValMsg->appendSetValue(icsneo::LiveDataValueType::MANUAL_TRIGGER, ldValueManTrig);
|
setValMsg->appendSetValue(icsneo::LiveDataValueType::MANUAL_TRIGGER, *ldValueManTrig);
|
||||||
device->setValueLiveData(setValMsg);
|
setValMsg->appendSetValue(icsneo::LiveDataValueType::TIME_SINCE_MSG, *ldValueTimeSinceMsg);
|
||||||
|
|
||||||
|
std::cout << "\tSetting values: DAQ_ENABLE=" << (val / 3)
|
||||||
|
<< ", MANUAL_TRIGGER=" << val
|
||||||
|
<< ", TIME_SINCE_MSG=" << val << std::endl;
|
||||||
|
|
||||||
|
if (!device->setValueLiveData(setValMsg)) {
|
||||||
|
std::cout << "\tError setting values: " << icsneo::GetLastError() << std::endl;
|
||||||
|
}
|
||||||
++val;
|
++val;
|
||||||
// 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));
|
||||||
}
|
}
|
||||||
// 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
|
||||||
|
std::cout << "\tUnsubscribing... ";
|
||||||
ret = device->unsubscribeLiveData(msg->handle);
|
ret = device->unsubscribeLiveData(msg->handle);
|
||||||
|
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||||
// The handler should no longer print values
|
// The handler should no longer print values
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
device->removeMessageCallback(handler);
|
device->removeMessageCallback(handler);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
"""
|
||||||
|
LiveData subscription and monitoring example using icsneopy library.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import icsneopy
|
||||||
|
import time
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
|
||||||
|
def livedata_example():
|
||||||
|
"""Subscribe to and monitor LiveData signals."""
|
||||||
|
devices = icsneopy.find_all_devices()
|
||||||
|
if not devices:
|
||||||
|
raise RuntimeError("No devices found")
|
||||||
|
|
||||||
|
device = devices[0]
|
||||||
|
print(f"Using device: {device}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not device.open():
|
||||||
|
raise RuntimeError("Failed to open device")
|
||||||
|
|
||||||
|
if not device.go_online():
|
||||||
|
raise RuntimeError("Failed to go online")
|
||||||
|
|
||||||
|
device.enable_message_polling()
|
||||||
|
|
||||||
|
# Create subscription message
|
||||||
|
msg = icsneopy.LiveDataCommandMessage()
|
||||||
|
msg.handle = icsneopy.get_new_handle()
|
||||||
|
msg.cmd = icsneopy.LiveDataCommand.SUBSCRIBE
|
||||||
|
msg.update_period = timedelta(milliseconds=500)
|
||||||
|
msg.expiration_time = timedelta(milliseconds=0)
|
||||||
|
|
||||||
|
# Subscribe to various LiveData signals
|
||||||
|
msg.append_signal_arg(icsneopy.LiveDataValueType.GPS_LATITUDE)
|
||||||
|
msg.append_signal_arg(icsneopy.LiveDataValueType.GPS_LONGITUDE)
|
||||||
|
msg.append_signal_arg(icsneopy.LiveDataValueType.GPS_ACCURACY)
|
||||||
|
msg.append_signal_arg(icsneopy.LiveDataValueType.DAQ_ENABLE)
|
||||||
|
msg.append_signal_arg(icsneopy.LiveDataValueType.MANUAL_TRIGGER)
|
||||||
|
msg.append_signal_arg(icsneopy.LiveDataValueType.TIME_SINCE_MSG)
|
||||||
|
|
||||||
|
print("\nSubscribing to LiveData signals...")
|
||||||
|
if not device.subscribe_live_data(msg):
|
||||||
|
raise RuntimeError(f"Subscription failed: {icsneopy.get_last_error()}")
|
||||||
|
|
||||||
|
print("Subscription successful")
|
||||||
|
print("\nMonitoring LiveData for 5 seconds...")
|
||||||
|
|
||||||
|
response_count = 0
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
while time.time() - start_time < 5:
|
||||||
|
result = device.get_messages()
|
||||||
|
messages = result[0] if isinstance(result, tuple) else result
|
||||||
|
|
||||||
|
for m in messages:
|
||||||
|
if isinstance(m, icsneopy.LiveDataStatusMessage):
|
||||||
|
if m.handle == msg.handle:
|
||||||
|
print(f"\n[Status] Command: {m.requested_command}, Status: {m.status}")
|
||||||
|
|
||||||
|
elif isinstance(m, icsneopy.LiveDataValueMessage):
|
||||||
|
if m.handle == msg.handle:
|
||||||
|
response_count += 1
|
||||||
|
print(f"\n[Response #{response_count}]")
|
||||||
|
signal_names = ["GPS_LAT", "GPS_LON", "GPS_ACC",
|
||||||
|
"DAQ_EN", "MAN_TRIG", "TIME_SINCE"]
|
||||||
|
for idx, val in enumerate(m.values):
|
||||||
|
value = icsneopy.livedata_value_to_double(val)
|
||||||
|
name = signal_names[idx] if idx < len(signal_names) else f"Signal_{idx}"
|
||||||
|
print(f" {name:12s}: {value:10.2f}")
|
||||||
|
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
print(f"\nReceived {response_count} response messages")
|
||||||
|
|
||||||
|
# Demonstrate setting values
|
||||||
|
print("\nSetting custom values...")
|
||||||
|
set_msg = icsneopy.LiveDataSetValueMessage()
|
||||||
|
set_msg.handle = icsneopy.get_new_handle()
|
||||||
|
set_msg.cmd = icsneopy.LiveDataCommand.SET_VALUE
|
||||||
|
|
||||||
|
# Set DAQ_ENABLE
|
||||||
|
value = icsneopy.livedata_double_to_value(1.0)
|
||||||
|
if value:
|
||||||
|
set_msg.append_set_value(icsneopy.LiveDataValueType.DAQ_ENABLE, value)
|
||||||
|
|
||||||
|
# Set MANUAL_TRIGGER
|
||||||
|
value = icsneopy.livedata_double_to_value(1.0)
|
||||||
|
if value:
|
||||||
|
set_msg.append_set_value(icsneopy.LiveDataValueType.MANUAL_TRIGGER, value)
|
||||||
|
|
||||||
|
if device.set_value_live_data(set_msg):
|
||||||
|
print("Values set successfully")
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
# Check the results
|
||||||
|
result = device.get_messages()
|
||||||
|
messages = result[0] if isinstance(result, tuple) else result
|
||||||
|
for m in messages:
|
||||||
|
if isinstance(m, icsneopy.LiveDataStatusMessage):
|
||||||
|
if m.handle == set_msg.handle:
|
||||||
|
print(f" Set status: {m.status}")
|
||||||
|
|
||||||
|
# Keep device awake by resetting TIME_SINCE_MSG
|
||||||
|
print("\nResetting TIME_SINCE_MSG to keep device awake...")
|
||||||
|
reset_msg = icsneopy.LiveDataSetValueMessage()
|
||||||
|
reset_msg.handle = icsneopy.get_new_handle()
|
||||||
|
reset_msg.cmd = icsneopy.LiveDataCommand.SET_VALUE
|
||||||
|
|
||||||
|
value = icsneopy.livedata_double_to_value(0.0)
|
||||||
|
if value:
|
||||||
|
reset_msg.append_set_value(icsneopy.LiveDataValueType.TIME_SINCE_MSG, value)
|
||||||
|
if device.set_value_live_data(reset_msg):
|
||||||
|
print("TIME_SINCE_MSG reset to 0")
|
||||||
|
|
||||||
|
# Unsubscribe
|
||||||
|
print("\nUnsubscribing...")
|
||||||
|
if device.unsubscribe_live_data(msg.handle):
|
||||||
|
print("Unsubscribed successfully")
|
||||||
|
|
||||||
|
finally:
|
||||||
|
device.close()
|
||||||
|
print("\nDevice closed")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
livedata_example()
|
||||||
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include "icsneo/communication/command.h"
|
#include "icsneo/communication/command.h"
|
||||||
#include "icsneo/api/eventmanager.h"
|
#include "icsneo/api/eventmanager.h"
|
||||||
|
|
||||||
|
|
@ -157,7 +158,7 @@ namespace LiveDataUtil
|
||||||
|
|
||||||
LiveDataHandle getNewHandle();
|
LiveDataHandle getNewHandle();
|
||||||
double liveDataValueToDouble(const LiveDataValue& val);
|
double liveDataValueToDouble(const LiveDataValue& val);
|
||||||
bool liveDataDoubleToValue(const double& dFloat, LiveDataValue& value);
|
std::optional<LiveDataValue> liveDataDoubleToValue(const double& dFloat);
|
||||||
static constexpr uint32_t LiveDataVersion = 1;
|
static constexpr uint32_t LiveDataVersion = 1;
|
||||||
|
|
||||||
} // namespace LiveDataUtil
|
} // namespace LiveDataUtil
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue