1086 lines
36 KiB
C++
1086 lines
36 KiB
C++
#include "icsneo/icsneoc2.h"
|
|
#include "icsneo/icsneoc2messages.h"
|
|
#include "icsneoc2_internal.h"
|
|
#include "icsneo/device/device.h"
|
|
#include "icsneo/device/devicefinder.h"
|
|
#include "icsneo/icsneocpp.h"
|
|
#include "icsneo/communication/io.h"
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
#include <list>
|
|
#include <map>
|
|
#include <algorithm>
|
|
#include <optional>
|
|
#include <sstream>
|
|
#include <fstream>
|
|
|
|
using namespace icsneo;
|
|
|
|
bool safe_str_copy(char* dest, size_t* dest_size, std::string_view src) {
|
|
if(!dest || !dest_size || *dest_size == 0) {
|
|
return false;
|
|
}
|
|
// Need to save room for the null terminator
|
|
*dest_size -= 1;
|
|
try {
|
|
*dest_size = src.copy(dest, *dest_size, 0);
|
|
// Null terminate the buffer
|
|
dest[*dest_size] = '\0';
|
|
// If we truncated the string, return false to indicate not all data was copied.
|
|
if(*dest_size != src.length()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
} catch (std::out_of_range& ex) {
|
|
// if pos > size()
|
|
(void)ex;
|
|
// Null terminate the buffer
|
|
dest[0] = '\0';
|
|
*dest_size = 0;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_error_code_get(icsneoc2_error_t error_code, char* value, size_t* value_length) {
|
|
static const char* error_strings[icsneoc2_error_maxsize] = {
|
|
"Success", // icsneoc2_error_success
|
|
"Invalid function parameters", // icsneoc2_error_invalid_parameters
|
|
"Open failed", // icsneoc2_error_open_failed
|
|
"Going online failed", // icsneoc2_error_go_online_failed
|
|
"Enable message polling failed", // icsneoc2_error_enable_message_polling_failed
|
|
"Synchronizing RTC failed", // icsneoc2_error_sync_rtc_failed
|
|
"Getting messages failed", // icsneoc2_error_get_messages_failed
|
|
"Invalid type", // icsneoc2_error_invalid_type
|
|
"RTC failure", // icsneoc2_error_rtc_failure
|
|
"Getting settings failed", // icsneoc2_error_get_settings_failure
|
|
"Setting settings failed", // icsneoc2_error_set_settings_failure
|
|
"Transmitting message failed", // icsneoc2_error_transmit_message_failed
|
|
"String copy failed", // icsneoc2_error_string_copy_failed
|
|
"Invalid device", // icsneoc2_error_invalid_device
|
|
"Invalid message", // icsneoc2_error_invalid_message
|
|
"Out of memory", // icsneoc2_error_out_of_memory
|
|
"Disk format failed", // icsneoc2_error_format_disk_failed
|
|
"Script start failed", // icsneoc2_error_script_start_failed
|
|
"Script stop failed", // icsneoc2_error_script_stop_failed
|
|
"Script clear failed", // icsneoc2_error_script_clear_failed
|
|
"Script upload failed", // icsneoc2_error_script_upload_failed
|
|
"Script load prepare failed", // icsneoc2_error_script_load_prepare_failed
|
|
};
|
|
static_assert(std::size(error_strings) == icsneoc2_error_maxsize,
|
|
"error_strings is out of sync with _icsneoc2_error_t enum - update both together");
|
|
|
|
if(error_code >= icsneoc2_error_maxsize) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
|
|
if(!value || !value_length) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
|
|
return safe_str_copy(value, value_length, error_strings[error_code]) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_type_name_get(icsneoc2_devicetype_t device_type, char* value, size_t* value_length) {
|
|
if(!value || !value_length) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
|
|
return safe_str_copy(value, value_length, DeviceType::GetGenericProductName(static_cast<DeviceType::Enum>(device_type))) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_enumerate(icsneoc2_devicetype_t device_type, icsneoc2_device_info_t** device_info) {
|
|
if(!device_info) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
auto found_devices = DeviceFinder::FindAll();
|
|
|
|
icsneoc2_device_info_t* head = nullptr;
|
|
icsneoc2_device_info_t* tail = nullptr;
|
|
for(auto& dev : found_devices) {
|
|
if(device_type != 0 && static_cast<icsneoc2_devicetype_t>(dev->getType().getDeviceType()) != device_type) {
|
|
continue;
|
|
}
|
|
auto* node = new (std::nothrow) icsneoc2_device_info_t;
|
|
if(!node) {
|
|
icsneoc2_enumeration_free(head);
|
|
return icsneoc2_error_out_of_memory;
|
|
}
|
|
node->device = dev;
|
|
node->next = nullptr;
|
|
if(!head) {
|
|
head = node;
|
|
} else {
|
|
tail->next = node;
|
|
}
|
|
tail = node;
|
|
}
|
|
|
|
*device_info = head;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
void icsneoc2_enumeration_free(icsneoc2_device_info_t* devices) {
|
|
while(devices) {
|
|
auto* next = devices->next;
|
|
delete devices;
|
|
devices = next;
|
|
}
|
|
}
|
|
|
|
icsneoc2_device_info_t* icsneoc2_device_info_next(const icsneoc2_device_info_t* device_info) {
|
|
if(!device_info) {
|
|
return nullptr;
|
|
}
|
|
return device_info->next;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_info_serial_get(const icsneoc2_device_info_t* device_info, char* serial, size_t* serial_length) {
|
|
if(!device_info || !device_info->device || !serial || !serial_length) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
return safe_str_copy(serial, serial_length, device_info->device->getSerial())
|
|
? icsneoc2_error_success : icsneoc2_error_string_copy_failed;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_info_type_get(const icsneoc2_device_info_t* device_info, icsneoc2_devicetype_t* type) {
|
|
if(!device_info || !device_info->device || !type) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*type = static_cast<icsneoc2_devicetype_t>(device_info->device->getType().getDeviceType());
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_info_type_name_get(const icsneoc2_device_info_t* device_info, char* typeName, size_t* type_name_length) {
|
|
if(!device_info || !device_info->device || !typeName || !type_name_length) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
return safe_str_copy(typeName, type_name_length,
|
|
DeviceType::GetGenericProductName(device_info->device->getType().getDeviceType()))
|
|
? icsneoc2_error_success : icsneoc2_error_string_copy_failed;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_info_description_get(const icsneoc2_device_info_t* device_info, char* description, size_t* description_length) {
|
|
if(!device_info || !device_info->device || !description || !description_length) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
return safe_str_copy(description, description_length, device_info->device->describe())
|
|
? icsneoc2_error_success : icsneoc2_error_string_copy_failed;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_is_valid(const icsneoc2_device_t* device) {
|
|
if(!device) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
|
|
if(!device->device) {
|
|
return icsneoc2_error_invalid_device;
|
|
}
|
|
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_is_open(const icsneoc2_device_t* device, bool* is_open) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
if(!is_open) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
auto dev = device->device;
|
|
*is_open = dev->isOpen();
|
|
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_is_disconnected(const icsneoc2_device_t* device, bool* is_disconnected) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
if(!is_disconnected) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
auto dev = device->device;
|
|
*is_disconnected = dev->isDisconnected();
|
|
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
static icsneoc2_error_t open_device_with_options(std::shared_ptr<Device> dev, icsneoc2_open_options_t options, icsneoc2_device_t** device) {
|
|
if(!dev) {
|
|
return icsneoc2_error_invalid_device;
|
|
}
|
|
// Nothing to do if already open
|
|
if(dev->isOpen()) {
|
|
*device = new (std::nothrow) icsneoc2_device_t;
|
|
if(!*device) {
|
|
return icsneoc2_error_out_of_memory;
|
|
}
|
|
(*device)->device = dev;
|
|
return icsneoc2_error_success;
|
|
}
|
|
if(!dev->enableMessagePolling(std::make_optional<MessageFilter>())) {
|
|
return icsneoc2_error_enable_message_polling_failed;
|
|
}
|
|
if(!dev->open()) {
|
|
return icsneoc2_error_open_failed;
|
|
}
|
|
if((options & ICSNEOC2_OPEN_OPTIONS_SYNC_RTC) && !dev->setRTC(std::chrono::system_clock::now())) {
|
|
dev->close();
|
|
return icsneoc2_error_sync_rtc_failed;
|
|
}
|
|
if((options & ICSNEOC2_OPEN_OPTIONS_GO_ONLINE) && !dev->goOnline()) {
|
|
dev->close();
|
|
return icsneoc2_error_go_online_failed;
|
|
}
|
|
*device = new (std::nothrow) icsneoc2_device_t;
|
|
if(!*device) {
|
|
dev->close();
|
|
return icsneoc2_error_out_of_memory;
|
|
}
|
|
(*device)->device = dev;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_open(const icsneoc2_device_info_t* device_info, icsneoc2_open_options_t options, icsneoc2_device_t** device) {
|
|
if(!device_info || !device) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
if(!device_info->device) {
|
|
return icsneoc2_error_invalid_device;
|
|
}
|
|
return open_device_with_options(device_info->device, options, device);
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_open_serial(const char* serial, icsneoc2_open_options_t options, icsneoc2_device_t** device) {
|
|
if(!serial || !device) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
icsneoc2_device_info_t* devs = nullptr;
|
|
auto res = icsneoc2_device_enumerate(0, &devs);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
std::string_view target(serial);
|
|
for(auto* cur = devs; cur; cur = cur->next) {
|
|
if(cur->device && cur->device->getSerial() == target) {
|
|
res = open_device_with_options(cur->device, options, device);
|
|
icsneoc2_enumeration_free(devs);
|
|
return res;
|
|
}
|
|
}
|
|
icsneoc2_enumeration_free(devs);
|
|
return icsneoc2_error_invalid_device;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_open_first(icsneoc2_devicetype_t device_type, icsneoc2_open_options_t options, icsneoc2_device_t** device) {
|
|
if(!device) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
icsneoc2_device_info_t* devs = nullptr;
|
|
auto res = icsneoc2_device_enumerate(device_type, &devs);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
for(auto* cur = devs; cur; cur = cur->next) {
|
|
if(cur->device && !cur->device->isOpen()) {
|
|
res = open_device_with_options(cur->device, options, device);
|
|
icsneoc2_enumeration_free(devs);
|
|
return res;
|
|
}
|
|
}
|
|
icsneoc2_enumeration_free(devs);
|
|
return icsneoc2_error_invalid_device;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_close(icsneoc2_device_t* device) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
auto dev = device->device;
|
|
if(!dev->isOpen()) {
|
|
return icsneoc2_error_success;
|
|
}
|
|
dev->close();
|
|
delete device;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_description_get(const icsneoc2_device_t* device, char* value, size_t* value_length) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
// Copy the string into value
|
|
return safe_str_copy(value, value_length, device->device->describe()) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_type_get(const icsneoc2_device_t* device, icsneoc2_devicetype_t* value) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
auto dev = device->device;
|
|
*value = static_cast<icsneoc2_devicetype_t>(dev->getType().getDeviceType());
|
|
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_serial_get(const icsneoc2_device_t* device, char* value, size_t* value_length) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
auto dev = device->device;
|
|
// Copy the string into value
|
|
return safe_str_copy(value, value_length, dev->getSerial()) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed;
|
|
}
|
|
|
|
|
|
icsneoc2_error_t icsneoc2_device_go_online(const icsneoc2_device_t* device, bool go_online) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
auto dev = device->device;
|
|
// Go online
|
|
if(go_online && dev->goOnline()) {
|
|
return icsneoc2_error_success;
|
|
}
|
|
// Go offline
|
|
if(!go_online && dev->goOffline()) {
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
return icsneoc2_error_go_online_failed;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_is_online(const icsneoc2_device_t* device, bool* is_online) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
if(!is_online) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
auto dev = device->device;
|
|
*is_online = dev->isOnline();
|
|
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_is_online_supported(const icsneoc2_device_t* device, bool* is_online_supported) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
if(!is_online_supported) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
auto dev = device->device;
|
|
*is_online_supported = dev->isOnlineSupported();
|
|
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_message_polling_limit_set(const icsneoc2_device_t* device, uint32_t limit) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
auto dev = device->device;
|
|
dev->setPollingMessageLimit(static_cast<size_t>(limit));
|
|
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_message_polling_limit_get(const icsneoc2_device_t* device, uint32_t* limit) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
if(!limit) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
auto dev = device->device;
|
|
*limit = static_cast<uint32_t>(dev->getPollingMessageLimit());
|
|
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_timestamp_resolution_get(icsneoc2_device_t* device, uint32_t* resolution) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
if(!resolution) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
auto dev = device->device;
|
|
*resolution = static_cast<uint32_t>(dev->getTimestampResolution());
|
|
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_message_get(const icsneoc2_device_t* device, icsneoc2_message_t** message, uint32_t timeout_ms) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
if(!message) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
|
|
std::vector<std::shared_ptr<Message>> msgs(1);
|
|
if(!device->device->getMessages(msgs, 1, std::chrono::milliseconds(timeout_ms))) {
|
|
return icsneoc2_error_get_messages_failed;
|
|
}
|
|
|
|
if(!msgs.empty()) {
|
|
*message = new (std::nothrow) icsneoc2_message_t;
|
|
if(!*message) {
|
|
return icsneoc2_error_out_of_memory;
|
|
}
|
|
(*message)->message = msgs[0];
|
|
} else {
|
|
*message = nullptr;
|
|
}
|
|
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_message_transmit(const icsneoc2_device_t* device, const icsneoc2_message_t* message) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
if(!message) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
auto dev = device->device;
|
|
auto success = dev->transmit(std::static_pointer_cast<Frame>(message->message));
|
|
|
|
return success ? icsneoc2_error_success : icsneoc2_error_transmit_message_failed;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_network_type_name_get(icsneoc2_network_type_t network_type, char* value, size_t* value_length) {
|
|
if(!value || !value_length) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
auto network_type_str = std::string(Network::GetTypeString(static_cast<Network::Type>(network_type)));
|
|
// Copy the string into value
|
|
return safe_str_copy(value, value_length, network_type_str) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_event_get(icsneoc2_event_t** event, const icsneoc2_device_t* device) {
|
|
if(!event) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
|
|
if(device && !device->device) {
|
|
return icsneoc2_error_invalid_device;
|
|
}
|
|
|
|
const auto* dev = device ? device->device.get() : nullptr;
|
|
EventFilter filter(dev);
|
|
auto events = icsneo::GetEvents(filter, 1);
|
|
|
|
if(events.empty()) {
|
|
*event = nullptr;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
*event = new (std::nothrow) icsneoc2_event_t;
|
|
if(!*event) {
|
|
return icsneoc2_error_out_of_memory;
|
|
}
|
|
(*event)->event = events.front();
|
|
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_event_free(icsneoc2_event_t* event) {
|
|
if(!event) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
delete event;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_event_description_get(const icsneoc2_event_t* event, char* value, size_t* value_length) {
|
|
if(!event || !value || !value_length) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
// Copy the string into value
|
|
return safe_str_copy(value, value_length, event->event.describe()) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_rtc_get(const icsneoc2_device_t* device, int64_t* unix_epoch) {
|
|
if(!unix_epoch) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
if(auto rtc_time = device->device->getRTC(); rtc_time != std::nullopt) {
|
|
*unix_epoch = std::chrono::duration_cast<std::chrono::seconds>(rtc_time->time_since_epoch()).count();
|
|
} else {
|
|
*unix_epoch = 0;
|
|
return icsneoc2_error_rtc_failure;
|
|
}
|
|
return icsneoc2_error_success;
|
|
}
|
|
icsneoc2_error_t icsneoc2_device_rtc_set(const icsneoc2_device_t* device, int64_t unix_epoch) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
if(!device->device->setRTC(std::chrono::system_clock::time_point(std::chrono::seconds(unix_epoch)))) {
|
|
return icsneoc2_error_sync_rtc_failed;
|
|
}
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_supports_tc10(const icsneoc2_device_t* device, bool* supported) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
*supported = device->device->supportsTC10();
|
|
|
|
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) {
|
|
return res;
|
|
}
|
|
if(!value) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
|
|
// Convert icsneoc2 IO type to C++ IO enum (they match numerically)
|
|
auto cpp_io_type = static_cast<icsneo::IO>(type);
|
|
|
|
// Get the digital IO value
|
|
const std::optional<bool> val = device->device->getDigitalIO(cpp_io_type, number);
|
|
if(!val.has_value()) {
|
|
return icsneoc2_error_invalid_type;
|
|
}
|
|
|
|
*value = *val;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_digital_io_set(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) {
|
|
return res;
|
|
}
|
|
|
|
// Convert icsneoc2 IO type to C++ IO enum (they match numerically)
|
|
auto cpp_io_type = static_cast<icsneo::IO>(type);
|
|
|
|
// Set the digital IO value
|
|
if(!device->device->setDigitalIO(cpp_io_type, number, value)) {
|
|
return icsneoc2_error_set_settings_failure;
|
|
}
|
|
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_version_get(char* value, size_t* value_length) {
|
|
if(!value || !value_length) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
|
|
// Get version from libicsneo
|
|
auto version = icsneo::GetVersion();
|
|
|
|
// Format version string (e.g., "v1.0.0")
|
|
std::ostringstream version_stream;
|
|
version_stream << 'v' << version.major << '.' << version.minor << '.' << version.patch;
|
|
if(version.metadata[0] != '\0') {
|
|
version_stream << '+' << version.metadata;
|
|
}
|
|
|
|
auto version_str = version_stream.str();
|
|
return safe_str_copy(value, value_length, version_str) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_serial_num_to_string(uint32_t num, char* str, size_t* str_length) {
|
|
if(!str || !str_length) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
|
|
// Convert using Device static method
|
|
auto result = Device::SerialNumToString(num);
|
|
return safe_str_copy(str, str_length, result) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_serial_string_to_num(char* str, size_t str_length, uint32_t* num) {
|
|
if(!str || !num) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
|
|
// Convert using Device static method
|
|
*num = Device::SerialStringToNum(std::string(str, str_length));
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_disk_count_get(const icsneoc2_device_t* device, size_t* count) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
if(!count) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*count = device->device->getDiskCount();
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_supports_disk_formatting(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->supportsDiskFormatting();
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_disk_details_get(const icsneoc2_device_t* device, icsneoc2_disk_details_t** disk_details) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
if(!disk_details) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
auto details = device->device->getDiskDetails();
|
|
if(!details) {
|
|
return icsneoc2_error_invalid_device;
|
|
}
|
|
auto* out = new (std::nothrow) icsneoc2_disk_details_t;
|
|
if(!out) {
|
|
return icsneoc2_error_out_of_memory;
|
|
}
|
|
out->details = details;
|
|
*disk_details = out;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
void icsneoc2_disk_details_free(icsneoc2_disk_details_t* disk_details) {
|
|
delete disk_details;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_disk_details_count_get(const icsneoc2_disk_details_t* disk_details, size_t* count) {
|
|
if(!disk_details || !disk_details->details || !count) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*count = disk_details->details->disks.size();
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_disk_details_layout_get(const icsneoc2_disk_details_t* disk_details, icsneoc2_disk_layout_t* layout) {
|
|
if(!disk_details || !disk_details->details || !layout) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*layout = static_cast<icsneoc2_disk_layout_t>(disk_details->details->layout);
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_disk_details_layout_set(const icsneoc2_disk_details_t* disk_details, icsneoc2_disk_layout_t layout) {
|
|
if(!disk_details || !disk_details->details) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
disk_details->details->layout = static_cast<DiskLayout>(layout);
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_disk_details_flags_get(const icsneoc2_disk_details_t* disk_details, size_t index, icsneoc2_disk_format_flags_t* flags) {
|
|
if(!disk_details || !disk_details->details || !flags) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
if(index >= disk_details->details->disks.size()) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
const auto& disk = disk_details->details->disks[index];
|
|
icsneoc2_disk_format_flags_t result = 0;
|
|
if(disk.present) result |= ICSNEOC2_DISK_FORMAT_FLAGS_PRESENT;
|
|
if(disk.initialized) result |= ICSNEOC2_DISK_FORMAT_FLAGS_INITIALIZED;
|
|
if(disk.formatted) result |= ICSNEOC2_DISK_FORMAT_FLAGS_FORMATTED;
|
|
*flags = result;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_disk_details_flags_set(const icsneoc2_disk_details_t* disk_details, size_t index, icsneoc2_disk_format_flags_t flags) {
|
|
if(!disk_details || !disk_details->details || !flags) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
if(index >= disk_details->details->disks.size()) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
auto& disk = disk_details->details->disks[index];
|
|
disk.present = (flags & ICSNEOC2_DISK_FORMAT_FLAGS_PRESENT) != 0;
|
|
disk.initialized = (flags & ICSNEOC2_DISK_FORMAT_FLAGS_INITIALIZED) != 0;
|
|
disk.formatted = (flags & ICSNEOC2_DISK_FORMAT_FLAGS_FORMATTED) != 0;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_disk_details_size_get(const icsneoc2_disk_details_t* disk_details, size_t index, uint64_t* sectors, uint64_t* bytes_per_sector) {
|
|
if(!disk_details || !disk_details->details || !sectors || !bytes_per_sector) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
if(index >= disk_details->details->disks.size()) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
const auto& disk = disk_details->details->disks[index];
|
|
*sectors = disk.sectors;
|
|
*bytes_per_sector = disk.bytesPerSector;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_disk_details_full_format_get(const icsneoc2_disk_details_t* disk_details, bool* full_format) {
|
|
if(!disk_details || !disk_details->details || !full_format) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*full_format = disk_details->details->fullFormat;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_disk_details_full_format_set(const icsneoc2_disk_details_t* disk_details, bool full_format) {
|
|
if(!disk_details || !disk_details->details) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
disk_details->details->fullFormat = full_format;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_format_disk(const icsneoc2_device_t* device, icsneoc2_disk_details_t* disk_details, icsneoc2_disk_format_progress_fn progress_callback, void* user_data) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
if(!disk_details || !disk_details->details) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
|
|
Device::DiskFormatProgress handler;
|
|
if(progress_callback) {
|
|
handler = [progress_callback, user_data](uint64_t sectorsFormatted, uint64_t sectorsTotal) -> Device::DiskFormatDirective {
|
|
auto directive = progress_callback(sectorsFormatted, sectorsTotal, user_data);
|
|
if(directive == icsneoc2_disk_format_directive_stop) {
|
|
return Device::DiskFormatDirective::Stop;
|
|
}
|
|
return Device::DiskFormatDirective::Continue;
|
|
};
|
|
}
|
|
|
|
if(!device->device->formatDisk(*disk_details->details, handler)) {
|
|
return icsneoc2_error_format_disk_failed;
|
|
}
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
static icsneoc2_error_t get_supported_networks(const icsneoc2_device_t* device, const std::vector<Network>& nets, icsneoc2_netid_t* networks, size_t* count) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
if(!count) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
if(!networks) {
|
|
*count = nets.size();
|
|
return icsneoc2_error_success;
|
|
}
|
|
if(*count < nets.size()) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
size_t to_copy = std::min<size_t>(*count, nets.size());
|
|
for(size_t i = 0; i < to_copy; i++) {
|
|
networks[i] = static_cast<icsneoc2_netid_t>(nets[i].getNetID());
|
|
}
|
|
*count = to_copy;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_supported_rx_networks_get(const icsneoc2_device_t* device, icsneoc2_netid_t* networks, size_t* count) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
return get_supported_networks(device, device->device->getSupportedRXNetworks(), networks, count);
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_supported_tx_networks_get(const icsneoc2_device_t* device, icsneoc2_netid_t* networks, size_t* count) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
return get_supported_networks(device, device->device->getSupportedTXNetworks(), networks, count);
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_supports_coremini_script(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->supportsCoreminiScript();
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
static Disk::MemoryType to_memory_type(icsneoc2_memory_type_t type) {
|
|
switch(type) {
|
|
case icsneoc2_memory_type_flash:
|
|
return Disk::MemoryType::Flash;
|
|
case icsneoc2_memory_type_sd:
|
|
default:
|
|
return Disk::MemoryType::SD;
|
|
}
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_script_start(const icsneoc2_device_t* device, icsneoc2_memory_type_t memory_type) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
if(!device->device->startScript(to_memory_type(memory_type))) {
|
|
return icsneoc2_error_script_start_failed;
|
|
}
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_script_stop(const icsneoc2_device_t* device) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
if(!device->device->stopScript()) {
|
|
return icsneoc2_error_script_stop_failed;
|
|
}
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_script_clear(const icsneoc2_device_t* device, icsneoc2_memory_type_t memory_type) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
if(!device->device->clearScript(to_memory_type(memory_type))) {
|
|
return icsneoc2_error_script_clear_failed;
|
|
}
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_script_prepare_load(const icsneoc2_device_t* device, int8_t* status) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
if(!status) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*status = device->device->prepareScriptLoad();
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_coremini_upload_file(const icsneoc2_device_t* device, const char* path, icsneoc2_memory_type_t memory_type) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
if(!path) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
std::ifstream ifs(path, std::ios::binary);
|
|
if(!ifs.is_open()) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
if(!device->device->uploadCoremini(ifs, to_memory_type(memory_type))) {
|
|
return icsneoc2_error_script_upload_failed;
|
|
}
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_coremini_upload(const icsneoc2_device_t* device, const uint8_t* data, size_t length, icsneoc2_memory_type_t memory_type) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
if(!data || length == 0) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
std::string buf(reinterpret_cast<const char*>(data), length);
|
|
std::istringstream iss(buf, std::ios::binary);
|
|
if(!device->device->uploadCoremini(iss, to_memory_type(memory_type))) {
|
|
return icsneoc2_error_script_upload_failed;
|
|
}
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_device_script_status_get(const icsneoc2_device_t* device, icsneoc2_script_status_t** script_status) {
|
|
auto res = icsneoc2_device_is_valid(device);
|
|
if(res != icsneoc2_error_success) {
|
|
return res;
|
|
}
|
|
if(!script_status) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
auto status = device->device->getScriptStatus();
|
|
if(!status) {
|
|
return icsneoc2_error_invalid_device;
|
|
}
|
|
auto* out = new (std::nothrow) icsneoc2_script_status_t;
|
|
if(!out) {
|
|
return icsneoc2_error_out_of_memory;
|
|
}
|
|
out->status = status;
|
|
*script_status = out;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
void icsneoc2_script_status_free(icsneoc2_script_status_t* script_status) {
|
|
delete script_status;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_script_status_is_coremini_running(const icsneoc2_script_status_t* script_status, bool* value) {
|
|
if(!script_status || !script_status->status || !value) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*value = script_status->status->isCoreminiRunning;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_script_status_is_encrypted(const icsneoc2_script_status_t* script_status, bool* value) {
|
|
if(!script_status || !script_status->status || !value) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*value = script_status->status->isEncrypted;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_script_status_sector_overflows_get(const icsneoc2_script_status_t* script_status, uint32_t* value) {
|
|
if(!script_status || !script_status->status || !value) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*value = script_status->status->sectorOverflows;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_script_status_remaining_sector_buffers_get(const icsneoc2_script_status_t* script_status, uint32_t* value) {
|
|
if(!script_status || !script_status->status || !value) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*value = script_status->status->numRemainingSectorBuffers;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_script_status_last_sector_get(const icsneoc2_script_status_t* script_status, uint32_t* value) {
|
|
if(!script_status || !script_status->status || !value) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*value = script_status->status->lastSector;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_script_status_read_bin_size_get(const icsneoc2_script_status_t* script_status, uint32_t* value) {
|
|
if(!script_status || !script_status->status || !value) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*value = script_status->status->readBinSize;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_script_status_min_sector_get(const icsneoc2_script_status_t* script_status, uint32_t* value) {
|
|
if(!script_status || !script_status->status || !value) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*value = script_status->status->minSector;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_script_status_max_sector_get(const icsneoc2_script_status_t* script_status, uint32_t* value) {
|
|
if(!script_status || !script_status->status || !value) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*value = script_status->status->maxSector;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_script_status_current_sector_get(const icsneoc2_script_status_t* script_status, uint32_t* value) {
|
|
if(!script_status || !script_status->status || !value) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*value = script_status->status->currentSector;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_script_status_coremini_create_time_get(const icsneoc2_script_status_t* script_status, uint64_t* value) {
|
|
if(!script_status || !script_status->status || !value) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*value = script_status->status->coreminiCreateTime;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_script_status_file_checksum_get(const icsneoc2_script_status_t* script_status, uint16_t* value) {
|
|
if(!script_status || !script_status->status || !value) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*value = script_status->status->fileChecksum;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_script_status_coremini_version_get(const icsneoc2_script_status_t* script_status, uint16_t* value) {
|
|
if(!script_status || !script_status->status || !value) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*value = script_status->status->coreminiVersion;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_script_status_coremini_header_size_get(const icsneoc2_script_status_t* script_status, uint16_t* value) {
|
|
if(!script_status || !script_status->status || !value) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*value = script_status->status->coreminiHeaderSize;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_script_status_diagnostic_error_code_get(const icsneoc2_script_status_t* script_status, uint8_t* value) {
|
|
if(!script_status || !script_status->status || !value) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*value = script_status->status->diagnosticErrorCode;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_script_status_diagnostic_error_code_count_get(const icsneoc2_script_status_t* script_status, uint8_t* value) {
|
|
if(!script_status || !script_status->status || !value) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*value = script_status->status->diagnosticErrorCodeCount;
|
|
return icsneoc2_error_success;
|
|
}
|
|
|
|
icsneoc2_error_t icsneoc2_script_status_max_coremini_size_kb_get(const icsneoc2_script_status_t* script_status, uint16_t* value) {
|
|
if(!script_status || !script_status->status || !value) {
|
|
return icsneoc2_error_invalid_parameters;
|
|
}
|
|
*value = script_status->status->maxCoreminiSizeKB;
|
|
return icsneoc2_error_success;
|
|
}
|