rcbench
Servo and ESC test bench: panel, IOmcu and shared C
Loading...
Searching...
No Matches
arming.h File Reference

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.

Detailed Description

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:

  • A stop latches. Only an explicit arm clears it.
  • The heartbeat is suppressed while a stop is latched, and the IOmcu refuses to arm while the heartbeat is not trusted. An arm therefore clears the latch FIRST and waits for the line to settle before the write, or the two conditions deadlock each other.
  • A stop during that wait wins. The arm is abandoned, not queued.
  • With a far end to ask, the wait ends when the far end reports the line trusted and not when a fixed time is up: the monitor needs five edges, the fifth is 100 to 125ms after the latch clears at a 5ms pass, and later on a slower one. The wait is bounded; an arm the far end never trusts is given up before CLEAR is written.
  • A link that goes quiet while the bench is armed, or while an arm is waiting, is a stop. The far end may have restarted, and an ARM written again when it answers would arm it with no hand on the panel. A bank armed with no link is stopped when a far end appears, for the same reason.
  • Touch that has stopped answering disarms and refuses to arm: the panel is the only place a STOP button exists.
  • The run clock times a run, not the panel.
  • An arm covers the roles it was asked for. Asking for other roles while armed passes through a disarm: ARMING_ACT_DISARM first, then ARMING_ACT_ARM for the new roles after the settle, and a new run.

SPDX-License-Identifier: MIT

Macro Definition Documentation

◆ ARMING_LINE_WAIT_MS

#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.

Enumeration Type Documentation

◆ 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.

◆ arming_hs_at_t

Where the panel is in writing the arm arming_step() handed out with ARMING_ACT_ARM; see arming_handshake_step().

Enumerator
ARMING_HS_HANDED 

Just handed out: nothing done for it yet.

ARMING_HS_PREPARED 

The command is zero and the surfaces are asked to centre.

ARMING_HS_CLEARED 

CLEAR came back acknowledged.

A value outside this enum takes this point's rules.

ARMING_HS_ARMED 

The frame that arms came back acknowledged: the far end is armed.

◆ 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.

◆ arming_quiet_t

What an exchange with the far end that ended unanswered means; see arming_exchange_unanswered().

Enumerator
ARMING_QUIET_NOTHING 

The link was already down: a probe nobody answered.

Nothing.

ARMING_QUIET_LINK_DOWN 

The link is gone.

The caller sends nothing more on it and takes it down at its next poll.

ARMING_QUIET_STOP 

The link is gone under an armed bench or a waiting arm: a stop is latched as well, and the caller disarms its bank and zeroes the command now.

Function Documentation

◆ 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.

Parameters
aPolicy state.
link_uptrue when the link was up when the exchange was sent.
bank_armedtrue when this end's bank is armed.
Returns
What the caller does about the unanswered exchange.

◆ arming_handshake_open()

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:

  • before CLEAR, as arming_refused() gives one up. Nothing was written and the far end's latch stands;
  • after CLEAR, as a stop from the far end. The CLEAR released the far end's latch and no frame can set it again, so the stop withholds the heartbeat, and the far end sets its latch when it stops trusting the line, within HEARTBEAT_MAX_GAP_MS (150ms).

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.

Parameters
aPolicy state.
now_msCurrent time, in milliseconds.
clearedtrue once CLEAR has been acknowledged.
Returns
true up to and at the deadline; false once it has passed or no arm is in hand.

◆ arming_handshake_step()

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.

Parameters
aPolicy state.
atPoint the arm has reached.
fWhat the panel learned for the arm.
now_msCurrent time, in milliseconds.
Returns
What the panel does next with the arm.

◆ arming_heartbeat()

bool arming_heartbeat ( const arming_t * a,
uint32_t now_ms )

Whether the heartbeat may edge.

Parameters
aPolicy state.
now_msCurrent time, in milliseconds.
Returns
true when the heartbeat may edge.

◆ arming_init()

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.

Parameters
aPolicy state to initialise.
now_msCurrent time, in milliseconds.
settle_msTime the heartbeat is given to become trusted, in milliseconds.

◆ arming_line_nobody()

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.

Parameters
aPolicy state.

◆ arming_line_report()

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.

Parameters
aPolicy state.
trustedtrue when the far end's monitor believes the line.

◆ arming_line_wanted()

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.

Parameters
aPolicy state.
now_msCurrent time, in milliseconds.
Returns
true when the caller is to ask the far end in this pass.

◆ arming_link_found()

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.

Parameters
aPolicy state.
bank_armedtrue when this end's bank is armed.
Returns
true when it stopped, and the caller disarms its bank.

◆ arming_link_lost()

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.

Parameters
aPolicy state.
bank_armedtrue when this end's bank is armed.
Returns
true when it stopped, and the caller disarms its bank.

◆ arming_link_needed()

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.

Parameters
aPolicy state.
bank_armedtrue when this end's bank is armed.
Returns
true when a link going quiet now would be a stop.

◆ arming_pressed_count()

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().

Parameters
aPolicy state.
Returns
The number of stops pressed by an operator.

◆ arming_refused()

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.

Parameters
aPolicy state.

◆ arming_request_arm()

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.

Parameters
aPolicy state.
now_msCurrent time, in milliseconds.

◆ arming_request_arm_roles()

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.

Parameters
aPolicy state.
now_msCurrent time, in milliseconds.
rolesNon-zero set of the caller's role bits; 0 is ignored.

◆ arming_request_disarm()

void arming_request_disarm ( arming_t * a)

The operator asked to disarm.

Parameters
aPolicy state.

◆ arming_roles()

uint8_t arming_roles ( const arming_t * a)

The roles of the arm in force or waiting: 0 before any request.

Parameters
aPolicy state.
Returns
The roles of the arm in force or waiting.

◆ arming_run_seconds()

uint32_t arming_run_seconds ( const arming_t * a)

Seconds of the current run, or of the last one once it has ended.

Parameters
aPolicy state.
Returns
Seconds of the current run, or of the last one.

◆ arming_set_line_wait()

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.

Parameters
aPolicy state.
wait_msBound past the settle, in milliseconds; 0 turns the wait off.

◆ arming_step()

arming_action_t arming_step ( arming_t * a,
uint32_t now_ms )

Advance, and say what the link owes the IOmcu.

Parameters
aPolicy state.
now_msCurrent time, in milliseconds.
Returns
The action the caller must put on the link.

◆ arming_stop()

void arming_stop ( arming_t * a)

STOP.

Latches; abandons an arm that is waiting for the line.

Parameters
aPolicy state.

◆ arming_stop_count()

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.

Parameters
aPolicy state.
Returns
The number of stops applied since arming_init().

◆ arming_stop_from_far_end()

void arming_stop_from_far_end ( arming_t * a)

The IOmcu disarmed us: a NACK on a control write, or a failsafe.

Parameters
aPolicy state.

◆ arming_stop_pressed()

void arming_stop_pressed ( arming_t * a)

A STOP an operator pressed: arming_stop(), counted as pressed too.

Parameters
aPolicy state.

◆ arming_stop_unserved()

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.

Parameters
aPolicy state.
servedStop count last let go; updated when true is returned.
Returns
true when a stop applied since served is owed to the caller.

◆ arming_stopped()

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.

Parameters
aPolicy state.
Returns
true when the stop latch is set.

◆ arming_touch_dead()

bool arming_touch_dead ( const arming_t * a,
uint32_t now_ms )

True once touch has been silent for ARMING_TOUCH_DEAD_MS.

Parameters
aPolicy state.
now_msCurrent time, in milliseconds.
Returns
true when touch has been silent for ARMING_TOUCH_DEAD_MS.

◆ arming_touch_poll()

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.

Parameters
aPolicy state.
now_msCurrent time, in milliseconds.

◆ arming_touch_seen()

void arming_touch_seen ( arming_t * a,
uint32_t now_ms )

The touch controller answered.

Parameters
aPolicy state.
now_msCurrent time, in milliseconds.

◆ arming_write_failed()

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.

Parameters
aPolicy state.
answeredtrue when the far end answered with a refusal.
Returns
true when the arm was stopped.