sync/gen_statemachine.ysl2
author Edouard Tisserant <edouard@pep-project.org>
Sun, 04 Dec 2016 02:51:59 +0100
branchENGINE-133
changeset 1460 66ceb5a7f718
parent 1445 5d233bcdde76
child 1467 ff7c60d14af0
permissions -rw-r--r--
ENGINE-133 added state 'payload' pointer to data attached to the state, allocated/dealocated on transitions. This state payload allows spontaneous transitions having no context (i.e. timeout) to get some context about the state. It can be used in other cases like checking that received group keys are from the partner we expect (TODO)
vb@577
     1
// generate state machine code
vb@577
     2
vb@577
     3
// Copyleft (c) 2016, p≡p foundation
vb@577
     4
vb@577
     5
// Written by Volker Birk
vb@577
     6
vb@577
     7
include yslt.yml2
vb@577
     8
vb@577
     9
tstylesheet {
edouard@1460
    10
    include standardlib.ysl2
vb@623
    11
    include ./functions.ysl2
Edouard@605
    12
vb@811
    13
    template "/protocol" {
vb@811
    14
        document "../src/Makefile.protocols", "text"
vb@811
    15
            apply "fsm", 0, mode="make";
vb@811
    16
        apply "fsm", 0, mode=gen;
vb@811
    17
    }
vb@811
    18
vb@811
    19
    template "fsm", mode=make
vb@811
    20
    ||
vb@811
    21
    «@filename»_fsm.c: ../sync/devicegroup.fsm
vb@811
    22
    \tmake -C ../«@filename»
vb@811
    23
    ||
vb@811
    24
vb@811
    25
    template "fsm", mode=gen {
vb@807
    26
        document "../src/{@filename}_fsm.h", "text" {
vb@609
    27
        ||
vb@609
    28
        #pragma once
vb@577
    29
vb@609
    30
        // state machine for «@name»
vb@577
    31
vb@1099
    32
        #include "message_api.h"
vb@654
    33
        
vb@654
    34
        #ifdef __cplusplus
vb@654
    35
        extern "C" {
vb@654
    36
        #endif
vb@608
    37
vb@609
    38
        // types
Edouard@605
    39
vb@609
    40
        typedef pEp_identity * Identity;
vb@939
    41
        typedef stringlist_t * Stringlist;
vb@939
    42
        typedef union _param { Identity partner; stringlist_t *keylist; } param_t;
vb@583
    43
vb@609
    44
        // error values
vb@577
    45
vb@609
    46
        typedef enum _fsm_error {
vb@964
    47
            // these error values are corresponding to
vb@964
    48
            // PEP_SYNC_STATEMACHINE_ERROR - value
vb@743
    49
            invalid_state = -2,
vb@959
    50
            invalid_event = -3,
vb@959
    51
            invalid_condition = -4,
vb@964
    52
            invalid_action = -5,
vb@964
    53
vb@964
    54
            // out of memory condition
vb@964
    55
            invalid_out_of_memory = -128
vb@609
    56
        } fsm_error;
vb@583
    57
vb@951
    58
        // conditions
vb@951
    59
vb@959
    60
        `` for "func:distinctName(condition)" | int «@name»(PEP_SESSION session`apply "parm", 0`);
vb@951
    61
vb@609
    62
        // states
vb@578
    63
vb@609
    64
        typedef enum _«@name»_state {
vb@1043
    65
            // error values also in this namespace
vb@1043
    66
            «@name»_state_invalid_state = (int) invalid_state,
vb@1043
    67
            «@name»_state_invalid_event = (int) invalid_event,
vb@1043
    68
            «@name»_state_invalid_condition = (int) invalid_condition,
vb@1043
    69
            «@name»_state_invalid_action = (int) invalid_action,
vb@1043
    70
            «@name»_state_invalid_out_of_memory = (int) invalid_out_of_memory,
vb@1043
    71
vb@951
    72
            «@name»_state_NONE = 0,
vb@623
    73
        `` for "func:distinctName(state)" |> «@name»`if "position()!=last()" > , `
vb@609
    74
        } «@name»_state;
vb@583
    75
vb@609
    76
        // events
vb@578
    77
vb@609
    78
        typedef enum _«@name»_event {
vb@951
    79
            «@name»_event_NONE = 0,
vb@711
    80
        ||
vb@711
    81
        for "func:distinctName(state/event[not(not(/protocol/fsm/tag/@name=@name))])" {
vb@711
    82
            const "name", "@name";
vb@711
    83
            |> «$name» = «/protocol/fsm/tag[@name=$name]/@id»,
vb@711
    84
        }
vb@711
    85
        for "func:distinctName(state/event[not(/protocol/fsm/tag/@name=@name)])"
vb@711
    86
            |> «@name»`if "position()!=last()" > , `
vb@711
    87
        ||
vb@609
    88
        } «@name»_event;
vb@583
    89
vb@609
    90
        // actions
vb@582
    91
vb@690
    92
        `` const "name", "@name"
vb@939
    93
        `` for "func:distinctName(//action)" | PEP_STATUS «@name»(PEP_SESSION session, «$name»_state state, Identity partner, void *extra);
Edouard@605
    94
edouard@1205
    95
        // event injector
edouard@1205
    96
edouard@1205
    97
        PEP_STATUS inject_DeviceState_event(
edouard@1205
    98
            PEP_SESSION session, 
edouard@1205
    99
            DeviceState_event event,
edouard@1205
   100
            Identity partner,
edouard@1205
   101
            void *extra);
edouard@1205
   102
vb@951
   103
        // message receiver
vb@951
   104
        
edouard@1165
   105
        PEP_STATUS receive_DeviceState_msg(
edouard@1165
   106
                PEP_SESSION session, 
edouard@1165
   107
                message *src, 
edouard@1165
   108
                PEP_rating rating, 
edouard@1165
   109
                stringlist_t *keylist
edouard@1165
   110
            );
vb@951
   111
vb@626
   112
        // state machine
vb@626
   113
vb@626
   114
        «@name»_state fsm_«@name»(
vb@627
   115
                PEP_SESSION session,
vb@626
   116
                «@name»_state state,
vb@626
   117
                «@name»_event event,
vb@939
   118
                Identity partner,
edouard@1445
   119
                void *extra,
edouard@1445
   120
                time_t *timeout
vb@626
   121
            );
vb@626
   122
vb@609
   123
        // driver
Edouard@605
   124
vb@782
   125
        DYNAMIC_API PEP_STATUS fsm_«@name»_inject(
vb@690
   126
                PEP_SESSION session,
vb@690
   127
                «@name»_event event,
vb@690
   128
                Identity partner,
edouard@1445
   129
                void *extra,
edouard@1445
   130
                time_t *timeout
vb@690
   131
            );
Edouard@605
   132
vb@654
   133
        #ifdef __cplusplus
vb@654
   134
        }
vb@654
   135
        #endif
vb@654
   136
vb@609
   137
        ||
vb@711
   138
        }
vb@807
   139
        document "../src/{@filename}_driver.c", "text"
vb@690
   140
        ||
vb@690
   141
        // Driver for «@name» state machine
vb@690
   142
vb@690
   143
        #include <assert.h>
vb@690
   144
        #include "pEp_internal.h"
vb@690
   145
vb@690
   146
vb@743
   147
        DYNAMIC_API PEP_STATUS fsm_«@name»_inject(
vb@690
   148
                PEP_SESSION session,
vb@690
   149
                «@name»_event event,
vb@690
   150
                Identity partner,
edouard@1445
   151
                void *extra,
edouard@1445
   152
                time_t *timeout
vb@690
   153
            )
vb@690
   154
        {
vb@951
   155
            assert(session);
vb@951
   156
            if (!session)
vb@951
   157
                return PEP_ILLEGAL_VALUE;
vb@690
   158
edouard@1152
   159
            while(true)
edouard@1152
   160
            {
edouard@1152
   161
                «@name»_state new_state = fsm_«@name»(session,
edouard@1445
   162
                    session->«@filename»_state, event, partner, extra, timeout);
edouard@1157
   163
edouard@1152
   164
                if (new_state == «@name»_state_invalid_out_of_memory)
edouard@1152
   165
                    return PEP_OUT_OF_MEMORY;
edouard@1157
   166
edouard@1152
   167
                if (new_state < 0)
edouard@1152
   168
                    return PEP_SYNC_STATEMACHINE_ERROR - new_state;
edouard@1152
   169
                
edouard@1157
   170
                if (new_state == session->«@filename»_state)
edouard@1152
   171
                    break;
vb@690
   172
edouard@1157
   173
                event = Init;
edouard@1157
   174
                extra = NULL;
edouard@1152
   175
                session->«@filename»_state = new_state;
edouard@1152
   176
            } 
edouard@1152
   177
vb@951
   178
            return PEP_STATUS_OK;
vb@690
   179
        }
vb@690
   180
vb@690
   181
        ||
vb@807
   182
        document "../src/{@filename}_fsm.c", "text"
vb@609
   183
        ||
vb@807
   184
        #include "«@filename»_fsm.h"
edouard@1460
   185
        #include "pEp_internal.h"
vb@609
   186
vb@609
   187
        // state machine for «@name»
vb@609
   188
vb@609
   189
        «@name»_state fsm_«@name»(
vb@627
   190
                PEP_SESSION session,
vb@609
   191
                «@name»_state state,
vb@609
   192
                «@name»_event event,
vb@939
   193
                Identity partner,
edouard@1445
   194
                void *extra,
edouard@1445
   195
                time_t *timeout
vb@609
   196
            )
vb@609
   197
        {
vb@964
   198
            int cond_result;
vb@959
   199
            PEP_STATUS status = PEP_STATUS_OK;
vb@959
   200
vb@609
   201
            switch (state) {
vb@951
   202
            `` apply "state", 2
vb@609
   203
                default:
vb@743
   204
                    return («@name»_state) invalid_state;
vb@609
   205
            }
vb@609
   206
vb@609
   207
            return state;
vb@577
   208
        }
vb@582
   209
vb@609
   210
        ||
vb@577
   211
    }
vb@577
   212
vb@580
   213
    template "state"
vb@577
   214
    ||
vb@577
   215
    case «@name»:
edouard@1460
   216
    {
edouard@1460
   217
        `` if "count(parm) > 1" error | # TODO composite state payload 
edouard@1460
   218
        `` apply "parm", 1 mode="stateParm"
edouard@1445
   219
        *timeout = «@timeout»;
vb@578
   220
        switch (event) {
edouard@1356
   221
        `` if "not(event[@name='Init'])" |>> case Init: break;
vb@578
   222
        `` apply "event", 2
vb@951
   223
            default:
vb@951
   224
                return («../@name»_state) invalid_event;
vb@578
   225
        }
vb@577
   226
        break;
edouard@1460
   227
    }
edouard@1460
   228
    ||
vb@577
   229
edouard@1460
   230
    template "parm" mode="stateParm" 
edouard@1460
   231
    {
edouard@1460
   232
        | «name(*[1])» «name(*[2])» = («name(*[1])»)session->sync_state_payload;
edouard@1460
   233
    }
vb@578
   234
vb@578
   235
    template "event"
vb@578
   236
    ||
vb@578
   237
    case «@name»:
edouard@1460
   238
    {
vb@951
   239
    `` apply "action|transition|condition";
vb@582
   240
    `` if "name(*[position()=last()]) != 'transition'" |> break;
edouard@1460
   241
    }
vb@578
   242
    ||
vb@578
   243
vb@582
   244
    template "action" {
vb@582
   245
        indent(0);
vb@959
   246
        > status = «@name»(session, state, 
vb@582
   247
        choose {
vb@582
   248
            when "parm" > «name(parm/*)»
vb@582
   249
            otherwise > NULL
vb@582
   250
        }
edouard@1166
   251
        choose {
edouard@1166
   252
            when "parm[2]" > , extra /*«name(parm[2]/*)»*/
edouard@1166
   253
            otherwise > , NULL
edouard@1166
   254
        }
edouard@1166
   255
        > );\n
vb@964
   256
        | if (status == PEP_OUT_OF_MEMORY)
vb@1043
   257
        |> return (int) invalid_out_of_memory;
vb@959
   258
        | if (status != PEP_STATUS_OK)
vb@1043
   259
        |> return (int) invalid_action;
vb@582
   260
    }
vb@582
   261
vb@951
   262
    template "condition" {
vb@959
   263
        | cond_result = «@name»(session`apply "parm", 0`);
vb@964
   264
        | if (cond_result < 0)
vb@964
   265
        |> return cond_result;
vb@959
   266
        | if (cond_result) {
vb@951
   267
        apply "action|transition|condition";
vb@951
   268
        | }
vb@951
   269
    }
vb@951
   270
vb@951
   271
    template "parm" choose {
vb@951
   272
        when "count(*) = 1"
vb@951
   273
            > , «name(*)»
vb@951
   274
        otherwise
vb@951
   275
            > , «name(*[1])» «name(*[2])»
vb@951
   276
    }
vb@951
   277
edouard@1460
   278
    template "transition"{
edouard@1460
   279
        const "stateparm", "ancestor::state/child::parm";
edouard@1460
   280
        if "$stateparm" {
edouard@1460
   281
            | if(session->sync_state_payload){
edouard@1460
   282
            |     free_«yml:lcase(name($stateparm[1]/*))»((«name($stateparm[1]/*)»)session->sync_state_payload);
edouard@1460
   283
            |     session->sync_state_payload = NULL;
edouard@1460
   284
            | }
edouard@1460
   285
        }
edouard@1460
   286
        if "parm" {
edouard@1460
   287
            const "nextstatename", "@target";
edouard@1460
   288
            const "nextstateparm", "ancestor::fsm/child::state[@name = $nextstatename]/child::parm";
edouard@1460
   289
            | session->sync_state_payload = «yml:lcase(name($nextstateparm/*))»_dup(«name(parm/*)»);
edouard@1460
   290
        }
edouard@1460
   291
        | return «@target»;
edouard@1460
   292
    }
vb@577
   293
}
vb@577
   294