Delphi Clinic C++Builder Gate Training & Consultancy Delphi Notes Weblog Dr.Bob's Webshop
Dr.Bob's Delphi Notes Dr.Bob's Delphi Clinics
 Using App Tethering to enable CodeSite for Tracing Mobile Apps
See Also: Delphi Papers and Columns

One of the major new features of Delphi was the support for App Tethering, which basically means the option to interact between two applications that can exist on different devices (but must be connected on the same subnet, so not over the internet and as of Delphi XE7 also using Bluetooth).

With App Tethering we can discover other Delphi applications that support app tethering running on the same device or on other connected devices on the same subnet (or on the same machine or device).

Once we are connected to another device, we can share data between applications (standard data types and streams) and execute actions that are shared by an application using App Tethering. We can use passwords to ensure that not everyone can execute these actions using App Tethering (although it still only works on the same subnet, we should ensure that not everyone can access the data or execute actions without permission).

App Tethering Logging
One of the way in which I've used App Tethering myself is in order to allow my (mobile) apps to send log messages using CodeSite. You can download the full source code here.

Start a new Delphi VCL application, save it as CodeSiteAppTetheringManager. Optionally, we can place a TMemo on the form, to hold the last message that was sent to the CodeSite App Tethering Manager.

Now, place a TTetheringManager componenton a form, with the following properties:

object TetheringManager1: TTetheringManager
  Password = 'geheim'
  Text = 'CodeSiteManager'
  AllowedAdapters = 'Network'
end
This means that we'll require an AppTethering connecting app over the network, using password "geheim" to connect to our CodeSiteManager

Also place a TTetheringAppProfile component on the form, with the following properties set:

object TetheringAppProfile1: TTetheringAppProfile
  Manager = TetheringManager1
  Text = 'CodeSiteProfile'
  Group = 'eBob42'
  OnResourceReceived = TetheringAppProfile1ResourceReceived
end
This means that the CodeSiteProfile belongs to the group "eBob42", is connected to the TetheringManager, and response to the OnResourceReceived event.

In the event handler, we can handle the incoming message as follows:

implementation
uses
  CodeSiteLogging;

{$R *.dfm}

procedure TForm1.TetheringAppProfile1ResourceReceived(
  const Sender: TObject;
  const AResource: TRemoteResource);
begin
  Memo1.Text := DateTimeToStr(Now) + #13#10 + AResource.Value.AsString;
  CodeSite.Send(AResource.Value.AsString)
end;
This means that any resource message received will be sent to CodeSite (and also shown in the Memo field, including the current date and time of the message).

If we run this CodeSiteAppTetheringManager application, we can allow any application that runs on the network (including mobile devices that contain apps that we need to test and debug), to send log messages to this CodeSiteAppTetheringManager, so we can see these messages show up in the CodeSite LiveViewer (or Logfile viewer).

To create a client that can connect to the CodeSiteAppTetheringManager, we need a data module (called DataModErrorTethering) with three components: a TTetheringManager, a TTetheringAppProfile and (for VCL clients only) a TApplicationEvents component:

The individual components are configured as follows:

object DataModErrorTethering: TDataModErrorTethering
  object TetheringManager1: TTetheringManager
    Password = 'geheim'
    Text = 'TetheringManager1'
    AllowedAdapters = 'Network'
  end
  object TetheringAppProfile1: TTetheringAppProfile
    Manager = TetheringManager1
    Text = 'TetheringAppProfile1'
    Group = 'eBob42'
  end
  object ApplicationEvents1: TApplicationEvents
  end
end
The ApplicationEvents component is the one that can respond to unhandled exceptions (the ones that I want to send to CodeSite), by responding to the OnException event handler, implemented as follows:
procedure TDataModErrorTethering.ApplicationEvents1Exception(
  Sender: TObject; E: Exception);
var
  i: Integer;
begin
  for i:=0 to TetheringManager1.RemoteProfiles.Count-1 do
    TetheringAppProfile1.SendString(TetheringManager1.RemoteProfiles[i],
      E.Classname, E.Message);
end;
This basically means that it looks through all connected remote profiles, and sends the exception classname and messages to all connected CodeSiteAppTetheringManagers that it could connect to. Note that for Mobile Clients, you need to hook the OnException event handler in a slightly different way, which is demonstrated in my Delphi Mobile Development courseware manual.

In order to make all this work, however, we do need to implement the necessary task to connect the data module to any (or all) CodeSiteAppTetheringManagers.

A procedure RegisterTethering is added to the public section of the data module, so the application that includes this data module can call the RegisterTethering method (for example in the OnCreate event) in order to register it. Inside this method, we should make sure to Unpair any existing managers that may exist, and then start the discovery process of AppTethering Managers.

procedure TDataModErrorTethering.RegisterTethering;
var
  i: Integer;
begin
  for i:=TetheringManager1.PairedManagers.Count-1 downto 0 do
    TetheringManager1.UnPairManager(TetheringManager1.PairedManagers[i]);

  TetheringManager1.DiscoverManagers;
end;
We also need to implement three event handlers of the TetheringManager: OnEndManagerDiscovery, OnEndProfilesDiscovery, and OnRequestManagerPassword.
procedure TDataModErrorTethering.TetheringManager1EndManagersDiscovery(
  const Sender: TObject;
  const ARemoteManagers: TTetheringManagerInfoList);
var
  i: Integer;
begin
  for i:=0 to ARemoteManagers.Count-1 do
    if ARemoteManagers[i].ManagerText = 'CodeSiteManager' then
      TetheringManager1.PairManager(ARemoteManagers[I])
end;
In the OnEndManagerDiscovery, we verify that the TetheringManager has the text CodeSiteManager (we only want to connect to CodeSite AppTethering Managers at this time), and if so, we'll pair the managers.

In the OnEndProfilesDiscovery, we'll connect our TetheringProfile to the remote profiles from the TetheringManagers, so we can send messages from our profile to the others.

procedure TDataModErrorTethering.TetheringManager1EndProfilesDiscovery(
  const Sender: TObject;
  const ARemoteProfiles: TTetheringProfileInfoList);
var
  i: Integer;
begin
  for i:=0 to TetheringManager1.RemoteProfiles.Count-1 do
    TetheringAppProfile1.Connect(TetheringManager1.RemoteProfiles[i])
end;
And finally, in the OnRequestManagerPassword, we need to pass the password that the remote CodeSiteAppTetheringManager expects from us: "geheim".
procedure TDataModErrorTethering.TetheringManager1RequestManagerPassword(
  const Sender: TObject; const ARemoteIdentifier: string;
  var Password: string);
begin
  Password := 'geheim';
end;
This will ensure that the calling demo application (with this data module included) can actually connect to the CodeSiteAppTatheringManager.

Now, when we run the CodeSiteAppTetheringClient application, any unhandled exception will be sent to any running CodeSiteAppTetheringManager applications that were found and connected when the client application was started. Although this is "nice" for a Windows client application, it is in fact very helpful when writing and testing / debugging mobile applications, since there is no CodeSite for Android or iOS yet, but this tethering technique will allow mobile devices to send CodeSite log messages to the manager which will forward them to the actual CodeSite Dispatcher and LiveViewer or LogViewer.

We can even extend the data module with a protected method called CodeSiteSend taking a Msg parameter of type String, to mimic the call to CodeSite.Send itself. The implementation would be similar to the exception handler, sending the CodeSite message to the CodeSite AppTethering Manager as follows:

procedure TDataModErrorTethering.CodeSiteSend(Msg: String);
var
  i: Integer;
begin
  for i:=0 to TetheringManager1.RemoteProfiles.Count-1 do
    TetheringAppProfile1.SendString(TetheringManager1.RemoteProfiles[i],
      'CodeSite', Msg);
end;
Note that this should be a private method, because the easiest way to make it available to developers is to create a class called CodeSite with a class method called Send, as follows:
type
  CodeSite = class
  public
    class procedure Send(Msg: String);
  end;
With the implementation of the CodeSite.Send method as follows:
class procedure CodeSite.Send(Msg: String);
begin
  if Assigned(DataModErrorTethering) then
    DataModErrorTethering.CodeSiteSend(Msg);
end;
Using the CodeSiteAppTetheringDataModule unit, we can call CodeSite.Send from any type of application, including mobile applications, and if they are able to connect to the CodeSiteAppTetheringManager on the local WiFi, we can send CodeSite messages from the client, and see them in the LIve Viewer or Log Viewers.


This webpage © 2000-2017 by Bob Swart (aka Dr.Bob - www.drbob42.com). All Rights Reserved.