=================== hsmCommunicator.fsm =================== :download:`Download <../Source/hsmCommunicator.fsm>` .. code-block:: fsmlang native { #ifndef DBG_PRINTF #include #define DBG_PRINTF(A, ...) printf((A), __VA_ARGS__); printf("\n"); #endif extern unsigned queue_count; } /** This machine manages communications using a "stop and wait" protocol. Only one message is allowed to be outstanding. 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. 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. While the top level machine is in the ESTABLISHING_SESSION state, the establishSession machine does the establishment work. 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. */ machine hsmCommunicator native impl { /* The barest skeleton of a queue has only a count. We aren't really sending messages, after all. */ unsigned queue_count = 0; } { /** This event comes from our client code, asking us to send a message. */ event SEND_MESSAGE; /** This event comes from our establishSession submachine, indicating that it has successfully completed its work. We then forward it to our sendMessage submachine to indicate that it may now begin to send messages. */ event SESSION_ESTABLISHED; /** 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 establishSession submachine; while in the IN_SESSION state, we forward it to the sendMessage submachine. */ event MESSAGE_RECEIVED; /** The wakeup 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 establishSession submachine. While in this state, the MESSAGE_RECEIVED 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 MESSAGE_RECEIVED event is forwarded to the sendMessage submachine. */ state IN_SESSION; /** Establish a connection with the peer. 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. 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. */ machine establishSession { event ESTABLISH_SESSION_REQUEST, STEP0_RESPONSE, STEP1_RESPONSE; event parent::MESSAGE_RECEIVED; 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]; /** Notify parent that session is established. */ action notifyParent[STEP1_RESPONSE, AWAITING_RESPONSE] transition IDLE; /** Parse the incoming message */ action parseMessage[MESSAGE_RECEIVED, AWAITING_RESPONSE]; /* these lines are informational; they affect the html output, but do not affect any C code generated. */ sendStep0Message returns noEvent; sendStep1Message returns noEvent; notifyParent returns parent::SESSION_ESTABLISHED; parseMessage returns STEP0_RESPONSE, STEP1_RESPONSE, noEvent; } /** Send a message to the peer. 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. 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. */ machine sendMessage { event parent::SEND_MESSAGE , parent::MESSAGE_RECEIVED , ACK; state IDLE, AWAITING_ACK; /** 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 */ /** Start the session establishment process by activating the establishSession machine. */ action startSessionEstablishment[SEND_MESSAGE, IDLE] transition ESTABLISHING_SESSION; /** Start the session timer and notify the sendMessage machine that the session is established. */ action completeSessionStart[SESSION_ESTABLISHED, ESTABLISHING_SESSION] transition IN_SESSION; /** Pass the MESSAGE_RECEIVED event along. */ action passMessageReceived[MESSAGE_RECEIVED, (ESTABLISHING_SESSION, IN_SESSION)]; /** Extend the session timer and queue the message */ action queueMessage[SEND_MESSAGE, ESTABLISHING_SESSION]; /** Extend the session timer and pass the message to be sent to the sendMessage machine. */ action requestMessageTransmission[SEND_MESSAGE, IN_SESSION]; transition [SESSION_TIMEOUT, IN_SESSION] IDLE; /* these lines are informational; they affect the html output, but do not affect any C code generated. */ startSessionEstablishment returns establishSession::ESTABLISH_SESSION_REQUEST; completeSessionStart returns noEvent; requestMessageTransmission returns noEvent; queueMessage returns noEvent; }