diff --git a/api/icsneoc2/icsneoc2.cpp b/api/icsneoc2/icsneoc2.cpp index d42e4a7e..ea3a0c06 100644 --- a/api/icsneoc2/icsneoc2.cpp +++ b/api/icsneoc2/icsneoc2.cpp @@ -825,6 +825,30 @@ icsneoc2_error_t icsneoc2_device_gptp_status_get(const icsneoc2_device_t* device return icsneoc2_error_success; } +icsneoc2_error_t icsneoc2_device_supports_reboot(const icsneoc2_device_t* device, bool* supported) { + auto res = icsneoc2_device_is_valid(device); + if(res != icsneoc2_error_success) { + return res; + } + if(!supported) { + return icsneoc2_error_invalid_parameters; + } + *supported = device->device->supportsReboot(); + + return icsneoc2_error_success; +} + +icsneoc2_error_t icsneoc2_device_reboot(const icsneoc2_device_t* device, bool safe) { + auto res = icsneoc2_device_is_valid(device); + if(res != icsneoc2_error_success) { + return res; + } + if(!device->device->reboot(safe)) { + return icsneoc2_error_transmit_message_failed; + } + return icsneoc2_error_success; +} + icsneoc2_error_t icsneoc2_device_digital_io_get(const icsneoc2_device_t* device, icsneoc2_io_type_t type, uint32_t number, bool* value) { auto res = icsneoc2_device_is_valid(device); if(res != icsneoc2_error_success) { diff --git a/bindings/python/icsneopy/device/device.cpp b/bindings/python/icsneopy/device/device.cpp index fde0bd3e..3b165469 100644 --- a/bindings/python/icsneopy/device/device.cpp +++ b/bindings/python/icsneopy/device/device.cpp @@ -52,9 +52,11 @@ void init_device(pybind11::module_& m) { .def("set_digital_io", pybind11::overload_cast(&Device::setDigitalIO), pybind11::arg("type"), pybind11::arg("number"), pybind11::arg("value"), pybind11::call_guard()) .def("set_polling_message_limit", &Device::setPollingMessageLimit) .def("set_rtc", &Device::setRTC, pybind11::call_guard()) + .def("reboot", &Device::reboot, pybind11::arg("safe") = false, pybind11::call_guard()) .def("start_script", &Device::startScript, pybind11::call_guard()) .def("stop_script", &Device::stopScript, pybind11::call_guard()) .def("supports_tc10", &Device::supportsTC10) + .def("supports_reboot", &Device::supportsReboot) .def("supports_live_data", &Device::supportsLiveData) .def("subscribe_live_data", &Device::subscribeLiveData, pybind11::arg("message"), pybind11::call_guard()) .def("unsubscribe_live_data", &Device::unsubscribeLiveData, pybind11::arg("handle"), pybind11::call_guard()) diff --git a/device/device.cpp b/device/device.cpp index ae4fc163..b766d5e8 100644 --- a/device/device.cpp +++ b/device/device.cpp @@ -3735,6 +3735,15 @@ bool Device::requestTC10Sleep(Network::NetID network) { return typed->response == ExtendedResponse::OK; } +bool Device::reboot(bool safe) { + if(!supportsReboot()) { + report(APIEvent::Type::NotSupported, APIEvent::Severity::Error); + return false; + } + // The device reboots in response to this command, so no reply is expected. + return com->sendCommand(ExtendedCommand::Reboot, { uint8_t(safe ? 1 : 0) }); +} + std::optional Device::getTC10Status(Network::NetID network) { if(!supportsTC10()) { report(APIEvent::Type::NotSupported, APIEvent::Severity::Error); diff --git a/include/icsneo/device/device.h b/include/icsneo/device/device.h index dbcb26c3..ea5bf1ad 100644 --- a/include/icsneo/device/device.h +++ b/include/icsneo/device/device.h @@ -862,10 +862,17 @@ public: virtual bool supportsGPTP() const { return false; } + virtual bool supportsReboot() const { return false; } + bool requestTC10Wake(Network::NetID network); bool requestTC10Sleep(Network::NetID network); + // Reboot the device. When safe is true the device boots the Linux rescue image and does not + // load coremini ("safe boot"); otherwise it reboots normally. The device reboots in response, + // so no reply is expected. Only supported on devices where supportsReboot() is true. + bool reboot(bool safe = false); + std::optional getTC10Status(Network::NetID network); std::optional getGPTPStatus(std::chrono::milliseconds timeout = std::chrono::milliseconds(100)); diff --git a/include/icsneo/device/tree/neoviconnect/neoviconnect.h b/include/icsneo/device/tree/neoviconnect/neoviconnect.h index 65da5dc3..d7006e20 100644 --- a/include/icsneo/device/tree/neoviconnect/neoviconnect.h +++ b/include/icsneo/device/tree/neoviconnect/neoviconnect.h @@ -34,6 +34,8 @@ public: return supportedNetworks; } + bool supportsReboot() const override { return true; } + ProductID getProductID() const override { return ProductID::Connect; } diff --git a/include/icsneo/device/tree/neovifire3/neovifire3.h b/include/icsneo/device/tree/neovifire3/neovifire3.h index 57bcd8dc..6d4285b7 100644 --- a/include/icsneo/device/tree/neovifire3/neovifire3.h +++ b/include/icsneo/device/tree/neovifire3/neovifire3.h @@ -53,6 +53,8 @@ public: } size_t getEthernetActivationLineCount() const override { return 2; } + bool supportsReboot() const override { return true; } + ProductID getProductID() const override { return ProductID::neoVIFIRE3; } diff --git a/include/icsneo/device/tree/neovifire3flexray/neovifire3flexray.h b/include/icsneo/device/tree/neovifire3flexray/neovifire3flexray.h index 8652eb0f..474b9f6c 100644 --- a/include/icsneo/device/tree/neovifire3flexray/neovifire3flexray.h +++ b/include/icsneo/device/tree/neovifire3flexray/neovifire3flexray.h @@ -55,6 +55,8 @@ public: return supportedNetworks; } + bool supportsReboot() const override { return true; } + ProductID getProductID() const override { return ProductID::neoVIFIRE3; } diff --git a/include/icsneo/device/tree/neovifire3t1slin/neovifire3t1slin.h b/include/icsneo/device/tree/neovifire3t1slin/neovifire3t1slin.h index 7ac13733..f5d7c325 100644 --- a/include/icsneo/device/tree/neovifire3t1slin/neovifire3t1slin.h +++ b/include/icsneo/device/tree/neovifire3t1slin/neovifire3t1slin.h @@ -61,6 +61,8 @@ public: bool supportsTC10() const override { return true; } + bool supportsReboot() const override { return true; } + ProductID getProductID() const override { return ProductID::neoVIFIRE3; } diff --git a/include/icsneo/device/tree/neovired2/neovired2.h b/include/icsneo/device/tree/neovired2/neovired2.h index 6cafae19..d289d8bd 100644 --- a/include/icsneo/device/tree/neovired2/neovired2.h +++ b/include/icsneo/device/tree/neovired2/neovired2.h @@ -39,6 +39,8 @@ public: bool supportsGPTP() const override { return true; } + bool supportsReboot() const override { return true; } + ProductID getProductID() const override { return ProductID::neoVIFIRE3; } diff --git a/include/icsneo/icsneoc2.h b/include/icsneo/icsneoc2.h index 3db32292..32d38168 100644 --- a/include/icsneo/icsneoc2.h +++ b/include/icsneo/icsneoc2.h @@ -602,6 +602,29 @@ icsneoc2_error_t icsneoc2_device_supports_gptp(const icsneoc2_device_t* device, * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters or icsneoc2_error_invalid_type otherwise. */ icsneoc2_error_t icsneoc2_device_gptp_status_get(const icsneoc2_device_t* device, uint32_t timeout_ms, icsneoc2_gptp_status_t* status); + +/** + * Check if the device supports rebooting. + * + * @param[in] device The device to check against. + * @param[out] supported Pointer to a bool to copy the value into. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +icsneoc2_error_t icsneoc2_device_supports_reboot(const icsneoc2_device_t* device, bool* supported); + +/** + * Reboot the device. When safe is true the device boots the Linux rescue image and does not load + * coremini ("safe boot"); otherwise it reboots normally. The device reboots in response, so no reply + * is expected. Only supported on devices where icsneoc2_device_supports_reboot() reports true. + * + * @param[in] device The device to reboot. + * @param[in] safe true to reboot into safe boot mode, false to reboot normally. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters or icsneoc2_error_transmit_message_failed otherwise. + */ +icsneoc2_error_t icsneoc2_device_reboot(const icsneoc2_device_t* device, bool safe); + /** * Get the current state of a digital I/O pin. * diff --git a/test/unit/icsneoc2.cpp b/test/unit/icsneoc2.cpp index c36ade50..4d5d20c4 100644 --- a/test/unit/icsneoc2.cpp +++ b/test/unit/icsneoc2.cpp @@ -228,6 +228,8 @@ TEST(icsneoc2, test_icsneoc2_error_invalid_parameters_and_invalid_device) ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_tc10_status_get(NULL, icsneoc2_netid_dwcan_01, &sleep_s, &wake_s)); ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_tc10_status_get(NULL, icsneoc2_netid_dwcan_01, NULL, NULL)); } + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_supports_reboot(NULL, &placeholderBool)); + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_reboot(NULL, false)); ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_netid_network_type_get(icsneoc2_netid_dwcan_01, NULL)); ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_timestamp_resolution_get(NULL, &placeholderInteger32));