// plc_time_macros.ems // EtherasMicroScript - Human-time helpers (NTP-based) // Assumptions per request: // - Current time source is NTP. // - NOWUTC() exists and returns epoch seconds (UTC). // - DOW() exists and returns 0..6 where 0 = Monday, 6 = Sunday. // - There is no builtin TIME_OK(): we implement TIME_OK as a MACRO. // Notes: // - Without built-in local time conversion helpers (HOUR(), MIN()), these macros // rely on extracting time-of-day from NOWUTC() (UTC time). // - If your firmware later provides HOUR()/MIN() in local time, you can swap the // implementation of HOUR_UTC/MIN_UTC macros accordingly. // - Private variables starting with '_' are assumed to be auto-renamed per MACRO instance. // ------------------------------ // TIME_OK: detect whether time is valid/synced // ------------------------------ // Heuristic: consider time "OK" if NOWUTC() is after a reasonable epoch threshold. // 1700000000 ~ Nov 2023. Adjust if needed. MACRO TIME_OK(OUT ok) { ok = 0 IF NOWUTC() > 1700000000 { ok = 1 } } // ------------------------------ // Basic time-of-day extraction from UTC epoch seconds // ------------------------------ // SECONDS_OF_DAY_UTC: 0..86399 MACRO SECONDS_OF_DAY_UTC(OUT sod) { _t = NOWUTC() sod = _t % 86400 } // MINUTES_OF_DAY_UTC: 0..1439 MACRO MINUTES_OF_DAY_UTC(OUT mod) { SECONDS_OF_DAY_UTC(_sod) mod = _sod / 60 } // HOUR_UTC: 0..23 MACRO HOUR_UTC(OUT h) { MINUTES_OF_DAY_UTC(_mod) h = _mod / 60 } // MIN_UTC: 0..59 MACRO MIN_UTC(OUT m) { MINUTES_OF_DAY_UTC(_mod) m = _mod % 60 } // ------------------------------ // Time window & schedules (UTC-based) // ------------------------------ // TIME_WINDOW_UTC: true when current UTC time is within [h1:m1, h2:m2) // Handles windows that cross midnight. MACRO TIME_WINDOW_UTC(h1,m1, h2,m2, OUT q) { MINUTES_OF_DAY_UTC(_now) _a = h1*60 + m1 _b = h2*60 + m2 IF _a <= _b { q = (_now >= _a) AND (_now < _b) } ELSE { q = (_now >= _a) OR (_now < _b) } } // DOW_IN: check if current day-of-week (0=Mon..6=Sun) is in mask. // mask bit0=Mon ... bit6=Sun MACRO DOW_IN(mask, OUT q) { _d = DOW() IF _d < 0 { q = 0 } ELSE { _p = 1 FOR _i FROM 1 TO _d { _p = _p * 2 } q = (mask / _p) % 2 } } // WEEKLY_UTC: LOGO-like weekly timer using UTC clock // - mask bit0=Mon ... bit6=Sun // - active within time window MACRO WEEKLY_UTC(mask, h1,m1, h2,m2, OUT q) { DOW_IN(mask, _dayOk) TIME_WINDOW_UTC(h1,m1, h2,m2, _timeOk) q = _dayOk AND _timeOk } // AT_TIME_UTC: one-shot pulse when UTC clock reaches hh:mm // Fires once per minute at the target time. MACRO AT_TIME_UTC(hh, mm, OUT q) { q = 0 MINUTES_OF_DAY_UTC(_nowMin) _target = hh*60 + mm IF _nowMin == _target AND _last != _nowMin { q = 1 } _last = _nowMin } // SCHEDULED_UTC: safe schedule gating (requires TIME_OK) MACRO SCHEDULED_UTC(mask, h1,m1, h2,m2, OUT q) { TIME_OK(_ok) IF _ok { WEEKLY_UTC(mask, h1,m1, h2,m2, q) } ELSE { q = 0 } }