# [Legato] Layered Publish-Subscribe Handlers > Legato 官網對 Layered Publish-Subscribe Handlers 的說明:https://docs.legato.io/latest/c_eventLoop.html#c_event_layeredPublishSubscribe 在使用 Legato 框架做開發時,若需要讓 client 因 server 發佈某個 event 而去執行 handler function,這時就可以用 Legato 的 Layered Publish-Subscribe Handler 機制。 Layered Publish-Subscribe Handlers 的 `Layered` 是指有兩層的 handler,第一層 handler 由 server 端執行,第二層 handler 由 client 端執行,當第一層 handler 執行完就會去呼叫第二層 handler。 基本流程: 1. Server 發布一個 event 2. Server 先執行第一層 handler,並且有需要的話可以在第一層 handler 內處理 event data。然後在第一層 handler 內呼叫第二層 handler,並且將剛剛處理過的 event data 一起傳給第二層 handler 3. 最後 client 執行第二層 handler。 在介紹 Layered Publish-Subscribe Handlers 的用法之前,必須要先介紹在 `.api` 檔案內使用 `EVENT` 和 `HANDLER` 的用法。 ## `EVENT` & `HANDLER` in API File 我們可以在 `.api` 檔案裡面設定 `EVENT` 和 `HANDLER` 給 client。由於官網對這兩者實際的用途好像說明不多,查了 Legato github 裡面的 sample code,`EVENT` 和 `HANDLER` 似乎主要都是用在 [Layered Publish-Subscribe Handlers](https://docs.legato.io/latest/c_eventLoop.html#c_event_layeredPublishSubscribe)。所以以下主要以 [Layered Publish-Subscribe Handlers](https://docs.legato.io/latest/c_eventLoop.html#c_event_layeredPublishSubscribe) 的角度來介紹關於在 `.api` 裡面使用 `EVENT` 和 `HANDLER` 的用法。 ### Syntax #### `HANDLER` 以下是在 `.api` 內定義一個 `HANDLER` 的語法: ```api HANDLER <handlerType> ( [<parameterList>] ); ``` - `parameterList` 可以是一個參數,或者是多個參數(由逗號分開),或者如果沒有參數的話也可以是空的。 - `parameterList` 僅能是 scalar type 或者是 `string` type。 - 所有的 parameter 都應該是 `IN` parameters Example: ```api HANDLER MyHandler ( int32 n1, int32 n2 ); ``` #### `EVENT` 以下是在 `.api` 內定義一個 `EVENT` 的語法: ```api EVENT <eventType> ( <parameterList> ); ``` - `parameterList` 可以是一個參數,或者是多個參數(由逗號分開)。 - 只要對 function 是合法的,都可以是參數。 - 其中一個參數一定要是 handler(也就是處理這個 `EVENT` 的 `HANDLER`)。 - The parameters are used when registering a handler for the specified event. Example: ``` EVENT MyEvent ( int32 eventParam1, // Used when registering the handler i.e. it is // passed into the generated ADD_HANDLER function. MyHandler handler // 每個 EVENT 一定要有一個參數是 HANDLER ); ``` :::info 定義在 API 檔裡面的 `HANDLER` 和 `EVENT`,若是用在 Layered Publish-Subscribe Handlers 的情境,Handler 就是指第二層的 handler,是由 client 實做的,而 Event 則是由 server 所發布。 ::: ## `EVENT` and `HANDLER` in C 接下來看 `.api` 裡面的 `EVENT` 和 `HANDLER` 在 C 語言會被轉換成什麼樣子。假設有以下的 project structure: ``` ~/eventHandlerAPI ├── clientApp │   ├── layeredHandlerClientApp.adef │   └── layeredHandlerClientComp │   ├── Component.cdef │   └── layeredHandlerClient.c ├── serverApp │ ├── layeredHandlerServerApp.adef │ └── layeredHandlerServerComp │ ├── Component.cdef │ └── layeredHandlerServer.c └── serverAPI.api ``` `serverAPI.api` ```api HANDLER MyHandler ( int32 myHandlerParam1 IN, int32 myHandlerParam2 IN ); EVENT MyEvent ( int32 myEventParam1, int32 myEventParam2, int32 myEventParam3, MyHandler handler ); ``` `serverApp/layeredHandlerServerComp/Component.cdef` 在這裡可以看到 server 端的 interface 名稱是 `serverInterface`: ```cdef sources: { layeredHandlerServer.c } provides: { api: { serverInterface = serverAPI.api } } ``` `clientApp/layeredHandlerClientComp/Component.cdef` 在這裡可以看到 client 端的 interface 名稱是 `clientInterface`: ```cdef sources: { layeredHandlerClient.c } requires: { api: { clientInterface = serverAPI.api } } ``` 對這兩個 App 執行 `mkapp`: ```bash ~/eventHandlerAPI$ mkapp -t simulation -i ./ serverApp/layeredHandlerServerApp.adef ~/eventHandlerAPI$ mkapp -t simulation -i ./ clientApp/layeredHandlerClientApp.adef ``` 執行完以上步驟後,會產生 `_build_layeredHandlerServerApp` 和 `_build_layeredHandlerClientApp` 這兩個資料夾。 `_build_layeredHandlerServerApp/simulation/api/042696e536911f5b64f43e65f41e5464/serverAPI_common.h` ```cpp=21 //-------------------------------------------------------------------------------------------------- /** * Reference type used by Add/Remove functions for EVENT 'serverAPI_MyEvent' */ //-------------------------------------------------------------------------------------------------- typedef struct serverAPI_MyEventHandler* serverAPI_MyEventHandlerRef_t; //-------------------------------------------------------------------------------------------------- /** */ //-------------------------------------------------------------------------------------------------- typedef void (*serverAPI_MyHandlerFunc_t) ( int32_t myHandlerParam1, ///< int32_t myHandlerParam2, ///< void* contextPtr ///< ); ``` `_build_layeredHandlerServerApp/simulation/api/042696e536911f5b64f43e65f41e5464/server/serverInterface_server.h` ```cpp=52 //-------------------------------------------------------------------------------------------------- /** */ //-------------------------------------------------------------------------------------------------- typedef serverAPI_MyHandlerFunc_t serverInterface_MyHandlerFunc_t; //-------------------------------------------------------------------------------------------------- /** * Reference type used by Add/Remove functions for EVENT 'serverInterface_MyEvent' */ //-------------------------------------------------------------------------------------------------- typedef serverAPI_MyEventHandlerRef_t serverInterface_MyEventHandlerRef_t; //-------------------------------------------------------------------------------------------------- /** * Add handler function for EVENT 'serverInterface_MyEvent' */ //-------------------------------------------------------------------------------------------------- serverInterface_MyEventHandlerRef_t serverInterface_AddMyEventHandler ( int32_t myEventParam1, ///< [IN] int32_t myEventParam2, ///< [IN] int32_t myEventParam3, ///< [IN] serverInterface_MyHandlerFunc_t handlerPtr, ///< [IN] void* contextPtr ///< [IN] ); //-------------------------------------------------------------------------------------------------- /** * Remove handler function for EVENT 'serverInterface_MyEvent' */ //-------------------------------------------------------------------------------------------------- void serverInterface_RemoveMyEventHandler ( serverInterface_MyEventHandlerRef_t handlerRef ///< [IN] ); ``` `_build_layeredHandlerClientApp/simulation/api/042696e536911f5b64f43e65f41e5464/serverAPI_common.h` ```cpp=21 //-------------------------------------------------------------------------------------------------- /** * Reference type used by Add/Remove functions for EVENT 'serverAPI_MyEvent' */ //-------------------------------------------------------------------------------------------------- typedef struct serverAPI_MyEventHandler* serverAPI_MyEventHandlerRef_t; //-------------------------------------------------------------------------------------------------- /** */ //-------------------------------------------------------------------------------------------------- typedef void (*serverAPI_MyHandlerFunc_t) ( int32_t myHandlerParam1, ///< int32_t myHandlerParam2, ///< void* contextPtr ///< ); ``` `_build_layeredHandlerClientApp/simulation/api/042696e536911f5b64f43e65f41e5464/client/clientInterface_interface.h` ```cpp=122 //-------------------------------------------------------------------------------------------------- /** */ //-------------------------------------------------------------------------------------------------- typedef serverAPI_MyHandlerFunc_t clientInterface_MyHandlerFunc_t; //-------------------------------------------------------------------------------------------------- /** * Reference type used by Add/Remove functions for EVENT 'clientInterface_MyEvent' */ //-------------------------------------------------------------------------------------------------- typedef serverAPI_MyEventHandlerRef_t clientInterface_MyEventHandlerRef_t; //-------------------------------------------------------------------------------------------------- /** * Add handler function for EVENT 'clientInterface_MyEvent' */ //-------------------------------------------------------------------------------------------------- clientInterface_MyEventHandlerRef_t clientInterface_AddMyEventHandler ( int32_t myEventParam1, ///< [IN] int32_t myEventParam2, ///< [IN] int32_t myEventParam3, ///< [IN] clientInterface_MyHandlerFunc_t handlerPtr, ///< [IN] void* contextPtr ///< [IN] ); //-------------------------------------------------------------------------------------------------- /** * Remove handler function for EVENT 'clientInterface_MyEvent' */ //-------------------------------------------------------------------------------------------------- void clientInterface_RemoveMyEventHandler ( clientInterface_MyEventHandlerRef_t handlerRef ///< [IN] ); ``` 可以看到 server 端跟 client 端都會產生以下的 prototype: 1. `<interface_name>_Add<event_name>Handler` 2. `<interface_name>_Remove<event_name>Handler` 3. ```cpp type void (*<interface_name>_<handler_name>Func_t) ( <handler parameter_list>, void *contextPtr ); ``` 以上三者在 Layered Publish-Subscribe Handlers 的使用情境: - `<handler_name>Func_t`: - 就是前面所說的 second layer handler function - 由 client 端實作 - `Add<event_name>Handler` 和 `Remove<event_name>Handler`: - 兩者皆由 server 端實作 - Server 在 `Add<event_name>Handler` 內呼叫 [`le_event_AddLayeredHandler`](https://docs.legato.io/latest/le__eventLoop_8h.html#a8b906d38935f64953482f42c745e1c18) 來指定: 1. Event ID 2. 前項的 event 被發佈後,server 欲執行的 first layer handler 3. Second layer handler - Client 使用 `Add<event_name>Handler` 來: 1. 傳送 event parameter 2. 指定說若 server 發佈 event 了,那要呼叫哪個 second layer handler function 3. 設定欲傳給 second layer handler 使用的 context - Client 使用 `Remove<event_name>Handler` 來取消 second layer handler ## Layered Publish-Subscribe Handlers ### 實作流程 <!-- #### 1. 假設有以下 `.api` 檔案: ```api HANDLER MyHandler ( int32 myHandlerParam1 IN, int32 myHandlerParam2 IN ); // 這是 second layer handler,由 client 實作此 handler function EVENT MyEvent ( int32 myEventParam1, int32 myEventParam2, int32 myEventParam3, MyHandler handler ); // 此 event 由 server 發佈 ``` #### 2. Server 實作 First Layer Handler、`Add<event>Handler`、以及 `Remove<event>Handler` ```cpp void FirstLayerHandlerFunc ( void *reportPtr, void *secondLayerHandlerFunc ) { // do something in the first layer handler serverInterface_MyHandlerFunc_t clientHandlerFunc = secondLayerHandlerFunc; // 在 first layer handler 最後要呼叫 second layer handler clientHandlerFunc(myHandlerParam1, myHandlerParam2, le_event_GetContextPtr()); // 我們要用 le_event_GetContextPtr() 將 contextPtr 傳到 second layer handler } ``` :::info First Layer Handler 的 Prototype: ```cpp typedef void (*le_event_LayeredHandlerFunc_t) ( void *reportPtr, void *secondLayerFunc ) ``` ::: --> ![legato_layered_publish_subscribe_handler_part1](https://hackmd.io/_uploads/rkd8uxE0R.png) 之後,當 server 發佈該 event: 1. Server 執行 first layer handler,並在 first layer handler 內最後呼叫 second layer handler 2. Client 執行 second layer handler 最後,若 client 呼叫 `Remove<event>Handler`,server 就會執行 `Remove<event>Handler`。 ### Example 以下例子,`EVENT` 有三個參數,都是 `int32_t`,`HANDLER` 則有兩個 `int32_t` 參數。 此外,event data 用以下 structure 來傳遞: ```cpp typedef struct { int32_t n1; int32_t n2; int32_t n3; } MyEventData_t; ``` Server 總共會發布五次 event,每次發布 event 時會連同 event data 一起發布,並且在 first layer handler 內將 event data 的 `n1` 和 `n3` 相加,並傳入 second layer handler 的第一個參數,以及將 event data 的 `n2` 傳入 second layer handler 的第二個參數。 Client 的 second layer handler 被觸發後,就會將收到的兩個參數印出來。 #### Project Structure ``` ~/layeredHandlerAPI$ tree . ├── clientApp │   ├── layeredHandlerClientApp.adef │   └── layeredHandlerClientComp │   ├── Component.cdef │   └── layeredHandlerClient.c ├── serverApp │ ├── layeredHandlerServerApp.adef │ └── layeredHandlerServerComp │ ├── Component.cdef │ └── layeredHandlerServer.c └── serverAPI.api ``` #### `serverAPI.api` ```api HANDLER MyHandler ( int32 myHandlerParam1 IN, int32 myHandlerParam2 IN ); EVENT MyEvent ( int32 myEventParam1, int32 myEventParam2, int32 myEventParam3, MyHandler handler ); ``` #### `serverApp/layeredHandlerServerApp.adef` ```adef executables: { layeredHandlerServerExec = ( layeredHandlerServerComp ) } processes: { run: { layeredHandlerServerProc = ( layeredHandlerServerExec ) } } extern: { interfaceAlias = layeredHandlerServerExec.layeredHandlerServerComp.serverInterface } ``` #### `serverApp/layeredHandlerServerComp/Component.cdef` ```cdef sources: { layeredHandlerServer.c } provides: { api: { serverInterface = serverAPI.api } } ``` #### `serverApp/layeredHandlerServerComp/layeredHandlerServer.c` ```cpp #include "legato.h" #include "interfaces.h" static le_event_Id_t myTestEvent; static le_thread_Ref_t workerThreadRef = NULL; static le_sem_Ref_t semaphoreRef = NULL; typedef struct { int32_t n1; int32_t n2; int32_t n3; } MyEventData_t; MyEventData_t clientSideEventParams = {.n1 = 0, .n2 = 0, .n3 = 0}; // function prototype for FirstLayerHandlerFunc: // typedef void(* le_event_LayeredHandlerFunc_t) (void *reportPtr, void *secondLayerFunc) static void FirstLayerHandlerFunc ( void *reportPtr, void *secondLayerHandlerFunc ) { MyEventData_t *eventDataPtr = (MyEventData_t *) reportPtr; int32_t myHandlerParam1 = eventDataPtr->n1 + eventDataPtr->n3; int32_t myHandlerParam2 = eventDataPtr->n2; LE_INFO("[SERVER] inside FirstLayerHandlerFunc, n1: %d, n2: %d", eventDataPtr->n1, eventDataPtr->n2); serverInterface_MyHandlerFunc_t clientHandlerFunc = secondLayerHandlerFunc; // call client handler // 我們只能使用 le_event_GetContextPtr() 來傳遞 contextPtr clientHandlerFunc(myHandlerParam1, myHandlerParam2, le_event_GetContextPtr()); } // the server side Add<event_name>Handler function will not be called // until the client has called the client side Add<event_name>Handler function serverInterface_MyEventHandlerRef_t serverInterface_AddMyEventHandler ( int32_t myEventParam1, int32_t myEventParam2, int32_t myEventParam3, serverInterface_MyHandlerFunc_t handlerPtr, void *contextPtr ) { LE_INFO("[SERVER] inside serverInterface_AddMyEventHandler"); LE_INFO("Event Paramters from Client: myEventParam1: %d, myEventParam2: %d, myEventParam3: %d", myEventParam1, myEventParam2, myEventParam3); clientSideEventParams.n1 = myEventParam1; clientSideEventParams.n2 = myEventParam2; clientSideEventParams.n3 = myEventParam3; LE_INFO("value of contextPtr: %p", contextPtr); // 設定 event、first layer handler、以及 second layer handler le_event_HandlerRef_t handlerRef = le_event_AddLayeredHandler("MyLayeredHandler", myTestEvent, FirstLayerHandlerFunc, (le_event_HandlerFunc_t) handlerPtr); // 只能用這方式處理 contextPtr le_event_SetContextPtr(handlerRef, contextPtr); le_sem_Post(semaphoreRef); return (serverInterface_MyEventHandlerRef_t) (handlerRef); } void serverInterface_RemoveMyEventHandler ( serverInterface_MyEventHandlerRef_t addHandlerRef ) { LE_INFO("[SERVER] inside serverInterface_RemoveMyEventHandler"); le_event_RemoveHandler((le_event_HandlerRef_t) addHandlerRef); } void *workerThreadFunc(void *context) { LE_INFO("[SERVER] inside wokerThreadFunc"); MyEventData_t eventData = {.n1 = 2, .n2 = 5000, .n3 = 500}; LE_INFO("address of evenData: %p", (void *) &eventData); // wait until the AddHandler function actually starts le_sem_Wait(semaphoreRef); for (int i = 0; i < 5; ++i) { le_event_Report(myTestEvent, &eventData, sizeof(eventData)); LE_INFO("report event (%d)", i + 1); eventData.n1 += clientSideEventParams.n1; eventData.n2 += clientSideEventParams.n2; eventData.n3 += clientSideEventParams.n3; le_thread_Sleep(2); } return NULL; } COMPONENT_INIT { LE_INFO("[SERVER] inside COMPONENT_INIT"); myTestEvent = le_event_CreateId("MyTestingEvent", sizeof(MyEventData_t)); semaphoreRef = le_sem_Create("MySemaphore", 0); workerThreadRef = le_thread_Create("MyWorkerThread", workerThreadFunc, NULL); le_thread_Start(workerThreadRef); LE_INFO("[SERVER] just started the worker thread"); } ``` #### `clientApp/layeredHandlerClientApp.adef` ```adef executables: { layeredHandlerClientExec = ( layeredHandlerClientComp ) } processes: { run: { layeredHandlerClientProc = ( layeredHandlerClientExec ) } } bindings: { layeredHandlerClientExec.layeredHandlerClientComp.clientInterface -> serverApp.interfaceAlias } ``` #### `clientApp/layeredHandlerClientComp/Component.cdef` ```cdef sources: { layeredHandlerClient.c } requires: { api: { clientInterface = serverAPI.api } } ``` #### `clientApp/layeredHandlerClientComp/layeredHandlerClient.c` ```cpp #include "legato.h" #include "interfaces.h" static int secondLayeredHandlerCalledTime = 0; static le_thread_Ref_t workerThreadRef = NULL; static clientInterface_MyEventHandlerRef_t MyEventHandlerRef; // the handlerRef can be used to remove handler // function prototype of the secondLayerHandlerFunc: // void clientInterface_MyHandlerFunc_t(int32_t myHandlerParam1, int32_t myHandlerParam2, void *contextPtr) void secondLayerHandlerFunc(int32_t myHandlerParam1, int32_t myHandlerParam2, void *contextPtr) { LE_INFO("[CLIENT] inside secondLayerHandlerFunc"); secondLayeredHandlerCalledTime += 1; LE_INFO("myHandlerParam1: %d, myHandlerParam2: %d, contextPtr: %p, *contextPtr: %s", myHandlerParam1, myHandlerParam2, contextPtr, (char *) contextPtr); if (secondLayeredHandlerCalledTime == 5) { le_thread_Exit(NULL); } } void workerThreadDestructor(void *context) { LE_INFO("[CLIENT] inside workerThreadDestructor"); LE_INFO("[CLIENT] is going to unregister the second layered handler"); clientInterface_RemoveMyEventHandler(MyEventHandlerRef); LE_INFO("[CLIENT] just unregistered the second layered handler"); } void *workerThreadFunc(void *context) { LE_INFO("[CLIENT] inside workerThreadFunc"); clientInterface_ConnectService(); int32_t myEventParam1 = 10, myEventParam2 = 20, myEventParam3 = 30; char *msgFromClient = "Hello from Client"; LE_INFO("[CLIENT] is going to call the Add<event_name>Handler function"); MyEventHandlerRef = clientInterface_AddMyEventHandler(myEventParam1, myEventParam2, myEventParam3, secondLayerHandlerFunc, (void *) msgFromClient); le_thread_AddDestructor(workerThreadDestructor, NULL); le_event_RunLoop(); return NULL; } COMPONENT_INIT { LE_INFO("[CLIENT] inside COMPONENT_INIT"); workerThreadRef = le_thread_Create("WorkerThread", workerThreadFunc, NULL); le_thread_Start(workerThreadRef); } ``` #### Result ``` Sep 24 07:44:20 simulation user.info TelAF: INFO | layeredHandlerServerProc[2758]/layeredHandlerServerComp T=main | layeredHandlerServer.c _layeredHandlerServerComp_COMPONENT_INIT() 107 | [SERVER] inside COMPONENT_INIT Sep 24 07:44:20 simulation user.info TelAF: INFO | layeredHandlerServerProc[2758]/layeredHandlerServerComp T=main | layeredHandlerServer.c _layeredHandlerServerComp_COMPONENT_INIT() 116 | [SERVER] just started the worker thread Sep 24 07:44:20 simulation user.info TelAF: INFO | layeredHandlerServerProc[2758]/layeredHandlerServerComp T=MyWorkerThread | layeredHandlerServer.c workerThreadFunc() 79 | [SERVER] inside wokerThreadFunc Sep 24 07:44:21 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=main | layeredHandlerClient.c _layeredHandlerClientComp_COMPONENT_INIT() 58 | [CLIENT] inside COMPONENT_INIT Sep 24 07:44:21 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=WorkerThread | layeredHandlerClient.c workerThreadFunc() 36 | [CLIENT] inside workerThreadFunc Sep 24 07:44:21 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=WorkerThread | layeredHandlerClient.c workerThreadFunc() 42 | Address of msgFromClient: 0x14f809d19b89 Sep 24 07:44:21 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=WorkerThread | layeredHandlerClient.c workerThreadFunc() 44 | [CLIENT] is going to call the Add<event_name>Handler function Sep 24 07:44:21 simulation user.info TelAF: INFO | layeredHandlerServerProc[2758]/layeredHandlerServerComp T=main | layeredHandlerServer.c serverInterface_AddMyEventHandler() 48 | [SERVER] inside serverInterface_AddMyEventHandler Sep 24 07:44:21 simulation user.info TelAF: INFO | layeredHandlerServerProc[2758]/layeredHandlerServerComp T=main | layeredHandlerServer.c serverInterface_AddMyEventHandler() 50 | Event Paramters from Client: myEventParam1: 10, myEventParam2: 20, myEventParam3: 30 Sep 24 07:44:21 simulation user.info TelAF: INFO | layeredHandlerServerProc[2758]/layeredHandlerServerComp T=main | layeredHandlerServer.c serverInterface_AddMyEventHandler() 55 | The address pointed by contextPtr: 0x148f5f6f13b0 Sep 24 07:44:21 simulation user.info TelAF: INFO | layeredHandlerServerProc[2758]/layeredHandlerServerComp T=MyWorkerThread | layeredHandlerServer.c workerThreadFunc() 88 | report event (1) Sep 24 07:44:21 simulation user.info TelAF: INFO | layeredHandlerServerProc[2758]/layeredHandlerServerComp T=main | layeredHandlerServer.c FirstLayerHandlerFunc() 29 | [SERVER] inside FirstLayerHandlerFunc, n1: 2, n2: 5000 Sep 24 07:44:21 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=WorkerThread | layeredHandlerClient.c secondLayerHandlerFunc() 13 | [CLIENT] inside secondLayerHandlerFunc Sep 24 07:44:21 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=WorkerThread | layeredHandlerClient.c secondLayerHandlerFunc() 16 | myHandlerParam1: 502, myHandlerParam2: 5000 Sep 24 07:44:21 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=WorkerThread | layeredHandlerClient.c secondLayerHandlerFunc() 17 | contextPtr: 0x14f809d19b89, *contextPtr: Hello from Client Sep 24 07:44:23 simulation user.info TelAF: INFO | layeredHandlerServerProc[2758]/layeredHandlerServerComp T=MyWorkerThread | layeredHandlerServer.c workerThreadFunc() 88 | report event (2) Sep 24 07:44:23 simulation user.info TelAF: INFO | layeredHandlerServerProc[2758]/layeredHandlerServerComp T=main | layeredHandlerServer.c FirstLayerHandlerFunc() 29 | [SERVER] inside FirstLayerHandlerFunc, n1: 12, n2: 5020 Sep 24 07:44:23 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=WorkerThread | layeredHandlerClient.c secondLayerHandlerFunc() 13 | [CLIENT] inside secondLayerHandlerFunc Sep 24 07:44:23 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=WorkerThread | layeredHandlerClient.c secondLayerHandlerFunc() 16 | myHandlerParam1: 542, myHandlerParam2: 5020 Sep 24 07:44:23 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=WorkerThread | layeredHandlerClient.c secondLayerHandlerFunc() 17 | contextPtr: 0x14f809d19b89, *contextPtr: Hello from Client Sep 24 07:44:25 simulation user.info TelAF: INFO | layeredHandlerServerProc[2758]/layeredHandlerServerComp T=MyWorkerThread | layeredHandlerServer.c workerThreadFunc() 88 | report event (3) Sep 24 07:44:25 simulation user.info TelAF: INFO | layeredHandlerServerProc[2758]/layeredHandlerServerComp T=main | layeredHandlerServer.c FirstLayerHandlerFunc() 29 | [SERVER] inside FirstLayerHandlerFunc, n1: 22, n2: 5040 Sep 24 07:44:25 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=WorkerThread | layeredHandlerClient.c secondLayerHandlerFunc() 13 | [CLIENT] inside secondLayerHandlerFunc Sep 24 07:44:25 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=WorkerThread | layeredHandlerClient.c secondLayerHandlerFunc() 16 | myHandlerParam1: 582, myHandlerParam2: 5040 Sep 24 07:44:25 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=WorkerThread | layeredHandlerClient.c secondLayerHandlerFunc() 17 | contextPtr: 0x14f809d19b89, *contextPtr: Hello from Client Sep 24 07:44:27 simulation user.info TelAF: INFO | layeredHandlerServerProc[2758]/layeredHandlerServerComp T=MyWorkerThread | layeredHandlerServer.c workerThreadFunc() 88 | report event (4) Sep 24 07:44:27 simulation user.info TelAF: INFO | layeredHandlerServerProc[2758]/layeredHandlerServerComp T=main | layeredHandlerServer.c FirstLayerHandlerFunc() 29 | [SERVER] inside FirstLayerHandlerFunc, n1: 32, n2: 5060 Sep 24 07:44:27 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=WorkerThread | layeredHandlerClient.c secondLayerHandlerFunc() 13 | [CLIENT] inside secondLayerHandlerFunc Sep 24 07:44:27 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=WorkerThread | layeredHandlerClient.c secondLayerHandlerFunc() 16 | myHandlerParam1: 622, myHandlerParam2: 5060 Sep 24 07:44:27 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=WorkerThread | layeredHandlerClient.c secondLayerHandlerFunc() 17 | contextPtr: 0x14f809d19b89, *contextPtr: Hello from Client Sep 24 07:44:29 simulation user.info TelAF: INFO | layeredHandlerServerProc[2758]/layeredHandlerServerComp T=MyWorkerThread | layeredHandlerServer.c workerThreadFunc() 88 | report event (5) Sep 24 07:44:29 simulation user.info TelAF: INFO | layeredHandlerServerProc[2758]/layeredHandlerServerComp T=main | layeredHandlerServer.c FirstLayerHandlerFunc() 29 | [SERVER] inside FirstLayerHandlerFunc, n1: 42, n2: 5080 Sep 24 07:44:29 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=WorkerThread | layeredHandlerClient.c secondLayerHandlerFunc() 13 | [CLIENT] inside secondLayerHandlerFunc Sep 24 07:44:29 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=WorkerThread | layeredHandlerClient.c secondLayerHandlerFunc() 16 | myHandlerParam1: 662, myHandlerParam2: 5080 Sep 24 07:44:29 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=WorkerThread | layeredHandlerClient.c secondLayerHandlerFunc() 17 | contextPtr: 0x14f809d19b89, *contextPtr: Hello from Client Sep 24 07:44:29 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=WorkerThread | layeredHandlerClient.c workerThreadDestructor() 27 | [CLIENT] inside workerThreadDestructor Sep 24 07:44:29 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=WorkerThread | layeredHandlerClient.c workerThreadDestructor() 29 | [CLIENT] is going to unregister the second layered handler Sep 24 07:44:29 simulation user.info TelAF: INFO | layeredHandlerServerProc[2758]/layeredHandlerServerComp T=main | layeredHandlerServer.c serverInterface_RemoveMyEventHandler() 73 | [SERVER] inside serverInterface_RemoveMyEventHandler Sep 24 07:44:29 simulation user.info TelAF: INFO | layeredHandlerClientProc[2779]/layeredHandlerClientComp T=WorkerThread | layeredHandlerClient.c workerThreadDestructor() 31 | [CLIENT] just unregistered the second layered handler ```