Legato Timer API. Can I create multiple handlers for a single timer?


#1

I have a number of things that need to occur with my user interface when a timer expires. For example, I need to turn on an LED, turn on a vibrator, create a data connection to a server, etc. Each of these things is in a separate component of my application. Can I create a single timer, stick its reference in a global data area, and have each of these components created handlers using that global reference? I tried it, and it didn’t seem to work, but maybe I was doing it incorrectly. The other more complicated approach would be to report an event when the timer expires, and have the other components subscribe to that event.


#2

Hi @BillD,

Since there is only one handler per timer with the current API (le_timer_SetHandler) it doesn’t seem like you can do directly what you’re looking for.

To distribute the event, you could have a component in your application that does some timer management.
Not sure what the context is, but lets call it AlarmComp.
This component could manage the calls to le_timer internally and expose a alarm handler which your other components can register to. It’s a bit more code within your application but eventually you get an added abstraction which prevents some global variable regarding the timer. Plus you can centralize all your timer configuration in that alarm component.


#3

I think there’s an alternate solution which involves writing less code, but is probably less efficient. I think you could create an event using the le_eventLoop.h API and call le_event_Report() when the timer fires. Then you have have each of your subscribers register handlers to the event that you created.


#4

Thanks guys. I was afraid of that. One handler per timer.

I was hoping that I could avoid creating an event and having the other components within the application subscribing to it. I seem to be having problems with that. I was able to get two components in different applications to share an event (publish/subscribe), but for some reason I keep getting “Missing bindings” errors when trying to get two components within the same application to share an event.

Do you know of any example Legato code demonstrating this?


#5

Could you post some info about what the declaration(s) looks like and what the error is?


#6

Sure. I can do that.

I have an application called “Main”. It determines that a button has been pressed, and then creates an event for other components and applications to subscribe to. These other components can be within Main (like vibrator) as well as in other applications. Its .adef file looks like this:

sandboxed: true
executables:
{
    mainApplication = ( mainComponent vibratorComponent)
}
processes:
{
    run:
    {
        ( mainApplication )
    }
}
bindings:
{   
    mainApplication.mainComponent.pushButtonGpio -> <root>.le_gpioPin13

    // Bind the buttonStateEventApi to the vibrator component of the Main Application
    // mainApplication.vibratorComponent.buttonStateEventApi -> mainApp.buttonStateEventApi  // Doesn't work
    mainApplication.vibratorComponent.buttonStateEventApi -> mainApplication.mainComponent.buttonStateEventApi  // Doesn't work      
}
extern:
{
    // This extern makes the API available to other applications
    mainApplication.mainComponent.buttonStateEventApi
}

The .cdef file for the mainComponent looks like this:

sources:
{
    main.c
}
requires:
{
    api:
    {
        pushButtonGpio = le_gpio.api
    }
}
provides:
{
    api:
    {
        // This app provides an API.  Need to provide its location.
        $WEARABLE/mainApplication/interfaces/buttonStateEventApi.api
    }
}

The .cdef file for the vibratorComponent looks like this:

sources:
{
    vibrator.c
}
requires:
{
    component:
    {
        mainComponent
    }
    api:
    {
        vibratorGpio = le_gpio.api
        
        // This app requires an API.  Need to provide its location.
        $WEARABLE/mainApplication/interfaces/buttonStateEventApi.api        
    }
}

I have the following in the INIT block of the vibrator.c file

//    LE_DEBUG("Create an event handler for button state events from the Main Application");
//    buttonStateEventApi_AddStateHandler(ButtonStateEventHandler, NULL);

And I have the following in the INIT block of the main.c file

LE_DEBUG("Create an event for button state events.");
buttonStateEventId = le_event_CreateId("Button State Event", sizeof(buttonStateEventPayload_t));

My main.c file also contains the API functions:
buttonStateEventApi_RemoveStateHandler
buttonStateEventApi_AddStateHandler
FirstLayerStateHandler

The errors that I am getting are: (Note that the “serverApplication” is another app that is subscribing to the button event. I have this part working fine as long as I do not add in the vibrator component also subscribing to the button event. Once I add in the vibrator, the server application breaks as well.)

Missing binding: mainApp.mainApplication.vibratorComponent.buttonStateEventApi -> mainApp.buttonStateEventApi
Missing binding: serverApp.serverApplication.serverComponent.buttonStateEventApi -> mainApp.buttonStateEventApi

#7

Hi BillD,

Just a thought, maybe you already know this, but you have to make sure the INIT block has exited for bindings to work, otherwise you’ll get a missing binding error.


#8

Hi mahtab

That’s a good tip. Thanks. I went back and checked all of my INIT blocks and none seem to have code that would prevent them from exiting. They all contain calls to create timers, events, and event handlers. Is there a preferred way of ensuring that the INIT blocks exit? For example, should I put a “return;” statement at the end?


#9

Hi,

nothing particular needed, but you can eventually add a LE_INFO to help you trace the init of your components in the logs.


#10

Problem solved.

In the .adef file, all of the components needed to be split out, like this:

executables:
{
   main = ( mainComponent )
   button = ( buttonComponent )
   vibrator = ( vibratorComponent )
   timer = ( timerComponent )
}
processes:
{
   run:
   {
      ( main )
      ( button )
      ( vibrator )
      ( timer )
   }
}

I previously had them all grouped together

executables:
{
   mainApplication = ( mainComponent buttonComponent timerComponent vibratorComponent) 
}
processes:
{
   run:
   {
      ( mainApplication )
   }
}

#11

Great news.

Here are a couple of related tips:

  1. If your client and server are both in the same executable, you can run into problems where the client and server are sharing a thread, which results in a deadlock. The client blocks, waiting for the server to respond, but the server never runs because the thread it shares with the client was blocked by the client. We plan to have the IPC code automatically detect this and handle it gracefully, but that hasn’t been implemented yet. To work around this, you can either
  • run the client and server in different threads (e.g., mark the server-side interface “[manual-start]” and then have the server COMPONENT_INIT spawn a thread that then calls your service’s “xxxx_AdvertiseService()” function), or

  • run the client and server in different processes (which it looks like you have done).

  1. Because your vibratorComponent “requires” your mainComponent, you don’t have to include the mainComponent in the list of components in your executable in your .adef. When you add the vibratorComponent to the executable, the mainComponent will automatically get pulled in because it’s required by the vibratorComponent. But, it doesn’t hurt to explicitly add the mainComponent to your executable too. It’s really great that you’re telling the tools that your vibratorComponent depends on your mainComponent, though. That’s very important because it ensures that your mainComponent’s COMPONENT_INIT will get run before your vibratorComponent’s COMPONENT_INIT.

#12

Thanks for the tips jchitty Such a steep learning curve on Legato.


#13

Yeah, the APIs are really built to go between processes, but we are looking at improvements to make them more flexible for situations where you want to call a function that is served by the same process. That should make situations like this work more smoothly and with less nasty surprises (and more efficiently).

I think a graphical binding tool would help people visualize what’s going on too.

If there are other areas that you found difficult, I’d like to hear about them too.

Cheers,

–Jen


#14

“If there are other areas that you found difficult, I’d like to hear about them too.”

Thanks for the offer, but there are too many to list. There is something new and confusing each week. Right now we are hung up on audio. I have a separate question open about that on the mangOH forum.