IBM i und mobile Geräte Teil 2

22. Oktober 2013 | Von | Kategorie: Cloud, Social Media, Devices, Load`n`go

Nachdem ich Ihnen im vorangegangenen Artikel („IBM i und mobile Geräte Ausgabe 06-07/13“) einen Überblick über die Entwicklung von mobilen Anwendungen gegeben habe, erhalten Sie hier ein konkretes Beispiel. Ziel dieses Artikels ist es, Ihnen die nötigen Schritte zu demonstrieren, um eine Client Server App zu realisieren sowie Einblick in die Technologien und Entwicklungsumgebungen zu geben um das gesetzte Ziel zu erreichen.

mobilVON DENIS KOHL

Das Beispiel ist eine einfache App (Mobile-Anwendung), die Daten an die AS/400 übertragen kann und in einer physikalischen Datei bzw. SQL Tabelle ablegt. Von dort können die Daten dann weiter bearbeitet werden. Damit das Beispiel ein wenig interessanter wird, werden wir Barcodes scannen und mit Daten ergänzen. An dieser Stelle möchte ich der UBR Unternehmensberatung Reetmeyer in Bonn danken, für die Möglichkeit auf ihren Systemen zu testen. Gelesen werden die bekannten EAN 13 Strichcodes.

Als Plattform werde ich der Einfachheit halber ein iPad benutzen. Die gesendeten Daten werden auf der IBM i mit einem PHP Programm empfangen. Ziel ist, daß die Anwendung die im Barcode kodierte Zahlenkombination ausliest und mit weiteren Informationen und Daten verbindet und kombiniert und dann in einer Datenbank ablegt. An dieser Stelle weise ich darauf hin, dass die Kameras in diesen Geräten sich sehr gut eignen, einzelne Etiketten zu scannen, hingegen nicht für Massenscanner geeignet sind. Dafür sind die Kameras zu langsam.

Planung

Welche Funktionen soll die mobile Applikation (App) haben?

mobil2

Es soll möglich sein, Barcodes mit der Kamera zu scannen und zu erkennen. An dieser Stelle sollte man versuchen, eine bestehende Bibliothek (Sammlung von Teilprogrammen) zu verwenden. Vergleichsweise schnell fi ndet man zwei Bibliotheken. Erstens Open- VC http://www.opencv.org/ eine frei verfügbare Bildund Videobearbeitungsbibliothek von intel. Diese ist kostenfrei erhältlich, ist in der Programmiersprache C implementiert und ist somit direkt native für Apples iOS (Betriebssystem für iPhone iPad) verfügbar. Zweitens zbar http://zbar.sourceforge.net/ eine ebenfalls frei verfügbare Bibliothek, sie ist ebenfalls in C implementiert und kostenfrei erhältlich. Ich wähle den Weg des geringsten Widerstands und benutzezbar.

Wie sollen die Daten gespeichert werden?

Hierbei ist besonders wichtig im Auge zu behalten: Welches Programm soll diese Daten später nutzen? Man kann sich durch die IBM i wieder viel Arbeit abnehmen lassen, da alle Daten im Datenbankkontext gespeichert werden können. Somit kann ein anderes Programm jederzeit auf die Daten zugreifen, ohne dass man auf Zugriffsfenster oder Serialisierbarkeit achten muss. Hier enden die Vorab-Überlegungen auch schon. Die erfassten Daten werden in einer Tabelle (physikalische Datei) gespeichert und dann entsprechend durch andere Programme weiter verarbeitet.

Wie kann der Barcode gelesen werden, handelt es sich um selbstgenerierte Barcodes oder sind diese international registriert?

Barcodes sind ein umfassendes Thema. Grundsätzlich kann man sie in zwei Lager teilen. Erstens die international registrierten Barcodes. Diese fi ndet man auf nahezu allen Produkten im Supermarkt und auf der Rückseite der meisten Bücher. Diese sind zum einen in der GEPIR (Global Electronic Party Information) Datenbank gespeichert. Der Zugang zu dieser Datenbank ist kostenpflichtig. Zweitens gibt es die Möglichkeit jederzeit eigene Barcodes zu erstellen. Der Barcode steht für eine Ziffernkolonne bzw. eine Zahl, dieser Zahl-Wert kann gespeichert und mit weiteren Informationen bzw. Daten verknüpft werden. Damit kann z.B. ein Lager- und/oder Produktionsprozess leichter elektronisch erfasst werden.

(Listing 1) M8ViewController.h
M8ViewController.h
//
// M8ViewController.h
// BarCodeScanner400
//
// Created by denis kohl on 15.08.13.
// Copyright (c) 2013 denis kohl. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "ZBarSDK.h"
@interface M8ViewController : UIViewController 
@property (weak, nonatomic) IBOutlet UILabel *serverAdresse;
@property (weak, nonatomic) IBOutlet ZBarReaderView *ReaderView;
@property (weak, nonatomic) IBOutlet UILabel *statusLabel;
@property (weak, nonatomic) IBOutlet UITextField *produktTextField;
@property (weak, nonatomic) IBOutlet UITextField *anzahlTextField;
@property (weak, nonatomic) IBOutlet UILabel *barcodeTextField;
@property NSString * server;
- (IBAction)sendSave:(id)sender;
@property (weak, nonatomic) IBOutlet UIButton *sendSaveButton;
- (void)receivedData:(NSData *)data;
- (void)emptyReply;
- (void)timedOut;
- (void)downloadError:error;
@end

abb1

Welche zusätzlichen Eingabefunktionen werden benötigt?

Auch hier vereinfache ich. Wir werden nur zwei Werte ergänzen – zum einen die Anzahl, dann den Namen. Sollte der Barcode noch nicht mit einem Produktnamen verknüpft sein, soll der Bediener aufgefordert werden, diesen anzugeben.

Wie sollen die eingegebenen Daten verwaltet werden?

Auf dem Ziel-System, einer IBM Minimum Release 5.4, wird ein kleines PHP Programm installiert. Das Lizenz Programm für OS 400 7.1 ist 2ZSVRPI *BASE 5001 Zend Server for IBM i 5.6.0 ( PHP 5.3 ). Es wäre auch möglich, eine Implementierung in JAVA zu verwenden. Mit entsprechendem Mehr-Aufwand ist auch RPG möglich. Das Server Programm hat alleine die Aufgabe die empfangenen Daten in eine vorbereitete Tabelle (physikalische Datei) einzutragen. Entweder direkt mit der bestehenden Applikation, oder in eine temporäre Tabelle und anschließend einen Transport der Daten vorzunehmen, nach einer Eingabekontrolle. Diese Entscheidung wird je Anwendungsfall getroffen.

Umsetzung

Welche Plattform ist die Richtige? Welche Infrastruktur und Software wird gebraucht? Die Wahl der Plattform wird von vielen Kriterien beeinfl usst. Auf welcher Plattform bzw. Umgebung basiert die bestehende IT?

Abbildung 2

Abbildung 2

Hier gehe ich davon aus, dass es Ziel des Unternehmens ist, die eigene IT nicht weiter zu diversifi zieren sondern Bestehendes zu nutzen, bzw. zu ergänzen, also die neuen Funktionen auf die IBM i zu integrieren. Ohne zusätzliche Software Lizenzgebühren ist der einfachste Weg die Implementierung einer HTTP bzw. REST Schnittstelle in PHP.

Hierbei werden die gesendeten und empfangenen Pakete behandelt wie Anfragen an einen Webserver. Nur enthalten die übermittelten Daten keine vollständigen Webseiten, sondern nur Teile von einer HTML Datei oder sogar nur strukturierte Daten. Wir werden an dieser Stelle eine einfaches Datenformat benutzen das in den letzten Jahren immer stärker zum Einsatz kommt. Und auf der Objekt Notation von Javascript basiert – JSON (JavaScript Objekt Notation).

Welche Endgeräte sollen benutzt werden?

Es bestehen hier zwei beziehungsweise drei Möglichkeiten. Erstens mit einem Apple iPad, zweitens ein Android Tablet und drittens ein Microsoft RT Tablet. Für den Prototyp nutze ich ein Apple iPad und die XCode Entwicklungsumgebung von Apple. XCode ist kostenfrei. Allerdings nur unter Apple MacOS ausführbar. Um die App im Apple App Store veröffentlichen zu können erwirbt man ein Apple Entwickler Konto (Apple Developer Account). Weiterhin wird noch eine Möglichkeit benötigt, die Barcodes zu decodieren. Ich nutze hier die API (Applikation Programming Interface) von zbar http://zbar.sourceforge. net/iphone/sdkdoc/index.html. Diese ist eine frei verfügbare Bibliothek, welche Barcodes decodieren kann.

Das App Design der Oberfläche.

Die Oberfläche kann mit XCode bzw. Interface Builder durch drag and drop erstellt werden. Hier ist besonders auf die Benutzbarkeit zu achten! Die genaue Positionierung und farbige Gestaltung ist hier absolut unabhängig von der Implementierung. Dann wird das ZBarSDK importiert. Dieses benötigt als Grundlage weitere Bibliotheken (API‘s): Quartz-Core (Bildbearbeitung), CoreVideo (Bildaufzeichnung und Speicherung), CoreMedia (Bildaufzeichnung und Bilderkennung), AVFoundation (Grundlage für QuartzCore, CoreVideo und CoreMedia). Die libiconv.dylib dient zur Zeichenerkennung.

Abbildung 3

Abbildung 3

Zur einfacheren besseren Übersicht habe ich diese zusätzlichen Dateien in dem Ordner Supporting Files abgelegt – siehe Abbildung 2.

Die eigentliche Implementierung ist in BarCodeScanner400. In den Storyboard Dateien wird das Oberfl ächen Design erstellt – siehe Abbildung 3.

Für die Implementierung benutzte ich die Apple Haussprache Objective- C, die Apple von der Firma Next Inc. übernahm. Wie der Name erahnen lässt, handelt es sich um eine Objekt-orientierte Sprache. Demzufolge ist die Implementierung in Klassen zusammen gefasst. Jede Objective-c Klasse besteht aus zwei Dateien:

.h beschreibt die Klasse, .m implementiert die Klasse. Für uns ist nur die M8ViewController Klasse wichtig, Listing 1 und 2.

Im ersten Schritt wird eine Verbindung zum Server hergestellt. Im Statusfeld wird angezeigt, ob das iPad mit dem Server verbunden ist. Diese Anfrage wird in der Methode (void) viewDidLoad aufgerufen. Dies ist die erste Methode die beim Start der App aufgerufen wird.

Anhand der Ergebnisstruktur erkennt die App, ob es sich um eine neue Verbindung handelt. Auch an dieser Stelle erhalten wir wieder Vorteile durch die Nutzung von JSON um die Server-Client-Kommunikation zu implementieren. In JSON besteht ein Objekt immer aus zwei Teilen: {key:value}. An dieser Stelle wird das Anmelde- Objekt übertragen {‘server‘: ‘localhost‘}.

Auf dem Weg vom Client zum Server benutzen wir die HTTP Schnittstelle – CGI, das Common Gateway Interface – also eine mit Variablen ergänzte URL „http:// server/barcode.php?data=50505 82930818&produkt=DVD&anza hl=23“

In PHP werden die Variablen in einer Liste $_GET übergeben. Man kann dann mit $_GET[,data‘] auf die übergebenen Daten zugreifen.

Die nächste Frage lautet, welche möglichen Nutzeraktionen bearbeitet werden müssen, um ein komfortables und effi zientes Arbeiten zu ermöglichen.

Es muss möglich sein, anhand der Barcodes zu erkennen, ob das Produkt schon in der Datenbank existiert oder neu angelegt werden muss. Dies ist sehr einfach möglich, wenn man die Daten in einer physikalischen Datei bzw. SQL Tabelle ablegt.

(Listing 2) gekürzt
#import "M8ViewController.h"
@interface M8ViewController ()
@end
@implementation M8ViewController
@synthesize
ReaderView,barcodeTextField,statusLabel,anzahlTextField,produktTextField,sendSa
veButton,serverAdresse, server;
- (void)viewDidLoad
{
[super viewDidLoad];
ReaderView.readerDelegate = self;
[ReaderView sizeToFit];
[ReaderView layoutSubviews ];
server = [NSString stringWithFormat:@"server/kohl/barcode.php"];
[self requestServer:server sendData:nil sendProdukt:nil sendAnzahl:nil];
}
 .
 .
 .
-(void)viewDidAppear:(BOOL)animated
{
[ReaderView setAllowsPinchZoom:false];
[ReaderView start];
}
- (void) readerView:(ZBarReaderView *)readerView didReadSymbols:(ZBarSymbolSet
*)symbols fromImage:(UIImage *)image
{
for (ZBarSymbol * sym in symbols) {
barcodeTextField.text = sym.data;
break;
}
}
- (void) requestServer:(NSString *)url sendData:(NSString * )data sendProdukt:
(NSString *)produkt sendAnzahl:(NSString *)anzahl
{
NSMutableURLRequest * request = [[NSMutableURLRequest alloc] init];
[request setHTTPMethod:@"GET"];
if (data == nil) {
[request setURL:[NSURL URLWithString:url]];
}else{
NSString * str = [NSString stringWithFormat:@"%@?data=%@&produkt=
%@&anzahl=%@",url,data,produkt,anzahl];
NSLog(@"URL: %@", str);
[request setURL:[NSURL URLWithString:str]];
}
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request queue:queue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
if ([data length] > 0 && error == nil)
[self receivedData:data];
else if ([data length] == 0 && error == nil)
[self emptyReply];
else if (error != nil && error.code == NSURLErrorTimedOut)
[self timedOut];
else if (error != nil)
[self downloadError:error];
}];
}
(Listing 3 gekürzt) barcode.php

©Denis Kohl, nur für Abonnenten frei verwendbar mit Langversion im online-Magazin

<?php require_once 'Zend/Db/Adapter/Db2.php'; error_reporting(5); function db_query($query){ $conn = connectDatabase(); if(!$conn){ error_log("Can't connect DB"); sendError("Can't connect DB"); return; }else{ $result = mysql_query($query) || die(sendError(mysql_error())); mysql_close(); return $result; } } function connectDatabase(){ $IntLibl = array(I5_OPTIONS_INITLIBL => 'KOHL');
$conn = i5_connect(,,,$IntLibl);
if (!$conn) {
error_log("DB Error");
exit();
}
return $conn;
}
function logon(){
error_log("Barcode Login ");
$hallo_arr = array('server' => 'localhost');
echo json_encode($hallo_arr);
}
function checkBarcode($barcode){
error_log("Find Barcode: ". $barcode);
$query = "select produkt from barcodes where barcode = '".
$barcode."';";
.
.
.
function processData($produkt,$anzahl,$barcode){
error_log("Data Resived Produkt:".$produkt. " Anzahl:".$anzahl. "
Barcode:". $barcode);
$query = "select count(*) from barcodes where barcode = '" .
$barcode."'";
error_log("$query");
$result = db_query($query);
.
.
.
$row = mysql_fetch_array($result);
error_log("Anzahl der Zeilen: ". $row[0]);
$num = $row[0];
if ($num == 0) {
$query = "insert into barcodes
(barcode,produkt,anzahl)values('". $barcode ."','". $produkt
."',".$anzahl.");";
error_log("$query");
db_query($query);
$status_arr = array('status' => 'Product instered');
echo json_encode($status_arr);
}else{
$query = "update barcodes set anzahl ='". $anzahl . "' where
barcode = '". $barcode."';";
error_log($query);
db_query($query);
$status_arr = array('status' => 'Product updated');
echo json_encode($status_arr);
}
}
function sendError($error){
.
.
.
Listing 2 ungekürzt
Listing 3 ungekürzt

Wir benötigen an dieser Stelle nur vier Felder.

  • a) Eine eindeutige Identifi kation, um jede Zeile eindeutig adressieren zu können. Das Füllen der Tabelle überlassen wir der DB2.
  • b) Ein 13 Zeichen langes Feld welches die durch den Barcode kodierte Nummer aufnimmt.
  • c) Ein maximal 50 Zeichen langes Feld welches einen menschlich lesbaren Produktnamen aufnimmt.
  • d) Die Anzahl des jeweiligen Produkts.

CREATE TABLE barcodes (id integer not null generated always as identity (start with 100, increment by 1), barcode char(13), produkt varchar(50), anzahl integer, primary key(ID));

Jetzt kann über die Abfrage SELECT COUNT(*) FROM barcode WHERE barcode = ,5050582930818‘ festgestellt werden, wie oft der Barcode 5050582930818 schon in der Datenbank hinterlegt ist. Jeder Barcode sollte natürlich maximal nur einmal hinterlegt sein. Dies kann man mit einem Unique Constrain auf dem Feld Barcode absichern.

Wenn der Barcode noch nicht erscheint, dann handelt es es sich um eine neues Produkt und muss neu in die Datenbank eingefügt werden. Sollte es sich um ein bereits gelistetes Produkt handeln, dann muss nur die Anzahl aktualisiert werden.

Dies kann man ausführen mit dem SQL Befehl

  • UPDATE barcodes SET anzahl = <anzahl> WHERE barcode = <barcode>;

Diese SQL Befehle werden von dem PHP Script ausgeführt. Daraus ergibt sich jetzt folgendes Ablaufdiagramm: Da bei der fi nalen Übermittlung alle Daten übergeben werden, ist es nötig auch an dieser Stelle zu überprüfen inwieweit der Barcode schon genutzt wurde. Sollte er vorhanden sein, wird nur die Anzahl des Produktes innerhalb des Eintrages geändert, sollte er noch nicht eingetragen sein wird er angelegt.

Dies vermeidet mögliche Mehrfacheingaben durch die Benutzer.

Erstens: ein Nutzer glaubt, dass das Produkt neu und noch nicht gelistet ist und will alle Daten auf einmal übergeben. Das kann sinnvoll sein. Dennoch kann es zu Problemen führen, wenn das Produkt schon da war, hingegen unter einem anderen Namen abgelegt wurde.

Die angegebene Anzahl wird unter der Nummer speichernd abgelegt. Der bereits bestehende Name wird beibehalten, ein möglicherweise angegebener neuer Name wird verworfen. Eine Änderung des Namens für den Artikel ist auf diesem Weg nicht vorgesehen. Als Schlüssel dient die durch den Barcode kodierte Zahl. Die zweite – und als Standardvorgehen vorgesehene – Möglichkeit ist, dass der Nutzer auf die Rückmeldung vom Server wartet und entweder den Produktnamen angezeigt erhält, oder die Rückmeldung „New Product“ zurückerhält.

Sollte das der Fall sein, muss er diesen Platzhalter durch einen sinnvollen Produkt Namen ersetzen. Dem geneigten Leser ist vielleicht schon aufgefallen, dass es kein Session Management zwischen Client und Server gibt. Es ist bei dieser einfachen Anwendung nicht nötig. Und eine oder mehrere Variablen über den gesamten Verbindungszeitraum zu halten – dies ist in PHP auch nur recht aufwändig möglich. Wenn eine Anwendung so komplex wird, dass dies unumgänglich wird, rate ich persönlich dazu einen Websphere Application Server als Grundlage der Implementierung zu benutzen.

Die „Intelligenz“ der Anwendung liegt im Client, also dem Objective-C Code. Insbesondere in der Barcode- Erkennung, da hier mit einem Bild gearbeitet wird und dies aufwändig ist.

Das Einlesen des Barcodes erfolgt automatisch durch die Kamera.

Die Kamera wird gestartet, wenn mit Aufruf der Methode – (void)viewDidLoad die Zeile ReaderView.readerDelegate = self; erreicht wird.

Hierbei wird die Ausführung der Kamera an die Implementierungsklasse der Oberfl äche gebunden. Wenn Sie sich intensiver mit diesem Thema beschäftigen wollen: Es gibt in der Objekt-orientierten Entwicklung mehrere Entwurfsmuster. Im Objektive-C Umfeld wird sehr häufi g das Delegater Muster verwendet. Hierbei hat jede Klasse eine vor-defi nierte Abfolge von Methoden, die entweder selbst implementiert werden müssen oder fertig übergeben werden.

Zum Beispiel muss in unserem Beispiel die Methode (void) readerView: (ZBarReaderView *) readerView didReadSymbols: (ZBarSymbolSet *) symbols fromImage: (UIImage *) image implementiert werden.

Der Aufruf der Methode erfolgt automatisch. An diese Art zu programmieren muss man sich erst gewöhnen!

Ich möchte an dieser Stelle nur noch auf eine Methode näher eingehen:

  • (IBAction) sendSave: (id) sender

Diese ist direkt mit dem Sendeknopf verknüpft. Dieser Knopf löst die Datenübergabe an den Server aus. Sie können in Listing 2 feststellen, dass es keinen direkten TCP/IP Aufruf gibt – anstelle dessen nutze ich die HTTP Schnittstelle. Dieses Vorgehen erspart viele mögliche Fehler, darüber hinaus kann man sich darauf verlassen, dass man ausschliesslich mit menschen-lesbaren Protokollen arbeitet. Das vereinfacht Debuging bzw. Fehlersuche. ♦

Zum Schluss möchte ich mich bei allen Mitwirkenden bedanken: Sebastian Lück, Finanzkanzlei Lück, Selbständiger Handelsvertreter für Swiss Life Select Deutschland GmbH, Manfred Reetmeyer – UBR Unternehmensberatung Reetmeyer

Schlagworte: , , , , , , , , , , , , ,

Schreibe einen Kommentar

Sie müssen eingeloggt sein, um einen Kommentar schreiben.