Nr60 Blaise 60 UK Total
Nr60 Blaise 60 UK Total
Nr60 Blaise 60 UK Total
R E L A T E D L A N G U A G E S / A N D R O I D,
I O S, M A C , W I N D O W S & L I N U X
P R I N T E D, P D F, & O N L I N E V I E W
DX
CONTENTS
ARTICLES:
ADVERTISERS
ANDROID LOGCAT FREE TOOL COMPONENTS4DEVELOPERS PAGE 14
BARNSTEN PAGE 5
COMPONENTS4DEVELOPERS PAGE 44
DELPHI & PASCAL EVENTS PAGE 2
DEVELOPERS EXPERTS PAGE 22
ONLINE VIEW BLAISE PASCAL MAGAZINE PAGE 18
Wim Van Ingen Schenau -Editor Peter van der Sman Rik Smit
wisone @ xs4all.nl sman @ prisman.nl rik @ blaisepascal.eu
www.romplesoft.de
Editor - in - chief
Detlef D. Overbeek, Netherlands Tel.: +31 (0)30 890.66.44 / Mobile: +31 (0)6 21.23.62.68
News and Press Releases email only to [email protected]
Editors
Peter Bijlsma, W. (Wim) van Ingen Schenau, Rik Smit,
Correctors
Howard Page-Clark, James D. Duff
Trademarks
All trademarks used are acknowledged as the property of their respective owners.
Caveat Whilst we endeavour to ensure that what is published in the magazine is correct, we cannot accept responsibility for any errors or omissions.
If you notice something which may be incorrect, please contact the Editor and we will publish a correction where relevant.
Subscriptions ( 2013 prices )
1: Printed version: subscription € 80.-- Incl. VAT 6 % (including code, programs and printed magazine,
10 issues per year excluding postage).
2: Electronic - non printed subscription € 50.-- Incl. VAT 21% (including code, programs and download magazine)
Subscriptions can be taken out online at www.blaisepascal.eu or by written order, or by sending an email to [email protected]
Subscriptions can start at any date. All issues published in the calendar year of the subscription will be sent as well.
Subscriptions run 365 days. Subscriptions will not be prolonged without notice. Receipt of payment will be sent by email.
Subscriptions can be paid by sending the payment to:
ABN AMRO Bank Account no. 44 19 60 863 or by credit card: Paypal
Name: Pro Pascal Foundation-Foundation for Supporting the Pascal Programming Language (Stichting Ondersteuning Programeertaal Pascal)
IBAN: NL82 ABNA 0441960863 BIC ABNANL2A VAT no.: 81 42 54 147 (Stichting Programmeertaal Pascal)
Subscription department Edelstenenbaan 21 / 3402 XA IJsselstein, The Netherlands / Tel.: + 31 (0) 30 890.66.44 / Mobile: + 31 (0) 6 21.23.62.68
[email protected]
Copyright notice
All material published in Blaise Pascal is copyright © SOPP Stichting Ondersteuning Programeertaal Pascal unless otherwise noted and may
not be copied, distributed or republished without written permission. Authors agree that code associated with their articles will be made
available to subscribers after publication by placing it on the website of the PGG for download, and that articles and code will be placed on
distributable data storage media. Use of program listings by subscribers for research and study purposes is allowed, but not for commercial
purposes. Commercial use of program listings and code is prohibited without the written permission of the author.
Order directly from our shop with 10% discount on Enterprise (Upgrade) licenses and
15% on Architect (Upgrade) licenses till March 31, 2017.
www.barnsten.com .
5
Issue Nr 2 2017 BLAISE PASCAL MAGAZINE 5
M2M MESSAGING PAGE 1/4 BY BRUNO FIERENS
WITH DELPHI OR LAZARUS AND MQTT
starter expert
DX & Lazarus
Delphi Now, whenever another client sends a message
for this topic, this message will be received by
INTRODUCTION
our client and the event
MQTT is the leading machine-to-machine (m2m) TTMSMQTTClient.OnPublishReceived()
messaging protocol and comprises a lightweight
will be triggered.
publish/subscribe messaging transport, ideal for small
code footprint IoT apps. This event returns the message packet ID, the
With the TMS MQTT Client component topic the message was sent for and the payload
http://www.tmssoftware.com/site/tmsmqtt.asp
(actual message data). The payload is an array of
we developed, it now just became extremely easy to
bytes holding the message content. This simple
tap into the power of m2m and introduce messaging
based communication between IoT devices, event handler will extract the message as string:
mobile devices, desktop & web applications procedure TForm1.TMSMQTTClient1PublishReceived(
(thanks to websocket). Our TMS MQTT Client can be ASender: TObject;
used from VCL, FMX and LCL applications, thus can APacketID: Word; ATopic: string;
target Windows, macOS, iOS, Android, Linux, APayload: TArray<system.byte>);
Raspbian. var msg: string;
begin
msg := TEncoding.UTF8.GetString(APayload);
CONCEPTS end;
MQTT is a publish/subscribe messaging
transport protocol. Messages can contain any Now, this is all to get the receiving side working.
type of data: binary or text. MQTT messages are Sending the message is equally simple. You can
routed via a broker. just call the TTMSMQTTClient.Publish() method
A broker is responsible for delivering the right which offers overloads for sending a text
message to the right clients. message or a binary message
A client can send a message for a given topic
and a client can inform the broker it is listening TMSMQTTClient1.Publish(
to a topic or range of topics by using wildcards. 'tms/mytopic', 'my first MQTT text message');
For full information about the MQTT protocol, A chat between mobile and desktop applications
see: http://docs.oasis-open.org/mqtt/ Let's put this knowledge together and implement
mqtt/v3.1.1/os/mqtt-v3.1.1-os.html
an MQTT based chat client that will allow us to
Our TMS TTMSMQTTClient implements setup a chat between a mobile device app and a
the full MQTT protocol specification, including desktop app. We'll create a FireMonkey
the keep alive messages, auto reconnect, quality application for this so we can use exactly the
of service settings, … same code for the mobile app as for the desktop
Note that a broker is crucial and several free and
app. For this sample, we'll use the (free)
commercial brokers exist. An overview of Mosquitto MQTT test broker so we do not need to
brokers can be found here: install an MQTT broker ourselves.
https://github.com/mqtt/mqtt.github.i
o/wiki/servers
To connect to the broker and subscribe for the
GETTING STARTED
topic 'tms/chat' in this case, following code is
Setup is not more than dropping the
used:
TTMSMQTTClient component on the form and
defining the broker via begin
TMSMQTTClient1.BrokerHostName and call TMSMQTTClient1.BrokerHostName :=
TTMSMQTTClient.Connect. 'test.mosquitto.org';
When a successful connection could be made to TMSMQTTClient1.Connect();
the broker, the end;
TTMSMQTTClient.OnConnectedStatusChang
procedure
ed() event is triggered.
TForm1.TMSMQTTClient1ConnectedStatusChanged(
In this event, we can subscribe to a topic or Asender: TObject;
range of topics: const AConnected: Boolean;
procedure Astatus: TTMSMQTTConnectionStatus);
TForm1.TMSMQTTClient1ConnectedStatusChanged( begin
ASender: TObject; if AConnected then
const AConnected: Boolean; TMSMQTTClient1.Subscribe('tms/chat');
AStatus: TTMSMQTTConnectionStatus); end;
begin if AConnected then
TMSMQTTClient1.Subscribe('tms/mytopic');
end;
To send a text to the chat, we use a FireMonkey When we put this all together in a FireMonkey
TMemo control and upon a button click, the text application , the result becomes:
can be sent as chat message.
To know the originator of the message,
the unique TTMSMQTTClient.ClientID will
be used as prefix for the message. With this
unique ID of the client, we can determine upon
receipt of a message whether this message was
sent from the app itself or from another app
with which the chat is communicating as all
chat apps subscribe to the same topic and thus
receive all topic messages, including the own
sent messages on this topic. So, to send the
message, we use:
TMSMQTTClient1.Publish('tms/chat',
TMSMQTTClient1.ClientID+'!'+ memo1.Lines.Text);
procedure TForm1.VrScope1NeedData(
Sender: TObject; Channel: Integer;
var Value: Integer);
begin
Value := LastVal;
end;
With these samples, we hope we did whet About the author Bruno Fierens
your appetite to enter this fascinating world Studied civil electronic engineering at university of Ghent,
of m2m communications and convinced Belgium (1987-1992) and started a career as R&D digital
you how easy the TTMSMQTTClient hardware engineer. Besides the fascination for electronics, Bruno
component makes it to add solid and robust Fierens set the first steps in programming with Turbo Pascal v3.0
m2m in desktop applications, mobile device and used all Borland Pascal & Delphi versions since that time. In
apps and IoT apps on Raspberry Pi / 1996, he founded TMS software for the activity of application
Raspbian based SBC's and component development with Delphi.
TMS software became Borland Technology Partner in 1998,
developed Delphi Informant award-winning grid & scheduling
components and now has an international team of software
developers working on a large portfolio of components. Bruno
Fierens is from 2012 Embarcadero MVP and frequent speaker at
Delphi conferences world-wide. He does and oversees VCL,
IntraWeb, .NET and FireMonkey component
Issue Nr 2 2017 development 9
WORK WITH WINEHQ, WHAT IS IT? PAGE 1/4
BY MAX KLEINER maXbox
Instead of simulating internal Windows logic like a
virtual machine or emulator, Wine translates and
converts API calls from the original Win Exe!
So it translates Win API calls into POSIX calls on-the-
fly and runtime, eliminating the performance, data
maXbox Starter 46 structure and memory penalties of other methods and
allowing you to cleanly integrate Win applications
A COMPATIBILITY LAYER FOR OPERATING into your desktop just with a separate loader.
SYSTEMS
Wine is free software and under constant development
Today we step through emulator and NOT
and improvement. Other platforms may benefit as well.
emulator.The framework Wine (originally an acronym for
• Loads Windows
“Wine Is Not an Emulator”) is a compatibility layer
9x/NT/2000/XP/Vista/7/8,10 Windows 3.x
capable of RUNNING WINDOWS APPLICATIONS and DOS programs and libraries
ON SEVERAL POSIX-COMPLIANT OPERATING • Win32 compatible memory layout,
SYSTEMS, SUCH AS LINUX (UBUNTU, REDHAT, exception handling, file-system threads
SUSE, DEBIAN ETC.), OS X, SOLARIS AND FREE and processes
BSD. • Designed for POSIX compatible operating
systems (eg. Linux, Solaris and FreeBSD)
• “bug-for-bug” stack compatibility
with Windows
https://github.com/maxkleiner/maXbox4.git
HOW TO INSTALL IT? The other settings focus on getting Wine itself to
Wine provides a run-time environment for a behave the way you want it to.
Windows application by serving Windows APIs Note: The Applications, Libraries, and Graphics
called by the application. As of today, there are tabs are linked together! If you have "Default
more than 20,000 Windows applications that are Settings" selected under Applications, all of the
supported by Wine. http://www.winehq.org/ changes made within the Libraries and
Graphics tabs will be changed for all
Since Wine is included in the most default and applications to use it.
known repository of Linux (Debian, Ubuntu, Suse),
you can install it with apt-get . However, if you So this is not the end of the line, install on Mac is
are using 64-bit DEBIAN or Ubuntu, you need to a bit different.
enable multi-architecture, as Wine is a 32-bit Nowadays, Windows and Mac play almost nicely
application: $ sudo apt-get install wine together.
You can install Windows and Mac side by side
On Ubuntu-derivatives (Kubuntu or Lubuntu) or and switch between them using Boot Camp , but
Linux Mint, you can install Wine using the Wine that requires a reboot every time, and you can
PPA maintained by WineHQ team. only use one operating system at a time.
http://ask.xmodulo.com/install-wine- With wine WineBottler you get both!
linux.html
As you can see also the 3 PI icons down right gets All examples can be found online:
extracted from the resource part of the exe and ..\examples\
once again, its the same exe (maXbox4.exe ) as on 161_bigint_class_maxprove3.txt
windows running.
The call respectively the calculation goes like this:
http://www.softwareschule.ch/examples/
The Wine project maintains also a database called
161_bigint_class_maxprove3.txt
the AppDB that has user reviews of how well
specific or known Windows programs work under
Wine. Search for your program and find out!
http://appdb.winehq.org/
starter
procedure TMainForm.FormCreate(Sender:
TObject);
begin
FIL:=TFileInfoCollector.Create(Self);
Fil.ImageList:=ImageList1;
Fil.SmallIcons:=True;
BEDir.Text:=ExtractFilePath(ParamStr(0));
FetchFiles;
end;
procedure TMainForm.FetchFiles;
var
Info : TSearchRec;
anItem : TListItem;
Ext : String;
I : integer;
begin
LVDir.Items.Clear;
If FindFirst(BEDir.Text+PathDelim+'*.*',0,Info)=0
then
try
Repeat
FIGURE : 2 Select Directory to Display Ext:=ExtractFileExt(Info.Name);
AnItem:=LVDir.Items.Add;
The code is pretty straightforward. AnItem.Caption:=Info.Name;
If the SHGetFileInfo call succeeds, a new item I:=FIL.IndexOfExtension(ext,false);
is added to the collection, and the icon is copied if (I<>-1) then
to the image list. Depending on the begin
AnItem.ImageIndex:=Fil.ImageIndex[I];
FreeIconHandles property, the icon handle is
AnItem.SubItems.Add(Ext);
freed. The last thing that is done is read the mime AnItem.SubItems.Add(Fil.Descriptions[i]);
type from the registry: known extensions are AnItem.SubItems.Add(Fil.MimeTypes[i]);
present as keys below HKEY_CLASSES_ROOT, end;
and the actual Mime Type (if present) is in the until (FindNext(Info)<>0);
finally
stringContent Type below the extension key. FindClose(Info);
end;
end;
CONCLUSION
Using the icons which the operating system shows when
displaying files is not hard, as can be seen in the small
code snippets displayed here.
To make things work a bit more optimal, a component
has been presented which caches the results of querying
the OS; For optimal convenience an imagelist can be
filled to make displaying the icon in controls that use an
image list (such as a listview or treeview). The component can
probably be improved by having 2 image lists: one for
small and one for large images. This improvement is left
as an exercise to the reader.
ONLINE VIEW
IF YOU SUBSCRIBE OR RENEW
IT WILL BE AVAILBLE FOR FREE.
FOR ONE YEAR.
DON'T ASK. SIMPLY SUBSCRIBE.
http://www.blaisepascal.eu/subscribers/UK/UK_R enewal_Department.html
MVVM DELPHI APPLICATIONS PART II PART 2 EX PERTS
DX
BY OLAF MONIEN
starter expert
Delphi & Lazarus begin
Application.Initialize;
MVVM is a design pattern that has been created
Application.MainFormOnTaskbar := True;
to help developing applications that separate UI Application.CreateForm(TModelMain, ModelMain);
from business logic. Application.CreateForm(TViewModelMain,
In this second article, we will look at templates ViewModelMain);
for Views, Models and ViewModels. We will also Application.CreateForm(TViewMain, ViewMain);
use TMessageCenter for notification. Application.Run;
In the first part of this article series I introduced end.
the MVVM design pattern and its general goals
and advantages for developing applications with
user interfaces. As the flow of ownership in MVVM starts with a
The core idea behind MVVM is to keep things View, we would want Views only created in the DPR
apart and organized. In terms of MVVM
“Separation of Concerns (SoC)” means that
file. Each view should then create its ViewModel,
UI, UI logic/state and business/data logic are which in turn should create its Model. So the DPR
three different concerns and that these should be should look more like this:
de-coupled as much as possible.
begin
ReportMemoryLeaksOnShutdown := DebugHook <> 0;
Accordingly, our simple demo application was Application.Initialize;
Application.MainFormOnTaskbar := true;
separated into three units: Application.CreateView(TViewMain);
● MainView.pas/dfm – implemented as Application.Run;
TForm descendant, holding all the UI controls, end.
implementing event handlers that connect
the controls with logic in the ViewModel.
(Notice the line
● MainViewModel.pas/dfm – implemented
“ReportMemoryLeaksOnShutDown”. That's what I
as TDataModule descendant, holding the code
always have as first line in my projects – it warns you
that reacts to UI events, by executing code in
of any memory leaks while running the project
model and pulling data from the Model.
through the Delphi debugger, so that you will be
● MainModel.pas/dfm – implemented as aware of any oversights as soon as possible).
TDataModule descendant, holding the
business logic and data access code. As you can see there is a call to
Important is that the Model neither knows about “Application.CreateView” with just a class as
the ViewModel nor does it know about the View. parameter. Obviously “CreateView” does not
The ViewModel only knows about (and owns) the exisits in TApplication (neither in VCL, nor in
Model and the View only knows about the FMX). It's a Class Helper that I created, to simplify
ViewModel (and owns it) creation of MVVM applications in Delphi. It has a
The demo application, that we started with, descriptive name “CreateView” to more comply
doesn't comply with the flow of ownerships as with the MVVM terms and, more important,
depicted in though. It just used global variables, it avoids the usage of global variables.
that are created by default when adding new
forms/datamodules to Delphi applications.
THE APPLICATION
If you examine the form design you will understand how
the app works. I would not like you to drag and drop all
of the elements on to the form, but I want to show you
some of the basic principles, of which the first is to give
each control its own self-descriptive name, such as:
BtnCreate_Archive, BtnRestore.
ClientDataSet1.LoadFromFile(ExtractFilePath(Application.exename)+'ToDoListUTF.xml');
ClientDataSet2.LoadFromFile(ExtractFilePath(Application.exename)+'ToDoListBackUpUTF.xml');
// the Extract file path is added to make sure the Data can be found
All in all this is pretty simple and fast if handled correctly. The CDS doesn’t know SQL. You will have to use filters.
But if you make a mistake or forget something you should Filtering is far less attractive then creating SQL. To have
be aware that it may be easier and less time consuming to the advantage of SQL functionality you will have to use
start over again. one of the other CDSs: FireDAC’s MemTable, or
kbmMW’s MemTable. A follow-up article in the next
Recovering from mistakes is hard and frustrating. issue will address that possibility.
Please enter data only AFTER the project’s structure is Meanwhile you would do well to read one of Cary
finished and all the fields are set up correctly. That may Jensen’s books about client datasets.
seem obvious, but often you realise after your initial setup Filtering is of course possible in all CDSs, and the
that you have missed a needed field (such as “Progress”). following example shows how it works (and that it is
If this is the case, first remove any data, right-click on the relatively complex to code compared to using SQL).
dataset and choose Clear Data. For example:
DataMod.CDSUpdatedLeden.Filtered := True;
EXTRA SKILLS
If you really need to repair or extend the number of
fields or otherwise change them, make a copy of your
dataset and rename it to something like
CDS_ORIGINAL so you will still have your data and
structure. Make sure you also copy the xml file and
rename it: ToDoListUTF → ToDoListUTF_ORIGINAL.
After you have finished altering your CDS you will
then be able to import your original data.
COMPONENTS
DEVELOPERS 4 Android logcat tool
People developing for Android knows that checking it’s logfiles is a must, debugging any new
application. One can do it directly using adb.exe with the logcat command, but it outputs data to the
console and is generally not really very useful due to the typical high amount of data produced by it.
Android Studio contains a log viewer application, but alas that requires installation of the complete
Android Studio, which perhaps is overkill to check the contents of the log.
Some 3rdparty options also exists. Unfortunately they typically require Java or .Net to be available, and
most of them are trials or limited commercials.
So when nothing exists that fulfills the requirements, the option is to make it yourself.
Thats what I did. May I present kbmLogCat!
su
sb
sc
r
F ibe at h
R ttps:
E //com
E pon
e
T nts4de
O velop
O ers.w
L ordpre
s. s
Issue Nr 2 2017 BLAISE PASCAL MAGAZINE
co 31
m
/
32 Issue Nr 2 2017 BLAISE PASCAL MAGAZINE
BLUETOOTH CONTROLLED ROBOT WITH PART 1
VISUINO AND DELPH PAGE 1 / 11
BY BOIAN MITOV
starter expert
- +
In the previous articles I have shown you how you
can program Arduino and ESP8266 boards to read
data from sensors or control Relays and Servos. I
have also shown you how you can connect to the
micro controllers from Delphi applications and
Android apps over Ethernet, Wi-Fi and Bluetooth
LE. We can also use Bluetooth classic to connect to
the boards. Again, there are many options, but
one of the most popular is the HC-06 Bluetooth
module. You can see how the HC-06 module looks
on this picture:
The module implements Serial communication You can see the assembled robot and the Android
Bluetooth service, and can be connected directly to phone that I used for the article here:
Arduino serial port. The Arduino micro-controllers are
also a popular choice for controlling robots. There are
many Arduino Robot kits, and some of them come with
the HC-06 module included for remote control of the
robot. -
+
In this article I will show you how you can program a
mobile robot to control it from Android Delphi app
over Bluetooth. You can use any mobile robot kit that
includes the HC-06 module. I used the Elegoo robot kit.
It is quite good and affordable kit available on Amazon.
https://www.amazon.com/gp/product/
B01DPH0SWY/ref=od_aui_detailpages00?ie=
UTF8&psc=1
The Robot kit includes Arduino UNO, Sensor First we will use Visuino to program the robot.
Shield for the Arduino, Motors, and Motor Driver Start Visuino.
board, Ultrasonic Distance Sensor with servo for The robot uses L298N module to control the
rotating left and right, line tracking sensors, motors, and we will need to add component for it.
Infrared Receiver sensor, HC-06 Bluetooth Type "motor" in the Filter box of the Component
module, Rechargeable Batteries, and Battery Toolbox then select the "Dual DC Motor Driver 3
Charger. Pin Bridge(L298N)" component:
And drop it in the design area. Next we need to Connect the "Reverse" output pin of the
connect the DualMotorDriver1 component to the "Motors[ 1 ]" channel of the DualMotorDriver1
Arduino pins to control the motors. Connect the component to the "Digital" input pin of
"Forward" output pin of the "Motors[ 0 ]" channel “Digital[ 9 ]" channel of the Arduino component:
of the DualMotorDriver1 component to the
"Digital" input pin of "Digital[ 7 ]" channel of the
Arduino component:
Click on the "Tools" button of the AnalogValue1 In the Elements Editor select the "Set Value State1".
component to open the "Elements" dialog:
In the Object Inspector set the value of the Type "compare" in the Filter box of the
"Value" property of the element to "1": Component Toolbox then select the "Compare
Char Value" component:
V. 5.00
- New!
Native BSON, JSON, MessagePack, YAML and XML. - Native improved XSD importer
Convert forth and back via unified object for generating marshal
notation object trees! able Delphi objects from XML schemas.
- New! - High speed, unified database access
Marshal Delphi objects to and from BSON, JSON, (35+ supported database APIs) with connection
MessagePack, YAML and XML. pooling, metadata and data caching on all tiers
- New! - Multi head access to the application server,
High performance and simple Spring Boot inspired REST via REST/AJAX, native binary, Publish/Subscribe, SOAP,
with full Delphi object support. XML, RTMP from web browsers, embedded devices,
- New! linked application servers, PCs, mobile devices, Java
High performance HTTP.SYS server transport for REST. systems and many more clients
- New! - Full FastCGI hosting support.
High quality pronouncable password Host PHP/Ruby/Perl/Python applications in kbmMW!
generator framework! - Native AMQP support ( Advanced Message Queuing
- New! Protocol) with AMQP 0.91 client side gateway
High quality random functions for cryptographic use! support and sample.
- Many improvements - Fully end 2 end secure brandable Remote Desktop
– Improved log framework with backup rollover and with near REALTIME HD video, 8 monitor support,
LogFormatter, REST CORS support, custom enum texture detection, compression and clipboard sharing.
support for object marshalling, improved scheduler - Bundled kbmMemTable Professional
features, anonymous function callback in WIB, which is the fastest and most feature rich in
improved data resolver features and more. memory table for Embarcadero products.
- Multimonitor remote desktop V5 (VCL and FMX) kbmMemTable is the fastest and most feature rich in
- Rad studio and Delphi 2009 to memory table for Embarcadero products.
Latest 10.1 BERLIN support - Easily supports large datasets with millions of records
- Win32, Win64, Android, IOS 32, IOS 64 - Easy data streaming support
and OSX client and server support! - Optional to use native SQL engine
- Native PHP, Java, OCX, ANSI C, C#, - Supports nested transactions and undo
Apache Flex client support! - Native and fast build in M/D, aggregation /grouping,
- High performance LZ4 and Jpeg compression range selection features
- Native high performance 100% developer defined app - Advanced indexing features for extreme performance
server with support for loadbalancing and failover
COMPONENTS
DEVELOPERS 4 DX
EESB, SOA,MoM, EAI TOOLS FOR INTELLIGENT SOLUTIONS. kbmMW IS THE PREMIERE N-TIER PRODUCT FOR DELPHI /
C++BUILDER BDS DEVELOPMENT FRAMEWORK FOR WIN 32 / 64, .NET AND LINUX WITH CLIENTS RESIDING ON WIN32 / 64, .NET, LINUX, UNIX
MAINFRAMES, MINIS, EMBEDDED DEVICES, SMART PHONES AND TABLETS.