From b3bbf91e8cc5c902d1347986017397ab990858d4 Mon Sep 17 00:00:00 2001 From: Joseph Niksa Date: Thu, 20 Apr 2023 18:37:05 +0000 Subject: [PATCH] icsneolegacy: Implemented get() and set() RTC functions --- api/icsneoc/icsneoc.cpp | 25 +++++++ api/icsneolegacy/icsneolegacy.cpp | 49 +++++++++++++ device/device.cpp | 88 ++++++++++++++++++++--- examples/cpp/simple/src/SimpleExample.cpp | 36 +++++++++- include/icsneo/communication/command.h | 2 + include/icsneo/communication/network.h | 5 ++ include/icsneo/device/device.h | 5 +- include/icsneo/icsnVC40.h | 10 +++ include/icsneo/icsneoc.h | 18 +++++ include/icsneo/icsneolegacy.h | 4 ++ 10 files changed, 229 insertions(+), 13 deletions(-) diff --git a/api/icsneoc/icsneoc.cpp b/api/icsneoc/icsneoc.cpp index ebad927..2ee9d3e 100644 --- a/api/icsneoc/icsneoc.cpp +++ b/api/icsneoc/icsneoc.cpp @@ -689,3 +689,28 @@ bool icsneo_setTerminationFor(const neodevice_t* device, neonetid_t netid, bool return device->device->settings->setTerminationFor(Network(netid), enabled); } + +bool icsneo_getRTC(const neodevice_t* device, uint64_t* output) +{ + if(!icsneo_isValidNeoDevice(device)) + return false; + + const std::optional> rtc = device->device->getRTC(); + if(!rtc) + return false; + + auto duration = std::chrono::duration_cast(rtc->time_since_epoch()); + *output = static_cast(duration.count()); + + return true; +} + +bool icsneo_setRTC(const neodevice_t* device, uint64_t input) +{ + if(!icsneo_isValidNeoDevice(device)) + return false; + + std::chrono::seconds duration(input); + const std::chrono::system_clock::time_point time(duration); + return device->device->setRTC(time); +} diff --git a/api/icsneolegacy/icsneolegacy.cpp b/api/icsneolegacy/icsneolegacy.cpp index eabcaaa..6e8dd48 100644 --- a/api/icsneolegacy/icsneolegacy.cpp +++ b/api/icsneolegacy/icsneolegacy.cpp @@ -20,6 +20,7 @@ #ifdef _MSC_VER #pragma warning(disable : 4100) // unreferenced formal parameter +#pragma warning(disable : 4996) // STL time functions #endif using namespace icsneo; @@ -559,6 +560,54 @@ void LegacyDLLExport icsneoSetISO15765RxParameters(void* hObject, int lNetwork, return; } +int LegacyDLLExport icsneoGetRTC(void* hObject, icsSpyTime* time) +{ + if(!icsneoValidateHObject(hObject)) + return false; + neodevice_t* device = reinterpret_cast(hObject); + + uint64_t time64 = 0; + if(!icsneo_getRTC(device, &time64)) + return false; + + std::time_t seconds = time64; + /* To accommodate local time bugzilla #6600 https://intrepidcs.homeip.net:100/bugzilla/show_bug.cgi?id=6600 */ + // local time must be used here + const auto timeInfo = std::localtime(&seconds); + if(!timeInfo) + return false; + + time->sec = (unsigned char)timeInfo->tm_sec; // Will never hit 60 (leap second) because tm_sec comes from RTCCTIME + time->min = (unsigned char)timeInfo->tm_min; + time->hour = (unsigned char)timeInfo->tm_hour; + time->day = (unsigned char)timeInfo->tm_mday; + time->month = (unsigned char)timeInfo->tm_mon + 1; + time->year = (unsigned char)timeInfo->tm_year % 100; + + return true; +} + +int LegacyDLLExport icsneoSetRTC(void* hObject, const icsSpyTime* time) +{ + if(!icsneoValidateHObject(hObject)) + return false; + neodevice_t* device = reinterpret_cast(hObject); + + std::tm timeInfo{}; + timeInfo.tm_sec = time->sec; + timeInfo.tm_min = time->min; + timeInfo.tm_hour = time->hour; + timeInfo.tm_mday = time->day; + timeInfo.tm_mon = time->month - 1; + timeInfo.tm_year = time->year + 100; + + #ifdef _MSC_VER + #define timegm _mkgmtime + #endif + + return icsneo_setRTC(device, (uint64_t)timegm(&timeInfo)); +} + //Device Functions int LegacyDLLExport icsneoGetConfiguration(void* hObject, unsigned char* pData, int* lNumBytes) { diff --git a/device/device.cpp b/device/device.cpp index 76b50d2..6a731d8 100644 --- a/device/device.cpp +++ b/device/device.cpp @@ -1,20 +1,27 @@ -#include "icsneo/device/device.h" -#include "icsneo/communication/message/callback/messagecallback.h" +#include #include "icsneo/api/eventmanager.h" -#include "icsneo/communication/command.h" +#include "icsneo/communication/message/extendedresponsemessage.h" +#include "icsneo/device/device.h" #include "icsneo/device/extensions/deviceextension.h" #include "icsneo/disk/fat.h" -#include "icsneo/communication/packet/wivicommandpacket.h" -#include "icsneo/communication/message/wiviresponsemessage.h" -#include "icsneo/communication/message/scriptstatusmessage.h" -#include "icsneo/communication/message/extendedresponsemessage.h" -#include -#include -#include -#include + +#ifdef _MSC_VER +#pragma warning(disable : 4996) // STL time functions +#endif using namespace icsneo; +struct RTCCTIME { + uint8_t FracSec;// --- fractions of seconds (00-99). Note that you can write only 0x00 here! + uint8_t Sec;// --- Seconds (00-59) + uint8_t Min;// --- (00-59) + uint8_t Hour;// --- (00-23) + uint8_t DOW;// --- (01-07) + uint8_t Day;// --- (01-31) + uint8_t Month;// --- (01-12) + uint8_t Year;// --- (00-99) +}; + static const uint8_t fromBase36Table[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15, @@ -1686,3 +1693,62 @@ std::optional Device::SetCollectionUploaded(uint32_t collectionEntryByteAd // Valid device with a properly formed respose, return success return std::make_optional(success); } + +std::optional> Device::getRTC() +{ + static const std::shared_ptr filter = std::make_shared(Network::NetID::RED_GET_RTC); + std::shared_ptr generic = com->waitForMessageSync([this]() { + return com->sendCommand(Command::GetRTC); + }, filter, std::chrono::milliseconds(3000)); + if(!generic) // Did not receive a message + return std::nullopt; + + auto rawMes = std::dynamic_pointer_cast(generic); + if(!rawMes) + return std::nullopt; + + if(rawMes->data.size() != sizeof(RTCCTIME)) + return std::nullopt; + + const auto* time = (RTCCTIME*)rawMes->data.data(); + std::tm stdTime = {}; + // std::tm has no member for the `FracSec` member of RTCCTIME struct + stdTime.tm_sec = time->Sec; + stdTime.tm_min = time->Min; + stdTime.tm_hour = time->Hour; + stdTime.tm_mday = time->Day; + stdTime.tm_mon = time->Month - 1; // [0-11] + stdTime.tm_year = time->Year + 100; // Number of years since 1900+100 + stdTime.tm_wday = time->DOW; + // RTCCTIME struct has no member for `tm_yday` + + #ifdef _MSC_VER + #define timegm _mkgmtime + #endif + + return std::chrono::system_clock::from_time_t(timegm(&stdTime)); +} + +bool Device::setRTC(const std::chrono::time_point& time) +{ + auto now = std::chrono::system_clock::to_time_t(time); + const auto timeInfo = std::gmtime(&now); + if(!timeInfo) + return false; + + // Populate the RTCCTIME struct using the timeInfo and offsets + // Create a vector of arguments to send as the payload to the communication command + std::vector bytestream(sizeof(RTCCTIME)); + auto rtcVals = (RTCCTIME*)bytestream.data(); + rtcVals->FracSec = (uint8_t)0X00; + rtcVals->Sec = (uint8_t)timeInfo->tm_sec; + rtcVals->Min = (uint8_t)timeInfo->tm_min; + rtcVals->Hour = (uint8_t)timeInfo->tm_hour; + rtcVals->DOW = (uint8_t)timeInfo->tm_wday + 1; + rtcVals->Day = (uint8_t)timeInfo->tm_mday; + rtcVals->Month = (uint8_t)timeInfo->tm_mon + 1; // [0-11] + rtcVals->Year = (uint8_t)timeInfo->tm_year % 100; // divide by 100 and take remainder to get last 2 digits of year + + com->sendCommand(Command::SetRTC, bytestream); + return true; +} \ No newline at end of file diff --git a/examples/cpp/simple/src/SimpleExample.cpp b/examples/cpp/simple/src/SimpleExample.cpp index 960cf50..210a68a 100644 --- a/examples/cpp/simple/src/SimpleExample.cpp +++ b/examples/cpp/simple/src/SimpleExample.cpp @@ -5,6 +5,10 @@ #include "icsneo/icsneocpp.h" +#ifdef _MSC_VER +#pragma warning(disable : 4996) // STL time functions +#endif + int main() { // Print version std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl; @@ -118,6 +122,36 @@ int main() { } std::cout << "OK" << std::endl; + // Set the real time clock on the device using the system clock + // First, let's see if we can get the time from the device (if it has an RTC) + std::cout << "\n\tChecking and setting the RTC on the device" << std::endl; + auto dTime = device->getRTC(); + if (dTime.has_value()) { + std::time_t time = std::chrono::system_clock::to_time_t(dTime.value()); + const auto timeInfo = std::gmtime(&time); + if(!timeInfo) + return false; + std::cout << "\t\tGetting current UTC time: " << std::put_time(timeInfo, "%Y-%m-%d %H:%M:%S") << std::endl; + } else { + std::cout << "\t\tTime not available" << std::endl; + } + + // Now, set the time using the system's clock so that we can check it again to ensure it's set + auto now = std::chrono::system_clock::now(); + device->setRTC(now); + std::cout << "\t\tTime is now set..." << std::endl; + + // Get the time again after setting + auto dTime2 = device->getRTC(); + if (dTime2.has_value()) { + std::time_t time2 = std::chrono::system_clock::to_time_t(dTime2.value()); + const auto timeInfo2 = std::gmtime(&time2); + if(!timeInfo2) + return false; + std::cout << "\t\tGetting current UTC time: " << std::put_time(timeInfo2, "%Y-%m-%d %H:%M:%S") << std::endl; + } else { + std::cout << "\t\tTime is not available" << std::endl; + } // Now we can either register a handler (or multiple) for messages coming in // or we can enable message polling, and then call device->getMessages periodically @@ -127,7 +161,7 @@ int main() { // Keep in mind that 20k messages comes quickly at high bus loads! // We can transmit messages - std::cout << "\tTransmitting an extended CAN FD frame... "; + std::cout << "\n\tTransmitting an extended CAN FD frame... "; auto txMessage5 = std::make_shared(); txMessage5->network = icsneo::Network::NetID::HSCAN; txMessage5->arbid = 0x1C5001C5; diff --git a/include/icsneo/communication/command.h b/include/icsneo/communication/command.h index bbf8a23..c36ae06 100644 --- a/include/icsneo/communication/command.h +++ b/include/icsneo/communication/command.h @@ -12,6 +12,8 @@ enum class Command : uint8_t { NeoWriteMemory = 0x41, ClearCoreMini = 0x42, LoadCoreMini = 0x43, + GetRTC = 0x49, + SetRTC = 0x50, 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 diff --git a/include/icsneo/communication/network.h b/include/icsneo/communication/network.h index 25844bd..a69a33a 100644 --- a/include/icsneo/communication/network.h +++ b/include/icsneo/communication/network.h @@ -146,6 +146,7 @@ public: DWCAN16 = 541, LIN7 = 542, LIN8 = 543, + RED_SET_RTC = 544, Any = 0xfffe, // Never actually set as type, but used as flag for filtering Invalid = 0xffff }; @@ -325,6 +326,8 @@ public: case NetID::ExtendedCommand: case NetID::NeoMemorySDRead: case NetID::NeoMemoryWriteDone: + case NetID::RED_GET_RTC: + case NetID::RED_SET_RTC: return Type::Internal; case NetID::Invalid: case NetID::Any: @@ -453,6 +456,8 @@ public: return "RED_HARDWARE_EXCEP"; case NetID::RED_GET_RTC: return "RED_GET_RTC"; + case NetID::RED_SET_RTC: + return "RED_SET_RTC"; case NetID::ISO9141_3: return "ISO 9141 3"; case NetID::HSCAN2: diff --git a/include/icsneo/device/device.h b/include/icsneo/device/device.h index b7c85bc..ab706ca 100644 --- a/include/icsneo/device/device.h +++ b/include/icsneo/device/device.h @@ -50,7 +50,6 @@ #define ICSNEO_FINDABLE_DEVICE_BY_PID(className, type, pid) \ static constexpr const uint16_t PRODUCT_ID = pid; \ ICSNEO_FINDABLE_DEVICE_BASE(className, type) - namespace icsneo { class DeviceExtension; @@ -545,6 +544,10 @@ public: */ virtual bool getEthPhyRegControlSupported() const { return false; } + //RTC declarations + std::optional> getRTC(); + bool setRTC(const std::chrono::time_point& time); + /** * Returns true if this device supports the Wireless neoVI featureset */ diff --git a/include/icsneo/icsnVC40.h b/include/icsneo/icsnVC40.h index 36fe3b0..331554c 100644 --- a/include/icsneo/icsnVC40.h +++ b/include/icsneo/icsnVC40.h @@ -2442,6 +2442,16 @@ typedef struct uint8_t bIPV4_Address[4]; // The Ipv4 address assigned to the Network interface. If not available, all bytes are set to zero. } NETWORK_ADAPTER_INFO; +typedef struct +{ + unsigned char sec;// --- Seconds (00-59) + unsigned char min;// --- (00-59) + unsigned char hour;// --- (00-23) + unsigned char day;// --- (01-31) + unsigned char month;// --- (01-12) + unsigned char year;// --- (00-99) +} icsSpyTime; + #ifndef INTREPID_NO_CHECK_STRUCT_SIZE #if defined(__cplusplus) && (__cplusplus > 199711L) diff --git a/include/icsneo/icsneoc.h b/include/icsneo/icsneoc.h index 0f6ee23..b6ca1be 100644 --- a/include/icsneo/icsneoc.h +++ b/include/icsneo/icsneoc.h @@ -808,6 +808,24 @@ extern bool DLLExport icsneo_isTerminationEnabledFor(const neodevice_t* device, */ extern bool DLLExport icsneo_setTerminationFor(const neodevice_t* device, neonetid_t netid, bool enabled); +/** + * \brief Get the real-time clock for the given device. + * \param[out] device A pointer to the neodevice_t structure specifying the device to read the RTC from. + * \param[out] output A pointer to a uint64_t where the RTC will be stored. This value is in seconds. + + * \returns True if the RTC was successfully retrieved. + */ +extern bool icsneo_getRTC(const neodevice_t* device, uint64_t* output); + +/** + * \brief Set the real-time clock for the given device. + * \param[out] device A pointer to the neodevice_t structure specifying the device to write the RTC to. + * \param[in] input A uint64_t object holding the RTC value. This value is in seconds. + + * \returns True if the RTC was successfully set. + */ +extern bool icsneo_setRTC(const neodevice_t* device, uint64_t input); + #ifdef __cplusplus } // extern "C" #endif diff --git a/include/icsneo/icsneolegacy.h b/include/icsneo/icsneolegacy.h index 40a730b..c0650d7 100644 --- a/include/icsneo/icsneolegacy.h +++ b/include/icsneo/icsneolegacy.h @@ -40,6 +40,10 @@ extern void LegacyDLLExport icsneoGetISO15765Status(void* hObject, int lNetwork, extern void LegacyDLLExport icsneoSetISO15765RxParameters(void* hObject, int lNetwork, int lEnable, spyFilterLong* pFF_CFMsgFilter, icsSpyMessage* pTxMsg, int lCFTimeOutMs, int lFlowCBlockSize, int lUsesExtendedAddressing, int lUseHardwareIfPresent); +//RTC Functions +extern int LegacyDLLExport icsneoGetRTC(void* hObject, icsSpyTime* time); +extern int LegacyDLLExport icsneoSetRTC(void* hObject, const icsSpyTime* time); + //Device Functions extern int LegacyDLLExport icsneoGetConfiguration(void* hObject, unsigned char* pData, int* lNumBytes); extern int LegacyDLLExport icsneoSendConfiguration(void* hObject, unsigned char* pData, int lNumBytes);