[Question] Scanning Barcode Data

Ok, sled is up to date, as is the iOS

Will the onStartup trigger and then wait for input from the scan barcode action or will it fire off all at once before I can scan?

Okay, so this is how I managed your task:

  1. A Button has an onClick Event which triggers the Scan Barcode Action
  2. My View contains a label bound against session.props.device.barcodes.
  3. The Lable binding also has a script transform with the following code:
if len(value) == 0:
	return 'No Barcodes Scanned this Session'
else:
	return value[-1].data.text

ScanBarcode.json (1.6 KB)

With this setup, when I click the button (on a mobile device), I use the native barcode scanning to read a barcode.
On leaving the barcode scanning, I return to my View and the Label displays the scanned barcode value.

Unfortunately, the onStartup area I suggested will NOT work for client-side (session) actions like Scan Barcode at this time, as the onStartup and onShutdown events are Gateway-side. Short-term, we’ll look to remove the Scan Barcode Action from the View-level Event Configuration. Long-term, we’ll probably want to provide A View Events category which would include something like “onLoad” - and Scan barcode would go there.

Ok, this was pretty much the same as in the video link above, at about 38:45 to about 41:40ish…

1 Like

:white_check_mark:

I always forget that he did that walkthrough (even when it’s linked in the thread #shame) - it probably would have saved me some time…

Yes, his example is spot-on, although his example will only provide the first scanned barcode, whereas the code I supplied will get you the most recently scanned barcode.

something more like, as you mentioned, the View Events… or similar…
onLoad scan barcode, onInput set last barcode == text of specific label, trigger barcode listen again, onInput set last barcode == text of next specific label, and so on…

…With the variation being in the return value[-1].data.text instead of value[0].data.text

Would it be possible to do something where the onStartup event sets some property to the current datetime and then have a component in the view that checks the current datetime against this property? After 1-2 seconds initiate the Scan Barcode Action in the View? Basically the same functionality as having the Timer component from the Vision module.

1 Like

Long-term, nearly anything is possible, however, an important part of your question is that you’re using a script to initiate barcode scan; there’s no scripting API for that functionality at this time.

If our solution is to only add the appropriate scripting calls (system.perspective.scanBarcode() ), what you would most likely do is something along the following lines:

  1. In your View’s onStartup Event, perform self.view.custom.scan = int(self.view.custom.scan) + 1
  2. Provide an OnChange Script for the view.custom.scan property, and have that script invoke the barcode scan.

If our solution is instead to provide an Event more along the lines of onViewLoad, then you would be able to simply use the Scan Barcode Action when the Event occurs.

One last thing, just for clarity: the blocking factor here is not timing, so the 1-2 second wait will not alleviate anything - the blocking factor is where the script executes (even though it’s a View’s onStartup Event, the script will execute on the Gateway), combined with the limitations in what the Gateway can do vs what the Session can do (the Session can request access to the camera, but the Gateway is unable to do so).

2 Likes

The onViewLoad event is not available yet, correct?

Correct. At this point that’s only a concept that we’re looking into.

It does make sense to me to have the onViewLoad event handler though since Perspective handles things differently than Vision does. This will allow us to have triggers for those views that we won’t need to be initialized before that view loads and are ok to send to GC when we leave that view.

When trying the “self.view.custom.scan = int(self.view.custom.scan) + 1” in my onStartup Event, it gives me the following error:

INFO | jvm 1 | 2018/11/19 12:41:19 | File “function:runAction”, line 2, in runAction
INFO | jvm 1 | 2018/11/19 12:41:19 | AttributeError: ‘com.inductiveautomation.perspective.gateway.script’ object has no attribute ‘view’

if I change it to self.custom.props.scan I get the following…

INFO | jvm 1 | 2018/11/19 12:42:05 | File “function:runAction”, line 2, in runAction
INFO | jvm 1 | 2018/11/19 12:42:05 | AttributeError: ‘com.inductiveautomation.perspective.gateway.script’ object has no attribute ‘props’

What you’re trying won’t work because that suggestion was hypothetical based off of a potential fix:

But the reason you’re seeing those errors is because of what self is referring to.

The reason self.view works in binding scripts is because the self being referenced is a ComponentModelScriptWrapper, which knows about the Component, and through the Component, the View in use.

The onStartup self refers to a ViewModelScriptWrapper, so you should probably use self.custom.propertyName

Has the previously mentioned changes to the barcode scanning come through yet? it appears that the Session.props.device.barcode property is no longer available?

I have the same question… I can’t seem to find the Session.props.device.barcodes either.
Does anyone happen to have a sample demo project for performing a barcode scan?

Indeed, barcode scanning has now been migrated to a more mature system (as well as nfc ndef, with more sensors/data to follow shortly). Documentation is going to catch up, but until then, here’s the gist:

TLDR:

  1. configure action to enable data collection
  2. write Perspective session event handling script for type (see list in session events menu of designer)
    3a. use Perspective scripting to fire an event with data*
    3b. Capture said event at the desired property with an message handler*

* optional, if you want some of the information to make it back to component/session properties

Mobile devices can produce data for Perspective 's consumption. These data are provided to the system via one of two routes (note, terms defined are descriptive, not technical definitions):

  1. Direct Client Handling: The data is sent from the mobile devices and applied directly to the properties in the client, typically to session props (examples: geolocation info, continuous mode accelerometer values). In other words, this is data that is handled locally, in the client, and generally does not require special handling to make use of. Simply bind directly to the session property of interest.

  2. Remote Handling: The data is sent from the mobile device to the Perspective Client, which packages the data up and sends it back to the gateway to be handled by a Perspective Session Event script where a user can further manipulate the data, apply it to the session, broadcast it as a event message for component consumption, etc. I.E. - this is data that is handled remotely, at the gateway.

Client Handling is how all data was previously exposed. Remote handling is a new API / featureset that gives more flexibility in how data is handled. Most importantly, it allows us to collect data on the mobile device when in a disconnected state, and that data is submitted to the gateway upon reconnect. On the gateway, the data is then submitted to a Designer-Authored script on a ‘first in, first out’ basis: meaning, you get to deal with the data created in a single Session in the order it was received, even if it was collected offline.

Currently the remote(2) handling system has implemented support for:

* Barcode scanning
* NFC Ndef scanning
* Accelerometer 'batch' mode data (pending publication of updated mobile apps with feature)

Usage Info

As with all mobile data sources, the user will generally need to enable the collection. In the case of accelerometer, NFC, and geolocation, this typically means firing an action that requests the data or enables the sensor. If not previously agreed to, this will prompt the devices to accept the permissions to collect the data. For instance, the device will request location permissions if trying to use GPS, or camera/image permissions if trying to scan barcodes.

Data should always be handled in a ‘first in, first out’ basis, meaning that regardless of connectivity state, by the time the data makes it to the gateway, the first data collected should always be the first data handled, data should not go missing, and it should not be submitted more than once.

When actually collecting data it’s sent from the mobile device to the web environment. From there, it’s added to a local cache, and then sent to the gateway, and upon successful receipt of the data at the gw the cache is cleared. If the data can’t be submitted, it is held in the cache until we reach a connected state at which point it should attempt resending. Note: current implementation has a cache-size limit that is dependent on the Browser used on the mobile device. We have ways increase the size in future releases if we find it’s currently too limited.

This system should also gracefully handle situations where data is in the process of being submitted (there is a pending data POST waiting response from the gw), and new data is sent from the mobile device. In that case, the client should wait for the pending data to complete. If the send is successful, then the waiting data will then be submitted. If the send is not successful, both the failed data as well as the new data should be retained in cache for later submission, and when submission does occur, should be handled in order.

Remote Handler Configuration

Create a remote handler by opening up the Perspective Session Event’s editor (dropdown menu in menu bar of designer). Looking at the signatures of the handlers, they generally have:

  • session: a reference to the Perspective project session
  • data: PyDict representation of ‘data object’, specific to the type of data (see below)
  • context: User defined context object, created when the action enabling/requesting the data collection is configured.

Data Structures for Mobile Data Sources - Configuration & Result

Each mobile data source has a unique ‘shape’ of data that it may require for configuration. In addition, they provide unique data as a result. The ‘action’ to enable any of these ‘Remote’ handling configuration actions uses a configuration shape that looks like this:

{
    "config": { <data type specific configuration> },
    "context": { <anything you, the designer, would find useful> }
}

Note that the context is an object that is completely arbitrary. The system does no processing on it, it’s simply passed through with the data. The intention is that a user can configure some meta information about the data collection that is useful in figuring out who/what/why the data was collected.

For the Data Source’s unique configuration and data shape, they are currently as follows:

Barcode

Config

"type" is a string representing the barcode type that the mobile device should attempt to focus on, a list of supported types will be provided prior to release.

{
    type: string  
}

Data

{
    text: string;
}

NFC NDEF

NDef is the most popular/common standard format for an NFC tag. It’s supported by Android and iOS. https://stackoverflow.com/questions/18013972/what-is-the-relationship-between-nfc-and-ndef provides a decent intro.

Config

{
    enable: boolean,
    continuousRead: boolean // Android specific option
}

Data

{
    messages: Array<NdefDataItem>
}

NDefDataItem - represents the shape of data provided by a single NFC NDef scan, and generally follows the Ndef spec, with some convenience fields.

{
    typeNameFormat: int,
    id: undefined or variable length string,
    payload: string, // variable length base64 encoded representation of raw bytes generated by device, more on this in a below
    type: string, //  variable length,  generally the Record Type Definition (RTD)

   // not NDef related fields, but are added for user convenience. see below.
    bytes: Array<byte>,
    string: string
}

In the script handler, we take this raw NDef data and add two fields:

  • bytes: a bytearray, which is simply the raw bytes from the b64 string
  • string: a variable length string that is simply the utf-8 encoded instantiation of the byte array. This may be meaningless depending on the actual contents of the bytes, but may be convenient when the bytes happen to be a utf-8 encoded string.

Note that the payload represents the raw payload bytes as provided by the device, in base 64 encoding.

So the ‘data’ received by the gateway handling script consists of an array of scan items, each containing the 5-6 fields (remember, id is optional according to the spec).

Hopefully this sheds some light on this new functionality while our docs catch up. Please feel free to follow up with any questions, comments, or feedback.

3 Likes

Quick question (I think I know the answer but want to confirm).
When triggering a barcode scan from a Perspective button, using the Scan Barcode action, I create an Object Member in the Context called “scanType” and assign a value of “Machine”. In the Session Event Barcode Scanned logic, do I simply access this Key:Value pair like a simple dictionary (e.g. myScanType = context[‘scanType’]?

I’m working remotely with this system so don’t have physical access to do testing myself, so I want my ducks in a row before bothering the client to do testing for me.

Thanks,
Mike

Hi Mike,

Sorry about the delay, apparently missed your message when I returned. Just happened to notice this in my inbox looking for another message, did you still need a hand with this?

Hey Perry,

No, I was finally able to get my configuration tested.
It’s difficult when you’re working on a system remotely and are relying on overly taxed folks on the client end to trigger events needed to do that testing.

Thanks for following up.

A post was split to a new topic: Barcode scan not returning type when set to ‘Any’?