refactor: pin which radio job holds a shade's simulated movement

Review flagged the outer My/Favorite losing to the nested Up/Down it queues.
Every such nested send is from a simMy shade, where the Up/Down is what
starts the motor, so the behaviour stays.  The hold moves into TxHold in
ShadeMotion with a comment explaining the order and unit tests pinning it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
cjkas 2026-10-12 18:33:19 +02:00
parent a212d562cb
commit 0ba5260d1d
5 changed files with 118 additions and 18 deletions

View file

@ -12,4 +12,23 @@ float travelPosition(float startPos, int8_t dir, uint32_t elapsedMs, int32_t tra
// A position as reported to clients: -1 when unset, otherwise the whole
// percentage, counted from the other end when flipped.
int8_t displayPosition(float pos, bool flip);
// The radio job that set an idle shade moving. Its simulated movement is held
// until the job goes on air, since that is when the motor starts.
struct TxHold {
uint32_t seq = 0; // 0 when nothing is held.
uint32_t since = 0; // millis() when the hold started.
// Holds for job seq unless the shade was already moving (its motor carries on
// with the earlier command) or a job is already held. The first job held is
// the one that starts the motor. A command sent while processing another
// registers before it: for a simMy shade My/Favorite queues its Up/Down from
// moveToMyPosition() first, and that Up/Down is what starts a motor with no
// favorite of its own, so the outer My/Favorite is ignored on purpose.
void mark(uint32_t jobSeq, bool moving, uint32_t now);
// Ends the hold when jobSeq is the held job. True when it was.
bool release(uint32_t jobSeq);
bool held() const { return this->seq != 0; }
bool timedOut(uint32_t now, uint32_t timeoutMs) const { return now - this->since >= timeoutMs; }
void clear() { this->seq = 0; }
};
#endif