From e82b5d15e051a0e930c5956b1f2ed35b615d6b91 Mon Sep 17 00:00:00 2001 From: Paul Hollinsky Date: Sun, 11 Apr 2021 22:13:51 -0400 Subject: [PATCH] Support software controllable termination --- api/icsneoc/icsneoc.cpp | 28 ++++ api/icsneocpp/event.cpp | 9 ++ device/idevicesettings.cpp | 134 ++++++++++++++++++ examples/c/interactive/src/main.c | 47 +++++- .../interactive/src/InteractiveExample.cpp | 41 +++++- include/icsneo/api/event.h | 3 + include/icsneo/device/idevicesettings.h | 74 +++++++++- .../tree/neovifire2/neovifire2settings.h | 25 ++++ .../tree/radgigalog/radgigalogsettings.h | 25 ++++ .../tree/radgigastar/radgigastarsettings.h | 23 +++ .../settings/valuecan4-2elsettings.h | 15 ++ .../valuecan4/settings/valuecan4-2settings.h | 15 ++ .../valuecan4/settings/valuecan4-4settings.h | 21 +++ .../settings/valuecan4industrialsettings.h | 15 ++ .../device/tree/vividcan/vividcansettings.h | 49 +++++++ include/icsneo/icsneoc.h | 70 +++++++++ 16 files changed, 588 insertions(+), 6 deletions(-) diff --git a/api/icsneoc/icsneoc.cpp b/api/icsneoc/icsneoc.cpp index 41dd378..445afc3 100644 --- a/api/icsneoc/icsneoc.cpp +++ b/api/icsneoc/icsneoc.cpp @@ -644,3 +644,31 @@ bool icsneo_setDigitalIO(const neodevice_t* device, neoio_t type, uint32_t numbe return device->device->setDigitalIO(static_cast(type), number, value); } + +bool icsneo_isTerminationSupportedFor(const neodevice_t* device, uint16_t netid) { + if(!icsneo_isValidNeoDevice(device)) + return false; + + return device->device->settings->isTerminationSupportedFor(Network(netid)); +} + +bool icsneo_canTerminationBeEnabledFor(const neodevice_t* device, uint16_t netid) { + if(!icsneo_isValidNeoDevice(device)) + return false; + + return device->device->settings->canTerminationBeEnabledFor(Network(netid)); +} + +bool icsneo_isTerminationEnabledFor(const neodevice_t* device, uint16_t netid) { + if(!icsneo_isValidNeoDevice(device)) + return false; + + return device->device->settings->isTerminationEnabledFor(Network(netid)).value_or(false); +} + +bool icsneo_setTerminationFor(const neodevice_t* device, uint16_t netid, bool enabled) { + if(!icsneo_isValidNeoDevice(device)) + return false; + + return device->device->settings->setTerminationFor(Network(netid), enabled); +} diff --git a/api/icsneocpp/event.cpp b/api/icsneocpp/event.cpp index 79b8548..7773ddc 100644 --- a/api/icsneocpp/event.cpp +++ b/api/icsneocpp/event.cpp @@ -96,6 +96,9 @@ static constexpr const char* CANFD_NOT_SUPPORTED = "This device does not support static constexpr const char* RTR_NOT_SUPPORTED = "RTR is not supported with CANFD."; static constexpr const char* DEVICE_DISCONNECTED = "The device was disconnected."; static constexpr const char* ONLINE_NOT_SUPPORTED = "This device does not support going online."; +static constexpr const char* TERMINATION_NOT_SUPPORTED_DEVICE = "This device does not support software selectable termination."; +static constexpr const char* TERMINATION_NOT_SUPPORTED_NETWORK = "This network does not support software selectable termination on this device."; +static constexpr const char* ANOTHER_IN_TERMINATION_GROUP_ENABLED = "A mutually exclusive network already has termination enabled."; // Transport Errors static constexpr const char* FAILED_TO_READ = "A read operation failed."; @@ -193,6 +196,12 @@ const char* APIEvent::DescriptionForType(Type type) { return DEVICE_DISCONNECTED; case Type::OnlineNotSupported: return ONLINE_NOT_SUPPORTED; + case Type::TerminationNotSupportedDevice: + return TERMINATION_NOT_SUPPORTED_DEVICE; + case Type::TerminationNotSupportedNetwork: + return TERMINATION_NOT_SUPPORTED_NETWORK; + case Type::AnotherInTerminationGroupEnabled: + return ANOTHER_IN_TERMINATION_GROUP_ENABLED; // Transport Errors case Type::FailedToRead: diff --git a/device/idevicesettings.cpp b/device/idevicesettings.cpp index 984a5cd..acf9cb1 100644 --- a/device/idevicesettings.cpp +++ b/device/idevicesettings.cpp @@ -517,6 +517,140 @@ bool IDeviceSettings::setFDBaudrateFor(Network net, int64_t baudrate) { } } +bool IDeviceSettings::isTerminationSupportedFor(Network net) const { + for(const auto& group : getTerminationGroups()) { + for(const auto& supportedNet : group) { + if(net == supportedNet) + return true; + } + } + return false; +} + +bool IDeviceSettings::canTerminationBeEnabledFor(Network net) const { + if(!settingsLoaded) { + report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error); + return false; + } + + if(disabled) { + report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error); + return false; + } + + // Even though we will not be writing here, if the settings are read only the termination will not be enablable + if(readonly) { + report(APIEvent::Type::SettingsReadOnly, APIEvent::Severity::Error); + return false; + } + + // Reference the mutable termination enables as we want to allow a disable/enable within a group without applying + const uint64_t* currentQueuedTerminationEnables = const_cast(this)->getMutableTerminationEnables(); + if(currentQueuedTerminationEnables == nullptr) { + report(APIEvent::Type::TerminationNotSupportedDevice, APIEvent::Severity::Error); + return false; + } + + for(const auto& group : getTerminationGroups()) { + bool found = false; + for(const auto& supportedNet : group) { + if(net == supportedNet) { + found = true; + break; + } + } + + if(found) { + for(const auto& supportedNet : group) { + // Allow termination on the current network even if it's already enabled + if(net == supportedNet) + continue; + + const auto cmNet = supportedNet.getCoreMini(); + if(!cmNet.has_value() || uint64_t(*cmNet) >= 64) { + // Hitting this assert means that a supported network has an invalid CoreMini Network ID + assert(false); + continue; + } + + // If this network is enabled, it excludes the queried network from being enabled + if((*currentQueuedTerminationEnables >> uint64_t(*cmNet)) & 0x1) { + report(APIEvent::Type::AnotherInTerminationGroupEnabled, APIEvent::Severity::Error); + return false; + } + } + return true; + } + } + return false; +} + +optional IDeviceSettings::isTerminationEnabledFor(Network net) const { + if(!settingsLoaded) { + report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error); + return nullopt; + } + + if(disabled) { + report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error); + return nullopt; + } + + const uint64_t* terminationEnables = getTerminationEnables(); + if(terminationEnables == nullptr) { + report(APIEvent::Type::TerminationNotSupportedDevice, APIEvent::Severity::Error); + return nullopt; + } + + const auto cmNet = net.getCoreMini(); + if(!cmNet.has_value() || uint64_t(*cmNet) >= 64 || !isTerminationSupportedFor(net)) { + report(APIEvent::Type::TerminationNotSupportedNetwork, APIEvent::Severity::Error); + return nullopt; + } + + return (*terminationEnables >> uint64_t(*cmNet)) & 0x1; +} + +bool IDeviceSettings::setTerminationFor(Network net, bool enabled) { + if(!settingsLoaded) { + report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error); + return false; + } + + if(disabled) { + report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error); + return false; + } + + if(readonly) { + report(APIEvent::Type::SettingsReadOnly, APIEvent::Severity::Error); + return false; + } + + uint64_t* terminationEnables = getMutableTerminationEnables(); + if(terminationEnables == nullptr) { + report(APIEvent::Type::TerminationNotSupportedDevice, APIEvent::Severity::Error); + return false; + } + + // This function reports its own error statuses + if(!canTerminationBeEnabledFor(net)) + return false; + + const auto cmNet = net.getCoreMini(); + if(!cmNet.has_value() || uint8_t(*cmNet) >= 64) { + report(APIEvent::Type::TerminationNotSupportedNetwork, APIEvent::Severity::Error); + return false; + } + + const uint64_t mask = 1ull << uint8_t(*cmNet); + if(enabled) + *terminationEnables |= mask; + else + *terminationEnables &= ~mask; + return true; +} + template bool IDeviceSettings::applyStructure(const T& newStructure) { if(!settingsLoaded) { report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error); diff --git a/examples/c/interactive/src/main.c b/examples/c/interactive/src/main.c index f3dad6a..509d36a 100644 --- a/examples/c/interactive/src/main.c +++ b/examples/c/interactive/src/main.c @@ -87,6 +87,7 @@ void printMainMenu() { printf("I - Set HS CAN to 250K\n"); printf("J - Set HS CAN to 500K\n"); printf("L - Set Digital IO\n"); + printf("M - Set HS CAN termination\n"); printf("X - Exit\n"); } @@ -236,7 +237,7 @@ int main() { while(true) { printMainMenu(); printf("\n"); - char input = getCharInput(24, 'A', 'a', 'B', 'b', 'C', 'c', 'D', 'd', 'E', 'e', 'F', 'f', 'G', 'g', 'H', 'h', 'I', 'i', 'J', 'j', 'L', 'l', 'X', 'x'); + char input = getCharInput(26, 'A', 'a', 'B', 'b', 'C', 'c', 'D', 'd', 'E', 'e', 'F', 'f', 'G', 'g', 'H', 'h', 'I', 'i', 'J', 'j', 'L', 'l', 'M', 'm', 'X', 'x'); printf("\n"); switch(input) { // List current devices @@ -699,6 +700,50 @@ int main() { } } break; + // Set HS CAN termination + case 'M': + case 'm': + { + // Select a device and get its description + if(numDevices == 0) { + printf("No devices found! Please scan for new devices.\n\n"); + break; + } + selectedDevice = selectDevice(); + + // Get the product description for the device + char productDescription[ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION] = {}; + size_t descriptionLength = ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION; + icsneo_describeDevice(selectedDevice, productDescription, &descriptionLength); + + printf("Termination is "); + const bool val = icsneo_isTerminationEnabledFor(selectedDevice, ICSNEO_NETID_HSCAN); + neoevent_t err = {}; + if(icsneo_getLastError(&err)) { + printf("not available at this time: %s\n\n", err.description); + break; + } + printf(val ? "currently enabled\n" : "currently disabled\n"); + + printf("[0] Disable\n[1] Enable\n[2] Cancel\n\n"); + char selection2 = getCharInput(3, '0', '1', '2'); + printf("\n"); + + if(selection2 == '2') { + printf("Canceling!\n\n"); + break; + } + + // Attempt to set baudrate and apply settings + if(icsneo_setTerminationFor(selectedDevice, ICSNEO_NETID_HSCAN, selection2 == '1') && icsneo_settingsApply(selectedDevice)) { + printf("Successfully set HS CAN termination for %s!\n\n", productDescription); + } else { + printf("Failed to set HS CAN termination for %s!\n\n", productDescription); + printLastError(); + printf("\n"); + } + } + break; // Exit case 'X': case 'x': diff --git a/examples/cpp/interactive/src/InteractiveExample.cpp b/examples/cpp/interactive/src/InteractiveExample.cpp index 7659237..add2a84 100644 --- a/examples/cpp/interactive/src/InteractiveExample.cpp +++ b/examples/cpp/interactive/src/InteractiveExample.cpp @@ -43,6 +43,7 @@ void printMainMenu() { std::cout << "J - Set LSFT CAN to 250K" << std::endl; std::cout << "K - Add/Remove a message callback" << std::endl; std::cout << "L - Set Digital IO" << std::endl; + std::cout << "M - Set HS CAN termination" << std::endl; std::cout << "X - Exit" << std::endl; } @@ -192,7 +193,7 @@ int main() { while(true) { printMainMenu(); std::cout << std::endl; - char input = getCharInput(std::vector {'A', 'a', 'B', 'b', 'C', 'c', 'D', 'd', 'E', 'e', 'F', 'f', 'G', 'g', 'H', 'h', 'I', 'i', 'J', 'j', 'K', 'k', 'L', 'l', 'X', 'x'}); + char input = getCharInput(std::vector {'A', 'a', 'B', 'b', 'C', 'c', 'D', 'd', 'E', 'e', 'F', 'f', 'G', 'g', 'H', 'h', 'I', 'i', 'J', 'j', 'K', 'k', 'L', 'l', 'M', 'm', 'X', 'x'}); std::cout << std::endl; switch(input) { @@ -721,6 +722,44 @@ int main() { std::cout << "Failure! (" << icsneo::GetLastError() << ")" << std::endl << std::endl; } break; + // Set HS CAN termination + case 'M': + case 'm': + { + // Select a device and get its description + if(devices.size() == 0) { + std::cout << "No devices found! Please scan for new devices." << std::endl << std::endl; + break; + } + selectedDevice = selectDevice(devices); + + std::cout << "Termination is "; + const auto val = selectedDevice->settings->isTerminationEnabledFor(icsneo::Network::NetID::HSCAN); + if(!val.has_value()) { + std::cout << "not available at this time: " << icsneo::GetLastError() << std::endl << std::endl; + break; + } + std::cout << (*val ? "currently enabled" : "currently disabled") << std::endl; + + std::cout << "[0] Disable\n[1] Enable\n[2] Cancel" << std::endl << std::endl; + char selection2 = getCharInput({ '0', '1', '2' }); + std::cout << std::endl; + + if(selection2 == '2') { + std::cout << "Canceling!" << std::endl << std::endl; + break; + } + + // Attempt to set termination and apply settings + if(selectedDevice->settings->setTerminationFor(icsneo::Network::NetID::HSCAN, selection2 == '1') && selectedDevice->settings->apply()) { + std::cout << "Successfully set HS CAN termination for " << selectedDevice->describe() << std::endl; + } else { + std::cout << "Failed to set HS CAN termination for " << selectedDevice->describe() << std::endl; + std::cout << icsneo::GetLastError() << std::endl;; + } + std::cout << std::endl; + } + break; // Exit case 'X': case 'x': diff --git a/include/icsneo/api/event.h b/include/icsneo/api/event.h index a9f5bf0..3be2404 100644 --- a/include/icsneo/api/event.h +++ b/include/icsneo/api/event.h @@ -73,6 +73,9 @@ public: RTRNotSupported = 0x2021, DeviceDisconnected = 0x2022, OnlineNotSupported = 0x2023, + TerminationNotSupportedDevice = 0x2024, + TerminationNotSupportedNetwork = 0x2025, + AnotherInTerminationGroupEnabled = 0x2026, // Transport Events FailedToRead = 0x3000, diff --git a/include/icsneo/device/idevicesettings.h b/include/icsneo/device/idevicesettings.h index 3a34381..ae52020 100644 --- a/include/icsneo/device/idevicesettings.h +++ b/include/icsneo/device/idevicesettings.h @@ -580,6 +580,7 @@ typedef struct { #ifdef __cplusplus #include "icsneo/communication/communication.h" +#include "icsneo/platform/optional.h" #include #include @@ -587,6 +588,8 @@ namespace icsneo { class IDeviceSettings { public: + using TerminationGroup = std::vector; + static constexpr uint16_t GS_VERSION = 5; static uint16_t CalculateGSChecksum(const std::vector& settings); static CANBaudrate GetEnumValueForBaudrate(int64_t baudrate); @@ -595,11 +598,11 @@ public: IDeviceSettings(std::shared_ptr com, size_t size) : com(com), report(com->report), structSize(size) {} virtual ~IDeviceSettings() {} bool ok() { return !disabled && settingsLoaded; } - - bool refresh(bool ignoreChecksum = false); // Get from device + + virtual bool refresh(bool ignoreChecksum = false); // Get from device // Send to device, if temporary device keeps settings in volatile RAM until power cycle, otherwise saved to EEPROM - bool apply(bool temporary = false); + virtual bool apply(bool temporary = false); bool applyDefaults(bool temporary = false); virtual int64_t getBaudrateFor(Network net) const; @@ -648,6 +651,59 @@ public: return reinterpret_cast((void*)(settings.data() + (offset - settingsInDeviceRAM.data()))); } + /** + * Some devices have groupings of networks, where software + * switchable termination can only be applied to one network + * in the group at a time. This function returns those groups + * for the given device. + * + * If a device does not support CAN Termination, an empty vector + * is returned. + */ + virtual std::vector getTerminationGroups() const { return {}; } + + /** + * Check whether software switchable termination is supported + * for a given network on this device. + * + * This does not check whether another network in the termination + * group has termination enabled, check canTerminationBeEnabledFor + * for that. + */ + bool isTerminationSupportedFor(Network net) const; + + /** + * Check whether software switchable termination can currently + * be enabled for a given network. If another network in the + * group is already enabled, or if termination is not supported + * on this network, false is returned and an error will have + * been reported in icsneo::getLastError(). + */ + bool canTerminationBeEnabledFor(Network net) const; + + /** + * Check whether software switchable termination is currently + * enabled for a given network in the currently active device settings. + * + * Note that if the termination status is set, but not yet + * applied to the device, the current device status will be + * reflected here rather than the pending status. + */ + optional isTerminationEnabledFor(Network net) const; + + /** + * Enable or disable software switchable termination for a + * given network. + * + * All other networks in the termination group must be disabled + * prior to the call, but the change does not need to be applied + * to the device before enqueing the enable. + * + * Returns true if the call was successful, otherwise an error + * will have been reported in icsneo::getLastError(). + */ + bool setTerminationFor(Network net, bool enabled); + const void* getRawStructurePointer() const { return settingsInDeviceRAM.data(); } void* getMutableRawStructurePointer() { return settings.data(); } template const T* getStructurePointer() const { return reinterpret_cast(getRawStructurePointer()); } @@ -659,7 +715,7 @@ public: // if settings are disabled for this device. always false unless constructed null bool disabled = false; - + bool readonly = false; bool disableGSChecksumming = false; @@ -679,6 +735,16 @@ protected: typedef void* warn_t; IDeviceSettings(warn_t createInoperableSettings, std::shared_ptr com) : disabled(true), readonly(true), report(com->report), structSize(0) { (void)createInoperableSettings; } + + virtual const uint64_t* getTerminationEnables() const { return nullptr; } + virtual uint64_t* getMutableTerminationEnables() { + if(disabled || readonly) + return nullptr; + const uint8_t* offset = (const uint8_t*)getTerminationEnables(); + if(offset == nullptr) + return nullptr; + return reinterpret_cast((void*)(settings.data() + (offset - settingsInDeviceRAM.data()))); + } }; } diff --git a/include/icsneo/device/tree/neovifire2/neovifire2settings.h b/include/icsneo/device/tree/neovifire2/neovifire2settings.h index 0e028bd..b56d3be 100644 --- a/include/icsneo/device/tree/neovifire2/neovifire2settings.h +++ b/include/icsneo/device/tree/neovifire2/neovifire2settings.h @@ -191,6 +191,31 @@ public: return nullptr; } } + + virtual std::vector getTerminationGroups() const override { + return { + { + Network(Network::NetID::HSCAN), + Network(Network::NetID::HSCAN3), + Network(Network::NetID::HSCAN5), + Network(Network::NetID::HSCAN7) + }, + { + Network(Network::NetID::MSCAN), + Network(Network::NetID::HSCAN2), + Network(Network::NetID::HSCAN4), + Network(Network::NetID::HSCAN6) + } + }; + } + +protected: + const uint64_t* getTerminationEnables() const override { + auto cfg = getStructurePointer(); + if(cfg == nullptr) + return nullptr; + return &cfg->termination_enables; + } }; } diff --git a/include/icsneo/device/tree/radgigalog/radgigalogsettings.h b/include/icsneo/device/tree/radgigalog/radgigalogsettings.h index d26eb5c..3a146aa 100644 --- a/include/icsneo/device/tree/radgigalog/radgigalogsettings.h +++ b/include/icsneo/device/tree/radgigalog/radgigalogsettings.h @@ -152,6 +152,31 @@ public: return nullptr; } } + + virtual std::vector getTerminationGroups() const override { + return { + { + Network(Network::NetID::HSCAN), + Network(Network::NetID::HSCAN3), + Network(Network::NetID::HSCAN5), + Network(Network::NetID::HSCAN7) + }, + { + Network(Network::NetID::MSCAN), + Network(Network::NetID::HSCAN2), + Network(Network::NetID::HSCAN4), + Network(Network::NetID::HSCAN6) + } + }; + } + +protected: + const uint64_t* getTerminationEnables() const override { + auto cfg = getStructurePointer(); + if(cfg == nullptr) + return nullptr; + return &cfg->termination_enables; + } }; } diff --git a/include/icsneo/device/tree/radgigastar/radgigastarsettings.h b/include/icsneo/device/tree/radgigastar/radgigastarsettings.h index ec3b59e..5460daa 100644 --- a/include/icsneo/device/tree/radgigastar/radgigastarsettings.h +++ b/include/icsneo/device/tree/radgigastar/radgigastarsettings.h @@ -133,6 +133,29 @@ public: return nullptr; } } + + virtual std::vector getTerminationGroups() const override { + return { + { + Network(Network::NetID::HSCAN), + Network(Network::NetID::HSCAN2), + Network(Network::NetID::HSCAN3), + Network(Network::NetID::HSCAN4) + }, + { + Network(Network::NetID::MSCAN), + Network(Network::NetID::HSCAN5) + } + }; + } + +protected: + const uint64_t* getTerminationEnables() const override { + auto cfg = getStructurePointer(); + if(cfg == nullptr) + return nullptr; + return &cfg->termination_enables; + } }; typedef struct { diff --git a/include/icsneo/device/tree/valuecan4/settings/valuecan4-2elsettings.h b/include/icsneo/device/tree/valuecan4/settings/valuecan4-2elsettings.h index 02dc9b2..7fba7c1 100644 --- a/include/icsneo/device/tree/valuecan4/settings/valuecan4-2elsettings.h +++ b/include/icsneo/device/tree/valuecan4/settings/valuecan4-2elsettings.h @@ -37,6 +37,21 @@ public: return nullptr; } } + + virtual std::vector getTerminationGroups() const override { + return { + { Network(Network::NetID::HSCAN) }, + { Network(Network::NetID::HSCAN2) } + }; + } + +protected: + const uint64_t* getTerminationEnables() const override { + auto cfg = getStructurePointer(); + if(cfg == nullptr) + return nullptr; + return &cfg->termination_enables; + } }; } diff --git a/include/icsneo/device/tree/valuecan4/settings/valuecan4-2settings.h b/include/icsneo/device/tree/valuecan4/settings/valuecan4-2settings.h index f24abaf..353fef5 100644 --- a/include/icsneo/device/tree/valuecan4/settings/valuecan4-2settings.h +++ b/include/icsneo/device/tree/valuecan4/settings/valuecan4-2settings.h @@ -36,6 +36,21 @@ public: return nullptr; } } + + virtual std::vector getTerminationGroups() const override { + return { + { Network(Network::NetID::HSCAN) }, + { Network(Network::NetID::HSCAN2) } + }; + } + +protected: + const uint64_t* getTerminationEnables() const override { + auto cfg = getStructurePointer(); + if(cfg == nullptr) + return nullptr; + return &cfg->termination_enables; + } }; } diff --git a/include/icsneo/device/tree/valuecan4/settings/valuecan4-4settings.h b/include/icsneo/device/tree/valuecan4/settings/valuecan4-4settings.h index 11b1350..5c64eb9 100644 --- a/include/icsneo/device/tree/valuecan4/settings/valuecan4-4settings.h +++ b/include/icsneo/device/tree/valuecan4/settings/valuecan4-4settings.h @@ -45,6 +45,27 @@ public: return nullptr; } } + + virtual std::vector getTerminationGroups() const override { + return { + { + Network(Network::NetID::HSCAN), + Network(Network::NetID::HSCAN3) + }, + { + Network(Network::NetID::HSCAN2), + Network(Network::NetID::HSCAN4) + } + }; + } + +protected: + const uint64_t* getTerminationEnables() const override { + auto cfg = getStructurePointer(); + if(cfg == nullptr) + return nullptr; + return &cfg->termination_enables; + } }; } diff --git a/include/icsneo/device/tree/valuecan4/settings/valuecan4industrialsettings.h b/include/icsneo/device/tree/valuecan4/settings/valuecan4industrialsettings.h index d566976..9a6a722 100644 --- a/include/icsneo/device/tree/valuecan4/settings/valuecan4industrialsettings.h +++ b/include/icsneo/device/tree/valuecan4/settings/valuecan4industrialsettings.h @@ -37,6 +37,21 @@ public: return nullptr; } } + + virtual std::vector getTerminationGroups() const override { + return { + { Network(Network::NetID::HSCAN) }, + { Network(Network::NetID::HSCAN2) } + }; + } + +protected: + const uint64_t* getTerminationEnables() const override { + auto cfg = getStructurePointer(); + if(cfg == nullptr) + return nullptr; + return &cfg->termination_enables; + } }; } diff --git a/include/icsneo/device/tree/vividcan/vividcansettings.h b/include/icsneo/device/tree/vividcan/vividcansettings.h index 375fd81..dc5f092 100644 --- a/include/icsneo/device/tree/vividcan/vividcansettings.h +++ b/include/icsneo/device/tree/vividcan/vividcansettings.h @@ -82,6 +82,55 @@ public: return nullptr; } } + + virtual std::vector getTerminationGroups() const override { + return { + { Network(Network::NetID::HSCAN) } + }; + } + + bool refresh(bool ignoreChecksum = false) override { + // Because VividCAN uses a nonstandard 16-bit termination_enables + // we need to keep the standard 64-bit values in memory and update + // the structure when applying + if(!IDeviceSettings::refresh(ignoreChecksum)) + return false; + auto cfg = getStructurePointer(); + if(cfg == nullptr) + return false; + activeTerminationEnables = queuedTerminationEnables = cfg->termination_enables; + return true; + } + + bool apply(bool permanent = true) override { + auto cfg = getMutableStructurePointer(); + if(cfg) + cfg->termination_enables = uint16_t(queuedTerminationEnables & 0xFFFF); + + const bool success = IDeviceSettings::apply(permanent); + if(success) + activeTerminationEnables = cfg->termination_enables; + return success; + } + +protected: + const uint64_t* getTerminationEnables() const override { + // Check the structure pointer even though we're not using it so + // all of the other checks that go along with it are performed + if(getStructurePointer() == nullptr) + return nullptr; + return &activeTerminationEnables; + } + + uint64_t* getMutableTerminationEnables() override { + if(getMutableStructurePointer() == nullptr) + return nullptr; + return &queuedTerminationEnables; + } + +private: + uint64_t queuedTerminationEnables = 0; + uint64_t activeTerminationEnables = 0; }; } diff --git a/include/icsneo/icsneoc.h b/include/icsneo/icsneoc.h index 77003cc..e8db4d3 100644 --- a/include/icsneo/icsneoc.h +++ b/include/icsneo/icsneoc.h @@ -738,6 +738,60 @@ extern bool DLLExport icsneo_getDigitalIO(const neodevice_t* device, neoio_t typ */ extern bool DLLExport icsneo_setDigitalIO(const neodevice_t* device, neoio_t type, uint32_t number, bool value); +/** + * \brief Check whether software switchable termination is supported for a given network on this device. + * \param[in] device A pointer to the neodevice_t structure specifying the device to operate on. + * \param[in] netid The network ID to check + * \returns True if software switchable termination is supported + * + * This does not check whether another network in the termination + * group has termination enabled, check canTerminationBeEnabledFor + * for that. + */ +extern bool DLLExport icsneo_isTerminationSupportedFor(const neodevice_t* device, uint16_t netid); + +/** + * \brief Check whether software switchable termination can currently be enabled for a given network. + * \param[in] device A pointer to the neodevice_t structure specifying the device to operate on. + * \param[in] netid The network ID to check + * \returns True if software switchable termination can currently be enabled + * + * If another network in the group is already enabled, or if + * termination is not supported on this network, false is + * returned and an error will have been reported in + * icsneo_getLastError(). + */ +extern bool DLLExport icsneo_canTerminationBeEnabledFor(const neodevice_t* device, uint16_t netid); + +/** + * \brief Check whether software switchable termination is currently + * enabled for a given network in the currently active device settings. + * \param[in] device A pointer to the neodevice_t structure specifying the device to operate on. + * \param[in] netid The network ID to check + * \returns True if software switchable termination is currently enabled + * + * Note that if the termination status is set, but not yet + * applied to the device, the current device status will be + * reflected here rather than the pending status. + * + * False will be returned and an error will be set in + * icsneo_getLastError if the setting is unreadable. + */ +extern bool DLLExport icsneo_isTerminationEnabledFor(const neodevice_t* device, uint16_t netid); + +/** + * \brief Enable or disable software switchable termination for a given network. + * \param[in] device A pointer to the neodevice_t structure specifying the device to operate on. + * \param[in] netid The network ID to affect + * \param[in] enabled Whether to enable or disable switchable termination + * \returns True if if the call was successful, otherwise an error will have been reported in icsneo_getLastError(). + * + * All other networks in the termination group must be disabled + * prior to the call, but the change does not need to be applied + * to the device before enqueing the enable. + */ +extern bool DLLExport icsneo_setTerminationFor(const neodevice_t* device, uint16_t netid, bool enabled); + #ifdef __cplusplus } // extern "C" #endif @@ -894,6 +948,18 @@ fn_icsneo_getDigitalIO icsneo_getDigitalIO; typedef bool(*fn_icsneo_setDigitalIO)(const neodevice_t* device, neoio_t type, uint32_t number, bool value); fn_icsneo_setDigitalIO icsneo_setDigitalIO; +typedef bool(*fn_icsneo_isTerminationSupportedFor)(const neodevice_t* device, uint16_t netid); +fn_icsneo_isTerminationSupportedFor icsneo_isTerminationSupportedFor; + +typedef bool(*fn_icsneo_canTerminationBeEnabledFor)(const neodevice_t* device, uint16_t netid); +fn_icsneo_canTerminationBeEnabledFor icsneo_canTerminationBeEnabledFor; + +typedef bool(*fn_icsneo_isTerminationEnabledFor)(const neodevice_t* device, uint16_t netid); +fn_icsneo_isTerminationEnabledFor icsneo_isTerminationEnabledFor; + +typedef bool(*fn_icsneo_setTerminationFor)(const neodevice_t* device, uint16_t netid, bool enabled); +fn_icsneo_setTerminationFor icsneo_setTerminationFor; + #define ICSNEO_IMPORT(func) func = (fn_##func)icsneo_dynamicLibraryGetFunction(icsneo_libraryHandle, #func) #define ICSNEO_IMPORTASSERT(func) if((ICSNEO_IMPORT(func)) == NULL) return 3 void* icsneo_libraryHandle = NULL; @@ -958,6 +1024,10 @@ int icsneo_init() { ICSNEO_IMPORTASSERT(icsneo_getTimestampResolution); ICSNEO_IMPORTASSERT(icsneo_getDigitalIO); ICSNEO_IMPORTASSERT(icsneo_setDigitalIO); + ICSNEO_IMPORTASSERT(icsneo_isTerminationSupportedFor); + ICSNEO_IMPORTASSERT(icsneo_canTerminationBeEnabledFor); + ICSNEO_IMPORTASSERT(icsneo_isTerminationEnabledFor); + ICSNEO_IMPORTASSERT(icsneo_setTerminationFor); icsneo_initialized = true; return 0;