mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-09-20 16:08:37 +02:00
Compare commits
5
Commits
05f981b314
...
c1dc61d622
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1dc61d622 | ||
|
|
40d83f3b7c | ||
|
|
60918c4b6d | ||
|
|
749552e443 | ||
|
|
31d4a750d8 |
@@ -439,6 +439,16 @@ icsneoc2_error_t icsneoc2_device_serial_get(const icsneoc2_device_t* device, cha
|
||||
return safe_str_copy(value, value_length, dev->getSerial()) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed;
|
||||
}
|
||||
|
||||
icsneoc2_error_t icsneoc2_device_product_name_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->getProductName()) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed;
|
||||
}
|
||||
|
||||
icsneoc2_error_t icsneoc2_device_pcb_serial_get(const icsneoc2_device_t* device, uint8_t* value, size_t* value_length) {
|
||||
auto res = icsneoc2_device_is_valid(device);
|
||||
if(res != icsneoc2_error_success) {
|
||||
|
||||
@@ -923,6 +923,36 @@ icsneoc2_error_t icsneoc2_settings_external_wifi_antenna_enabled_set(icsneoc2_de
|
||||
return icsneoc2_error_success;
|
||||
}
|
||||
|
||||
icsneoc2_error_t icsneoc2_settings_perf_test_enabled_get(icsneoc2_device_t* device, bool* value) {
|
||||
// Make sure the device is valid
|
||||
auto res = icsneoc2_device_is_valid(device);
|
||||
if(res != icsneoc2_error_success) {
|
||||
return res;
|
||||
}
|
||||
if(!value) {
|
||||
return icsneoc2_error_invalid_parameters;
|
||||
}
|
||||
if(auto result = device->device->settings->isPerfTestEnabled(); result.has_value()) {
|
||||
*value = result.value();
|
||||
return icsneoc2_error_success;
|
||||
} else {
|
||||
*value = false;
|
||||
return icsneoc2_error_get_settings_failure;
|
||||
}
|
||||
}
|
||||
|
||||
icsneoc2_error_t icsneoc2_settings_perf_test_enabled_set(icsneoc2_device_t* device, bool value) {
|
||||
// Make sure the device is valid
|
||||
auto res = icsneoc2_device_is_valid(device);
|
||||
if(res != icsneoc2_error_success) {
|
||||
return res;
|
||||
}
|
||||
if(!device->device->settings->setPerfTestEnable(value)) {
|
||||
return icsneoc2_error_set_settings_failure;
|
||||
}
|
||||
return icsneoc2_error_success;
|
||||
}
|
||||
|
||||
icsneoc2_error_t icsneoc2_settings_linux_configuration_port_get(icsneoc2_device_t* device, icsneoc2_linux_configuration_port_t* value) {
|
||||
// Make sure the device is valid
|
||||
auto res = icsneoc2_device_is_valid(device);
|
||||
|
||||
@@ -133,6 +133,10 @@ EthernetPacketizer::EthernetPacket::EthernetPacket(const uint8_t* data, size_t s
|
||||
|
||||
int EthernetPacketizer::EthernetPacket::loadBytestream(const std::vector<uint8_t>& bytestream) {
|
||||
errorWhileDecodingFromBytestream = 0;
|
||||
if (bytestream.size() < 24) {
|
||||
errorWhileDecodingFromBytestream = 1;
|
||||
return errorWhileDecodingFromBytestream;
|
||||
}
|
||||
for(size_t i = 0; i < 6; i++)
|
||||
destMAC[i] = bytestream[i];
|
||||
for(size_t i = 0; i < 6; i++)
|
||||
|
||||
+17
-14
@@ -737,15 +737,17 @@ bool Device::uploadCoremini(std::istream& stream, Disk::MemoryType memType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto connected = isLogicalDiskConnected();
|
||||
|
||||
if(!connected) {
|
||||
return false; // Already added an API error
|
||||
}
|
||||
|
||||
if(!(*connected)) {
|
||||
report(APIEvent::Type::DiskNotConnected, APIEvent::Severity::Error);
|
||||
return false;
|
||||
if (memType == Disk::MemoryType::SD) {
|
||||
auto connected = isLogicalDiskConnected();
|
||||
|
||||
if(!connected) {
|
||||
return false; // Already added an API error
|
||||
}
|
||||
|
||||
if(!(*connected)) {
|
||||
report(APIEvent::Type::DiskNotConnected, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!stopScript()) {
|
||||
@@ -756,7 +758,6 @@ bool Device::uploadCoremini(std::istream& stream, Disk::MemoryType memType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if(!eraseScriptMemory(memType, static_cast<uint64_t>(bin.size()))) {
|
||||
return false;
|
||||
}
|
||||
@@ -846,10 +847,12 @@ std::optional<CoreminiHeader> Device::readCoreminiHeader(Disk::MemoryType memTyp
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto connected = isLogicalDiskConnected();
|
||||
|
||||
if(!connected) {
|
||||
return std::nullopt; // Already added an API error
|
||||
if (memType == Disk::MemoryType::SD) {
|
||||
auto connected = isLogicalDiskConnected();
|
||||
|
||||
if(!connected) {
|
||||
return std::nullopt; // Already added an API error
|
||||
}
|
||||
}
|
||||
|
||||
#pragma pack(push, 2)
|
||||
|
||||
@@ -90,3 +90,19 @@ TC10
|
||||
.. literalinclude:: ../../examples/c2/tc10/src/main.c
|
||||
:language: c
|
||||
|
||||
gPTP
|
||||
====
|
||||
|
||||
:download:`Download example <../../examples/c2/gptp/src/main.c>`
|
||||
|
||||
.. literalinclude:: ../../examples/c2/gptp/src/main.c
|
||||
:language: c
|
||||
|
||||
PerfTest
|
||||
========
|
||||
|
||||
:download:`Download example <../../examples/c2/perf_test/src/main.c>`
|
||||
|
||||
.. literalinclude:: ../../examples/c2/perf_test/src/main.c
|
||||
:language: c
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ option(LIBICSNEO_BUILD_C2_ETHERNET_RECEIVE_EXAMPLE "Build the C2 ethernet receiv
|
||||
option(LIBICSNEO_BUILD_C2_T1S_LOOPBACK_EXAMPLE "Build the C2 RAD-Comet3 T1S loopback example." ON)
|
||||
option(LIBICSNEO_BUILD_C2_TC10_EXAMPLE "Build the C2 TC10 example." ON)
|
||||
option(LIBICSNEO_BUILD_C2_GPTP_EXAMPLE "Build the C2 gPTP settings example." ON)
|
||||
option(LIBICSNEO_BUILD_C2_PERF_TEST_EXAMPLE "Build the C2 PerfTest setting example." ON)
|
||||
option(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE "Build the simple C++ example." ON)
|
||||
option(LIBICSNEO_BUILD_CPP_DEVICE_INFO_EXAMPLE "Build the C++ device info example." ON)
|
||||
option(LIBICSNEO_BUILD_CPP_INTERACTIVE_EXAMPLE "Build the command-line interactive C++ example." ON)
|
||||
@@ -104,6 +105,10 @@ if(LIBICSNEO_BUILD_C2_GPTP_EXAMPLE)
|
||||
add_subdirectory(c2/gptp)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_BUILD_C2_PERF_TEST_EXAMPLE)
|
||||
add_subdirectory(c2/perf_test)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE)
|
||||
add_subdirectory(cpp/simple)
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
add_executable(libicsneoc2-perf_test-example src/main.c)
|
||||
target_link_libraries(libicsneoc2-perf_test-example icsneoc2-static)
|
||||
|
||||
if(WIN32)
|
||||
target_compile_definitions(libicsneoc2-perf_test-example PRIVATE _CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* PerfTest setting example.
|
||||
*
|
||||
* Opens the first available device, enables the device-global PerfTest mode
|
||||
* via icsneoc2_settings_perf_test_enabled_set(), reads messages for a few
|
||||
* seconds while PerfTest is active, then disables PerfTest again. Each step
|
||||
* reads the value back with icsneoc2_settings_perf_test_enabled_get() to
|
||||
* confirm the change took effect.
|
||||
*/
|
||||
|
||||
#include <icsneo/icsneoc2.h>
|
||||
#include <icsneo/icsneoc2settings.h>
|
||||
#include <icsneo/icsneoc2messages.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
static int print_error_code(const char* message, icsneoc2_error_t error) {
|
||||
char error_str[64] = {0};
|
||||
size_t error_str_len = sizeof(error_str);
|
||||
icsneoc2_error_t res = icsneoc2_error_code_get(error, error_str, &error_str_len);
|
||||
if(res != icsneoc2_error_success) {
|
||||
fprintf(stderr, "%s: failed to get string for error code %u\n", message, error);
|
||||
return (int)res;
|
||||
}
|
||||
fprintf(stderr, "%s: \"%s\" (%u)\n", message, error_str, error);
|
||||
return (int)error;
|
||||
}
|
||||
|
||||
/* Sets PerfTest, applies it to the device, then reads it back to confirm. */
|
||||
static icsneoc2_error_t set_and_verify_perf_test(icsneoc2_device_t* device, bool enable) {
|
||||
icsneoc2_error_t res = icsneoc2_settings_perf_test_enabled_set(device, enable);
|
||||
if(res != icsneoc2_error_success)
|
||||
return res;
|
||||
|
||||
res = icsneoc2_settings_apply(device);
|
||||
if(res != icsneoc2_error_success)
|
||||
return res;
|
||||
|
||||
/* Re-read settings from the device so the read-back reflects what was
|
||||
* actually persisted, not just the local settings buffer. */
|
||||
res = icsneoc2_settings_refresh(device);
|
||||
if(res != icsneoc2_error_success)
|
||||
return res;
|
||||
|
||||
bool value = !enable;
|
||||
res = icsneoc2_settings_perf_test_enabled_get(device, &value);
|
||||
if(res != icsneoc2_error_success)
|
||||
return res;
|
||||
|
||||
printf("\tPerfTest now reads back as: %s\n", value ? "enabled" : "disabled");
|
||||
if(value != enable) {
|
||||
fprintf(stderr, "\tERROR: expected PerfTest %s but device reports %s\n",
|
||||
enable ? "enabled" : "disabled", value ? "enabled" : "disabled");
|
||||
return icsneoc2_error_get_settings_failure;
|
||||
}
|
||||
return icsneoc2_error_success;
|
||||
}
|
||||
|
||||
/* Continuously drains and counts messages for the given wall-clock duration. */
|
||||
static icsneoc2_error_t read_messages_for(icsneoc2_device_t* device, unsigned seconds) {
|
||||
size_t total = 0;
|
||||
printf("\tReading messages for %u seconds...\n", seconds);
|
||||
time_t start = time(NULL);
|
||||
while((time_t)(time(NULL) - start) < (time_t)seconds) {
|
||||
/* Short timeout so an idle bus still lets us re-check the clock,
|
||||
* while a flooding bus is drained as fast as messages arrive. */
|
||||
icsneoc2_message_t* message = NULL;
|
||||
icsneoc2_error_t res = icsneoc2_device_message_get(device, &message, 50);
|
||||
if(res != icsneoc2_error_success)
|
||||
return res;
|
||||
if(message == NULL)
|
||||
continue;
|
||||
++total;
|
||||
icsneoc2_message_free(message);
|
||||
}
|
||||
printf("\tReceived %zu messages in %u seconds.\n", total, seconds);
|
||||
return icsneoc2_error_success;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("Opening first available device...\n");
|
||||
icsneoc2_device_t* device = NULL;
|
||||
icsneoc2_error_t res = icsneoc2_device_open_first(0, icsneoc2_open_options_default, &device);
|
||||
if(res != icsneoc2_error_success)
|
||||
return print_error_code("\tFailed to open first device", res);
|
||||
|
||||
char description[255] = {0};
|
||||
size_t description_length = sizeof(description);
|
||||
res = icsneoc2_device_description_get(device, description, &description_length);
|
||||
if(res != icsneoc2_error_success) {
|
||||
icsneoc2_device_free(device);
|
||||
return print_error_code("\tFailed to get device description", res);
|
||||
}
|
||||
printf("\tOpened device: %s\n", description);
|
||||
|
||||
/* Pull the current settings down from the device before reading/modifying them. */
|
||||
res = icsneoc2_settings_refresh(device);
|
||||
if(res != icsneoc2_error_success)
|
||||
goto cleanup;
|
||||
|
||||
bool initial = false;
|
||||
res = icsneoc2_settings_perf_test_enabled_get(device, &initial);
|
||||
if(res != icsneoc2_error_success) {
|
||||
print_error_code("\tFailed to read initial PerfTest state (device may not support it)", res);
|
||||
goto cleanup;
|
||||
}
|
||||
printf("\tInitial PerfTest state: %s\n", initial ? "enabled" : "disabled");
|
||||
|
||||
printf("Enabling PerfTest...\n");
|
||||
res = set_and_verify_perf_test(device, true);
|
||||
if(res != icsneoc2_error_success) {
|
||||
print_error_code("\tFailed to enable PerfTest", res);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
res = read_messages_for(device, 3);
|
||||
if(res != icsneoc2_error_success) {
|
||||
print_error_code("\tFailed while reading messages", res);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
printf("Disabling PerfTest...\n");
|
||||
res = set_and_verify_perf_test(device, false);
|
||||
if(res != icsneoc2_error_success) {
|
||||
print_error_code("\tFailed to disable PerfTest", res);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
printf("PerfTest enable/read/disable cycle completed successfully.\n");
|
||||
|
||||
cleanup:
|
||||
icsneoc2_device_close(device);
|
||||
icsneoc2_device_free(device);
|
||||
return (int)res;
|
||||
}
|
||||
@@ -96,10 +96,6 @@ protected:
|
||||
return 15*1024*1024;
|
||||
}
|
||||
|
||||
std::optional<MemoryAddress> getCoreminiStartAddressSD() const override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
|
||||
protected:
|
||||
RADComet2(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) {
|
||||
initialize<RADComet2Settings>(makeDriver);
|
||||
initialize<RADComet2Settings, Disk::NeoMemoryDiskDriver, Disk::NeoMemoryDiskDriver>(makeDriver);
|
||||
}
|
||||
|
||||
void setupPacketizer(Packetizer& packetizer) override {
|
||||
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
|
||||
protected:
|
||||
RADComet3(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) {
|
||||
initialize<RADComet3Settings>(makeDriver);
|
||||
initialize<RADComet3Settings, Disk::NeoMemoryDiskDriver, Disk::NeoMemoryDiskDriver>(makeDriver);
|
||||
}
|
||||
|
||||
void setupPacketizer(Packetizer& packetizer) override {
|
||||
|
||||
@@ -131,7 +131,7 @@ protected:
|
||||
}
|
||||
|
||||
std::optional<MemoryAddress> getCoreminiStartAddressFlash() const override {
|
||||
return 512*4;
|
||||
return 92*1024*1024;
|
||||
}
|
||||
|
||||
std::optional<MemoryAddress> getCoreminiStartAddressSD() const override {
|
||||
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
}
|
||||
protected:
|
||||
RADMoonT1S(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) {
|
||||
initialize<RADMoonT1SSettings>(makeDriver);
|
||||
initialize<RADMoonT1SSettings, Disk::NeoMemoryDiskDriver, Disk::NeoMemoryDiskDriver>(makeDriver);
|
||||
}
|
||||
|
||||
void setupPacketizer(Packetizer& packetizer) override {
|
||||
@@ -83,6 +83,10 @@ protected:
|
||||
void setupSupportedTXNetworks(std::vector<Network>& txNetworks) override {
|
||||
setupSupportedRXNetworks(txNetworks);
|
||||
}
|
||||
|
||||
std::optional<MemoryAddress> getCoreminiStartAddressFlash() const override {
|
||||
return 32*1024*1024;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
}
|
||||
protected:
|
||||
RADStar2(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) {
|
||||
initialize<RADStar2Settings>(makeDriver);
|
||||
initialize<RADStar2Settings, Disk::NeoMemoryDiskDriver, Disk::NeoMemoryDiskDriver>(makeDriver);
|
||||
}
|
||||
|
||||
virtual void setupPacketizer(Packetizer& packetizer) override {
|
||||
@@ -80,11 +80,7 @@ protected:
|
||||
void setupSupportedTXNetworks(std::vector<Network>& txNetworks) override { setupSupportedRXNetworks(txNetworks); }
|
||||
|
||||
std::optional<MemoryAddress> getCoreminiStartAddressFlash() const override {
|
||||
return 512*4;
|
||||
}
|
||||
|
||||
std::optional<MemoryAddress> getCoreminiStartAddressSD() const override {
|
||||
return 0;
|
||||
return 14*1024*1024;
|
||||
}
|
||||
|
||||
bool supportsGetAllMACAddresses() const override {
|
||||
|
||||
@@ -331,6 +331,20 @@ icsneoc2_error_t icsneoc2_device_type_get(const icsneoc2_device_t* device, icsne
|
||||
*/
|
||||
icsneoc2_error_t icsneoc2_device_serial_get(const icsneoc2_device_t* device, char* value, size_t* value_length);
|
||||
|
||||
/**
|
||||
* Get the product name of a device.
|
||||
*
|
||||
* This is the device-specific marketing/product name (e.g. "neoVI FIRE 3"), which may differ from the
|
||||
* generic device-type name. Prefer this where a human-readable product name is needed.
|
||||
*
|
||||
* @param[in] device The device to get the product name of.
|
||||
* @param[out] value Pointer to a buffer to copy the product name into. Null terminated.
|
||||
* @param[in,out] value_length Size of the value buffer. Modified with the length of the product name.
|
||||
*
|
||||
* @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise.
|
||||
*/
|
||||
icsneoc2_error_t icsneoc2_device_product_name_get(const icsneoc2_device_t* device, char* value, size_t* value_length);
|
||||
|
||||
/**
|
||||
* Get the PCB serial of a device.
|
||||
*
|
||||
|
||||
@@ -696,6 +696,26 @@ icsneoc2_error_t icsneoc2_settings_external_wifi_antenna_enabled_get(icsneoc2_de
|
||||
*/
|
||||
icsneoc2_error_t icsneoc2_settings_external_wifi_antenna_enabled_set(icsneoc2_device_t* device, bool value);
|
||||
|
||||
/**
|
||||
* Get whether the performance/throughput test mode is enabled.
|
||||
*
|
||||
* @param[in] device The device to query.
|
||||
* @param[out] value Pointer to a bool to copy the value into (true if PerfTest is enabled).
|
||||
*
|
||||
* @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters or icsneoc2_error_get_settings_failure otherwise.
|
||||
*/
|
||||
icsneoc2_error_t icsneoc2_settings_perf_test_enabled_get(icsneoc2_device_t* device, bool* value);
|
||||
|
||||
/**
|
||||
* Set whether the performance/throughput test mode is enabled.
|
||||
*
|
||||
* @param[in] device The device to configure.
|
||||
* @param[in] value true to enable PerfTest, false to disable it.
|
||||
*
|
||||
* @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters or icsneoc2_error_set_settings_failure otherwise.
|
||||
*/
|
||||
icsneoc2_error_t icsneoc2_settings_perf_test_enabled_set(icsneoc2_device_t* device, bool value);
|
||||
|
||||
/**
|
||||
* Get which Ethernet port(s) are reserved for the Linux configuration interface (Fire3 family devices).
|
||||
*
|
||||
|
||||
@@ -214,6 +214,7 @@ TEST(icsneoc2, test_icsneoc2_error_invalid_parameters_and_invalid_device)
|
||||
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_rtc_get(NULL, (int64_t *)&placeholderUnsignedInteger64));
|
||||
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_rtc_set(NULL, 0));
|
||||
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_serial_get(NULL, placeholderStr, &placeholderSizeT));
|
||||
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_product_name_get(NULL, placeholderStr, &placeholderSizeT));
|
||||
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_pcb_serial_get(NULL, &placeholderInteger8, &placeholderSizeT));
|
||||
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_mac_addresses_enumerate(NULL, NULL));
|
||||
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_mac_network_id_get(NULL, NULL));
|
||||
@@ -303,6 +304,8 @@ TEST(icsneoc2, test_icsneoc2_error_invalid_parameters_and_invalid_device)
|
||||
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_settings_linux_boot_enabled_set(NULL, false));
|
||||
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_settings_external_wifi_antenna_enabled_get(NULL, &placeholderBool));
|
||||
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_settings_external_wifi_antenna_enabled_set(NULL, false));
|
||||
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_settings_perf_test_enabled_get(NULL, &placeholderBool));
|
||||
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_settings_perf_test_enabled_set(NULL, false));
|
||||
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_settings_linux_configuration_port_get(NULL, &placeholderLinuxConfigPort));
|
||||
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_settings_linux_configuration_port_set(NULL, placeholderLinuxConfigPort));
|
||||
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_settings_disabled_get(NULL, &placeholderBool));
|
||||
|
||||
Reference in New Issue
Block a user