menu
  Home  ==>  papers  ==>  web  ==>  delphi_mobile_point_of_sale_software   

Delphi Mobile Point of Sale Software - Felix John COLIBRI.

Using FireBase, FireDac, DataSnap, Rest, Wifi, and FireMonkey

  • abstract : Android tablet or smartphone point of sale application using a WIFI connection to a DataSnap REST Server connected with FireDac to a FireBird database
  • key words : Android, WIFI, point of sale device, REST Client, REST Server, JSON, DataSnap methods, FireDac, FireBird
  • software used : Windows XP Home, Delphi XE7, Android 4.3
  • hardware used : Pentium 2.800Mhz, 512 M memory, 140 G hard disc, Samsung Galaxy Nexus, Android 4.3
  • scope : Delphi Xe6 to Xe8, Delphi Seattle, Delphi Berlin
  • level : Delphi developer
  • plan :


1 - WIFI Point of Sale devices

For Shop management, data is usually centralized in some database, but the shop workers move around the shop and must have access to this database.

Wifi is quite adapted to solve this kind of problem.

From a Delphi point of view, this can be implemented using

  • Firemonkey for the Client mobile device (smartphone, tablet, dedicated mobile device)
  • REST services for communications between the client and the server
  • to offer those services from the server, FireBase as a database, FireDac for data access, DataSnap as a REST server and Json for packet formatting


The objective is to display on a tablet the content of one or several table contents. The mobile device will interrogate a Server who which will load the data and send it over to the Client.

delphi_wifi_point_of_sale_devices



In this article we will present the structure of such a REST Server / REST clients architecture :

  • the Rest Server
  • a Rest Client on a PC using the VCL
  • a Rest Client using FireMonkey for the shop workers



2 - The Rest Server

2.1 - The choice of the components

The Database can be any database. We chose FireBase which is installed in 3 minutes sharp and, according to some surveys, is the most popular database for Delphi users. But you can use Oracle, Sql Server, Postgres, Interbase, MySql etc.

To communicate with the Clients, the Server can use any transfer protocol. However this protocol must be compatible with the mobile devices, and, in this case, Rest is a good solution.

To implement Rest services, a Tcp Server could be used. We would have to handle the marshalling / unmarshalling of the objects ourselves. Here comes JSon, which was just created for this very purpose: transfer objets from one place to the other.

And for JSon, FireDac offers an out of the box library.

Finally the Client must be able to request the data. If the data is in the form of objects, DataSnap with methods (function returning objects in our case) can be used.



2.2 - Building the DataSnap Rest Server

Four your comfort, initialize the folder of your projects:
   start delphi
   select "Tools | Options" and in "Default Project:", fill the project folder name and click "Ok"

project_default_folder



To create the Rest Server
   select "File | New Other"
   Delphi displays all the projects available

file_new_other

   select "DataSnap Server"
   all available servers are displayed

datasnap_rest_application

   select "DataSnap Rest Application" and click "Ok"
   Delphi asks what kind of WebBroker Application we want to create

webbroker_application_types

   we prefer to use the Indy Tcp Server. So select "Stand Alone" and click "Next"
   Delphi ask us if we want "Vcl" or "FireMonkey"

webbroker_vcl_application

   for the Rest Server, keep "Vcl" selected and click "Next"
   Delphi asks about the Port of this Web Server

webbroker_port

   when clicking "test port", in our case 8080 was used. So we selected 8081 and click "test port" again:
   the port can be used

selection_and_test_of_the_port

   click "Ok" and "Next"
   Delphi asks us which features we want

server_methods

   in our case, no authentication, and we keep, for this tutorial, the sample DataSnap method (a "reverse string" method). So keep the defaults and click "Next"
   Delphi can add a tDataModule

add_a_tdatamodule

   select "tDataModule" and click "Next"
   Delphi proposes the project folder

poject_folder

   navigate to your projects folder and click "Finish"
   Delphi builds the necessary files and presents the Server Form

the_webbroker_rest_server



2.3 - The WebBroker Rest Server Units

The first step is to save all the generated files
   click "Save Project"
   save "FormUnit1.Pas" as "u_server_main.pas". This is the tForm that will be displayed on the desktop, allowing the user to start and stop the server
   save "WebModuleUnit1.pas" as "u_web_module.pas"
   save "ServerMethodsUnit1.pas" as "u_server_methods.pas"
   save "Project1.dproj" as "p_12_rest_server.dproj"
And
  • u_server_main.pas will start / stop the server, and can be used to launch a web browser for test purposes
    rest_server_main_form
  • u_web_module.pas contains all the components which manage the Rest Server. It contains the basic DataSnap Server components, plus the components used by the Rest type of Server
    rest_web_module
  • u_server_methods.pas is a tDataModule containing all the methods the Rest Server will offer to its clients. In Our case, the Wizard has prepared two functions, EchoString and ReverseString.

    Unit u_server_methods;
      Interface

        Uses System.SysUtilsSystem.ClassesSystem.Json,
            Datasnap.DSServerDatasnap.DSAuth,
            DataSnap.DSProviderDataModuleAdapter;

        Type
          {$METHODINFO ON}
          TServerMethods1 =
              Class(TDataModule)
                Private
                Public
                  Function EchoString(Valuestring): string;
                End;
          {$METHODINFO OFF}

      Implementation

        {$R *.dfm}

        Uses System.StrUtils;

        Function TServerMethods1.EchoString(Valuestring): string;
          Begin
            Result := Value;
          End// EchoString

        End.

    Notice

    • the tDataModule ancestor
    • the $METHODINFO compiler directive which enables the Server to call our methods


Compile the project. Since we changed the Server Methods unit name, we must change the same name in the u_web_module.pas

change_server_methods_name

So
   change all ServerMethodsUnit1 identifiers into u_server_methods (3 occurences)


Eventually unblock the Windows lock :

firemonkey_windows_client



2.4 - Test the Rest Server

The main form can be used to start the server and open a browser to test all DataSnap methods.

To test "reverse string"
   run the project
   the server form is displayed

rest_server_form

   click "Start"
   the Tcp Server is started ("start" and the port are grayed)
   click "Open Browser"
   the default browser is opened with the
  http://localhost:8081/
URL

test_browser

   clicking on "Reverse String" calls the WebBroker default action and reverses the string
   to see the other available functions, click the "Server Functions" hyper link
   two groups of methods are displayed; Admin and ServerMethods

server_function_invoker

   click ON THE PLUS before "TServerMethods" (yes, it is a pale gray on a pale blue plus button)

   all the available methods are displayed

test_server_methods

   to test the "ReverseString" method, click the "+" before "ReverseString" (just as difficult to see as the other "+" on this form)

   an edit for typing the parameter of the function are presented

test_reversestring

   type a string, say abcd

   the result of the method are presented

test_reversestring_result



Note

  • it would have been hard to better hide the test functions then they did. We did not see the "+" sign, until we read about them in the doc
  • to stop the server, you cannot use "Ctrl F2". Use the "Stop" button and then close the Server form
  • the test .HTML pages have been generated by the two tPageProducers which are on the tWebModule.

    test_page_producers.png

    Those .HTML page also use all the css\, images\ js\ and templates\ displayed in the project manager. Those files would not be necessary for a simple REST server.



2.5 - The DataSet transfer Rest method

Our Rest Server is going to send the content of some Table to its clients.

We chose the Delphi "MastApp" database, and will use

  • the CUSTOMER table
  • the ORDERS table (with a CUSTNO foreign key)
  • the ITEMS table (with an ORDERNO foreign key)
You can dowload the .ZIP of this Database

If you select another database, just find 2 or 3 tables with a master / detail relationship. We added the Sql Scripts to allow you to create and fill those tables.



Copy the database near the Server:
   create a _data\ folder
   copy the MASTAPP_25.FDB base there


We will start with a client which will display the CUSTOMER ( CUSTNO, COMPANY ) table.



2.6 - The Firedac components

To read and write the tables, we will use FireDac.

First we initialize the link to the FireBird driver :
   select the u_server_methods unit

   from the Tool Palette, drop a tFDGUIxWaitCursort on the DataModule

   then drop a tFDPhysIBDriverLink
   double click on the DataModule to create its OnCreate method and initialize the driver's DriverId, VendorHome and VendorLib Properties by code :

Procedure TServerMethods1.DataModuleCreate(SenderTObject);
  Begin
    With FDPhysIBDriverLink1 Do
    Begin
      DriverId:= 'FB25';
      VendorHome := 'C:\Program Files\Firebird\Firebird_2_5';
      VendorLib := 'fbclient.dll';
    End// with ADPhysIBDriverLink1
  End// DataModuleCreate

Please note:

  • we hard coded the location of our FireBird 2.5 fbclient.dll location
  • if you use another version of Firebird, or Firebird was installed in another folder, adjust the code accordingly
  • we also assume a 32 bit PC. If you use a 64 bit PC, you should change the Firebird Client .DLL also
  • you could also initialize the FireDac driver using the FdDrivers.Ini file. This has been explained in great detail in the following Delphi FireDac Connection article
  • of course, if you use another database engine, you should use the matching initialization code


To initialize the database connection
   drop a tFdConnection on the DataModule
   in the DataModuleCreate event, add this code which essential fills the tFdConnection Params property:

Procedure TServerMethods1.DataModuleCreate(SenderTObject)                                      ; //
  Begin
    // o o o

    With FdConnection1 Do
    Begin
      Close;
      With Params Do
      Begin
        Add('DriverID=IB');

        Add('Server=localhost');

        Add('Database= C:\prog\delphi_6\2014_xe7\13_mobile_pos\_data\Mastapp_25.fdb');

        Add('User_Name=SYSDBA');
        Add('Password=masterkey');
      End// with Params do

      LoginPrompt:= False;

      Open;
    End// with FdConnection1
  End// DataModuleCreate




Note that
  • the connection cannot be tested, but we will create a Server Method to test it


Finally, we add a tDataSet
   drop a tFdQuery on the DataModule, rename it fd_customer_query, and in it's Sql property type

 
SELECT custno, company
  FROM customer


2.7 - Test of the Database Connection

The easiest way to test the connection and the query is to build a DataSnap function :
   in the u_server_methods unit, in the tServerMethods Class add the following definition:

Function f_test_connectionstring;

   type Shift Ctrl C to create the implementation and fill the body:

Function TServerMethods1.f_test_connectionstring;
  Begin
    FDConnection1.Open;
    If FDConnection1.Connected
      Then Begin
          Result:= 'connected';

          FdQuery1.Open;
          If FDQuery1.Active
            Then Result:= Result' fdQuery Open'
            Else Result:= Result' *** fdQuery Closed';
        End
      Else Result:= '*** connection pb';
  End;

   run, start the server, open the browser, click on the function invoker link, open the TServerMethod, click on the "+" before f_test_connection, click "EXECUTE"

   the database is connected and the query opened:

datasnap_server_test_firedac_connection



2.8 - The Server Method returning a tDataSet

The Client will ask for the CUSTOMER dataset. This resource will be sent over as the result of a DataSnap function.

To marshall (send over as a stream) the dataset, we will use the Json formatting. Our CUSTOMER ( CUST_NO, COMPANY ) table will be formatted as :

json_serialization

For the illustration, we indented the JSON text, but usually it does not contain spaces (the punctuation is enough to parse the format)  :

json_no_spaces



This dataset formatting will be performed by FireDac components :
   select the u_web_module
   from the Tool Palette
  • select a tTFDStanStorageBin and drop it on the module
  • select a tFDStanStorageJSON and drop it on the module


Now we will add the DataSnap Method:
   select the u_server_methods datamodule
   in the tTServerMethods1 Class, add this function :

Function f_c_get_customer_name_listTFDJSONDataSets;

   since the tFDJSONDataSets is unknown (the red wriggles), add the Data.FireDACJSONReflect to the Interface Uses clause:

   with the cursor between Class and End, press Shif Ctrl Click to create the function body, and type this code:

Function TServerMethods1.f_c_get_customer_name_listTFDJSONDataSets;
  Begin
    // -- Clear active so that query will reexecute.
    fd_customer_query.Active := False;

    Result := TFDJSONDataSets.Create;

    // -- convert the Query Answer Set into a JSON DataSet
    TFDJSONDataSetsWriter.ListAdd(Resultfd_customer_query);
  End// f_c_get_customer_name_list




To explain the function content :

  • FireDac has a tFDJSONDataSetsWriter Class with a ListAdd Class function.
  • we can add one or several queries to this Class
  • when requested, the writer will convert the queries Answer Sets into the tFDJSONDataSet using RTTI (Reflection, in Java parlance).


2.9 - Keep the Server running

So the server is running. To be able to import the tDataSet in a Rest client, the server must be running. So start the server with "Run Without Debugging", or alternately from the File Explorer (the .EXE is located in Win32\Debug\ path (relative to the .DPR)




3 - The Desktop VCL Rest Client

3.1 - The first Client

We will start with a standard Windows VCL Client.

To build the client
   launch Delphi, and select "File | New | Vcl Application"
   a standard VCL application is created
   save the project and unit in a Client_VCL\ folder under u_client_vcl and p_13_rest_vcl_client
   run the project to make sure everything is ok


3.2 - The Client Rest import unit

To build the Rest import unit
   make sure the Rest Server is running. The server will be called by the Delphi IDE to import the classes and method definitions
   add the import units by selecting "File | New | Other | DataSnap Server"
   the Rest components are displayed. Since we are not creating a DataSnap server (we started a standard VCL project), the dialog now presents the DataSnap client units:

datasnap_server_test_firedac_rest_client_module

   select the "DataSnap Rest Client Module" and click "Ok"
   the wizard asks to select the local or remote server

use_local_server

   for this VCL client located on the same PC, we keep the "Local Server" and click "Next"
   the wizard asks what kind of server to use

use_rest_server_kind

   we keep the "DataSnap Stand Alone Server" and click "Next"
   the wizard asks what the connection parameters are :

datasnap_rest_client_connection_parameters

   we change the port to 8081 and click "test"
   the parameters are correct

test_client_connection_parameters

   we click "Ok" and "Finish"
   the wizard has created the two import units.


The import units are
  • the ClientClassesUnit1 which generates the DataSnap methods proxies
  • the ClientModuleUnit1 tDataModule with a TDSRestConnection component which will be uses as a TCP client


Rename both units
   change ClientClassesUnit1 into u_client_classes
   add Data.FireDACJSONReflect to the Interface Uses clause
   change ClientModuleUnit1 into u_client_datamodule. In this Unit, replace ClientClassesUnit1 with u_client_classes
   Run the client and close it


And we must also add the FireDac JSON components to unmarshall the dataset:
   select the u_client_datamodule form
   from the Tool Palette
  • select a tTFDStanStorageBin and drop it on the module
  • select a tFDStanStorageJSON and drop it on the module


3.3 - Fetch the Customer from the Client

To get the CUSTOMER ( CUSTNO, COMPANY ) from the Server, we must
  • add a tDataSet to the client Form. In this case a memory dataset, since the content will be provided by the Rest Server
  • call the f_c_get_customer_name_list Function to fill the dataset.
Therefore, for the memory dataset ;
   select the u_client_form
   from the Tool Palette, select a tFdMemTable, drop it on the form and rename it fd_customer_list_dataset
and for the filling procedure ;
   add the u_server_methods to the Implementation Uses clause
   also add the Data.FireDACJSONReflect which contains the TFDJSONDataSets Class
   in the Private section of tForm1 Class, add this Procedure declaration  :

Procedure get_customer_name_list;

   create the body (Shift Ctrl C) and type its content:

Procedure TForm1.get_customer_name_list;
  Var l_c_json_dataset_listTFDJSONDataSets;
  Begin
    // -- clear the dataset
    fd_customer_list_dataset.Close;
    // -- fetch the dataset list from the Rest Server
    l_c_json_dataset_list := ClientModule1.ServerMethods1Client.f_c_get_customer_name_list;
    // -- read the first dataset of this list into the memory dataset
    fd_customer_list_dataset.AppendData(
      TFDJSONDataSetsReader.GetListValue(l_c_json_dataset_list, 0));

    // -- opent the customer_list dataset
    fd_customer_list_dataset.Open;
  End// get_customer_name_list

   compile the project (or


This procedure will be called from a tButton, and the result of the dataset displayed. Since the tFdMemDataSet is a tDataSet descendent, it is compatible with a tDataSource / tDbGrid. Therefore
   from the Tool PalEtte
  • drop a tDataSource component and set its DataSet to fd_customer_list_dataset
  • drop a tDbGrid component and set its DataSource property to DataSource1
   drop a tButton, fetch the data and display it in the grid :

Procedure TForm1.display_customer_list_Click(SenderTObject);
  Begin
    get_customer_name_list;
  End// display_customer_list_Click

   run the Client and click display_customer_list_
   here is the glorious result :

rest_server_and_rest_client




4 - The Mobile Rest Client

4.1 - The SmartPhone DataSnap Rest Client

We will now add another Client which will run on a mobile device.

We will demonstrate the case with an Android Smartphone (Samsung Nexus), but any other Android phone or tablet, or Apple iPhone or tablet would work, thanks to the "single code base" Delphi principle.



4.2 - The Mobile check

This is not a paper about starting with Delphi Mobile programming.

However just a couple of reminders

  • check your Java pathes. We installed Delphi Xe7 asking to install the Java files. So they were all downloaded and copied into
    • Program Files\ for the Java SDK
    • C:\Documents and Settings\All Users\Documents\Embarcadero\Studio\15.0\PlatformSDKs
      for the Android SDK and NDK
    You can check that Delphi can use those files with "Tools | Options | Environment Options | SDK Manager", where no combo should display a yellow triangle warning
  • to deploy the Delphi build ARM code, the smartphone must be linked to the PC using an USB cable. When this cable is plugged into the PC, the phone asks whether you want to use USB debugging, which you should accept


4.3 - Testing the Android WIFI and connection

4.3.1 - WIFI connection

Our smartphone uses WIFI to connect to our DataSnap Rest Server (USB for deployment, but WIFI for execution).

Before diving into the FireMonkey code, it is important to test the connection.



Our REST Server and REST client behave in a classical Client / Server mode:

  • the Server is started at some known IP, Port
  • the Client connects to this fixed IP, Port, and sends some request
  • the Server sends some answer back


4.3.2 - Assigning a Fixed IP to the Server

The first thing is to assign a fixed IP, Port to the Server.

Our server is located on a PC linked to a switch. This switch has a manager which

  • displays the WIFI password
  • contains a DHCP server which allocates the devices IP.
The manager can be used to freeze the IP of one or several connected devices. The other devices are allocated random IPs.

So we froze the Server IP, setting its value to 198.168.1.14.

We can check this using IPCONFIG :

rest_server_ip

The Port of our REST Server application was set when we created this Server with Delphi. It is 8081.



4.3.3 - Adding the Android device to the network

The mobile device must be hooked to the WIFI network:
  • the switch WIFI manager has a dialog displaying its WIFI parameters. In our case
    • security WPA etc
    • password : some value, in our case a 26 byte Hex number, like 439F5 etc
  • we typed this password on the Android device, by selecting "settings | WIFI", in the WIFI servers in the area, selecting our WIFI network, and then entering the WIFI password


4.3.4 - Testing the Android connection

Knowing the Client IP is not important. We can optionnally check it
  • the Switch displays the IP of all WIFI connected devices. In our case the dynamically assigned IP was 192.168.1.20
  • we can also display the WIFI information including the device's IP on the smartphone itself. Those WIFI settings can be tested by a Delphi application using JNI (Java Native Interface) Classes. This not obvious, but Stack Overflow and Google allowed us to find the solution. Here is our Delphi Android app displaying the Android IP:

    ping_android_mobile_2

Knowing the Client IP, we can PING the device from the switch or from the Server;

ping_android_mobile



4.3.5 - Testing the TCP Connection

The most important is to be check that the Client can connect to the Server and send some requests.

This test was performed using a tIdTcpClient which tries to Connect.



We used a small FireMonkey project with a tIdTcpClient, 2 edits, a button and a memo. Here is the main FireMonkey form code :

Unit u_test_android_connection_to_server;
  Interface
    Uses System.SysUtils, ...
        , IdBaseComponentIdComponentIdTCPConnectionIdTCPClient
        ...
        , FMX.Edit;

    Type TForm1 =
           Class(TForm)
             Memo1TMemo;
             connect_TButton;
             IdTCPClient1TIdTCPClient;
             ip_edit_TEdit;
             port_edit_TEdit;
             Procedure connect_Click(SenderTObject);
           Private
           Public
          End;

    Var Form1TForm1;

  Implementation

    {$R *.fmx}
    {$R *.NmXhdpiPh.fmx ANDROID}

    Procedure display(p_textString);
      Begin
        Form1.Memo1.Lines.Add(p_text);
      End// display

    Procedure TForm1.connect_Click(SenderTObject);
      Var l_ip_portString;
      Begin
        With IdTCPClient1 Do
        Begin
          Host := ip_edit_.Text;
          Port:= StrToInt(port_edit_.Text);
          ConnectTimeout := 2000; //2 secs
          l_ip_port:= Format('Host %s : %s is ',
                [ip_edit_.Textport_edit_.Text]);
          Try
            Connect;
            display(l_ip_port'reachable');
          Except
            On eException Do
              display('*** 'l_ip_port'UNreachable');
          End;
          Disconnect;
        End// with IdTCPClient1
      End// connect_Click

    End



And here is the successful result :

test_android_connection_to_rest_server



4.4 - The FireMonkey DataSnap REST Client

4.4.1 - The steps

We must
  • run the server
  • create the FireMonkey application
  • add the DataSnap method import units
  • write the user code


First, make sure the Rest Server is running. The server will be called by the Delphi IDE to import the classes and method definitions.



The FireMonkey client application steps are quite similar to the VCL client applications steps, with a couple of differences

  • we must assign the Server IP
  • to display the data, we cannot use a tDbGrid which is Windows VCL specific, but will use FireMonkey controls


4.4.2 - The FireMonkey application

To create the FireMonkey REST Client :
   start Delphi
   select "File | New | Multi Device Application"
   the FireUi multi device dialog is presented

the_multi_device_designer

   select "Blank Application" and click "Ok"
   the Windows Master page of the multi designer is displayed

multi_designer_master_page

(both left and right panel have been shrinked to display the central Master / View part)

   select the "Views" combo, display the available formats, and select "
   all available form factors are presented

select_designer_view

   select the "Android 4" form factor (or whatever view matching your Android device)
   the phone look is displayed :

android_4_inch_phone_view

   select "File | Save Project As", create a folder client_Firemonkey\  and type
  • u_android_client
  • zz_pos_client
We use the "zz_" prefix for our Delphi Android projects to display them at the end of the Android Application pages


To allow Delphi to send the Android ARM executable to the mobile device, connect the device to your developpment machine :
   connect the USB cable of your android device to the Delphi PC
   the android device asks you to confirm the debug mode : "Allow Usb Debugging ?"

   touch "ok"
   the status line displays the debugging icon

android_accepts_debugging

   brushing the finger down on the status line confirms this debugging mode :

android_accepts_debugging

   and the phone is displayed in the windows explorer

smartphone_connected_to_the_pc

Delphi can now deploy any FireMonkey to the Android device



Check that everything is Ok by running the project
   drop a Button on the Form
   select the Android platform. In the project

select_the_android_target_platform

   run the application (F9)
   the compiler will display "compiling, linking, deploying, stripping symbols, packaging, installing"

compiling_the_android_project

If the device is active, you will see our page with Button1. If the phone is in sleep mode, pushing "Power" will display the same thing.

compiling_the_android_project

and the application is at the end of the application pages:

android_applications



4.4.3 - The REST Client application

We now add the REST import units:
   make sure the Rest Server is running. The server will be called by the Delphi IDE to import the classes and method definitions
   add the import units by selecting "File | New | Other | DataSnap Server"
   the Rest components are displayed. Since we are not creating a DataSnap server (we started a standard VCL project), the dialog now presents the DataSnap client units:

datasnap_server_test_firedac_rest_client_module

   select the "DataSnap Rest Client Module" and click "Ok"
   the wizard asks to select the local or remote server

use_local_server

   for this FireMonkey client connected by WIFI, select the "Remote Server" and click "Next"
   the wizard asks what kind of server to use

use_rest_server_kind

   we keep the "DataSnap Stand Alone Server" and click "Next"
   the wizard asks what the connection parameters are :

http_rest_client_remote_server_parameters

   we type the 192.168.1.14 IP and change the port to 8081. Then click "test"
   the parameters are correct

test_the_rest_server_connection

   we click "Ok" and "Finish"
   the wizard has created the two import units.


Rename both units
   change ClientClassesUnit1 into u_client_classes
   add Data.FireDACJSONReflect to the Interface Uses clause
   change ClientModuleUnit1 into u_client_datamodule. In this Unit, replace ClientClassesUnit1 with u_client_classes
   compile the client by typing Shift F9 (or "Project | Build")


Please note :
  • the Android cross-compilation and Android deployment is rather lengthy. This is the reason we used "Build" rather than "Run"
  • even then, the compiling is still longer then the Windows compilation. It is possible to return to the Windows target platform until the project is complete, and then shift back to the Android target platform
  • as far as the import units are concerned, we could have copied the VCL DataSnap import unit, and changed the DsRestConnection1 host address. We can even test the connection by double clicking on this component :

    modifying_the_vcl_import_units



4.4.4 - Add the FireDac JSON components

And we must also add the FireDac JSON components to unmarshall the dataset:
   select the u_client_datamodule form
   from the Tool Palette
  • select a tTFDStanStorageBin and drop it on the module
  • select a tFDStanStorageJSON and drop it on the module


4.5 - Fetch the Customer from the Client

We now must add a tDataSet to the client Form. Therefore :
   select the u_android_client_form
   from the Tool Palette, select a tFdMemTable, drop it on the form and rename it fd_customer_list_dataset
To test the fetching of the data, we first will use a Memo :
   drop a tMemo on the tForm
   write the display global Procedure which simply adds the text to the Form's Memo1 :

Procedure display(p_textString);
  Begin
    Form1.Memo1.Lines.Add(p_text);
  End// display


and for the filling procedure :
   add the u_server_methods to the Implementation Uses clause
   also add the Data.FireDACJSONReflect which contains the TFDJSONDataSets Class
   in the Private section of tForm1 Class, add this Procedure  :

Procedure get_customer_name_list;

   create the body (Shift Ctrl C) and type its content:

Procedure TForm1.get_customer_name_list;
  Var l_c_json_dataset_listTFDJSONDataSets;
  Begin
    // -- clear the dataset
    fd_customer_list_dataset.Close;
    // -- fetch the dataset list from the Rest Server
    l_c_json_dataset_list := ClientModule1.ServerMethods1Client.f_c_get_customer_name_list;
    // -- read the first dataset of this list into the memory dataset
    fd_customer_list_dataset.AppendData(
      TFDJSONDataSetsReader.GetListValue(l_c_json_dataset_list, 0));

    // -- opent the customer_list dataset
    fd_customer_list_dataset.Open;
  End// get_customer_name_list

Both the definition and the body can be copied from the VCL u_client_form Unit

   drop a tButton and write the code which fetches the data and displays it in the tMemo :

Procedure TForm1.display_customer_list_Click(SenderTObject);
  Begin
    get_customer_name_list;

    With fd_customer_list_dataset Do
      While Not Eof Do
      Begin
        display(Format('%6s %s',
            [ Fields[0].AsStringFields[1].AsString ] ));
        Next;
      End// while not Eof
  End// display_customer_list_Click

   for the first test, change the platform target to "Windows" ("Project Manager | zz_pos_project | Target Platform"
   run the VCL project
   this is the output :

firemonkey_windows_client



Note that the form has been displayed in "Windows Format" (not the phone width and height).



4.6 - Bind the DataSet to Visual Controls

Our memo display was just to check the fetching.

For production applications, we will use FireMonkey controls, and bind them visually to the tFdMemDataSet.



To bind the tFdMemDataSet fields to the columns of the tListView, we must create dataset fields. Creating tFields is difficult at design time, but creating FieldDefs is possible

To create the tFieldDefs
   select the tFdMemDataSet
   in the Object Inspector, select the FieldDefs property and click on the Ellipsis ( ... )

add_firedac_dataset_fielddefs

   this is a standard sub-item editor

firedac_fielddefs_editor

   add a tFieldDefs by clicking on the top-left yellow icon "Add New"
   a new tFieldDef has been created and is displayed in the Object Inspector
   change the Name property into CUSTNO
   do the same for the second tFieldDef, with name COMPANY
   here is the result :

firedac_adding_fieldefs



Now add a tListView and bind it :
   drop a tListView on the form
   select the "Detail" listview, by selecting in the Object Inspector "Listview | ItemAppearance | ListItemRightDetail"

firemonkey_listview_appearance

The listview will display a .INI kind of two "key-value" columns list

   open the LiveBindings Designer by selecting "View | LiveBindings Designer" (or by selecting the ListView1, selecting it's LiveBindings

livebindings_designer

   the LiveBindings Designer is opened, displaying all the bindable objects

customer_dataset_and_listview

   bind "Listview Item.Text" to "MemDataSet CUSTNO" (drag the mouse from the Item.Text to the CUSTNO
   bind the Item.Detail to the COMPANY
   this is the current situation :

bind_customer_dataset_and_listview

   run the (VCL) project and click on the Button
   opening the DataSet automatically displays the values in the ListView :

firemonkey_rest_client_result



Please note

  • in our Delphi XE7 version, the LiveBindings Designer would not display the tFdMemDataSet fieldefs we just created. Closing the app and reloading it did the trick.


4.7 - Run on the Android Mobile Device

Since the syntax is correct, we will now adjust to Android.

Therefore
   change the Designer view back to "Android 4 inches"

android_4_inches_phone_view

   change the Target Platform to "Android"

android_target_platform

   run the application
   this is the result

android_customer_display



Please note

  • in our case, the "Run" took between 2 and 3 minutes. This is the reason why we preferred to first run in Windows mode
  • let's stress again that it is the "single code base" which allowed us to code in Windows Mode and then run in Android mode
    • we also encountered a "F2039" error, because we added an incorrect second "]" in the Format of the fields
    • our display is quite primitive, to say the least. For customer applications, we would resize the controls, add shading, more sophisticated controls etc.



5 - Remarks



The FireUi Multi device Designer

"File | New | Multi Device Application" automatically calls the multi device designer. This wizard works with a "Master View" which contains the common elements of the design, and one or several device specific views. This explains why

  • properties like the Name of controls are in the Master View, and to change a Name, we must switch to the Master View
  • if we move the controls in the Master View, the changes are not propagated to the other Views. Each view manages the graphic properties. Those properties are saved in a separate .DFM, named called in our case "u_android_client_form.NmXhdpiPh.fmx".

    This .DFM obviously contains the delta values :

     
    Inherited Form1_NmXhdpiPhTForm1_NmXhdpiPh
      ClientHeight = 615
      ClientWidth = 400
      DesignerMasterStyle = 0
      Inherited display_customer_list_TButton
        Size.Width = 225.000000000000000000
      End
      Inherited customer_memo_TMemo
        Size.Width = 385.000000000000000000
        Size.Height = 73.000000000000000000
      End
      Inherited ListView1TListView
        Size.Width = 385.000000000000000000
        Size.Height = 457.000000000000000000
      End
      Inherited fd_customer_list_datasetTFDMemTable
        Left = 264
      End
      Inherited BindSourceDB1TBindSourceDB
        Left = 136
        Top = 64
      End
      Inherited BindingsList1TBindingsList
        Left = 36
        Top = 61
      End
    End

  • we think that this multi-device designer is a good tool for similar size devices, but for building applications targeted to phones, tablets and desktop devices, the design will have to be separated

  • for our first Android trials we used the Windows Target Platform because of round trip speed. Of course the resulting FireMonkey Windows layout is not adapted. The goal was NOT to build a desktop application. When the code was working as expected, we switched to the Android Platform.

    This also demonstrate that the Design Views and the Target Platform are two separate concerns. In the end, however, we selected at the same time an Android view and an Android target platform



5.1 - What's Next ?

We presented the case of a simple table in read-only mode.

There are many possible extensions and improvements :

  • add an "update" function. The user can enter data which is then sent back to the Server
  • operate on many table : our single table example is quite simplistic. At least we should be able to use Master / Detail relationships
  • in a multi user environment, we should take care of the threads
  • finally a Business Object organization can simplify the Business Rules handling.


5.2 - What kind of devices / applications

We simply sketched the structure of a POS software. The detailed content of the applications depends on the specific application. It could be as simple as retrieving a list of products or as complex as a full fledged ERP. In the case of more complex software, the big applications like accounting or order processing would be handled on desktop PC's, not on mobile devices. Those PCs can still be connected via WIFI, an can use REST and FireMonkey. But the benefit is less obvious than for hand held mobile devices.



And different devices can be used for different kind of applications

  • phones for simple requests / entries with simple screens
  • tablets for more elaborate reports, computations, advertising or guides
  • desktop devices for application with richer screen designs : cash register, accounting etc


In the case of Point of Sale software, FireMonkey is particularly well suited. Il allows attractive simple touch screen design for applications like restaurant ordering, cash register, payment terminal etc



5.3 - The Datasnap Wizards

Special congratulations to Delphi (Embarcadero ? Idera ?) for the two DataSnap wizards. Without them it would be nearly impossible to set up the REST Servers and Clients. Not only for the components and their links, but also for the generated code.



5.4 - Proxy Generation

As with SOAP Web Services, the Server Application uses some objects (a tDataSet in our case) and this object's data has to be sent over to the Client. The general solution is to build a proxy object on the Client side. Then
  • the DataSnap framework generates the units containing the proxies
  • at run time, the Client works with the proxy, and the REST Client communicates with the REST Server to get information or other objects from the Server.
  • the code of the proxy was completely handled by the DataSnap framework. All we had to do was call the method to retrieve and instantiate the client proxy.
We used a tDataSet object, and FireDac handled the marshalling using JSON. If we switch to Business Objects, DataSnap directly creates the proxies, and data can be exchanged using JSON.



5.5 - The article title

Could we add other buzzwords to our "Using FireBase, FireDac, DataSnap, Rest, Wifi, and FireMonkey" title ? No problem here, what about "Android, LiveBindings, JSON, FireUi, JNI, DHCP, WebBroker". Well, piling up buzzwords was not the objective of this paper, and does not guarantee readership. As this story demonstrates: a survey showed that the three best selling books were about Abraham Lincoln, doctors and dogs. So one feller wrote a book "Dr Lincoln's dog". Which was a total flop.




5.6 - References

About REST web services :
  • the Delphi WIKI contains several tutorials and demos about REST services
  • Pawel GLOWACKI wrote a series of 11 Delphi Labs: DataSnap code samples, ranging form a simple calculator to authentication and callbacks


For displaying the Smarphone screen :
  • we used Jim MCKEETH screen grabber Android Screen View.
    As explained in the "readme", it simply uses the ADB (Android Debug Bridge) to fetch the screen image and display in the grabber's Form.


For testing the WIFI connection
  • those tests involve JNI (Java Native Interface). We used two sources
  • Remy LEBEAU wrote about Delphi XE6 and Android Ping
    Whenever we had any Tcp / Ip problem, this is the man who always provided, in Embarcadero's forums or StackOverflow, an answer, usually with a piece of code which solved the problem.
  • also many thanks to Alexis FRUHINSHOLZ, co founder of SocialCompare, who helped us with the WIFI connection diagnostic tools



6 - Download the Sources

Here are the source code files:

The .ZIP file(s) contain:

  • the main program (.DPR, .DOF, .RES), the main form (.PAS, .DFM), and any other auxiliary form
  • any .TXT for parameters, samples, test data
  • all units (.PAS) for units
Those .ZIP
  • are self-contained: you will not need any other product (unless expressly mentioned).
  • for Delphi 6 projects, can be used from any folder (the pathes are RELATIVE)
  • will not modify your PC in any way beyond the path where you placed the .ZIP (no registry changes, no path creation etc).
To use the .ZIP:
  • create or select any folder of your choice
  • unzip the downloaded file
  • using Delphi, compile and execute
To remove the .ZIP simply delete the folder.

The Pascal code uses the Alsacian notation, which prefixes identifier by program area: K_onstant, T_ype, G_lobal, L_ocal, P_arametre, F_unction, C_lasse etc. This notation is presented in the Alsacian Notation paper.



As usual:

  • please tell us at fcolibri@felix-colibri.com if you found some errors, mistakes, bugs, broken links or had some problem downloading the file. Resulting corrections will be helpful for other readers
  • we welcome any comment, criticism, enhancement, other sources or reference suggestion. Just send an e-mail to fcolibri@felix-colibri.com.
  • or more simply, enter your (anonymous or with your e-mail if you want an answer) comments below and clic the "send" button
    Name :
    E-mail :
    Comments * :
     

  • and if you liked this article, talk about this site to your fellow developpers, add a link to your links page ou mention our articles in your blog or newsgroup posts when relevant. That's the way we operate: the more traffic and Google references we get, the more articles we will write.



7 - The author

Felix John COLIBRI works at the Pascal Institute. Starting with Pascal in 1979, he then became involved with Object Oriented Programming, Delphi, Sql, Tcp/Ip, Html, UML. Currently, he is mainly active in the area of custom software development (new projects, maintenance, audits, BDE migration, Delphi Xe_n migrations, refactoring), Delphi Consulting and Delph training. His web site features tutorials, technical papers about programming with full downloadable source code, and the description and calendar of forthcoming Delphi, FireBird, Tcp/IP, Web Services, OOP  /  UML, Design Patterns, Unit Testing training sessions.
Created: sep-16. Last updated: sep-16 - 107 articles, 228 .ZIP sources, 1174 figures
Copyright © Felix J. Colibri   http://www.felix-colibri.com 2004 - 2016. All rigths reserved
Back:    Home  Papers  Training  Delphi developments  Links  Download
the Pascal Institute

Felix J COLIBRI

+ Home
  + articles_with_sources
    + database
    + web_internet_sockets
      – tcp_ip_sniffer
      – socket_programming
      – socket_architecture
      – simple_web_server
      – simple_cgi_web_server
      – cgi_database_browser
      – whois
      – web_downloader
      – web_spider
      – rss_reader
      – news_message_tree
      – indy_news_reader
      – delphi_web_designer
      – intraweb_architecture
      – ajax_tutorial
      – bayesian_spam_filter
      + asp_net
      – mobile_pos_software
    + oop_components
    + uml_design_patterns
    + debug_and_test
    + graphic
    + controls
    + colibri_utilities
    + colibri_helpers
    + delphi
    + firemonkey
    + compilers
    + vcl
  + delphi_training
  + delphi_developments
  + sweet_home
  – download_zip_sources
  + links
Contacts
Site Map
– search :

RSS feed  
Blog