http://qs1969.pair.com?node_id=11130680

stevieb has asked for the wisdom of the Perl Monks concerning the following question:

Greetings and good day my fellow esteemed Monks.

I come today with a Dancer2 and simultaneous asynchronous request question(s). I have a microcontroller (Wemos D1 mini with an ESP8266 chip) in my garage that presents data about my Tesla vehicle state on an OLED screen and a strip of LEDs. Here's how it works:

What I want to have happen, is something like this:

Essentially, I want the Pi to asynchronously request data from Tesla's API about my vehicle, respond with the last (global) poll data before the current (Tesla API) request is done (effectively terminating the HTTP connection), then update the global variable that contains the vehicle data when the async Tesla API call is complete for the next microcontroller call.

How can I respond back to the microcontroller with previous data before the current Tesla API call is completed?

Also, how can I ensure that if the first async Tesla API call is done before spawning off a second one (ie. if two seconds have elapsed and a new microcontroller check comes in and the async process isn't finished from last time), respond with the existing data without respawning a new async process?

  • Comment on Dancer2: respond to client while making its own async call

Replies are listed 'Best First'.
Re: Dancer2: respond to client while making its own async call
by 1nickt (Canon) on Apr 01, 2021 at 21:10 UTC
Re: Dancer2: respond to client while making its own async call
by stevieb (Canon) on Apr 01, 2021 at 19:33 UTC

    I figured it out, using one of my own CPAN distributions no less. I updated my Async::Event::Interval software to accept an "interval" of zero, which instead of looping every N seconds, simply runs a single time. So when the Dancer2 route is called, it'll spin off an external event to fetch and update the data, and then I can return existing data if it exists immediately while the background task is still working.

    The software also already has a built-in check to see if the event is still running or not. Here's a minimal demo:

    use warnings; use strict; use Async::Event::Interval; use IPC::Shareable; my $data; tie $data, 'IPC::Shareable', 'TSLA', {create => 1, destroy => 1}; $data = '{"charge": 85}'; my $event = Async::Event::Interval->new(0, sub {$data = '{"charge": 10 +0}'}); print "$data\n"; $event->start; print "Main program continues...\n"; print "Background event still running...\n" if $event->status; sleep 1; print $event->status == -1 ? "Event stopped\n" : "Event running\n"; print "$data\n";

    Output:

    {"charge": 85} Main program continues... Background event still running... Event stopped {"charge": 100}

    Since the Dancer2 app is always running, I just make the event itself along with the data string (JSON) global, and use IPC::Shareable (which I have CO-MAINT to) to share the data between the processes. The Dancer2 route just calls $event->start if $event->status != -1;, and can then return back to the calling microcontroller immediately while the updater event runs as a separate proc.