From 59406e83f3cd3211e0241a92c27df18046f2d74c Mon Sep 17 00:00:00 2001 From: Nathan Schulte <8540239+nmschulte@users.noreply.github.com> Date: Wed, 23 Nov 2022 14:53:16 -0600 Subject: [PATCH] avoid ctime, use UTC ISO 8601 for CMD_DATE format (#4764) * java_console: use UTC ISO 8601 for CMD_DATE format * ChibiOS RTC avoids ctime * firmware: controller sets time via ISO 8601 * avoid ctime in CAN dash --- firmware/controllers/can/can_dash.cpp | 25 ++- firmware/controllers/settings.cpp | 41 +++- firmware/controllers/settings.h | 3 + firmware/hw_layer/rtc_helper.cpp | 191 ++++++++---------- firmware/hw_layer/rtc_helper.h | 16 +- .../java/com/rusefi/ui/console/MainFrame.java | 12 +- 6 files changed, 151 insertions(+), 137 deletions(-) diff --git a/firmware/controllers/can/can_dash.cpp b/firmware/controllers/can/can_dash.cpp index f70e2fbed2..dd219fb2c4 100644 --- a/firmware/controllers/can/can_dash.cpp +++ b/firmware/controllers/can/can_dash.cpp @@ -15,6 +15,7 @@ #include "can_bmw.h" #include "can_vag.h" +#include "rusefi_types.h" #include "rtc_helper.h" #include "fuel_math.h" @@ -554,16 +555,22 @@ void canDashboardBMWE90(CanCycle cycle) { if (!cluster_time_set) { - struct tm timp; - date_get_tm(&timp); +#if EFI_RTC + efidatetime_t dateTime = getRtcDateTime(); +#else // EFI_RTC + efidatetime_t dateTime = { + .year = 0, .month = 0, .day = 0, + .hour = 0, .minute = 0, .second = 0, + }; +#endif // EFI_RTC CanTxMessage msg(CanCategory::NBC, E90_TIME, 8); - msg[0] = timp.tm_hour; - msg[1] = timp.tm_min; - msg[2] = timp.tm_sec; - msg[3] = timp.tm_mday; - msg[4] = (((timp.tm_mon + 1) << 4) | 0x0F); - msg[5] = (timp.tm_year + 1900) & 0xFF; - msg[6] = ((timp.tm_year + 1900) >> 8) | 0xF0; + msg[0] = dateTime.hour; + msg[1] = dateTime.minute; + msg[2] = dateTime.second; + msg[3] = dateTime.day; + msg[4] = (dateTime.month << 4) | 0x0F; + msg[5] = dateTime.year & 0xFF; + msg[6] = (dateTime.year >> 8) | 0xF0; // collides CAN dash at 4096! msg[7] = 0xF2; cluster_time_set = 1; } diff --git a/firmware/controllers/settings.cpp b/firmware/controllers/settings.cpp index 0a8c7b27da..6f189985f8 100644 --- a/firmware/controllers/settings.cpp +++ b/firmware/controllers/settings.cpp @@ -17,9 +17,11 @@ #include "alternator_controller.h" #include "trigger_emulator_algo.h" #include "value_lookup.h" +#if EFI_RTC +#include "rtc_helper.h" +#endif // EFI_RTC #if EFI_PROD_CODE -#include "rtc_helper.h" #include "can_hw.h" #include "rusefi.h" #include "hardware.h" @@ -874,14 +876,9 @@ static void getValue(const char *paramStr) { efiPrintf("invertCamVVTSignal=%s", boolToString(engineConfiguration->invertCamVVTSignal)); } else if (strEqualCaseInsensitive(paramStr, "isHip9011Enabled")) { efiPrintf("isHip9011Enabled=%d", engineConfiguration->isHip9011Enabled); - } - -#if EFI_RTC - else if (strEqualCaseInsensitive(paramStr, CMD_DATE)) { + } else if (strEqualCaseInsensitive(paramStr, CMD_DATE)) { printDateTime(); - } -#endif - else { + } else { efiPrintf("Invalid Parameter: %s", paramStr); } } @@ -1069,11 +1066,9 @@ static void setValue(const char *paramStr, const char *valueStr) { #endif // EFI_PROD_CODE } else if (strEqualCaseInsensitive(paramStr, "targetvbatt")) { engineConfiguration->targetVBatt = valueF; -#if EFI_RTC } else if (strEqualCaseInsensitive(paramStr, CMD_DATE)) { // rusEfi console invokes this method with timestamp in local timezone setDateTime(valueStr); -#endif } engine->resetEngineSnifferIfInTestMode(); } @@ -1155,6 +1150,32 @@ void initSettings(void) { #endif // EFI_PROD_CODE } +void printDateTime() { +#if EFI_RTC + printRtcDateTime(); +#else // EFI_RTC + efiPrintf("Cannot print time: RTC not supported"); +#endif // EFI_RTC +} + +void setDateTime(const char * const isoDateTime) { +#if EFI_RTC + if (strlen(isoDateTime) > 0) { + efidatetime_t dateTime; + if (sscanf("%04u-%02u-%02uT%02u:%02u:%02u", isoDateTime, + &dateTime.year, &dateTime.month, &dateTime.day, + &dateTime.hour, &dateTime.minute, &dateTime.second) + == 6) { // 6 fields to properly scan + setRtcDateTime(&dateTime); + return; + } + } + efiPrintf("date_set Date parameter %s is wrong", isoDateTime); +#else // EFI_RTC + efiPrintf("Cannot set time: RTC not supported"); +#endif // EFI_RTC +} + #endif // ! EFI_UNIT_TEST void setEngineType(int value) { diff --git a/firmware/controllers/settings.h b/firmware/controllers/settings.h index a2d0e3b97e..43f836995c 100644 --- a/firmware/controllers/settings.h +++ b/firmware/controllers/settings.h @@ -18,3 +18,6 @@ void scheduleStopEngine(void); void printTPSInfo(void); void setEngineType(int value); void readPin(const char *pinName); + +void printDateTime(); +void setDateTime(const char * const isoDateTime); diff --git a/firmware/hw_layer/rtc_helper.cpp b/firmware/hw_layer/rtc_helper.cpp index bab4814d49..01e0d5f189 100644 --- a/firmware/hw_layer/rtc_helper.cpp +++ b/firmware/hw_layer/rtc_helper.cpp @@ -8,11 +8,14 @@ #include "pch.h" -#include -#include +#include #include "rtc_helper.h" +#if EFI_RTC +#include "rusefi_types.h" +#endif // EFI_RTC + #if EFI_PROD_CODE #include @@ -23,55 +26,69 @@ extern "C" int _gettimeofday(timeval* tv, void* tzvp) { } #endif // EFI_PROD_CODE -void date_set_tm(tm *timp) { - (void)timp; #if EFI_RTC - RTCDateTime timespec; - rtcConvertStructTmToDateTime(timp, 0, ×pec); - rtcSetTime(&RTCD1, ×pec); -#endif // EFI_RTC +void initRtc() { + efiPrintf("initRtc()"); + printDateTime(); // this would test RTC, see 'rtcWorks' variable, see #311 } -void date_get_tm(tm *timp) { - (void)timp; -#if EFI_RTC - RTCDateTime timespec; - rtcGetTime(&RTCD1, ×pec); - rtcConvertDateTimeToStructTm(×pec, timp, NULL); -#endif // EFI_RTC +static const char * const monthAbbrs[] = { + "Jan", "Feb", "Mar", + "Apr", "May", "Jun", + "Jul", "Aug", "Sep", + "Oct", "Nov", "Dec" +}; + +void printRtcDateTime() { + efidatetime_t dateTime = getRtcDateTime(); + // prints the date like: 19 sep 2022 21:19:55 + efiPrintf("Current RTC time: %02u %s %04u %02u:%02u:%02u", + dateTime.day, monthAbbrs[dateTime.month - 1], dateTime.year, + dateTime.hour, dateTime.minute, dateTime.second); } -#if EFI_RTC -static time_t GetTimeUnixSec() { - tm tim; - RTCDateTime timespec; - - rtcGetTime(&RTCD1, ×pec); - rtcConvertDateTimeToStructTm(×pec, &tim, NULL); - time_t result = mktime(&tim); - - return result; -} - -static void SetTimeUnixSec(time_t unix_time) { - tm tim; - -#if defined __GNUC__ - tm *canary; - /* If the conversion is successful the function returns a pointer - to the object the result was written into.*/ - canary = localtime_r(&unix_time, &tim); - osalDbgCheck(&tim == canary); -#else // defined __GNUC__ - tm *t = localtime(&unix_time); - memcpy(&tim, t, sizeof(tm)); -#endif // defined __GNUC__ - - RTCDateTime timespec; - rtcConvertStructTmToDateTime(&tim, 0, ×pec); +void setRtcDateTime(efidatetime_t const * const dateTime) { + RTCDateTime timespec = convertRtcDateTimeFromEfi(dateTime); rtcSetTime(&RTCD1, ×pec); } +efidatetime_t getRtcDateTime() { + RTCDateTime timespec; + rtcGetTime(&RTCD1, ×pec); + return convertRtcDateTimeToEfi(×pec); +} + +efidatetime_t convertRtcDateTimeToEfi(RTCDateTime const * const timespec) { + uint32_t second = timespec->millisecond / 1000; + uint16_t minute = second / 60; + second -= minute * 60; + uint8_t hour = minute / 60; + minute -= hour * 60; + + efidatetime_t const dateTime = { + .year = timespec->year + RTC_BASE_YEAR, + .month = (uint8_t)timespec->month, + .day = (uint8_t)timespec->day, + .hour = hour, + .minute = (uint8_t)minute, + .second = (uint8_t)second, + }; + return dateTime; +} + +RTCDateTime convertRtcDateTimeFromEfi(efidatetime_t const * const dateTime) { + RTCDateTime timespec; + timespec.year = dateTime->year - RTC_BASE_YEAR; // ChibiOS year origin is e.g. 1980 + timespec.month = dateTime->month; // [1..12] + timespec.day = dateTime->day; // [1..31] + timespec.millisecond = (((dateTime->hour * 60) + dateTime->minute) * 60 + dateTime->second) * 1000; // ms since midnight + timespec.dayofweek = RTC_DAY_CATURDAY; // CATURDAY: 0 ... ? + timespec.dstflag = 0; // 0 ... ? + return timespec; +} + +// TODO(nms): move to e.g. efitime ? + static void put2(int offset, char *lcd_str, int value) { static char buff[_MAX_FILLER]; efiAssertVoid(CUSTOM_ERR_6666, value >=0 && value <100, "value"); @@ -84,97 +101,53 @@ static void put2(int offset, char *lcd_str, int value) { lcd_str[offset + 1] = buff[1]; } } +#endif // EFI_RTC + /** * @return true if we seem to know current date, false if no valid RTC state */ bool dateToStringShort(char *lcd_str) { +#if EFI_RTC strcpy(lcd_str, "000000_000000\0"); - tm timp; - date_get_tm(&timp); - if (timp.tm_year < 116 || timp.tm_year > 130) { + efidatetime_t dateTime = getRtcDateTime(); + if (dateTime.year < 2016 || dateTime.year > 2030) { // 2016 to 2030 is the valid range lcd_str[0] = 0; return false; } - put2(0, lcd_str, timp.tm_year % 100); // Years since 1900 - format as just the last two digits - put2(2, lcd_str, timp.tm_mon + 1); // months since January 0-11 - put2(4, lcd_str, timp.tm_mday); // day of the month 1-31 + put2(0, lcd_str, dateTime.year % 100); // year, format as just the last two digits + put2(2, lcd_str, dateTime.month); // month 1-12 + put2(4, lcd_str, dateTime.day); // day of the month 1-31 - put2(7, lcd_str, timp.tm_hour); // hours since midnight 0-23 - put2(9, lcd_str, timp.tm_min); // Minutes - put2(11, lcd_str, timp.tm_sec); // seconds + put2(7, lcd_str, dateTime.hour); // hours since midnight 0-23 + put2(9, lcd_str, dateTime.minute); // minutes + put2(11, lcd_str, dateTime.second); // seconds return true; +#else // EFI_RTC + lcd_str[0] = 0; + return false; +#endif // EFI_RTC } void dateToString(char *lcd_str) { +#if EFI_RTC // todo: // re-implement this along the lines of chvprintf("%04u-%02u-%02u %02u:%02u:%02u\r\n", timp.tm_year + 1900, timp.tm_mon + 1, timp.tm_mday, timp.tm_hour, // timp.tm_min, timp.tm_sec); // this would require a temporary mem stream - see datalogging and other existing usages strcpy(lcd_str, "00/00 00:00:00\0"); - tm timp; - date_get_tm(&timp); // get RTC date/time + efidatetime_t dateTime = getRtcDateTime(); - put2(0, lcd_str, timp.tm_mon + 1); - put2(3, lcd_str, timp.tm_mday); - put2(6, lcd_str, timp.tm_hour); - put2(9, lcd_str, timp.tm_min); - put2(12, lcd_str, timp.tm_sec); -} - -static const char* const months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - -void printDateTime() { - tm timp; - - time_t unix_time = GetTimeUnixSec(); - if (unix_time == -1) { - efiPrintf("invalid time in RTC cell"); - } else { - efiPrintf("Current Unix time: %d", unix_time); - date_get_tm(&timp); - - auto month = months[timp.tm_mon]; - - // Prints something like "19 Sep 2022 21:19:55" - efiPrintf("Current RTC time is: %02u %s %04u %02u:%02u:%02u", timp.tm_mday, month, timp.tm_year + 1900, timp.tm_hour, timp.tm_min, timp.tm_sec); - } -} - -void setDateTime(const char *strDate) { - if (strlen(strDate) > 0) { - time_t unix_time = atoi(strDate); - if (unix_time > 0) { - SetTimeUnixSec(unix_time); - printDateTime(); - return; - } - } - efiPrintf("date_set Date parameter %s is wrong", strDate); -} - + put2(0, lcd_str, dateTime.month); + put2(3, lcd_str, dateTime.day); + put2(6, lcd_str, dateTime.hour); + put2(9, lcd_str, dateTime.minute); + put2(12, lcd_str, dateTime.second); #else // EFI_RTC - -bool dateToStringShort(char *lcd_str) { lcd_str[0] = 0; - return false; -} - -void dateToString(char *lcd_str) { - lcd_str[0] = 0; -} - -#endif // EFI_RTC - -void initRtc() { -#if EFI_RTC - GetTimeUnixSec(); // this would test RTC, see 'rtcWorks' variable, see #311 - efiPrintf("initRtc()"); - - printDateTime(); #endif // EFI_RTC } diff --git a/firmware/hw_layer/rtc_helper.h b/firmware/hw_layer/rtc_helper.h index e8c0a4d216..e8b7806ee8 100644 --- a/firmware/hw_layer/rtc_helper.h +++ b/firmware/hw_layer/rtc_helper.h @@ -4,14 +4,22 @@ * * @date Feb 5, 2014 * @author Andrey Belomutskiy, (c) 2012-2020 + * @author Nathan Schulte, (c) 2022 */ #pragma once -void printDateTime(); -void setDateTime(const char *strDate); +#include "rusefi_types.h" + +#if EFI_RTC void initRtc(); -void date_set_tm(struct tm *); -void date_get_tm(struct tm *); +void printRtcDateTime(); +efidatetime_t getRtcDateTime(); +void setRtcDateTime(const efidatetime_t * const dateTime); + +efidatetime_t convertRtcDateTimeToEfi(const RTCDateTime * const timespec); +RTCDateTime convertRtcDateTimeFromEfi(const efidatetime_t * const dateTime); +#endif // EFI_RTC + void dateToString(char *buffer); bool dateToStringShort(char *lcd_str); diff --git a/java_console/ui/src/main/java/com/rusefi/ui/console/MainFrame.java b/java_console/ui/src/main/java/com/rusefi/ui/console/MainFrame.java index 528f80fcf4..54a136503e 100644 --- a/java_console/ui/src/main/java/com/rusefi/ui/console/MainFrame.java +++ b/java_console/ui/src/main/java/com/rusefi/ui/console/MainFrame.java @@ -16,7 +16,9 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.util.Objects; -import java.util.TimeZone; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.ZoneOffset; import static com.devexperts.logging.Logging.getLogging; import static com.rusefi.core.preferences.storage.PersistentConfiguration.getConfig; @@ -27,6 +29,7 @@ public class MainFrame { @NotNull private final ConsoleUI consoleUI; private final TabbedPanel tabbedPane; + /** * @see StartupFrame */ @@ -55,7 +58,6 @@ public class MainFrame { public MainFrame(ConsoleUI consoleUI, TabbedPanel tabbedPane) { this.consoleUI = Objects.requireNonNull(consoleUI); - this.tabbedPane = tabbedPane; listener = (String s) -> { }; @@ -67,10 +69,10 @@ public class MainFrame { setTitle(); UiUtils.trueRepaint(tabbedPane.tabbedPane); // this would repaint status label if (ConnectionStatusLogic.INSTANCE.getValue() == ConnectionStatusValue.CONNECTED) { - long unixGmtTime = System.currentTimeMillis() / 1000L; - long withOffset = unixGmtTime + TimeZone.getDefault().getOffset(System.currentTimeMillis()) / 1000; + LocalDateTime dateTime = LocalDateTime.now(ZoneOffset.systemDefault()); + String isoDateTime = dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); consoleUI.uiContext.getLinkManager().execute(() -> consoleUI.uiContext.getCommandQueue().write(IoUtil.getSetCommand(Fields.CMD_DATE) + - " " + withOffset, CommandQueue.DEFAULT_TIMEOUT, + " " + isoDateTime, CommandQueue.DEFAULT_TIMEOUT, InvocationConfirmationListener.VOID, false)); } }));