diff --git a/communication/decoder.cpp b/communication/decoder.cpp index 7882488..092ff01 100644 --- a/communication/decoder.cpp +++ b/communication/decoder.cpp @@ -118,6 +118,26 @@ bool Decoder::decode(std::shared_ptr& result, const std::shared_ptrdata.size() < 24) { + report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error); + return false; + } + + result = HardwareCANPacket::DecodeToMessage(packet->data); + if(!result) { + report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error); + return false; // A nullptr was returned, the packet was malformed + } + // Timestamps are in (resolution) ns increments since 1/1/2007 GMT 00:00:00.0000 + // The resolution depends on the device + result->timestamp *= timestampResolution; + result->network = packet->network; + return true; + } case Network::NetID::DeviceStatus: { result = std::make_shared(); result->network = packet->network; diff --git a/device/device.cpp b/device/device.cpp index 639f0f4..6594fa5 100644 --- a/device/device.cpp +++ b/device/device.cpp @@ -470,6 +470,46 @@ optional Device::getDigitalIO(IO type, size_t number /* = 1 */) { report(APIEvent::Type::ValueNotYetPresent, APIEvent::Severity::Error); return backupPowerGood; + case IO::Misc: { + bool found = false; + for(const auto& misc : getMiscIO()) { + if(misc.number == number) { + found = misc.supportsDigitalIn; + break; + } + } + if(!found) + break; // ParameterOutOfRange + + if(number > miscDigital.size()) + break; // ParameterOutOfRange + + if(!miscDigital[number - 1].has_value()) + report(APIEvent::Type::ValueNotYetPresent, APIEvent::Severity::Error); + + return miscDigital[number - 1]; + } + case IO::EMisc: { + bool found = false; + for(const auto& misc : getEMiscIO()) { + if(misc.number == number) { + found = misc.supportsDigitalIn; + break; + } + } + if(!found) + break; // ParameterOutOfRange + + if(number > miscDigital.size()) + break; // ParameterOutOfRange + + // If there is ever a device with overlapping misc IOs and emisc IOs, + // you will need to make a new member variable for the emisc IOs. + if(!miscDigital[number - 1].has_value()) + report(APIEvent::Type::ValueNotYetPresent, APIEvent::Severity::Error); + + return miscDigital[number - 1]; + } }; report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error); @@ -522,12 +562,74 @@ bool Device::setDigitalIO(IO type, size_t number, bool value) { }); case IO::BackupPowerGood: break; // Read-only, return ParameterOutOfRange + case IO::Misc: + case IO::EMisc: + break; // Read-only for the moment, return ParameterOutOfRange }; report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error); return false; } +optional Device::getAnalogIO(IO type, size_t number /* = 1 */) { + if(number == 0) { // Start counting from 1 + report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error); + return false; + } + + std::lock_guard lk(ioMutex); + switch(type) { + case IO::EthernetActivation: + case IO::USBHostPower: + case IO::BackupPowerEnabled: + case IO::BackupPowerGood: + break; + case IO::Misc: { + bool found = false; + for(const auto& misc : getMiscIO()) { + if(misc.number == number) { + found = misc.supportsAnalogIn; + break; + } + } + if(!found) + break; // ParameterOutOfRange + + if(number > miscAnalog.size()) + break; // ParameterOutOfRange + + if(!miscAnalog[number - 1].has_value()) + report(APIEvent::Type::ValueNotYetPresent, APIEvent::Severity::Error); + + return miscAnalog[number - 1]; + } + case IO::EMisc: { + bool found = false; + for(const auto& misc : getEMiscIO()) { + if(misc.number == number) { + found = misc.supportsAnalogIn; + break; + } + } + if(!found) + break; // ParameterOutOfRange + + if(number > miscAnalog.size()) + break; // ParameterOutOfRange + + // If there is ever a device with overlapping misc IOs and emisc IOs, + // you will need to make a new member variable for the emisc IOs. + if(!miscAnalog[number - 1].has_value()) + report(APIEvent::Type::ValueNotYetPresent, APIEvent::Severity::Error); + + return miscAnalog[number - 1]; + } + }; + + report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error); + return nullopt; +} + void Device::addExtension(std::shared_ptr&& extension) { std::lock_guard lk(extensionsLock); extensions.push_back(extension); @@ -552,6 +654,12 @@ void Device::handleInternalMessage(std::shared_ptr message) { case Network::NetID::Reset_Status: latestResetStatus = std::dynamic_pointer_cast(message); break; + case Network::NetID::Device: { + auto canmsg = std::dynamic_pointer_cast(message); + if(canmsg) + handleNeoVIMessage(std::move(canmsg)); + break; + } case Network::NetID::DeviceStatus: // Device Status format is unique per device, so the devices need to decode it themselves handleDeviceStatus(message); @@ -565,6 +673,27 @@ void Device::handleInternalMessage(std::shared_ptr message) { }); } +void Device::handleNeoVIMessage(std::shared_ptr message) { + switch(message->arbid) { + case 0x103: { // Report Message (neoVI FIRE 2) + if(message->data.size() < 34) { + report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::EventWarning); + return; + } + + uint16_t emisc[2]; + memcpy(emisc, message->data.data() + 24, sizeof(emisc)); + std::lock_guard lk(ioMutex); + miscAnalog[0] = (message->data[24] | (uint16_t(message->data[25]) << 8)) * 0.01015511; // In volts now + miscAnalog[1] = (message->data[26] | (uint16_t(message->data[27]) << 8)) * 0.01015511; + miscDigital[0] = message->data[28] & 0x01; + miscDigital[1] = message->data[29] & 0x01; + miscDigital[4] = message->data[30] & 0x01; + miscDigital[5] = message->data[31] & 0x01; + } + } +} + void Device::updateLEDState() { std::vector args {(uint8_t) ledState}; com->sendCommand(Command::UpdateLEDState, args); diff --git a/examples/cpp/simple/src/SimpleExample.cpp b/examples/cpp/simple/src/SimpleExample.cpp index 17f6cf2..4dcc7ed 100644 --- a/examples/cpp/simple/src/SimpleExample.cpp +++ b/examples/cpp/simple/src/SimpleExample.cpp @@ -249,6 +249,19 @@ int main() { ret = device->transmit(ethTxMessage); // This will return false if the device does not support OP (BR) Ethernet 2 std::cout << (ret ? "OK" : "FAIL") << std::endl; + std::vector emisc = device->getEMiscIO(); + if(!emisc.empty()) { + std::cout << "\tReading EMisc values..." << std::endl; + for(const auto& io : emisc) { + icsneo::optional val = device->getAnalogIO(icsneo::IO::EMisc, io.number); + std::cout << "\t\tEMISC" << io.number; + if(val.has_value()) + std::cout << " - OK (" << val.value() << "V)" << std::endl; + else + std::cout << " - FAIL, it may need to be enabled in neoVI Explorer (" << icsneo::GetLastError() << ")" << std::endl; + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Go offline, stop sending and receiving traffic diff --git a/include/icsneo/communication/io.h b/include/icsneo/communication/io.h index 35ad2ac..15c0af3 100644 --- a/include/icsneo/communication/io.h +++ b/include/icsneo/communication/io.h @@ -1,15 +1,30 @@ #ifndef __ICSNEO_IO_H_ #define __ICSNEO_IO_H_ +typedef struct _neomiscio_t { + size_t number; + bool supportsDigitalIn; + bool supportsDigitalOut; + bool supportsAnalogIn; +#ifdef __cplusplus + _neomiscio_t(size_t n, bool dIn = false, bool dOut = false, bool aIn = false) + : number(n), supportsDigitalIn(dIn), supportsDigitalOut(dOut), supportsAnalogIn(aIn) {} +#endif +} neomiscio_t; + #ifdef __cplusplus namespace icsneo { +using MiscIO = neomiscio_t; + enum class IO { EthernetActivation = 0, // The DoIP activation line, 0 is HiZ and 1 is pulled up to VBAT USBHostPower = 1, BackupPowerEnabled = 2, // The FIRE 2's backup super capacitor BackupPowerGood = 3, // Whether or not the FIRE 2's backup super capacitor is charged (read only) + Misc = 4, // General purpose IO on the device + EMisc = 5, // Extended general purpose IO on the device }; // Note that the C API does a static cast between this and neoio_t so keep them in sync! @@ -24,6 +39,8 @@ typedef enum _neoio_t { ICSNEO_IO_USB_HOST_POWER = (1), ICSNEO_IO_BACKUP_POWER_EN = (2), ICSNEO_IO_BACKUP_POWER_GOOD = (3), + ICSNEO_IO_MISC = (4), + ICSNEO_IO_EMISC = (5), } neoio_t; #endif diff --git a/include/icsneo/communication/network.h b/include/icsneo/communication/network.h index 9034365..381856d 100644 --- a/include/icsneo/communication/network.h +++ b/include/icsneo/communication/network.h @@ -263,6 +263,7 @@ public: case NetID::MOST150: return Type::MOST; case NetID::RED: + case NetID::Device: case NetID::Reset_Status: case NetID::DeviceStatus: case NetID::FlexRayControl: @@ -309,7 +310,7 @@ public: static const char* GetNetIDString(NetID netid) { switch(netid) { case NetID::Device: - return "Device"; + return "neoVI"; case NetID::HSCAN: return "HSCAN"; case NetID::MSCAN: diff --git a/include/icsneo/device/device.h b/include/icsneo/device/device.h index 4b38268..91c8e15 100644 --- a/include/icsneo/device/device.h +++ b/include/icsneo/device/device.h @@ -107,6 +107,18 @@ public: */ virtual bool getBackupPowerSupported() const { return false; } + /** + * Retrieve the information about the misc IOs present on this + * device. + */ + virtual std::vector getMiscIO() const { return {}; } + + /** + * Retrieve the information about the emisc IOs present on this + * device. + */ + virtual std::vector getEMiscIO() const { return {}; } + /** * Get the value of a digital IO, returning an empty optional if the * value is not present, the specified IO is not valid for this device, @@ -131,6 +143,16 @@ public: */ bool setDigitalIO(IO type, bool value) { return setDigitalIO(type, 1, value); } + /** + * Get the value of an analog IO, returning an empty optional if the + * value is not present, the specified IO is not valid for this device, + * or if an error occurs. + * + * The index number starts counting at 1 to keep the numbers in sync + * with the numbering on the device, and is set to 1 by default. + */ + optional getAnalogIO(IO type, size_t number = 1); + virtual std::vector> getFlexRayControllers() const { return {}; } const device_eventhandler_t& getEventHandler() const { return report; } @@ -150,6 +172,8 @@ protected: optional usbHostPowerStatus; optional backupPowerEnabled; optional backupPowerGood; + std::array, 6> miscDigital; + std::array, 2> miscAnalog; // START Initialization Functions Device(neodevice_t neodevice = { 0 }) { @@ -251,6 +275,8 @@ private: std::vector supportedTXNetworks; std::vector supportedRXNetworks; + void handleNeoVIMessage(std::shared_ptr message); + enum class LEDState : uint8_t { Offline = 0x04, CoreMiniRunning = 0x08, // This should override "offline" if the CoreMini is running diff --git a/include/icsneo/device/tree/neovifire2/neovifire2.h b/include/icsneo/device/tree/neovifire2/neovifire2.h index 03f66a9..b611a1f 100644 --- a/include/icsneo/device/tree/neovifire2/neovifire2.h +++ b/include/icsneo/device/tree/neovifire2/neovifire2.h @@ -73,6 +73,18 @@ public: size_t getEthernetActivationLineCount() const override { return 1; } size_t getUSBHostPowerCount() const override { return 1; } bool getBackupPowerSupported() const override { return true; } + std::vector getMiscIO() const override { + return { + {5, true, true, false}, + {6, true, true, false} + }; + } + std::vector getEMiscIO() const override { + return { + {1, true, true, true}, + {2, true, true, true} + }; + } protected: NeoVIFIRE2(neodevice_t neodevice) : Device(neodevice) {