Publish/Subscribe Events - to what extent can they be used


#1

Hello.

First time poster here and new to Legato, and embedded Linux as well.

I’m trying to learn about publish/subscribe events, so I’m creating some small apps to test the API. I have created a simple app with one component and an associated C file, and I was able to create an event ID, report an event, and have a handler process the report. Now I would like to extend that, if possible, to have an app with 2 components. Is it possible to have one component create the event ID and report an event which the second component handles? I have tried to do this, but I keep getting the following runtime error:

Jun 20 16:06:13 | eventTestApp1[31471]/framework T=main | LE_FILENAME le_event_AddLayeredHandler() 1100 | Assert Failed: ‘eventPtr != NULL’

Which seems to indicate that the pointer to the event ID is NULL. This error occurs in the second component when I try to create the handler.

My guess is that somehow I’ve not made my event ID visible to the second component. My event ID is defined in a header file that is included into both components’ C-file. Is there some additional verbage required in the .adef or .cdef files so that the 2 components can share this event ID?

I’d like to then extend this more by having 2 applications share an event ID. For example, application 1 creates an ID and reports an event. Application 2 handles the event. I’m less convinced that this is even possible.

I haven’t yet learned how to use the Developer Studio debugger, so that is hampering my efforts to figure out what is going wrong.

Thanks in advance for any help.


#2

Can you attach your code? Is your second component part of the same executable, or a different one?

If you want to use events between different executables or apps you need to declare an EVENT in a .api file (as described at http://legato.io/legato-docs/latest/apiFilesSyntax.html#apiFilesSyntax_event). Then in the process which generates the event, you need to implement the Add/Remove functions – and you can use le_event_AddLayeredHandler to implement these.


#3

Thanks for the response kdunwoody.

I’ll have to admit that I read the paragraphs that you provided a link for at least 3 times and I still cannot make heads nor tails of what it means. Perhaps an example would help me understand it better. Being new to Legato, I have a feeling that I am going about this the wrong way and am totally off base.

Unfortunately, this forum will not let new users upload code files, so I’ll have to paste my simplified code here. Please excuse the formatting mess. Hopefully it is understandable.

Structure:

eventTestApp
—Component1
------component1.c
------component1.h
------Component.cdef
—Component2
------component2.c
------Component.cdef
—eventTestApp.adef

—component1.h-------
ifndef EVENT_TEST_H
define EVENT_TEST_H

include "legato.h"
include “interfaces.h”

ifdef __cplusplus
extern “C” {
endif

typedef enum {
ZERO,
ONE,
TWO
} EventReport_t;

extern le_event_Id_t eventId;

endif // EVENT_TEST_H

----component1.c------
include "component1.h"
include "legato.h"
include “interfaces.h”

static le_timer_Ref_t timerRef;
le_event_Id_t eventId = NULL;

//-------------------------------------------------------------------------------------------------
/**

  • Callback function for event handler
    */
    // -------------------------------------------------------------------------------------------------

static void Component1EventHandler(void* reportPayloadPtr)
{
EventReport_t testReportPtr = reportPayloadPtr;
LE_INFO(“Event received by event handler: %d”, testReportPtr);
}
//-------------------------------------------------------------------------------------------------
/

  • Callback function for timer
    */
    // -------------------------------------------------------------------------------------------------
    static void TimerCallback(le_timer_Ref_t timerRef)
    {
    EventReport_t eventReport = ONE;
    le_event_Report(eventId, &eventReport, sizeof(eventReport));
    }

COMPONENT_INIT
{
le_clk_Time_t interval;

eventId = le_event_CreateId("Test Event", sizeof(EventReport_t));
le_event_AddHandler("Handler Name", eventId, Component1EventHandler);

interval.sec  = 10;  // Create a 10 sec repeating timer with a handler
interval.usec = 0;
timerRef = le_timer_Create("Test Timer");
LE_ASSERT(le_timer_SetInterval(timerRef, interval) == LE_OK);
LE_ASSERT(le_timer_SetRepeat(timerRef, 0) == LE_OK); // A "0" implies "repeat forever"
LE_ASSERT(le_timer_SetContextPtr(timerRef, NULL) == LE_OK);
LE_ASSERT(le_timer_SetHandler(timerRef, TimerCallback) == LE_OK);

LE_INFO("Start timer");
LE_ASSERT(le_timer_Start(timerRef) == LE_OK);

return;

}

----component2.c
include "legato.h"
include "…/Component1/component1.h"
include “interfaces.h”

//-------------------------------------------------------------------------------------------------
/**

  • Callback function for event handler
    */
    // -------------------------------------------------------------------------------------------------

static void Component2EventHandler(void* reportPayloadPtr)
{
EventReport_t *testReportPtr = reportPayloadPtr;
LE_INFO(“Event received by event handler: %d”, *testReportPtr);
}

COMPONENT_INIT
{
le_event_AddHandler(“Handler Name”, eventId, Component2EventHandler);
return;
}

----Component.cdef (for component 1)

sources:
{
component1.c
}

-----Component.cdef (for component 2)
sources:
{
component2.c
}
requires:
{
component:
{
component1
}
}


#4

I need to investigate a bit more what’s happening with your code. On the surface it looks to me like it should work.

For an example of inter-process events, try taking a look at what the voiceCallService component is doing with the State event.

First the event is declared in le_voicecall.api as:

EVENT State
(
     StateHandler handler
);

then in components/voiceCallService/voiceCallServiceServer.c there’s an implementation of le_voicecall_AddStateHandler and le_voicecall_RemoveStateHandler which call le_event_AddLayeredHandler()/le_event_RemoveHandler() respectively to manage the handler. When another process wants to register a state handler, it will include

requires:
{
    api:
    {
        le_voicecall.api
    }
}

and then it can call le_voicecall_AddStateHandler to register a state change handler. The Legato framework will generate the appropriate stub code to do the IPC so the function (actually a stub) is passed to le_voicecall_AddStateHandler() in the voiceCallServer component. Then when the stub is called it triggers a message back which causes the handler to be called in the original process.


#5

Your sample works for me with a few small changes:

in component1.c I changed:

le_event_Id_t eventId = NULL;

to

LE_SHARED le_event_Id_t eventId = NULL;

I get an undefined symbol on eventId otherwise.

For my .adef I used:

executables:
{
    eventTest = ( component2 )
}

After installing, I then ran the process using:

app runProc eventTestApp --exe=eventTest

I also changed the order of the includes to always place:

#include "legato.h"
#include "interfaces.h"

first, although I don’t think this matters.

I’m using the latest Legato from git.


#6

Thanks kdunwoody! This worked!

My runtime error identified in my original post was my own “C” programming error; I had defined the event ID to be global incorrectly. Once I fixed that problem, I was getting an “undefined reference to ‘variable’” error. That was solved by adding the LE_SHARED keyword that you provided. I would have never figured out that one. The Legato documentation for this macro is very sparse. Thank you for that! I did not need to make the change to my .adef file or my includes order, and I simply ran the app from inside the device tab of Developer Studio.


#7

Missing Bindings!!!

I’ve spent the last day trying to create an example of 2 applications sharing an event, using the le_voiceCall service as an example, per kdunwoody’s recommendation above. Most of that time has been spent chasing “missing bindings” errors at runtime.

I have the following structure:

eventTestApp2
   Component3
      component3.c
      Component.cdef
   interfaces
      eventTest.api
   eventTestApp2.adef

eventTestApp3
   Component4
      component4.c
      Component.cdef
   eventTestApp3.adef

My components’ .cdef files look like this:


sources:
{
	component3.c
}
provides:
{
    api:
    {
        $TESTAPPS/eventTestApp2/interfaces/eventTest.api
    }
}

sources:
{
	component4.c
}
requires:
{
    api:
    {
        $TESTAPPS/eventTestApp2/interfaces/eventTest.api
    }
}

My applications’ .adef files look like this:


sandboxed: true
version: 1.0.0
executables:
{
	eventTestApp2 = ( Component3 )
	eventTestApp3 = ( Component4 )
}
processes:
{
	run:
	{
		( eventTestApp2 )
	}
}
bindings:
{
    eventTestApp3.Component4.eventTest -> eventTestApp2.Component3.eventTest
}

sandboxed: true
version: 1.0.0
executables:
{
	eventTestApp3 = ( Component4 )
	eventTestApp2 = ( Component3 )
}
processes:
{
	run:
	{
		( eventTestApp3 )
	}
}
bindings:
{
    eventTestApp3.Component4.eventTest -> eventTestApp2.Component3.eventTest
}

I’ve tried many different variations of this, but all still give the “missing bindings” error at runtime.

Process eventTestApp3/eventTestApp3 has 1 missing bindings

Missing binding: eventTestApp3.eventTestApp3.Component3.eventTest -> eventTestApp3.eventTestApp2.Component3.eventTest

The output of the ‘sdir list’ command shows the api provider running but the api requirer waiting.

eventTestApp2.eventTestApp2.Component3.eventTest (protocol ID = ‘75773f5d6f0b188c7f47cb1c08b8ac69’, max message size = 1104 bytes)

WAITING CLIENTS

[pid 7135] eventTestApp3.eventTestApp3.Component4.eventTest WAITING for eventTestApp3.eventTestApp2.Component3.eventTest (protocol ID = ‘75773f5d6f0b188c7f47cb1c08b8ac69’)

I’ve read the Legato documentation over and over and have tried many different things, but nothing works. Does anyone have any suggestions for what I am missing?


#8

Hi @BillD,

Have a look at declaring IPC availability and also read up on the extern section for .adef files.

The app which provides the API must also declare that same IPC API as available for binding to IPC API interfaces of other apps.

Try adding an extern section to eventTestApp2.adef:

extern:
{
    eventTestApp2.Component3.eventTest
}

Raf


#9

Thanks for the suggestion Raf.

I have read the IPC section (many times, in fact) and have also tried adding extern. Perhaps I did it incorrectly. I will try adding extern again, as you suggest, and hopefully that will solve my problem.


#10

I finally got rid of the missing bindings error. I did the following:

Removed from the executables section of eventTestApp2 .adef

eventTestApp3 = ( Component4 )

Removed bindings from eventTestApp2 .adef

bindings:
{
eventTestApp3.Component4.eventTest -> eventTestApp2.Component3.eventTest
}

Added extern to eventTestApp2 .adef

extern:
{
eventTestApp2.Component3.eventTest
}

Removed from the executables section of eventTestApp3 .adef

eventTestApp2 = ( Component3 )

Unfortunately, even though the bindings are sorted out, my eventTestApp3 application crashes when it receives an event.:

Jun 26 12:50:58 | eventTestApp2[16141]/framework T=main | eventTest_server.c AsyncResponse_eventTest_AddStateHandler() 402 | Sending message to client session 0x27dcc : 50 bytes sent
Jun 26 12:50:58 | eventTestApp2[16141]/framework T=main | eventTest_server.c CleanupClientData() 195 | Client 0x27dcc is closed !!!
Jun 26 12:50:58 | eventTestApp2[16141]/framework T=main | eventTest_server.c CleanupClientData() 221 | Found session ref 0x27dcc; match found, so needs cleanup
Jun 26 12:50:58 | supervisor[15648]/supervisor T=main | proc.c proc_SigChildHandler() 1956 | Process ‘eventTestApp3’ (PID: 16218) has exited due to signal 11 (Segmentation fault).
Jun 26 12:50:58 | supervisor[15648]/supervisor T=main | proc.c GetFaultAction() 1744 | No fault action specified for process ‘eventTestApp3’. Assuming ‘ignore’.
Jun 26 12:50:58 | supervisor[15648]/supervisor T=main | app.c app_SigChildHandler() 3221 | Process ‘eventTestApp3’ in app ‘eventTestApp3’ faulted: Ignored.
Jun 26 12:50:58 | _appStopClient[16284]/framework T=main | LE_FILENAME CreateSocket() 550 | Socket opened as standard i/o file descriptor 2!
Jun 26 12:50:58 | supervisor[15648]/supervisor T=main | apps.c DeactivateAppContainer() 340 | Application ‘eventTestApp3’ has stopped.

I can’t yet determine if the app had crashed prior to its handler receiving the event, or if its handler receiving the event is what causes the crash. I’ll work on that next.