{"id":13812217,"url":"https://github.com/FSMLang/FSMLang","last_synced_at":"2025-05-14T21:32:32.626Z","repository":{"id":163431564,"uuid":"600219322","full_name":"FSMLang/FSMLang","owner":"FSMLang","description":"FSMLang is a finite state machine description language","archived":false,"fork":false,"pushed_at":"2025-04-27T04:17:30.000Z","size":1544,"stargazers_count":9,"open_issues_count":19,"forks_count":2,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-04-27T05:20:38.793Z","etag":null,"topics":["fsm"],"latest_commit_sha":null,"homepage":"https://fsmlang.github.io/","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/FSMLang.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-02-10T21:18:34.000Z","updated_at":"2025-03-19T03:20:15.000Z","dependencies_parsed_at":"2023-10-28T05:21:50.203Z","dependency_job_id":"cb24eecd-8d9d-4627-bc7b-f189ff861580","html_url":"https://github.com/FSMLang/FSMLang","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FSMLang%2FFSMLang","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FSMLang%2FFSMLang/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FSMLang%2FFSMLang/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FSMLang%2FFSMLang/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FSMLang","download_url":"https://codeload.github.com/FSMLang/FSMLang/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254231219,"owners_count":22036322,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fsm"],"created_at":"2024-08-04T04:00:49.355Z","updated_at":"2025-05-14T21:32:27.602Z","avatar_url":"https://github.com/FSMLang.png","language":"C","readme":"# FSMLang\n\n- [FSMLang](#fsmlang)\n- [Philosophy](#philosophy)\n\t- [Example: Simple State Machine](#example-simple-state-machine)\n\t- [Hierarchical](#hierarchical)\n\t- [State Entry and Exit Functions](#state-entry-and-exit-functions)\n \t- [Event Data](#event-data)\n- [Using the State Machine](#using-the-state-machine)\n- [Commenting](#commenting)\n- [Making the FSMLang Executable](#making-the-fsmlang-executable)\n- [Command Syntax](#command-syntax)\n- [Basic Language Syntax](#basic-language-syntax)\n- [More examples](#more-examples)\n\n\n## Philosophy\n\nFSMLang is designed to allow design work in the problem space of finite state machines without the encumbrances of any particular implementation language. Thus, FSMLang is implemented as a \"pre-processor,\" generating code in any desired general programming language to implement the described finite state machine. FSMLang allows effort to be focused on the definition of events, states, and transitions; indeed, though the action to be taken in any particular event/state intersection is declarable (of course), the actual definition of that action is treated as a detail which falls outside the scope of FSMLang. Moreover, the mechanisms for collecting or determining events are also outside the language scope. FSMLang creates an object or objects in a target programming language which, when inserted into the larger program structure will invoke the correct actions and make the correct transitions for the events handed it.\n\n(Though it is said, \"any desired general programming language,\" implementation of FSMLang output in languages other than C is an exercise currently left to the reader.)\n\nThe created state machine contains a single state variable, which should not be manipulated by any user-written function. This variable is maintained on the heap, not on the machine's function call stack. This means that the machine must not be called recursively; neither from within any action function, nor from separate threads of execution. The keyword *reentrant* can be used to designate machines which are called from different execution threads. Macros will be inserted at the beginning and end of the state machine function which must be defined to properly protect the machine from re-entrance.\n\n[Top of page](#fsmlang)\n\n## Example: Simple State Machine\n\nAs an example, consider a simple communications protocol which specifies that an acknowledgement must be received for each message before another may be sent. The sender of messages in such a protocol must have at least two states: In the first state, which will be called IDLE, the sender has sent no message. In the second, which will be called AWAITING_ACK, the sender has sent a message and is awaiting an acknowledgement. The events which this automaton will see are SEND_MESSAGE, the request by a client entity for a message to be sent, and ACK, the receipt of the acknowledgment from the peer automaton.\n\nThe valid actions, then, are to send a message if one is requested, and the automaton is in the IDLE state,and to simply return to the IDLE state if an ACK is received while in the AWAITING_ACK state. Sending a message requires a transition to the AWAITING_ACK state. The receipt of an acknowledgement while in the IDLE state represents a protocol error which may be safely ignored. A request to send a message while in the AWAITING_ ACK state, however, must result in the message being queued for later transmission.\n\nUsing FSMLang, this machine can be described this way:\n\n```\n/**\n   This machine manages communications using a \"stop and wait\" protocol.\n      Only one message is allowed to be outstanding.\n*/\nmachine simpleCommunicator\n{\n\n  state IDLE,\n        AWAITING_ACK;\n\n  event SEND_MESSAGE,\n        ACK;\n\n  /**\n   Transmit message to the peer.\n  */\n  action sendMessage[SEND_MESSAGE,IDLE] transition AWAITING_ACK;\n\n  /**\n   Place message into queue.\n  */\n  action queueMessage[SEND_MESSAGE,AWAITING_ACK];\n\n  /**\n   Check queue for messages; if found pop from queue and return SEND_MESSAGE.\n   If no message is found in the queue return noEvent.\n  */\n  action checkQueue[ACK,AWAITING_ACK] transition IDLE;\n\n  /* these lines are informational; they affect the html output,\n      but do not affect any code generated.\n  */\n  queueMessage returns noEvent;\n  sendMessage  returns noEvent;\n  checkQueue   returns SEND_MESSAGE, noEvent;\n\n}\n```\n\n(When no transition is specified, the machine remains in the state it was in when the event occurred. And, a comma separated list of events or states enclosed within parentheses may be used in place of any single event or state designation; in which case, the action specified will be taken in the \"cross product\" of the two (event and/or state) vectors so described.)\n\nThis is the header file created\n\n```c\n/**\n\tsimpleCommunicator.h\n\n\tThis file automatically generated by FSMLang\n*/\n\n#ifndef _SIMPLECOMMUNICATOR_H_\n#define _SIMPLECOMMUNICATOR_H_\n\n#ifdef SIMPLE_COMMUNICATOR_DEBUG\n#include \u003cstdio.h\u003e\n#include \u003cstdlib.h\u003e\n#endif\n\n#define FSM_VERSION \"1.26\"\n\n#define RUN_STATE_MACHINE(A,B) \\\n\t((*(A)-\u003efsm)((A),(B)))\n\n#ifndef INIT_FSM_DATA\n#define INIT_FSM_DATA {0}\n#endif\n#define DECLARE_SIMPLE_COMMUNICATOR_MACHINE(A) \\\nSIMPLE_COMMUNICATOR (A) =\\\n{\\\n\tsimpleCommunicator_IDLE,\\\n\t\u0026simpleCommunicator_action_array,\\\n\tsimpleCommunicatorFSM\\\n};\\\nSIMPLE_COMMUNICATOR *p##A = \u0026A;\n\n/* Event naming convenience macros. */\n#undef THIS\n#define THIS(A) simpleCommunicator_##A\ntypedef enum {\n\t simpleCommunicator_SEND_MESSAGE\n\t, simpleCommunicator_ACK\n\t, simpleCommunicator_noEvent\n\t, simpleCommunicator_numEvents\n} SIMPLE_COMMUNICATOR_EVENT;\n\n#ifdef SIMPLE_COMMUNICATOR_DEBUG\nextern char *SIMPLE_COMMUNICATOR_EVENT_NAMES[];\n#endif\n\ntypedef enum {\n\t simpleCommunicator_IDLE\n\t, simpleCommunicator_AWAITING_ACK\n\t, simpleCommunicator_numStates\n} SIMPLE_COMMUNICATOR_STATE;\n\n#ifdef SIMPLE_COMMUNICATOR_DEBUG\nextern char *SIMPLE_COMMUNICATOR_STATE_NAMES[];\n#endif\n\ntypedef struct _simpleCommunicator_struct_ SIMPLE_COMMUNICATOR, *pSIMPLE_COMMUNICATOR;\nextern SIMPLE_COMMUNICATOR simpleCommunicator;\n\nextern pSIMPLE_COMMUNICATOR psimpleCommunicator;\n\ntypedef SIMPLE_COMMUNICATOR_EVENT (*SIMPLE_COMMUNICATOR_ACTION_FN)(pSIMPLE_COMMUNICATOR);\n\ntypedef void (*SIMPLE_COMMUNICATOR_FSM)(pSIMPLE_COMMUNICATOR,SIMPLE_COMMUNICATOR_EVENT);\n\nvoid simpleCommunicatorFSM(pSIMPLE_COMMUNICATOR,SIMPLE_COMMUNICATOR_EVENT);\n\ntypedef struct _simpleCommunicator_action_trans_struct_ {\n\tSIMPLE_COMMUNICATOR_ACTION_FN\taction;\n\tSIMPLE_COMMUNICATOR_STATE\ttransition;\n} SIMPLE_COMMUNICATOR_ACTION_TRANS, *pSIMPLE_COMMUNICATOR_ACTION_TRANS;\n\nextern const SIMPLE_COMMUNICATOR_ACTION_TRANS simpleCommunicator_action_array[simpleCommunicator_numEvents][simpleCommunicator_numStates];\n\nstruct _simpleCommunicator_struct_ {\n\tSIMPLE_COMMUNICATOR_STATE\t\t\t\t\tstate;\n\tSIMPLE_COMMUNICATOR_ACTION_TRANS const\t(*actionArray)[simpleCommunicator_numEvents][simpleCommunicator_numStates];\n\tSIMPLE_COMMUNICATOR_FSM\t\t\t\t\t\tfsm;\n};\n\nSIMPLE_COMMUNICATOR_EVENT simpleCommunicator_sendMessage(pSIMPLE_COMMUNICATOR);\nSIMPLE_COMMUNICATOR_EVENT simpleCommunicator_queueMessage(pSIMPLE_COMMUNICATOR);\nSIMPLE_COMMUNICATOR_EVENT simpleCommunicator_checkQueue(pSIMPLE_COMMUNICATOR);\nSIMPLE_COMMUNICATOR_EVENT simpleCommunicator_noAction(pSIMPLE_COMMUNICATOR);\n\n\n\n#endif\n\n```\n\nThis is the source file created\n\n```c\n/**\n\tsimpleCommunicator.c\n\n\tThis file automatically generated by FSMLang\n*/\n\n#include \"simpleCommunicator.h\"\n\nconst SIMPLE_COMMUNICATOR_ACTION_TRANS simpleCommunicator_action_array[simpleCommunicator_numEvents][simpleCommunicator_numStates] =\n{\n\t{\n\t\t/* -- SEND_MESSAGE -- */\n\n\t\t/* -- IDLE -- */\t{simpleCommunicator_sendMessage,simpleCommunicator_AWAITING_ACK}\n\t\t/* -- AWAITING_ACK -- */\t, {simpleCommunicator_queueMessage,simpleCommunicator_AWAITING_ACK}\n\t},\n\t{\n\t\t/* -- ACK -- */\n\n\t\t/* -- IDLE -- */\t{simpleCommunicator_noAction, simpleCommunicator_IDLE}\n\t\t/* -- AWAITING_ACK -- */\t, {simpleCommunicator_checkQueue,simpleCommunicator_IDLE}\n\t},\n};\nSIMPLE_COMMUNICATOR simpleCommunicator = {\n\tsimpleCommunicator_IDLE,\n\t\u0026simpleCommunicator_action_array,\n\tsimpleCommunicatorFSM\n};\n\npSIMPLE_COMMUNICATOR psimpleCommunicator = \u0026simpleCommunicator;\n\nvoid simpleCommunicatorFSM(pSIMPLE_COMMUNICATOR pfsm, SIMPLE_COMMUNICATOR_EVENT event)\n{\n/* writeOriginalFSM */\n\tSIMPLE_COMMUNICATOR_EVENT new_e;\n\n\tSIMPLE_COMMUNICATOR_EVENT e = event;\n\n\twhile (e != simpleCommunicator_noEvent) {\n\n#ifdef SIMPLE_COMMUNICATOR_DEBUG\nDBG_PRINTF(\"event: %s; state: %s\"\n,SIMPLE_COMMUNICATOR_EVENT_NAMES[e]\n,SIMPLE_COMMUNICATOR_STATE_NAMES[pfsm-\u003estate]\n);\n#endif\n\n\t\tnew_e = ((* (*pfsm-\u003eactionArray)[e][pfsm-\u003estate].action)(pfsm));\n\n\t\tpfsm-\u003estate = (*pfsm-\u003eactionArray)[e][pfsm-\u003estate].transition;\n\n\t\te = new_e;\n\n\t} \n\n}\n\nSIMPLE_COMMUNICATOR_EVENT __attribute__((weak)) simpleCommunicator_sendMessage(pSIMPLE_COMMUNICATOR pfsm)\n{\n\tDBG_PRINTF(\"weak: simpleCommunicator_sendMessage\");\n\treturn THIS(noEvent);\n}\n\nSIMPLE_COMMUNICATOR_EVENT __attribute__((weak)) simpleCommunicator_queueMessage(pSIMPLE_COMMUNICATOR pfsm)\n{\n\tDBG_PRINTF(\"weak: simpleCommunicator_queueMessage\");\n\treturn THIS(noEvent);\n}\n\nSIMPLE_COMMUNICATOR_EVENT __attribute__((weak)) simpleCommunicator_checkQueue(pSIMPLE_COMMUNICATOR pfsm)\n{\n\tDBG_PRINTF(\"weak: simpleCommunicator_checkQueue\");\n\treturn THIS(noEvent);\n}\n\nSIMPLE_COMMUNICATOR_EVENT __attribute__((weak)) simpleCommunicator_noAction(pSIMPLE_COMMUNICATOR pfsm)\n{\n\tDBG_PRINTF(\"weak: simpleCommunicator_noAction\");\n\treturn simpleCommunicator_noEvent;\n}\n\n\n#ifdef SIMPLE_COMMUNICATOR_DEBUG\nchar *SIMPLE_COMMUNICATOR_EVENT_NAMES[] = {\n\t \"simpleCommunicator_SEND_MESSAGE\"\n\t, \"simpleCommunicator_ACK\"\n\t, \"simpleCommunicator_noEvent\"\n\t, \"simpleCommunicator_numEvents\"\n};\n\nchar *SIMPLE_COMMUNICATOR_STATE_NAMES[] = {\n\t \"simpleCommunicator_IDLE\"\n\t,\"simpleCommunicator_AWAITING_ACK\"\n};\n\n#endif\n\n```\n\nBy default, action functions return events, and the main state machine function loops until an action indicates that there are no further events.\n\nThe action functions themselves are not generated; instead, they are written by the machine implementor. This allows FSMLang to remain focused on the state machine, rather than the details of the implementation of its purpose for existing.\n\n[Top of page](#fsmlang)\n\n## Hierarchical\n\nA more complex example shows how FSMLang treats hierarchical state machines. We reuse the Simple Communicator from the first example, but add the requirement to establish a session with the peer before any message is sent. One way to do this is to have a top-level state machine with two sub state machines: One machine to establish the session; the other to send the messages, again with the requirement that only one message can be in transit at any time.\n\nThe fsm code describes the machines in this way. Note the similarity of the sendMessage sub machine to the simpleCommunicator, above.\n\n```\n/**\n   \nThis machine manages communications using a \"stop and wait\" protocol.\n      Only one message is allowed to be outstanding.\n\t \n\n\n\n\t \nBefore any message can be exchanged, however, a session must be established\n      with the peer.  Establishing a connection requires several exchanges to\n      authenticate.  The connection will remain active as long as messages\n      continue to be exchanged with a minimum frequency.\n   \n\n\n*/\nmachine hsmCommunicator {\n\n\tstate IDLE,\n        ESTABLISHING_SESSION,\n        IN_SESSION;\n\n\tevent SEND_MESSAGE,\n        SESSION_ESTABLISHED,\n        SESSION_TIMEOUT;\n\n\t/**\n\t\tEstablish a connection with the peer.\n  */\n\tmachine establishSession {\n\n\t\tstate IDLE,\n\t\t\t\t\tAWAITING_RESPONSE;\n\n\t\tevent ESTABLISH_SESSION_REQUEST,\n \t\t\t\t\tSTEP0_RESPONSE,\n\t\t\t\t\tSTEP1_RESPONSE;\n\n\t\t/**\n\t\t\tStart the session establishment process.\n\t\t*/\n\t\taction sendStep0Message[ESTABLISH_SESSION_REQUEST, IDLE] transition AWAITING_RESPONSE;\n\n\t\t/**\n\t\t\tContinue session establishment\n\t\t*/\n\t\taction sendStep1Message[STEP0_RESPONSE, AWAITING_RESPONSE];\n\n\t\t/**\n\t\t\tNotify parent that session is established.\n\t\t*/\n\t\taction notifyParent[STEP1_RESPONSE, AWAITING_RESPONSE] transition IDLE;\n\n\t  /* these lines are informational; they affect the html output,\n\t      but do not affect any C code generated.\n\t\t*/\n\t\tsendStep0Message returns noEvent;\n\t\tsendStep1Message returns noEvent;\n\t\tnotifyParent     returns parent::SESSION_ESTABLISHED;\n\n\t}\n\n\tmachine sendMessage {\n\n\t\tstate\tIDLE,\n \t\t\t\tIN_SESSION,\n\t\t\t\tAWAITING_ACK;\n\n\t\tevent\tSEND_MESSAGE,\n\t\t\t\tACK,\n\t\t\t\tSESSION_ESTABLISHED,\n\t\t\t\tSESSION_TIMEOUT;\n\n\t\t/**\n\t\t\tTransmit message to the peer.\n\t\t*/\n\t\taction\tsendMessage[SEND_MESSAGE,IN_SESSION] transition AWAITING_ACK;\n\n\t\t/**\n\t\t\tPlace message into queue.\n\t\t*/\n\t\taction\tqueueMessage[SEND_MESSAGE,(IDLE, AWAITING_ACK)];\n\n\t\t/**\n\t\t\tCheck queue for messages; if found pop from queue and return SEND_MESSAGE.\n\t\t\tIf no message is found in the queue return noEvent.\n\t\t*/\n\t\taction\tcheckQueue[ACK,AWAITING_ACK]          transition IN_SESSION,\n\t\t         checkQueue[SESSION_ESTABLISHED, IDLE] transition IN_SESSION;\n\n\n\t\ttransition [SESSION_TIMEOUT, (IN_SESSION, AWAITING_ACK)] IDLE;\n\n\t\t/* these lines are informational; they affect the html output,\n\t\t\tbut do not affect any C code generated.\n\t\t*/\n\t\tqueueMessage returns noEvent;\n\t\tsendMessage  returns noEvent;\n\t\tcheckQueue   returns SEND_MESSAGE, noEvent;\n\n\t}\n\n\t/* these are actions of the top level machine */\n\n\t/**\n\t\tStart the session establishment process by activating the establishSession machine.\n\t*/\n\taction startSessionEstablishment[SEND_MESSAGE, IDLE] transition ESTABLISHING_SESSION;\n\n\t/**\n\t\tStart the session timer and notify the sendMessage machine that the session is\n\t\t established.\n\t*/\n\taction completeSessionStart[SESSION_ESTABLISHED, ESTABLISHING_SESSION] transition IN_SESSION;\n\n\t/**\n\t\tNotify the sendMessage machine that the session has timed-out.\n\t*/\n\taction notifySessionTimeout[SESSION_TIMEOUT, (ESTABLISHING_SESSION, IN_SESSION)] transition IDLE;\n\n\t/**\n\t\tExtend the session timer and pass the message to be sent to the sendMessage machine.\n\t*/\n\taction requestMessageTransmission[SEND_MESSAGE, (ESTABLISHING_SESSION, IN_SESSION)];\n\n\n\t/* these lines are informational; they affect the html output,\n\t    but do not affect any C code generated.\n\t*/\n\tstartSessionEstablishment   returns SEND_MESSAGE;\n\tcompleteSessionStart        returns sendMessage::SESSION_ESTABLISHED;\n\tnotifySessionTimeout        returns sendMessage::SESSION_TIMEOUT;\n\trequestMessageTransmission  returns sendMessage::SEND_MESSAGE;\n\n}\n```\n\nFSMLang produces one header/source file pair, or one html file for each machine it encounters. The top-level machine's event enumeration contains the events of all machines so that any event can be passed to the machine. Each sub-machine has its own enumeration, but these enumerations do not start at 0. Rather, they are arranged so that each noEvent event has the same numeric value as the corresponding event in the top-level enumeration. In this way, sub-machines are easily selected based on their numeric event range.\n\nThe FSM function of a sub-machine returns a top-level machine event, providing the mechanism by which the sub-machines communicate with the top-level machine. The return value of the sub-machine FSM function is the event returned by a sub-machine action function when that event is not a sub-machine event. When the sub-machine action returns the sub-machine's noEvent that event is translated into the top-level machine's noEvent.\n\nFSMLang does not provide for sub-machines to indicate that their action functions return an event from another sub-machine. Rather, sub-machine actions can return events from their own machine, or from their parent. This is by design. The top-level machine should provide actions when necessary to bridge between the activation of different sub-machines.\n\nHowever, it is possible for a sub-machine to designate any event as being \"from the parent\" (event parent::e1, for example). The name, of course, must be that of an event actually declared in the parent. Moreover, by also giving the name of a data translation function (e.g. event parent::e1 data translator dt1) When this is done, FSMLang generates code for the parent to call the submachine when that event occurs. If a data translator is given, it will be called before the sub-machine is invoked. More than one sub-machine can share a parent event; the machines will be called in order. The loop will be exited when a machine returns anything other than noEvent, so that the parent can then handle that event.\n\nIt is possible, that the parent would want to inhibit the operation of the sub-machines. state s1 inhibits submachines will do just that; no submachines will be run, unless shared events exist and the parent handles those events in that state.\n\nThe html file created for the top-level machine contains a list of sub-machines. The list is hyper-linked to the html file for the respective machine.\n\nAt the moment, only one level of sub-machine is supported. This limitation may be removed in later releases.\n\nThe -ts flag should always be used to generate code due the way the event enumerations are generated for sub-machines. This limitation, too, may be removed in later releases.\n\nThese are the header files created\n\n```c\n/**\n\thsmCommunicator.h\n\n\tThis file automatically generated by FSMLang\n*/\n\n#ifndef _HSMCOMMUNICATOR_H_\n#define _HSMCOMMUNICATOR_H_\n\n#ifdef HSM_COMMUNICATOR_DEBUG\n#include \u003cstdio.h\u003e\n#include \u003cstdlib.h\u003e\n#endif\n\n#define FSM_VERSION \"1.26\"\n\n#define RUN_STATE_MACHINE(A,B) \\\n\t((*(A)-\u003efsm)((A),(B)))\n\n#ifndef INIT_FSM_DATA\n#define INIT_FSM_DATA {0}\n#endif\n#define DECLARE_HSM_COMMUNICATOR_MACHINE(A) \\\nHSM_COMMUNICATOR (A) =\\\n{\\\n\thsmCommunicator_IDLE,\\\n\t\u0026hsmCommunicator_action_array,\\\n\thsmCommunicatorFSM\\\n};\\\nHSM_COMMUNICATOR *p##A = \u0026A;\n\n/* Event naming convenience macros. */\n#undef THIS\n#define THIS(A) hsmCommunicator_##A\n#undef HSM_COMMUNICATOR\n#define HSM_COMMUNICATOR(A) hsmCommunicator_##A\n#undef ESTABLISH_SESSION\n#define ESTABLISH_SESSION(A) hsmCommunicator_establishSession_##A\n#undef SEND_MESSAGE\n#define SEND_MESSAGE(A) hsmCommunicator_sendMessage_##A\n\ntypedef enum {\n\t hsmCommunicator_SEND_MESSAGE\n\t, hsmCommunicator_SESSION_ESTABLISHED\n\t, hsmCommunicator_SESSION_TIMEOUT\n\t, hsmCommunicator_noEvent\n\t, hsmCommunicator_numEvents\n\t, hsmCommunicator_establishSession_ESTABLISH_SESSION_REQUEST\n\t, hsmCommunicator_establishSession_STEP0_RESPONSE\n\t, hsmCommunicator_establishSession_STEP1_RESPONSE\n\t, hsmCommunicator_establishSession_noEvent\n\t, hsmCommunicator_sendMessage_SEND_MESSAGE\n\t, hsmCommunicator_sendMessage_ACK\n\t, hsmCommunicator_sendMessage_SESSION_ESTABLISHED\n\t, hsmCommunicator_sendMessage_SESSION_TIMEOUT\n\t, hsmCommunicator_sendMessage_noEvent\n} HSM_COMMUNICATOR_EVENT;\n\n#ifdef HSM_COMMUNICATOR_DEBUG\nextern char *HSM_COMMUNICATOR_EVENT_NAMES[];\n#endif\n\ntypedef enum {\n\t hsmCommunicator_IDLE\n\t, hsmCommunicator_ESTABLISHING_SESSION\n\t, hsmCommunicator_IN_SESSION\n\t, hsmCommunicator_numStates\n} HSM_COMMUNICATOR_STATE;\n\n#ifdef HSM_COMMUNICATOR_DEBUG\nextern char *HSM_COMMUNICATOR_STATE_NAMES[];\n#endif\n\ntypedef struct _hsmCommunicator_struct_ HSM_COMMUNICATOR, *pHSM_COMMUNICATOR;\nextern HSM_COMMUNICATOR hsmCommunicator;\n\nextern pHSM_COMMUNICATOR phsmCommunicator;\n\ntypedef HSM_COMMUNICATOR_EVENT (*HSM_COMMUNICATOR_ACTION_FN)(pHSM_COMMUNICATOR);\n\ntypedef void (*HSM_COMMUNICATOR_FSM)(pHSM_COMMUNICATOR,HSM_COMMUNICATOR_EVENT);\n\nvoid hsmCommunicatorFSM(pHSM_COMMUNICATOR,HSM_COMMUNICATOR_EVENT);\n\n/* Sub Machine Declarations */\n\n/* enumerate sub-machines */\ntypedef enum {\n\t hsmCommunicator_establishSession\n\t, hsmCommunicator_firstSubMachine = hsmCommunicator_establishSession\n\t,  hsmCommunicator_sendMessage\n\t, hsmCommunicator_numSubMachines\n} HSM_COMMUNICATOR_SUB_MACHINES;\n\ntypedef HSM_COMMUNICATOR_EVENT (*HSM_COMMUNICATOR_SUB_MACHINE_FN)(HSM_COMMUNICATOR_EVENT);\ntypedef struct _hsmCommunicator_sub_fsm_if_ HSM_COMMUNICATOR_SUB_FSM_IF, *pHSM_COMMUNICATOR_SUB_FSM_IF;\nstruct _hsmCommunicator_sub_fsm_if_\n{\n\tHSM_COMMUNICATOR_EVENT                first_event;\n\tHSM_COMMUNICATOR_EVENT                last_event;\n\tHSM_COMMUNICATOR_SUB_MACHINE_FN       subFSM;\n};\n\nextern pHSM_COMMUNICATOR_SUB_FSM_IF hsmCommunicator_sub_fsm_if_array[hsmCommunicator_numSubMachines];\n\nextern HSM_COMMUNICATOR_SUB_FSM_IF establishSession_sub_fsm_if;\nextern HSM_COMMUNICATOR_SUB_FSM_IF sendMessage_sub_fsm_if;\n\ntypedef struct _hsmCommunicator_action_trans_struct_ {\n\tHSM_COMMUNICATOR_ACTION_FN\taction;\n\tHSM_COMMUNICATOR_STATE\ttransition;\n} HSM_COMMUNICATOR_ACTION_TRANS, *pHSM_COMMUNICATOR_ACTION_TRANS;\n\nextern const HSM_COMMUNICATOR_ACTION_TRANS hsmCommunicator_action_array[hsmCommunicator_numEvents][hsmCommunicator_numStates];\n\nstruct _hsmCommunicator_struct_ {\n\tHSM_COMMUNICATOR_STATE\t\t\t\t\tstate;\n\tHSM_COMMUNICATOR_ACTION_TRANS const\t(*actionArray)[hsmCommunicator_numEvents][hsmCommunicator_numStates];\n\tpHSM_COMMUNICATOR_SUB_FSM_IF\t(*subMachineArray)[hsmCommunicator_numSubMachines];\n\tHSM_COMMUNICATOR_FSM\t\t\t\t\t\tfsm;\n};\n\nHSM_COMMUNICATOR_EVENT hsmCommunicator_startSessionEstablishment(pHSM_COMMUNICATOR);\nHSM_COMMUNICATOR_EVENT hsmCommunicator_completeSessionStart(pHSM_COMMUNICATOR);\nHSM_COMMUNICATOR_EVENT hsmCommunicator_notifySessionTimeout(pHSM_COMMUNICATOR);\nHSM_COMMUNICATOR_EVENT hsmCommunicator_requestMessageTransmission(pHSM_COMMUNICATOR);\nHSM_COMMUNICATOR_EVENT hsmCommunicator_noAction(pHSM_COMMUNICATOR);\n\n\n\n#endif\n\n```\n\n```c\n/**\n\testablishSession.h\n\n\tThis file automatically generated by FSMLang\n*/\n\n#ifndef _ESTABLISHSESSION_H_\n#define _ESTABLISHSESSION_H_\n\n#include \"hsmCommunicator.h\"\n\n#ifdef ESTABLISH_SESSION_DEBUG\n#include \u003cstdio.h\u003e\n#include \u003cstdlib.h\u003e\n#endif\n\n#define DECLARE_ESTABLISH_SESSION_MACHINE(A) \\\nESTABLISH_SESSION A =\\\n{\\\n\testablishSession_IDLE,\\\n\t\u0026establishSession_action_array,\\\n\testablishSessionFSM\\\n};\\\nESTABLISH_SESSION *p##A = \u0026(A);\n\n/*\n\tsub-machine events are included in the top-level machine event enumeration.\n\tThese macros set the appropriate names for events from THIS machine\n\tand those from the PARENT machine.\n\n\tThey may be turned off as needed.\n*/\n#ifndef NO_EVENT_CONVENIENCE_MACROS\n#undef THIS\n#define THIS(A) hsmCommunicator_establishSession_##A\n#define PARENT(A) hsmCommunicator_##A\n#endif\n\n#ifdef ESTABLISH_SESSION_DEBUG\nextern char *ESTABLISH_SESSION_EVENT_NAMES[];\n#endif\n\ntypedef enum {\n\t establishSession_IDLE\n\t, establishSession_AWAITING_RESPONSE\n\t, establishSession_numStates\n} ESTABLISH_SESSION_STATE;\n\n#ifdef ESTABLISH_SESSION_DEBUG\nextern char *ESTABLISH_SESSION_STATE_NAMES[];\n#endif\n\ntypedef struct _establishSession_struct_ ESTABLISH_SESSION, *pESTABLISH_SESSION;\nextern ESTABLISH_SESSION establishSession;\n\nextern pESTABLISH_SESSION pestablishSession;\n\ntypedef HSM_COMMUNICATOR_EVENT (*ESTABLISH_SESSION_ACTION_FN)(pESTABLISH_SESSION);\n\ntypedef HSM_COMMUNICATOR_EVENT (*ESTABLISH_SESSION_FSM)(pESTABLISH_SESSION,HSM_COMMUNICATOR_EVENT);\n\nHSM_COMMUNICATOR_EVENT establishSessionFSM(pESTABLISH_SESSION,HSM_COMMUNICATOR_EVENT);\n\ntypedef enum { establishSession_numEvents = 3} ESTABLISH_SESSION_EVENTS;\ntypedef struct _establishSession_action_trans_struct_ {\n\tESTABLISH_SESSION_ACTION_FN\taction;\n\tESTABLISH_SESSION_STATE\ttransition;\n} ESTABLISH_SESSION_ACTION_TRANS, *pESTABLISH_SESSION_ACTION_TRANS;\n\nextern const ESTABLISH_SESSION_ACTION_TRANS establishSession_action_array[establishSession_numEvents][establishSession_numStates];\n\nstruct _establishSession_struct_ {\n\tESTABLISH_SESSION_STATE\t\t\t\t\tstate;\n\tESTABLISH_SESSION_ACTION_TRANS const\t(*actionArray)[establishSession_numEvents][establishSession_numStates];\n\tESTABLISH_SESSION_FSM\t\t\t\t\t\tfsm;\n};\n\nHSM_COMMUNICATOR_EVENT establishSession_sendStep0Message(pESTABLISH_SESSION);\nHSM_COMMUNICATOR_EVENT establishSession_sendStep1Message(pESTABLISH_SESSION);\nHSM_COMMUNICATOR_EVENT establishSession_notifyParent(pESTABLISH_SESSION);\nHSM_COMMUNICATOR_EVENT establishSession_noAction(pESTABLISH_SESSION);\n\n\n#endif\n\n```\n\n```c\n/**\n\tsendMessage.h\n\n\tThis file automatically generated by FSMLang\n*/\n\n#ifndef _SENDMESSAGE_H_\n#define _SENDMESSAGE_H_\n\n#include \"hsmCommunicator.h\"\n\n#ifdef SEND_MESSAGE_DEBUG\n#include \u003cstdio.h\u003e\n#include \u003cstdlib.h\u003e\n#endif\n\n#define DECLARE_SEND_MESSAGE_MACHINE(A) \\\nSEND_MESSAGE A =\\\n{\\\n\tsendMessage_IDLE,\\\n\t\u0026sendMessage_action_array,\\\n\tsendMessageFSM\\\n};\\\nSEND_MESSAGE *p##A = \u0026(A);\n\n/*\n\tsub-machine events are included in the top-level machine event enumeration.\n\tThese macros set the appropriate names for events from THIS machine\n\tand those from the PARENT machine.\n\n\tThey may be turned off as needed.\n*/\n#ifndef NO_EVENT_CONVENIENCE_MACROS\n#undef THIS\n#define THIS(A) hsmCommunicator_sendMessage_##A\n#define PARENT(A) hsmCommunicator_##A\n#endif\n\n#ifdef SEND_MESSAGE_DEBUG\nextern char *SEND_MESSAGE_EVENT_NAMES[];\n#endif\n\ntypedef enum {\n\t sendMessage_IDLE\n\t, sendMessage_IN_SESSION\n\t, sendMessage_AWAITING_ACK\n\t, sendMessage_numStates\n} SEND_MESSAGE_STATE;\n\n#ifdef SEND_MESSAGE_DEBUG\nextern char *SEND_MESSAGE_STATE_NAMES[];\n#endif\n\ntypedef struct _sendMessage_struct_ SEND_MESSAGE, *pSEND_MESSAGE;\nextern SEND_MESSAGE sendMessage;\n\nextern pSEND_MESSAGE psendMessage;\n\ntypedef HSM_COMMUNICATOR_EVENT (*SEND_MESSAGE_ACTION_FN)(pSEND_MESSAGE);\n\ntypedef HSM_COMMUNICATOR_EVENT (*SEND_MESSAGE_FSM)(pSEND_MESSAGE,HSM_COMMUNICATOR_EVENT);\n\nHSM_COMMUNICATOR_EVENT sendMessageFSM(pSEND_MESSAGE,HSM_COMMUNICATOR_EVENT);\n\ntypedef enum { sendMessage_numEvents = 4} SEND_MESSAGE_EVENTS;\ntypedef struct _sendMessage_action_trans_struct_ {\n\tSEND_MESSAGE_ACTION_FN\taction;\n\tSEND_MESSAGE_STATE\ttransition;\n} SEND_MESSAGE_ACTION_TRANS, *pSEND_MESSAGE_ACTION_TRANS;\n\nextern const SEND_MESSAGE_ACTION_TRANS sendMessage_action_array[sendMessage_numEvents][sendMessage_numStates];\n\nstruct _sendMessage_struct_ {\n\tSEND_MESSAGE_STATE\t\t\t\t\tstate;\n\tSEND_MESSAGE_ACTION_TRANS const\t(*actionArray)[sendMessage_numEvents][sendMessage_numStates];\n\tSEND_MESSAGE_FSM\t\t\t\t\t\tfsm;\n};\n\nHSM_COMMUNICATOR_EVENT sendMessage_sendMessage(pSEND_MESSAGE);\nHSM_COMMUNICATOR_EVENT sendMessage_queueMessage(pSEND_MESSAGE);\nHSM_COMMUNICATOR_EVENT sendMessage_checkQueue(pSEND_MESSAGE);\nHSM_COMMUNICATOR_EVENT sendMessage_noAction(pSEND_MESSAGE);\n\n\n#endif\n\n```\n\nThese are the source files created\n\n```c\n/**\n\thsmCommunicator.c\n\n\tThis file automatically generated by FSMLang\n*/\n\n#include \"hsmCommunicator.h\"\n\nconst HSM_COMMUNICATOR_ACTION_TRANS hsmCommunicator_action_array[hsmCommunicator_numEvents][hsmCommunicator_numStates] =\n{\n\t{\n\t\t/* -- SEND_MESSAGE -- */\n\n\t\t/* -- IDLE -- */\t{hsmCommunicator_startSessionEstablishment,hsmCommunicator_ESTABLISHING_SESSION}\n\t\t/* -- ESTABLISHING_SESSION -- */\t, {hsmCommunicator_requestMessageTransmission,hsmCommunicator_ESTABLISHING_SESSION}\n\t\t/* -- IN_SESSION -- */\t, {hsmCommunicator_requestMessageTransmission,hsmCommunicator_IN_SESSION}\n\t},\n\t{\n\t\t/* -- SESSION_ESTABLISHED -- */\n\n\t\t/* -- IDLE -- */\t{hsmCommunicator_noAction, hsmCommunicator_IDLE}\n\t\t/* -- ESTABLISHING_SESSION -- */\t, {hsmCommunicator_completeSessionStart,hsmCommunicator_IN_SESSION}\n\t\t/* -- IN_SESSION -- */\t, {hsmCommunicator_noAction, hsmCommunicator_IN_SESSION}\n\t},\n\t{\n\t\t/* -- SESSION_TIMEOUT -- */\n\n\t\t/* -- IDLE -- */\t{hsmCommunicator_noAction, hsmCommunicator_IDLE}\n\t\t/* -- ESTABLISHING_SESSION -- */\t, {hsmCommunicator_notifySessionTimeout,hsmCommunicator_IDLE}\n\t\t/* -- IN_SESSION -- */\t, {hsmCommunicator_notifySessionTimeout,hsmCommunicator_IDLE}\n\t},\n};\n\npHSM_COMMUNICATOR_SUB_FSM_IF hsmCommunicator_sub_fsm_if_array[hsmCommunicator_numSubMachines] =\n{\n\t\u0026establishSession_sub_fsm_if\n\t, \u0026sendMessage_sub_fsm_if\n};\n\nHSM_COMMUNICATOR hsmCommunicator = {\n\thsmCommunicator_IDLE,\n\t\u0026hsmCommunicator_action_array,\n\t\u0026hsmCommunicator_sub_fsm_if_array,\n\thsmCommunicatorFSM\n};\n\npHSM_COMMUNICATOR phsmCommunicator = \u0026hsmCommunicator;\n\nstatic HSM_COMMUNICATOR_EVENT findAndRunSubMachine(pHSM_COMMUNICATOR, HSM_COMMUNICATOR_EVENT);\n\nvoid hsmCommunicatorFSM(pHSM_COMMUNICATOR pfsm, HSM_COMMUNICATOR_EVENT event)\n{\n/* writeOriginalFSM */\n\tHSM_COMMUNICATOR_EVENT new_e;\n\n\tHSM_COMMUNICATOR_EVENT e = event;\n\n\twhile (e != hsmCommunicator_noEvent) {\n\n#ifdef HSM_COMMUNICATOR_DEBUG\nDBG_PRINTF(\"event: %s; state: %s\"\n,HSM_COMMUNICATOR_EVENT_NAMES[e]\n,HSM_COMMUNICATOR_STATE_NAMES[pfsm-\u003estate]\n);\n#endif\n\n\t\tif (e \u003c hsmCommunicator_noEvent)\n\t\t{\n\n\t\t\tnew_e = ((* (*pfsm-\u003eactionArray)[e][pfsm-\u003estate].action)(pfsm));\n\n\t\t\tpfsm-\u003estate = (*pfsm-\u003eactionArray)[e][pfsm-\u003estate].transition;\n\n\t\t\te = new_e;\n\n\t\t} \n\t\telse\n\t\t{\n\t\t\te = findAndRunSubMachine(pfsm, e);\n\t\t}\n\n\t}\n\n}\n\n\nstatic HSM_COMMUNICATOR_EVENT findAndRunSubMachine(pHSM_COMMUNICATOR pfsm, HSM_COMMUNICATOR_EVENT e)\n{\n\tfor (HSM_COMMUNICATOR_SUB_MACHINES machineIterator = hsmCommunicator_firstSubMachine;\n\t     machineIterator \u003c hsmCommunicator_numSubMachines;\n\t     machineIterator++\n\t    )\n\t{\n\t\t\tif (\n\t\t\t   ((*pfsm-\u003esubMachineArray)[machineIterator]-\u003efirst_event \u003c= e)\n\t\t\t   \u0026\u0026 ((*pfsm-\u003esubMachineArray)[machineIterator]-\u003elast_event \u003e= e)\n\t\t\t    )\n\t\t\t{\n\t\t\t\treturn ((*(*pfsm-\u003esubMachineArray)[machineIterator]-\u003esubFSM)(e));\n\t\t\t}\n\t}\n\n\treturn hsmCommunicator_noEvent;\n\n}\n\nHSM_COMMUNICATOR_EVENT __attribute__((weak)) hsmCommunicator_startSessionEstablishment(pHSM_COMMUNICATOR pfsm)\n{\n\tDBG_PRINTF(\"weak: hsmCommunicator_startSessionEstablishment\");\n\treturn THIS(noEvent);\n}\n\nHSM_COMMUNICATOR_EVENT __attribute__((weak)) hsmCommunicator_completeSessionStart(pHSM_COMMUNICATOR pfsm)\n{\n\tDBG_PRINTF(\"weak: hsmCommunicator_completeSessionStart\");\n\treturn THIS(noEvent);\n}\n\nHSM_COMMUNICATOR_EVENT __attribute__((weak)) hsmCommunicator_notifySessionTimeout(pHSM_COMMUNICATOR pfsm)\n{\n\tDBG_PRINTF(\"weak: hsmCommunicator_notifySessionTimeout\");\n\treturn THIS(noEvent);\n}\n\nHSM_COMMUNICATOR_EVENT __attribute__((weak)) hsmCommunicator_requestMessageTransmission(pHSM_COMMUNICATOR pfsm)\n{\n\tDBG_PRINTF(\"weak: hsmCommunicator_requestMessageTransmission\");\n\treturn THIS(noEvent);\n}\n\nHSM_COMMUNICATOR_EVENT __attribute__((weak)) hsmCommunicator_noAction(pHSM_COMMUNICATOR pfsm)\n{\n\tDBG_PRINTF(\"weak: hsmCommunicator_noAction\");\n\treturn hsmCommunicator_noEvent;\n}\n\n\n#ifdef HSM_COMMUNICATOR_DEBUG\nchar *HSM_COMMUNICATOR_EVENT_NAMES[] = {\n\t \"hsmCommunicator_SEND_MESSAGE\"\n\t, \"hsmCommunicator_SESSION_ESTABLISHED\"\n\t, \"hsmCommunicator_SESSION_TIMEOUT\"\n\t, \"hsmCommunicator_noEvent\"\n\t, \"hsmCommunicator_numEvents\"\n\t, \"hsmCommunicator_establishSession_ESTABLISH_SESSION_REQUEST\"\n\t, \"hsmCommunicator_establishSession_STEP0_RESPONSE\"\n\t, \"hsmCommunicator_establishSession_STEP1_RESPONSE\"\n\t, \"hsmCommunicator_establishSession_noEvent\"\n\t, \"hsmCommunicator_sendMessage_SEND_MESSAGE\"\n\t, \"hsmCommunicator_sendMessage_ACK\"\n\t, \"hsmCommunicator_sendMessage_SESSION_ESTABLISHED\"\n\t, \"hsmCommunicator_sendMessage_SESSION_TIMEOUT\"\n\t, \"hsmCommunicator_sendMessage_noEvent\"\n};\n\nchar *HSM_COMMUNICATOR_STATE_NAMES[] = {\n\t \"hsmCommunicator_IDLE\"\n\t,\"hsmCommunicator_ESTABLISHING_SESSION\"\n\t,\"hsmCommunicator_IN_SESSION\"\n};\n\n#endif\n\n```\n\n```c\n/**\n\testablishSession.c\n\n\tThis file automatically generated by FSMLang\n*/\n\n#include \"establishSession.h\"\n\n\nconst ESTABLISH_SESSION_ACTION_TRANS establishSession_action_array[establishSession_numEvents][establishSession_numStates] =\n{\n\t{\n\t\t/* -- ESTABLISH_SESSION_REQUEST -- */\n\n\t\t/* -- IDLE -- */\t{establishSession_sendStep0Message,establishSession_AWAITING_RESPONSE}\n\t\t/* -- AWAITING_RESPONSE -- */\t, {establishSession_noAction, establishSession_AWAITING_RESPONSE}\n\t},\n\t{\n\t\t/* -- STEP0_RESPONSE -- */\n\n\t\t/* -- IDLE -- */\t{establishSession_noAction, establishSession_IDLE}\n\t\t/* -- AWAITING_RESPONSE -- */\t, {establishSession_sendStep1Message,establishSession_AWAITING_RESPONSE}\n\t},\n\t{\n\t\t/* -- STEP1_RESPONSE -- */\n\n\t\t/* -- IDLE -- */\t{establishSession_noAction, establishSession_IDLE}\n\t\t/* -- AWAITING_RESPONSE -- */\t, {establishSession_notifyParent,establishSession_IDLE}\n\t},\n};\nHSM_COMMUNICATOR_EVENT establishSession_sub_machine_fn(HSM_COMMUNICATOR_EVENT e)\n{\n\treturn establishSessionFSM(pestablishSession, e);\n}\n\n\nHSM_COMMUNICATOR_SUB_FSM_IF establishSession_sub_fsm_if =\n{\n\t\t.subFSM = establishSession_sub_machine_fn\n\t, .first_event = hsmCommunicator_establishSession_ESTABLISH_SESSION_REQUEST\n\t, .last_event = hsmCommunicator_establishSession_STEP1_RESPONSE\n};\n\nESTABLISH_SESSION establishSession = {\n\testablishSession_IDLE,\n\t\u0026establishSession_action_array,\n\testablishSessionFSM\n};\n\npESTABLISH_SESSION pestablishSession = \u0026establishSession;\n\nHSM_COMMUNICATOR_EVENT establishSessionFSM(pESTABLISH_SESSION pfsm, HSM_COMMUNICATOR_EVENT event)\n{\n/* writeOriginalSubFSM */\n\tHSM_COMMUNICATOR_EVENT new_e;\n\n\tHSM_COMMUNICATOR_EVENT e = event;\n\n\twhile (\n\t\t(e != THIS(noEvent))\n\t\t\u0026\u0026 (e \u003e= THIS(ESTABLISH_SESSION_REQUEST))\n\t)\n\t{\n\n#ifdef ESTABLISH_SESSION_DEBUG\nDBG_PRINTF(\"event: %s; state: %s\"\n,ESTABLISH_SESSION_EVENT_NAMES[e - THIS(ESTABLISH_SESSION_REQUEST)]\n,ESTABLISH_SESSION_STATE_NAMES[pfsm-\u003estate]\n);\n#endif\n\n\t\tnew_e = ((* (*pfsm-\u003eactionArray)[e - THIS(ESTABLISH_SESSION_REQUEST)][pfsm-\u003estate].action)(pfsm));\n\n\t\tpfsm-\u003estate = (*pfsm-\u003eactionArray)[e - THIS(ESTABLISH_SESSION_REQUEST)][pfsm-\u003estate].transition;\n\n\t\te = new_e;\n\n\t} \n\treturn e == THIS(noEvent) ? PARENT(noEvent) : e;\n\n}\n\nHSM_COMMUNICATOR_EVENT __attribute__((weak)) establishSession_sendStep0Message(pESTABLISH_SESSION pfsm)\n{\n\tDBG_PRINTF(\"weak: establishSession_sendStep0Message\");\n\treturn THIS(noEvent);\n}\n\nHSM_COMMUNICATOR_EVENT __attribute__((weak)) establishSession_sendStep1Message(pESTABLISH_SESSION pfsm)\n{\n\tDBG_PRINTF(\"weak: establishSession_sendStep1Message\");\n\treturn THIS(noEvent);\n}\n\nHSM_COMMUNICATOR_EVENT __attribute__((weak)) establishSession_notifyParent(pESTABLISH_SESSION pfsm)\n{\n\tDBG_PRINTF(\"weak: establishSession_notifyParent\");\n\treturn THIS(noEvent);\n}\n\n\n#ifdef ESTABLISH_SESSION_DEBUG\nchar *ESTABLISH_SESSION_EVENT_NAMES[] = {\n\t \"establishSession_ESTABLISH_SESSION_REQUEST\"\n\t, \"establishSession_STEP0_RESPONSE\"\n\t, \"establishSession_STEP1_RESPONSE\"\n\t, \"establishSession_noEvent\"\n\t, \"establishSession_numEvents\"\n};\n\nchar *ESTABLISH_SESSION_STATE_NAMES[] = {\n\t \"establishSession_IDLE\"\n\t,\"establishSession_AWAITING_RESPONSE\"\n};\n\n#endif\n\n```\n\n```c\n/**\n\tsendMessage.c\n\n\tThis file automatically generated by FSMLang\n*/\n\n#include \"sendMessage.h\"\n\n\nconst SEND_MESSAGE_ACTION_TRANS sendMessage_action_array[sendMessage_numEvents][sendMessage_numStates] =\n{\n\t{\n\t\t/* -- SEND_MESSAGE -- */\n\n\t\t/* -- IDLE -- */\t{sendMessage_queueMessage,sendMessage_IDLE}\n\t\t/* -- IN_SESSION -- */\t, {sendMessage_sendMessage,sendMessage_AWAITING_ACK}\n\t\t/* -- AWAITING_ACK -- */\t, {sendMessage_queueMessage,sendMessage_AWAITING_ACK}\n\t},\n\t{\n\t\t/* -- ACK -- */\n\n\t\t/* -- IDLE -- */\t{sendMessage_noAction, sendMessage_IDLE}\n\t\t/* -- IN_SESSION -- */\t, {sendMessage_noAction, sendMessage_IN_SESSION}\n\t\t/* -- AWAITING_ACK -- */\t, {sendMessage_checkQueue,sendMessage_IN_SESSION}\n\t},\n\t{\n\t\t/* -- SESSION_ESTABLISHED -- */\n\n\t\t/* -- IDLE -- */\t{sendMessage_checkQueue,sendMessage_IN_SESSION}\n\t\t/* -- IN_SESSION -- */\t, {sendMessage_noAction, sendMessage_IN_SESSION}\n\t\t/* -- AWAITING_ACK -- */\t, {sendMessage_noAction, sendMessage_AWAITING_ACK}\n\t},\n\t{\n\t\t/* -- SESSION_TIMEOUT -- */\n\n\t\t/* -- IDLE -- */\t{sendMessage_noAction, sendMessage_IDLE}\n\t\t/* -- IN_SESSION -- */\t, {sendMessage_noAction,sendMessage_IDLE}\n\t\t/* -- AWAITING_ACK -- */\t, {sendMessage_noAction,sendMessage_IDLE}\n\t},\n};\nHSM_COMMUNICATOR_EVENT sendMessage_sub_machine_fn(HSM_COMMUNICATOR_EVENT e)\n{\n\treturn sendMessageFSM(psendMessage, e);\n}\n\n\nHSM_COMMUNICATOR_SUB_FSM_IF sendMessage_sub_fsm_if =\n{\n\t\t.subFSM = sendMessage_sub_machine_fn\n\t, .first_event = hsmCommunicator_sendMessage_SEND_MESSAGE\n\t, .last_event = hsmCommunicator_sendMessage_SESSION_TIMEOUT\n};\n\nSEND_MESSAGE sendMessage = {\n\tsendMessage_IDLE,\n\t\u0026sendMessage_action_array,\n\tsendMessageFSM\n};\n\npSEND_MESSAGE psendMessage = \u0026sendMessage;\n\nHSM_COMMUNICATOR_EVENT sendMessageFSM(pSEND_MESSAGE pfsm, HSM_COMMUNICATOR_EVENT event)\n{\n/* writeOriginalSubFSM */\n\tHSM_COMMUNICATOR_EVENT new_e;\n\n\tHSM_COMMUNICATOR_EVENT e = event;\n\n\twhile (\n\t\t(e != THIS(noEvent))\n\t\t\u0026\u0026 (e \u003e= THIS(SEND_MESSAGE))\n\t)\n\t{\n\n#ifdef SEND_MESSAGE_DEBUG\nDBG_PRINTF(\"event: %s; state: %s\"\n,SEND_MESSAGE_EVENT_NAMES[e - THIS(SEND_MESSAGE)]\n,SEND_MESSAGE_STATE_NAMES[pfsm-\u003estate]\n);\n#endif\n\n\t\tnew_e = ((* (*pfsm-\u003eactionArray)[e - THIS(SEND_MESSAGE)][pfsm-\u003estate].action)(pfsm));\n\n\t\tpfsm-\u003estate = (*pfsm-\u003eactionArray)[e - THIS(SEND_MESSAGE)][pfsm-\u003estate].transition;\n\n\t\te = new_e;\n\n\t} \n\treturn e == THIS(noEvent) ? PARENT(noEvent) : e;\n\n}\n\nHSM_COMMUNICATOR_EVENT __attribute__((weak)) sendMessage_sendMessage(pSEND_MESSAGE pfsm)\n{\n\tDBG_PRINTF(\"weak: sendMessage_sendMessage\");\n\treturn THIS(noEvent);\n}\n\nHSM_COMMUNICATOR_EVENT __attribute__((weak)) sendMessage_queueMessage(pSEND_MESSAGE pfsm)\n{\n\tDBG_PRINTF(\"weak: sendMessage_queueMessage\");\n\treturn THIS(noEvent);\n}\n\nHSM_COMMUNICATOR_EVENT __attribute__((weak)) sendMessage_checkQueue(pSEND_MESSAGE pfsm)\n{\n\tDBG_PRINTF(\"weak: sendMessage_checkQueue\");\n\treturn THIS(noEvent);\n}\n\n\n#ifdef SEND_MESSAGE_DEBUG\nchar *SEND_MESSAGE_EVENT_NAMES[] = {\n\t \"sendMessage_SEND_MESSAGE\"\n\t, \"sendMessage_ACK\"\n\t, \"sendMessage_SESSION_ESTABLISHED\"\n\t, \"sendMessage_SESSION_TIMEOUT\"\n\t, \"sendMessage_noEvent\"\n\t, \"sendMessage_numEvents\"\n};\n\nchar *SEND_MESSAGE_STATE_NAMES[] = {\n\t \"sendMessage_IDLE\"\n\t,\"sendMessage_IN_SESSION\"\n\t,\"sendMessage_AWAITING_ACK\"\n};\n\n#endif\n\n```\n\nNote that the -tp option was used to create PlantUML output, which was then processed by PlantUML to produce an SVG image. The html was created using the --include-svg-img=true option to include the image in the output.\n\nAn unrealized goal of the FSMLang effort is to optimize the output machine for speed and size based on an analysis of the event-state matrix. There are command-line switches which force the creation of a compact table, or the use of switch statements instead of a table, but these are manual. One should be able to make those decisions based on the density of the event-state matrix. It may also be possible, using matrix transformations to make some part of the matrix very dense, then use a hybrid table/switch approach in the code.\n\n[Top of page](#fsmlang)\n\n## State Entry and Exit Functions\n\nEntry and exit functions may be added to states.  For example,\n\n```\nstate some_state on entry prepare_some_state;\n```\n\nadds a named entry function to *some_state*.\n\nSimilarly,\n\n```\nstate some_state on exit tidy_up_some_state;\n```\nadds a named exit function to *some_state*.\n\nWhen entry or exit functions exist, code will be generated to call them appropriately.  Names given in the .fsm \nfile are prepended with the name of the machine.  \"Anonymous\" entry or exit functions can be declared simply by\nommiting the names.  In this case names will be generated using the pattern \u003cmachine_name\u003e_onEntryTo_\u003cstate_name\u003e\nfor entry functions, and similarly, using *onExitFrom* for exit functions.  When weak function generation is not\ndisabled, weak versions of these anonymous functions are created.\n\nBecause they are powerful, entry and exit functions can be mis-used.  As the names chosen here suggest, they\nshould be used only to prepare a state or to leave it in a tidy condition.  They should not be used as a substitue for\nthe creation of sub-machines, or well-thought out action sequences.\n\n[Top of page](#fsmlang)\n\n## Event Data\n\nEvents may be declared to have associated data.  For example:\n\n```\nevent data_packet_arrived data {\n   unsigned length;\n   uint8_t  *packet;\n   };\n\n```\n\nWhen any event is declared with data, FSMLang shifts the event declaration from a simple enumeration to a structure containing the event enumeration and the union of the event data structures.  This structure becomes the method for passing events in from the outside world into the state machine.\n\nContinuing the example, event data_packet_arrived will cause the declaration of this structure for the event's data:\n\n```c\ntypedef struct _\u003cmachine_name\u003e_data_packet_arrived_ {\n   unsigned length;\n   uint8t_t *packet;\n} \u003cMACHINE_NAME\u003e_DATA_PACKET_ARRIVED_DATA, *p\u003cMACHINE_NAME\u003e_DATA_PACKET_ARRIVED_DATA;\n```\n\nThe event data union is declared:\n\n```c\ntypedef union {\n   \u003cMACHINE_NAME\u003e_DATA_PACKET_ARRIVED_DATA data_packet_arrived_data;\n   .\n   .\n   .\n} \u003cMACHINE_NAME\u003e_EVENT_DATA, *p\u003cMACHINE_NAME\u003e_EVENT_DATA;\n```\n\nFinally, the machine's event enumeration will be named \u003cMACHINE_NAME\u003e_EVENT_ENUM, and a structure, containing the event enumeration and the event data union will be created:\n\n```c\ntypedef struct {\n   \u003cMACHINE_NAME\u003e_EVENT_ENUM event;\n   \u003cMACHINE_NAME\u003e_EVENT_DATA event_data;\n} \u003cMACHINE_NAME\u003e_EVENT, *p\u003cMACHINE_NAME\u003e_EVENT;\n```\n\nAs indicated above, this structure is used to communicated external events to the state machine.  It is *not* used to communicate events internally.  Internally, events are communicated *only* through the event enumeration, as is done by machines not having events with data.  Thus, action functions are declared as returning \u003cMACHINE_NAME\u003e_EVENT_ENUM, rather than \u003cMACHINE_NAME\u003e_EVENT.  Because of this, the data communicated by the (external) event must be moved into the machine's data structure, in order to be visible to the action functions.\n\nThe movement of data is done through a invocation of a data translator.  The translater can be named in the machine description:\n\n```\nevent data_packet_arrived data translator copy_data_packet {\n   unsigned length;\n   uint8t_t *packet;\n};\n```\n\nOtherwise, a translator function will be declared as \u003cmachine_name\u003e_translate_\u003cevent_name\u003e_data.\n\nIn either case, the function arguments are first, a pointer to the machine structure; and second, a pointer to the event data.\n\n[Top of page](#fsmlang)\n\n## Using the State Machine\n\nTo use the state machine in C code, include the generated header file, and use the provided RUN_STATE_MACHINE macro to call the state machine function with the desired event. Unless prohibited on the FSMLang command line, the generated header file provides a pointer to an instance of the machine, p\u003cmachineName\u003e. This is the first argument to the macro. The second is the event name as found in the machine's event enumeration.\n\nUsing the Simple Communicator as an example, this line runs the machine with the ACK event:\n\n```c\n    RUN_STATE_MACHINE(psimpleCommunicator,simpleCommunicator_ACK);\n```\n\nThis would be placed in the function which receives the message. Since such functions are often IRSs, it must be remembered that the machine is not reentrant. Invocations of the machine outside of this ISR context must be properly protected from this interrupt. The needed protection can be facilitated through the use of the reentrant keyword to modify the machine declaration. Or, it may be done by simply wraping the invocation with interrupt protection.\n\nWith the 1.40 release, a function is provided which can be used to invoke the singleton state machine normally included in the generated code. The function is declared as\n\n```c\n    run_\u003cmachine_name\u003e(\u003cp\u003e\u003cMACHINE_NAME\u003e_EVENT);\n```\n\nFor the simpleCommunicator, this would be:\n\n```c\n    run_simpleCommunicator(SIMPLE_COMMUNICATOR_EVENT);\n```\n\n[Top of page](#fsmlang)\n\n## Commenting\n\nAs shown in the examples above, \"document comments\" can be added to the FSMLang file. In addition to illuminating the FSMLang file itself, these comments are used in the HTML and PLANTUML output. Release 1.40 brings a slight modification to how document comments are processed.\n\nThe most visible change is that document comments appearing before action or transition matrices now adhere to the matrix. Formerly, such comments would adhere either to the action, or to the first event in the matrix, in the case of a transition only matrix. (This last circumstance was not really desireable, of course.) By adhering to the matrix, the comment addresses the involved event and state vectors (along with any associated transition), rather than the action. Previously, there was no way to make such a comment.\n\nWith this change, the comments are now used differently in the HTML output, and used for the first time in the plantuml output.\n\nIn the HTML, the comments are placed in the relevant cell of the event/state table. If the matrix is a cross-product of non-trivial event and state vectors, each cell in that product will contain the comment. If this is not desired, the vectors can be broken up into trivial vectors (*i.e.* individual event, state pairs), each being given the appropriate comment. \n\nIn the plantuml, these comments are used as notes *on link*, appearing next to the line linking two states by the transition.\n\nHTML formatting can be controlled by providing an alternate style sheet (*--css-content-filename=\u003cfilename\u003e *).  To provide independence to the HTML output, the stylesheet can be included directly into the HTML document (*--css-content-internal=true*).\n\nThe generated plantuml can be altered in several ways:  The machine name can be set as the diagram title ( *--add-plantuml-title=true*).  Strings may be given which will be placed after the opening @plantuml, but before any FSMLang generated content (*--add-plantuml-prefix-string=\u003ctext\u003e*).  And, whole files can be named which will be similarly placed (*--add-plantuml-prefix-file=\u003cfilename\u003e*).  The last two options can be used any number of times; the text or files will be added in the order given; but all strings will be added before any files.  Finally, *--add-plantuml-legend=true* will add a legend, which, by default, will contain the names and associated document commnents for all events, states, and, actions of the machine.  Any of these three may be excluded by *--exclude-[events|states|actions]-from-plantuml-legend=true*.\n\n[Top of page](#fsmlang)\n\n## Making the FSMLang Executable\n\nThe source is in a Git repository at https://github.com/FSMLang/FSMLang.\n\nFour targets are provided to support Linux (RH 6.5 has been tested, but may now need some maintenance), Cygwin (on Windows 10), and, MinGW and MinGWsa (also on Windows 10). MinGW uses the MinGW compiler, but expects to be built in a Cygwin shell, while MinGWsa works for a stand-alone MinGW installation (but, use the bash shell to do the build). To make the executable for any of these, simply type make \u003ctarget\u003e, spelling the name of the target as given in the previous sentence. Appending \".test\" to any target name will execute the full test suite. Appending \".clean\" will do as expected.\n\nsimpleCommunicator.fsm is at the top of the tree; it is as shown in this page.\n\nThere are irrelevant files at the top of the tree. Ignore them. (Identifying these is an exercise also left to the reader.)\n\n[Top of page](#fsmlang)\n\n## Command Syntax\n```\nUsage : fsm [-tc|s|h|p] [-o outfile] [-s] filename, where filename ends with '.fsm'\n\t and where 'c' gets you c code output based on an event/state table,\n\t 's' gets you c code output with individual state functions using switch constructions,\n\t and 'h' gets you html output\n\t and 'p' gets you PlantUML output\n\t-i0 inhibits the creation of a machine instance\n\t\tany other argument to 'i' allows the creation of an instance;\n\t\tthis is the default\n\t-c will create a more compact event/state table when -tc is used\n\t\twith machines having actions which return states\n\t-s prints some useful statistics and exits\n\t-o  \u003coutfile\u003e will use \u003coutfile\u003e as the filename for the top-level machine output.\n\t\tAny sub-machines will be put into files based on the sub-machine names.\n\t--generate-weak-fns=false suppresses the generation of weak function stubs.\n\t--force-generation-of-event-passing-actions forces the generation of actions which pass events\n\t\twhen weak function generation is disabled..\n\t\tThe generated functions are not weak.\n\t--core-logging-only=true suppresses the generation of debug log messages in all but the core FSM function.\n\t--generate-run-function\u003c=true|false\u003e enables or supresses the generation of a run\n\t\tfunction to replace the RUN_STATE_MACHINE machro.\n\t\tThe default is to generate the macro; the option argument is optional, if not given, \"true\" is assumed.\n\t--include-svg-img=true adds \u003cimg/\u003e tag referencing \u003cfilename\u003e.svg to include an image at the top of the web page.\n\t--css-content-internal=true puts the CSS directly into the html.\n\t--css-content-filename=\u003cfilename\u003e uses the named file for the css citation, or\n\t\tfor the content copy.\n\t--add-plantuml-title=\u003c*true|false\u003e adds the machine name as a title to the plantuml.\n\t--add-plantuml-legend=\u003c*center|left|right|top|*bottm\u003e adds a legend to the plantuml.\n\t\tCenter, bottom are the defaults.  Horizontal and vertial parameters can be added in a quoted string.\n\t\tCenter is a horizontal parameter.\n\t\tBy default, event, state, and action lists are included in the legend, and event descriptions are removed\n\t\tfrom the body of the diagram.\n\t--exclude-states-from-plantuml-legend=\u003c*true|false\u003e excludes state information from the plantuml legend.\n\t\tWhen excluded from legend, state comments are included in the diagram body.\n\t--exclude-events-from-plantuml-legend=\u003c*true|false\u003e excludes event information from the plantuml legend.\n\t--exclude-actions-from-plantuml-legend=\u003c*true|false\u003e excludes action information from the plantuml legend.\n\t--add-machine-name adds the machine name when using the --short-debug-names option\n\t--add-event-cross-reference\u003c=true|false\u003e adds a cross-reference list as a comment block\n\t\tin front of the machine event enumeration. Omitting the optional argument is equivalent\n\t\tto specifying \"true\"\n\t--add-plantuml-prefix-string=\u003ctext\u003e will add the specified text to the plantuml output before\n\t\tany generated output.  This option can be specified multiple times; all text will be\n\t\tadded in the order given\n\t\tfor the content copy.\n\t--add-plantuml-prefix-file=\u003ctext\u003e will add the text in the specified file\n\t\tto the plantuml output before any generated output.\n\t\tThis option can be specified multiple times; all text will be\n\t\tadded in the order given\n\t\tfor the content copy.\n\t-v prints the version and exits\n```\nThe default is to generate C code using an action table.\n\n[Top of page](#fsmlang)\n\n## Basic Language Syntax\n\nThe example machine above illustrates the major language features and syntax. There are five basic keywords: machine, state, event, action, transition. A sixth keyword, returns, was shown in the example in its use as an informational element for actions, but is not further discussed here. Identifiers follow the C rules, as do declarations, which may be comma seperated, as in the state and event declarations above, or set seperately delimited by semi-colons as are all of the action declarations. State and event declarations may be interspersed, but all must come before the first action declaration. Naming scope is within a given machine; that is, on the one hand, all states and events must be declared within a machine in order to be used by the machine, and on the other, different machines may use the same names for states and events because they will apply only within that machine definition.\n\nThe action declaration is :\n\n```\naction action_identifier[event_vector,state_vector];\n```\n\nOr,\n\n```\naction action_identifier[event_vector,state_vector] transition state_identifier;\n```\nWhere event_vector is\n\nevent_identifier\n\nor,\n\n(event_identifier_list)\n\nwith event_identifier_list being\n\nevent_identifier\n\nor\n\nevent_identifier, event_identifier_list.\n\nThe analogous definition holds for state_vectors.\n\nIt is also possible to declare an actionless transition:\n\n```\ntransition[event_vector, state_vector] state_identifier;\n```\n\n[Top of page](#fsmlang)\n\n## More examples\n\nThere are many files with the .fsm extension included in the distribution, most found under the test directory. All illustrate valid constructions. Moreover, the tests starting with full_test create executables with action functions supplied. The effects of the use of other keywords not discussed here can also be seen, such as:\n\ndesignating a machine to use action functions which return a state instead of an event\ngiving a machine some data\ndesignating header files to be included by the generated header (perhaps needed by the data given to the machine)\ndesignating a function to call on every machine transition\ntransitioning via functions that return states (with machines where actions return events).\nassigning values from external enums to the internal event enumerations (this should be used only when forcing the generation of switch statements for state handlers).\nAlso found in the examples is some new functionality involving complex events. The html side of this works, but a full test has not yet been created. This may be of interest to ThreadX fans, since the idea of the complex event comes from a working system running under ThreadX where some events are messages taken from a queue, and can themselves contain enumerations which further refine the event. For example a message to control a subsystem might contain an enumeration indicating whether to activate or deactivate the system. In this way, the actual real events can be tracked in FSMLang (subsystem.activate being an event distinct from subsystem.deactivate).\n\nFinally, the test makefiles show how to add .fsm targets to a makefile.\n\n[Top of page](#fsmlang)\n","funding_links":[],"categories":["Common"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FFSMLang%2FFSMLang","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FFSMLang%2FFSMLang","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FFSMLang%2FFSMLang/lists"}