#pragma once #include #include #include #include #include #include namespace secsgem::gem { enum class TimeAck : uint8_t { Accept = 0, Error = 1, NotDoneNotEmpty = 2, }; // The equipment clock. current_time_string() returns the 16-char SECS-II // TIME format ("YYYYMMDDhhmmsscc"), with an offset applied if the host has // previously set the time via S2F31. class Clock { public: std::string current_time_string() const { using namespace std::chrono; const auto now = system_clock::now() + seconds(offset_seconds_); const auto t = system_clock::to_time_t(now); const auto ms = duration_cast(now.time_since_epoch()) % 1000; std::tm tm{}; gmtime_r(&t, &tm); std::array buf{}; std::snprintf(buf.data(), buf.size(), "%04d%02d%02d%02d%02d%02d%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, static_cast(ms.count() / 10)); return std::string(buf.data()); } TimeAck set_time_string(const std::string& s) { if (s.size() != 14 && s.size() != 16) return TimeAck::Error; int y, mo, d, h, mi, se; if (!parse_digits(s.data() + 0, 4, y) || !parse_digits(s.data() + 4, 2, mo) || !parse_digits(s.data() + 6, 2, d) || !parse_digits(s.data() + 8, 2, h) || !parse_digits(s.data() + 10, 2, mi) || !parse_digits(s.data() + 12, 2, se)) { return TimeAck::Error; } std::tm tm{}; tm.tm_year = y - 1900; tm.tm_mon = mo - 1; tm.tm_mday = d; tm.tm_hour = h; tm.tm_min = mi; tm.tm_sec = se; const std::time_t target = timegm(&tm); if (target == static_cast(-1)) return TimeAck::Error; offset_seconds_ = static_cast(target - std::time(nullptr)); return TimeAck::Accept; } private: static bool parse_digits(const char* p, std::size_t n, int& out) { int v = 0; for (std::size_t i = 0; i < n; ++i) { if (p[i] < '0' || p[i] > '9') return false; v = v * 10 + (p[i] - '0'); } out = v; return true; } std::int64_t offset_seconds_ = 0; }; } // namespace secsgem::gem