Meter Plugin


Examples Source Code

The source code of all example use cases of the meter plugin can be found in the iOS SDK Bundle

Plugin Description

Simultaneous Barcode Scanning

Starting from SDK 3.8 Anyline supports simultaneous barcode scanning for any plugin. Additional Information on how to implement this on iOS can be found at Simultaneous Barcode Scanning

The Anyline Meter plugin is capable of scanning Analog Electric-, Gas- and Water Meter readings. It is also possible to scan Bar- and QR-codes, which is useful for identifying meters, as well as the Serial Number. Common Digital Meters and Heat Meters can also be scanned.

The description of all parameters can be found at Plugins > Meter

How to implement the Meter Plugins

This is a step by step guide on how to implement the Meter Plugin in iOS.

Add the Anyline Meter Plugins to your UIViewController

The first step is to add the three main components of every Anyline use case: the Scan Plugin (handling the scanning part), the Scan View Plugin (handling the UI) and the Scan View (handling camera, flash and overall communication between Anyline components).

Add the Anyline Meter Plugins
// The Anyline plugins used to scan
@property (nonatomic, strong) ALMeterScanViewPlugin *meterScanViewPlugin;
@property (nonatomic, strong) ALMeterScanPlugin *meterScanPlugin;
@property (nullable, nonatomic, strong) ALScanView *scanView;

Anyline ScanPlugin

Initialise the Scan Plugin

You have to initialise the plugin with your Anyline License Key Generation, an ID (String) and the delegate inside viewDidLoad.

Initialise the Scan Plugin
    //Add Meter Scan Plugin (Scan Process)
    NSError *error = nil;
    self.meterScanPlugin = [[ALMeterScanPlugin alloc] initWithPluginID:@"ENERGY" licenseKey:kDemoAppLicenseKey delegate:self error:&error];
    NSAssert(self.meterScanPlugin, @"Setup Error: %@", error.debugDescription);

Anyline ScanViewPlugin

Initialise the Scan View Plugin

After initialising the scanPlugin, the next step is to create the scanViewPlugin with the scanPlugin you just created. The ScanViewPlugin will handle and display the UI used for scanning.

Initialise the Scan View Plugin
    //Add Meter Scan View Plugin (Scan UI)
    self.meterScanViewPlugin = [[ALMeterScanViewPlugin alloc] initWithScanPlugin:self.meterScanPlugin];

Set the ScanViewPluginConfig

The View Configuration configurates the look and feel of the scanning process. You can set it the following way:

Set the ScanViewPluginConfig
NSString *confPath = [[NSBundle mainBundle] pathForResource:@"meter_capture_config" ofType:@"json"];
ALScanViewPluginConfig *scanViewPluginConfig = [ALScanViewPluginConfig configurationFromJsonFilePath:confPath];

self.meterScanViewPlugin = [[ALMeterScanViewPlugin alloc] initWithScanPlugin:self.meterScanPlugin
                                                scanViewPluginConfig:scanViewPluginConfig];

Limitations for the Meter Plugin

The Size and Ratio are fixed and optimised for the best results
The Cutout Alignment should be set to TOP with a little offset, as this reduces reflections

Anyline ScanView

Initialise the Scan View

The last Anyline Object you have to create is the so called scanView, it will handle the camera, the flash and manage the previously created ScanViewPlugin and ScanPlugin. You need to instantiate the scanView with the previous created scanViewPlugin and the frame it should occupy. Usually, that’s the bounds of the screen.

Initialise the Scan View
    //Add ScanView (Camera and Flashbutton)
    self.scanView = [[ALScanView alloc] initWithFrame:frame scanViewPlugin:self.meterScanViewPlugin];

Adding the Scan View to the View Hierarchy and start the Camera Feed

To add the scanView to your view hierarchy add the following in viewDidLoad:

Adding the view and starting the camera
    [self.view addSubview:self.scanView];
    [self.scanView startCamera];

Final steps to configure and use the Meter Plugin

Set your Scan Mode

All Meter-related use cases can be started from the Meter plugin by setting the Scan Mode to the respective use case.

Setting the scanMode
    //Set ScanMode to ALAutoAnalogDigitalMeter
    BOOL success = [self.meterScanPlugin setScanMode:ALAutoAnalogDigitalMeter error:&error];
    if( !success ) {
        // Something went wrong. The error object contains the error description
        [[[UIAlertView alloc] initWithTitle:@"Set ScanMode Error"
                                    message:error.debugDescription
                                   delegate:self
                          cancelButtonTitle:@"OK"
                          otherButtonTitles:nil] show];
        
    }
Mode Parameter in Module Description
ALAutoAnalogDigitalMeter Auto Analog/Digital Meter Description
ALAnalogMeter Analog Meter Descripton
ALDigitalMeter Digital Meter Descripton
ALHeatMeter4 Heat Meter 4 Descripton
ALHeatMeter5 Heat Meter 5 Descripton
ALHeatMeter6 Heat Meter 6 Descripton
ALBarcode Barcode Descripton
ALSerialNumber Serial Number Descripton
ALDialMeter Dial Meter Descripton
ALDotMatrixMeter Dot Matrix Meter Description

The Meter Delegate

In the Meter Plugin of Anyline SDK, you will be provided the final results via delegate calls.

In case of the Meter Scan Plugin, the protocol is called ALMeterScanPluginDelegate. This section describes the ALMeterScanPluginDelegate callbacks in detail.

anylineMeterScanPlugin:didFindScanResult:

New in version 4.

This is the main callback method for the Meter Plugin. It is called once a valid result has been found.

The callback returns the result as an ALMeterResult, which contains an actual scan result, the scan image and e.g. the last processed full frame image.

Getting a result
#pragma mark - ALMeterScanPluginDelegate methods
/*
 The main delegate method Anyline uses to report its scanned codes
 */
- (void)anylineMeterScanPlugin:(ALMeterScanPlugin *)anylineMeterScanPlugin
                 didFindResult:(ALMeterResult *)scanResult {
    
    [self anylineDidFindResult:scanResult.result barcodeResult:self.barcodeResult image:(UIImage*)scanResult.image scanPlugin:anylineMeterScanPlugin viewPlugin:self.meterScanViewPlugin  completion:^{
        //Display the result
        NSMutableArray <ALResultEntry*> *resultData = [[NSMutableArray alloc] init];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Meter Reading" value:scanResult.result]];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Barcode" value:self.barcodeResult]];
        
        ALResultViewController *vc = [[ALResultViewController alloc] initWithResultData:resultData image:scanResult.image];
        [self.navigationController pushViewController:vc animated:YES];
    }];
    
    //reset found barcodes
    self.barcodeResult = @"";
}
  • ALMeterResult scanResult
Field Type Nullable Description
result NSString The actual result of the scanning process
image UIImage The image the result was found on. The image is cropped to the cutout
fullImage UIImage The full image the result was found on.
confidence NSInteger The confidence of the SDK in the detected result

Enabling or Disabling the Reporting

The reporting of the (intermediate) results (including an image) helps us to improve the accuracy of the SDK and detect potential shortcomings.

However, you can easily turn the reporting off (viewDidLoad())

Enabling or disabling of the Reporting
[self.meterScanPlugin enableReporting:YES];

Reporting & License

Depending, on your license, the reporting may not be turned on or off.

Multiple Scan Use Cases

If you have multiple scan use cases in your application, and you want different reporting for each usee case, you have to set enableReporting for each use case seperately. I.e. setting reporting to on means that it is turned on for your whole application.

Starting Scanning

Hint

Use [scanView startCamera] within viewDidload before starting Anyline.

After everything is initialised, you can start the scanning process, by calling startAndReturnError: on the plugin.

It is advised to place this call in the viewDidAppear: lifecycle method of the UIViewController.

Starting the plugin
/*
 This method will be called once the view controller and its subviews have appeared on screen
 */
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    /*
     This is the place where we tell Anyline to start receiving and displaying images from the camera.
     Success/error tells us if everything went fine.
     */
    NSError *error = nil;
    BOOL success = [self.meterScanViewPlugin startAndReturnError:&error];
    if( !success ) {
        // Something went wrong. The error object contains the error description
        [[[UIAlertView alloc] initWithTitle:@"Start Scanning Error"
                                    message:error.debugDescription
                                   delegate:self
                          cancelButtonTitle:@"OK"
                          otherButtonTitles:nil] show];
    }
}

Stop Scanning

To stop the scanning process, call stopAndReturnError: on the plugin.

To make sure the SDK is properly stopped upon leaving the Activity, make sure to place stopAndReturnError: in the viewWillDisappear: lifecycle method of the UIViewController.

Stopping the plugin
/*
 Cancel scanning to allow the module to clean up
 */
- (void)viewWillDisappear:(BOOL)animated {
    [self.meterScanViewPlugin stopAndReturnError:nil];
}

Switching the Scan Mode

You can switch the Scan Mode at any time, without having to call cancelScanningAndReturnError:.

Switching Scan Modes
//Set ScanMode to ALAnalogMeter
BOOL success = [self.meterScanPlugin setScanMode:ALAnalogMeter error:&error];

Full Example

Full Meter iOS Example
#import "NSUserDefaults+ALExamplesAdditions.h"
#import "ALAutoAnalogDigitalMeterScanViewController.h"
#import <Anyline/Anyline.h>
#import "ALAppDemoLicenses.h"
#import "ALResultEntry.h"
#import "ALResultViewController.h"



// This is the license key for the examples project used to set up Aynline below
NSString * const kAutoAnalogDigitalMeterScanLicenseKey = kDemoAppLicenseKey;

static const NSInteger padding = 7;

// The controller has to conform to <AnylineEnergyModuleDelegate> to be able to receive results
@interface ALAutoAnalogDigitalMeterScanViewController ()<ALMeterScanPluginDelegate, AnylineNativeBarcodeDelegate>

// The Anyline plugins used to scan
@property (nonatomic, strong) ALMeterScanViewPlugin *meterScanViewPlugin;
@property (nonatomic, strong) ALMeterScanPlugin *meterScanPlugin;
@property (nullable, nonatomic, strong) ALScanView *scanView;

//Native barcode scanning properties
@property (nonatomic, strong) NSString *barcodeResult;

@property (nonatomic, strong) UIView *enableBarcodeView;
@property (nonatomic, strong) UISwitch *enableBarcodeSwitch;
@property (nonatomic, strong) UILabel *enableBarcodeLabel;

@end

@implementation ALAutoAnalogDigitalMeterScanViewController

/*
 We will do our main setup in viewDidLoad. Its called once the view controller is getting ready to be displayed.
 */
- (void)viewDidLoad {
    [super viewDidLoad];
    
    // Set the background color to black to have a nicer transition
    self.view.backgroundColor = [UIColor blackColor];
    
    self.title = @"Analog/Digital Meter";
    
    CGRect frame = [[UIScreen mainScreen] applicationFrame];
    frame = CGRectMake(frame.origin.x, frame.origin.y + self.navigationController.navigationBar.frame.size.height, frame.size.width, frame.size.height - self.navigationController.navigationBar.frame.size.height);
    
    //Add Meter Scan Plugin (Scan Process)
    NSError *error = nil;
    self.meterScanPlugin = [[ALMeterScanPlugin alloc] initWithPluginID:@"ENERGY" licenseKey:kDemoAppLicenseKey delegate:self error:&error];
    NSAssert(self.meterScanPlugin, @"Setup Error: %@", error.debugDescription);
    
    //Add Meter Scan View Plugin (Scan UI)
    self.meterScanViewPlugin = [[ALMeterScanViewPlugin alloc] initWithScanPlugin:self.meterScanPlugin];
    
    
    //Set ScanMode to ALAutoAnalogDigitalMeter
    BOOL success = [self.meterScanPlugin setScanMode:ALAutoAnalogDigitalMeter error:&error];
    if( !success ) {
        // Something went wrong. The error object contains the error description
        [[[UIAlertView alloc] initWithTitle:@"Set ScanMode Error"
                                    message:error.debugDescription
                                   delegate:self
                          cancelButtonTitle:@"OK"
                          otherButtonTitles:nil] show];
        
    }
    
    //Add ScanView (Camera and Flashbutton)
    self.scanView = [[ALScanView alloc] initWithFrame:frame scanViewPlugin:self.meterScanViewPlugin];
    
    [self.view addSubview:self.scanView];
    [self.scanView startCamera];
    
    BOOL enableReporting = [NSUserDefaults AL_reportingEnabled];
    [self.meterScanPlugin enableReporting:enableReporting];
    self.meterScanViewPlugin.translatesAutoresizingMaskIntoConstraints = NO;
    
    //After setup is complete we add the module to the view of this view controller
    [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scanView]|" options:0 metrics:nil views:@{@"scanView" : self.scanView}]];
    id topGuide = self.topLayoutGuide;
    [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[topGuide]-0-[scanView]|" options:0 metrics:nil views:@{@"scanView" : self.scanView, @"topGuide" : topGuide}]];

    
    self.controllerType = ALScanHistoryElectricMeter;

    self.barcodeResult = @"";
    [self.view sendSubviewToBack:self.scanView];
    [self.scanView addSubview:[self createBarcoeSwitchView]];
    [self.scanView bringSubviewToFront:self.enableBarcodeView];
}

/*
 This method will be called once the view controller and its subviews have appeared on screen
 */
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    /*
     This is the place where we tell Anyline to start receiving and displaying images from the camera.
     Success/error tells us if everything went fine.
     */
    NSError *error = nil;
    BOOL success = [self.meterScanViewPlugin startAndReturnError:&error];
    if( !success ) {
        // Something went wrong. The error object contains the error description
        [[[UIAlertView alloc] initWithTitle:@"Start Scanning Error"
                                    message:error.debugDescription
                                   delegate:self
                          cancelButtonTitle:@"OK"
                          otherButtonTitles:nil] show];
    }
}

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
        
    [self updateLayoutBarcodeSwitchView];
}

/*
 Cancel scanning to allow the module to clean up
 */
- (void)viewWillDisappear:(BOOL)animated {
    [self.meterScanViewPlugin stopAndReturnError:nil];
}

#pragma mark - IBAction methods


- (IBAction)toggleBarcodeScanning:(id)sender {
    
    if (self.scanView.captureDeviceManager.barcodeDelegates.count > 0) {
        self.enableBarcodeSwitch.on = false;
        [self.scanView.captureDeviceManager removeBarcodeDelegate:self];
        //reset found barcode
        self.barcodeResult = @"";
    } else {
        self.enableBarcodeSwitch.on = true;
        [self.scanView.captureDeviceManager addBarcodeDelegate:self error:nil];
    }
}

#pragma mark - Barcode View layouting
- (UIView *)createBarcoeSwitchView {
    //Add UISwitch for toggling barcode scanning
    self.enableBarcodeView = [[UIView alloc] init];
    self.enableBarcodeView.frame = CGRectMake(100, 100, 150, 50);
    
    self.enableBarcodeLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 30)];
    self.enableBarcodeLabel.text = @"Barcode Detection";
    UIFont *font = [UIFont systemFontOfSize:14 weight:UIFontWeightThin];
    self.enableBarcodeLabel.font = font;
    self.enableBarcodeLabel.numberOfLines = 0;
    
    self.enableBarcodeLabel.textColor = [UIColor whiteColor];
    [self.enableBarcodeLabel sizeToFit];
    
    self.enableBarcodeSwitch = [[UISwitch alloc] init];
    [self.enableBarcodeSwitch setOn:false];
    self.enableBarcodeSwitch.onTintColor = [UIColor whiteColor];
    [self.enableBarcodeSwitch setOnTintColor:[UIColor colorWithRed:0.0/255.0 green:153.0/255.0 blue:255.0/255.0 alpha:1.0]];
    [self.enableBarcodeSwitch addTarget:self action:@selector(toggleBarcodeScanning:) forControlEvents:UIControlEventValueChanged];
    
    [self.enableBarcodeView addSubview:self.enableBarcodeLabel];
    [self.enableBarcodeView addSubview:self.enableBarcodeSwitch];
    
    return self.enableBarcodeView;
}

- (void)updateLayoutBarcodeSwitchView {
    self.enableBarcodeLabel.center = CGPointMake(self.enableBarcodeLabel.frame.size.width/2,
                                                 self.enableBarcodeView.frame.size.height/2);

    self.enableBarcodeSwitch.center = CGPointMake(self.enableBarcodeLabel.frame.size.width + self.enableBarcodeSwitch.frame.size.width/2 + padding,
                                                  self.enableBarcodeView.frame.size.height/2);

    CGFloat width = self.enableBarcodeSwitch.frame.size.width + padding + self.enableBarcodeLabel.frame.size.width;
    self.enableBarcodeView.frame = CGRectMake(self.scanView.frame.size.width-width-15,
                                              self.scanView.frame.size.height-self.enableBarcodeView.frame.size.height-55,
                                              width,
                                              50);
}

#pragma mark - ALMeterScanPluginDelegate methods
/*
 The main delegate method Anyline uses to report its scanned codes
 */
- (void)anylineMeterScanPlugin:(ALMeterScanPlugin *)anylineMeterScanPlugin
                 didFindResult:(ALMeterResult *)scanResult {
    
    [self anylineDidFindResult:scanResult.result barcodeResult:self.barcodeResult image:(UIImage*)scanResult.image scanPlugin:anylineMeterScanPlugin viewPlugin:self.meterScanViewPlugin  completion:^{
        //Display the result
        NSMutableArray <ALResultEntry*> *resultData = [[NSMutableArray alloc] init];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Meter Reading" value:scanResult.result]];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Barcode" value:self.barcodeResult]];
        
        ALResultViewController *vc = [[ALResultViewController alloc] initWithResultData:resultData image:scanResult.image];
        [self.navigationController pushViewController:vc animated:YES];
    }];
    
    //reset found barcodes
    self.barcodeResult = @"";
}

#pragma mark - AnylineNativeBarcodeDelegate methods
/*
 An additional delegate which will add all found, and unique, barcodes to a Dictionary simultaneously.
 */
- (void)anylineCaptureDeviceManager:(ALCaptureDeviceManager *)captureDeviceManager didFindBarcodeResult:(NSString *)scanResult type:(NSString *)barcodeType {
    dispatch_async(dispatch_get_main_queue(), ^{
        if ([scanResult length] > 0 && ![self.barcodeResult isEqualToString:scanResult]) {
            self.barcodeResult = scanResult;
        }
    });
}


@end