The model

The model

We followed the same scheme as before for combining the LUSTRE controller.

There is however a difference: as LUSTRE is a much more high-level language than our simple AUTOC language, we modelled much of the physical environement in the LUSTRE part, leaving only differential equations/inclusions in the AUTOC part (as well as the scheduler).

The sources are steamboiler.preamble, steamboiler.lus, and steamboiler.environment.m4.

Our model takes into account all the requirement detailed in [1], with 3 pumps (instead of 4). In particular we model

The resulting model is quite big: 63 Boolean or enumerated state variables, and 7 real variables, to which one should add the variables of the environement and of the scheduler linking them.

We detailed below some part of the specification.

The generic error protocol

The same error protocol is used forall physical devices (pumps, pump controllers, level and steam sensor).



-- **********************************************************************
-- generic 2-states automaton :
-- **********************************************************************
-- switch form false to true if "on" is true
-- switch form true to false if "off" is true
-- remains "init" until the first "on" or the first "off"
node switch(init, on, off : bool) returns (s : bool); 
let 
   s = if(not (init -> pre s)) then (on) else (not off);
tel
-- **********************************************************************
-- 3-state generic automaton 
-- **********************************************************************
node switch_on1off12(init,on,off1,off2: bool) returns (state:bool)
var waiting: bool;
let
        waiting = switch(false,off1,jafter(off2));
        state = switch(init,on, waiting and off2);
tel
-- **********************************************************************
-- Error protocol
-- Special outputs:
-- physical_failure_state: not visible from the controller
-- **********************************************************************
node ErrorProtocol(
        FAILURE_DETECTION       : bool; 
        REPAIR_ACK              : bool;
        iFAILURE                : bool;
)
returns (
        FAILURE_ACK             : bool;
        REPAIRED                : bool;
        physical_failure_state  : bool;
)
let
physical_failure_state = 
        switch_on1off12(false,
                iFAILURE, 
                FAILURE_ACK,iFAILURE);
FAILURE_ACK = 
        switch(false,
                jafter(FAILURE_DETECTION),
                jafter(FAILURE_ACK));
REPAIRED =
        switch(false,
               xedge(not physical_failure_state),
               REPAIR_ACK);
tel

The combined pump/pump model



-- **********************************************************************
-- A pump and its associated controller
-- Special inputs (simulate non-determinism)
-- iPUMP_FAILURE: first occurence: start of a failure,
--                second: end of the failure
-- iPUMP_CONTROL_FAILURE: idem
-- **********************************************************************

node Pump(
        OPEN_PUMP       : bool;
        CLOSE_PUMP      : bool;
        PUMP_FAILURE_DETECTION          : bool;
        PUMP_CONTROL_FAILURE_DETECTION  : bool;
        PUMP_REPAIR_ACK         : bool;
        PUMP_CONTROL_REPAIR_ACK : bool;

        iPUMP_FAILURE           : bool;
        iPUMP_CONTROL_FAILURE   : bool; 
)
returns (
        PUMP_STATE              : bool;
        PUMP_CONTROL_STATE      : bool;
        PUMP_FAILURE_ACK        : bool;
        PUMP_CONTROL_FAILURE_ACK: bool;
        PUMP_REPAIRED           : bool;
        PUMP_CONTROL_REPAIRED   : bool;
        pump_physical_state                     : bool;
        pump_control_physical_state                     : bool;
        pump_physical_failure_state             : bool;
        pump_control_physical_failure_state     : bool
)
let
(PUMP_FAILURE_ACK,PUMP_REPAIRED,pump_physical_failure_state) =
        ErrorProtocol(  PUMP_FAILURE_DETECTION,
                        PUMP_REPAIR_ACK,
                        iPUMP_FAILURE);
(PUMP_CONTROL_FAILURE_ACK,PUMP_CONTROL_REPAIRED,pump_control_physical_failure_state) =
        ErrorProtocol(  PUMP_CONTROL_FAILURE_DETECTION,
                        PUMP_CONTROL_REPAIR_ACK,
                        iPUMP_CONTROL_FAILURE);

pump_physical_state = (false ->
                if pump_physical_failure_state then false -- models the effect of the failure: pump_state becomes wrong
                else if CLOSE_PUMP then false
                else if OPEN_PUMP then true
                else pre pump_physical_state);
PUMP_STATE = jafter(pump_physical_state); -- the controller has the knowledge at the next cycle

pump_control_physical_state = (false ->
                if  pump_physical_failure_state or pump_control_physical_failure_state then false
                else if CLOSE_PUMP then false
                else if jafter(OPEN_PUMP) then true
                else pre pump_control_physical_state);
PUMP_CONTROL_STATE = jafter(pump_control_physical_state);
tel

The command of the pump

We display some parts of the node Controller.



-- computed quantities
clevel1 = LEVEL -> pre (alevel1) + apump1*DELTA - 125.0;
clevel2 = LEVEL -> pre (alevel2) + apump2*DELTA;
csteam1 = 0.0;
csteam2 = W;
cpump1 = PumpFlow1(N,PUMP_ERROR,PUMP_CONTROL_STATE);
cpump2 = PumpFlow2(N,PUMP_ERROR,PUMP_CONTROL_STATE);

-- estimated quantities
(alevel1,alevel2) = 
if LEVEL<0.0 then (clevel1,clevel2) 
else (LEVEL,LEVEL);
(asteam1,asteam2) = if STEAM_ERROR then (csteam1,csteam2) else (STEAM,STEAM);
(apump1,apump2) = (cpump1,cpump2);

-- number of pumps that should be active
shouldbeactive = 
        if LEVEL<0.0 then -- level failure
                if alevel2 < 500.0 then TROIS
                else if alevel1 < N1 then DEUX
                else ZERO
        else
                if LEVEL < 500.0 then TROIS
                else if LEVEL < N2 then DEUX
                else ZERO
;
pfunc = PUMP_CONTROL_STATE and not PUMP_ERROR;
active = if Tall(N, pfunc) then TROIS
          else if Tnone(N,pfunc) then ZERO
          else if Tone(N,pfunc) then UN
          else DEUX
;
open = Inf(active,shouldbeactive); -- should we open a pump ?
close = Sup(active,shouldbeactive); -- should we close a pump ?

OPEN_PUMP = if open then SearchPumpToOpen(Sub(shouldbeactive,active),PUMP_CONTROL_STATE,PUMP_ERROR) else false^N;
CLOSE_PUMP = if close then SearchPumpToClose(Sub(active,shouldbeactive),PUMP_CONTROL_STATE,PUMP_ERROR) else false^N;  

The following node choose a number of pumps not in failure state to open.



node SearchPumpToOpen(
        nb : Integer;
        PUMP_CONTROL_STATE: bool^N;
        PUMP_ERROR      : bool^N
)
returns(
        open            : bool^N
)
var     nbdone          : Integer^N;
        done            : bool^N;
let
nbdone[0] = if open[0] then Pred(nb) else nb;
nbdone[1..N-1] = if open[1..N-1] then Pred(nbdone[0..N-2]) else nbdone[0..N-2]; 
done = EgalZero(nbdone);
open[0] = false -> (not PUMP_ERROR[0] and not PUMP_CONTROL_STATE[0]);
open[1..N-1] = false^(N-1) -> (not done[0..N-2] and not PUMP_ERROR[1..N-1] and not PUMP_CONTROL_STATE[1..N-1]);
tel


The model