From 1721b05ed756c89625a9d2d7b5246915485f6a37 Mon Sep 17 00:00:00 2001 From: Balazs Racz Date: Tue, 29 Sep 2020 20:49:08 +0200 Subject: [PATCH] Adds activity LED support. (#438) * Adds activity LED support. Adds a hook into the openlcb interface that calls a user supplied function whenever a message is transmitted to the network. Adds a simple implementaiton for this hook that flashes an LED when there is bus activity originated from this node. * Adds comments and parametrizes the period of activity LED. --- src/openlcb/If.hxx | 21 ++++++++- src/openlcb/SimpleStack.hxx | 12 +++++ src/utils/ActivityLed.hxx | 88 +++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 src/utils/ActivityLed.hxx diff --git a/src/openlcb/If.hxx b/src/openlcb/If.hxx index 4dd3ee4fa..c0b82c516 100644 --- a/src/openlcb/If.hxx +++ b/src/openlcb/If.hxx @@ -282,12 +282,20 @@ public: MessageHandler *global_message_write_flow() { HASSERT(globalWriteFlow_); + if (txHook_) + { + txHook_(); + } return globalWriteFlow_; } /** @return Flow to send addressed messages to the NMRAnet bus. */ MessageHandler *addressed_message_write_flow() { HASSERT(addressedWriteFlow_); + if (txHook_) + { + txHook_(); + } return addressedWriteFlow_; } @@ -399,7 +407,15 @@ public: * the interface holds internally. Noop for TCP interface. Must be called * on the interface executor. */ virtual void canonicalize_handle(NodeHandle *h) {} - + + /// Sets a transmit hook. This function will be called once for every + /// OpenLCB message transmitted. Used for implementing activity LEDs. + /// @param hook function to call for each transmit message. + void set_tx_hook(std::function hook) + { + txHook_ = std::move(hook); + } + protected: void remove_local_node_from_map(Node *node) { auto it = localNodes_.find(node->node_id()); @@ -416,6 +432,9 @@ private: /// Flow responsible for routing incoming messages to handlers. MessageDispatchFlow dispatcher_; + /// This function is pinged every time a message is transmitted. + std::function txHook_; + typedef Map VNodeMap; /// Local virtual nodes registered on this interface. diff --git a/src/openlcb/SimpleStack.hxx b/src/openlcb/SimpleStack.hxx index 5adb6a2b5..6a5c67f2d 100644 --- a/src/openlcb/SimpleStack.hxx +++ b/src/openlcb/SimpleStack.hxx @@ -54,6 +54,7 @@ #include "openlcb/SimpleNodeInfo.hxx" #include "openlcb/TractionTrain.hxx" #include "openlcb/TrainInterface.hxx" +#include "utils/ActivityLed.hxx" #include "utils/GcTcpHub.hxx" #include "utils/GridConnectHub.hxx" #include "utils/HubDevice.hxx" @@ -153,6 +154,17 @@ public: return &configUpdateFlow_; } + /// Adds an activiy LED which will be flashed every time a message is sent + /// from this node to the network. + /// @param gpio LED that will be flashed on for each packet. + /// @param period defines in nanosecond the time to spend between updates. + void set_tx_activity_led( + const Gpio *led, long long period = MSEC_TO_NSEC(33)) + { + auto *al = new ActivityLed(iface(), led, period); + iface()->set_tx_hook(std::bind(&ActivityLed::activity, al)); + } + /// Reinitializes the node. Useful to call after the connection has flapped /// (gone down and up). void restart_stack(); diff --git a/src/utils/ActivityLed.hxx b/src/utils/ActivityLed.hxx new file mode 100644 index 000000000..0e78cd145 --- /dev/null +++ b/src/utils/ActivityLed.hxx @@ -0,0 +1,88 @@ +/** \copyright + * Copyright (c) 2020, Balazs Racz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \file ActivityLed.hxx + * + * State flow that controls an activity LED based on triggers of events. + * + * @author Balazs Racz + * @date 26 Sep 2020 + */ + +#ifndef _UTILS_ACTIVITYLED_HXX_ +#define _UTILS_ACTIVITYLED_HXX_ + +#include "executor/StateFlow.hxx" +#include "os/Gpio.hxx" + +/// Operates an LED to visually display some activity. When the activity() +/// function is called at least once within a period, we turn the LED on for +/// the next period, if no call was made, the LED turns off for the next +/// period. +class ActivityLed : private ::Timer +{ +public: + /// Constructor. + /// @param service defines which executor this timer should be running on. + /// @param pin the LED of the output. Will be high for activity, low for + /// inactivity. Use InvertedGPIO if needed. + /// @param period defines in nanosecond the time to spend between updates. + ActivityLed( + Service *service, const Gpio *pin, long long period = MSEC_TO_NSEC(33)) + : ::Timer(service->executor()->active_timers()) + , gpio_(pin) + { + start(period); + } + + /// Call this function when activity happens. + void activity() + { + triggerCount_++; + } + +private: + long long timeout() override + { + if (triggerCount_) + { + gpio_->write(true); + triggerCount_ = 0; + } + else + { + gpio_->write(false); + } + return RESTART; + } + + /// Output pin to blink for activity. + const Gpio *gpio_; + /// How many triggers happened since the last run of the timer. + unsigned triggerCount_ {0}; +}; + +#endif // _UTILS_ACTIVITYLED_HXX_