Public Access
Updates: when a release may install by itself, as a state machine
Issue #52: once a newer release is known, wait for a quiet moment, ask for 30 seconds, and install unless the answer is no. The caller says what busy and idle are. Host-tested. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace roro::release {
|
||||
|
||||
// Installing a release without being asked to (issue #52): once a newer one is known, wait for a
|
||||
// quiet moment (nothing recording, nothing written, no key for a while), then say so on the
|
||||
// screen for 30 seconds. Unless someone answers no, it is installed. Host-tested; the caller says
|
||||
// what "quiet" is and does the installing.
|
||||
class AutoUpdate {
|
||||
public:
|
||||
static constexpr uint32_t kIdleMs = 120000; // no key for this long: nobody is using the device
|
||||
static constexpr uint32_t kAskMs = 30000; // how long the question stays before it answers itself
|
||||
|
||||
enum class Action : uint8_t {
|
||||
None,
|
||||
Ask, // the question goes up
|
||||
Dismiss, // the question comes down, nothing is installed
|
||||
Install, // the question comes down, install tag()
|
||||
};
|
||||
|
||||
// A newer release is known. A second offer while one is waiting or asked replaces its tag.
|
||||
void offer(const std::string& tag) {
|
||||
tag_ = tag;
|
||||
if (state_ == State::Idle) state_ = State::Waiting;
|
||||
}
|
||||
// The setting went off, or the release is no longer wanted.
|
||||
void withdraw() { withdrawn_ = true; }
|
||||
// The user's answer to the question.
|
||||
void answer(bool install) {
|
||||
if (state_ != State::Asking) return;
|
||||
answered_ = true;
|
||||
accepted_ = install;
|
||||
}
|
||||
|
||||
// Call often. `busy`: something is recording or being written, and a restart would cut it.
|
||||
// `idleMs`: how long since the last key.
|
||||
Action step(uint32_t nowMs, bool busy, uint32_t idleMs) {
|
||||
bool withdrawn = withdrawn_;
|
||||
withdrawn_ = false;
|
||||
switch (state_) {
|
||||
case State::Idle: return Action::None;
|
||||
case State::Waiting:
|
||||
if (withdrawn) return state_ = State::Idle, Action::None;
|
||||
if (busy || idleMs < kIdleMs) return Action::None;
|
||||
state_ = State::Asking;
|
||||
askedMs_ = nowMs;
|
||||
answered_ = false;
|
||||
return Action::Ask;
|
||||
case State::Asking:
|
||||
if (withdrawn) return state_ = State::Idle, Action::Dismiss;
|
||||
if (answered_) return state_ = State::Idle, accepted_ ? Action::Install : Action::Dismiss;
|
||||
// Something started meanwhile (a Track from a console): the question goes, and comes back later.
|
||||
if (busy) return state_ = State::Waiting, Action::Dismiss;
|
||||
if (nowMs - askedMs_ >= kAskMs) return state_ = State::Idle, Action::Install;
|
||||
return Action::None;
|
||||
}
|
||||
return Action::None;
|
||||
}
|
||||
|
||||
bool waiting() const { return state_ == State::Waiting; }
|
||||
bool asking() const { return state_ == State::Asking; }
|
||||
const std::string& tag() const { return tag_; }
|
||||
// Whole seconds until the question answers itself, rounded up.
|
||||
int secondsLeft(uint32_t nowMs) const {
|
||||
uint32_t gone = nowMs - askedMs_;
|
||||
return !asking() || gone >= kAskMs ? 0 : static_cast<int>((kAskMs - gone + 999) / 1000);
|
||||
}
|
||||
|
||||
private:
|
||||
enum class State : uint8_t { Idle, Waiting, Asking };
|
||||
State state_ = State::Idle;
|
||||
std::string tag_;
|
||||
uint32_t askedMs_ = 0;
|
||||
bool answered_ = false, accepted_ = false, withdrawn_ = false;
|
||||
};
|
||||
|
||||
} // namespace roro::release
|
||||
@@ -24,6 +24,12 @@ inline bool shouldAnnounce(const Release& latest, const std::string& running, co
|
||||
return isNewer(latest, running) && latest.tag != failed && latest.tag != announced;
|
||||
}
|
||||
|
||||
// Should the background check offer to install it by itself (issue #52)? Any newer release, each
|
||||
// day it is still the newest; never a version that rolled back on this device.
|
||||
inline bool shouldInstall(const Release& latest, const std::string& running, const std::string& failed) {
|
||||
return isNewer(latest, running) && latest.tag != failed;
|
||||
}
|
||||
|
||||
// Is `url` a download this device takes from the project's server, for this repository? Whatever
|
||||
// the API says, the device only fetches from where it asked (a redirected or rewritten answer
|
||||
// can't send it elsewhere).
|
||||
|
||||
Reference in New Issue
Block a user