Skip to content

Commit

Permalink
feature(discover): trigger debounced discovery on consul config change (
Browse files Browse the repository at this point in the history
#35)

#31

Signed-off-by: Anthony Casagrande <anthony.j.casagrande@intel.com>
  • Loading branch information
ajcasagrande authored Jun 16, 2021
1 parent 2cbcfcc commit 8772bf5
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 6 deletions.
20 changes: 14 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,18 @@ make docker
```

**Configure subnet information**
>_Note: This script requires EdgeX and device-rfid-llrp to be running first._
>_**Note 1:** This script requires EdgeX and device-rfid-llrp to be running first._

>_**Note 2:** This step is optional if you already configured the subnets beforehand in the configuration.toml file._
```bash
./bin/auto-configure.sh
```
**Trigger a device discovery**

*Note: make sure your LLRP devices are plugged in and powered on before this step*
>_**Note 1:** Make sure your LLRP devices are plugged in and powered on before this step_

>_**Note 2:** The system will trigger a discovery 10 seconds after any change is made to the subnet
> or discover port config, so this step is often not required_
```bash
curl -X POST http://localhost:49989/api/v1/discovery
```
Expand All @@ -58,8 +63,8 @@ For more detailed info, see [Device Discovery](#Device-Discovery) and
[EdgeX Device Naming](#EdgeX-Device-Naming).

## Device Discovery
*Note: Device discovery is currently only compatible with IPv4 networks.
If using an IPv6-only network, you will need to [manually add your devices to EdgeX](#manually-adding-a-device).*
>_**Note:** Device discovery is currently only compatible with IPv4 networks.
If using an IPv6-only network, you will need to [manually add your devices to EdgeX](#manually-adding-a-device)._

This service has the functionality to probe the local network
in an effort to discover devices that support LLRP.
Expand All @@ -70,7 +75,7 @@ for existing installations, and [configuration.toml][config_toml] for default va

The discovery configuration can be modified via the `[Driver]` section of the [configuration.toml][config_toml] file.

_Note: Please read the [Notes on configuration.toml](#Notes-on-configurationtoml) for things to be
>_**Note:** Please read the [Notes on configuration.toml](#Notes-on-configurationtoml) for things to be
aware of when modifying this file._

[consul_discovery]: http://localhost:8500/ui/dc1/kv/edgex/devices/1.0/edgex-device-rfid-llrp/Device/Discovery/
Expand Down Expand Up @@ -106,6 +111,9 @@ What this command does is check your local machine's network interfaces to see w
and a physical device (instead of virtual). It uses that information to fill in the `DiscoverySubnets`
field in Consul for you.
>_**Note:** Whenever a change to `DiscoverySubnets` or `ScanPort` is detected via a Consul watcher,
> a discovery is automatically triggered after a 10-second debounced delay._
Discovery can be manually triggered via REST:
```bash
# POST http://<hostname>:<device-rfid-llrp-go port>/api/v1/discovery
Expand Down Expand Up @@ -162,7 +170,7 @@ is converted into lowercase hexadecimal and used as the `<ID>`. Example: `LLRP-1
You can add devices directly via [EdgeX's APIs][add_device]
or via the [toml configuration][config_toml], as in the following example:
_Note: Please read the [Notes on configuration.toml](#Notes-on-configurationtoml) for things to be
>_Note: Please read the [Notes on configuration.toml](#Notes-on-configurationtoml) for things to be
aware of when modifying this file._
```
Expand Down
56 changes: 56 additions & 0 deletions internal/driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"net/url"
"os"
"path/filepath"
"reflect"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -59,6 +60,10 @@ const (

GenericDeviceProfile = "Device.LLRP.Profile"
ImpinjDeviceProfile = "Impinj.LLRP.Profile"

// discoverDebounceDuration is the amount of time to wait for additional changes to discover
// configuration before auto-triggering a discovery
discoverDebounceDuration = 10 * time.Second
)

var (
Expand Down Expand Up @@ -94,6 +99,10 @@ type Driver struct {
watchersMu sync.Mutex

svc ServiceWrapper

// debounceTimer and debounceMu keep track of when to fire a debounced discovery call
debounceTimer *time.Timer
debounceMu sync.Mutex
}

type MultiErr []error
Expand Down Expand Up @@ -238,8 +247,16 @@ func (d *Driver) watchForConfigChanges() error {
newCfg, ok := raw.(*driverConfiguration)
if ok {
d.configMu.Lock()
oldSubnets := d.config.DiscoverySubnets
oldScanPort := d.config.ScanPort
d.config = newCfg
d.configMu.Unlock()

if !reflect.DeepEqual(newCfg.DiscoverySubnets, oldSubnets) ||
newCfg.ScanPort != oldScanPort {
d.lc.Info("discover configuration has changed!")
d.debouncedDiscover()
}
} else {
d.lc.Warn("unable to decode incoming configuration from registry")
}
Expand Down Expand Up @@ -784,6 +801,45 @@ func (d *Driver) addProvisionWatchers() error {
return nil
}

// debouncedDiscover adds or updates a future call to Discover. This function is intended to be
// called by the config watcher in response to any configuration changes related to discovery.
// The reason Discover calls are being debounced is to allow the user to make multiple changes to
// their configuration, and only fire the discovery once.
//
// The way it works is that this code creates and starts a timer for discoverDebounceDuration.
// Every subsequent call to this function before that timer elapses resets the timer to
// discoverDebounceDuration. Once the timer finally elapses, the Discover function is called.
func (d *Driver) debouncedDiscover() {
d.lc.Debug(fmt.Sprintf("trigger debounced discovery in %v", discoverDebounceDuration))

// everything in this function is mutex-locked and is safe to access asynchronously
d.debounceMu.Lock()
defer d.debounceMu.Unlock()

if d.debounceTimer != nil {
// timer is already active, so reset it (debounce)
d.debounceTimer.Reset(discoverDebounceDuration)
} else {
// no timer is active, so create and start a new one
d.debounceTimer = time.NewTimer(discoverDebounceDuration)

// asynchronously listen for the timer to elapse. this go routine will only ever be run
// once due to mutex locking and the above if statement.
go func() {
// wait for timer to tick
<-d.debounceTimer.C

// remove timer. we must lock the mutex as this go routine runs separately from the
// outer function's locked scope
d.debounceMu.Lock()
d.debounceTimer = nil
d.debounceMu.Unlock()

d.Discover()
}()
}
}

// Discover performs a discovery of LLRP readers on the network and passes them to EdgeX to get provisioned
func (d *Driver) Discover() {
d.lc.Info("Discover was called.")
Expand Down

0 comments on commit 8772bf5

Please sign in to comment.