blaisepascalmagazine 44 uk
DESCRIPTION
O V E R V I E W O F D E L P H I T O D E L P H I H I S T O R YE U C L I D E SA N A G E P U Z Z L EB Y D A V I D D I R K S EA R D U I N O : T H E V I S U I N O P R O J E C T - P A R T 4I N T E R N E T O F T H I N G S - WI T H A R D U I N O A N D D E L P H IU S E E T H E R N E T S H I E L D , P R O G R A M I T W I T H V I S U I N O ,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 NO V E R T H E L O C A L N E T W O R K O R I N T E R N E TB Y B O I A N M I T O VD A T A B A S E WO R K B E N C H 5T 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 SB Y P E T E R V A N D E R S M A NT I P S A N D T R I C K S WI T H K B M M E M T A B L EB Y K I M M A D S E NTRANSCRIPT
-
PRINTED ISSUE PRICE 15.00
DOWNLOAD ISSUE PRICE 7 .50
BLAISE PASCAL MAGAZINEBLAISE PASCAL MAGAZINEBLAISE PASCAL MAGAZINE 444444
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 A L 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
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,
CONNECT FROM A DELPHI APPLICATION
OVER THE LOCAL NETWORK OR INTERNET
BY BOIAN MITOV
DATABASE WORKBENCH 5
THE SWISS ARMY KNIFE FOR DATABASES
BY PETER VAN DER SMAN
TIPS AND TRICKS WITH KBMMEMTABLE
BY KIM MADSEN
-
CONTENTS
BLAISE PASCAL MAGAZINEBLAISE PASCAL MAGAZINE
Articles
D E L P H I, L A Z A R U S, S M A R T M O B I L E S T U D I O,
A N D P A S C A L R E L A T E D L A N G U A G E S
F O R 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
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE
444444
OVERVIEW OF DELPHI TO DELPHI HISTORY PAGE 6
EUCLIDES PAGE 7
AN AGE PUZZLE PAGE 8
BY DAVID DIRKSE
ARDUINO: THE VISUINO PROJECT - PART 4
INTERNET OF THINGS - WITH ARDUINO AND DELPHI
USE ETHERNET SHIELD, PROGRAM IT WITH VISUINO,
CONNECT FROM A DELPHI APPLICATION
OVER THE LOCAL NETWORK OR INTERNET
BY BOIAN MITOV PAGE 12
DATABASE WORKBENCH 5
THE SWISS ARMY KNIFE FOR DATABASES
BY PETER VAN DER SMAN PAGE 28
TIPS AND TRICKS WITH KBMMEMTABLE
BY KIM MADSEN 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
2
-
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.
Copyright notice
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 [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
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]
Authors - Christian name in alphabethical order
A Andrea Raimondi ,
B Peter Bijlsma, Dmitry BoyarintsevStephen Ball,
C Marco Cant, Michal Van Canneyt,
D David Dirkse, Daniele Teti
F Bruno Fierens
G Primo Gabrijeli, Mattias Gaertner
H Fikret Hasovic
J Cary Jensen
L Wagner R. Landgraf, Sergey Lyubeznyy
K Max Kleiner
M Kim Madsen, Felipe Monteiro de Cavalho
N Jeremy North,
O Inoussa Ouedraogo
P Howard Page-Clark,
S Rik Smit, Bob Swart,
Z Siegfried Zuhr
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
Bruno Fierens
www.tmssoftware.com
bruno.fierens @ tmssoftware.com
Primo Gabrijeli
www.
primoz @ gabrijelcic.org
Cary Jensen
www.jensendatasystems.com
http://caryjensen.blogspot.nl
Wagner R. Landgraf
wagner @ tmssoftware.com
Kim Madsen
www.component4developers
Peter van der Sman
Jeremy North
jeremy.north @ gmail.com
Benno Evers
b.evers
@everscustomtechnology.nl
Detlef Overbeek - Editor in Chief
www.blaisepascal.eu
editor @ blaisepascal.eu
Howard Page Clark
E-mail: hdpc @ talktalk.net
Rik Smit
rik @ blaisepascal.eu
Bob Swart
www.eBob42.com
Bob @ eBob42.com
Max Kleiner
www.softwareschule.ch
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
Issue Nr 5 2015 BLAISE PASCAL MAGAZINE
Daniele Teti
www.danieleteti.it
Andrea Raimondi
John Kuiper
Wim Van Ingen Schenau
-Editor
wisone @ xs4all.nl
3
-
BLAISE PASCAL MAGAZINEis proud to announce the first edition of David Dirkses book:
COMPUTER & MATH
IN GAMES PASCAL
DAVID DIRKSEpresales at
www.blaisepascal.eu/DavidDirkse/ComputerMath_Games.html
procedure ;
var
begin
:= for to doi 1 9
begin
;end
end;
-
DAVID DIRKSEs
www.blaisepascal.eu/DavidDirkse/ComputerMath_Games.html
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
COMPUTER & MATH
IN GAMES PASCAL
-
Page 9
WATER CLOCK - CHINA - BEGINNING OF TIME (BC 4000)
Some authors claim that water clocks appeared in China
as early as 4000 BC
DELPHI
The cult of Apollo at
Delphi probably
dates back to
the 700s B . C .,
Plato in Classical Attic;
428/427 or 424/423 348/347 BC)
was a philosopher, as well as mathematician,
in Classical Greece.
Pythagoras Philosopher
Pythagoras of Samos was an
Ionian Greek philosopher, mathematician,
and founder of the religious movement
called Pythagoreanism.
Born: 571 - 495 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
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
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
Roman Empire 700 BC
Decay of the Roman Empire 500
Building of Europe
Babbage Difference Engine No. 2
OVERVIEW OF DELPHI TO DELPHI HISTORY
Building of France 5852 BC
Building of Spain 912 and Portugal 800
Discovery of Amerca by the Vikings 990 - 1050
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.
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
Niklaus Wirthborn 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.
Windows
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.
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
DELPHI XE8
DELPHI 7 released in August 2002
DELPHI released February 14, 1995
Embarcadero Technologies in 2008.
Codegear Delphi 2007.
Charles Babbage - mathematician conceived of the first programmable computer in the 1830s
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.
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.
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.
Difference Engine No. 1, portion,1832
ISSUE 40
IN THIS ISSUE (43)
-
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE 7
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
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.
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.
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.
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.
EUCLIDES
-
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE
8
AN AGE PUZZLE PAGE 1/4BY DAVID DIRKSE
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. How Black may know that it is White's
birthday?
2. What is White's age?
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:
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 the bottom of the form at full width there is
a statictext component for messages.
The picture below shows : 4 solutions found.
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.
And these global variables:
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.
Tsolution = record
: ;birthyear word
: ;age byte
: ;birthdaypassed boolean
;end
var : ;thisyear word
: [ ] ;solutions maxsolution Tsolutionarray of1..
: ; solutionNr byte
-
9MaxSolution 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.
The procedure ShowSolutions:
function ( : ) : ;sumdigits year word byte
var string : ;s
: ;i byte
begin
:= ( );s inttostr year
:= ;result 0
:= ( ) := + ( [ ])- ( );for to doi length s result result ord s i ord1 '0'
end;
Ord('0') is 48, the ASCII code of digit 0.
So ord('8') ord('0') = 8.
Without -ord('0') we would get 56.
THE SEARCH PROCESS
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 to do := birthyear startyear thisyear
begin
:= ( );digitsum sumdigits birthyear
= - if thendigitsum thisyear birthyear
( , , );savesolution digitsum birthyear true
= - - if thendigitsum thisyear birthyear 1
( , , );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
-
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE10
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 doform1 PaintBox1
with docanvas
begin
. := ;brush Style bsSolid
. := ;brush color $ffffff
( ( , , , ));fillrect rect width height0 0
;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 in thenkey key'0' '9' #08 #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
// starts search
begin
= if thenkey VK_RETURN
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!
AN AGE PUZZLE PAGE 3/4
-
50 hands-on recipes to master the power of Delphi for
cross-platform and mobile development on Windows,
Mac OS X, Android, and iOS
Daniele Teti
Quick answers to common problems
Delphi Cookbook
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
OICH EC SR
OT
ID
E
A
MAZIN
G
BLAISE PASCAL
MAGAZINE
30,00
including VAT
39
including
the
printed
book,
ebook
and shipping
-
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE12
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... :
ARDUINO: THE VISUINO PROJECT - PART 4 PAGE - 1/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
-
13
Set the IP address for the Arduino
as example 192.168.0.55:
And set the Enabled property of the IPAddress
to True so the IP address will be used when
Arduino starts:
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:
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE
Add TCP/IP Server socket:
Set the UseDHCP to false, so Arduino will
work with a fixed IP address:
ARDUINO: THE VISUINO PROJECT - PART 4 PAGE - 2/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
-
6 Issue Nr 6 2015 BLAISE PASCAL MAGAZINE14
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:
ARDUINO: THE VISUINO PROJECT - PART 4 PAGE - 3/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
-
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE 15
Connect the data source to the Input Pin of the
Server Socket:
Press F9 to generate the Arduino
Sketch and open the Arduino IDE:
ARDUINO: THE VISUINO PROJECT - PART 4 PAGE - 4/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
-
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE16
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:
ARDUINO: THE VISUINO PROJECT - PART 4 PAGE - 5/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
-
17Issue Nr 6 2015 BLAISE PASCAL MAGAZINE
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: Set the Port to 8080:
ARDUINO: THE VISUINO PROJECT - PART 4 PAGE - 6/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
-
18 Issue Nr 6 2015 BLAISE PASCAL MAGAZINE
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:
Drop TCLTerminal on the form:
ARDUINO: THE VISUINO PROJECT - PART 4 PAGE - 7/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
-
19Issue Nr 6 2015 BLAISE PASCAL MAGAZINE
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.
ARDUINO: THE VISUINO PROJECT - PART 4 PAGE - 8/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
-
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE20
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:
Next, add a Packet component:
ARDUINO: THE VISUINO PROJECT - PART 4 PAGE - 9/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
-
21Issue Nr 6 2015 BLAISE PASCAL MAGAZINE
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:
ARDUINO: THE VISUINO PROJECT - PART 4 PAGE - 10/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
-
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE22
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:
ARDUINO: THE VISUINO PROJECT - PART 4 PAGE - 11/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
-
23Issue Nr 6 2015 BLAISE PASCAL MAGAZINE
Next add a TCLUnpacket, TSLScope,
TILAngularGauge and 2 TILLed components:
Switch to the OpenWire tab, and double click on the
CLUnpacket1:
ARDUINO: THE VISUINO PROJECT - PART 4 PAGE - 12/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
-
24
Add 2 Float and 2 Boolean channels:
Expand the HeaderMarker and for the bytes click
on the ... (called ellipsis)
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE
ARDUINO: THE VISUINO PROJECT - PART 4 PAGE - 13/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
-
25Issue Nr 6 2015 BLAISE PASCAL MAGAZINE
In the Bytes editor enter 55 55, then click OK:
Connect the components as shown in the picture:
ARDUINO: THE VISUINO PROJECT - PART 4 PAGE - 14/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
-
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE26
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.
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
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.
ARDUINO: THE VISUINO PROJECT - PART 4 PAGE - 15/15
INTERNET OF THINGS WITH ARDUINO AND DELPHI
-
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 [email protected].
AVAILABLE ON THE NEXT
PASCON 15 SEPTEMBER 2015
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?
VISUINO IS THE LATEST INNOVATIVE PRODUCT
FROM MITOV SOFTWARE.
www.visuino.com
-
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE28
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 )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.
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.
-
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE 29
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.
DATABASE WORKBENCH 5THE SWISS ARMY KNIFE FOR DATABASES PAGE 2/8
-
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE30
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 Sub-
diagram 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
DATABASE WORKBENCH 5THE SWISS ARMY KNIFE FOR DATABASES PAGE 3/8
-
31
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.
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.
Figure 5. The first conceptual
diagram
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
drop three tables (in
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
DATABASE WORKBENCH 5THE SWISS ARMY KNIFE FOR DATABASES PAGE 4/8
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE
-
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE32
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
Figure 6. The half worked out conceptual diagram
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).
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.
Figure 7. The fully adapted conceptual diagram
DATABASE WORKBENCH 5THE SWISS ARMY KNIFE FOR DATABASES PAGE 5/8
-
33Issue Nr 6 2015 BLAISE PASCAL MAGAZINE
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 Sub-
diagram can be used:
Figure 7b. The Articles sub-diagram
FROM CONCEPTUAL TO PHYSICAL DIAGRAM
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 8. Conversion to Firebird
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 9a. Table Articles for Firebird.
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 10. Adapted Table Blaise
DATABASE WORKBENCH 5THE SWISS ARMY KNIFE FOR DATABASES PAGE 6/8
-
34
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:
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE
DATABASE WORKBENCH 5THE SWISS ARMY KNIFE FOR DATABASES PAGE 7/8
-
35Issue Nr 6 2015 BLAISE PASCAL MAGAZINE
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. .
DATABASE WORKBENCH 5THE SWISS ARMY KNIFE FOR DATABASES PAGE 8/8
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
-
36 Issue Nr 6 2015 BLAISE PASCAL MAGAZINE
better office benelux | asterlaan 6 5582EH waalre | 040 222 26 43 [email protected]
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.
Specialist help and consultancy
for kbmMW
CUSTOM TECHNOLOGY
COMPONENTS
DEVELOPERS4
-
http://www.blaisepascal.eu/DucthPascon/Pascon2015UK.html
-
1 2 34
5126
7 8
1314 15
16 17
25 262720
2128 22 23
24
3334 35
36
37 38
39
40 41
42
3229 30 31
910
1118 19
WINDOWS 10
L I B R A R Y 2 0 1 5
B L A I S E P A S C A L M A G A Z I N E
A L L I S S U E S I N O N E F I L EA U T H O R S A L FA B E T I C A L
CLICK TO OPEN
ITEM (NUMBER)
CLICK TO OPEN
ITEM (NUMBER)
CLICK TO OPEN
DELPHI URL
CLICK TO OPEN
A BROWSER WITH
URL
CLICK TO OPEN
THE TOTAL
OVERVIEW OF ALL
ITEMS IN ONE
CLICK TO OPEN
THE TOTAL
OVERVIEW OF ALL
ITEMS IN ONE
www.blaisepascal.eu/subscribers/UK/UK_CD_DVD_USB_Department.html
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE 38
-
Delphi
expertstarter
Issue Nr 6 2015 BLAISE PASCAL MAGAZINECOMPONENTS
DEVELOPERS439
TIPS AND TRICKS WITH KBMMEMTABLE PAGE 1/9
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.
var
: ;mt TkbmMemTable
begin
// Define fields (you may already
// have them defined at designtime).
. . ( , );mt FieldDefs Add ftInteger'ID'
. . ( , , );mt FieldDefs Add ftString'Name' 80
. . ( , , );mt FieldDefs Add ftString'Address' 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 ixPrimary'iID' 'ID'
. . ( ,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:
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 to doi 1 1000000
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 then . ( , ,[]) mt Locate 'Name' 'Jens Hansen'
(MessageDlg 'Found record.
Address=' 'Address'+ . ( ). );mt FieldByName AsString
CREATE A MEMORY TABLE WITH
INDEXES IN CODE
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.
BY KIM MADSEN
-
PAGE - 2/4
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE COMPONENTS
DEVELOPERS440
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.
v mt Lookup:= . ( , , );'Name' 'Jens Hansen' 'Address'
( ) if not thenVarIsNull v
( + );MessageDlg v'Found record.Address='
You can also search for partial values
if . ( , ,[ ]) mt Locate loPartialKey'Name' 'Jens'
then MessageDlg mt FieldByName AsString( + . ( ). );'Found record. Address=' 'Address'
How to search on multiple fields?
Let kMemTable automatically create indexes for you
to improve search performance
This may however result in creation of many indexes
over time if you often search on new columns and
having many indexes to maintain when
inserting/editing/deleting records will always give a
performance penalty, so use with care.
HOW TO SORT?
Adding an index will always result in the data
being sorted according to an index. So you can
add an index and switch to it.
Or even easier:
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 Greater than
< Less than
>= Greather than or equal
-
Issue Nr 6 2015 BLAISE PASCAL MAGAZINECOMPONENTS
DEVELOPERS441
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.
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.
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
-
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE COMPONENTS
DEVELOPERS442
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.
kbmMemTable have two properties controlling
versioning: EnableVersioning and
VersioningMode. EnableVersioning is a
Boolean, while VersioningMode can have
the value of mtvm1SinceCheckPoint or
mtvmAllSinceCheckPoint.
Versioning allows kbmMemTable to store
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.
Default EnableVersioning is false and thus
the records you see in the memory table are
the ones you have.
No other versions are created.
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.
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.
'__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.
'__MT__DEFAULT_' (kbmDefaultIndex).
This index is automatically created when you set
IndexFieldNames to a set of fields for which no
index already exist.
'__MT__RANGE_' (kbmRangeIndex).
This index is automatically created when you
apply a range filter.
'__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.
TIPS AND TRICKS WITH KBMMEMTABLE PAGE 4/9
-
If you compile and upload the Arduino code as described in the
previous articles, and then connect to it with Visuino, you will see the
sine wave deformed by the quadratic function plotted in the scope:
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE43
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:
Example:
In this example a record is inserted, a transaction is
started, then the record is edited and then deleted.
With VersioningMode set to
mtvm1SinceCheckPoint, then you can only
choose to roll back to where the record was
inserted. The version containing the edited record is
lost the moment the record is deleted. Only one
version in addition to the original record is kept.
To allow for a multi level undo, then
VersioningMode should be set to
mtvmAllSinceCheckPoint. Then you can do
this:
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:
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.
When you insert/append records, those will be
marked with usInserted as their
UpdateStatus propery. That also happens when
you load the records from another dataset, because
technically they have been inserted into the
memory table. You can however ask kbmMemTable
to consider these newly loaded records, as being
the original ones and thus that they should have
the UpdateStatus of usUnmodified.
The latest version of any record is now considered
the original version and marked as
usUnmodified, and all intermediate versions
have been removed. That also means that if records
were deleted, they are now permanently deleted
and can't be recovered. To undo last change of the
current record, you can use:
Remember that the number of undo's you can
make on the current record, depends on the
setting of EnableVersioning and
VersioningMode.
// An example on how to create a deltahandler.
TMyDeltaHandler TkbmCustomDeltaHandler = ( )class
protected
( : ; procedure varInsertRecord Retry boolean
var override : ); ;State TUpdateStatus
( : ; procedure varDeleteRecord Retry boolean
var override : ); ;State TUpdateStatus
( : ;procedure varModifyRecord Retry boolean
: ); ;var overrideState TUpdateStatus
// procedure UnmodifiedRecord(var Retry:boolean;
var State:TUpdateStatus); override;
end;
mt AppendRecord. ();
mt StartTransaction. ;
mt Edit mt Post. . . ;
mt Delete. ;
mt Rollback. ;
mt AppendRecord. ();
mt StartTransaction. ; // First level of transaction
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
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
mt CheckPoint. ;
mt Undo. ;
TIPS AND TRICKS WITH KBMMEMTABLE PAGE 5/9
COMPONENTS
DEVELOPERS4
-
procedure var var . ( : ; : );TMyDeltaHandler InsertRecord Retry boolean State TUpdateStatus
var string : ; , : ; : ;i integer s sv v variant
begin
:= ;s ''
:= - for to doi FieldCount0 1
begin
:= [ ];v Values i
( ( )) :=if then VarIsNull v sv ''
( [ ]. ) :=else if not in then Fields i DataType kbmBinaryTypes sv v
else
:= ;sv ''
:= + + ;s s sv ' '
;end
( ( ,[ ]));ShowMessage Format s'Inserted record (%s)'
end;
procedure var var . ( : ; : );TMyDeltaHandler DeleteRecord Retry boolean State TUpdateStatus
var string : ; , : ; : ;i integer s sv v variant
begin
:= ;s ''
:= - for to doi FieldCount0 1
begin
:= [ ];v Values i
( ( )) :=if then VarIsNull v sv ''
( [ ]. ) :=else if not in then Fields i DataType kbmBinaryTypes sv v
:= ;else sv ''
:= + + ;s s sv ' '
;end
( ( ,[ ]));ShowMessage Format s'Deleted record (%s)'
end;
procedure var var . ( : ; : );TMyDeltaHandler ModifyRecord Retry boolean State TUpdateStatus
var stringi integer s1 s2 sv v variant: ; , , : ; : ;
begin
:= ;s1 ''
:= ;s2 ''
:= - for to doi FieldCount0 1
begin
:= [ ];v Values i
( ( )) :=if then VarIsNull v sv ''
( [ ]. ) :=else if not in then Fields i DataType kbmBinaryTypes sv v
:= ;else sv ''
:= + + ;s1 s1 sv ' '
:= [ ];v OrigValues i
( ( )) :=if then VarIsNull v sv ''
( [ ]. ) :=else if not in then Fields i DataType kbmBinaryTypes sv v
:= ;else sv ''
:= + + ;s2 s2 sv ' '
;end
( ( ,[ , ]));ShowMessage Format s2 s1'Modified record (%s) to (%s)'
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;
TIPS AND TRICKS WITH KBMMEMTABLE PAGE 6/9
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE COMPONENTS
DEVELOPERS444
-
LOADING DATA
Data can be loaded into a memory table in multiple
ways:
1. Inserting them one record at a time
2. Loading the data from another dataset
3. 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:
mtcpoStructure:
Copies table structure (field definitions) from the
source. Any field definitions you had in your local
memory table are removed.
mtcpoOnlyActiveFields:
Only field definitions in the source that actually
are represented as a true field, are copied.
mtcpoProperties:
Copy over field properties like DisplayWidth,
DisplayLabel, Required, ReadOnly,
Visible, DefaultExpression,
Alignment, ProviderFlags, Lookup,
LookupCache, LookupDataset,
LookupKeyFields, LookupResultField,
KeyFields, DisplayFormat,
EditFormat, MaxValue, MinValue,
DisplayValues, TransLiterate,
Precision, Currency and BlobType.
mtcpoLookup:
Also copy lookup fields from the source dataset.
mtcpoCalculated:
Also copy calculated fields from the source
dataset.
mtcpoAppend:
Append records to the existing records in the
memory table. This cant be used with
mtcpoStructure or mtcpoProperties.
mtcpoFieldIndex: Copy the index position of
fields to ensure field order is the same as in the
original.
mtcpoDontDisableIndexes:
Default a batch insertion is made, where indexes
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.
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 kbmMemTable 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.
If the source dataset has field 'str1' which is a
ftWideString field, while the local
memorytable has the same field 'str1' defined as a
ftString field, it will UTF8 encode the data
coming from the source field before putting it into
the destination field. Similarly if a source field is
of type ftString, but the destination is of type
ftWideString then an UTF8 decoding will take
place before putting the value into the destination
field. The same takes place with
ftMemo/ftWideMemo and
ftFixedChar/ftWideFixedChar.
If you already have an existing field structure in
your local memory table, and want to copy fields
from a source dataset, where the field names do
not match, you can take advantage of field name
mapping.
Eg.
This is telling kbmMemTable that the local field
named str1 is called str_1 in the source table etc.
As for option 3, loading from a file, you can use:
mt.LoadFromFileViaFormat('somefilename',
someformatinstance);
mt LoadFromDataset anotherdataset. ( ,
[], );'str1=str_1;int2=int_2'
TIPS AND TRICKS WITH KBMMEMTABLE PAGE 7/9
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE45COMPONENTS
DEVELOPERS4
-
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'.
A different extraction method is GetRows which
returns requested fields as an array of an array of
variant.
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.
someformatinstance 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.
sf TkbmMemCSVStreamFormat Create:= . ( );nil
try
. ( , )mt LoadFromFileViaFormat sf'.\mydata.csv'
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 put an equal
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);
Then you can access that value by:var v variant: ;
begin
:= ( . [ ] v TkbmVariantObject slData Objects i
). ;Value
:= . [ ];s slData Strings i
end;
var v variant: ;
begin
:= . ( , , );v mt GetRows kbmGetRowsRest kbmBookMarkFirst 1
end;
v mt GetRows kbmGetRowsRest kbmBookMarkFirst VarArrayOf:= . ( , , ( , ));'fld1' 'fld2'
TIPS AND TRICKS WITH KBMMEMTABLE PAGE 8/9
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE COMPONENTS
DEVELOPERS446
-
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.
v will contain the sum of fld1 for all records in the
current index.
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 f