hsmCommunicator.fsmΒΆ

Download

native
{
#ifndef DBG_PRINTF
#include <stdio.h>
#define DBG_PRINTF(...) printf(__VA_ARGS__); printf("\n");
#endif

typedef enum
{
        msg_none
        , msg_step0_response
        , msg_step1_response
        , msg_session_rejected
        , msg_ack
} msg_e_t;

typedef struct _queue_str_
{
        unsigned queue_count;
} queue_str_t;

}

/**
<p>This machine manages communications using a "stop and wait" protocol. Only one message is allowed to be outstanding.</p>
<p>Before any message can be exchanged, however, a session must be established with the peer. Establishing a connection
requires several exchanges to authenticate. The session will remain active as long as messages continue to be
exchanged with a minimum frequency.</p>
<p>The user of this machine calls run_hsmCommunicator, passing the SEND_MESSAGE event.  For the first message,
the machine will be IDLE, and thus needs to queue the message, start the establishSession machine, and transition
to the ESTABLISHING_SESSION state.  Requests to send messages received in this state will simply be queued. </p>
<p>While the top level machine is in the ESTABLISHING_SESSION state, the establishSession machine does the establishment work.</p>
<p>When the establishSession machine receives the STEP1_RESPONSE event, it reports to the top level machine that
the session is established by returning the parent's SESSION_ESTABLISHED event.  This will move the top level
machine to its IN_SESSION state and cause it to send the message(s) which are enqueued.</p>
*/
machine hsmCommunicator
on transition track_transitions;
native impl
{
    #define INIT_FSM_DATA {msg_none, { 0 }}
}
{
    data
    {
        msg_e_t current_msg;
        queue_str_t queue;
    }

    /** System initialization */
    event INIT;

    /** This event comes from our client code, asking us to send a message.
    */
    event SEND_MESSAGE;

    /** This event comes from our <i>establishSession</i> submachine, indicating that it has successfully
        completed its work.  We then forward it to our <i>sendMessage</i> submachine to indicate that
        it may now begin to send messages.
    */
    event SESSION_ESTABLISHED;

    /** Our peer has rejected our attempt to establish a session. */
    event SESSION_REJECTED;

    /** This event comes from our external timer, indicating that we've not tickled it in a while, and
        thus should close down our session.
    */
    event SESSION_TIMEOUT;

    /** This event comes from our lower comm layers, indicating that a peer message has arrived.
        While we're in the ESTABLISHING_SESSION state, we forward this event to the <i>establishSession</i>
        submachine; while in the IN_SESSION state, we forward it to the <i>sendMessage</i> submachine.
    */
    event MESSAGE_RECEIVED
          data
          translator store_message
          {
             msg_e_t message;
          }
          ;

    /** The wakeup state. */
    state UNINITIALIZED;

    /** The first initialized state.  Also, this is the state to which the machine
        returns when a session times out.
    */
    state IDLE;

    /** The machine is establishing a session.  The actual work is being done by the <i>establishSession</i>
        submachine.  While in this state, the <i>MESSAGE_RECEIVED</i> event is forwarded to that submachine.
    */
    state ESTABLISHING_SESSION;

    /** A session has been established, and messages are being exchanged with the peer.  While in this
        state, the <i>MESSAGE_RECEIVED</i> event is forwarded to the <i>sendMessage</i> submachine.
    */
    state IN_SESSION
        on entry start_session_timer
        on exit  stop_session_timer
        ;

    /**
    <p>Establish a connection with the peer.
    </p>
    <p>Two messages must be exchanged with the peer to successfully establish the session.  The machine needs
    only two states, IDLE and AWAITING_RESPONSE since the top level machine tracks whether or not it is in a
    session.  The AWAITING_RESPONSE state serves for both required messages, since the receipt of each message produces
    a unique event.
    </p>
    <p>When the STEP1_RESPONSE event is received, the session is considered established.  This machine will then
    return the parent's SESSION_ESTABLISHED message and move to its IDLE state.
    </p>
    */
    machine establishSession
    native impl
    {
        #define INIT_FSM_DATA {msg_none}
    }
    {
    data
    {
        msg_e_t current_msg;
    }

    event ESTABLISH_SESSION_REQUEST, STEP0_RESPONSE;
    event parent::MESSAGE_RECEIVED data translator copy_current_message;

    state IDLE, AWAITING_RESPONSE;

    /** Start the session establishment process. */
    action sendStep0Message[ESTABLISH_SESSION_REQUEST, IDLE]  transition AWAITING_RESPONSE;

    /** Continue session establisment */
    action sendStep1Message[STEP0_RESPONSE, AWAITING_RESPONSE];

    /** Parse the incoming message */
    action parseMessage[MESSAGE_RECEIVED, AWAITING_RESPONSE] transition decide_parse_transition;

    /* these lines are informational; they affect the html output, but do not affect any C code generated. */
    sendStep0Message returns noEvent;

    sendStep1Message returns noEvent;

    parseMessage returns STEP0_RESPONSE, parent::SESSION_ESTABLISHED, parent::SESSION_REJECTED, noEvent;

    decide_parse_transition returns IDLE, noTransition;

    }

    /**
    <p>Send a message to the peer.
    </p>
    <p>Since the protocol allows only one message to be outsanding, the machine dequeues and transmits a message only
    from the IDLE state, transitioning to the AWAITING_ACK state immediately thereafter.
    </p>
    <p>In the AWAITNG_ACK state, incomming messages are parsed and, when an ACK is found, the machine checks the queue
    and transitions to the IDLE state.  Checking the queue can return the SEND_MESSAGE event, which will be handled
    from the IDLE state, thus resulting in a transmission and return to the AWAITING_ACK state.
    </p>
    */
    machine sendMessage
    native impl
    {
        #define INIT_FSM_DATA {msg_none, NULL}
    }
    {
    data
    {
        msg_e_t current_msg;
        queue_str_t *pqueue;
    }

    event parent::INIT data translator init_data;

    event parent::MESSAGE_RECEIVED data translator copy_current_message
          , SEND_MESSAGE
          , ACK;

    state UNINITIALIZED, IDLE, AWAITING_ACK;

    transition [INIT, UNINITIALIZED] IDLE;

    /** Dequeue and transmit message to the peer. */
    action      sendMessage[SEND_MESSAGE,IDLE] transition AWAITING_ACK;

    /** Check queue for messages; if found return SEND_MESSAGE; otherwise, return noEvent. */
    action      checkQueue[ACK,AWAITING_ACK]          transition IDLE;

    action      parseMessage[MESSAGE_RECEIVED, AWAITING_ACK];

    /* these lines are informational; they affect the html output, but do not affect any C code generated. */
    sendMessage  returns noEvent;

    checkQueue   returns SEND_MESSAGE, noEvent;

    parseMessage returns ACK, noEvent;

    }

    /* these are actions of the top level machine */

    /** Initialize the machine */
    action initialize[INIT, UNINITIALIZED] transition IDLE;

    /** Start the session establishment process by activating the <i>establishSession</i> machine. */
    action startSessionEstablishment[SEND_MESSAGE, IDLE] transition  ESTABLISHING_SESSION;

    /** Pass the MESSAGE_RECEIVED event along. */
    action passMessageReceived[MESSAGE_RECEIVED, (ESTABLISHING_SESSION, IN_SESSION)];

    /** Clear the queue and return to the IDLE state */
    action clearQueue[SESSION_REJECTED, ESTABLISHING_SESSION] transition IDLE;

    /** Notify the <i>sendMessage</i> machine that the session is established. */
    action completeSessionStart[SESSION_ESTABLISHED, ESTABLISHING_SESSION] transition IN_SESSION;

    /** Extend the session timer, queue the message, and alert the <i>sendMessage</i> machine. */
    action requestMessageTransmission[SEND_MESSAGE, (ESTABLISHING_SESSION, IN_SESSION)];

    transition [SESSION_TIMEOUT, IN_SESSION] IDLE;

    startSessionEstablishment   returns establishSession::ESTABLISH_SESSION_REQUEST;

    completeSessionStart        returns sendMessage::SEND_MESSAGE;

    requestMessageTransmission  returns sendMessage::SEND_MESSAGE;

}