|
rcbench
Servo and ESC test bench: panel, IOmcu and shared C
|
When the bench is allowed to be armed, and what the panel must tell the IOmcu about it. More...
#include <stdbool.h>#include <stdint.h>Go to the source code of this file.
Data Structures | |
| struct | arming_t |
| struct | arming_hs_facts_t |
| What the panel learned for the arm since the last point. More... | |
Macros | |
| #define | ARMING_TOUCH_DEAD_MS 500u |
| How long the touch controller may go without answering before the bench is disarmed and refused an arm. | |
| #define | ARMING_LINE_WAIT_MS 200u |
| How long past the settle the panel waits for the far end to report the line trusted before it gives the arm up: 200ms, 300ms from the hold with the 100ms settle. | |
| #define | ARMING_ROLES_ALL 0xFFu |
| Every role: what arming_request_arm() asks for. | |
Enumerations | |
| enum | arming_quiet_t { ARMING_QUIET_NOTHING = 0 , ARMING_QUIET_LINK_DOWN , ARMING_QUIET_STOP } |
| What an exchange with the far end that ended unanswered means; see arming_exchange_unanswered(). More... | |
| enum | arming_action_t { ARMING_ACT_NONE = 0 , ARMING_ACT_DISARM , ARMING_ACT_ARM , ARMING_ACT_GIVE_UP } |
| What the caller must put on the link, if anything, after a step. More... | |
| enum | arming_hs_at_t { ARMING_HS_HANDED = 0 , ARMING_HS_PREPARED , ARMING_HS_CLEARED , ARMING_HS_ARMED } |
| Where the panel is in writing the arm arming_step() handed out with ARMING_ACT_ARM; see arming_handshake_step(). More... | |
| enum | arming_hs_t { ARMING_HS_GO = 0 , ARMING_HS_LOCAL , ARMING_HS_PROG , ARMING_HS_SERVO_HELD , ARMING_HS_SERVO_RATE , ARMING_HS_ENDPOINTS , ARMING_HS_CLOSED , ARMING_HS_WITHDRAWN } |
| What the panel does next with the arm, after arming_handshake_step(). More... | |
Functions | |
| void | arming_init (arming_t *a, uint32_t now_ms, uint32_t settle_ms) |
settle_ms is how long the heartbeat is given to become trustworthy before an arm is written: HEARTBEAT_GOOD_RUN intervals, plus one. | |
| void | arming_set_line_wait (arming_t *a, uint32_t wait_ms) |
Make an arm wait for the far end's word on the line, for at most wait_ms past the settle. 0 turns the wait off. | |
| bool | arming_line_wanted (const arming_t *a, uint32_t now_ms) |
| Whether the caller is to ask the far end about the line in this pass. | |
| void | arming_line_report (arming_t *a, bool trusted) |
What the far end said: trusted when its monitor believes the line. | |
| void | arming_line_nobody (arming_t *a) |
| There is no far end to ask in this pass: the next arming_step() lets the settle alone decide, bound or no bound. | |
| void | arming_touch_seen (arming_t *a, uint32_t now_ms) |
| The touch controller answered. | |
| bool | arming_touch_dead (const arming_t *a, uint32_t now_ms) |
| True once touch has been silent for ARMING_TOUCH_DEAD_MS. | |
| bool | arming_stopped (const arming_t *a) |
| Whether the stop latch is set: a STOP, a dead touch, or the far end. | |
| uint32_t | arming_stop_count (const arming_t *a) |
| How many stops have been applied. | |
| void | arming_stop_pressed (arming_t *a) |
| A STOP an operator pressed: arming_stop(), counted as pressed too. | |
| uint32_t | arming_pressed_count (const arming_t *a) |
| How many of the stops were pressed (arming_stop_pressed()); never more than arming_stop_count() has counted since arming_init(). | |
| void | arming_touch_poll (arming_t *a, uint32_t now_ms) |
| Sample the touch controller's health, and act on the edge where it dies. | |
| void | arming_stop (arming_t *a) |
| STOP. | |
| void | arming_request_arm (arming_t *a, uint32_t now_ms) |
| The operator asked to arm every role: arming_request_arm_roles() with ARMING_ROLES_ALL. | |
| void | arming_request_arm_roles (arming_t *a, uint32_t now_ms, uint8_t roles) |
The operator asked to arm the outputs of roles, a non-zero set of the caller's role bits; 0 is ignored. | |
| uint8_t | arming_roles (const arming_t *a) |
| The roles of the arm in force or waiting: 0 before any request. | |
| void | arming_request_disarm (arming_t *a) |
| The operator asked to disarm. | |
| void | arming_refused (arming_t *a) |
| The IOmcu refused the arm. | |
| bool | arming_handshake_open (arming_t *a, uint32_t now_ms, bool cleared) |
Whether the arm arming_step() handed out may still write its next frame, asked before CLEAR (cleared false) and before the frame that arms (cleared true, the CLEAR acknowledged). | |
| bool | arming_write_failed (arming_t *a, bool answered) |
| The CLEAR or the frame that arms did not come back acknowledged. | |
| arming_hs_t | arming_handshake_step (arming_t *a, arming_hs_at_t at, const arming_hs_facts_t *f, uint32_t now_ms) |
The next step of writing the arm arming_step() handed out, at at with what the panel learned in f. | |
| bool | arming_stop_unserved (const arming_t *a, uint32_t *served) |
Whether a stop has been applied that the caller has not let go for yet: true once for every change of arming_stop_count() from *served, which it then takes. | |
| void | arming_stop_from_far_end (arming_t *a) |
| The IOmcu disarmed us: a NACK on a control write, or a failsafe. | |
| arming_quiet_t | arming_exchange_unanswered (arming_t *a, bool link_up, bool bank_armed) |
| An exchange with the far end ended with no answer: it waited out its timeout, or its frame did not reach the wire. | |
| bool | arming_link_needed (const arming_t *a, bool bank_armed) |
Whether a link going quiet now would be a stop: this end's bank is armed (bank_armed), the policy is armed, or an arm is waiting. | |
| bool | arming_link_lost (arming_t *a, bool bank_armed) |
| The far end stopped answering. | |
| bool | arming_link_found (arming_t *a, bool bank_armed) |
| A far end started answering. | |
| arming_action_t | arming_step (arming_t *a, uint32_t now_ms) |
| Advance, and say what the link owes the IOmcu. | |
| bool | arming_heartbeat (const arming_t *a, uint32_t now_ms) |
| Whether the heartbeat may edge. | |
| uint32_t | arming_run_seconds (const arming_t *a) |
| Seconds of the current run, or of the last one once it has ended. | |
When the bench is allowed to be armed, and what the panel must tell the IOmcu about it.
The policy only: no touch driver, no link, no task. The panel's control loop feeds it what happened and acts on what it returns, and the host suite can then hold the rules that decide whether something spins.
The rules, each of which has cost this project a defect:
SPDX-License-Identifier: MIT
| #define ARMING_LINE_WAIT_MS 200u |
How long past the settle the panel waits for the far end to report the line trusted before it gives the arm up: 200ms, 300ms from the hold with the 100ms settle.
The monitor needs five edges. An edge is emitted on the first pass at or after HEARTBEAT_PERIOD_MS (20ms) from the last, so five take 100ms at a 5ms pass, 135ms at 9ms and 300ms at a pass of 30ms, six times the control task's period.
| enum arming_action_t |
What the caller must put on the link, if anything, after a step.
| Enumerator | |
|---|---|
| ARMING_ACT_DISARM | write ARM = 0 |
| ARMING_ACT_ARM | write CLEAR, then ARM = arming_roles() |
| ARMING_ACT_GIVE_UP | The far end did not come to trust the line within the bound: the arm is given up, nothing is written, the operator is told. |
| enum arming_hs_at_t |
Where the panel is in writing the arm arming_step() handed out with ARMING_ACT_ARM; see arming_handshake_step().
| enum arming_hs_t |
What the panel does next with the arm, after arming_handshake_step().
| Enumerator | |
|---|---|
| ARMING_HS_GO | Go on: at ARMING_HS_HANDED prepare, at ARMING_HS_PREPARED write CLEAR, at ARMING_HS_CLEARED write the frame that arms. |
| ARMING_HS_LOCAL | No link: arm this end's bank for arming_roles(), nothing written. |
| ARMING_HS_PROG | The servo programmer holds the port: given up as arming_refused() gives one up, nothing written. |
| ARMING_HS_SERVO_HELD | A servo release the far end has not taken: the arm fails as a write that did not go through, nothing written. The policy is unchanged; the caller ends the arm as arming_write_failed() or arming_link_lost() does, by whether the link went quiet. |
| ARMING_HS_SERVO_RATE | The surfaces' rate is not known; the same as ARMING_HS_SERVO_HELD. |
| ARMING_HS_ENDPOINTS | The throttle's pulse endpoints are not in force at the far end. At ARMING_HS_PREPARED the same as ARMING_HS_SERVO_HELD; at ARMING_HS_CLEARED given up as arming_refused() gives one up, and the frame that arms is not written; at ARMING_HS_ARMED the arm is taken back as arming_request_disarm() takes one back, and the caller writes the disarm at once. |
| ARMING_HS_CLOSED | Past the arm's deadline: given up as arming_handshake_open() gives it up, before CLEAR as a refusal and after it as a stop. |
| ARMING_HS_WITHDRAWN | A stop or a disarm landed while CLEAR was on the wire: given up as arming_refused() gives one up. The operator asked for it. |
| enum arming_quiet_t |
What an exchange with the far end that ended unanswered means; see arming_exchange_unanswered().
| arming_quiet_t arming_exchange_unanswered | ( | arming_t * | a, |
| bool | link_up, | ||
| bool | bank_armed ) |
An exchange with the far end ended with no answer: it waited out its timeout, or its frame did not reach the wire.
A refusal is an answer and is not reported here.
The one decision for every exchange, whatever it carried and wherever in a pass it was sent. link_up is whether the caller held the link as up when it sent it; bank_armed whether its bank is armed.
link down ARMING_QUIET_NOTHING
link up, disarmed ARMING_QUIET_LINK_DOWN
link up, armed or arming ARMING_QUIET_STOP, the stop latched as
arming_link_lost() latches it
With the link down the exchange was a probe, and a bank armed with no far end – the simulated bench – runs on through probes nobody answers.
| a | Policy state. |
| link_up | true when the link was up when the exchange was sent. |
| bank_armed | true when this end's bank is armed. |
| bool arming_handshake_open | ( | arming_t * | a, |
| uint32_t | now_ms, | ||
| bool | cleared ) |
Whether the arm arming_step() handed out may still write its next frame, asked before CLEAR (cleared false) and before the frame that arms (cleared true, the CLEAR acknowledged).
The arm's deadline is the one the wait for the line ends at, settle plus line wait from the hold. The exchanges of the handshake are answered in about a millisecond each and can be answered up to the exchange timeout late; the deadline is what keeps a slow one from arming the bench that much after the hold. True up to and at the deadline. Past it the arm is given up and false is returned; the caller writes nothing further and tells the operator, who holds ARM again:
Also false, with nothing changed, when no arm is in hand. Always true for an arm that asked no far end. What it cannot bound is the frame that arms itself: one acknowledged late has armed the far end, at rest.
| a | Policy state. |
| now_ms | Current time, in milliseconds. |
| cleared | true once CLEAR has been acknowledged. |
| arming_hs_t arming_handshake_step | ( | arming_t * | a, |
| arming_hs_at_t | at, | ||
| const arming_hs_facts_t * | f, | ||
| uint32_t | now_ms ) |
The next step of writing the arm arming_step() handed out, at at with what the panel learned in f.
HANDED port taken ARMING_HS_PROG otherwise ARMING_HS_GO PREPARED link down ARMING_HS_LOCAL release owed ARMING_HS_SERVO_HELD rate not settled ARMING_HS_SERVO_RATE endpoints blocked ARMING_HS_ENDPOINTS deadline passed ARMING_HS_CLOSED otherwise ARMING_HS_GO, write CLEAR CLEARED stopped, or a disarm asked ARMING_HS_WITHDRAWN endpoints blocked ARMING_HS_ENDPOINTS deadline passed ARMING_HS_CLOSED otherwise ARMING_HS_GO, write ARM ARMED endpoints blocked ARMING_HS_ENDPOINTS otherwise ARMING_HS_GO
The verdicts that end the arm change the policy as their comments say; ARMING_HS_GO and ARMING_HS_LOCAL change nothing. The CLEAR and the frame that arms can still fail on the wire; arming_write_failed() and the caller's own rule for a link gone quiet decide those.
| a | Policy state. |
| at | Point the arm has reached. |
| f | What the panel learned for the arm. |
| now_ms | Current time, in milliseconds. |
| bool arming_heartbeat | ( | const arming_t * | a, |
| uint32_t | now_ms ) |
Whether the heartbeat may edge.
| a | Policy state. |
| now_ms | Current time, in milliseconds. |
| void arming_init | ( | arming_t * | a, |
| uint32_t | now_ms, | ||
| uint32_t | settle_ms ) |
settle_ms is how long the heartbeat is given to become trustworthy before an arm is written: HEARTBEAT_GOOD_RUN intervals, plus one.
| a | Policy state to initialise. |
| now_ms | Current time, in milliseconds. |
| settle_ms | Time the heartbeat is given to become trusted, in milliseconds. |
| void arming_line_nobody | ( | arming_t * | a | ) |
There is no far end to ask in this pass: the next arming_step() lets the settle alone decide, bound or no bound.
Nothing can be stale where nothing was asked, and with no link the pass itself can be a second late behind an identity probe nobody answers. Holds for that one step; a caller with no link says so before every step.
| a | Policy state. |
| void arming_line_report | ( | arming_t * | a, |
| bool | trusted ) |
What the far end said: trusted when its monitor believes the line.
A question nobody answered is reported as false. With no far end the caller reports true. Ignored unless an arm is waiting.
| a | Policy state. |
| trusted | true when the far end's monitor believes the line. |
| bool arming_line_wanted | ( | const arming_t * | a, |
| uint32_t | now_ms ) |
Whether the caller is to ask the far end about the line in this pass.
| a | Policy state. |
| now_ms | Current time, in milliseconds. |
| bool arming_link_found | ( | arming_t * | a, |
| bool | bank_armed ) |
A far end started answering.
A bank armed with no link – the simulator's – is stopped the same way: the far end was never asked to arm, and the ARM written at every poll would ask it now. An arm still waiting is left to complete: it writes CLEAR and the frame that arms, as an arm made with the link up does.
Returns true when it stopped, with the same duty for the caller.
| a | Policy state. |
| bank_armed | true when this end's bank is armed. |
| bool arming_link_lost | ( | arming_t * | a, |
| bool | bank_armed ) |
The far end stopped answering.
bank_armed is whether this end's bank is armed. An armed bench and an arm that is waiting are stopped as arming_stop_from_far_end() stops them; a disarmed bench is left as it is.
Returns true when it stopped: the caller then disarms its bank and zeroes the command, because the policy's own disarm is gated on armed, which the stop has already cleared.
| a | Policy state. |
| bank_armed | true when this end's bank is armed. |
| bool arming_link_needed | ( | const arming_t * | a, |
| bool | bank_armed ) |
Whether a link going quiet now would be a stop: this end's bank is armed (bank_armed), the policy is armed, or an arm is waiting.
What arming_link_lost() acts on; a caller asks it to decide whether an unanswered exchange is worth taking the link down for at once.
| a | Policy state. |
| bank_armed | true when this end's bank is armed. |
| uint32_t arming_pressed_count | ( | const arming_t * | a | ) |
How many of the stops were pressed (arming_stop_pressed()); never more than arming_stop_count() has counted since arming_init().
| a | Policy state. |
| void arming_refused | ( | arming_t * | a | ) |
The IOmcu refused the arm.
The latch stays clear: a heartbeat that is running is the truth about a loop that is running, and the operator can ask again.
| a | Policy state. |
| void arming_request_arm | ( | arming_t * | a, |
| uint32_t | now_ms ) |
The operator asked to arm every role: arming_request_arm_roles() with ARMING_ROLES_ALL.
| a | Policy state. |
| now_ms | Current time, in milliseconds. |
| void arming_request_arm_roles | ( | arming_t * | a, |
| uint32_t | now_ms, | ||
| uint8_t | roles ) |
The operator asked to arm the outputs of roles, a non-zero set of the caller's role bits; 0 is ignored.
Ignored while touch is dead.
Asked while armed for other roles, the bench is not re-armed in place: the next arming_step() returns ARMING_ACT_DISARM, and the arm for roles follows the settle as any arm does. The outputs of the old roles have stopped before those of the new ones start, and the run clock starts a new run. A STOP in between wins, as it does over any arm that waits.
| a | Policy state. |
| now_ms | Current time, in milliseconds. |
| roles | Non-zero set of the caller's role bits; 0 is ignored. |
| void arming_request_disarm | ( | arming_t * | a | ) |
The operator asked to disarm.
| a | Policy state. |
| uint8_t arming_roles | ( | const arming_t * | a | ) |
The roles of the arm in force or waiting: 0 before any request.
| a | Policy state. |
| uint32_t arming_run_seconds | ( | const arming_t * | a | ) |
Seconds of the current run, or of the last one once it has ended.
| a | Policy state. |
| void arming_set_line_wait | ( | arming_t * | a, |
| uint32_t | wait_ms ) |
Make an arm wait for the far end's word on the line, for at most wait_ms past the settle. 0 turns the wait off.
From the settle to the bound arming_line_wanted() is true on every pass until arming_line_report() has said the line is trusted; arming_step() then returns ARMING_ACT_ARM in that pass. At the bound without a yes, and past the bound whatever was reported, it returns ARMING_ACT_GIVE_UP: the report is an exchange that can come back late, and a yes that does arms nothing.
| a | Policy state. |
| wait_ms | Bound past the settle, in milliseconds; 0 turns the wait off. |
| arming_action_t arming_step | ( | arming_t * | a, |
| uint32_t | now_ms ) |
Advance, and say what the link owes the IOmcu.
| a | Policy state. |
| now_ms | Current time, in milliseconds. |
| void arming_stop | ( | arming_t * | a | ) |
STOP.
Latches; abandons an arm that is waiting for the line.
| a | Policy state. |
| uint32_t arming_stop_count | ( | const arming_t * | a | ) |
How many stops have been applied.
Changes on every stop, latched or not, so a caller can act on the event rather than on the level.
| a | Policy state. |
| void arming_stop_from_far_end | ( | arming_t * | a | ) |
The IOmcu disarmed us: a NACK on a control write, or a failsafe.
| a | Policy state. |
| void arming_stop_pressed | ( | arming_t * | a | ) |
A STOP an operator pressed: arming_stop(), counted as pressed too.
| a | Policy state. |
| bool arming_stop_unserved | ( | const arming_t * | a, |
| uint32_t * | served ) |
Whether a stop has been applied that the caller has not let go for yet: true once for every change of arming_stop_count() from *served, which it then takes.
Every stop counts, latched or not, armed or not, and two stops between calls are let go for once.
| a | Policy state. |
| served | Stop count last let go; updated when true is returned. |
served is owed to the caller. | bool arming_stopped | ( | const arming_t * | a | ) |
Whether the stop latch is set: a STOP, a dead touch, or the far end.
A screen asks so a gesture already under way can be abandoned.
| a | Policy state. |
| bool arming_touch_dead | ( | const arming_t * | a, |
| uint32_t | now_ms ) |
True once touch has been silent for ARMING_TOUCH_DEAD_MS.
| a | Policy state. |
| now_ms | Current time, in milliseconds. |
| void arming_touch_poll | ( | arming_t * | a, |
| uint32_t | now_ms ) |
Sample the touch controller's health, and act on the edge where it dies.
Called by whatever judges touch, as often as it judges it, and by arming_step(). The two are not the same caller and need not run at the same rate: a link exchange can wait a second while touch is still being pumped, and a controller that died and recovered inside that wait would leave nothing for the policy to see afterwards.
| a | Policy state. |
| now_ms | Current time, in milliseconds. |
| void arming_touch_seen | ( | arming_t * | a, |
| uint32_t | now_ms ) |
The touch controller answered.
| a | Policy state. |
| now_ms | Current time, in milliseconds. |
| bool arming_write_failed | ( | arming_t * | a, |
| bool | answered ) |
The CLEAR or the frame that arms did not come back acknowledged.
answered: the far end answered and refused, which is arming_refused(). Not answered, the exchange has waited out its timeout and the link has gone quiet under an arm: a stop, as arming_link_lost() makes one. The far end may have taken the frame and lost only the acknowledgement, so it may be armed; the stop withholds the heartbeat from it.
Returns true when it stopped, with arming_link_lost()'s duty for the caller.
| a | Policy state. |
| answered | true when the far end answered with a refusal. |