Code Monkey home page Code Monkey logo

device-rfid-llrp-go's Introduction

Device RFID LLRP Go

Build Status Code Coverage Go Report Card GitHub Latest Dev Tag) GitHub Latest Stable Tag) GitHub License GitHub go.mod Go version GitHub Pull Requests GitHub Contributors GitHub Committers GitHub Commit Activity

Warning
The main branch of this repository contains work-in-progress development code for the upcoming release, and is not guaranteed to be stable or working. It is only compatible with the main branch of edgex-compose which uses the Docker images built from the main branch of this repo and other repos.

The source for the latest release can be found at Releases.

EdgeX device service for communicating with LLRP-based RFID readers. This service provides the capabilities to configure and enable LLRP-based RFID readers to generate asynchronous EdgeX readings that contain LLRP ROAccessReport and Reader messages. A ROAccessReport can be used to examine data read from one or more RFID tags seen by the reader over a given time period, and a ReaderEventNotifications message can be used for data regarding device connection changes, such as attempted connections, connections closed, and errors in a connection.

The LLRP RFID Inventory Service can be used to automatically configure this service and readers it manages. This repository also provides a higher-level abstraction for working with RFID tag by parsing ROAccessReports and generating higher-level tag-specific readings (e.g. TAG_APPEARED, TAG_MOVED, etc).

Table of contents

First Run

Build Native

make build

Build Docker

make docker

Run EdgeX Jakarta

Run device-rfid-llrp

  • Docker

    • Use compose-builder
    • For non secure mode make gen ds-llrp no-secty
    • For secure mode make gen ds-llrp
    • docker-compose -p edgex up -d
  • Native

    cd cmd && EDGEX_SECURITY_SECRET_STORE=false ./device-rfid-llrp-go -cp -r

Configure subnet information

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.yaml file.

./bin/auto-configure.sh

Trigger a device discovery

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

curl -X POST http://localhost:59989/api/v2/discovery

At this point the device-rfid-llrp service should have discovered your LLRP devices on the network and registered them with EdgeX.

For more detailed info, see Device Discovery and 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.

This service has the functionality to probe the local network in an effort to discover devices that support LLRP.

This discovery also happens at a regular interval and can be configured via EdgeX Consul for existing installations, and configuration.yaml for default values.

The additional discovery configuration can be modified via the [AppCustom] section of the configuration.yaml file.

Note: Please read the Notes on configuration.yaml for things to be aware of when modifying this file.

AppCustom:
    # List of IPv4 subnets to perform LLRP discovery process on, in CIDR format (X.X.X.X/Y)
    # separated by commas ex: "192.168.1.0/24,10.0.0.0/24"
    DiscoverySubnets: ""

    # Maximum simultaneous network probes
    ProbeAsyncLimit: 4000  

    # Maximum amount of seconds to wait for each IP probe before timing out.
    # This will also be the minimum time the discovery process can take.
    ProbeTimeoutSeconds: 2

    # Port to scan for LLRP devices on
    ScanPort: "5084"

    # Maximum amount of seconds the discovery process is allowed to run before it will be cancelled.
    # It is especially important to have this configured in the case of larger subnets such as /16 and /8
    MaxDiscoverDurationSeconds: 300

The DiscoverySubnets config option defaults to blank, and needs to be provided before a discovery can occur. The easiest way of doing this is via the following script:

./bin/auto-configure.sh

This script requires access to Consul. If running with security enabled, then a Consul token is required. In that case it should be passed as an argument to the script, i.e:

./bin/auto-configure.sh a7910d82-69ae-ea21-214d-fd1326e68545

What this command does is check your local machine's network interfaces to see which ones are both online 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:

# POST http://<hostname>:<device-rfid-llrp-go port>/api/v2/discovery
curl -X POST http://localhost:59989/api/v2/discovery

Every IP address in each of the subnets provided in DiscoverySubnets are probed at the specified ScanPort (default 5084). If a device returns LLRP response messages, a new EdgeX device is created.

EdgeX Device Naming

EdgeX device names are generated from information it receives from the LLRP device. In the case of Impinj readers, this device name should match the device's hostname given by Impinj, however the hostname information is not available through LLRP, so the generated name may differ in certain edge cases.

The device names are generated using the following naming format:

<Prefix>-<ID>

<Prefix> is generated based on the Vendor and Model of the LLRP device. If the device is a model with a known naming scheme such as most Impinj readers, the prefix will be set accordingly, otherwise it will default to LLRP.

<ID> field is based on the LLRP value GetReaderConfigResponse.Identification.ReaderID and can be one of two things.

If the LLRP device returns a MAC address (ID_MAC_EUI64) for the GetReaderConfigResponse.Identification.IDType field, the last 3 octets of the mac address will be used in the following format: XX-XX-XX. So given the following MAC address 00:ef:16:19:fe:16, the <ID> portion of the device name would be 19-FE-16.

If the LLRP device returns an EPC (ID_EPC) for the GetReaderConfigResponse.Identification.IDType field, the entire value of the GetReaderConfigResponse.Identification.ReaderID field is converted into lowercase hexadecimal and used as the <ID>. Example: LLRP-12fec5432453df3ac

Example Device Names by Model

MAC based
  • Impinj Speedway R120, R220, R420, R700 and xPortal:
    • SpeedwayR-19-FE-16
  • Impinj xSpan:
    • xSpan-19-FE-16
  • Impinj xArray, xArray EAP and xArray WM:
    • xArray-19-FE-16
  • Other Vendors and Unknown Models
    • LLRP-19-FE-16
EPC based
  • Other Vendors and Unknown Models
    • LLRP-12fec5432453df3ac

Manually Adding a Device

You can add devices directly via EdgeX's APIs or via the yaml configuration, as in the following example:

Note: Please read the Notes on configuration.yaml for things to be aware of when modifying this file.

DeviceList:
  Name: "Speedway"
  Profile: "Device-LLRP-Profile"
  Description: "LLRP RFID Reader"
  Labels: ["LLRP", "RFID"]
  Protocols:
    tcp:
      host: "192.168.86.88"
      port: "5084"

Device Profiles, Custom LLRP Messages, and Service Limitations

For some use cases, you may want or need to supply your own deviceProfile, but most LLRP operations are available via the included profile. The section below details how deviceResources and deviceCommands are mapped to LLRP Messages and Parameters, but first, here's what you can and can't do with the default profile:

  • Get the Reader's Capabilities.
  • Get the Reader's Configuration.
  • Set the Reader's Configuration, including custom parameters.
  • Add ROSpecs and AccessSpecs, including custom parameters.
  • Get the current collection of ROSpecs or AccessSpecs.
  • Enable, Start, Stop, Disable, and Delete ROSpecs.
  • Enable, Disable, and Delete AccessSpecs.
  • Receive ROAccessReports and ReaderEventNotifications (the service always sends reports and notifications to EdgeX automatically).

If a Reader returns a response with an LLRPStatusCode other than Success (including ERROR_MESSAGE, Message Type 100), then the service decodes any contained ParameterErrors or FieldErrors and returns them as an error with the LLRPStatus's ErrorDescription.

The only LLRP Message that you can send with a custom profile but can't send with the default profile is the CustomMessage (Message Type 1023). As noted above, you can send CustomParameters (Parameter Type 1023) using the default profile when writing Configuration or ROSpecs/AccessSpecs. Other than that, you may find it useful to create a custom profile to bundle multiple read requests, supplying default, or mapping special names to ROSpecs.

The following LLRP operations are not supported at this time:

  • When requesting the Capabilities or Configuration, it is not possible to specify the RequestedData field nor to append CustomParameters in the request; this service always requests All data from the Reader.
  • There isn't a way to send GetReport (Message Type 60), which means you should not configure ROReportSpecs with a NULL trigger.
  • There isn't a way to send EnableEventsAndReports (Message Type 64), so you should not set EventsAndReports to true in the ReaderConfiguration.
  • The service handles connection management and version negotiation, so you cannot explicitly send any of these:
    • CloseConnection (Message Type 14)
    • KeepAliveAck (Message Type 72)
    • GetSupportedVersion (Message Type 46)
    • SetProtocolVersion (Message Type 47)
  • It's not possible to send ClientRequestOpResponse, so it's not useful to configure a ClientRequestOpSpec, though we don't explicitly prevent you from doing so. There's really no reasonable general-purpose way to support it except through code, so if you need it, consider forking this repo and adding the interaction by using our LLRP Library.

A ProvisionWatcher is used during the device discovery process to match discovered readers. It also determines the device profile to be used when adding the new device. The two pre-defined watchers are a generic one, and one for Impinj. Any new provision watchers added to the same directory will be auto-imported on service startup.

Data Format

EdgeX requires that deviceResources are representable as a basic type, a homogeneous array of a basic type, or a CBOR "binary" type. Because LLRP Messages and Parameters are highly structured, this device service maps Specs, Configuration, Capabilities, and Notifications to and from JSON-encoded string using Go's json package and the structures defined in our LLRP Library.

When a Parameter or Message includes arbitrary data (e.g., the contents of a tag's EPC memory bank or a CustomParameter payload), they're represented in Go as a []byte, which Go marshals and unmarshals as strings representing the base64-encoded data.

For requests to read a deviceResource (i.e., a GET request), the service determines which LLRP message to send based upon the resource name. It marshals the result to JSON and returns it as a string EdgeX Reading. LLRP constants are encoded according to the LLRP spec (e.g., the StopTriggerType of an AISpec is returned as 0, 1, or 2). The service uses only the resource name and ignores any attributes it may have; custom LLRP parameter extensions are not supported for resources read requests, nor is the LLRP CustomMessage (Message Type 1023).

The following list details the resource names the service recognizes and how it satisfies the read request:

  • ReaderCapabilities sends GET_READER_CAPABILITIES (Message Type 1) with RequestedData: All. It returns the resulting GET_READER_CAPABILITIES_RESPONSE (Message Type 12).
  • ReaderConfig sends GET_READER_CONFIG (Message Type 2) with RequestedData: All, and AntennaID, GPIPort, and GPOPort set to 0. It returns the resulting GET_READER_CONFIG_RESPONSE (Message Type 12).
  • ROSpec sends GET_ROSPECS (Message Type 26) and returns GET_ROSPECS_RESPONSE (Message Type 36).
  • AccessSpec sends GET_ACCESSSPECS (Message Type 44) and returns GET_ACCESSSPECS_RESPONSE (Message Type 44).

You can configure deviceCommands in your device profile to read more than one resource at a time, in which case the device service attempts each of the commands requests in the order specified by the device profile and returns the results of all of them.

For LLRP messages that require only an ROSpecID or AccessSpecID, we define them as operations upon uint32 deviceResources with those names. (note that EdgeX requires passing these as strings when calling deviceCommands) To disambiguate the desired write operation, deviceCommands that use them must specify them as the first resource to set, and must include a "pseudo-resource" called Action, a string which must be one of "Enable", "Disable", "Start", "Stop", or "Delete". Note that it is not possible in LLRP to start or stop an AccessSpec, so those only apply to ROSpecs. Because we must use this pseudo-resource to know what Action to take, It is not possible to write more than one deviceResource at a time.

To add an ROSpec or AccessSpec, or to set the ReaderConfig, you can use deviceCommands that write a deviceResource of the same name When you PUT a new instance of these resource types, the service attempts to unmarshal the resource's parameter string according to its name into the appropriate LLRP message structure defined in our LLRP library.

Unlike read requests, the service handles write requests on deviceResources with names other than those defined above. It assumes these resources are accessible via CustomMessage (Message Type 1023), and looks for vendor and subtype attributes on the resource, which it inserts into the relevant fields of the LLRP message. Although in LLRP these are a uint32 and uint8 (respectively), note that EdgeX requires all attributes values are passed as strings. Assuming these are present, the service interprets the parameter string as a base64-encoded byte array, which it uses as the payload of the CustomMessage.

You can see an example device profile that defines a deviceResource to enable Impinj's custom extensions.

Connection Management

After an LLRP device is added, either via discovery or directly through EdgeX, the driver works to maintain a connection to it and monitor its health. When it detects an unhealthy connection, it closes it and redials the Reader. If it fails to connect two times consecutively, it sets the device's OperationState to DISABLED, but continues to attempt to restore the connection indefinitely. It'll reattempt the connection using exponential backoff with jitter, capped to a max of 30 mins between attempts. If its IP address changes (either manually or via Discovery), the service attempts to connect to it at the new address.

The device service sets the device to DISABLED in EdgeX as soon as it thinks it's disabled, but exactly how long this takes depends on the conditions leading to failure. Nevertheless, a disconnected device should appear DISABLED within about 2 minutes.

The device service sets a 60s timeout when reading from OS's TCP connection. To ensures that a healthy connection will not timeout, it configures Readers to send KeepAlive messages every 30s. Because it uses this to monitor the connection health, it overrides the KeepAliveSpec in SetReaderConfig requests with its own.

These timeout values are not configurable, but they are easy to change when building the service by changing this code.

Example Scripts

There are a couple of example scripts here to interact with devices through EdgeX's APIs. They aren't meant to be perfect or necessarily the best way to do things, but they should help give examples of what's possible. They don't do much error handling, so don't rely on them for much more than happy-path testing.

  • command: interacts with the commands service to get/set LLRP configs and such.
  • data: interacts with the data service to view reports and the like.
  • read tags example: runs a "full" example -- sends/enables ROSpec to the first reader that EdgeX knows about, waits a bit, disables/deletes it from the reader, then displays any collected tags.

They assume everything is running and expect you have a these on your path: jq, curl, sed, xargs, base64, and od. By default, they all try to connect to localhost on the typical EdgeX ports. command.sh and data.sh take args/options; use --help to see their usage. example.sh uses a couple of variables defined at the top of the file to determine which host/port/file to use.

The command script in particular shows some examples in its usage. You can use it to control arbitrary LLRP configuration, such as adding/modifying/removing ROSpecs and AccessSpecs, changing a Reader's default ROAccessReport reported data, enabling/modifying/disabling KeepAlive messages, and enabling/disabling specific ReaderEventNotifications.

Testing

There are many unit tests available to run with the typical go tools. make test executes go test ./... -coverprofile=coverage.out and so can be used to quickly run all tests and generate a coverage report.

LLRP Functional Tests

There are some tests in the internal/llrp package which expect access to a reader. By default, they're skipped. To run them, supply a -reader=<host>:<port> argument to Go's test tool. For example, from the internal/llrp directory, you can run go test -reader=192.0.2.1:5084; assuming an LLRP device is reachable at that IP and port, it will connect to it, get its config and capabilities, then try to send and enable/start a basic ROSpec. It waits a short time, possibly collecting ROAccessReports (assuming tags are in your reader's antennas' FoVs), and the disables/deletes the ROSpec. The full options it will respond to:

  • short skips the ROSpec test described above, since it takes a little while to wait for the reports.
  • verbose logs some extra marshaling/unmarshaling data.
  • reader sets an address of an LLRP device and runs functional tests against it.
  • ro-access-dir uses a different subdirectory of internal/llrp/testdata (by default, roAccessReports) when running TestClient_withRecordedData. See below for more info.
  • update is used in the context of functional tests, but is only needed in special circumstances and should not be used unless you understand the consequences.

Note that if you're using the Goland IDE, you can put these options in a test config's program arguments, though the short and verbose options need the test. prefix.

Recorded Data Tests

The internal/llrp/testdata folder contains a series of .json and .bytes files. They're used by the TestClient_withRecordedData unit test which uses them roughly as follows:

  1. Convert .json -> struct 1 -> new bytes
  2. Convert .bytes -> struct 2 -> new JSON
  3. Compare .json and new JSON
  4. Compare .bytes and new bytes

The test only passes if the unmarshaling/comparisons are successful, which assumes it's able to match the name to an LLRP message type (it'll print an error if it doesn't have a match). Files in that directory only need to match the format {LLRP Message Type}-{3 digits}.{json|bytes}; other file names are ignored. The message name must also be specified in the test's switch, or it'll show an error about not finding a matching message type.

The test runs the same process using files in roAccessReports subdirectory, which just makes it a little easier to organize those files. You can use a different test directory with different reports by using the ro-access-dir flag while running that test, but the alternative directory must be a subdirectory of testdata.

For example, to run the test with files in a testdata/giantReports directory:

go test -v -run ^TestClient_withRecordedData$ -ro-access-dir=giantReports

By default, this directory is set to roAccessReports. If you set it to "", it'll skip checking it entirely.

There's actually nothing special about the directory name nor this flag that requires its contents be ROAccessReport message specifically, so you're free to segment other json/binary message file pairs into various directories and rerun the test with appropriate flag values. As long as the filenames match the pattern described above and that name is in the switch block of the compareMessages function of the test it'll test them.

Updating Recorded Test Data

As the test name implies, the .bytes data is recorded from an actual reader. It's possible to use the included Functional tests to record new data by passing the -update flag. Under most circumstances, this isn't necessary (some cases where it is are described below).

When -update is true, the TestClientFunctional test skips its normal tests and instead runs the collectData function. That function sends a series of messages to the -reader and records the binary results in the testdata directory, overwriting existing ones if present (they're version-controlled for a reason). Most messages are only sent once, hence they'll end in -000. When listening for ROSpecs, on the other hand, it'll write as many as it collects. The .gitignore is configured to ignore most of them, but it can be handy for testing.

At present, the recorder ignores the ro-access-dir flag described above, and writes the output directly to the testdata directory; the data tests will happily handle them in testdata, so this can still be fine for testing, but in the future, it'd probably be better for it to make use of that flag.

So when is this flag useful? Basically, if the marshaling/unmarshaling code changes in a way that results in different JSON/binary interpretation or output. For the binary side, the format should be fixed, as its subject to the LLRP specification. Furthermore, even if the unmarshaling code is wrong, if the llrp.Client code is correctly handling the message boundaries, the binary/.bytes files should not have reason to change.

On the other hand, the JSON format is not specific to LLRP. If the names of keys used in the JSON formats change, these tests most likely will no longer pass. For instance, it's possible to implement json.(Unm|M)arshaler/encoding.Text(Unm|M)arshaler interfaces to make some LLRP values easier to read. Doing so may break the JSON -> Go struct conversion, which will result in zero values when going Go struct -> binary, which will change the output. In the other direction, the binary -> Go struct conversion should be unaffected, but Go struct -> JSON should differ from the recorded value. The test outputs the location of "first difference" along with some surrounding context to aid in correcting these tests. For just name changes, it's probably better to make the changes to the JSON by hand. However, if the change is large enough, it could be easier to just throw away the existing folder and repopulate it. That is when the -update flag makes sense:

go test -run ^TestClientFunctional$ -reader="$READER_ADDR" -update

Test Helpers

There is a test helper file with some objects/methods that may be useful when developing unit tests. The following are particularly useful:

  • go doc llrp.TestDevice
  • go doc llrp.GetFunctionalClient

Footnotes

Notes on configuration.yaml

  • Modifying the configuration.yaml file will require you to rebuild the docker image and re-deploy using the new image
  • Any modifications made to the configuration.yaml file will be ignored if this is not the first time you have deployed the service and EdgeX Consul is enabled.
    • Consul will always overwrite any modifications to the yaml.
    • To get around this behavior, you may delete the whole configuration group for this service within EdgeX Consul.

device-rfid-llrp-go's People

Contributors

ajcasagrande avatar bill-mahoney avatar cloudxxx8 avatar dependabot[bot] avatar dvcorreia avatar ejlee3 avatar ernestojeda avatar farshidtz avatar felixting avatar jackchenjc avatar jim-wang-intel avatar jpwhitemn avatar jumpingliu avatar marcpfuller avatar monicaisher avatar seananthony21 avatar sicoyle avatar soda480 avatar weichou1229 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

device-rfid-llrp-go's Issues

[Device LLRP] Drive code causing events to be sent before SDK is fully initialized which causes panic

๐Ÿž Bug Report

Affected Services [REQUIRED]

The issue is located in Drive Initialize

Is this a regression? Maybe, did not see issue in Levski

Yes, the previous version in which this bug was not present was: Levski

Description and Minimal Reproduction [REQUIRED]

Start Simulator
Start service
Restart the service
Service will panic when incrementing EventsSent metric because it is nil

Last portion of Driver Initialize needs to be moved to Driver Start so SDK can complete it initialization, i.e. create metric counter

Anything else relevant?

Device discover IP address change requires service restart

When an existing non-connected device is discovered under a different IP Address, the address is updated in EdgeX, but the device service attempts to connect to it using the old IP Address. Issue is resolved after restarting the service to get the latest IP from Core Metadata.

Logs:

level=INFO ts=2021-06-09T17:42:56.355155912Z app=edgex-device-rfid-llrp source=driver.go:789 msg="Discover was called."                                                                                                
...
level=INFO ts=2021-06-09T17:42:56.362965848Z app=edgex-device-rfid-llrp source=discover.go:389 host=10.0.0.112 port=5084 msg="Attempting to connect to potential LLRP device..."                                       
...                                      
level=INFO ts=2021-06-09T17:42:56.384388542Z app=edgex-device-rfid-llrp source=discover.go:395 host=10.0.0.112 port=5084 msg="Connection initiated successfully."                                                      
level=INFO ts=2021-06-09T17:42:56.384452862Z app=edgex-device-rfid-llrp source=discover.go:427 msg="Discovered device: &{deviceName:SpeedwayR-10-EF-18 host:10.0.0.112 port:5084 vendor:25882 model:2001002}"          
level=INFO ts=2021-06-09T17:42:56.384624032Z app=edgex-device-rfid-llrp source=discover.go:235 oldInfo="map[host:192.168.0.112 port:5084]" discoveredInfo="&{deviceName:SpeedwayR-10-EF-18 host:10.0.0.112 port:5084 ve
ndor:25882 model:2001002}" msg="Existing device has been discovered with a different network address."     
level=DEBUG ts=2021-06-09T17:42:56.384659789Z app=edgex-device-rfid-llrp source=manageddevices.go:127 msg="Updating managed Device: : SpeedwayR-10-EF-18\n"                                                            
level=INFO ts=2021-06-09T17:42:58.358526671Z app=edgex-device-rfid-llrp source=driver.go:842 msg="Discovered 0 new devices in 2.003289609s."                                                                           
level=DEBUG ts=2021-06-09T17:42:58.358622488Z app=edgex-device-rfid-llrp source=async.go:164 msg="Filtered device addition finished"                                                                                   
level=ERROR ts=2021-06-09T17:43:26.354557118Z app=edgex-device-rfid-llrp source=device.go:141 error="dial tcp 192.168.0.112:5084: i/o timeout" address=192.168.0.112:5084 device=SpeedwayR-10-EF-18 msg="Failed to dial
 Reader."

Missing curl in snap package

The auto-configure script uses curl to make HTTP requests. The curl package is staged in the snap's auto-configure part but not included in the final snap package.

# copy auto-configure.sh to /bin and make sure curl is installed
auto-configure:
plugin: dump
source: bin/
stage:
- bin/auto-configure.sh
organize:
auto-configure.sh: bin/auto-configure.sh
stage-packages:
- curl

This is because lines 133-134 specifies a list of that needs to be staged and curl is not included.

๐Ÿž Bug Report

Affected Services [REQUIRED]

The issue is located in: snap

Is this a regression?

No. The snap hasn't had a stable release.

Description and Minimal Reproduction [REQUIRED]

$ snap install edgex-device-rfid-llrp --edge
edgex-device-rfid-llrp (edge) 2.1.0-dev.15 from Canonicalโœ“ installed
$ edgex-device-rfid-llrp.auto-configure
Dependencies Check: ...
Failed! Please install curl in order to use this script!

๐Ÿ”ฅ Exception or Error





๐ŸŒ Your Environment

Deployment Environment:
Snap

EdgeX Version [REQUIRED]:
2.1.0-dev.15

Anything else relevant?

Trigger discovery on config change

The device-rfid service currently requires manual configuration of a configuration setting named Driver.DiscoverySubnets before device discovery can be triggered. It's possible to provide this configuration via environment variable override, or by providing a custom configuration.toml file. The service also provides a script called auto-configure.sh which can be used to automatically populate this setting.

The default discovery interval for the service is set to 1 hour, so the current recommendation is to manually trigger discovery after the script has been successfully run.

This issue is a feature request to add support for automatically triggering discovery when Consul notifies the service that the setting has been changed.

This issue is being reported against the initial version of this device service (as it was just recently accepted by the project) as of June '21.

Please refer to the Device Discovery section of the service's README file for more details.

Clean up READMe

Clean up main repository README.md

  • reference to holding repo for LLRP app service
  • Reference older version APIs in Swagger

Remove code that loads static provision watchers (functionality now in latest SDK)

๐Ÿš€ Tech Debt

Relevant Package [REQUIRED]

This feature request is for Driver

Description [REQUIRED]

SDK now loads static provision watchers like it already did for devices and profiles. Service no longer needs to do this.

Describe the solution you'd like

Remove addProvisionWatchers() code

https://github.com/edgexfoundry/device-rfid-llrp-go/blob/main/internal/driver/driver.go#L712-L713

Note the folder name the SDK uses is provisionwatchers, so the provision_watchers folder must be renamed.

Describe alternatives you've considered

Have you considered any alternative solutions or workarounds?

Not reading EPC values with Zebra FX7500 Reader

๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘

Hi everyone, I'm using this device service for reading EPC tags data from different RFID readers. I'm having many problems with Zebra FX7500 reader with two antennas attached(number 1 and 4).
I'm sending a ROSpec.json file attached below, using the command.sh script located in the examples folder.

{
    "ROSpecID": 1,
    "Priority": 0,
    "ROSpecCurrentState": 0,
    "ROBoundarySpec": {
        "StartTrigger": {
            "Trigger": 1,
            "PeriodicTrigger": null,
            "GPITrigger": null
        },
        "StopTrigger": {
            "Trigger": 1,
            "DurationTriggerValue": 500,
            "GPITriggerValue": null
        }
    },
    "AISpecs": [
        {
            "AntennaIDs": [
                0
            ],
            "StopTrigger": {
                "Trigger": 0,
                "DurationTriggerValue": 0,
                "GPITrigger": null,
                "TagObservationTrigger": null
            },
            "InventoryParameterSpecs": [
                {
                    "InventoryParameterSpecID": 1,
                    "AirProtocolID": 1,
                    "AntennaConfigurations": null,
                    "Custom": null
                }
            ],
            "Custom": null
        }
    ],
    "RFSurveySpecs": null,
    "Custom": null,
    "LoopSpec": null,
    "ROReportSpec": {
        "Trigger": 2,
        "N": 0,
        "TagReportContentSelector": {
            "EnableROSpecID": true,
            "EnableSpecIndex": true,
            "EnableInventoryParamSpecID": true,
            "EnableAntennaID": true,
            "EnableChannelIndex": true,
            "EnablePeakRSSI": true,
            "EnableFirstSeenTimestamp": true,
            "EnableLastSeenTimestamp": true,
            "EnableTagSeenCount": true,
            "EnableAccessSpecID": true,
            "C1G2EPCMemorySelectors": {
                "CRCEnabled": true,
                "PCBitsEnabled": true
            },
            "Custom": null
        },
        "Custom": null
    }
}

In particular in the first terminal I'm launching the device-rfid-llrp-go service and in the second one I'm giving these commands:
1)./command.sh add ROSpec ROSpec.json
2)./command.sh enable ROSpec 1

After that I read all different EPC tags in the first terminal, however the problem is when I kill the execution and I relaunch the device service. In this case I don't read any tags and I'll read them again only after I'll restart the reader using its interface webpage. Can anyone give me a hand to solve this issue?

๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘๐Ÿ›‘

Tagging for v2

The latest tag is v1.0.1-dev.7 but the development is now done towards or beyond v2.

Shouldn't we have dev tags for v2 instead?

Remove Service Wrapper now that Device SDK has same interface

๐Ÿš€ Tech Debt

Relevant Package [REQUIRED]

This feature request is for Driver

Description [REQUIRED]

A clear and concise description of the problem or missing capability...

Code here is no longer needed now that it is in the Device SDK
https://github.com/edgexfoundry/device-rfid-llrp-go/blob/main/internal/driver/service.go#L16

Describe the solution you'd like

Remove code and refactor usages to use the code from the SDK.

Describe alternatives you've considered

Have you considered any alternative solutions or workarounds?

Service registry failures...

While reviewing a new PR for triggering discovery when config changes I ran into the following problem while testing.

When I build the latest version of device-rfid-llrp and run it against the latest/stable edgexfoundry snap, the device service is reporting that it cannot register to the registry because service information is not set.

Steps to reproduce:

  1. Install edgexfoundry from latest/stable channel (i.e. Hanoi)

$ sudo snap install edgexfoundry

  1. Build this device service (note, it seems this service has already been updated to use go 1.16 even though it hasn't been fully updated to use V2 yet).

  2. Run the service:

$ ./cmd/device-rfid-llrp-go --cp=consul.http://localhost:8500 --registry --confdir=./cmd/res
level=INFO ts=2021-06-15T17:37:57.568201468Z app=edgex-device-rfid-llrp source=config.go:193 msg="Loaded configuration from ./cmd/res/configuration.toml"
level=INFO ts=2021-06-15T17:37:57.569435976Z app=edgex-device-rfid-llrp source=config.go:168 msg="Using Configuration provider (consul) from: http://localhost:8500 with base path of edgex/devices/1.0/edgex-device-rfid-llrp"
level=INFO ts=2021-06-15T17:37:57.661931575Z app=edgex-device-rfid-llrp source=config.go:304 msg="Configuration has been pushed to into Configuration Provider (0 envVars overrides applied)"
level=INFO ts=2021-06-15T17:37:57.66202106Z app=edgex-device-rfid-llrp source=registry.go:80 msg="Using Registry (consul) from http://localhost:8500"
level=WARN ts=2021-06-15T17:37:57.662972521Z app=edgex-device-rfid-llrp source=registry.go:140 msg="could not register service with Registry: unable to register service with consul: Service information not set"
level=WARN ts=2021-06-15T17:37:58.665509302Z app=edgex-device-rfid-llrp source=registry.go:140 msg="could not register service with Registry: unable to register service with consul: Service information not set"
level=WARN ts=2021-06-15T17:37:59.667690141Z app=edgex-device-rfid-llrp source=registry.go:140 msg="could not register service with Registry: unable to register service with consul: Service information not set"
level=WARN ts=2021-06-15T17:38:00.669953378Z app=edgex-device-rfid-llrp source=registry.go:140 msg="could not register service with Registry: unable to register service with consul: Service information not set"
^Clevel=WARN ts=2021-06-15T17:38:01.672800492Z app=edgex-device-rfid-llrp source=registry.go:140 msg="could not register service with Registry: unable to register service with consul: Service information not set"
level=ERROR ts=2021-06-15T17:38:01.672854758Z app=edgex-device-rfid-llrp source=bootstrap.go:45 msg="aborted RegisterWithRegistry()"

Retry backoff not reset on successful re-connection

I started noticing that the time to retry reader connections was becoming increasingly long the more times you connect/disconnect cycle the readers without restarting the device service.

I added extra printouts to the following code in order to track it down a little better

Snippet of interest which shows what i am referring to:

2021/06/29 16:53:42 retry attempt: 5, wait: 2m5s
level=DEBUG ts=2021-06-29T23:55:47.365787007Z app=edgex-device-rfid-llrp source=device.go:151 device=SpeedwayR-11-25-D6 msg="Attempting LLRP Client connection."
level=INFO ts=2021-06-29T23:55:47.366315604Z app=edgex-device-rfid-llrp source=device.go:470 device=SpeedwayR-11-25-D6 msg="Device connection restored."
...
level=ERROR ts=2021-06-29T23:56:45.720013566Z app=edgex-device-rfid-llrp source=device.go:166 error="failed to get next message: failed to read header: EOF" device=SpeedwayR-11-25-D6 msg="Client disconnected unexpectedly."
2021/06/29 16:56:45 retry attempt: 1, wait: 0s
level=DEBUG ts=2021-06-29T23:56:45.720256459Z app=edgex-device-rfid-llrp source=device.go:139 address=10.0.0.53:5084 device=SpeedwayR-11-25-D6 msg="Attempting to dial Reader."
level=ERROR ts=2021-06-29T23:56:45.720555502Z app=edgex-device-rfid-llrp source=device.go:144 error="dial tcp 10.0.0.53:5084: connect: connection refused" address=10.0.0.53:5084 device=SpeedwayR-11-25-D6 msg="Failed to dial Reader."
2021/06/29 16:56:45 retry attempt: 6, wait: 3m15s

Explanation:

  • Notice that 2 mins go by waiting for attempt 5 (retry.Slow), when it is able to successfully reconnect.

  • A little while later the connection is dropped and immediately, the retry.Quick attempts to reconnect and fails (retry attempt: 1, wait: 0s).

  • After this the retry.Slow waits for attempt 6 for over 3 minutes.

    • Because the connection was restored after attempt 5, the retry.Slow should be reset back to attempt 1, or at least the back-off time should be reset.

Full log here

Failed DEBUG - message from device-rfid-llrp service while tag reading and DEBUG mode log setting in consul.

Consul mode - set logging mode "DEBUG"
Start tag reading after device discovery - below the failed log message.

edgex-device-rfid-llrp | level=DEBUG ts=2022-04-06T23:26:58.797373245Z app=device-rfid-llrp source=transform.go:96 msg="failed to read ResourceOperation: failed to find ResourceOpertaion with DeviceResource ROAccessReport in Profile LLRP-Impinj-Profile"
In DEBUG logging mode - this failed msg keep logging in console at start reading from app service.

Scripts are not working as mentioned in readme on "example-scripts" segment

Readme - https://github.com/edgexfoundry/device-rfid-llrp-go#example-scripts

Folder path " device-rfid-llrp-go/examples/
Script files - command.sh, example.sh, data.sh

Issue:
Script files - example.sh, command.sh, data.sh - mainly used for Add/enable/Disable/Delete RO Sepc - and check device config and capabilities, read data etc.

This files are not executing due json tags name are changed. command to check all supported device commands are available- "curl http://0.0.0.0:59882/api/v2/device/all | jq ."

Screenshot from 2022-04-06 16-48-55

TLS Support?

It's unclear from the top-level README.md file whether or not this device service supports TLS when communicating with a reader.

The latest RFID specification includes a section titled "Security in TCP Transport" which indicates that both server and client TLS authentication are possible...

Note - this issue is being reported as of the initial untagged version of the service, after it's recent promotion from holding.

Device LLRP: Add missing CORS configuration section

The following needs to be added to the Service section

  [Service.CORSConfiguration]
  EnableCORS = false
  CORSAllowCredentials = false
  CORSAllowedOrigin = "https://localhost"
  CORSAllowedMethods = "GET, POST, PUT, PATCH, DELETE"
  CORSAllowedHeaders = "Authorization, Accept, Accept-Language, Content-Language, Content-Type, X-Correlation-ID"
  CORSExposeHeaders = "Cache-Control, Content-Language, Content-Length, Content-Type, Expires, Last-Modified, Pragma, X-Correlation-ID"
  CORSMaxAge = 3600

Newly discovered reader does not auto connect

After discovering a new LLRP reader, the device service does not create a new connection to this device. A restart of the service is required.

Also the device cache is not updated to reflect this new device, so it will continue to be discovered only to be rejected by core-metadata as being a duplicate device.

This is potentially related to the workaround for adding new devices due to previous concerns with provision watchers.

todo(edgex): Align with latest EdgeX Hanoi provision watcher code

While developing this device service many bugs were found in the EdgeX Device SDK Go, which have all been marked as addressed. This issue is to remove any workarounds that are no longer needed when using EdgeX Hanoi release.


Issues:
edgexfoundry/device-sdk-go#598
edgexfoundry/device-sdk-go#606
edgexfoundry/device-sdk-go#598
edgexfoundry/device-sdk-go#609


Workarounds:

// Note: We have to send data over this channel to let the SDK know we are done discovering.
// see: https://github.com/edgexfoundry/device-sdk-go/issues/609
d.deviceCh <- nil
d.lc.Info(fmt.Sprintf("Discovered %d new devices in %v.", len(result), time.Now().Sub(t1)))

// Note: For now we have to resort to adding our discovered devices ourselves due to multiple bugs in the
// provision watcher code, as well as no clear way to tell if a device was matched by a PW or not.
// see: https://github.com/edgexfoundry/device-sdk-go/issues/598
// see also: https://github.com/edgexfoundry/device-sdk-go/issues/606
for _, discovered := range result {
if _, err := d.registerDevice(discovered); err != nil {
d.lc.Error("Error adding device.", "name", discovered.Name, "error", err)
}
}

// Note: This is a bit of a workaround based on provision watcher logic. It was only left
// this way (as opposed to a total refactor) in order to allow a smooth transition
// back to provision watchers once the assortment of bugs have been fixed.
if discovered.Protocols["tcp"]["vendorPEN"] == strconv.FormatUint(uint64(Impinj), 10) {
profile = ImpinjDeviceProfile
}

// Note that we are adding vendorPEN to the protocol properties in order to
// allow the provision watchers to be able to match against that info. Currently
// that is the only thing the provision watchers use for matching against.
//
// We would prefer to put the vendorPEN, and possibly model and fw version
// in a separate "protocol", possibly named "metadata", however there is a bug in the
// current logic that does not allow this.
//
// see: https://github.com/edgexfoundry/device-sdk-go/issues/598
return dsModels.DiscoveredDevice{
Name: info.deviceName,
Protocols: map[string]contract.ProtocolProperties{
"tcp": {
"host": info.host,
"port": info.port,
"vendorPEN": strconv.FormatUint(uint64(info.vendor), 10),
},

Add .md pages describing how to test (with real hardware)

Per Kamakura planning meeting (Nov 21), each device service should provide a HOWTOTEST.md page that describe how to check the base functionality of the device service with real hardware. These tests should be run manually before each release.

Each device service should also provide a WORKSWITH.md page that lists the actual hardware (make, model, web site link, etc.) that the device service has been tested with and known to work with. Provide enough details on the device/sensor so that an adopter would know where to go to get the same device.

Version not set properly and SDK version never set

๐Ÿž Bug Report

Affected Services [REQUIRED]

/version endpoint

Is this a regression?

No, think it have been like this a along. Need to also be fixed in Jakarta

Description and Minimal Reproduction [REQUIRED]

run device-snmp
hit localhost:59989/api/v2/version
which result in the following response:

{
    "apiVersion": "v2",
    "version": "to be replaced by makefile",
    "serviceName": "device-rfid-llrp",
    "sdk_version": "0.0.0"
}

Service Version is hard coded to 1.0.0
SDK version is never set

๐Ÿ”ฅ Exception or Error





๐ŸŒ Your Environment

Deployment Environment:

EdgeX Version [REQUIRED]:

Anything else relevant?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.