Library Apps.TrustedTickBox
Require Import Coq.Program.Basics Coq.NArith.NArith Coq.Lists.List.
Require Import FunctionApp.
Set Implicit Arguments.
Local Open Scope list_scope.
Local Open Scope program_scope.
Set Implicit Arguments.
Require Import FunctionApp.
Set Implicit Arguments.
Local Open Scope list_scope.
Local Open Scope program_scope.
Set Implicit Arguments.
Summary
System Clock (tick) │ ↓ notify change ┌──────────┐ ---------------> │ │ │ │ publish value │ │ (data in) │ Tick Box │ -----> publish update (data out) ---------------> │ │ │ │ <--------------- │ │ request update └──────────┘
Example
Configurable parameters
- publishInterval (X above) - the number of ticks to wait
between successive publishings. A value of 0 means to publish
as soon as the number of ticks increments.
- publishDuration (Y above) - the number of PublishIntervals
after the most recent change to continue publishing updates. A
value of 0 means to publish at most once for each change. A
special flag value of ∞ means to always publish.
- waitBeforeUpdateInterval (Z above) - the number of ticks to
wait after a publish before requesting an update.
- publishPrecision (W above) - Suppose the clock does not publish on every tick, and we find that, previously, we were given tick X₀ < X, and on the current update, we are given X₁ > X. We will emit a warning iff X₁ - X > W + 1.
Tick mod X ┌───────────────┐ Notify Change ┌───────────────────┐ ┌───────── │ initial state │───────────────────────> │ initially waiting │ ───┐ Tick mod X └────────> │ no data │ Fire: Request Update │ on data │ <──┘ └───────────────┘ └───────────────────┘ ^ │ | │ +--------------------+ | │ | Ready to publish? | | │ Data Ready +--------------------+ | │ Yes | | ^ | │ Fire: Warning | No | Tick │ | │ V V │ | V ┌────────────────────┐ | ┌───────────────────┐ ───┐ Tick to ≤ X Notify Change │ │ | Data Ready │ │ <──┘ ┌─────────── │ waiting on data │ ──────────────────────────> │ have data │ └──────────> │ │ | │ │ ───┐ Notify Change reset publishes └────────────────────┘ | └───────────────────┘ <──┘ reset publishes to 0 to 0 ^ | │ │ | │ │ | │ Fire: │ Tick to ≥ Z | │ Request Update │ | │ │ | │ │ | │ │ Tick to | │ │ < Z | │ │ ┌─────┐ | │ │ │ │ | │ │ V │ | │ ┌────────────────────┐ | │ Notify Change │ │ | │ ┌─────────── │ waiting on ticks │ | │ └──────────> │ │ | │ reset publishes └────────────────────┘ | │ to 0 ^ | │ | | │ Tick to > X | No | Yes │ Fire: Transmit Data | | V +-----------------------+ No +-------------------+ | | <------------- | | | Enough Publishes? | | Ticks too coarse? | | | Yes | | +-----------------------+ <------------- +-------------------+ Fire: Warning
Inductive PublishDurationT := durationOf (n : N) | inf.
Bind Scope duration_scope with PublishDurationT.
Delimit Scope duration_scope with duration.
Notation "∞" := inf : duration_scope.
Coercion durationOf : N >-> PublishDurationT.
Local Open Scope duration_scope.
Local Open Scope bool_scope.
Local Open Scope N_scope.
Definition duration_leb (x : N) (y : PublishDurationT) : bool :=
match y with
| durationOf y' ⇒ x <=? y'
| inf ⇒ true
end.
Infix "<=?" := duration_leb : duration_scope.
Local Open Scope duration_scope.
Notation "x >? y" := (y <? x) (at level 70, no associativity) : N_scope.
Notation "x >=? y" := (y <=? x) (at level 70, no associativity) : N_scope.
Section trustedTickBox.
Variable dataT : Type.
Inductive TickBoxPreState :=
| NoData (ticks : N)
| InitiallyWaitingOnData (ticks : N)
| HaveData (ticks : N) (data : dataT) (publishesSinceLastChange : option N)
| WaitingOnData (ticks : N) (publishesSinceLastChange : option N)
| WaitingOnTicks (ticks : N) (publishesSinceLastChange : option N).
Record TickBoxState :=
{
curData :> TickBoxPreState;
publishInterval : N;
publishDuration : PublishDurationT;
waitBeforeUpdateInterval : N;
publishPrecision : N
}.
Definition set_curData (st : TickBoxState) (v : TickBoxPreState)
:= {| curData := v;
publishInterval := st.(publishInterval);
publishDuration := st.(publishDuration);
waitBeforeUpdateInterval := st.(waitBeforeUpdateInterval);
publishPrecision := st.(publishPrecision) |}.
Definition set_publishInterval (st : TickBoxState) (v : N)
:= {| curData := st.(curData);
publishInterval := v;
publishDuration := st.(publishDuration);
waitBeforeUpdateInterval := st.(waitBeforeUpdateInterval);
publishPrecision := st.(publishPrecision) |}.
Definition set_publishDuration (st : TickBoxState) (v : PublishDurationT)
:= {| curData := st.(curData);
publishInterval := st.(publishInterval);
publishDuration := v;
waitBeforeUpdateInterval := st.(waitBeforeUpdateInterval);
publishPrecision := st.(publishPrecision) |}.
Definition set_waitBeforeUpdateInterval (st : TickBoxState) (v : N)
:= {| curData := st.(curData);
publishInterval := st.(publishInterval);
publishDuration := st.(publishDuration);
waitBeforeUpdateInterval := v;
publishPrecision := st.(publishPrecision) |}.
Definition set_publishPrecision (st : TickBoxState) (v : N)
:= {| curData := st.(curData);
publishInterval := st.(publishInterval);
publishDuration := st.(publishDuration);
waitBeforeUpdateInterval := st.(waitBeforeUpdateInterval);
publishPrecision := v |}.
Inductive tbConfigInput :=
| tbSetPublishInterval (_ : N)
| tbSetPublishDuration (_ : PublishDurationT)
| tbSetWaitBeforeUpdateInterval (_ : N)
| tbSetPublishPrecision (_ : N).
Inductive tbEventInput :=
| tbNotifyChange
| tbTick (addedTickCount : N)
| tbValueReady (val : dataT).
Definition tbInput := (tbConfigInput + tbEventInput)%type.
Inductive tbWarningOutput :=
| tbWarnNoDataReady
| tbWarnTicksTooInfrequent (ticks : N)
| tbWarnInvalidWaitBeforeUpdateInterval (_ : N)
| tbWarnInvalidEvent (st : TickBoxPreState) (ev : tbEventInput)
| tbDebugStateTransition (from to : TickBoxPreState) (ev : tbInput).
Inductive tbEventOutput :=
| tbRequestDataUpdate
| tbPublishUpdate (val : dataT)
| tbSleepFor (ticks : N).
Definition tbOutput := (tbWarningOutput + tbEventOutput)%type.
Context (world : Type)
(handle : tbOutput → action world).
The OCaml runtime / our framework doesn't seem to be able to
achieve a granularity of more than 3-0.3 milliseconds. So we
set the precision to 3 ms.
Definition initState : TickBoxState :=
{| curData := NoData 0;
publishInterval := 5000000000;
publishDuration := ∞;
waitBeforeUpdateInterval := 4000000000;
publishPrecision := 3000000 |}.
Definition ticksToTransmit (ticks : N) (st : TickBoxState) : N :=
st.(publishInterval) - ticks.
Definition ticksToGetUpdate (ticks : N) (st : TickBoxState) : N :=
st.(waitBeforeUpdateInterval) - ticks.
Definition invalidWaitBeforeUpdateInterval (val : N) (st : TickBoxState) : bool :=
val >=? st.(publishInterval).
{| curData := NoData 0;
publishInterval := 5000000000;
publishDuration := ∞;
waitBeforeUpdateInterval := 4000000000;
publishPrecision := 3000000 |}.
Definition ticksToTransmit (ticks : N) (st : TickBoxState) : N :=
st.(publishInterval) - ticks.
Definition ticksToGetUpdate (ticks : N) (st : TickBoxState) : N :=
st.(waitBeforeUpdateInterval) - ticks.
Definition invalidWaitBeforeUpdateInterval (val : N) (st : TickBoxState) : bool :=
val >=? st.(publishInterval).
Should we emit a warning about tbTick not being called often
enough?
Definition ticksTooCoarse (ticks : N) (st : TickBoxState) : bool :=
ticks - st.(publishInterval) >? st.(publishPrecision) + 1.
ticks - st.(publishInterval) >? st.(publishPrecision) + 1.
Should we emit a warning about tbTick not being called often
enough, when we're waiting to request an update?
Definition ticksTooCoarseWaitingOnTicks (ticks : N) (st : TickBoxState) : bool :=
ticks - st.(waitBeforeUpdateInterval) >? st.(publishPrecision) + 1.
ticks - st.(waitBeforeUpdateInterval) >? st.(publishPrecision) + 1.
Have we transmitted enough times since the last change to sleep?
Definition enoughTransmissions (publishesSinceLastChange : N) (st : TickBoxState) : bool :=
negb (publishesSinceLastChange <=? st.(publishDuration)).
Definition ticksMod (ticks : N) (st : TickBoxState)
: N
:= N.modulo ticks st.(publishInterval).
Definition tickBoxLoopPreBody'
(st : TickBoxState)
: tbInput → (list tbOutput) × TickBoxState
:= fun i ⇒
match i, st.(curData) with
| inl (tbSetPublishInterval val), _
⇒ (nil, set_publishInterval st val)
| inl (tbSetWaitBeforeUpdateInterval val), _
⇒ if invalidWaitBeforeUpdateInterval val st
then ((inl (tbWarnInvalidWaitBeforeUpdateInterval val))::nil, st)
else (nil, set_waitBeforeUpdateInterval st val)
| inl (tbSetPublishDuration val), _
⇒ (nil, set_publishDuration st val)
| inl (tbSetPublishPrecision val), _
⇒ (nil, set_publishPrecision st val)
| inr tbNotifyChange, NoData ticks
⇒ ((inr tbRequestDataUpdate)::nil, set_curData st (InitiallyWaitingOnData ticks))
| inr tbNotifyChange, HaveData ticks data publishesSinceLastChange
⇒ (nil, set_curData st (HaveData ticks data None))
| inr tbNotifyChange, WaitingOnData ticks publishesSinceLastChange
⇒ (nil, set_curData st (WaitingOnData ticks None))
| inr tbNotifyChange, WaitingOnTicks ticks publishesSinceLastChange
⇒ (nil, set_curData st (WaitingOnTicks ticks None))
| inr tbNotifyChange, InitiallyWaitingOnData ticks
⇒ (nil, st)
| inr (tbValueReady data), InitiallyWaitingOnData ticks
⇒ ((inr (tbSleepFor (ticksToTransmit ticks st)))::nil, set_curData st (HaveData ticks data (Some 0)))
| inr (tbValueReady data), WaitingOnData ticks publishesSinceLastChange
⇒ ((inr (tbSleepFor (ticksToTransmit ticks st)))::nil, set_curData st (HaveData ticks data publishesSinceLastChange))
| inr (tbValueReady data), HaveData _ _ _
⇒ ((inl (tbWarnInvalidEvent st.(curData) (tbValueReady data)))::nil, st)
| inr (tbValueReady data), NoData ticks
⇒ ((inl (tbWarnInvalidEvent st.(curData) (tbValueReady data)))::nil, st)
| inr (tbValueReady data), WaitingOnTicks _ _
⇒ ((inl (tbWarnInvalidEvent st.(curData) (tbValueReady data)))::nil, st)
| inr (tbTick ticksSinceLastTbTick), NoData ticks
⇒ (nil, set_curData st (NoData (ticksMod (ticks + ticksSinceLastTbTick) st)))
| inr (tbTick ticksSinceLastTbTick), InitiallyWaitingOnData ticks
⇒ (nil, set_curData st (InitiallyWaitingOnData (ticksMod (ticks + ticksSinceLastTbTick) st)))
| inr (tbTick ticksSinceLastTbTick), HaveData ticks data publishesSinceLastChange
⇒ let ticks' := ticksSinceLastTbTick + ticks in
let st' := set_curData st (HaveData ticks' data publishesSinceLastChange) in
let cur_ticksToTransmit := ticksToTransmit ticks' st' in
if cur_ticksToTransmit >? 0
then ((inr (tbSleepFor cur_ticksToTransmit))::nil, st')
else let publishesSinceLastChange' := match publishesSinceLastChange with
| None ⇒ 0
| Some n ⇒ n + 1
end in
let actions := (inr (tbPublishUpdate data))::nil in
let actions := (if ticksTooCoarse ticks' st'
then (inl (tbWarnTicksTooInfrequent (ticks' - st'.(publishInterval))))::actions
else actions) in
let ticks'' := ticksMod ticks' st' in
if enoughTransmissions publishesSinceLastChange' st'
then (actions, set_curData st' (NoData ticks''))
else ((inr (tbSleepFor (ticksToGetUpdate ticks'' st')))::actions,
set_curData st' (WaitingOnTicks ticks'' (Some publishesSinceLastChange')))
| inr (tbTick ticksSinceLastTick), WaitingOnTicks ticks publishesSinceLastChange
⇒ let ticks' := ticksSinceLastTick + ticks in
let st_request := set_curData st (WaitingOnData ticks' publishesSinceLastChange) in
let st_waiting := set_curData st (WaitingOnTicks ticks' publishesSinceLastChange) in
let actions := (if ticksTooCoarseWaitingOnTicks ticks' st
then (inl (tbWarnTicksTooInfrequent (ticks' - st.(waitBeforeUpdateInterval))))::nil
else nil) in
let cur_ticksToGetUpdate := ticksToGetUpdate ticks' st in
if cur_ticksToGetUpdate >? 0
then ((inr (tbSleepFor cur_ticksToGetUpdate))::actions, st_waiting)
else ((inr tbRequestDataUpdate)::actions, st_request)
| inr (tbTick ticksSinceLastTick), WaitingOnData ticks publishesSinceLastChange
⇒ let ticks' := ticks + ticksSinceLastTick in
((if ticksToTransmit ticks' st =? 0
then (inl tbWarnNoDataReady)::nil
else nil),
set_curData st (WaitingOnData ticks' publishesSinceLastChange))
end.
Definition tickBoxLoopPreBody
(st : TickBoxState)
: tbInput → (list tbOutput) × TickBoxState
:= fun i ⇒
let (a, st') := tickBoxLoopPreBody' st i in
((inl (tbDebugStateTransition st st' i))::a, st').
Definition tickBoxLoopBody {T}
(tickBoxLoop : TickBoxState → T)
(st : TickBoxState)
: tbInput → action world × T
:= fun i ⇒ let outputs := fst (tickBoxLoopPreBody st i) in
let st' := snd (tickBoxLoopPreBody st i) in
(fold_left compose (map handle outputs) id,
tickBoxLoop st').
CoFixpoint tickBoxLoop (st : TickBoxState) :=
Step (tickBoxLoopBody tickBoxLoop st).
Definition tickBox : process _ _ := tickBoxLoop initState.
End trustedTickBox.
negb (publishesSinceLastChange <=? st.(publishDuration)).
Definition ticksMod (ticks : N) (st : TickBoxState)
: N
:= N.modulo ticks st.(publishInterval).
Definition tickBoxLoopPreBody'
(st : TickBoxState)
: tbInput → (list tbOutput) × TickBoxState
:= fun i ⇒
match i, st.(curData) with
| inl (tbSetPublishInterval val), _
⇒ (nil, set_publishInterval st val)
| inl (tbSetWaitBeforeUpdateInterval val), _
⇒ if invalidWaitBeforeUpdateInterval val st
then ((inl (tbWarnInvalidWaitBeforeUpdateInterval val))::nil, st)
else (nil, set_waitBeforeUpdateInterval st val)
| inl (tbSetPublishDuration val), _
⇒ (nil, set_publishDuration st val)
| inl (tbSetPublishPrecision val), _
⇒ (nil, set_publishPrecision st val)
| inr tbNotifyChange, NoData ticks
⇒ ((inr tbRequestDataUpdate)::nil, set_curData st (InitiallyWaitingOnData ticks))
| inr tbNotifyChange, HaveData ticks data publishesSinceLastChange
⇒ (nil, set_curData st (HaveData ticks data None))
| inr tbNotifyChange, WaitingOnData ticks publishesSinceLastChange
⇒ (nil, set_curData st (WaitingOnData ticks None))
| inr tbNotifyChange, WaitingOnTicks ticks publishesSinceLastChange
⇒ (nil, set_curData st (WaitingOnTicks ticks None))
| inr tbNotifyChange, InitiallyWaitingOnData ticks
⇒ (nil, st)
| inr (tbValueReady data), InitiallyWaitingOnData ticks
⇒ ((inr (tbSleepFor (ticksToTransmit ticks st)))::nil, set_curData st (HaveData ticks data (Some 0)))
| inr (tbValueReady data), WaitingOnData ticks publishesSinceLastChange
⇒ ((inr (tbSleepFor (ticksToTransmit ticks st)))::nil, set_curData st (HaveData ticks data publishesSinceLastChange))
| inr (tbValueReady data), HaveData _ _ _
⇒ ((inl (tbWarnInvalidEvent st.(curData) (tbValueReady data)))::nil, st)
| inr (tbValueReady data), NoData ticks
⇒ ((inl (tbWarnInvalidEvent st.(curData) (tbValueReady data)))::nil, st)
| inr (tbValueReady data), WaitingOnTicks _ _
⇒ ((inl (tbWarnInvalidEvent st.(curData) (tbValueReady data)))::nil, st)
| inr (tbTick ticksSinceLastTbTick), NoData ticks
⇒ (nil, set_curData st (NoData (ticksMod (ticks + ticksSinceLastTbTick) st)))
| inr (tbTick ticksSinceLastTbTick), InitiallyWaitingOnData ticks
⇒ (nil, set_curData st (InitiallyWaitingOnData (ticksMod (ticks + ticksSinceLastTbTick) st)))
| inr (tbTick ticksSinceLastTbTick), HaveData ticks data publishesSinceLastChange
⇒ let ticks' := ticksSinceLastTbTick + ticks in
let st' := set_curData st (HaveData ticks' data publishesSinceLastChange) in
let cur_ticksToTransmit := ticksToTransmit ticks' st' in
if cur_ticksToTransmit >? 0
then ((inr (tbSleepFor cur_ticksToTransmit))::nil, st')
else let publishesSinceLastChange' := match publishesSinceLastChange with
| None ⇒ 0
| Some n ⇒ n + 1
end in
let actions := (inr (tbPublishUpdate data))::nil in
let actions := (if ticksTooCoarse ticks' st'
then (inl (tbWarnTicksTooInfrequent (ticks' - st'.(publishInterval))))::actions
else actions) in
let ticks'' := ticksMod ticks' st' in
if enoughTransmissions publishesSinceLastChange' st'
then (actions, set_curData st' (NoData ticks''))
else ((inr (tbSleepFor (ticksToGetUpdate ticks'' st')))::actions,
set_curData st' (WaitingOnTicks ticks'' (Some publishesSinceLastChange')))
| inr (tbTick ticksSinceLastTick), WaitingOnTicks ticks publishesSinceLastChange
⇒ let ticks' := ticksSinceLastTick + ticks in
let st_request := set_curData st (WaitingOnData ticks' publishesSinceLastChange) in
let st_waiting := set_curData st (WaitingOnTicks ticks' publishesSinceLastChange) in
let actions := (if ticksTooCoarseWaitingOnTicks ticks' st
then (inl (tbWarnTicksTooInfrequent (ticks' - st.(waitBeforeUpdateInterval))))::nil
else nil) in
let cur_ticksToGetUpdate := ticksToGetUpdate ticks' st in
if cur_ticksToGetUpdate >? 0
then ((inr (tbSleepFor cur_ticksToGetUpdate))::actions, st_waiting)
else ((inr tbRequestDataUpdate)::actions, st_request)
| inr (tbTick ticksSinceLastTick), WaitingOnData ticks publishesSinceLastChange
⇒ let ticks' := ticks + ticksSinceLastTick in
((if ticksToTransmit ticks' st =? 0
then (inl tbWarnNoDataReady)::nil
else nil),
set_curData st (WaitingOnData ticks' publishesSinceLastChange))
end.
Definition tickBoxLoopPreBody
(st : TickBoxState)
: tbInput → (list tbOutput) × TickBoxState
:= fun i ⇒
let (a, st') := tickBoxLoopPreBody' st i in
((inl (tbDebugStateTransition st st' i))::a, st').
Definition tickBoxLoopBody {T}
(tickBoxLoop : TickBoxState → T)
(st : TickBoxState)
: tbInput → action world × T
:= fun i ⇒ let outputs := fst (tickBoxLoopPreBody st i) in
let st' := snd (tickBoxLoopPreBody st i) in
(fold_left compose (map handle outputs) id,
tickBoxLoop st').
CoFixpoint tickBoxLoop (st : TickBoxState) :=
Step (tickBoxLoopBody tickBoxLoop st).
Definition tickBox : process _ _ := tickBoxLoop initState.
End trustedTickBox.