You are on page 1of 48

D E L P H I,

L A Z A R U S,
O X Y G E N E,
S M A R T M O B I L E,
A N D
P A S C AL
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

BLAISE
BLAISE PASCAL
PASCAL MAGAZINE
MAGAZINE 44

OVERVIEW OF DELPHI TO DELPHI HISTORY


EUCLIDES
AN AGE PUZZLE
BY DAVID DIRKSE
ARDUINO: THE VISUINO PROJECT - PART 4
INTERNET OF THINGS - WITH ARDUINO AND DELPHI
USE ETHERNET SHIELD, PROGRAM IT WITH VISUINO,
C O N N E C T F R O M A D E L P H I A P P L I C AT I O N
OVER THE LOCAL NETWORK OR INTERNET

BY BOIAN MITOV
DATABASE WORKBENCH 5
T H E S W I S S A R M Y K N I F E F O R D ATA B A S E S
BY PETER VAN DER SMAN
TIPS AND TRICKS WITH KBMMEMTABLE
BY KIM MADSEN

PRINTED ISSUE PRICE 15.00


DOWNLOAD ISSUE PRICE 7.50

BLAISE
BLAISE PASCAL
PASCAL MAGAZINE
MAGAZINE 44
D E L P H I,
L A Z A R U S, S M A R T M O B I L
A N D
P A S C A L
R E L A T E D
L
A
N
F O R
A N D R O I D,
I O S, M A C,
W I N D O W S

E
S T U D
G
U
A
G
& L I N U X

I
E

O,
S

CONTENTS
Articles
OVERVIEW OF DELPHI TO DELPHI HISTORY
EUCLIDES
AN AGE PUZZLE
BY DAVID DIRKSE
ARDUINO: THE VISUINO PROJECT - PART 4
INTERNET OF THINGS - WITH ARDUINO AND DELPHI

PAGE 6
PAGE 7
PAGE 8

USE ETHERNET SHIELD, PROGRAM IT WITH VISUINO,


C O N N E C T F R O M A D E L P H I A P P L I C AT I O N
OVER THE LOCAL NETWORK OR INTERNET

BY BOIAN MITOV
DATABASE WORKBENCH 5
T H E S W I S S A R M Y K N I F E F O R D ATA B A S E S
BY PETER VAN DER SMAN
TIPS AND TRICKS WITH KBMMEMTABLE
BY KIM MADSEN

PAGE 12

PAGE 28
PAGE 39

Advertisers
BETTER OFFICE
COMPONENTS 4 DEVELOPERS 48
COMPUTER MATH & GAMES 4/5
DANIEL TETI DELPHI COOKBOOK 11
EVERS 36
NEW BLAISE LIBRARY 38
PASCON DELPHI 37
UPSCENE 35
VISUINO MITOV 27

Publisher: Foundation for Supporting the Pascal Programming Language


in collaboration with the Dutch Pascal User Group (Pascal Gebruikers Groep)
Stichting Ondersteuning Programmeertaal Pascal

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

Stephen Ball
http://delphiaball.co.uk
@DelphiABall

Peter Bijlsma
-Editor peter @ blaisepascal.eu

Michal Van Canneyt,


michael @ freepascal.org

Marco Cant
www.marcocantu.com
marco.cantu @ gmail.com

David Dirkse
www.davdata.nl
E-mail: David @ davdata.nl

Benno Evers
b.evers
@everscustomtechnology.nl

Bruno Fierens
www.tmssoftware.com
bruno.fierens @ tmssoftware.com

Primo Gabrijeli
www.
primoz @ gabrijelcic.org

Cary Jensen
www.jensendatasystems.com
http://caryjensen.blogspot.nl

Max Kleiner
www.softwareschule.ch
max@kleiner.com

John Kuiper
john_kuiper@kpnmail.nl

Wagner R. Landgraf
wagner @ tmssoftware.com

Kim Madsen
www.component4developers

Peter van der Sman


sman@prisman.nl

Jeremy North

Detlef Overbeek - Editor in Chief


www.blaisepascal.eu
editor @ blaisepascal.eu

Howard Page Clark


E-mail: hdpc @ talktalk.net

Andrea Raimondi

Wim Van Ingen Schenau


-Editor
wisone @ xs4all.nl

Rik Smit
rik @ blaisepascal.eu

Bob Swart
www.eBob42.com
Bob @ eBob42.com

jeremy.north @ gmail.com

Daniele Teti
www.danieleteti.it
d.teti@bittime.it

Please note: extra space characters have been deliberately added around the @ symbol in
these email addresses, which need to be removed if you use them.

editor @ blaisepascal.eu
Authors - Christian name in alphabethical order
A
B
C
D
F
G
H
J

Andrea Raimondi ,
Stephen Ball, Peter Bijlsma, Dmitry Boyarintsev
Michal Van Canneyt, Marco Cant,
David Dirkse, Daniele Teti
Bruno Fierens
Primo Gabrijeli, Mattias Gaertner
Fikret Hasovic
Cary Jensen

L
K
M
N
O
P
S
Z

Wagner R. Landgraf, Sergey Lyubeznyy


Max Kleiner
Kim Madsen, Felipe Monteiro de Cavalho
Jeremy North,
Inoussa Ouedraogo
Howard Page-Clark,
Rik Smit, Bob Swart,
Siegfried Zuhr

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 editor@blaisepascal.eu
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 65.-- Incl. VAT 6 % (including code, programs and printed magazine,
10 issues per year excluding postage).
2: Electronic - non printed subscription 45.-- 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 office@blaisepascal.eu
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
office@blaisepascal.eu

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.

Issue Nr 5 2015 BLAISE PASCAL MAGAZINE

DAVID DIRKSE

presales at
www.blaisepascal.eu/DavidDirkse/ComputerMath_Games.html

procedure ;
var
begin
for i := 1 to 9 do
begin
end;
end;

BLAISE PASCAL MAGAZINE


is proud to announce the first
edition of David Dirkses book:

COMPUTER MATH &


GAMES IN PASCAL

www.blaisepascal.eu/DavidDirkse/ComputerMath_Games.html

DAVID DIRKSEs

COMPUTER MATH &


GAMES IN PASCAL
A book printed in full color, sewn back bound with a hard
cover. Quality first. A fully indexed PDF file is included.
The book contains 87 chapters, 53 projects with source
code and compiled programs (exe).
All of these projects you can download at our special
website www.blaisepascal.eu
The book is highly educational and suitable for beginners
as well as for professionals.
Play board games, solve puzzles, operate a vintage
mechanical calculator, Produce 3-dimensional computer
art, generate lists of prime numbers, explore and draw
any mathematical function.
Solve systems of equations, calculate the area of complex
polygons.
Draw lines, circles and ellipses.
Resize, rotate, compress digital images.
Design your own font, generate and reduce Truth Tables
from Boolean algebra.
And more important: understand how it all works!
For the games, winning strategies are explained.
For puzzles the search algorithm.
For all projects: the math behind is thoroughly discussed.
The Delphi 3 7 (or later) source code is available
together with full explanation. Most of the projects can
be done with FPC Lazarus as well.
Pascal is the most educative, easy to learn and only
language available for several operating systems like
Windows, Linux, Mac and Android.
It is a great programming language

OVERVIEW OF DELPHI TO DELPHI HISTORY

DELPHI XE8

On February 8, 2006 Borland announced that it was


looking for a buyer for its IDE and database line of products,
including Delphi, to concentrate on its ALM line. On November
14, 2006 Borland transferred the development tools group to
an independent subsidiary company named CodeGear,
instead of selling it. Borland sold CodeGear to Embarcadero
Technologies in 2008. Embarcadero retained the CodeGear
division created by Borland to identify its tool and database
offerings, but identified its own database tools under the
DatabaseGear name.

Embarcadero Technologies in 2008.


Codegear Delphi 2007.
DELPHI 7 released in August 2002

DELPHI
released February 14, 1995

The roots of Turbo Pascal v1.0 started in Denmark. The first step,
in 1981, was the Blue Label Software Pascal Compiler - BLS Pascal
Compiler v1.2, copyright 1981 by Poly-Data microcenter ApS,
Strandboulvarden 63, DK 2100 Copenhagen - written by Anders
Hejlsberg for the NASCOM kit computer.

Turbo Pascal
Developer(s) Anders Hejlsberg while
working at Borland
Operating system CP/M, CP/M-86, DOS,
Windows 3.x, Macintosh
Platform 8080/Z80, 8085, x86

Lisa - Pascal

was a Pascal implementation for the Apple Lisa workstation.


It was an extension of the earlier Apple Pascal for Apple II machines,
but generated object code for 68000 processors that had to be linked
against the required libraries in the Lisa OS workshop.
Lisa Pascal laid the foundation for the development of Clascal and Mac Pascal
the first implementations of Object Pascal.

Windows

Charles Babbage - mathematician

Niklaus Wirth

conceived of the first programmable computer in the 1830s

born February 15, 1934 He is a Swiss


computer scientist, best known for
designing several programming languages,
including Pascal, and for pioneering several
classic topics in software engineering.

Babbage never built his Difference Engine


- a mechanical calculator with thousands of parts because of cost overruns and political disagreements,
but the inventor passed on plans for its completion,
and in 1991, the Science Museum in London actually
built it (the printing component was finished in 2000).
As suspected, it actually works.

Blaise Pascal (19 June 1623 19 August 1662)


was a French mathematician,
physicist, inventor, writer and Christian philosopher.
creates the first calculators
Blaise Pascal starts to gamble - result first statistics

Discovery of America by Columbus


Columbus led his three ships - the Nina,
the Pinta and the Santa Maria out of the Spanish port of Palos on August 3, 1492.
Discovery of Amerca by the Vikings 990 - 1050
Building of Spain 912 and Portugal 800
Building of France 5852 BC

Decay of the Roman Empire 500


Building of Europe

Roman Empire 700 BC

Archimedes Mathematician
Archimedes of Syracuse was an Ancient
Greek mathematician, physicist, engineer,
inventor, and astronomer.
He is regarded as one of the leading scientists
in classical antiquity. Wikipedia
Born: 287 - 212 BC, Syracuse, Italy
Plato in Classical Attic;
428/427 or 424/423 348/347 BC)
was a philosopher, as well as mathematician,
in Classical Greece.
Euclid Mathematician
Born Mid-4th century BC - 3rd century BC
Residence Alexandria, Hellenistic Egypt
Fields Mathematics Known for
Euclidean geometry / Euclid's Elements
Euclidean algorithm

IN THIS ISSUE (43)

Pythagoras Philosopher
Pythagoras of Samos was an
Ionian Greek philosopher, mathematician,
and founder of the religious movement
called Pythagoreanism.
Born: 571 - 495 BC,

Thales Philosopher
Thales of Miletus was a pre-Socratic Greek philosopher
from Miletus in Asia Minor and one of the
Seven Sages of Greece. Many, most notably Aristotle,
regard him as the first philosopher in the Greek tradition.
Born: 624 BC - 546
DELPHI
The cult of Apollo at
Delphi probably
dates back to
the 700s B . C .,

Difference Engine No. 1, portion,1832

ISSUE 40

Page 9
WATER CLOCK - CHINA - BEGINNING OF TIME (BC 4000)
Some authors claim that water clocks appeared in China
as early as 4000 BC

Babbage Difference Engine No. 2

EUCLIDES
Euclid (300 BC), sometimes called
Euclid of Alexandria to distinguish him
from Euclid of Megara, was a Greek
mathematician, often referred to as the
"Father of Geometry". He was active in
Alexandria during the reign of Ptolemy
I (323283 BC). His Elements is one of
the most influential works in the history
of mathematics, serving as the main
textbook for teaching mathematics
(especially geometry) from the time of
its publication until the late 19th or early 20th century.
In the Elements, Euclid deduced the principles of what is
now called Euclidean geometry from a small set of
axioms. Euclid also wrote works on perspective, conic
sections, spherical geometry, number theory and rigor.

Very few original references to Euclid survive, so little


is known about his life. The date, place and
circumstances of both his birth and death are unknown
and may only be estimated roughly relative to other
figures mentioned alongside him. He is rarely
mentioned by name by other Greek mathematicians
from Archimedes onward, who usually call him "the
author of Elements".The few historical references to
Euclid were written centuries after he lived, by Proclus
c. 450 AD and Pappus of Alexandria c. 320 AD
Proclus introduces Euclid only briefly in his
Commentary on the Elements. According to Proclus,
Euclid belonged to Plato's "persuasion" and brought
together the Elements, drawing on prior work by
several pupils of Plato. Proclus believes that Euclid is
not much younger than these, and that he must have
lived during the time of Ptolemy I because he was
mentioned by Archimedes (287212 BC). Although the
apparent citation of Euclid by Archimedes has been
judged to be an interpolation by later editors of his
works, it is still believed that Euclid wrote his works
before those of Archimedes.
Proclus later retells a story that, when Ptolemy I asked
if there was a shorter path to learning geometry than
Euclid's Elements, "Euclid replied there is no royal
road to geometry. In the only other key reference to
Euclid, Pappus briefly mentioned in the fourth century
that Apollonius "spent a very long time with the pupils
of Euclid at Alexandria 247222 BC.

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

Because the lack of biographical information


is unusual for the period (extensive
biographies are available for most significant
Greek mathematicians for several centuries
before and after Euclid), some researchers
have proposed that Euclid was not, in fact, a
historical character and that his works were
written by a team of mathematicians who took
the name Euclid from the historical character
Euclid of Megara.

Euclidean geometry is a mathematical system


attributed to the Alexandrian Greek mathematician
Euclid, which he described in his textbook on
geometry: the Elements. Euclid's method consists in
assuming a small set of intuitively appealing axioms,
and deducing many other propositions (theorems)
from these. Although many of Euclid's results had
been stated by earlier mathematicians, Euclid was the
first to show how these propositions could fit into a
comprehensive deductive and logical system. The
Elements begins with plane geometry, still taught in
secondary school as the first axiomatic system and the
first examples of formal proof. It goes on to the solid
geometry of three dimensions. Much of the Elements
states results of what are now called algebra and
number theory, explained in geometrical language.
For more than two thousand years, the adjective
"Euclidean" was unnecessary because no other sort of
geometry had been conceived. Euclid's axioms
seemed so intuitively obvious (with the possible
exception of the parallel postulate) that any theorem
proved from them was deemed true in an absolute,
often metaphysical, sense. Today, however, many
other self-consistent non-Euclidean geometries are
known, the first ones having been discovered in the
early 19th century. An implication of Albert Einstein's
theory of general relativity is that physical space itself
is not Euclidean, and Euclidean space is a good
approximation for it only where the gravitational field
is weak.
Euclidean geometry is an example of synthetic
geometry, in that it proceeds logically from axioms to
propositions without the use of coordinates. This is in
contrast to analytic geometry, which uses coordinates.

AN AGE PUZZLE PAGE 1/4


BY DAVID DIRKSE
The solutions are displayed in a paintbox, see
picture.
We notice columns for the solution number, the
year of birth, the age and the answer of the
question anniversary passed?.
Pressing the search button starts the search
process.
Also pressing the return key after the current
year starts the search.
The text on the form is placed in Tlabel
components.

On new year of the year 1997 mr. Black, a math


teacher, meets his former student White. White
remembers Blacks fascination for numbers and
greets him with: my age is equal to the sum of the
digits of my year of birth.
Black thinks for a while and then answers:
congratulations on your birthday.

QUESTIONS:

1.
2.

How Black may know that it is


birthday?
What is White's age?

White's

On the bottom of the form at full width there is


a statictext component for messages.
The picture below shows : 4 solutions found.

SOLUTION

If Peter was born in 2000 and we live in


the year 2015 there are two possibilities:
Peter is 14 years old and his birthday
still has to come or Peter 15 years old
and his birthday is passed.
The solution may be found by checking
all possible years and calculating the
sum of the birthyear digits. Test for
digitsum = current year birth year ...(1 if anniversary still has to come)
This means a lot of work, so better we
write a program to do the job.

THE PROGRAM

There may be more solutions. We choose


to calculate them all and store them in a
list. Thereafter the solutions are
displayed on the screen.
For a solution we define the data type:
Tsolution = record
birthyear : word;
age : byte;
birthdaypassed : boolean;
end;

And these global variables:


var thisyear
: word;
solutions : array[1..maxsolution] of Tsolution;
solutionNr : byte;

Thisyear comes from a TEdit component


(this year) where the current year was typed.
Solutions is an array[1..maxsolution] of type
TSolution.

SolutionNr is the number of stored solutions.


This value is 0 if no solution exists.
Maxsolution is a constant set at 20.

There are three reasons to make a function or a


procedure. First is the case of common code that
is needed at multiple places of the main program.
This reduces the total code.
The second reason is clearity. We place specific
code apart for readability.
For this reason we made a function to sum the
birthyear digits.

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

AN AGE PUZZLE PAGE 2/4


MaxSolution is a constant set to 20, the space in
the solutions array.
Which saves typing. Otherwise we had to add
solutions[solutionNr]. before
birthyear, age. Now this is done by Delphi.

Third is to concentrate specific operations for


maintainability. If the calculation of an area is
performed by one function, in case of a change
only one place in the program needs to be
changed.

function sumdigits(year : word) : byte;


var s : string;
i : byte;
begin
s := inttostr(year);
result := 0;
for i := 1 to length(s) do result := result+ord(s[i])-ord('0');
end;

The procedure ShowSolutions:

procedure showSolutions;

Ord('0') is 48, the ASCII code of digit 0.


So ord('8') ord('0') = 8.
Without -ord('0') we would get 56.

//display solutions in paintbox1


var i,n : byte;
s : string;
x,y : word;

THE SEARCH PROCESS

begin
clearpaintbox;
with form1.PaintBox1.Canvas do
begin
brush.style := bsClear;
font.Color := fontcolor;
font.Name := fontname;
font.Height := fontheight;
for n := 1 to solutionNr do
if n <= solutionsperdisplay then
begin
y := (n-1)*fontheight + ymarge;
for i := 0 to 3 do
begin
x := columns[i];
case i of
0 : s := inttostr(n);
1 : s := inttostr(solutions[n].birthyear);
2 : s := inttostr(solutions[n].age);
3 : if solutions[n].birthdaypassed
then s := 'yes'
else s := 'no';
end;//case
textout(x,y,s);
end;
end;//for..if
end;//width
end;

In procedure searchBrtClick, called by the search


button, we notice following local variables:
var

digitsum : byte ;
birthyear : word;

variabele birthyear runs from


startyear...thisyear.
startyear is a constant set to 1900.
Digitsum is the sum of the digits of
birthyear.
Before the search starts two checks are made:
1. the current year must be defined
2. the current year must be equal or bigger than
startyear.
Then solutionNr is set to zero.
All values of birthyear are checked.
If the test yields true, age (=digitsum) and
anniversary passed is added to the solutions
array. This program loop looks like:
for birthyear := startyear to thisyear do
begin
digitsum := sumdigits(birthyear);
if digitsum = thisyear - birthyear then
savesolution(digitsum,birthyear ,true);
if digitsum = thisyear - birthyear - 1 then
savesolution(digitsum,birthyear,false);
end; //for

The procedure SaveSolution:


procedure SaveSolution(ag,by:word; bp:boolean);

//ag:age; by:birthyear; bp:birthday passed


begin
inc(solutionNr);
if solutionNr <= maxsolution then
with solutions[solutionNr] do
begin
birthyear := by;
age := ag;
birthdaypassed := bp;
end;
end;

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

n adresses all solutions.


Fontcolor, fontname, fontheight are
preset constants.
Y is the vertical position of the text on paintbox1.
X is the horizontal position.
Per solution, so per line of text, a variable i counts
0..3 for the columns . Columns[i] holds the
x where the text is to start, also a preset constant.
Per case of i the text s is prepared.
Textout(...) prints s.

AN AGE PUZZLE PAGE 3/4


Why all this preset constants? Why not using the
values directly? Sure we could. But using
constants is smarter because they make future
changes much more easy. Say we want to change
the startyear. Without the constant definition we
would have to change all places where the
constant (1900) was used. But defined as a
constant we only need to change the constant
value once to be effective in all places in the
program.
PROCEDURE BEFORE CALLED
CLEARPAINTBOX.

This procedure erases the paintbox by painting


the canvas white. (for this color we could have used a
constant as well)
procedure clearpaintbox;
begin
with form1.PaintBox1 do
with canvas do
begin
brush.Style := bsSolid;
brush.color := $ffffff;
fillrect(rect(0,0,width,height));
end;
end;

The paintbox has properties width, height and


canvas. By not using with
form1.paintbox1.canvas but the separation
with form1.paintbox1 do with canvas do, we may
use both the canvas and the paintbox properties
without the name prefixes. Canvas property
brush takes care of background coloring.
Style is a property of the brush. If we write
brush.style := bsClear the fillrect does nothing,
the brush is turned off. Fillrect( r ) paints
rectangle r of the paintbox in the color
brush.color. r is of type Trect. Function rect
(defined inside Delphi) makes a Trect from
coordinates.
See picture:

We notice that the width occupies left ...


right-1 and the height is from top to
bottom-1. Width = right left.
Height = bottom top.
SETTINGS

In the TEdit component YearEdit we enter


the current year. Property maxlength of
YearEdit is set to 4. This prevents entering
more than four digits.
The event YearEditKeyPress points to a
procedure of this name. Reason is to allow only
the digits 0..9 together with backspace for
procedure TForm1.yeareditKeyPress(Sender: TObject;
var Key: Char);
begin
if not (key in ['0'..'9',#08]) then key := #0;
end;

8 is the ASCII code of the backspace key.


Prefix # denotes a following character code.
The data between [] is of the SET datatype.
We want to start the search as well by pressing
the return key after the current year.
The return code is catched before it reaches the
Tedit. This is done by setting the Tform
property KeyPreview to true and defining the
event FormKeyDown.
procedure TForm1.FormKeyDown(Sender: TObject;
var Key: Word;Shift: TShiftState);

//<return> starts search


begin
if key = VK_RETURN then
begin
key := 0;
searchBtnClick(self);
end;
end;

VK_RETURN is a predifined Delphi constant


representing the return key.
NOTE: key now is of the type word, not char.
Procedure searchBtnClick is called
with Form1 (self) as sender.
For more details please refer to the source code.
FINALLY

With the help of this program the reader may


solve the puzzle without doing the arithemetic
himself. Now it becomes clear why Black is sure
today is White's birthday.
Other, more difficult, questions would be birth
years that have zero or multiple solutions.
This would require some extensions of the
program.
Have fun!
10

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

30,00
including VAT
39
including
the
printed
book,
ebook
and shipping

Quick answers to common problems

DI

HOICE A
SC
R BLAISE
PASCAL
MAGAZINE

AZING
M

50 hands-on recipes to master the power of Delphi for


cross-platform and mobile development on Windows,
Mac OS X, Android, and iOS

TO

Delphi Cookbook

Daniele Teti

See our special offer:


if you take out a subscription
for two years the book
will cost you only 10,00

http://www.blaisepascal.eu/daniele_teti_book/DanieleTeti.html

ARDUINO: THE VISUINO PROJECT - PART 4

PAGE - 1/15

INTERNET OF THINGS WITH ARDUINO AND DELPHI


BY BOIAN MITOV
In the previous articles, you learned how to
program Arduino using Visuino, and how to
communicate with it using USB simulated serial
port from your Delphi code. This opens a lot of
interesting possibilities for collecting and
processing live data, but the direct USB
connection imposes some limitations. What if you
want to collect data from many sensors spread
over large area? Or what if you want to
communicate with remote sensors over Internet?
The basic Arduino UNO does not have built in
network adapter, but there is Ethernet shield
available for it. In addition many of the more
advanced Arduino boards and their clones come
with WiFi or wired Ethernet built in.
There are also cheap and simple ESP8266 WiFi
modules that can be connected to the Arduino, so
networking Arduinos is routinely done.
In this article you will learn how to setup Arduino
to use Ethernet Shield, how to program it with
Visuino, and how to connect to it from a Delphi
application over the local network or Internet.
Before you start, you will need to install Ethernet
Shield on the Arduino. This is fairly easy. Just
snap it on top of the board as shown in the
picture.

Add Ethernet Shield:

Next you need to specify the MAC address for


the shield. You can use a MAC address generator,
or one of the MAC addresses from the Arduino
tutorials. Here I use DE-AD-BE-EF-FE-ED:

You also will need to install CommunicationLab,


PlotLab and InstrumentLab from Mitov
Software. CommunicationLab is not officially
released yet, but prerelease builds are available
on request. You can also easily modify the
examples in this article not to use PlotLab or
InstrumentLab.
First you will create a simple Arduino server.
Start Visuino. Click on the Down arrow button in
the top right corner of the Arduino component,
and from the menu select Add Shields... :

12

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

ARDUINO: THE VISUINO PROJECT - PART 4


PAGE - 2/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
Set the UseDHCP to false, so Arduino will
work with a fixed IP address:

And set the Enabled property of the IPAddress


to True so the IP address will be used when
Arduino starts:

Set the IP address for the Arduino


as example 192.168.0.55:
Once the Ethernet Shield is configured, you can
add one or more TCP/IP Client, TCP/IP Server,
or UDP sockets to it. Click on the ... button
after the Sockets to add a socket:

Add TCP/IP Server socket:

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

13

ARDUINO: THE VISUINO PROJECT - PART 4


PAGE - 3/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
Set the Socket Port property to 8080:

To generate some test data from Arduino, you


can use a Sine Generator as shown here, or
you can use any other source of Analog data,
or one of the Analog channels:

6
14

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

ARDUINO: THE VISUINO PROJECT - PART 4


PAGE - 4/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
Connect the data source to the Input Pin of the
Server Socket:

Press F9 to generate the Arduino


Sketch and open the Arduino IDE:

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

15

ARDUINO: THE VISUINO PROJECT - PART 4


PAGE - 5/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
Click on the Upload button to compile and
upload the sketch:

The simplest way to see if the Arduino project is


working is to open a web browser and enter the
Arduino IP address, and socket number
192.168.0.55:8080 . You should see the data
appearing in the browser, in this case Chrome:

16

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

ARDUINO: THE VISUINO PROJECT - PART 4


PAGE - 6/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
Next its time to receive the data in Delphi.
Start RAD Studio, create a VCL Form project,
and drop a TCLClientSocket from
CommunicationLab on the form:

Set the IP Address to the same used in the


Visuino project 192.168.0.55:

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

Set the Port to 8080:

17

ARDUINO: THE VISUINO PROJECT - PART 4


PAGE - 7/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
Drop TCLTerminal on the form:

Switch to the OpenWire tab, and connect the


Output Pin of the Socket to the Input Pin of the
Terminal:

Compile and run the application. You will see


the data arriving in the terminal:

18

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

ARDUINO: THE VISUINO PROJECT - PART 4


PAGE - 8/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
If you need to access the data in your code,
the Socket component has OnReceive event:

Now you can receive data from Arduino over the


network, however the data arrives from a single
sensor, and in text form. This makes it difficult
to work with, and limits the data channels that
we can get.
As shown in the previous article, Visuino and
CommunicationLab have support for packet
data. You can use it with sockets the same way
you did with the serial port.
Start a new Visuino project, add and configure
the shield and the socket as you did in the
previous project.

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

19

ARDUINO: THE VISUINO PROJECT - PART 4


PAGE - 9/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
Next, add a Packet component:

Connect the Output Pin of the Packet component


to the Input Pin of the Socket:
Double click on the packet component to open
the elements editor. In the editor add 2 Analog
and 2 Digital elements:

20

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

ARDUINO: THE VISUINO PROJECT - PART 4


PAGE - 10/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
Connect the Input Pins of the Analog Channels
to the Output Pins of Analog Input Channel[ 0
] and Analog Input Channel[ 1 ], and the
Input Pins of the Digital Channels to the Output
Pins of Digital Channel 0 and 1 of the Arduino
Board component:

Expand the HeadMarker and for the Bytes click


on the ... button:

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

21

ARDUINO: THE VISUINO PROJECT - PART 4


PAGE - 11/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
In the Bytes editor enter 55 55, then click OK:

This will enure that the packet has unique


header and its starting point can be identified in
the data stream. Visuino uses special algorithm
to ensure that the header marker will not appear
in the packet itself.
Press F9 to generate, then compile and upload
the sketch in the Arduino IDE as you did in the
previous project.
Now that the Arduino is ready, lets switch to
Delphi.
Start a new VCL Form project, add the
TCLClientSocket, and set the IP Address and Port
as in the previous project:

22

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

ARDUINO: THE VISUINO PROJECT - PART 4


PAGE - 12/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
Next add a TCLUnpacket, TSLScope,
TILAngularGauge and 2 TILLed components:

Switch to the OpenWire tab, and double click on the


CLUnpacket1:

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

23

ARDUINO: THE VISUINO PROJECT - PART 4


PAGE - 13/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
Add 2 Float and 2 Boolean channels:

Expand the HeaderMarker and for the bytes click


on the ... (called ellipsis)

24

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

ARDUINO: THE VISUINO PROJECT - PART 4


PAGE - 14/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
In the Bytes editor enter 55 55, then click OK:

Connect the components as shown in the picture:

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

25

ARDUINO: THE VISUINO PROJECT - PART 4


PAGE - 15/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
Compile and run the application. You will see the
data arriving from Arduino over the 4 channels:

When you need to access the data from code,


you can use the TSLGenericRealValue as
example to receive the Floating point data and
process it in the OnProcessData event, as
shown in one of the previous articles. There are
also similar components for processing the
Boolean data included in LogicLab.
The communication to Arduino is equally easy.
In order to send the data from Delphi, use
TCLPacket component and in Visuino use
Unpacket component.

CONCLUSION
This article has given you enough information to
start communicating with one or more Arduino
devices over wired network, or Internet from your
Delphi code. This is your first introduction to the
exciting world of Internet of Things. In the
following articles you will learn how to
communicate with Arduino over WiFi, and how to
make multiple Arduino boards to talk to each
other.

BLAISE PASCAL MAGAZINE subscribers that


visit our PASCON - Event will receive a DVD with
lots ofprograms, information and as a
VERY SPECIAL INCENTIVE you will get an
ARDUINO-BOARD FOR FREE INCLUDING
THE VISUINO SOFTWARE from Boian Mitov to
be able to compose and create your own
software for the board

26

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

What is Visuino?
Visuino is the latest innovative software from Mitov Software. A visual programming
environment allowing you to program your Arduino boards. Although it currently
supports the official Arduino boards, it is not restricted to their support alone and
requests to support new hardware are welcome.
INTRODUCTION

What is Arduino?

The components found in the Visuino software represent their hardware components and you
will easily be able to create and design your programs using drag and drop. No equipment or
hardware is needed to run the software in design mode. Once you have completed the design,
you can connect Arduino board upload and run it.
For those people who are not strong on writing code then designing, compiling and creating
Arduino programs has never been easier! Why waste time on creating code when we have done
all the hard work for you already? You have your Arduino board, and great hardware design,
see it running in minutes, not hours!
Currently we are running a Beta program which you can be part of by joining our Google group.
Join the group now to download and test the software or send an email to mitov@mitov.com.

AVAILABLE ON THE NEXT


PASCON 15 SEPTEMBER 2015

www.visuino.com

VISUINO IS THE LATEST INNOVATIVE PRODUCT


FROM MITOV SOFTWARE.

DATABASE WORKBENCH 5

PAGE 1/8

THE SWISS ARMY KNIFE FOR DATABASES

BY PETER VAN DER SMAN

When you start working with databases sooner or


later you'll come at a point where you start
searching for a tool to maintain your database.
After a bit of searching you'll end up using
FlameRobin when your database is a Firebird
database, using other databases might result in
other programs. This is cumbersome at the
moment you can't avoid using different databases.
For instance when you have two customers each
using their own database. Database Workbench is
database tool working independent from
databases, that is to say it supports a lot of
different database managers. Time to take a closer
look at it.

EXAMINING A EXISTING DATABASE

As stated before, Database Workbench does


support different databases, but is should be said
the databases you can use is subject to the licence
you have. Having said that we start using
Firebird as a starting point as this seems to be the
database propagated for using with the current
versions of Delphi. So we start our freshly
installed program and firstly we have to select
our previously installed Firebird as Database
server to use. Then we can start opening a
existing database. As an example we choose the
Employees.fdb which comes as a demo with
your Firebird installation. It gives you directly a
nice overview ( right column )
Then we can give the things we like a closer view.
In the bottom part we find all kinds of
information about the database itself. A nice one
are the Connections under Activity. Opening
this gives a screen showing which programs are
currently connected to the database. Of course it
will allways show at least one connection as the
program itself is connected to it. After making
another connection, for example in the Delphi
Data explorer , a second entry will show up (in
this case bds.exe). Always good to know in case
you're wondering who is blocking the database.

28

Figure 1. The content for Employees.fdb


in the Navigator

Of course we can use the Navigator as above to


study the content of all the tables in the database.
But Database Workbench offers a much more
handy option for this. After opening a Diagram
Editor we can use the option Reverse Engineer
Database to build a schematic overview for the
database. We can even select to show only a part
of the Database but, greedy as we are, we of
course select for the whole database.

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

DATABASE WORKBENCH 5
THE SWISS ARMY KNIFE FOR DATABASES PAGE 2/8

Figure 2. Employees.fdb in the Diagram Editor (part)

Always difficult to get a nicely looking schema


for all this linked tables. You should consider it
as a starting point to make a schema as nice as
you want to have it. You can replace all tables,
smarten the links, whatever you like. The
Navigator is a nice tool to show where you are
in the total schema. Or to navigate quickly to
another part. After some manual adjustments
our schema could be like figure 3.

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

29

DATABASE WORKBENCH 5
THE SWISS ARMY KNIFE FOR DATABASES PAGE 3/8

Figure 3. Employees.fdb in the Diagram Editor


(after manual adjustment)

Who would have thought this is all needed for a


sample employee file! This schema is just of a
size to overview it at a glance. In the real world
your schema will easily by a lot larger. Luckily
enough we have the possibility to make a Subdiagram so you can divide your schema in
relevant parts.

In the schema we directly see which fields are


part of the primary key (having a golden key)
and which play a role in a reference (having a
grey key). More information about the table we
get with a double click on it. For example:

Figure 4. Columns for table EMPLOYEE

30

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

DATABASE WORKBENCH 5
THE SWISS ARMY KNIFE FOR DATABASES PAGE 4/8
More than enough information. You might
notice the Column Type for EMPNO for the
column EMP_NO. Looks a bit odd, this has to
do with defining your own types in Firebird.
In the schema (figure 1) you can find these
definitions under Domains. For our database
has no less than 15 types defined.
Our EMPNO' turnes out to be of type
SmallInt. The other tabs in the Table
Properties form show more information about
the table. The tab DLL gives you all the code
needed to create the table.
This design is used consequently. Double
clicking in the Navigator (figure 1) on
different items will result in similar screens to
popup. No matter if it is about a Domain, a
View, an Index, a Constraint, a Trigger,
an Exception or any other item, you'll get Tab
with relevant detail, and a tab DDL giving
you the code to implement in the database. In
fact, you don't really need this code, as we'll see
later on, but it can be a handy way to check up
what you have done with an example
implementation. Unless you're the type
inventing all by yourself and never looking at
example code?
The temptation is strong to directly start making
all kind of changes. The schema (figure 3) shows
us an emp_NO, proj_ID en job_CODE. Of
course there can be puristical reasons the make
the distinction between NO, ID and
CODE, but perhaps you would prefer to
change this all to ID. The good news is we
actually can easily make these changes in the
screen as shown in figure 4. Just change
EMP_NO to EMP_ID. The bad news is it
won't be of any use.
The diagram in the diagram editor is not
connected to a physical database.
The consequence is we cannot pass our changes
made to the database. In fact this is a good thing.
In a designing phase it could be a nice idea to
make the proposed changes, making them in an
existing database can be quite disastrous.
There's every change that these fields play a part
in a trigger, a view or a procedure. In fact, we
can change fieldnames using Database
Workbench: just open a table from the navigator
and change the name of the field. In this screen
there is this handy button Create/Alter Table.
Nine times out of 10 this will result in a message,
from Firebird, telling you the update was
unsuccessful due to data integrity.

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

But changing the AGED field from the table


SALES will work fine using this option.
The other way around will work fine. The option
Update From Database can be used to match
your diagram with an existing database. The
lay-out we made with so much effort (figure 3)
will be maintained.
DESIGNING A NEW DATABASE

The previous part showed Database Workbench


5 can be used to analyse the structure of a
database we received from a third party. But is it
of any good designing a new database? You
understand this question is rhetorical. Of course
we can. Let's just do it!
CONCEPTUAL AND PHYSICAL DIAGRAMS

The best way to start a new design is designing


it using the Diagram Editor. Starting with an
empty Diagram we'll find under the New
button two options: New Conceptual
Diagram and New Physical Diagram. It is
just like is sounds: you can start making a more
sketchy diagram or directly start with a design
tailored to the possibilities your database
manager of choice provides. If this would not be
clear at first sight: after selecting a New
Physical Diagram you must point out for which
database you're going to design.
So we choose for a
Conceptual
Diagram and get an
empty working area
where we can drop
new tables. Of
course I could have
chosen to redesign
this inevitable
databases like
Employees or
FishFacts, but let's
design a brandnew
database we can use
to browse all articles
ever published in

Blaise. Our first go


would be to have
three tables: blaise
edition, writer
and article. So we
Figure 5. The first conceptual
drop
three tables (in
diagram
this phase named
Entities ) on our working area, double click on
them to give them meaningful names and
adding some fields (in this phase named
Attributes). After some rearrangement it could
look like this

31

DATABASE WORKBENCH 5
THE SWISS ARMY KNIFE FOR DATABASES PAGE 5/8
Note I did'nt mind about the type definition
for my fields. No reason to bother about it
right now, we can just focus in getting the
general structure right.
Looking to our design we note the field
Language appearing in two tables. So
most likely we should consider adding a
new table/entity Languages. And of
course we must find a way to bind the
Articles to the proper Blaise edition.
The most flexible way to do it is using a new
table making the connecting between the
two tables. It gives the editor a way to
republish an article . Speaking about this:
most articles in effect will be republished in
different editions, that is to say, in
translation. So apart from the writer of the
original version there can be a translator
too. And we should be able to refer to the
original article. So we can add a table
Translators, but the easy way is to use the
table Writers for this purpose as well. This
directly solves the problem of persons who
both write articles and translate articles as
well. And besides, writing an article or
translating, both are a kind of writing. And,
in case you had noticed, we now have a
reason to use author in the Articles table
referring to a table writers. And a fieldname
date is a bit ambiguous, so let's change it to
DatePublished right away.
Let's make some connections as well. The icons
as shown in figure 5 point out we can use three
kinds of links: Identifying Relationship,
Non-Identifying Relationship and Documentation
link . The last one is meant to make connections
just for clarifications in the diagram, not for
implementing in the database. After some not
too hard labour it could look like this.
In the real world this would be the moment to
put it aside and to look at it again at a later point
of time and discussing it with a colleague.
However, in the world of Blaise the next edition
is coming soon, so we continue with our schema
right away. We do a bluff and ask Database
Workbench to convert this schema to a
Physical Diagram for Firebird. The result is a
pile of error messages: this is the time we should
point out the datatypes we are going to use.
Further on we did make connections between
tables, but I didn't really bother working them
out. The good news is we don't have to search
ourselves for missing data, we just get a list of
items to resolve. We can't blame the program it
initially is a long list. The following schema
shows the more adapted conceptual schema
(figure 7).
32

Figure 6. The half worked out conceptual diagram

Figure 7. The fully adapted conceptual diagram

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

DATABASE WORKBENCH 5
THE SWISS ARMY KNIFE FOR DATABASES PAGE 6/8
Where needed I provided for a unique key field
in all tables. All fields are given a type. Just for
showing the differences I used some different
types. And the connections are filled in with
relevant information. To get a nice picture I
removed the connection between
OriginalVersion and ArticleID from the
Diagram. But it still exists. To show it a Subdiagram can be used:

We just take the standard options, including


Firebird 3.0, and then click OK. That's all to get a
diagram adjusted for the desired database. All
types are translated to types provided by our
database. To illustrate this I made two
conversions, one to Firebird and one to MySQL.
The differences for the table Articles are
shown in figure 9. In fact you'll see just one
difference, the conversion of the type Unicode
text in the Abstract field . For Firebird it is
converted to BLOB(text), for MySQL the
LongText is used.

Figure 7b. The Articles sub-diagram

FROM CONCEPTUAL TO PHYSICAL DIAGRAM

Figure 9a. Table Articles for Firebird.

So now we are ready to convert to a physical


schema. This is the point to decide on which
database manager our database must be build. The
next thing to select the option Generate Physical
Diagram and select for Firebird resulting to the
following screen.

Figure 9b. Table Articles for MySQL

After looking closely to the final schema


again, it will probably result in some
desired changes. We don't have to go back
to the conceptual diagram, we just can
make the changes in our conceptual
diagram. The interface is nearly the same.
So - if this is the moment - we realise the
Dutch Edition 126 is a translated version
of the English 44 edition and we should
provide a way bring this into the database
we can adjust the table Blaise (in the
same way we did for Articles):

Figure 8. Conversion to Firebird

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

Figure 10. Adapted Table Blaise

33

DATABASE WORKBENCH 5
THE SWISS ARMY KNIFE FOR DATABASES PAGE 7/8
The reference from OriginalVersion back to the
table Blaise is called a Constraint. It is build
using the following screen.

Figure 11a. Definition of a Constraint

And, as promised before, if we really would be


interested we could find the code needed to add
this Constraint to the database in the DDL tab:

34

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

DATABASE WORKBENCH 5
THE SWISS ARMY KNIFE FOR DATABASES PAGE 8/8
Of course now we have to doubel check the total
schema again and adapt it where necessary.
We could add a Check Constraint , an Index or a
Trigger where desired.
After doing this we can use the schema to build a
real database. We have two options available.
The hard way is to use the option Extract DDL For
ALL Objects . It provides us a SQL script to be
used for generating the database. The advantage
might be you can recheck the script before
running it and/or maybe add some very special
code you couldn't add using the program.
And the script can be useful for documentation
purposes. But we also can use the easy way.
Just take the option Create Database from
Diagram and follow the instructions in the
Wizard. It will ask you the location of the
database, username and password and so on.
When we're done the database will be created
and the script will run in order to create your
structured tables. In anything does go wrong
with the script it will pop-up in the script editor
so you can make your adjustments. .

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

EPILOGUE

Database Workbench is a handy tool for


working with databases. Either in case you want
to examine an unknown database delivered to
you or in case you want to design a brand new
database, in both cases the program offers you a
lot of support to do so. Of course you can find
loads of other programs doing this. The nice
thing about Database Workbench is it supports
multiple databases. So in case you're working
with different databases you only have to know
about one single tool instead of learning about a
tool for each different database

35

COMPONENTS
DEVELOPERS

Specialist help and consultancy


for kbmMW

Benno Evers is our specialist for questions about


kbmMW.
He can help you with basic questions regarding kbmMW
as well as with turnkey Development and Consultancy.
Hes a specialist in netwoks, internet and hardware.

CUSTOM TECHNOLOGY
b.evers@eversct.nl

better office benelux | asterlaan 6 5582EH waalre | 040 222 26 43 gtan@better-office.com

36

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

http://www.blaisepascal.eu/DucthPascon/Pascon2015UK.html

L I B R A R Y 2 0 1 5
CLICK TO OPEN
ITEM (NUMBER)

CLICK TO OPEN
THE TOTAL
OVERVIEW OF ALL
ITEMS IN ONE

10
12
6
7
8 9
11
13
18 19
16
17
14 15
27
20
26
25
21
22 23 24
28
34 35 36
33
29 30
31 32
40 41
38
37
42
39

CLICK TO OPEN
A BROWSER WITH
URL
CLICK TO OPEN
DELPHI URL
CLICK TO OPEN
ITEM (NUMBER)

CLICK TO OPEN
THE TOTAL
OVERVIEW OF ALL
ITEMS IN ONE

WINDOWS 10

BLAISE PASCAL MAGAZINE


AUTHORS ALFABETICAL

ALL ISSUES IN ONE FILE

www.blaisepascal.eu/subscribers/UK/UK_CD_DVD_USB_Department.html
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

38

TIPS AND TRICKS WITH KBMMEMTABLE


BY KIM MADSEN
starter

expert

Delphi

kbmMemTable is an in memory row/column


oriented database which have a vast
number of features and which is very fast in
both storing data and locating stored data.
I will in this article focus on some basic
things and on some of the less known,
but very useful, features of kbmMemTable.

The Codegear edition is for free and can do it all


But it only supports specific versions... eg only
new xe8 has supprt for latest kbmmemtable.
Xe7 supports older kbmmt and edition xe2 even
older etc.

CREATE A MEMORY TABLE WITH


INDEXES IN CODE
var
mt:TkbmMemTable;
begin

// Define fields (you may already


// have them defined at designtime).
mt.FieldDefs.Add('ID',ftInteger);
mt.FieldDefs.Add('Name',ftString,80);
mt.FieldDefs.Add('Address',ftString,80);
// Define indexes (if you have a large amount of records
in your mt
// indexes will make searches faster, but inserts/edits slower).
mt.IndexDefs.Add('iID','ID',[ixPrimary]);
mt.IndexDefs.Add('iName',
Name',[ixCaseInsensitive]);
mt.CreateTable;
mt.Open;
end;

When searching on for records using the Name


field as search criteria, we have defined that it
should search case insensitively, so for example
uppercase A is the same as lowercase a.
FAST INSERTION INTO THE DATABASE

kbmMemTable exists in two primary versions,


Standard Edition and Professional Edition.
Standard Edition can be purchased separately,
while Professional Edition only is available as a
bundle with kbmMW Professional or kbmMW
Enterprise Edition.
While kbmMemTable Professional is the
absolutely fastest memory table in the world,
kbmMemTable Standard Edition can almost reach
its performance when used correctly.
If you are to insert a large number of records in
kbmMemTable Standard Edition, then you will
want to do like this:
39

COMPONENTS
DEVELOPERS

PAGE 1/9

var
fldID,
fldName,
fldAddress:TField;
begin

// First get quick access to the fields.


// This prevents having to search for a field each
// and every time a record is to be inserted.
fldID:=mt.FieldByName('ID');
fldName:=mt.FieldByName('Name');
fldAddress:=mt.FieldByName('Address');
// Prevent update of attached controls on each insert.
// This is a big performance factor when inserting huge
// amounts of records.
mt.DisableControls;
// Prevent update of indexes on each insert. Professional
// Edition is much faster in on the fly updates of indexes,
// so it will perform extremely fast even without disabling
// indexes while inserting. But the highest performance
// is obtained by disabling them before the batch insert.
mt.EnableIndexes:=false;
// Insert a lot of records.
for i:=1 to 1000000 do
begin
mt.Append;
fldID.AsInteger:=i;
fldName.AsString:='Name'+inttostr(i);
fldAddress.AsString:='Address'+inttostr(i);
mt.Post;
end;

// Enable and update indexes.


mt.EnableIndexes:=true;
mt.UpdateIndexes;
// Enable updating attached controls (grids etc).
Mt.EnableControls;

HOW TO SEARCH?

You either choose to switch to the index


representing the column(s) you want to search
on, or you simply just search and let
kbmMemTable find a relevant index (if any) to
use for the search.
If you want the absolutely fastest result, then you
should switch to the index first, to avoid that
kbmMemTable have to make an additional
search on your current index to sync with the
found place in the search index.
However the second index sync search is
generally very fast.
You can use Locate to search for a complete name
for example. If it finds a record with the given
name, that record will be the current record, and
you can access other fields in it immediately.
if mt.Locate('Name','Jens Hansen',[]) then
MessageDlg('Found record.
Address='+mt.FieldByName('Address').AsString);

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

TIPS AND TRICKS WITH KBMMEMTABLE


You can use Lookup to search for a record
containing the given value, and return the
contents of any field as result. It will not move
the current record.

PAGE 2/9
PAGE - 2/4

Filter records using a filter expression


mt.Filter:='(ID>=10) AND (ID<=20)';
mt.Filtered:=true;

This will give the same result as using the range


above, but do not require any indexes. It however
scans every record to find matching records, and
will thus be slow on large datasets.
The advantage with the expression filter is that you
can make quite complex filters using AND/OR.
When a field name contains spaces, you must
enclose the field name in brackets. For example:

v:=mt.Lookup('Name','Jens Hansen','Address');
if not VarIsNull(v) then
MessageDlg('Found record.Address='+v);
You can also search for partial values
if mt.Locate('Name','Jens',[loPartialKey]) then
MessageDlg('Found record.
Address='+mt.FieldByName('Address').AsString);
How to search on multiple fields?

if mt.Locate('Name','Jens',[loPartialKey])
then MessageDlg('Found record. Address='+mt.FieldByName('Address').AsString);
Let kMemTable automatically create indexes for you
to improve search performance
mt.AutoAddIndexes:=true;

[Home State] = 'CA' or [Home State] = 'MA'

The FilterOptions property controls case sensitivity


This may however result in creation of many indexes and filtering on partial comparisons.
over time if you often search on new columns and
The Filter can be changed at runtime at wish.
having many indexes to maintain when
The following field types can be part of a filter:
inserting/editing/deleting records will always give a
performance penalty, so use with care.
ftSmallInt, ftWord, ftInteger,
ftAutoInc, ftFloat, ftCurrency,
ftString, ftFixedChar, ftDate, ftTime,
HOW TO SORT?
ftDateTime, ftBoolean, ftBCD, and any
Adding an index will always result in the data
other field which can return a string or a numeric
being sorted according to an index. So you can
value. The following operators are available:
add an index and switch to it.

mt.AddIndex('iMyIndex','Name',[ixDescending]);
mt.IndexName:='iMyIndex';

Or even easier:
mt.SortOn('Name',[mtcoDescending]);

HOW TO ONLY SHOW CERTAIN RECORDS?

For this purpose, you can use a range or a filter.


A range can limit the records displayed in for
example a grid, to show only records within a
range (eg. 10<=ID<=20, only show records where the
ID is between 10 and 20). The fields participating in
the range must be part of an index.
mt.IndexName:='iID';
mt.SetRange([10],[20]);

This will use the index iID, and setup a range to


only make records having ID in the range 10 to 20
(inclusive) available.
Another variant exists of the SetRange method,
which accepts a string which contains names of
fields that is to be filtered on. A new range index
will automatically be generated, and switched to.
mt.SetRange('ID',[10],[20]);

Cancel a range again by


mt.CancelRange;

Delete all records within a range


mt.DeleteRange('ID',[10],[20]);

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

Arithmetic:

+ Addition can be used on all numeric


and string expressions.
- Subtraction can be used on all numeric expressions.
* Multiplication can be used on
all numeric expressions.
/ Division can be used on all numeric expressions.
Conditions:

=
Equal
>
Greater than
<
Less than
>= Greather than or equal
<= Less than or equal
<> Not equal
IS NULL
True if expression is null
IS NOT NULL True if expression is not null
NOT
Negates the boolean expression
OR
True if one of the two boolean
expressions are true
AND
True if both the two boolean
expressions are true
IN ( ... )
True if the result of the left side
expression is found in the list
of values.
LIKE '....'
True if the left side string
expression is matching the
wildcards in the right side string.

COMPONENTS
DEVELOPERS

40

TIPS AND TRICKS WITH KBMMEMTABLE


The wildcards consists of:

* Matches any number of unknown characters.


? Matches exactly one unknown character.
An example: Fld1 LIKE 'JOHN*'
Functions:

UPPER( expression )
Convert string expression to uppercase.
LOWER( expression )
Convert string expression to lowercase.
SUBSTRING( expression , startpos , count )
Extract a substring from the expression string
starting at startpos and with max length of count
chars. The count parameter can be omitted, in
which case is means the rest of the string.
TRIM( expression ) Trim string expression for all
leading and trailing spaces.
TRIMLEFT( expression ) Trim string expression
for all leading spaces.
TRIMRIGHT( expression ) Trim string expression
for all trailing spaces.
GETDATE Returns a floating point date/time
value for current time.
YEAR( expression ) Return the year of a floating
point date/time value.
MONTH( expression ) Return the month of the year
(1..12) of a floating point date/time value.
DAY( expression ) Return the day of the month
(1..28/29/30/31) of a floating point date/time
value.
HOUR( expression ) Return the hour (0..23) of a
floating point date/time value.
MINUTE( expression ) Return the minute (0..59) of
a floating point date/time value.
SECOND( expression ) Return the second (0..59) of
a floating point date/time value.
DATE( datestring , formatstring )
Convert the datestring to a floating point
date/time value according to the
SysUtils.FormatDateTime format string .
DATE( expression ) Return the date part of a
floating point date/time value.
TIME( timestring , formatstring )
Convert the timestring to a floating point
date/time value according to the
SysUtils.FormatDateTimeformat string .
TIME( expression ) Return the time part of a
floating point date/time value.

PAGE 3/9

This creates a new index, ordered (and quickly


searchable) by ID, and filtered by the given
filter string. You can filter on any field or
calculation of fields. It doesn't have to be fields
that are in the key fields list.
Every time you add or alters records, the index
will be updated and records may this way
disappear or appear in the filtered index
depending on the values of the record.
When you switch to a filtered index, and thus
only show the records participating in that
index, scrolling through or searching isjust as
fast as if you had no filter defined. Inserts and
updates have a small performance penalty, so
if you have many records to insert, use the
batch insert method outlined earlier in this
article.
STANDARD INDEXES?

With standard indexes, I refer to indexes


which are created/defined by the memtable
during its course of operation.
When a memory table is opened, there always
exist one single index, the row order index. It's
the index that is updated to ensure that order
of insertion of records can be established.
It's also responsible for holding a reference
to all records. The row order index is selected
by setting the IndexName to an empty string.
Internally you will find the roworder index to
be named '__MT__ROWORDER_'. A string constant
exists for that name: kbmRowOrderIndex.
The row order index can't be deleted.
Further a number of other indexes may be
defined at various occations.

Filtering however is done on each record every


time that record is accessed, thru scrolling or
counting number of records etc. So it's fairly slow
on large datasets to use a filter.
A better way, is to create a filtered index. That is a
real index which only contains references to records
that live up to the filter expression.
mt.AddFilteredIndex('iFilteredIndex','ID',[],'(ID>=10) AND (ID<=20)',[]);

41

COMPONENTS
DEVELOPERS

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

TIPS AND TRICKS WITH KBMMEMTABLE


'__MT__DETAIL_' (kbmDetailIndex).
This index is created (and automatically recreated)
when the master record changes in a master/detail
relationship. It ensures that browsing through
detail records is very fast, and quick to search on.
'__MT__DEFSORT_' (kbmDefSortIndex).
This index is created when you are using the Sort
or SortOn methods. It is only recreated when you
issue another Sort/SortOn method call.

PAGE 4/9

kbmMemTable supports whats called

transaction management via the methods


StartTransaction, Commit and Rollback.
Further it also supports an Undo function on
the record level. More about that in a moment.
To use transaction management, one also need
to understand versioning.

'__MT__DEFAULT_' (kbmDefaultIndex).
This index is automatically created when you set
IndexFieldNames to a set of fields for which no
index already exist.

kbmMemTable have two properties controlling


versioning: EnableVersioning and
VersioningMode. EnableVersioning is a
Boolean, while VersioningMode can have
the value of mtvm1SinceCheckPoint or
mtvmAllSinceCheckPoint.

'__MT__RANGE_' (kbmRangeIndex).
This index is automatically created when you
apply a range filter.

Versioning allows kbmMemTable to store

'__MT__AUTO_' (kbmAutoIndex). This is


actually not only one index, but potentially many
indexes, which are automatically created if you
have the property AutoAddIndexes set to true,
and you make a search locate/lookup)
on fields for which there are no existing indexes
available. The actual indexname will be
'__MT__AUTO_' + the semicolon separated field
names. Be aware that this index is not
automatically destroyed, so if you search on many
weird combinations of fields, you may end up
with many indexes, and may need to delete them
yourself if needed.

Default EnableVersioning is false and thus


the records you see in the memory table are
the ones you have.
No other versions are created.

INDEX INHERITANCE

If you want to temporarily sort on a different


field than the ones participating on the current
selected index (it could for example be a filtered
index), you simply do SortOn, as described
earlier on, while having the filtered index
selected.
This will create a new sorted index, based on
the filtered index, so only records that are
acceptable in the base index, are candidate for
inclusion in the new sort index.
TRANSACTIONS AND VERSIONING

If you make modifications to a number of


records in a table, it would, in some situations,
be nice to be able to roll back those changes in
one go. For example if you have an application
where the user can make modifications to a
number of entries (inventory for example), and at
a later stage decide to save those changes or
cancel them. Cancelling the changes would
require a roll back, while saving means that the
user commits to his changes.
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

multiple versions of the same record.


Whenever a record is inserted into the
memory table, one version of that record
exists, the one that was inserted.

If EnableVersioning is set to true, then


kbmMemTable will start to make multiple
versions of a record, depending on the setting
of VersioningMode.
If VersioningMode is set to
mtvm1SinceCheckPoint, then at most, an
original version is kept, and the altered one.
This is cool, if you need to update an external
storage with the changes made in the memory
table.

Then the original version identifies what you


have to search for, and the new version
identifies what to change to.
A deltahandler is what is used for handling
those scenarios, but now we will focus on
transactions. So in this case, you can only
undo back to the original version, and not
intermediate changes.

COMPONENTS
DEVELOPERS

42

TIPS AND TRICKS WITH KBMMEMTABLE

PAGE 5/9

If you compile and upload the Arduino code as described in the


yousee
insert/append
records, those will be
previous articles, and then connect to it with Visuino,When
you will
the
mt.AppendRecord();
with usInserted as their
sine wave deformed by the quadratic function plottedmarked
in the scope:
mt.StartTransaction;
UpdateStatus
propery. That also happens when
mt.Edit.mt.Post;
you load the records from another dataset, because
mt.Delete;
mt.Rollback;
technically they have been inserted into the
memory table. You can however ask kbmMemTable
In this example a record is inserted, a transaction is
to consider these newly loaded records, as being
started, then the record is edited and then deleted.
the original ones and thus that they should have
With VersioningMode set to
the UpdateStatus of usUnmodified.
mtvm1SinceCheckPoint, then you can only
choose to roll back to where the record was
mt.CheckPoint;
inserted. The version containing the edited record is
The latest version of any record is now considered
lost the moment the record is deleted. Only one
the original version and marked as
version in addition to the original record is kept.
To allow for a multi level undo, then
usUnmodified, and all intermediate versions
VersioningMode should be set to
have been removed. That also means that if records
mtvmAllSinceCheckPoint. Then you can do
were deleted, they are now permanently deleted
this:
and can't be recovered. To undo last change of the
mt.AppendRecord();
current record, you can use:
mt.StartTransaction; // First level of transaction
Example:

mt.Edit.mt.Post;
mt.StartTransaction; // Second level of transaction
mt.Delete;
mt.Rollback;
// Back to the edited record again
mt.Rollback; // Back to the original appended record

You see you can nest transactions this way.


If you want to make a change, that is protected by a
StartTransaction, stick, then you call
mt.Commit. After the commit, the change can only
be rolled back if there are multiple levels of nested
transactions as in this example:
mt.AppendRecord();
mt.StartTransaction; // First level of transaction
mt.Edit.mt.Post;
mt.StartTransaction; // Second level of transaction
mt.Delete;
mt.Commit;
// We will keep the delete but not the

edited record.
mt.Rollback; // Ah no.. we will revert to the original

appended record

You can check the state of a record by checking the


UpdateStatus property. It returns the current
records state. Was it inserted (usInserted),
deleted (usDeleted), modified (usModified)
and unchanged (usUnmodified). To actually see
usDeleted marked records, it require adding and
switching to a special index that allows for showing
deleted records check the overloaded version of
AddIndex which accepts providing a set of
UpdateStatus flags, for which records to include
in the index.

43

COMPONENTS
DEVELOPERS

mt.Undo;

Remember that the number of undo's you can


make on the current record, depends on the
setting of EnableVersioning and
VersioningMode.
THE DELTAHANDLER

A deltahandler is a class that can scan through


a memory table, and determine what has
happened with each record, and allow the
developer to do something depending on what
happened.
Was it inserted, deleted, modified or was
nothing changed with it?
It is essentially a great tool for making changes
in your dataset resolve back to from where the
data originated from (typically a SQL database or
files in a file system etc).
This is a simple deltahandler:
// An example on how to create a deltahandler.
TMyDeltaHandler = class(TkbmCustomDeltaHandler)
protected
procedure InsertRecord(var Retry:boolean;
var State:TUpdateStatus); override;
procedure DeleteRecord(var Retry:boolean;
var State:TUpdateStatus); override;
procedure ModifyRecord(var Retry:boolean;
var State:TUpdateStatus); override;

// procedure UnmodifiedRecord(var Retry:boolean;


var State:TUpdateStatus); override;
end;

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

TIPS AND TRICKS WITH KBMMEMTABLE

PAGE 6/9

procedure TMyDeltaHandler.InsertRecord(var Retry:boolean; var State:TUpdateStatus);


var i:integer; s,sv:string; v:variant;
begin
s:='';
for i:=0 to FieldCount-1 do
begin
v:=Values[i];
if (VarIsNull(v)) then sv:='<NULL>'
else if not (Fields[i].DataType in kbmBinaryTypes) then sv:=v
else
sv:='<Binary data>';
s:=s+sv+' ';
end;
ShowMessage(Format('Inserted record (%s)',[s]));
end;
procedure TMyDeltaHandler.DeleteRecord(var Retry:boolean; var State:TUpdateStatus);
var i:integer; s,sv:string; v:variant;
begin
s:='';
for i:=0 to FieldCount-1 do
begin
v:=Values[i];
if (VarIsNull(v)) then sv:='<NULL>'
else if not (Fields[i].DataType in kbmBinaryTypes) then sv:=v
else sv:='<Binary data>';
s:=s+sv+' ';
end;
ShowMessage(Format('Deleted record (%s)',[s]));
end;
procedure TMyDeltaHandler.ModifyRecord(var Retry:boolean; var State:TUpdateStatus);
var i:integer; s1,s2,sv:string; v:variant;
begin
s1:='';
s2:='';
for i:=0 to FieldCount-1 do
begin
v:=Values[i];
if (VarIsNull(v)) then sv:='<NULL>'
else if not (Fields[i].DataType in kbmBinaryTypes) then sv:=v
else sv:='<Binary data>';
s1:=s1+sv+' ';
v:=OrigValues[i];
if (VarIsNull(v)) then sv:='<NULL>'
else if not (Fields[i].DataType in kbmBinaryTypes) then sv:=v
else sv:='<Binary data>';
s2:=s2+sv+' ';
end;
ShowMessage(Format('Modified record (%s) to (%s)',[s2,s1]));
end;

//procedure TMyDeltaHandler.UnmodifiedRecord(var Retry:boolean; var State:TUpdateStatus);


//begin
//end;
And you start the deltahandler like this:
var
myDeltaHandler:TMyDeltaHandler;
begin
myDeltaHandler:=TMyDeltaHandler.Create(nil);
try
mt.DeltaHandler:=myDeltaHandler;
mt.Resolve;
finally
mt.DeltaHandler:=nil;
myDeltaHandler.Free;
end;
end;

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

44

TIPS AND TRICKS WITH KBMMEMTABLE


LOADING DATA

Data can be loaded into a memory table in multiple


ways:
1.
2.
3.

Inserting them one record at a time


Loading the data from another dataset
Loading the data from a file

As for 1, you can use the ordinary Insert/Post, or


AppendRecord methods.
As for 2, you can use:
mt.LoadFromDataset(anotherdataset,
[mtcpoStructure]);
This will make your memory table have the same
fields as the anotherdataset (due to the
mtcpoStructure option), and load a copy of all data
from the dataset into the memory table.
A number of copy options exists:

PAGE 7/9

That require that the indexes are updated on the


fly for each record. Standard Edition will have a
larger performance penalty than Professional
Edition of kbmMemTabl e for this situation.
mtcpoIgnoreErrors:
Ignore any copy errors instead of stopping
copying.
mtcpoLookupAsData:
Copy over lookup fields a regular datafield, with
its matching data.
mtcpoCalculatedAsData:
Copy over calculated fields as a regular data field
with its calculated data.
mtcpoStringAsWideString:
Assume that source ftString, ftFixedChar fields
should be created as ftWideString in your local
memory table.
mtcpoWideStringUTF8:
If string/character/memo fields in the source
dataset do not match wideness of the same in
the destination dataset (your local memory table),
then automatically encode or decode to/from
UTF8. Eg.

mtcpoStructure:
Copies table structure (field definitions) from the
source. Any field definitions you had in your local
If the source dataset has field 'str1' which is a
memory table are removed.
mtcpoOnlyActiveFields:
ftWideString field, while the local
Only field definitions in the source that actually
memorytable has the same field 'str1' defined as a
are represented as a true field, are copied.
ftString field, it will UTF8 encode the data
mtcpoProperties:
coming from the source field before putting it into
Copy over field properties like DisplayWidth,
the destination field. Similarly if a source field is
DisplayLabel, Required, ReadOnly,
of type ftString, but the destination is of type
Visible, DefaultExpression,
ftWideString then an UTF8 decoding will take
Alignment, ProviderFlags, Lookup,
place before putting the value into the destination
LookupCache, LookupDataset,
field.
The same takes place with
LookupKeyFields, LookupResultField,
ftMemo/ftWideMemo
and
KeyFields, DisplayFormat,
ftFixedChar/ftWideFixedChar.
EditFormat, MaxValue, MinValue,
DisplayValues, TransLiterate,
If you already have an existing field structure in
Precision, Currency and BlobType.
mtcpoLookup:
your local memory table, and want to copy fields
Also copy lookup fields from the source dataset.
from a source dataset, where the field names do
mtcpoCalculated:
not match, you can take advantage of field name
Also copy calculated fields from the source
mapping.
dataset.
mtcpoAppend:
Eg.
Append records to the existing records in the
mt . LoadFromDataset ( anotherdataset ,
memory table. This cant be used with
[], 'str1=str_1;int2=int_2' );
mtcpoStructure or mtcpoProperties.
mtcpoFieldIndex: Copy the index position of
This is telling kbmMemTable that the local field
fields to ensure field order is the same as in the
named str1 is called str_1 in the source table etc.
original.
As for option 3, loading from a file, you can use:
mtcpoDontDisableIndexes:
mt.LoadFromFileViaFormat('somefilename',
Default a batch insertion is made, where indexes
someformatinstance) ;
are only updated after all records have been
copied over. However if you for example have an
index with unique constraint on a field, then you
might want to have your copy to stop early, if
there is a duplicate of that field value.
45

COMPONENTS
DEVELOPERS

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

TIPS AND TRICKS WITH KBMMEMTABLE


is an instance of a stream
format component. kbmMemTable comes with
two, TkbmMemBinaryStreamFormat and
TkbmMemCSVStreamFormat.
The binary stream format class stores data
compactly and efficiently, and is giving the
fastest performance. However the CSV stream
format class allows you to read in (and write out)
comma separated formatted data, for easy
integration with other external systems.
kbmMW (our middleware product) comes with
additional stream formatters for XML and JSON.
Each stream format instance have a number of
flags that can be set, to tell how it's supposed to
handle the reading or writing of data, but that is
a story for another time.

Its practical if the data of field fld1 is required to


be quickly accessible in your string list. In this
example, s will only contain the value of fld2. If
you need the field value both as an object and as
part of the string, you can include it twice in your
field list.
Eg. 'fld1;fld1;fld2'.

someformatinstance

A different extraction method is GetRows which


returns requested fields as an array of an array of
variant.
var v:variant;
begin
v:=mt.GetRows(kbmGetRowsRest, kbmBookMarkFirst,1);
end;

sf:=TkbmMemCSVStreamFormat.Create(nil);
try
mt.LoadFromFileViaFormat('.\mydata.csv',sf)
finally
sf.Free;
end;

EXTRACTING DATA

A number of methods exist for easily extract data


from a memory table.
mt.Extract('fld1;fld2',slData);
This one will extract the fields fld1 and fld2 for all
records to a TStrings instance (TStringList
typically) that you will have to create beforehand.
If the optional AFormat string is given, each line
in TStrings will be formatted according to that
format. If none is given, then all field values for a
record will be separated by a space.
The above example will thus put a space between
the value of fld1 and fld2 for each record/line.
mt.Extract('fld1;fld2',slData,'%s=%s');

This will return an array of variant containing an


array of variant of size 1, with the contents of the
field with FieldNo=1.
Eg. v[0,0] is the value of the first field of the first
record.
Since we specified to ask for kbmGetRowsRest
number of records, starting with
kbmBookMarkFirst, then we are essentially
asking for all records. If we wanted to start from
another place in the record set, you should
provide a TBookmark value for that place.
That is created by navigating to the record, then
use the Bookmark function to obtain a bookmark
for exactly that place in the record set.
And by providing a number instead of
kbmGetRowsRest, you can limit the number of
records returned to that particular count.
The last argument of GetRows, can either be a
field number, a field name or an array of field
numbers or an array of field names.

This will put an equal


v:=mt.GetRows(kbmGetRowsRest,
sign between the value
of fld1 and fld2.
It is also possible to specify that the first field (in
the following case fld1) should be stored as an
object for the text line in the strings list.
mt.Extract('fld1;fld2',slData,'',true);
var v:variant;
Then you can
begin

PAGE 8/9

kbmBookMarkFirst,VarArrayOf('fld1','fld2'));

access that value by:

v:=TkbmVariantObject(slData.Objects[i]
).Value;
s:=slData.Strings[i];

end;

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

46

TIPS AND TRICKS WITH KBMMEMTABLE

PAGE 9/9

STATISTICS

kbmMemTable contains a number of


functions to allow very fast grouping and
calculation of statistical values for your
data. Returning a sum of fld1 for all records
visible in the current index.

Having the following values in mt

var v:variant;
begin
v:=mt.Aggregate('fld1:SUM');
end;

v will contain the sum of fld1 for all records in the


current index.

fldCountry

fldSales

US

10

GB

20

US

30

DE

15

DE

GB

10

US

12

var
v:variant;
begin
v:=mt.Aggregate('fld1:SUM;fld1:STDDEV;fld1:COUNT');
end;

v[0] will contain the sum of fld1 for all records in


the current index. v[1] will contain the standard
deviation for the records, and v[2] will contain the
count of records. The following functions can be
used:
MAX, MIN, AVG, COUNT, SUM, STDDEV
and USR1,USR2,USR3.
The USRx functions are special functions, where
the developer have to provide the calculation in
the eventhandler OnUserAggregate.
If you have data that you want to aggregate in
groups, like how big sales in each country, then
you can use the GroupBy methods.
As grouping often will result in a number of
records with aggregated data, then GroupBy
require a destination memory table to put the
result in.

Will result in the following values


in mtGrouped
fldCountry fldSales_Sum fldSales_MAX fldSales_COUNT
DE

20

15

GB

30

20

US

52

30

There are loads of more features available in


kbmMemTable
and this article touches only a subset of them. But
go exploring
and you will find attached datasets, which is
multiple datasets sharing the same data without
holding a copy of it.. essentially an advanced
version of having multiple cursors into the data,
SQL support, complex math expression evaluation
via the SQL manager, locale support etc.

var mtGrouped:TkbmMemTable;
begin
mtGrouped:=TkbmMemTable.Create(nil);
try
mt.GroupBy(mtGrouped,
'fldCountry',
'fldCountry;fldSales:SUM;fldSales:MAX;fldSales:COUNT');

Go have fun!
Kim Madsen

finally
mtGrouped.Free;
end;
end;

The mtGrouped table will be left open for you,


with the fields named fldCountry, fldSales_SUM,
fldSales_MAX and fldSales_COUNT defined in it.

47

COMPONENTS
DEVELOPERS

Issue Nr 6 2015 BLAISE PASCAL MAGAZINE

Now faster than ever!


Improved publish/subscribe message queues
Improved XML/JSON marshalling support
Delphi/C++Builder/RAD Studio XE8

- Native high performance 100% developer


defined application server with support for
loadbalancing and failover
- Native high performance JSON and XML
(DOM and SAX) for easy integration with
external systems
- Native support for RTTI assisted object
marshalling to and from XML/JSON, now also
with new fullfeatured XML schema
(XSD) import
- High speed, unified database access
(35+ supported database APIs) with
connection pooling, metadata and
data caching on all tiers
- Multi head access to the application server,
via AJAX, native binary, Publish/Subscribe,
SOAP, XML, RTMP from web browsers,
embedded devices, linked application
servers, PCs, mobile devices, Java systems
and many more clients
- Full FastCGI hosting support. Host PHP/Ruby
/Perl/Python applications in kbmMW!
- KBMMW V. 4.80 AMQP support

Supports Delphi/C++Builder/RAD Studio 2009


to XE8 (32 bit, 64 bit and OSX where applicable).
kbmMW for XE5 to XE8 includes full support for
Android and IOS (client and server).!
kbmMemTable is the fastest and most feature rich
in memory table for Embarcadero products.
-

Easily supports large datasets


with millions of records
Easy data streaming support
Optional to use native SQL engine
Supports nested transactions and undo
Native and fast build in M/D,
aggregation /grouping,
range selection features
Advanced indexing features for
extreme performance

Warning!

kbmMemTable and kbmMW


are highly addictive!
Once used, and you are hooked for life!

( Advanced Message Queuing Protocol)


-

Added AMQP 0.91 client side gateway


support and sample.
Updated StreamSec TLS transport plugin
component (by StreamSec).
Improved performance on Indy TCP/IP
Client messaging transport for large number
of inbound messages.

COMPONENTS
DEVELOPERS

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.

You might also like