Composite Scanning
Composite scanning allows you to set up a workflow where multiple items need to be scanned.
You can build a PluginComposite, add any number of ScanViewPlugins to it as children, and then pass in the composite to the initializer of a ScanView.
Plugin composites currently can not be nested. |
There are two processing modes available in a PluginComposite: sequential and parallel.
-
In sequential mode, the plugin composite runs each child plugin sequentially in the order they were added. As each plugin gets a result, the next plugin is started.
-
In parallel mode, cutouts for each child plugin are initially displayed simultaneously. As a plugin gets a result, its cutout is hidden so that the user can concentrate on scanning the remaining items. For instance, you can set up a parallel scan configuration consisting of an electric meter together with a barcode.
In a parallel composite, visual feedback and cutouts for all child plugins that haven’t scanned yet are shown at the same time. It’s a good idea to customise the view configuration for each child plugin. For instance, you could adjust the cutouts so that they do not overlap each other, or, if the cutouts do overlap, adjust the appearance of the scan feedback so it is clear to the user which feedback comes from which plugin. You could also use the ALScanViewDelegate method |
ALViewPluginComposite
ALViewPluginComposite
is the class representing a plugin composite. Each composite consists of:
-
a delegate object with the type
ALViewPluginCompositeDelegate
-
the processing mode, parallel or sequential
-
the composite’s plugin ID
-
a list of children view plugins
-
a map of
String → ScanViewPluginConfig
for each child plugin
Configuring a plugin composite with JSON
A JSON configuration for a plugin composite should be structured in this way:
{
"viewPluginCompositeConfig": {
"id": "parallel_composite_scanning",
"processingMode": <PROCESSING_MODE>,
"viewPlugins": [
{
"viewPluginConfig": {
"pluginConfig": {
"id": "com.anyline.configs.plugin.lp",
"licensePlateConfig": {
"scanMode": "auto"
}
},
...
},
"viewPluginConfig": {
...
},
...
}
]
}
}
viewPluginCompositeConfig
is the name of the root node’s JSON property.
PROCESSING_MODE
should be sequential
or parallel
.
id
should be a valid keyword string and is a required field.
viewPlugins
is an array where the viewPluginConfig
JSON bodies of each child plugin is listed.
To create a plugin composite with this, use the initWithJSONDictionary:error:
initializer:
NSError *error;
NSString *jsonConfig = [self getJSONConfigString];
NSDictionary *configDictionary = [jsonConfig toJSONObject:&error]; // check error
ALViewPluginComposite *pluginComposite = [[ALViewPluginComposite alloc] initWithJSONDictionary:JSONDict
error:&error]; // check error
pluginComposite.delegate = self; // implement the ALViewPluginCompositeDelegate
Then, provide the composite with the ScanView’s initializer:
ALScanView *scanView = [[ALScanView alloc] initWithFrame:frame
scanViewPlugin:pluginComposite
scanViewConfig:scanViewConfig
error:&error];
Configuring a plugin composite programmatically
To create a plugin composite with code, you would have to provide the children view plugins to ALViewPluginComposite
's initializer.
NSError *error;
NSArray<ALScanViewPlugin *> *children = @[ meterScanViewPlugin, barcodeScanViewPlugin ];
ALViewPluginComposite *pluginComposite = [[ALViewPluginComposite alloc] initWithID:@"parallel-meter-barcode"
mode:ProcessingModeParallel
children:children
error:&error]; // check error
pluginComposite.delegate = self; // implement the ALViewPluginCompositeDelegate
Reading the result
For both sequential and parallel plugin composites, once all the results have been scanned, the
ALViewPluginCompositeDelegate
method viewPluginComposite:allResultsReceived:
is called on the delegate, providing
a list of ALScanResult
found for each child plugin, in the order in which they were scanned.
On top of
|
Example JSON
Here you will see an example JSON configuration for a composite scan setup, using a license plate and VIN plugins as children running in parallel mode:
{
"cameraConfig": {
"captureResolution": "1080p",
"pictureResolution": "1080p"
},
"flashConfig": {
"mode": "manual_off",
"alignment": "bottom_left"
},
"viewPluginCompositeConfig": {
"id": "com.anyline.configs.plugin.parallel-lp-vin",
"processingMode": "parallel",
"viewPlugins": [
{
"viewPluginConfig": {
"pluginConfig": {
"id": "com.anyline.configs.plugin.lp",
"licensePlateConfig": {
"scanMode": "auto"
}
},
"cutoutConfig": {
"style": "rect",
"maxWidthPercent": "85%",
"maxHeightPercent": "100%",
"alignment": "top_half",
"width": 0,
"ratioFromSize": { "width": 4, "height": 1 },
"offset": { "x": 0, "y": 0 },
"strokeWidth": 2,
"cornerRadius": 10,
"strokeColor": "8be9fd",
"outerColor": "000000",
"outerAlpha": 0,
"feedbackStrokeColor": "0099FF"
},
"scanFeedbackConfig": {
"style": "rect",
"strokeWidth": 2,
"strokeColor": "0099FF",
"fillColor": "330099FF",
"cornerRadius": 0,
"beepOnResult": false,
"vibrateOnResult": false,
"blinkAnimationOnResult": true
}
}
},
{
"viewPluginConfig": {
"pluginConfig": {
"id": "com.anyline.configs.plugin.vin",
"vinConfig": {}
},
"cutoutConfig": {
"style": "rect",
"maxWidthPercent": "85%",
"alignment": "top_half",
"ratioFromSize": { "width": 62, "height": 9 },
"offset": { "x": 0, "y": 240 },
"outerColor": "000000",
"outerAlpha": 0,
"strokeWidth": 2,
"strokeColor": "ff79c6",
"cornerRadius": 4,
"feedbackStrokeColor": "0099FF"
},
"scanFeedbackConfig": {
"style": "animated_rect",
"animation": "traverse_multi",
"animationDuration": 250,
"strokeWidth": 2,
"strokeColor": "0099FF",
"fillColor": "220099FF",
"beepOnResult": false,
"vibrateOnResult": false,
"blinkAnimationOnResult": true
}
}
}
]
}
}