ShadeMotion.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include <algorithm> | ||
| 2 | #include "ShadeMotion.h" | ||
| 3 | |||
| 4 | 12794 | float travelPosition(float startPos, int8_t dir, uint32_t elapsedMs, int32_t travelMs) { | |
| 5 | // Work in time travelled from the end we are moving away from: the start | ||
| 6 | // position is turned into the time it would take to get there, the elapsed | ||
| 7 | // time is added and the total is turned back into a position. The addition | ||
| 8 | // wraps like the uint32 millis() so it keeps working across a rollover. | ||
| 9 | 12794 | if(dir > 0) { | |
| 10 | 10837 | if(travelMs == 0) return 100.0f; | |
| 11 | 10835 | int32_t ms = (int32_t)floor((startPos / 100) * travelMs); | |
| 12 | 10835 | ms += elapsedMs; | |
| 13 | 10835 | ms = std::min(travelMs, ms); | |
| 14 | 10835 | if(ms >= travelMs) return 100.0f; | |
| 15 | 10315 | return std::min(std::max(0.0f, (float)ms / (float)travelMs), 1.0f) * 100; | |
| 16 | } | ||
| 17 | 1957 | if(dir < 0) { | |
| 18 | 1957 | if(travelMs == 0) return 0.0f; | |
| 19 | 1955 | int32_t ms = travelMs - (int32_t)floor((startPos / 100) * travelMs); | |
| 20 | 1955 | ms += elapsedMs; | |
| 21 | 1955 | ms = std::min(travelMs, ms); | |
| 22 | 1955 | if(ms >= travelMs) return 0.0f; | |
| 23 | 1449 | float pos = (1.0f - std::min(std::max(0.0f, (float)ms / (float)travelMs), 1.0f)) * 100; | |
| 24 | 1449 | return pos <= 0.0f ? 0.0f : pos; | |
| 25 | } | ||
| 26 | ✗ | return startPos; | |
| 27 | } | ||
| 28 | 242 | int8_t displayPosition(float pos, bool flip) { | |
| 29 | 242 | if(pos < 0) return -1; | |
| 30 | 235 | return static_cast<int8_t>(flip && pos >= 0.00f ? floor(100.0f - pos) : floor(pos)); | |
| 31 | } | ||
| 32 | 10 | int8_t requestedFavoriteTilt(int8_t requestTilt, tilt_types tilt, float myTiltPos, bool flip) { | |
| 33 | 10 | if(tilt == tilt_types::none) return -1; | |
| 34 | 8 | if(requestTilt >= 0) return requestTilt; | |
| 35 | 4 | return displayPosition(myTiltPos, flip); | |
| 36 | } | ||
| 37 | 5 | float limitTarget(int8_t dir) { return dir > 0 ? 100.0f : 0.0f; } | |
| 38 | 29 | bool stepPosition(float pos, int8_t dir, uint32_t travelMs, uint16_t stepSize, uint8_t frameStepSize, float &out) { | |
| 39 | 29 | if(travelMs == 0 || stepSize == 0) return false; | |
| 40 | 22 | const float step = 100.0f / (static_cast<float>(travelMs) / static_cast<float>(stepSize * frameStepSize)); | |
| 41 | 22 | out = dir > 0 ? std::min(100.0f, pos + step) : std::max(0.0f, pos - step); | |
| 42 | 22 | return true; | |
| 43 | } | ||
| 44 | 26 | StepTargets stepTargets(tilt_types tilt, int8_t dir, float pos, float tiltPos, | |
| 45 | uint32_t upTime, uint32_t downTime, uint32_t tiltTime, uint16_t stepSize, uint8_t frameStepSize) { | ||
| 46 | 26 | StepTargets t; | |
| 47 | 26 | if(stepSize == 0) return t; | |
| 48 | 30 | auto atEnd = [dir](float p) { return dir > 0 ? p >= 100.0f : p <= 0.0f; }; | |
| 49 | 25 | const uint32_t liftTime = dir > 0 ? downTime : upTime; | |
| 50 | float step; | ||
| 51 | 25 | if(tilt == tilt_types::integrated) { | |
| 52 | 6 | if(atEnd(tiltPos) && atEnd(pos)) return t; | |
| 53 | 4 | if(!atEnd(tiltPos)) { | |
| 54 | 2 | if(!stepPosition(tiltPos, dir, tiltTime, stepSize, frameStepSize, step)) return t; | |
| 55 | 1 | t.setLift = true; | |
| 56 | 1 | t.lift = pos; | |
| 57 | 1 | t.setTilt = true; | |
| 58 | 1 | t.tilt = step; | |
| 59 | } | ||
| 60 | else { | ||
| 61 | 2 | if(!stepPosition(pos, dir, liftTime, stepSize, frameStepSize, step)) return t; | |
| 62 | 2 | t.setLift = true; | |
| 63 | 2 | t.lift = step; | |
| 64 | 2 | t.setTilt = true; | |
| 65 | 2 | t.tilt = tiltPos; | |
| 66 | } | ||
| 67 | } | ||
| 68 | 19 | else if(tilt == tilt_types::tiltonly) { | |
| 69 | 3 | if(!stepPosition(tiltPos, dir, tiltTime, stepSize, frameStepSize, step)) return t; | |
| 70 | 2 | t.setTilt = true; | |
| 71 | 2 | t.tilt = step; | |
| 72 | } | ||
| 73 | else { | ||
| 74 | 16 | if(atEnd(pos)) { | |
| 75 | 2 | t.action = StepAction::none; | |
| 76 | 2 | return t; | |
| 77 | } | ||
| 78 | 14 | if(!stepPosition(pos, dir, liftTime, stepSize, frameStepSize, step)) return t; | |
| 79 | 12 | t.setLift = true; | |
| 80 | 12 | t.lift = step; | |
| 81 | } | ||
| 82 | 17 | t.action = StepAction::move; | |
| 83 | 17 | return t; | |
| 84 | } | ||
| 85 | 50 | void TxHold::mark(uint32_t ticket, bool moving, uint32_t now) { | |
| 86 | 50 | if(ticket == 0 || this->ticket != 0 || moving) return; | |
| 87 | 41 | this->ticket = ticket; | |
| 88 | 41 | this->since = now; | |
| 89 | } | ||
| 90 | 53 | bool TxHold::release(uint32_t ticket) { | |
| 91 | 53 | if(ticket == 0 || ticket != this->ticket) return false; | |
| 92 | 25 | this->ticket = 0; | |
| 93 | 25 | return true; | |
| 94 | } | ||
| 95 | 16357 | TxHoldCheck TxHold::check(bool moving, uint32_t now, uint32_t timeoutMs) { | |
| 96 | 16357 | if(!this->held()) return TxHoldCheck::none; | |
| 97 | 3206 | if(!moving) { | |
| 98 | // The command didn't set the shade moving so there is nothing to hold. | ||
| 99 | 2 | this->clear(); | |
| 100 | 2 | return TxHoldCheck::none; | |
| 101 | } | ||
| 102 | 3204 | if(!this->timedOut(now, timeoutMs)) return TxHoldCheck::holding; | |
| 103 | 3 | this->clear(); | |
| 104 | 3 | return TxHoldCheck::expired; | |
| 105 | } | ||
| 106 | 15 | void ShadeTxState::stopSent(uint32_t ticket, uint32_t now, int8_t dir, bool tilt) { | |
| 107 | 15 | this->stopTicket = ticket; | |
| 108 | 15 | this->stopAt = now; | |
| 109 | 15 | this->stopDir = dir; | |
| 110 | 15 | this->stopTilt = tilt; | |
| 111 | 15 | } | |
| 112 | 10 | void ShadeTxState::setMySent(uint32_t ticket, float oldPos, float oldTiltPos) { | |
| 113 | 10 | this->myTicket = ticket; | |
| 114 | 10 | this->myOldPos = oldPos; | |
| 115 | 10 | this->myOldTiltPos = oldTiltPos; | |
| 116 | 10 | } | |
| 117 | 47 | ShadeTxActions ShadeTxState::onEvent(const somfy_tx_event_t &evt, bool moving) { | |
| 118 | 47 | ShadeTxActions act; | |
| 119 | 47 | if(evt.ticket == 0) return act; | |
| 120 | 45 | if(this->hold.release(evt.ticket)) { | |
| 121 | 22 | if(evt.type == tx_event_t::started) { | |
| 122 | 17 | act.startMovement = true; | |
| 123 | 17 | act.startAt = evt.at; | |
| 124 | } | ||
| 125 | 5 | else act.cancelMovement = true; | |
| 126 | } | ||
| 127 | 45 | if(this->myTicket == evt.ticket && evt.type != tx_event_t::started) { | |
| 128 | 6 | this->myTicket = 0; | |
| 129 | // The motor only stores the favorite when it gets the whole long press. | ||
| 130 | 6 | if(evt.type == tx_event_t::finished) act.commitMy = true; | |
| 131 | else { | ||
| 132 | 4 | act.restoreMy = true; | |
| 133 | 4 | act.myOldPos = this->myOldPos; | |
| 134 | 4 | act.myOldTiltPos = this->myOldTiltPos; | |
| 135 | } | ||
| 136 | } | ||
| 137 | 45 | if(this->stopTicket == evt.ticket) { | |
| 138 | 9 | this->stopTicket = 0; | |
| 139 | // Unless something has moved the shade since, the motor never got the stop | ||
| 140 | // and is still running. | ||
| 141 | 9 | if(evt.type == tx_event_t::dropped && !moving) { | |
| 142 | 4 | act.runToLimit = true; | |
| 143 | 4 | act.limitDir = this->stopDir; | |
| 144 | 4 | act.limitTilt = this->stopTilt; | |
| 145 | 4 | act.limitSince = this->stopAt; | |
| 146 | } | ||
| 147 | } | ||
| 148 | 45 | return act; | |
| 149 | } | ||
| 150 |