diff --git a/CMakeLists.txt b/CMakeLists.txt index 249656b..23ff690 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,6 +130,7 @@ set(SRC_FILES communication/packet/flexraypacket.cpp communication/packet/canpacket.cpp communication/packet/ethernetpacket.cpp + communication/packet/versionpacket.cpp communication/packet/iso9141packet.cpp communication/decoder.cpp communication/encoder.cpp diff --git a/communication/communication.cpp b/communication/communication.cpp index 53c4055..d1714ac 100644 --- a/communication/communication.cpp +++ b/communication/communication.cpp @@ -12,6 +12,7 @@ #include "icsneo/communication/message/serialnumbermessage.h" #include "icsneo/communication/message/filter/main51messagefilter.h" #include "icsneo/communication/message/readsettingsmessage.h" +#include "icsneo/communication/message/versionmessage.h" using namespace icsneo; @@ -118,7 +119,6 @@ bool Communication::getSettingsSync(std::vector& data, std::chrono::mil } std::shared_ptr Communication::getSerialNumberSync(std::chrono::milliseconds timeout) { - sendCommand(Command::RequestSerialNumber); std::shared_ptr msg = waitForMessageSync([this]() { return sendCommand(Command::RequestSerialNumber); }, Main51MessageFilter(Command::RequestSerialNumber), timeout); @@ -132,6 +132,37 @@ std::shared_ptr Communication::getSerialNumberSync(std::chr return std::dynamic_pointer_cast(m51); } +optional< std::vector< optional > > Communication::getVersionsSync(std::chrono::milliseconds timeout) { + std::vector< optional > ret; + + std::shared_ptr msg = waitForMessageSync([this]() { + return sendCommand(Command::GetMainVersion); + }, Main51MessageFilter(Command::GetMainVersion), timeout); + if(!msg) // Did not receive a message + return nullopt; + + auto ver = std::dynamic_pointer_cast(msg); + if(!ver) // Could not upcast for some reason + return nullopt; + + if(!ver->MainChip || ver->Versions.size() != 1) + return nullopt; + + ret.push_back(ver->Versions.front()); + + msg = waitForMessageSync([this]() { + return sendCommand(Command::GetSecondaryVersions); + }, Main51MessageFilter(Command::GetSecondaryVersions), timeout); + if(msg) { // This one is allowed to fail + ver = std::dynamic_pointer_cast(msg); + if(ver && !ver->MainChip) { + ret.insert(ret.end(), ver->Versions.begin(), ver->Versions.end()); + } + } + + return ret; +} + int Communication::addMessageCallback(const MessageCallback& cb) { std::lock_guard lk(messageCallbacksLock); messageCallbacks.insert(std::make_pair(messageCallbackIDCounter, cb)); diff --git a/communication/decoder.cpp b/communication/decoder.cpp index 1e76ecc..802316a 100644 --- a/communication/decoder.cpp +++ b/communication/decoder.cpp @@ -10,6 +10,7 @@ #include "icsneo/communication/packet/ethernetpacket.h" #include "icsneo/communication/packet/flexraypacket.h" #include "icsneo/communication/packet/iso9141packet.h" +#include "icsneo/communication/packet/versionpacket.h" #include using namespace icsneo; @@ -179,6 +180,24 @@ bool Decoder::decode(std::shared_ptr& result, const std::shared_ptrdata); + if(!result) { + report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error); + return false; + } + + return true; + } + case Command::GetSecondaryVersions: { + result = HardwareVersionPacket::DecodeSecondaryToMessage(packet->data); + if(!result) { + report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error); + return false; + } + + return true; + } default: auto msg = std::make_shared(); msg->network = packet->network; diff --git a/communication/encoder.cpp b/communication/encoder.cpp index 33d9a90..b1d8e20 100644 --- a/communication/encoder.cpp +++ b/communication/encoder.cpp @@ -153,6 +153,8 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector& result, case Command::RequestSerialNumber: case Command::EnableNetworkCommunication: case Command::EnableNetworkCommunicationEx: + case Command::GetMainVersion: + case Command::GetSecondaryVersions: // There is a firmware handling idiosyncrasy with these commands // They must be encoded in the short format m51msg->forceShortFormat = true; diff --git a/communication/packet/versionpacket.cpp b/communication/packet/versionpacket.cpp new file mode 100644 index 0000000..7725965 --- /dev/null +++ b/communication/packet/versionpacket.cpp @@ -0,0 +1,37 @@ +#include "icsneo/communication/packet/versionpacket.h" + +using namespace icsneo; + +std::shared_ptr HardwareVersionPacket::DecodeMainToMessage(const std::vector& bytestream) { + if(bytestream.size() < 3) // Not enough bytes to decode + return std::shared_ptr(); + + auto msg = std::make_shared(true); + + optional& version = msg->Versions.emplace_back(); + version.emplace(); + version->major = bytestream[1]; + version->minor = bytestream[2]; + + return msg; +} + +std::shared_ptr HardwareVersionPacket::DecodeSecondaryToMessage(const std::vector& bytestream) { + auto msg = std::make_shared(false); + + size_t bytesLeft = bytestream.size(); + if(bytesLeft) + bytesLeft--; // Disregard command byte + while(bytesLeft >= 3) { + const bool versionValid = bytestream[bytestream.size() - bytesLeft + 0]; + optional& version = msg->Versions.emplace_back(); + if(versionValid) { + version.emplace(); + version->major = bytestream[bytestream.size() - bytesLeft + 1]; + version->minor = bytestream[bytestream.size() - bytesLeft + 2]; + } + bytesLeft -= std::min(3, bytesLeft); + } + + return msg; +} diff --git a/device/device.cpp b/device/device.cpp index 1c4f19f..e4e4fd2 100644 --- a/device/device.cpp +++ b/device/device.cpp @@ -267,6 +267,8 @@ bool Device::open(OpenFlags flags, OpenStatusHandler handler) { } APIEvent::Type Device::attemptToBeginCommunication() { + versions.clear(); + if(!afterCommunicationOpen()) { // Very unlikely, at the time of writing this only fails if rawWrite does. // If you're looking for this error, you're probably looking for if(!serial) below. @@ -287,6 +289,13 @@ APIEvent::Type Device::attemptToBeginCommunication() { std::string currentSerial = getNeoDevice().serial; if(currentSerial != serial->deviceSerial) return APIEvent::Type::IncorrectSerialNumber; + + auto maybeVersions = com->getVersionsSync(); + if(!maybeVersions) + return getCommunicationNotEstablishedError(); + else + versions = std::move(*maybeVersions); + return APIEvent::Type::NoErrorFound; } diff --git a/include/icsneo/communication/command.h b/include/icsneo/communication/command.h index a45bce7..62599b2 100644 --- a/include/icsneo/communication/command.h +++ b/include/icsneo/communication/command.h @@ -9,11 +9,13 @@ enum class Command : uint8_t { EnableNetworkCommunication = 0x07, EnableNetworkCommunicationEx = 0x08, RequestSerialNumber = 0xA1, + GetMainVersion = 0xA3, // Previously known as RED_CMD_APP_VERSION_REQ SetSettings = 0xA4, // Previously known as RED_CMD_SET_BAUD_REQ, follow up with SaveSettings to write to EEPROM //GetSettings = 0xA5, // Previously known as RED_CMD_READ_BAUD_REQ, now unused SaveSettings = 0xA6, UpdateLEDState = 0xA7, SetDefaultSettings = 0xA8, // Follow up with SaveSettings to write to EEPROM + GetSecondaryVersions = 0xA9, // Previously known as RED_CMD_PERIPHERALS_APP_VERSION_REQ, versions other than the main chip RequestStatusUpdate = 0xBC, ReadSettings = 0xC7, // Previously known as 3G_READ_SETTINGS_EX MiscControl = 0xE7, diff --git a/include/icsneo/communication/communication.h b/include/icsneo/communication/communication.h index 60859b9..5f428cd 100644 --- a/include/icsneo/communication/communication.h +++ b/include/icsneo/communication/communication.h @@ -9,6 +9,7 @@ #include "icsneo/communication/packet.h" #include "icsneo/communication/message/callback/messagecallback.h" #include "icsneo/communication/message/serialnumbermessage.h" +#include "icsneo/device/deviceversion.h" #include "icsneo/api/eventmanager.h" #include "icsneo/communication/packetizer.h" #include "icsneo/communication/encoder.h" @@ -53,6 +54,7 @@ public: virtual bool sendCommand(Command cmd, std::vector arguments = {}); bool getSettingsSync(std::vector& data, std::chrono::milliseconds timeout = std::chrono::milliseconds(50)); std::shared_ptr getSerialNumberSync(std::chrono::milliseconds timeout = std::chrono::milliseconds(50)); + optional< std::vector< optional > > getVersionsSync(std::chrono::milliseconds timeout = std::chrono::milliseconds(50)); int addMessageCallback(const MessageCallback& cb); bool removeMessageCallback(int id); diff --git a/include/icsneo/communication/message/versionmessage.h b/include/icsneo/communication/message/versionmessage.h new file mode 100644 index 0000000..f6ca828 --- /dev/null +++ b/include/icsneo/communication/message/versionmessage.h @@ -0,0 +1,27 @@ +#ifndef __VERSIONMESSAGE_H_ +#define __VERSIONMESSAGE_H_ + +#ifdef __cplusplus + +#include "icsneo/communication/message/message.h" +#include "icsneo/device/deviceversion.h" +#include "icsneo/platform/optional.h" + +namespace icsneo { + +class VersionMessage : public Message { +public: + VersionMessage(bool main) : MainChip(main) { network = Network::NetID::Main51; } + + // If true, the included version is for the main chip + const bool MainChip; + + // nullopt here indicates invalid + std::vector< optional > Versions; +}; + +} + +#endif // __cplusplus + +#endif \ No newline at end of file diff --git a/include/icsneo/communication/packet/versionpacket.h b/include/icsneo/communication/packet/versionpacket.h new file mode 100644 index 0000000..53abb83 --- /dev/null +++ b/include/icsneo/communication/packet/versionpacket.h @@ -0,0 +1,21 @@ +#ifndef __VERSIONPACKET_H__ +#define __VERSIONPACKET_H__ + +#ifdef __cplusplus + +#include "icsneo/communication/message/versionmessage.h" +#include +#include + +namespace icsneo { + +struct HardwareVersionPacket { + static std::shared_ptr DecodeMainToMessage(const std::vector& bytestream); + static std::shared_ptr DecodeSecondaryToMessage(const std::vector& bytestream); +}; + +} + +#endif // __cplusplus + +#endif \ No newline at end of file diff --git a/include/icsneo/device/device.h b/include/icsneo/device/device.h index 2217bd8..b10fd0b 100644 --- a/include/icsneo/device/device.h +++ b/include/icsneo/device/device.h @@ -15,6 +15,7 @@ #include "icsneo/device/idevicesettings.h" #include "icsneo/device/nullsettings.h" #include "icsneo/device/devicetype.h" +#include "icsneo/device/deviceversion.h" #include "icsneo/communication/communication.h" #include "icsneo/communication/packetizer.h" #include "icsneo/communication/encoder.h" @@ -210,6 +211,11 @@ public: NODISCARD("If the Lifetime is not held, disconnects will be immediately unsuppressed") Lifetime suppressDisconnects(); + /** + * For use by extensions only. A more stable API will be provided in the future. + */ + const std::vector>& getVersions() const { return versions; } + /** * Some alternate communication protocols do not support DFU */ @@ -328,6 +334,7 @@ protected: private: neodevice_t data; std::shared_ptr latestResetStatus; + std::vector> versions; mutable std::mutex extensionsLock; std::vector> extensions; diff --git a/include/icsneo/device/deviceversion.h b/include/icsneo/device/deviceversion.h new file mode 100644 index 0000000..3a6fa48 --- /dev/null +++ b/include/icsneo/device/deviceversion.h @@ -0,0 +1,24 @@ +#ifndef __DEVICEVERSION_H_ +#define __DEVICEVERSION_H_ + +#ifdef __cplusplus + +#include "icsneo/platform/optional.h" +#include +#include + +namespace icsneo { + +struct DeviceAppVersion { + uint8_t major = 0; + uint8_t minor = 0; + + bool operator!=(const DeviceAppVersion& rhs) const { return major != rhs.major || minor != rhs.minor; } +}; + +} // namespace icsneo + + +#endif // __cplusplus + +#endif \ No newline at end of file