#pragma once #include #include #include "VaultConfig.h" enum class SubmitResult : uint8_t { DigitAccepted, CorrectCode, WrongCode, LockedOut, InvalidDigit }; class VaultState { public: VaultState() : _code(DEFAULT_CODE), _entry{}, _entryLength(0), _failedAttempts(0), _lockoutUntilMs(0) { } void setCode(const std::array &code) { _code = code; } const std::array &code() const { return _code; } SubmitResult pushDigit(uint8_t digit, uint32_t nowMs) { if (isLockedOut(nowMs)) { return SubmitResult::LockedOut; } if (digit < DIGIT_MIN || digit > DIGIT_MAX) { return SubmitResult::InvalidDigit; } if (_entryLength < CODE_LENGTH) { _entry[_entryLength++] = digit; } if (_entryLength < CODE_LENGTH) { return SubmitResult::DigitAccepted; } const bool ok = (_entry == _code); clearEntry(); if (ok) { _failedAttempts = 0; return SubmitResult::CorrectCode; } ++_failedAttempts; if (_failedAttempts >= MAX_FAILED_ATTEMPTS) { _failedAttempts = 0; _lockoutUntilMs = nowMs + LOCKOUT_MS; } return SubmitResult::WrongCode; } bool isLockedOut(uint32_t nowMs) const { return nowMs < _lockoutUntilMs; } uint32_t lockoutRemainingMs(uint32_t nowMs) const { if (!isLockedOut(nowMs)) { return 0; } return _lockoutUntilMs - nowMs; } void clearEntry() { _entry.fill(0); _entryLength = 0; } std::size_t enteredLength() const { return _entryLength; } uint8_t failedAttempts() const { return _failedAttempts; } void resetLockout() { _lockoutUntilMs = 0; _failedAttempts = 0; clearEntry(); } private: std::array _code; std::array _entry; std::size_t _entryLength; uint8_t _failedAttempts; uint32_t _lockoutUntilMs; };