Quantcast
Channel: TMS Software
Viewing all 1008 articles
Browse latest View live

TMS hands-on training day in Meerbusch, near Dusseldorf, Germany

$
0
0

We're pleased to inform that TMS software organizes a new training day on Friday March 11th. Our entire team will be present at the TMS Day in NRW Germany. Get the in-depth information directly from the principal architects of TMS products!

Tentative program

Please also send in the specific topics you want to see covered on this training day so we can finalize this tentative program and fulfill your needs in the best possible way. We suggest to bring your laptop if you want to follow the samples live or if you want one of our experts to have a look at your code or application. For a maximum interactivity, the number of seats will be limited to 30 persons.

Registrations until February 15 - number of seats limited to 30 persons!

The cost of the TMS training day is only 75 EU + VAT. This includes a full day access to the sessions and coffee/drinks during this day. A hot meal is served for lunch. Also, a 20% discount will be offered for NEW licenses for all attendees!

Location

  • Gut Dyckhof, Am Dyckhof 3, 40667 Meerbusch-Büderich - NRW, Germany
  • Very close to exit Meerbusch of the A52 highway
  • Free parking
  • Facilities for hotel rooms at the event are available for international attendees




Introducing FNC and TTMSFNCChart

$
0
0

As we have been developing UI VCL framework components since 1995 and FireMonkey framework components since 2011, we have recently been doing extensive research on approaches to create visual UI components that would simultanously support the VCL framework and the FireMonkey framework. As a result of this research, we came to the conclusion that the approach for VCL and FMX can also be easily applied to LCL, the component framework that comes with Lazarus.

Doing so offers a number of advantages both for users of the components and the developers of the components. With respect to using the components, the user can seemingly exchange code between a VCL project and an FMX project as the interface and class name of the component is 100% identical. When porting from VCL to FMX or vice versa all code can be shared as-is. Just like you'd write the code in VCL Edit.Text := 'Hello world', you can in exactly the same way do this with a FireMonkey TEdit or LCL TEdit. As with the result of this approach, the UI components also become usable with the LCL framework in Lazarus, this means that writing Pascal based GUI apps with these components also becomes possible for additional operating systems supported by Lazarus/FPC, i.e. Linux and its variants like Raspbian. Also for the development of the components, there are a number of advantages. With a sufficiently abstract layer in place, the component developer can focus on the component UI look & feel and behavior rather than with the particuliarities of each underlying framework. Support and maintance for highly complex components means managing a single code base as opposed to maintaining 3 code bases. This leads to faster development and support and ultimately to more stable code accross 3 frameworks.

For the result achieved, we came up with the name FNC components, which stands for Framework Neutral Components. As a proof of concept, we have now released our first FNC component TTMSFNCChart that supports a mind boggling array of frameworks and operating systems: VCL Win32/Win64 from Windows XP to Windows 10, FMX Win32/Win64, MacOS-X, iOS, Android, LCL Win32/Win64, Mac OS-X, iOS, Android, numerous Linux variants including Raspbian. Oh, and there is an extra benefit, the component is not only usable from Delphi or FreePascal but also from C++Builder. The feature set of the TTMSFNCChart is more or less identical to our FMX chart that includes line,bar, XY-line, XY-scatter, area, stacked bar, stacked area, digital line, pie, spider and donut charts with highly customizable legends, X-axis, Y-axis etc...

You can see the TTMSFNCChart initialized with 100% identical code on a couple of operating systems:


var
  i,j: integer;
  rng: array[0..2] of integer;
const
  val: array[1..12] of integer = (15,23,10,5,3,14,33,11,8,19,9,22);
begin
  Caption := 'TMS FNC Chart Demo';

  TMSFNCChart1.Width := 600;
  TMSFNCChart1.Height := 500;

  TMSFNCChart1.BeginUpdate;

  TMSFNCChart1.Title.Text := 'TMS FNC Chart : sales/month';
  TMSFNCChart1.Title.Font.Name := 'Helvetica';
  TMSFNCChart1.Title.Font.Style := [TFontStyle.fsBold];
  TMSFNCChart1.Title.Font.Size := 14;
  TMSFNCChart1.Title.Font.Color := gcBlue;

  TMSFNCChart1.Series[0].ChartType := ctLine;
  TMSFNCChart1.Series[1].ChartType := ctArea;
  TMSFNCChart1.Series[2].ChartType := ctBar;

  for j := 0 to 2 do
  begin
    TMSFNCChart1.Series[j].Points.Clear;
    TMSFNCChart1.Series[j].AutoXRange := arCommon;
    TMSFNCChart1.Series[j].Mode := smStatistical;
    TMSFNCChart1.Series[j].XValues.Angle := -90;
    TMSFNCChart1.Series[j].XValues.Title.Text := 'Months';
    TMSFNCChart1.Series[j].YValues.Title.Text := 'Units';
  end;

  TMSFNCChart1.Legend.Font.Size := 14;
  TMSFNCChart1.Series[0].LegendText := 'Retail';
  TMSFNCChart1.Series[1].LegendText := 'Consumer';
  TMSFNCChart1.Series[2].LegendText := 'B2B';
  TMSFNCChart1.Series[0].Markers.Shape := msSquare;
  TMSFNCChart1.Series[2].Markers.Visible := false;

  rng[0] := 70;
  rng[1] := 10;
  rng[2] := 40;

  for i := 1 to 12 do
    for j := 0 to 2 do
    begin
      TMSFNCChart1.Series[j].AddPoint(rng[j]+val[i], FormatSettings.LongMonthNames[i]);
    end;

  TMSFNCChart1.EndUpdate;
end;
The only difference in the project are references to units. For VCL, the unit namespaces are VCL.TMSFNC*, for FMX it is FMX.TMSFNC* and for LCL it is LCLTMSFNC* (as sadly FreePascal doesn't support a dot in the unit names)

Below are a number of screenshots of the chart running in a myriad of operating systems with exactly the same code:


TTMSFNCChart on iOS with FMX framework



TTMSFNCChart on Mac OS-X with FMX framework



TTMSFNCChart on Raspbian with LCL framework



TTMSFNCChart on Ubuntu with LCL framework



TTMSFNCChart on Windows with VCL framework

With this first proof of concept product ready now, we are eager to learn about your interest in this type of components and what other UI components you want to see us developing for FNC. We are curious to see your results bringing Pascal code bases to an amazing and vast number of operating systems and devices.

Creating Excel files in Windows, OSX and Linux with .NET Core

$
0
0

Introduction

In FlexCel .NET 6.7.9, we introduced preview support for .NET Core

This is a preview FlexCel release since it is compiled against a preview version of .NET Core and shouldn't be used in production, but it can be fun to explore.

This FlexCel version is also limited, because .NET core doesn't have currently any graphics support ( see https://github.com/dotnet/corefx/issues/2020 ) and so we can't do anything that needs graphics manipulations or measuring fonts. This means no rendering of Excel files, no exporting to PDF or HTML, no autofitting columns or rows. At the moment we are releasing support for almost the full API for reading and writing xls and xlsx files and also the reporting engine. The only limitation I can think of in the API is the Autofit methods to autofit rows and columns, because those need a graphics library to measure the fonts used in the cells. But the rest, including the full recalculation engine is there and working.

Getting Started

The first thing to do is of course to install .NET Core in Windows, OSX or Linux.
Once you have installed it, the next step would be to create a simple console application and see if it works. This is done by typing the following commands:
dotnet new
dotnet restore
dotenet run
It should look something like this:



Note:At the time of this writing, if you run those steps in OSX, you will see an error as explained here. Probably by the time you read this it is already solved, but if you get an error, as a temporary workaround you should try restoring the 10.10 (Yosemite) framework:
dotnet new
dotnet restore --runtime osx.10.10-x64
dotenet run
And the result should look like this:



Installing FlexCel

Ok, the first step is done. If we had any luck, we got a working .NET app, and we didn't even write a single line of code. So now, what about using FlexCel to create an xlsx file?

.NET core is all about NuGet, and even the .NET framework itself will be distributed via NuGet. So you need to download the FlexCel NuGet package from either the tms registered users page if you have a valid FlexCel license, or a trial from the product page.

Once we have the FlexCel NuGet package, we need to put it in a NuGet server so it can be used.
You can find information in how to setup a private nuget server for your own packages here: http://docs.nuget.org/docs/creating-packages/hosting-your-own-nuget-feeds

For this example, we are going to use the simplest way: Sharing a Windows folder. So we will put the package in r: mscomponents, and share that folder as "tmscomponents"



And that is it. Once you have shared the folder, you should see it in the Windows Explorer and in OSX Finder.

Configuring the project to use FlexCel

For the next steps, we are going to be using Visual Studio Code, but it can be done just as easy with any text editor. We will also be working in OSX, even when it is exactly the same for Linux or Windows. It just doesn't make sense to repeat the same information three times.

So we go to FIle->Open, and open the folder we created in the "Getting Started" section. In the screenshots, it was ~/dotnetcore.
Then we will edit our NuGet.Config file, and add the "tmscomponents" feed we created in the "Installing FlexCel" section to host the FlexCel NuGet package.

We need to add this line:
<add key="api.tmssoftware.com" value="/Volumes/tmscomponents" />

to the "packageSources" section of our project.json:



Note: This step is a little different depending in the operating system you are using. In Windows, you would write "\tmscomponents" instead of "/Volumes/tmscomponents" and in Linux you would write the path to the folder where you mounted the windows shared folder. Also note that in OSX and Linux, you might have to mount the folder before using it. In OSX this means opening the folder with the Finder, and in Linux it might change depending in the distribution you are using: either doing a mount command or opening it with the file explorer.

Adding a reference to FlexCel

Now, we need to add a reference to the nuget package in our project. Note that different from normal .NET where we add references to assemblies, we now add references to nupkg files which contain the assembly inside.

We will be adding the line:
"flexcel-dnx": "6.7.9-rc1"
to the "dependencies" section of the file project.json in our working folder:

Then we go back to the terminal, and type "dotnet restore" in our working folder. As we are in OSX and we have the bug we mentioned before, we will actually do "dotnet restore --runtime osx.10.10-x64". As mentioned, very shortly after this blog is published this won't be necessary.

The result should look something like this:



Note: For this reference, we use 6.7.9-rc1 which is the FlexCel version at the time I am writing this. .NET core uses semantic versioning for the names of the packages, and so the "-rc1" part of this release means it is not a stable release yet.

Writing the app

All the pieces of the puzzle are now in place: The only thing remaining now is to write the actual application. We will create an xlsx file with an image in my hard disk, some text and a formula. To do so, open the file Program.cs and replace the code in this file with the following:
using System;
using FlexCel.XlsAdapter;
using FlexCel.Core;
using System.IO;

namespace ConsoleApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var xls = new XlsFile(1, TExcelFileFormat.v2016, true);
            xls.SetCellValue(10, 1, "What");
            xls.SetCellValue(10, 2, "a");
            xls.SetCellValue(10, 3, "wonderful");
            xls.SetCellValue(10, 4, "world");
            xls.SetCellValue(11, 1, new TFormula("=A10 &  " " & B10 & " " & C10 & " " & D10"));
            xls.AddImage(File.ReadAllBytes("/Users/adrian/Documents/cube2b256.png"), 
            TXlsImgType.Png, 
            new TImageProperties(new TClientAnchor(TFlxAnchorType.MoveAndResize, 2, 0, 2, 0, 9, 0, 4, 0), ""));
            
            xls.Save("test-flexcel.xlsx");
            Console.WriteLine("File has been written to folder: " + Directory.GetCurrentDirectory());
        }
    }
}

For this particular example, I used an image I have at /Users/adrian/Documents/cube2b256.png. If you are running the code of this tutorial, make sure to write the path to a png you have on your machine.

After writing the code, save it, go back to the terminal and type:
dotnet run
If there are no errors, then you can type
open test-flexcel.xlsx
And if you have Excel installed in the mac, you should be able to see the file we just created:



Appendix: (Trying to) Convert the app to a native C++ app

So we have managed to create an Excel file with FlexCel and .NET Core in some short simple steps. And we did it in OSX, and we could have done it in Linux too. This is all interesting, but it is not like we couldn't have done it before with Mono and/or Xamarin

One of the things that I was really wanting to evaluate when we adapted FlexCel code to run in .NET Core was two mysterious lines which appear if you write
dotnet compile --help

Usage: dotnet compile [arguments] [options]

Arguments:
    The project to compile, defaults to the current directory. Can be a path to a project.json or a project directory

Options:
  -h|--help                           Show help information
  -o|--output             Directory in which to place outputs
  -t|--temp-output        Directory in which to place temporary outputs
  -f|--framework           Compile a specific framework
  -c|--configuration   Configuration under which to build
  --no-host                           Set this to skip publishing a runtime host when building for CoreCLR
  -n|--native                         Compiles source to native machine code.
  -a|--arch                     The architecture for which to compile. x64 only currently supported.
  --ilcargs                     Command line arguments to be passed directly to ILCompiler.
  --ilcpath                     Path to the folder containing custom built ILCompiler.
  --ilcsdkpath                  Path to the folder containing ILCompiler application dependencies.
  --appdepsdkpath               Path to the folder containing ILCompiler application dependencies.
  --cpp                               Flag to do native compilation with C++ code generator.
  --cppcompilerflags           Additional flags to be passed to the native compiler.

Now, before continuing: the word "native" has been so abused that it has lost any meaning. Everything is native and nothing is. You can have native code which is using Reflection/RTTI all over the place and behaving like interpreted code. You can have interpreted code that is JITed AOT and runs like compiled code. And what about memory management? Are we using a Garbage Collector, manual allocation/deallocation of maybe reference counting?

But well, from what I understand this --native switch seems to be a port of .NET Native but running in desktop Win64 apps (and OSX and Linux). This does seem indeed very interesting.

The --cpp switch seems even more interesting. It converts your C# code to C++ code, and uses Visual C++ to compile it. The generated cpp file is stored inside the obj folder so you can see it, even if you can't do much with it as it is not "readable C++" but more like "IL written in C++" which only a C++ compiler can understand.

So my idea was to run the tests in "normal", "native" and "cpp" modes to see how they behave. We have thousands of tests here which we can use to measure the performance, and while not designed as benchmarks, those tests do a lot of I/O (reading and writing thousands of Excel files), they do a lot of calculations (when testing the calculation engine) and they allocate and free a lot of memory (when testing huge files). In addition, they are heavily multithreaded. So is it any different? 1.5x, 2x, or maybe 10x faster? Or maybe slower?

And the answer is.... we don't have an answer yet. I couldn't get either the --native or the --cpp switches to work with FlexCel, and I didn't try much since I understand this is a prerelease version of .NET Core. What I could get was a simple console app with a single Console.WriteLine sentence to work in both "Native" and "CPP". But not anything complex enough as to benchmark it.

Ok, this is kind of a downer to finish this post. After all the nice stuff already possible with .NET core, it doesn't seem fair to close the post with something that doesn't really work yet (or that I wasn't able to make it work). Maybe I should delete this whole section before pressing "publish"? . I actually thought about it, maybe write something when I can actually make it work, but at the end I decided to keep it. The truth is, this whole native stuff is very interesting and I am really looking forward to see it come to life. I think it is well worth mentioning even if it isn't ready yet.

Is this the way to treat a customer?

$
0
0

In March, 2014, Thomas Milo Sorensen bought a TMS Business Subscription license. He was using Delphi XE5, the latest version available, and got the six products included in the subscription: TMS Aurelius, TMS Scripter, TMS Data Modeler, TMS Diagram Studio, TMS Workflow Studio and TMS Query Studio.

Later in the same month of March, 2014, TMS Sparkle, a framework to create and connect to HTTP servers, was released. Delphi XE6 was also released, and all TMS Business products were updated to support it. Those new versions and the new product were available for Thomas to download and install.

In April, 2014, a new product released, TMS RemoteDB, would allow existing client/server applications to be modified, with minimum effort, to access the database in the cloud, using simple HTTP connections, without requiring any database client, running from any platform. Existing Aurelius applications, could access remote databases with no modification at all.

In August, 2014, another product: TMS XData, which users could use to create a REST/JSON server that was strongly integrated with Aurelius. Sending, receiving and querying entities through REST was possible with a few lines of code

In September, 2014, Embarcadero released Delphi XE7, and all products got new releases that supported the new version.

Delphi XE8 and Delphi 10 Seattle were both released in 2015 and supported by the products. Several important updates were made to all products. TMS Scripter, which only worked on Win32, got support for all platforms Delphi targets, including Android, iOS and Mac OS X. TMS workflow got a significant revamp, with features like new SQL and mail blocks, variable editors, script transitions, among others. TMS Aurelius included several features voted by users, like concurrency control, object refresh and multi-model design. Even the new released products like XData and Sparkle got significant improvements.

In total, there were 71 (seventy one) product releases since he bought his license. 119 (one hundred and nineteen) new features added, without counting bug fixes and other minor improvements.

In that period, all support e-mails sent were answered. Many of them within hours. Most of them in 1-3 days. Even on weekends, holidays and vacation periods. The majority of questions in the TMS web forums were answered in the same timeframe. No major bugs reported were left unfixed.

And now, in February, 2016, TMS Echo will be released. The new replication system that will allow Aurelius applications to work offline and distributed by sending and receiving data from local to remote databases with minimal configuration. And, as you might guess, Thomas will be able to download, install and use this first TMS Echo version, for free. It's the 10th product available in TMS Business Subscription, and it was "only" six two years ago.

This is a fictional story based on real events. Thomas Milo Sorensen is not the real name of the customer. That's the fictional part.

Introducing myCloudData.net from TMS: Pascal generated data consumed by Delphi

$
0
0

Recently, we opened the beta for our new myCloudData.net service. myCloudData.net is both a service and SDK to put structured data in the cloud. After a long time of experience with all kinds of cloud services (based on OAUTH/REST/JSON APIs), we were frustrated there was not really a fast, flexible, simple and easy to use service for storing data in a structured way in the cloud. And thus, many months ago, the idea was born to create a service ourselves. A service you can use directly from our servers as well as an SDK to make this service available on your own server(s) for public or internal-only use. To illustrate what myCloudData.net is about and what it can do for you, we decided to do a sample project, bringing several bits & pieces like Lego blocks together, including myCloudData.net, our cloud components, our Raspberry Pi hardware access library and our VCL chart component.


The project

The project idea is really simple: a Raspberry Pi runs a Pascal application that retrieves the temperature from a temperature/barometric sensor and displays it on a 128x32 OLED screen and simultanously logs the retrieved temperature & pressure plus timestamp in the cloud in a simple table on the myCloudData.net service. At the other side, a Delphi VCL application retrieves the logged temperatures and displays the temperature in a chart. The idea is simple and the project is mainly about illustrating how fast such typical application can be put together with the right Lego blocks.


Used TMS components

For this project, we use quite a few TMS products. Of course, first there is the myCloudData.net cloud storage. We use the TMS Cloud Pack for VCL to access data from myCloudData.net and we use TMS Advanced Charts to display the data graphically and a TMS TAdvStringGrid to show the values in a grid. On the Raspberry Pi, we use the TMS LCL HW Pack to read our temperature & barometric sensor extension and to drive the 128x32 OLED display. Also used is the TMS LCL Cloud Pack to put the captured data on myCloudData.net

Setup

The first thing to do is creating an account on myCloudData.net. Head over to http://myCloudData.net and in a few seconds you can create a new account. Then, using this account, create a myCloudData.net application and you'll receive an application key and secret. This key & secret is used to connect from a Delphi app to myCloudData.net and in order to get started, the metadata is created for our table that will hold the captured sensor data. After dropping the TAdvmyCloudData component on the form, the code to do this is:


  // specify where to persist the TAdvMyCloudData access token once it is obtained
  AdvMyCloudData1.PersistTokens.Location := plIniFile;
  AdvMyCloudData1.PersistTokens.Key := '.myclouddata.ini';
  AdvMyCloudData1.PersistTokens.Section := 'tokens';

  // initializing the application key & secret for TAdvMyCloudData and setting the callback URL used in the authentication/authorization phase
  AdvMyCloudData1.App.Key := MYCLOUDDATAKEY;
  AdvMyCloudData1.App.Secret := MYCLOUDDATASECRET;

  AdvMyCloudData1.App.CallBackPort := 8888;
  AdvMyCloudData1.App.CallBackURL := 'http://127.0.0.1:8888';

  // obtain an access token or start using an existing access token to communicate with the service
  AdvMyCloudData1.Connect;
The AdvMyCloudData1.Connect will either go through a first time authentication/authorization step when there is not yet an access token or perform an access token validity check when there was one persisted in the INI file in this case. When the access token does not pass the test, a new authentication/authorization step will happen.
When either of these steps is succesfull, the TAdvmyCloudData.OnConnected event is triggered. In this event, we create the table on the myCloudData.net service when it does not already exist and the metadata is set. The code to do this is:

procedure TForm1.AdvMyCloudData1Connected(Sender: TObject);
var
  table: TmyCloudDataTable;
begin
  table := AdvMyCloudData1.TableByName('WEATHER');

  // when the table does not exist, create it and define its meta data
  if not Assigned(table) then
  begin
    table := AdvMyCloudData1.CreateTable('WEATHER');
    // string field that holds the ID of the Raspberry Pi generating the temperature/air pressure. Allows for multiple devices to log data
    table.MetaData.Add('STATIONID', ftString, 50);
    // timestamp of the measured temperature/air pressure
    table.MetaData.Add('TIMESTAMP',ftDateTime);
    // temperature & air pressure values
    table.MetaData.Add('TEMP', ftFloat);
    table.MetaData.Add('PRESS', ftFloat);
    table.SetMetaData;
  end;
end;

Generate the data from the Raspberry Pi with FPC/Lazarus/TMS LCL Cloud Pack and TMS LCL HW Pack

First of all connect the Adafruit 128x32OLED and Adafruit temperature/barometric sensor on the I2C pins of the Raspberry Pi.



Drop a TTMSLCLAdaDispl128x32 and TTMSLCLAdaBarTemp component on the form and open these devices with the code:

begin
  // initializes 128x32 OLED screen
  displ.InitGPIO;
  // open it, clear the display and show the text 'Ready....'
  if displ.Open then
  begin
    displ.Clear;
    displ.DrawTextLarge(0,4,'Ready....');
    displ.Display;
  end;
  // open the communication with the temperature/barometric sensor
  sensor.Open;
end;

Then also initialize the TTMSLCLCloudmyCloudData component. This initialization and connecting to the service is identical to its VCL counterpart:

procedure TForm1.FormCreate(Sender: TObject);
begin
  TMSLCLCloudMyCloudData1.PersistTokens.Location := plIniFile;
  TMSLCLCloudMyCloudData1.PersistTokens.Key := './myclouddata.ini';
  TMSLCLCloudMyCloudData1.PersistTokens.Section := 'tokens';

  TMSLCLCloudMyCloudData1.App.Key := MYCLOUDDATAKEY;
  TMSLCLCloudMyCloudData1.App.Secret := MYCLOUDDATASECRET;

  TMSLCLCloudMyCloudData1.App.CallBackPort := 8888;
  TMSLCLCloudMyCloudData1.App.CallBackURL := 'http://127.0.0.1:8888';

  TMSLCLCloudMyCloudData1.Connect;
end;

When the connection is succesful, the TTMSLCLCloudmyCloudData.OnConnected event is triggered where a reference (of type TmyCloudDataTable) to the WEATHER table on the service is obtained:

procedure TForm1.TMSLCLCloudMyCloudData1Connected(Sender: TObject);
begin
  table := TMSLCLCloudMyCloudData1.TableByName('WEATHER');
end; 

The final step is to capture the sensor data and effectively store the measured values on the cloud service. This is done at regular intervals via a timer's OnTimer event:

procedure TForm1.Timer1Timer(Sender: TObject);
var
  t,p: single;
  s: string;
  dt: TDateTime;
  ent: TMyCloudDataEntity;
begin
  if sensor.Connected then
  begin
    // Alternatingly capture the temperature & air pressure from the sensor and display its value on the OLED screen
    if ShowTemp then
    begin
      t := sensor.GetTemperature;
      s := 'T:' + Format('%.2f',[t]) + ' C';
    end
    else
    begin
      p := sensor.GetPressure;
      s := 'P:' + Format('%.0f',[p]) + 'mBar';
      lastpress := p;
    end;

    displ.Clear;
    displ.DrawTextLarge(0,4,s);
    displ.Display;

    dt := Now;

    // when a pair of temperature & air pressure is captured, store it on the myCloudData.net service
    if ShowTemp then
    begin
      if Assigned(table) then
      begin
        ent := table.Entities.Add;
        ent.Value['STATIONID'] := 'Home';
        ent.Value['TIMESTAMP'] := dt;
        ent.Value['TEMP'] := t;
        ent.Value['PRESS'] := lastpress;
        ent.Insert;
      end;
    end;

    // toggle capture & display
    ShowTemp := not ShowTemp;
  end;
end;

The Windows Delphi VCL desktop app showing the graph of captured temperature values

With the Raspberry Pi code running to generate our sensor data on the myCloudData.net service, now we can write a simple VCL application (or FMX if you want to do the same on a mobile device or Mac OS-X) that shows a graph of the captured temperature.

There is a one-time setup of the TMS Advanced Charts to have one line-chart and define the X-axis, Y-axis, Legend and Title text:

begin
  AdvChartView1.Panes[0].Series.Clear;
  AdvChartView1.Panes[0].Series.Add;
  AdvChartView1.Panes[0].Series[0].ChartType := ctLine;
  AdvChartView1.Panes[0].Series[0].LegendText := 'Temperature °C';
  AdvChartView1.Panes[0].Title.Text := 'my Raspberry Pi Weather station';
  AdvChartView1.Panes[0].XAxis.Text := 'time (sec)';
  AdvChartView1.Panes[0].YAxis.Text := 'temp (°C)';
end;

There is the same one-time setup of the TAdvmyCloudData component:

procedure TForm1.FormCreate(Sender: TObject);
begin
  AdvMyCloudData1.PersistTokens.Location := plIniFile;
  AdvMyCloudData1.PersistTokens.Key := '.myclouddata.ini';
  AdvMyCloudData1.PersistTokens.Section := 'tokens';

  AdvMyCloudData1.App.Key := MYCLOUDDATAKEY;
  AdvMyCloudData1.App.Secret := MYCLOUDDATASECRET;

  AdvMyCloudData1.App.CallBackPort := 8888;
  AdvMyCloudData1.App.CallBackURL := 'http://127.0.0.1:8888';

  AdvMyCloudData1.Connect;
end;


Upon succesful connection, it's straightforward to retrieve all captured values and display this in a TMS TAdvStringGrid and TMS Advanded Chart with the code:

procedure TForm1.GetRecords;
var
  i,r: integer;
  dt: TDateTime;
  t,p: double;
begin 
  // retrieve entities via the TAdvmyCloudData table
  table.Query();

  // setup the needed nr. of rows in the grid
  AdvstringGrid1.RowCount := 1 + table.Entities.Count;

  // set the X-axis range of the chart
  AdvChartView1.Panes[0].Range.RangeFrom := 0;
  AdvChartView1.Panes[0].Range.RangeTo := table.Entities.Count;

  // fill both chart & grid
  AdvChartView1.BeginUpdate;

  for i := 0 to table.Entities.Count - 1 do
  begin
    r := i + 1;

    dt := table.Entities[i].Value['TIMESTAMP'];
    t := table.Entities[i].Value['TEMP'];
    p := table.Entities[i].Value['PRESS'];

    AdvStringGrid1.Cells[0,r] := formatDateTime('dd/mm/yyyy hh:nn:ss', dt);
    AdvStringGrid1.Floats[1,r] := t;
    AdvStringGrid1.Floats[2,r] := p;

    AdvChartView1.Panes[0].Series[0].AddSinglePoint(t,inttostr(i*5)+'s');
  end;

  AdvChartView1.EndUpdate;

  AdvStringgrid1.AutoSizeColumns(false);
end;

To have some meaningful data for this sample project, we measured the sensor temperature reaction speed. This is done by a forced temperature increase with a heating source and then remove the heating source. In this chart, we can see the temperature every 4 seconds. It's very nice to see a fast and asymptotic curve of the reaction to a heat source and a much slower but also asymptotic curve of the reaction to cooling off.





Summary

We wanted to show how easy and fast it has become to have in this case two different Pascal applications working on the same cloud data. In less than one hour, you can setup a structured data storage in the cloud, write a Pascal Raspberry Pi app that talks to sensors, OLED displays and cloud data and at the same time a Delphi Windows, Mac OS-X or mobile device app that visualizes the Raspberry Pi captured data. All this requires literally not much more than hooking up some TMS components like we built the fire and police station with Lego blocks a couple of decennia ago, proving once more we, Pascal developers, are blessed with having a fun job.

Automated exception logging to the cloud

$
0
0

Exceptions


The new 1.1 release of TMS Logging supports automated exception handling. All you need is a simple Boolean property set to true and your existing applications can benefit from this new feature that will automatically
log unhandled exceptions to one or more registered output handlers.

The following sample demonstrates how easy it is to add this new functionality to your application.
TMSLogger.ExceptionHandling := True;
TMSLogger.RegisterOutputHandlerClass(TTMSLoggerBrowserOutputHandler, [Self]);
TMSLogger.Outputs := AllOutputs;
We all know the following code generates a division by zero and we should at least add an if statement to our code, checking whether b > 0 before attempting to execute the division and
assigning the result to the c variable, but let's say this code is more complex and hidden somewhere in a lost unit inside your project.
procedure TForm1.Button1Click(Sender: TObject);
var
  a, b, c: Integer;
begin
  a := 10;
  b := 0;
  c := a div b;
end;
Clicking the button will generate an unhandled division by zero exception.



Because the TMSLogger has the ExceptionHandling property set to True, the exception will automatically be sent to our registered output handler(s).
In this sample, we have registered a browser output handler as seen in the screenshot below.



As only unhandled exceptions are handled by the logger, wrapping the code with a try except code block will not send an exception output to the output handlers, therefore we have exposed an Exception method that will allow you to further customize the message that is logged.
procedure TForm1.Button1Click(Sender: TObject);
var
  a, b, c: Integer;
begin
  try
    a := 10;
    b := 0;
    c := a div b;
  except on e: EDivByZero do
    TMSLogger.ExceptionFormat('{%s} occured in Button1Click() at line 35', [e.Message]);
  end;
end;

Cloud


Now where does the above exception handling fit into the cloud stor(y)(age)? The new TMS VCL Cloud Pack and TMS FMX Cloud Pack have 2 new cloud components available that can be used as an output handler for the logger.
The first one is the myCloudData storage service which is able to (as the name indicates) store data in the cloud. The second one is the Exceptionless logging and exception handling service. The TMS Logging distribution has 2 separate units
available that can be used to link to both services and automatically log messages / exceptions to those services. Registering an Exceptionless or myCloudData outputhandler is done in the same way as any other output handler.
The output handler requires an already authenticated service, as the output handler will not perform authentication on the connected cloud service, it will try to directly send log or exception messages.

Exceptionless

Depending on the chosen framework, you can either add FMX.TMSLoggingExceptionlessOutputHandler or VCL.TMSLoggingExceptionlessOutputHandler unit to the uses list.
The initialization code authenticates with the Exceptionless service and afterwards registers the output handler specifically designed for this cloud service.
The cloud service component is passed as a parameter to the output handler registration along with a project ID, created through the Exceptionless dashboard.
uses
  FMX.TMSLoggingExceptionlessOutputHandler;

procedure TForm1.Button1Click(Sender: TObject);
var
  a, b, c: Integer;
begin
  a := 10;
  b := 0;
  c := a div b;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  p: TExceptionlessProject;
begin
  TMSFMXCloudExceptionLess1.Username := 'MyUsername';
  TMSFMXCloudExceptionLess1.Password := 'MyPassword';
  TMSFMXCloudExceptionLess1.DoAuth;
  if TMSFMXCloudExceptionLess1.TestTokens then
  begin
    TMSFMXCloudExceptionLess1.GetProjects;
    p := TMSFMXCloudExceptionLess1.GetProjectByName('TMSCloudPack');
    if Assigned(p) then
    begin
      TMSLogger.ExceptionHandling := True;
      TMSLogger.RegisterOutputHandlerClass(TTMSLoggerExceptionlessOutputHandler, [TMSFMXCloudExceptionLess1, p.ID]);
    end;
  end;
end;



myCloudData


The same approach can be applied to the myCloudData cloud service component, as with the Exceptionless cloud service component. The initialization code is slightly different due to the authentication process. Instead of a project ID, an optional tablename can be chosen. In this sample, the registration is done when the myCloudData service is connected after authentication. The initialization code additionally connects the cloud service through a dataset to our TDBAdvGrid component to display the records. The field names are stored in the MetaData property under the TTMSLoggermyCloudDataOutputHandler instance, which is returned by the RegisterOutputHandlerClass function.
procedure TForm1.AdvmyCloudData1Connected(Sender: TObject);
begin
  TMSLogger.RegisterOutputHandlerClass(TTMSLoggermyCloudDataOutputHandler, [AdvmyCloudData1, 'MyTableName']);
  ds.Active := True;
  grd.AutoSizeColumns(True);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  obj: TMyObject;
begin
  obj := TMyObject.Create;
  obj.X := 'Hello World !';
  obj.Y := 123.456;
  TMSLogger.StartTimer;
  TMSLogger.LogSystemInformation;
  TMSLogger.WarningFormat('The value for property Y is {%.3f}', [obj.Y]);
  TMSLogger.Trace(obj);
  TMSLogger.StopTimer;
  TMSLogger.Debug('
  • Item 1
  • Item 2
  • Item 3
'); obj.Free; ds.Refresh; grd.AutoSizeColumns(True); end; procedure TForm1.Button2Click(Sender: TObject); begin AdvmyCloudData1.Connect; end; procedure TForm1.FormCreate(Sender: TObject); begin grd := TDBAdvGrid.Create(Self); grd.Parent := Self; grd.Left := 20; grd.Top := 50; grd.Width := Width; AdvmyCloudData1.App.Key := 'MyAppKey'; AdvmyCloudData1.App.Secret := 'MyAppSecret'; AdvmyCloudData1.PersistTokens.Location := plIniFile; AdvmyCloudData1.PersistTokens.Key := '.myclouddata.ini'; AdvmyCloudData1.PersistTokens.Section := 'tokens'; AdvmyCloudData1.App.CallBackPort := 8888; AdvmyCloudData1.App.CallBackURL := 'http://127.0.0.1:8888'; da := TCloudDataStoreAdapter.Create(Self); da.CloudDataStore := AdvmyCloudData1; ds := TCloudDataSet.Create(Self); ds.Adapter := da; ds.Active := False; d := TDataSource.Create(Self); d.DataSet := ds; grd.DataSource := d; TMSLogger.Outputs := [loTimeStamp, loProcessID, loThreadID, loMemoryUsage, loLogLevel, loName, loValue, loType]; end;



TMS Day followup: TMS FlexCel

$
0
0

Back from TMS day

I am personally very happy with everything about the TMS day. It took 26 hours flying plus 6 hours in a car trip to Germany so I could speak for an hour, but it was completely worth it. There is still something magical about face to face communications, something that is lost in written words. I speak daily with tens of customers via email, I also write posts like this very one, and still there is something missing when you don't see the face of the human you are speaking to.

I also was happy about how the sessions worked: Instead of someone standing there and reading a powerpoint, the sessions were interactive and people in the room asked many interesting questions. This was how it was intended to be, and I'd like to think that we are all -the tms team and the people who assisted- happy about it. But of course we are also aware that things are never perfect, and a lot of stuff can be made better for the next time. You only get better if you correctly identify what can be made better first.

So let's stop with the good parts (which were a lot) and let's focus for a little in what can be improved. The main weak point, from my own impressions and from the impressions I got from the people I spoke in the breaks, seems to be that the sessions didn't go too much in depth in the technical part. This is kind of reasonable if you think about it: To cover an item in depth one hour isn't too much, and there was a *lot* of stuff that we wanted to cover. I could have spoken for a week about FlexCel and there would be still stuff left to be said.

The fact that the sessions were interactive didn't help either: It is hard to plan a presentation when you don't know in advance how much questions will be asked. In this point I must say that even if I was aware that the time in the actual presentation is much shorter than the time when you plan it, I still grossly miscalculated how much stuff I could fit in an hour, and how many interesting questions would appear. I presented just half of the stuff I had prepared and I feel bad about that.

And the last problem is about the technical differences between attendees. Being a TMS day, some people came there to hear in depth stuff about some specific TMS products and have an overview of the others. So some people were heavy FlexCel users while others hadn't heard about it. If you make the presentation too technical, the guys who came for the "overview" will get a good nap. If you make it too general, then the guys who eat FlexCel <#tags> and __ranges__ for breakfast will be bored.

So now, about the hard part: Solutions.

As said the main issue was time, and of course the obvious solution would be to do a "TMS week" not a "TMS day", but this is not really viable. We could show less stuff and focus more, and I think this is something to consider for next time. I also would like to have larger breaks because I think the breaks are the best part of any event. In them you get to speak casually with the guys doing the sessions, and also with other customers which are using the same solutions as you. And we get to speak casually with you. But it is a compromise: the larger the breaks you make, the less sessions you can make in one tms day. Even so, I would vote for longer breaks.

About the unpredictability of the sessions, we could make the sessions not interactive and leave all the questions for the end. But I don't think that is a great idea; in my opinion the greatest thing about the tms day is the interactivity. To watch a 1-hour lecture about something we could just upload a video to youtube and you could watch it from the comfort of your home. You could then ask the questions in the comments of the video. It does work, but it loses the magic. I much prefer to cover half of the themes and have a nice talk with the guys in the room, than to cover everything and not interact.

About the diversity of experience in the room, well, I don't see too much solution about that. I tried to do a mix of both general information and technical, but I ended up speaking more of the general parts than of the more technical stuff. And one thing I didn't know in advance is that most of the people in the room were using FlexCel so I could have skipped the introductory parts. I was surprised by that: I knew which products the people were using, but most people coming were using VCL subs or TMS all access, which doesn't gave me extra information. I honestly wasn't expecting so many FlexCel users, and even if that makes me very happy, I need to apologize: I'll try to make the presentation more technical the next time.

And finally, about all the stuff I couldn't say or expand because of the limited time: That bothers me a lot. There is so much stuff that I wanted to say and I couldn't! But at least I got a solution for this: I will be using the rest of this post to write some of the things I would have said if I had the time to expand even more in the questions that were asked. So without further delay, let's go down to business:

That Excel yellow warning

One of the questions raised when I was showing how to create Excel files with FlexCel is why when I opened them in Excel, Excel wasn't showing a warning that the file wasn't created with Excel. What I answered is that Excel doesn't do any check that the file is a "genuine" Excel file or anything like this. As long as the file conforms to the spec (and even many files which don't conform) Excel will be happy to open it.

There are two types of warnings that Excel might show when opening a file:

1)The "Red" warnings:

This is not a very common warning, and it happens when the file contains invalid data. You should never get this warning in files created with FlexCel, and if you do, you should contact us so we fix it.

2)The "Yellow" warnings:

This might be because the file was downloaded from the internet, because it has macros, or in general when the file is valid but there are some security concerns. The particular question was about the "file is downloaded from the internet" warning, and so this is what I will cover here. First of all, note that the warning isn't related to FlexCel, it just happens whenever you donwload a file from the internet, no matter if it was created with Excel, FlexCel or whatever, and it looks like this:


In the session I commented that the flag that triggers this warning is not really stored in the file, but in a separate hidden NTFS stream. But I didn't had the time to expand more into it. So today, I'll take the opportunity to dive a little bit deeper.

Firs of all, you can see the streams in a file by issuing this command in a command prompt:

dir /r
For a file downloaded from the internet, you should see something like this:



The tools to manipulate the stream in DOS are a little bit limited, but you can see the stream content in notepad by doing:

notepad  test_yellow_box.xlsx:Zone.Identifier
And there is also a "streams" utility by Mark Russinovich which you can use to explore NTFS streams: https://technet.microsoft.com/en-us/sysinternals/bb897440.aspx

But of course, this was a Delphi session, so we care more about on how to do it from Delphi itself. And it turns out it isn't difficult, as you can just delete the stream:

  DeleteFile('test_yellow_box.xlsx:Zone.Identifier');
This should get rid of the stream and the yellow warning on the file. Of course this isn't too useful since the files you generate locally don't have that stream, and you don't want to blindly remove the warning in the files you download from the internet. But it might be useful for some workflows, and actually, sometimes you really need to remove that stream in order to work with the files in certain apps.

The "Do you want to save changes?" warning and Excel 2016

Unable to leave a good thing alone, I spoke about other warning that I know many users care about (and you can already start to understand why 1 hour was never going to be enough to cover everything I wanted to cover). The warning dialog shows when you close the file immediately after opening it and without modifying it. The exact text of the warning varies with the type of file (xls or xlsx) and the Excel version, but normally looks like this:



Or sometimes like this:



It happens whenever you have formulas in your file and the Excel version the file declares it was saved with is different from the Excel version that you open the file with. What happens under the hood is that Excel recalculates the file when it was saved by an older version because the older version might have bugs in the calculated values. So then it offers to save the new recalculated values in the file, even if nothing did change. And it will also identify the new saved file as "Saved with the new Excel version" so it doesn't need to be recalculated again when you reopen it.

Again this is not specific to FlexCel: If you save a file with formulas in Excel 2010 and open it in Excel 2016, you will get a warning when closing the file.

Of course, FlexCel is not Excel 2010 or 2016, but we do have a property that allows us to "identify" the file as being created by an specific Excel version. So I showed first how the dialog appears in a simple file created with FlexCel. This happens because by default FlexCel identifies the file as created by an unknown Excel version, so it is always recalculated when you open it.

But then, I went to show on how you could make the warning disappear by setting the property:
xls.RecalcVersion := TRecalcVersion.Excel2016;
So I rerun the code and... as we could expect in a live event, the dialog stubbornly showed up. By that time we were already late in our schedule, so I had to move on to the next thing, but now I want to expand in what happened.

In short: There was a "January Update" of Excel 2016 which happened to introduce a bunch of new functions. (see https://support.office.com/en-us/article/What-s-new-in-Excel-2016-for-Windows-5fdb9208-ff33-45b6-9e08-1f5cdb3a6c73?ui=en-US&rs=en-US&ad=US

We were aware of this update, and since the functions introduced are probably the first useful new functions introduced since Excel 2007, we already had implemented recalculation for them all at the tms day timeframe (even if we hadn't yet released an update to the public). But what I hadn't realized is that this "January Update" also changed the recalculation id that must be saved with the file.

Files created with a "Pre-January-Update" Excel 2016 (or with FlexCel reporting as Excel 2016) would have a diffefent recalculation ID and trigger the save dialog when you opened them in "Post-January-Update" Excel 2016.

This was the reason the demo failed: FlexCel was still writing the "Pre-January-Update" ID into the file, but the Excel I used in the presentation had silently updated some days ago to the "January Update" and was recalculating the "old Excel 2016" file.

So we just released an update to FlexCel (6.7.16) which will:

1)Add full support for all the new functions in the Excel 2016 january update.

2)Identify the files saved with a RecalcID of Excel2016 with the "Post-January-2016" Id.

3)Add a new member "LatestKnownExcelVersion" to the TRecalcVersion enum. Now you can set:
xls.RecalcVersion := TRecalcVersion.LatestKnownExcelVersion;
And FlexCel will identify the file as saved with the latest version of Excel it knows about. So when Excel 2018 or whatever else comes and you update FlexCel, the new files you create will all automatically get the latest Recalc ID.

Virtual Datasets

This is one of the slides I had planned to show in the presentation, but which I didn't had time to:



And it was a shame, since many people contacted me after the session to inquire about this specific topic. So again, I will use the time I have in this blog post to expand on it:

As you might know if you are a FlexCelReport user, FlexCel can use any TDataSet, any TList and any TArray as datasources for the reports. But those 3 types aren't hardcoded anywhere, and you might have your own data containers which you want to use directly instead of copying the data to TList or a TDataSet. And it is not difficult to do so.

As you can see on the slide, FlexCel actually gets all of its data from 2 abstract classes: TVirtualDataTable and TVirtualDataTableState. FlexCel comes with specialized classes derived from them which implement it for a TDataSet, TList and TArray, but nothing is stopping you from creating your own specializations of TVirtualDataTable and TVirtualDataTableState.

Also mentioned in the slide there was an example project which creates specializations that allow you to use a TStringList as a datasource in your FlexCel Reports. You can get it here: http://www.tmssoftware.biz/flexcel/samples/stringlisttable.zip

To create a new datasource of FlexCel, you need to answer some questions like "how many records my datasource has?", or "what are the names of the columns for my datasource?". To do so, you need to create 2 different classes, descending from TVirtualDataTable and TVirtualDataTableState. Why 2 different classes? The difference between TVirtualDataTable and TVirtualDataTableState is that the first contains information which is static and can be used in different threads or different datasources in the same report without worries, while the second contains state information which changes for each dataset used, even if it the dataset is the same. FlexCel can keep a single copy of the TVirtualDataTable in memory and use it for all similar tables in a report, but it needs to create different TVirtualDataTableState classes for each table.

For example, a DataTable will have always the same columns and column names, no matter how many times it is used inside the same report. So the question: "what are the names of the columns for my datasource?" is answered in the TVirtualDataTable abstract class, by overriding the Get_ColumnCount, GetColumn, GetColumnName and GetColumnCaption methods.

For example, the TStringList datasource has a single column with the data (as it is just a list of strings). We will name this column "Data" and so the methods look like this:
function TStringListProvider.GetColumn(const columnName: UTF16String): Int32;
begin
  if not SameText(columnName, 'DATA') then raise Exception.Create('Unknown Column: ' + columnName);
  Result := 0;
end;

function TStringListProvider.GetColumnCaption(
  const columnIndex: Int32): UTF16String;
begin
  Result := GetColumnName(columnIndex);
end;

function TStringListProvider.GetColumnName(
  const columnIndex: Int32): UTF16String;
begin
  Result := 'Data';
end;

function TStringListProvider.Get_ColumnCount: Int32;
begin
  Result := 1;
end;

On the other hand, the question "how many records my datasource has?" depends in the master-state relationship and must be answered by a TVirtualDataTableState. For example, a detail table in a master-detail relationship might have 3 records for the first master record, but 5 for the second. In our TStringList example, we just override the Get_RowCount to return the number of items in the string list:
function TStringListStateProvider.Get_RowCount: Int32;
begin
  Result := FStringList.Count;
end;
If you now look at the source for our TStringList example, you will see that we have already covered almost everything. The only thing missing is to provide FlexCel with the actual values for a row and column. You do this by overriding the methods:
function TStringListStateProvider.GetValue(const column: Int32): TReportValue;
begin
  Result := FStringList[Position];
end;

function TStringListStateProvider.GetValue(const row,
  column: Int32): TReportValue;
begin
  Result := FStringList[row];
end;
And that is mostly it. The only method in the example that we did not cover is a method that will create a state from a TVirtualDataTable:
function TStringListProvider.CreateState(const sort: UTF16String;
  const masterDetailLinks: TMasterDetailLinkArray;
  const splitLink: TSplitLink): TVirtualDataTableState;
begin
   Result := TStringListStateProvider.Create(FStringList, self);
end;

And that is used by FlexCel to create the multiple VirtualDataTableState instances it needs from the single VirtualDataTable which you add with FlexCelReport.AddTable.

Of course this only shows the simplest virtual datatable wrapper that you can create. There are many other methods available for overriding in TVirthalDataTable and TVirtualDataTableState, which allow for advanced functionality. For example, if you want to do a lookup in the datasource with the <#lookup> tag, you might want to override the Lookup method (even when the default implementation is good). Some other functionality like DISTINCT might not be available if you don't override the corresponding method. But for most reports, what we covered here is enough.

The report designer

Another thing I got asked about is the report designer I had installed in Excel during the presentation. This is an Excel addin which appears as a new tab in the ribbon:



It allows you to design a report template in a simpler way, by dragging and dropping fields from a tag pane. The bad news is that it has been in its current state (not yet finished) for more than a year already, and I never seem to get the time to finish it. There are always more important things to do. But as I've been asked by many people in the tms day about the possibility to get a beta, I will do my best to get a beta released in a couple of weeks. Not all functionality will be working, but the basics should be there.

At TMS, ALL-ACCESS means ALL-SPOILED!

$
0
0

In October 2015 we introduced TMS ALL-ACCESS, our 1 year subscription to the entire product range.

This no-nonsense subscription was created due to the numerous requests we received from our customers. They simply wanted ALL products with no restrictions or limitations. At tmssoftware.com all products means all products we offer now but also all new products we'll release in the year of the subscription. Hereby a small overview of the new products we added to already large range of products in TMS ALL-ACCESS since the announcement in October 2015:


Nov 12, 2015 : TMS LCL Cloud Pack
Set of LCL components to offer easy access from Windows, Raspberry Pi, Mac OS-X, Linux applications to cloud services like Amazon, OneDrive, Box, DropBox, Facebook, Flickr, FourSquare, Picasa, Google Calendar/Drive/Tasks/Places, LinkedIn, Twitter, Windows Live Calendar/Contacts, Wunderground weather, YouTube, ...

Dec 3, 2015: TMS Logging
Compact cross-platform logging framework offering informative log output to a flexible number of targets with a minimum amount of code.

Jan 21, 2016: TMS FNC Chart
The TMS FNC Chart is a fully cross-platform component designed to display different kinds of data such as financial and marketing data, monthly business sales, graphical and math data and much more as a chart. The Chart supports types such as bar, area, line, marker and variants such as stacked bar, stacked area, stacked percentage area, stacked percentage bar XY-line, XY-scatter and digital line.

Feb 26, 2016: TMS Echo
Delphi framework for data replication. It allows you to have two or more databases and synchronize data between them. Changes you make to one client database (inserts, updates, deletes) can be sent to another database. It's bidirectional, so you can send and receive changes between databases.

Mar 31, 2016: myCloudData.net service
myCloudData.net offers an extremely simple, fast and flexible way to have data from desktop, web, mobile and IoT apps in the cloud. myCloudData.net offers a secure use of cloud data storage with API based metadata configuration. It uses OAUTH 2.0 and a full REST/JSON/HTTP API, and is accessible from all programming environments

Apr 5, 2016: TMS FNC UI Pack
Powerful, feature-rich UI controls in 1 component set for 3 frameworks and 5+ operating systems. Use one source-code base, one UI component set to create feature-rich applications for all popular operating systems that exist on the planet.


So basically, our TMS ALL-ACCESS customer were treated monthly with a new components set! ( Not sure if we can keep up this speed of new product releases but we have a long list of new component, new component features & new product ideas) So, with TMS ALL-ACCESS, you never have to worry again if you will miss something or if you have to take budget in account for future products. No more excuses... TMS ALL-ACCESS is just our best product you can get at tmssoftware.com! We invite you to have a look here at the list of products available now in TMS ALL-ACCESS.


A single-source TV-guide for 6 operating systems

$
0
0

Introduction

With the first release of our brand new TMS FNC UI Pack we are venturing into a new way of designing and creating components. A way that allows developers to easily switch between 3 frameworks (FMX, VCL and LCL). As the TMS FNC UI Pack targets these three frameworks it automatically comes with support for a multitude of operating systems. As we wanted to take "easily switching between 3 frameworks" to the test we have created a TV-guide application that uses the planner component, parses JSON retrieved with our TMS Cloud Components and made it running on 6! major operating systems: Windows 10, Mac OS X Yosemite, iOS 9.0, Android Lollipop, Ubuntu and Raspbian.

Click image for more screenshots.

Cross-platform, cross-framework code

Creating our business logic

After installing the TMS FNC UI Pack the TTMSFNCPlanner component is available on FMX, VCL and LCL and we are ready to start developing applications. Now, to start using it, it would be wise to think a few moments on how to write an application that is running on multiple frameworks, multiple operating systems. If we want to start with VCL and want to move to FMX in a couple of months, it would not be very resource and time friendly to write an application that does not use the power of FNC. Therefore we want to create a single source universal business logic unit that will be used in three different projects, one for every framework. To create a single source unit and use it in different projects, which is compatible with FMX, VCL and LCL we need to add a conditional define to our project to identify each framework, if only because unit names for FNC components must be different (requirement in the Delphi IDE hosting both FMX & VCL).





To initialize the planner and retrieving data from our service, we start by adding a TTVGuideLogic class that is instantiated in each separate project main form unit and contains the business logic for the app.
  TTVGuideLogic = class
  private
    FPlanner: TTMSFNCPlanner;
    FChannels: TTVChannels;
    FAccess: TCloudAccess;
  public
    destructor Destroy; override;
    function GetJSONArray(URL: string; AID: String = ''): TJSONArray;
    function FindChannelByName(AName: String): TTVChannel;
    procedure InitPlanner(APlanner: TTMSFNCPlanner);
    procedure InitChannels;
    procedure UpdateResources(AChannel: TTVChannel; AResource: Integer);
  end;
Each framework has its own set of units in order to compile succesfully. We use the conditional defines added to our project to make the difference between each framework.
uses
  {$IFDEF VCL}
  Classes, SysUtils, VCL.TMSFNCPlanner, VCL.TMSFNCCustomControl, VCL.TMSFNCPlannerBase, VCL.TMSFNCPlannerData, CloudBase,
  Generics.Collections, JSON, VCL.TMSFNCGraphics, VCL.TMSFNCUtils, DateUtils;
  {$ENDIF}

  {$IFDEF FMX}
  Classes, SysUtils, FMX.TMSFNCPlanner, FMX.TMSFNCCustomControl, FMX.TMSFNCPlannerBase, FMX.TMSFNCPlannerData, FMX.TMSCloudBase,
  Generics.Collections, JSON, FMX.TMSFNCGraphics, FMX.TMSFNCUtils, DateUtils;
  {$ENDIF}

  {$IFDEF LCL}
  Classes, SysUtils, LCLTMSCloudBase, LCLTMSFNCPlanner, LCLTMSFNCPlannerBase, LCLTMSFNCPlannerData, LCLTMSFNCGraphics, LCLTMSFNCUTils, DateUtils,
  fgl, fpjson, jsonparser;
  {$ENDIF}

Initializing the planner

The initialization code for the planner look & feel is added to the InitPlanner method, which is called after creating an instance of the TTVGuideLogic class in your project. When comparing this to our unit section, you will notice it doesn't require any conditional defines in order to succesfully compile. With the TMS FNC UI Pack we have added a few helper units to set the font size, set the color and have also created our own fill and stroke classes that are used in every FNC component.
procedure TTVGuideLogic.InitPlanner(APlanner: TTMSFNCPlanner);
var
  I: Integer;
begin
  FPlanner := APlanner;
  FPlanner.BeginUpdate;
  FPlanner.Items.Clear;
  FPlanner.Positions.Count := 6;
  FPlanner.OrientationMode := pomHorizontal;

  FPlanner.DefaultItem.TitleColor := gcSlategray;
  FPlanner.DefaultItem.TitleFontColor := gcWhite;
  FPlanner.DefaultItem.Color := gcWhitesmoke;
  FPlanner.DefaultItem.ActiveColor := gcSlateGray;

  FPlanner.Interaction.ReadOnly := True;

  FPlanner.Resources.Clear;
  for I := 0 to FPlanner.Positions.Count - 1 do
    FPlanner.Resources.Add;

  FPlanner.ItemsAppearance.Stroke.Color := gcWhite;
  FPlanner.ItemsAppearance.Stroke.Kind := gskSolid;
  FPlanner.ItemsAppearance.Stroke.Width := 2;
  FPlanner.ItemsAppearance.TitleStroke.Assign(FPlanner.ItemsAppearance.Stroke);

  FPlanner.GridCellAppearance.InActiveFill.Assign(FPlanner.GridCellAppearance.Fill);
  FPlanner.PositionsAppearance.Layouts := [pplTop, pplBottom];

  FPlanner.ModeSettings.StartTime := Now;
  FPlanner.ModeSettings.EndTime := Now;
  FPlanner.Mode := pmDay;

  FPlanner.TimeLineAppearance.Layouts := [ptlLeft, ptlRight];
  FPlanner.TimeLineAppearance.RightVerticalTextAlign := gtaTrailing;
  FPlanner.TimeLineAppearance.RightSubVerticalTextAlign := gtaLeading;
  FPlanner.TimeLine.CurrentTimePosition := pctpOverItems;
  FPlanner.TimeLine.DisplayUnitType := pduMinute;
  FPlanner.TimeLine.DisplayUnit := 5;
  FPlanner.TimeLine.DisplayStart := 0;
  FPlanner.TimeLine.DisplayEnd := (MinsPerDay div FPlanner.TimeLine.DisplayUnit) - 1;

  TTMSFNCUtils.SetFontSize(FPlanner.ItemsAppearance.TitleFont, 14);
  TTMSFNCUtils.SetFontSize(FPlanner.PositionsAppearance.BottomFont, 14);
  TTMSFNCUtils.SetFontSize(FPlanner.PositionsAppearance.TopFont, 14);

  FPlanner.EndUpdate;
  FPlanner.TimeLine.ViewStart := IncHour(Now, -2);

  InitChannels;

  UpdateResources(FindChannelByName('MTV'), 0);
  UpdateResources(FindChannelByName('Eurosport 1'), 1);
  UpdateResources(FindChannelByName('BBC 1'), 2);
  UpdateResources(FindChannelByName('TLC'), 3);
  UpdateResources(FindChannelByName('Disney XD'), 4);
  UpdateResources(FindChannelByName('CNN'), 5);
end;

Using the cloud to access information

A TV-guide application would only be a TV-guide application if it would show some TV-channels and the TV-shows that are playing at a specific time range. In the previous code snippet we have initialized the planner to show a time range of 24 hours, and the service that is used to retrieve the TV-shows of a specific TV-channel is parameterized to always return the TV-shows of today. To keep a reference to TV-channels and TV-shows we additionally add the classes needed to retrieve and persist information. In this code snippet, we have just conditional defines because of a small difference in handling generic lists between the Delphi compiler and the FPC compiler and the TMS Cloud access classes that have a different class name for VCL, FMX and LCL.
  TTVChannel = class;
  TTVShow = class;

  {$IFDEF VCL}
  TCloudAccess = class(TCloudBase);
  TTVShows = TObjectList<TTVShow>;
  TTVChannels = TObjectList<TTVChannel>;
  {$ENDIF}

  {$IFDEF FMX}
  TCloudAccess = class(TTMSFMXCloudBase);
  TTVShows = TObjectList<TTVShow>;
  TTVChannels = TObjectList<TTVChannel>;
  {$ENDIF}

  {$IFDEF LCL}
  TCloudAccess = class(TTMSLCLCloudBase);
  TTVShows = specialize TFPGObjectList<TTVShow>;
  TTVChannels = specialize TFPGObjectList<TTVChannel>;
  {$ENDIF}

  TTVShow = class
  private
    FGenre: string;
    FStartTime: TDateTime;
    FTitle: string;
    FID: string;
    FEndTime: TDateTime;
    FKind: string;
  public
    property ID: string read FID write FID;
    property Title: string read FTitle write FTitle;
    property Genre: string read FGenre write FGenre;
    property Kind: string read FKind write FKind;
    property StartTime: TDateTime read FStartTime write FStartTime;
    property EndTime: TDateTime read FEndTime write FEndTime;
  end;

  TTVChannel = class
  private
    FName: string;
    FID: string;
    FShows: TTVShows;
  public
    constructor Create;
    destructor Destroy; override;
    property ID: string read FID write FID;
    property Name: string read FName write FName;
    property Shows: TTVShows read FShows;
  end;
After defining the necessary classes, we create an instance of our cloud base component for FMX (TTMSFMXCloudBase) and we use that instance to retrieve our TV-channels in JSON.
procedure TTVGuideLogic.InitChannels;
var
  i: integer;
  arr: TJSONArray;
  o: TJSONObject;
  c: TTVChannel;
begin
  FChannels := TTVChannels.Create;

  FAccess := TCloudAccess.Create(nil);
  arr := GetJSONArray('http://www.tvgids.nl/json/lists/channels.php');
  if Assigned(arr) then
  begin
    for i := 0 to GetArraySize(arr) - 1 do
    begin
      o := GetArrayItem(arr, i) as TJSONObject;
      c := TTVChannel.Create;
      c.ID := FAccess.GetJSONProp(o,'id');
      c.Name := StringReplace(FAccess.GetJSONProp(o,'name'), '&eacute;', 'é', [rfReplaceAll]);
      FChannels.Add(c);
    end;
  end;
end;
And we call InitChannels in our planner initialization code
  InitChannels;
After retrieving the TV-channels, the TV-shows are also retrieved, parsed and added as items to our TTMSFNCPlanner.
procedure TTVGuideForm.UpdateResources(AChannel: TTVChannel; AResource: Integer);
var
  c: TTVChannel;
  s: TTVShow;
  arr: TJSONArray;
  i: Integer;
  o: TJSONObject;
  it: TTMSFNCPlannerItem;
  dt: TDateTime;
begin
  dt := Now;
  TMSFNCPlanner1.BeginUpdate;
  for I := TMSFNCPlanner1.Items.Count - 1 downto 0 do
  begin
    if TMSFNCPlanner1.Items[I].Resource = AResource then
      TMSFNCPlanner1.Items[I].Free;
  end;

  TMSFNCPlanner1.Resources[AResource].Text := 'No Channel Selected';
  if Assigned(AChannel) then
  begin
    c := AChannel;
    c.Shows.Clear;
    TMSFNCPlanner1.Resources[AResource].Text := c.Name;
    arr := GetJSONArray('http://www.tvgids.nl/json/lists/programs.php?channels='+c.ID+'&day=0', c.ID);
    if Assigned(arr) then
    begin
      for i := 0 to GetArraySize(arr) - 1 do
      begin
        o := GetArrayItem(arr, i) as TJSONObject;
        s := TTVShow.Create;
        s.ID := FAccess.GetJSONProp(o,'db_id');
        s.Title := FAccess.GetJSONProp(o,'titel');
        s.Genre := FAccess.GetJSONProp(o,'genre');
        s.Kind  := FAccess.GetJSONProp(o,'soort');
        s.StartTime := FAccess.IsoToDateTime(FAccess.GetJSONProp(o,'datum_start'));
        s.EndTime := FAccess.IsoToDateTime(FAccess.GetJSONProp(o,'datum_end'));
        c.Shows.Add(s);
        it := TMSFNCPlanner1.AddOrUpdateItem(s.StartTime, s.EndTime, s.Title, s.Kind);
        it.Resource := AResource;
        it.Hint := it.Title + ' [' + TimeToStr(Frac(s.StartTime)) + ' - ' + TimeToStr(Frac(s.EndTime)) + ']';
        if (dt >= it.StartTime) and (dt <= it.EndTime) then
        begin
          it.Color := gcYellowgreen;
          it.FontColor := gcWhite;
        end;
      end;
    end;
  end;

  TMSFNCPlanner1.EndUpdate;
end;
The UpdateResources call is also added to our planner initialization code and retrieves the channel of choice and adds an item for each show to the planner.
  UpdateResources(FindChannelByName('MTV'), 0);
  UpdateResources(FindChannelByName('Eurosport 1'), 1);
  UpdateResources(FindChannelByName('BBC 1'), 2);
  UpdateResources(FindChannelByName('TLC'), 3);
  UpdateResources(FindChannelByName('Disney XD'), 4);
  UpdateResources(FindChannelByName('CNN'), 5);
To starting using the code from this separate unit, we add the unit to our project and use the following code, after dropping an instance of the TTMSFNCPlanner on the form.
procedure TTVGuideForm.FormCreate(Sender: TObject);
begin
  FTVGuideLogic := TTVGuideLogic.Create;
  FTVGuideLogic.InitPlanner(TMSFNCPlanner1);
end;

procedure TTVGuideForm.FormDestroy(Sender: TObject);
begin
  FTVGuideLogic.Free;
end;
After succesfully retrieving our TV-channels and TV-shows, the application runs on Windows 10, but it could very well be iOS 9, or MAC OS X Yosemite, or Raspbian, or ...

The full source code is available for download

Click image for more screenshots.

Free IDE Tools for Delphi 10 Seattle

$
0
0

For easily installing our free IDE plugins we have created a bundled installer with support for the latest Delphi 10 Seattle IDE.

TMS Project Manager

Free IDE plugin to easily ZIP and upload projects.



TMS Rich Clipboard

Free IDE plugin to copy source code with syntax highlighting to the clipboard.



TMS Presentation Tool

Free IDE plugin to monitor clipboard, track clipboard history and to save & load code snippets for giving presentations with Delphi.



TMS What's New

Free IDE plugin to keep current with the latest releases, blogs & tweets from TMS software.



The bundled free IDE plugins can be downloaded here.

TMS rollout of components with RAD Studio 10.1 Berlin support started

$
0
0


This is a short notice for our customers who are eager to get started with RAD Studio 10.1 Berlin: we've started rolling out our bi-yearly wave of product updates with official built-in support for this new release. Starting from today, following product updates now have built-in support for RAD Studio 10.1 Berlin:

TMS Component Pack: Our suite of 400 feature-packed, sophisticated user interface controls, including advanced grids, ribbon, syntax highlighting memo, rich text editor, visual scheduling/planning control and much more... TMS Component Pack exists and helps software developers all over the world for almost 20 years now and has grown into a stable, vast, powerful, intuitive to use set of user interface controls you cannot miss.

TMS Pack for FireMonkey: Our suite of FMX controls for full cross-platform development targeting Windows, Mac OS-X, iOS, Android. TMS Pack for FireMonkey is a set of powerful controls that help you building advanced desktop or mobile applications including grid, planner, navigational controls, instrumentation controls, rich text editor and much more... Simply the most comprehensive bundle of cross-platform UI controls.

TMS FNC UI Pack: This is our brand new set of framework neutral UI controls allowing you to build single source code business logic based applications for VCL, FMX and LCL frameworks. Whether you'll write traditional Windows desktop VCL applications, cross-platform desktop or mobile applications with the FMX framework or use the free Lazarus IDE and FPC compiler to build Windows, Linux, Raspbian, ... applications, ... there is only one learning curve and only one source code base needed to use these framework neutral controls. With TMS FNC UI Pack, you're in full control, irrespective of the IDE or framework you use.

Our team is working hard to continue to add RAD Studio 10.1 Berlin support to our other wide range of component products. You can track here the progress of our work to cover other products.

Introducing TMS FNC UI Pack

$
0
0

TMS FNC UI Controls, a set of framework neutral controls

Introduction

As your customers use an ever increasing number of devices & operating systems, it is a daily challenge for us, software developers to make our software available for the myriad of target platforms in use. Fortunately, as Pascal developers, we already have a huge benefit that Delphi meanwhile targets 4 platforms: Windows, Mac OSX, iOS and Android. Add Lazarus to that, and Pascal developers can also target Linux and its derivatives such as Raspbian and many more. The 3 main frameworks we have available to create our software for these platforms are: VCL, FMX and LCL. The framework to use will be determined by the target and the IDE used. That implies that the controls that can be used are also typically determined by this choice and might limit your abilities when some controls or control features are not available for one of these frameworks. Addressing that limitation is exactly one of the primary goals of the TMS FNC UI Controls. It offers you a set of powerful & feature-rich UI controls that you can use in Delphi's VCL framework, FMX framework and Lazarus LCL framework. It allows to create Win32, Win64, Mac OS-X, iOS, Android, Linux, Raspbian, ... applications with a single codebase and a single learning curve.

Concept

FNC controls enable you to write and use 100% identical Pascal code, be it in a VCL app, FMX app or LCL app. The FNC components methods, properties and events are therefore 100% identical regardless of the framework being used. As an example, the following code creates a new event in our FNC Planner control:

var
  plIt: TTMSFNCPlannerItem;
begin
  plIt := TMSFNCPlanner1.Items.Add;
  plIt.StartTime := Date + EncodeTime(8,30,0,0);
  plIt.EndTime := Date + EncodeTime(11,0,0,0);
  plIt.Title := 'New event';
  plIt.Text := 'Content';
end;
and from this code, it is impossible to tell whether it will be from a VCL, FMX or LCL app. In the application UI, it will also look exactly the same regardless of framework or operating system:


This means that if you properly separate your logic or adopt an MVC approach, you can easily share .PAS files between VCL and FMX projects, between VCL and LCL projects etc... There are in this respect actually only two limitations. First limitation is the design-time form fileformat that is different between VCL, FMX and LCL. VCL uses the .dfm file, FMX uses the .fmx file and LCL uses the .lfm file. For applications for different devices with different form factors, it typically already requires to design the form separately for separate frameworks, so this isn't too much of a limitation. For other applications, a solution is to create the controls at runtime. A second limitation is the namespaces (unit names). To be able to register identical classnames for different framework controls in Delphi, it is required that these live in different namespaces. As such, the FNC VCL controls unit names have the prefix VCL., the FNC FMX controls unit names have the prefix FMX. and the FNC LCL controls use prefix LCL (without dot, to be able to support FPC versions older than v3.0)

In practice, this means that for the example above with the TMSFNCPlanner, the unit clauses for the different frameworks would be as below. To keep using a single source file, a solution is to set a define at project level depending on the framework and write:

uses 
{$IFDEF VCL}
  VCL.TMSFNCPlannerBase, VCL.TMSFNCPlannerData, VCL.TMSFNCPlanner, VCL.TMSFNCCustomControl;
{$ENDIF}

{$IFDEF FMX}
  FMX.TMSFNCPlannerBase, FMX.TMSFNCPlannerData, FMX.TMSFNCPlanner, FMX.TMSFNCCustomControl;
{$ENDIF}

{$IFDEF LCL}
 LCLTMSFNCPlannerBase,  LCLTMSFNCPlannerData, LCLTMSFNCPlanner, LCLTMSFNCCustomControl;    
{$ENDIF}
In the same way, when used, we could include the resource of our form file that is different in each framework via a conditional define:

{$IFDEF VCL}
{$R *.dfm}
{$ENDIF}

{$IFDEF FMX}
{$R *.fmx}
{$ENDIF}

{$IFDEF LCL}
{$R *.lfm}
{$ENDIF}
These are of course the things you need to take in account when you want to create a single codebase to build projects with multiple frameworks. In other cases, you do not need to take care of this and you can enjoy the exact same feature set of this UI component library irrespective of the IDE and platform you target.

Another important core concept is the introduction of the TMS FNC Graphics library that is included. This enables you to write graphics code that is framework independent. This includes framework neutral colors, fill, stroke, alignment, font, path types and the TTMSFNCGraphics class using this to draw everything you need. This is a sample code snippet of framework neutral drawing:

var
  gr: TTMSFNCGraphics;
begin
  gr := TTMSFNCGraphics.Create(PaintBox1.Canvas);

  gr.Fill.Color := gcYellow;
  gr.Stroke.Color := gcGray;

  gr.DrawRectangle(0,0,100,20);

  gr.Font.Color := gcRed;
  gr.DrawText(2,0,100,20,'Hello world',false)
  gr.Free;
end;
The result is:


and is exactly the same on every framework, target, device, ...

Controls

In TMS FNC UI Pack v1.0, there are already 29 controls included. On the tool palette this looks like:

VCL, FMX



LCL



This includes a grid, planner, richeditor, treeview, various color, font, fontsize, bitmap ... picker , toolbar, ... and more.

Introducing the TMS FNC Grid

The TMS FNC Grid is a high-performance, not data-bound grid capable of dealing with hundreds of thousands of rows, has a wide range of cell types and inplace editors, offers built-in sorting, filtering and grouping and can import and export data in several file formats. To illustrate some of the capabilities of the TMS FNC Grid, here is some framework code initializing the grid to show several cell capability features:

begin
  TMSFNCGrid1.FixedRows := 2;
  TMSFNCGrid1.ColumnCount := 7;
  TMSFNCGrid1.MergeCells(1,0,2,1);
  TMSFNCGrid1.MergeCells(3,0,2,1);
  TMSFNCGrid1.MergeCells(5,0,2,1);

  TMSFNCGrid1.Cells[1,0] := 'Monday';
  TMSFNCGrid1.HorzAlignments[1,0] := gtaCenter;
  TMSFNCGrid1.Cells[1,1] := 'AM';
  TMSFNCGrid1.Cells[2,1] := 'PM';

  TMSFNCGrid1.Cells[3,0] := 'Tuesday';
  TMSFNCGrid1.HorzAlignments[3,0] := gtaCenter;
  TMSFNCGrid1.Cells[3,1] := 'AM';
  TMSFNCGrid1.Cells[4,1] := 'PM';

  TMSFNCGrid1.Cells[5,0] := 'Wednesday';
  TMSFNCGrid1.HorzAlignments[5,0] := gtaCenter;
  TMSFNCGrid1.Cells[5,1] := 'AM';
  TMSFNCGrid1.Cells[6,1] := 'PM';
  TMSFNCGrid1.AutoNumberCol(0);

  TMSFNCGrid1.AddCheckBox(1,2,false);
  TMSFNCGrid1.AddRadioButton(1,3,1);

  TMSFNCGrid1.AddProgressBar(3,2,50);
  TMSFNCGrid1.Cells[3,3] := 'Hello world';

  TMSFNCGrid1.AddBitmapFile(5,2,'e:	mscalendar.png');
  TMSFNCGrid1.AddBitmapFile(5,3,'e:	msmail.png');

  TMSFNCGrid1.Cells[1,4] := 'Red';
  TMSFNCGrid1.Colors[1,4] := gcRed;
  TMSFNCGrid1.Cells[3,4] := 'Yellow';
  TMSFNCGrid1.Colors[3,4] := gcYellow;
  TMSFNCGrid1.Cells[5,4] := 'Lime';
  TMSFNCGrid1.Colors[5,4] := gcLime;

  TMSFNCGrid1.FontNames[1,4] := 'Courier New';
  TMSFNCGrid1.FontStyles[3,4] := [fsBold];
  TMSFNCGrid1.FontSizes[5,4] := 12;

  TMSFNCGrid1.AddNode(2,2);
end;


As another quick introduction to the grid, this 2 line snippet demonstrates how data from a CSV file can be loaded and automatic filtering via a drop down in the column header is enabled:

  TMSFNCGrid1.LoadFromCSV('e:	mscars.csv');
  TMSFNCGrid1.Options.Filtering.DropDown := true;

Of course, this is just a very brief introduction to the TMS FNC Grid. Just the FNC grid alone could deserve multiple articles to cover it in detail. You can familiarize yourself with the TMS FNC Grid by reading the full PDF developers guide you can find at http://www.tmssoftware.biz/download/manuals/TMSFNCGridDevGuide.pdf or use the trial or full version of the component that comes with several samples.

Introducing the TMS FNC Planner


Our TMS FNC Planner is a scheduling component with various built-in time-axis options, i.e. a day, week, month, period, half-day period, timeline as well as custom time-axis mode where you can fully control the duration of each timeslot in the Planner. The Planner supports single and multi resource views and can have the time-axis horizontal or vertical. When targetting the Planner to a mobile device, it will automatically use a touch-friendly approach to select, insert, delete, pan in the Planner. In a previous blog, we have presented a fully framework neutral sample TVGuide application that uses our TMS FNC Planner in this blog article: http://www.tmssoftware.com/site/blog.asp?post=335

Introducing the TMS FNC RichEditor

With the TMS FNC Rich Editor you can assemble a WordPad-style editor or Outlook style mail application in a matter of minutes. TMS FNC Rich Editor comes with capabilities to do WYSIWYG editing of rich text with images, URLs, bullet lists, custom graphics, mail merging etc... To make development even faster, there is a pre-built toolbar for rich editor editing and formatting and non-visual components to facilitate the import and export from HTML & RTF files and that of course in all frameworks, operating systems and target devices supported.

In this introduction sample, drop the TTMSFNCRichEditor on the form as well as the TTMSFNCRichEditorFormatToolbar and assign the TTMSFNCRichEditor to TTMSFNCRichEditorFormatToolbar.RichEditor. Also add a TTMSFNCRichEditorHTMLIO and TTMSFNCRichEditorRTFIO non-visual component on the form and also assign the TTMSFNCRichEditor to TTMSFNCRichEditorHTMLIO.RichEditor and TTMSFNCRichEditorRTFIO.RichEditor.

The rich editor content can be initialized with following code to perform a mail-merge that uses here two merge fields : NAME and EMAIL.

begin
  TMSFNCRichEditor1.AddText('Dear Mr. NAME');
  TMSFNCRichEditor1.AddLineBreak;
  TMSFNCRichEditor1.AddText('CC: EMAIL');
  TMSFNCRichEditor1.SelStart := 9;
  TMSFNCRichEditor1.SelLength := 4;
  TMSFNCRichEditor1.SetSelectionMergeField('NAME');

  TMSFNCRichEditor1.SelStart := 21;
  TMSFNCRichEditor1.SelLength := 5;
  TMSFNCRichEditor1.SetSelectionMergeField('EMAIL');
end;
When the app is started, the text can be further decorated by editing & formatting via the toolbar. When it is ready, following code performs the merge with the NAME and EMAIL field and is exported to RTF via TTMSFNCRichEditorRTFIO and after this, the merge is undone:


var
  sl: TStringList;
begin
  sl := TStringList.Create;

  try
    sl.Add('NAME=Elon Musk');
    sl.Add('EMAIL=elon@tesla.com');
    TMSFNCRichEditor1.Merge(sl);
  finally
    sl.Free;
  end;

  TMSFNCRichEditorRTFIO1.Save('e:	msmerge.rtf');

  TMSFNCRichEditor1.UnMerge;
end;

Introducing the TMS FNC TreeView

Finally, another large feature-packed control from the TMS FNC UI Controls set we want to introduce is the TMS FNC TreeView, TTMSFNCTreeView. This is a multi-column treeview control with regular mode and virtual mode and designed for and capable of using millions of nodes. In addition, the nodes support rich information, text atttributes can be customized per node cell, HTML formatted text in node cells is possible, images, checkboxes can be added and optional inplace editing is available.

In this introduction we'd like to demonstrate the difference in regular (node collection) based mode and virtual mode when using the TTMSFNCTreeView. The first code snippet demonstrates an initialization of a 2 column treeview:

var
  tn,cn: TTMSFNCTreeViewNode;
begin
  TMSFNCTreeView1.BeginUpdate;

  TMSFNCTreeView1.Columns.Clear;
  TMSFNCTreeView1.Nodes.Clear;

  TMSFNCTreeView1.Columns.Add.Text := 'Country';
  TMSFNCTreeView1.Columns.Add.Text := 'Capital';

  tn := TMSFNCTreeView1.AddNode(nil);
  tn.Text[0] := 'Europe';

  cn := TMSFNCTreeView1.AddNode(tn);
  cn.Text[0] := 'Germany';
  cn.Text[1] := 'Berlin';

  cn := TMSFNCTreeView1.AddNode(tn);
  cn.Text[0] := 'France';
  cn.Text[1] := 'Paris';

  cn := TMSFNCTreeView1.AddNode(tn);
  cn.Text[0] := 'United Kingdom';
  cn.Text[1] := 'London';

  tn := TMSFNCTreeView1.AddNode(nil);
  tn.Text[0] := 'Asia';

  cn := TMSFNCTreeView1.AddNode(tn);
  cn.Text[0] := 'Japan';
  cn.Text[1] := 'Tokyo';

  cn := TMSFNCTreeView1.AddNode(tn);
  cn.Text[0] := 'China';
  cn.Text[1] := 'Peking';

  TMSFNCTreeView1.EndUpdate;
end;



Important to note here is that the text in the multiple columns of the treeview can be simply accessed with an array indexed Node.Text[]: string property.

In a next step, we'll use the TTMSFNCTreeView in virtual mode and insert 1 million nodes! Columns are not virtual, so this must be initialized and to keep it simple, this will be initialized to one column:

begin
  TMSFNCTreeView1.Columns.Clear;
  TMSFNCTreeView1.Columns.Add;
  TMSFNCTreeView1.Columns[0].Text := 'Large treeview';
end;

To use the TTMSFNCTreeView in virtual mode, two events are crucial: the OnGetNumberOfNodes() event and the OnGetNodeText() event. The first is triggered to know how many nodes at root level or child level should be added. The latter is used to retrieve the column text of the node. Let's start with the OnGetNumberOfNodes event. This event has parameters ANode and a var parameter ANumberOfNodes. ANode is either a node with ANode.Level set to -1 indicating the number of root level nodes is requested or it contains the node for which the number of child nodes is requested. With the ANode.Level property, you can know how many hierarchical levels deep the node is. In this example, we'll insert 1 million (100x100x100) nodes by inserting 100 root level nodes that have each 100 childs and each child has again 100 subchilds.

This event takes care of this:

procedure TForm1.TMSFNCTreeView1GetNumberOfNodes(Sender: TObject;
  ANode: TTMSFNCTreeViewVirtualNode; var ANumberOfNodes: Integer);
begin
    if ANode.Level = -1 then
    ANumberOfNodes := 100
  else  
  if ANode.Level = 0 then
    ANumberOfNodes := 100
  else  
  if ANode.Level = 1 then
    ANumberOfNodes := 100;
end;

Then, the other event for virtual node handling, OnGetNodeText is used to return the text for node columns. Note that this event will be triggered for each node and for each column for this node. The column for which the event is triggered is indicated with the AColumn parameter. As we have only 1 column in this example, this is ignored and the node text is directly returned:

procedure TForm1.TMSFNCTreeView1GetNodeText(Sender: TObject;
  ANode: TTMSFNCTreeViewVirtualNode; AColumn: Integer;
  AMode: TTMSFNCTreeViewNodeTextMode; var AText: string);
begin
  if ANode.Level = 0 then
    AText := 'Root node '+inttostr(ANode.Index)
  else
  if ANode.Level = 1 then
    AText := 'Child node '+inttostr(ANode.Index)
  else
  if ANode.Level = 2 then
    AText := 'Subchild node '+inttostr(ANode.Index);
end;




We hope this brief introduction of the major controls in the TMS FNC UI Pack whetted your appetite to start exploring the components, discovering the benefits and efficiency of having one UI component set to cover all the target operating systems you want to target and perhaps cook up your first Linux GUI apps with LCL. We're eager to learn how your experience is going and to hear your feedback, comments and further wishes and needs in this direction.

Revisiting persisting app settings in the cloud

$
0
0

Back in 2013, I wrote a blog on persisting application settings in the cloud like Google Chrome for example does. The solution presented at that time was using the DropBox cloud service. The technique was quite simple. Persist settings in an INI file and automatically store and retrieve this INI file from a DropBox account.

Now, 3 years later, I wanted to revisit this technique and present a new solution that is more flexible and fine-grained and can be used from apps on Windows, Mac OSX, iOS, Android and Linux. The new technique will use the TMS structured data cloud storage service myCloudData.net.

As a brief introduction to myCloudData, think of it as configurable tables in the cloud, accessible with a REST API and using OAUTH 2 for authentication & authorization. If you want to learn more about myCloudData.net, check its API doc or another blog article written about it.

Time to get down to the nitty gritties! As an example, a VCL app was created with a left and right aligned panel and inbetween a memo. The user can customize the left panel and right panel width with a splitter control and by right-clicking the panel, the panel color can be changed. Further, there is a checkbox for both panels to toggle the visibility of the panel. This is a typical and basic skeleton of what many user configurable apps could be.



We'll now use the myCloudData.net service to store not only how the user configures this left and right panel, but also the last form's size and position on the screen. Whenever the user restarts the app, it will bring back the app as he originally left it on whatever machine the user starts this app. He will always find it back as he left it.
We'll drop a TAdvmyCloudData component on the form and in the form's constructor, we'll initialize it with the application key & secret obtained to use the myCloudData.net service. Further, this component is initialized to persist its application token & secret and finally, the call to AdvmyCloudData.Connect will test validity of possible existing tokens or will perform a new authentication & authorization when no valid tokens are found.

procedure TmCDDemo.FormCreate(Sender: TObject);
begin
  FConnected := false;
  AdvMyCloudData1.App.Key := MYCLOUDDATAKEY;
  AdvMyCloudData1.App.Secret := MYCLOUDDATASECRET;
  AdvMyCloudData1.App.CallBackPort := 8888;
  AdvMyCloudData1.App.CallBackURL := 'http://127.0.0.1:8888';

  AdvMyCloudData1.PersistTokens.Location := plIniFile;
  AdvMyCloudData1.PersistTokens.Key := '.myCloudData.ini';
  AdvMyCloudData1.PersistTokens.Section := 'tokens';
  AdvMyCloudData1.Connect;
end;
Once this step is performed, the AdvMyCloudData component triggers the OnConnected event from where we call the form's ReadSettings method.

In the ReadSettings method, it is checked whether the table to hold the settings exists for the user account on the service or not. When found, the first record from the table is read to retrieve the settings. When not found, the metadata for the table is created. Of course when the new table is created with its metadata, it's not yet possible to retrieve stored settings. This table will only be used the first time then when the user closes the app.
procedure TmCDDemo.ReadSettings;
var
  table: TMyCloudDataTable;

begin
  table := AdvMyCloudData1.TableByName('SETTINGS');

  if not Assigned(table) then
  begin
    table := AdvMyCloudData1.CreateTable('SETTINGS');
    table.MetaData.Add('PNLVISL', ftBoolean);
    table.MetaData.Add('PNLVISR', ftBoolean);
    table.MetaData.Add('PNLWL', ftInteger);
    table.MetaData.Add('PNLWR', ftInteger);
    table.MetaData.Add('PNLCLRL', ftInteger);
    table.MetaData.Add('PNLCLRR', ftInteger);
    table.MetaData.Add('FORMW', ftInteger);
    table.MetaData.Add('FORMH', ftInteger);
    table.MetaData.Add('FORML', ftInteger);
    table.MetaData.Add('FORMT', ftInteger);
    table.SetMetaData;
  end
  else
  begin
    table.Query;
    if table.Entities.Count > 0 then
    begin
      Self.Left := table.Entities[0].Value['FORML'];
      Self.Top := table.Entities[0].Value['FORMT'];
      Self.Width := table.Entities[0].Value['FORMW'];
      Self.Height := table.Entities[0].Value['FORMH'];
      LeftPanel.Visible := table.Entities[0].Value['PNLVISL'];
      RightPanel.Visible := table.Entities[0].Value['PNLVISR'];
      LeftPanel.Width := table.Entities[0].Value['PNLWL'];
      RightPanel.Width := table.Entities[0].Value['PNLWR'];
      LeftPanel.Color := TColor(table.Entities[0].Value['PNLCLRL']);
      RightPanel.Color := TColor(table.Entities[0].Value['PNLCLRR']);

      LeftCheckBox.Checked := LeftPanel.Visible;
      RightCheckBox.Checked := RightPanel.Visible;
    end;
  end;
end;
It was a choice to create this metadata programmatically. It could as well have been created in advance online via the metadata editor. When checking the myCloudData.net account, after this programmatic creation of metadata, it is shown as:

To store the settings, all we need to do is call WriteSettings from the form's OnClose event. This of course is only done when TAdvmyCloudData was able to succesfully connect to an account on the service:
procedure TmCDDemo.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if FConnected then
    WriteSettings;
end;
Persisting the settings in the WriteSettings procedure becomes:
procedure TmCDDemo.WriteSettings;
var
  table: TMyCloudDataTable;
  entity: TMyCloudDataEntity;
  ins: boolean;
begin
  table := AdvMyCloudData1.TableByName('SETTINGS');

  table.Query;

  ins := table.Entities.Count = 0;

  if not ins then
    entity := table.Entities[0]
  else
    entity := table.Entities.Add;

  entity.Value['FORML'] := Self.Left;
  entity.Value['FORMT'] := Self.Top;
  entity.Value['FORMW'] := Self.Width;
  entity.Value['FORMH'] := Self.Height;
  entity.Value['PNLVISL'] := LeftPanel.Visible;
  entity.Value['PNLVISR'] := RightPanel.Visible;
  entity.Value['PNLWL'] := LeftPanel.Width;
  entity.Value['PNLCLRL'] := integer(LeftPanel.Color);
  entity.Value['PNLWR'] := RightPanel.Width;
  entity.Value['PNLCLRR'] := integer(RightPanel.Color);

  if not ins then
    entity.Update
  else
    entity.Insert;
end;
So, with this small amount of code and a one-time authentication and authorization of the myCloudData account, the user will always have the last settings of the app he used, on any machine from where he is using the application.
I hope this serves as a small example of uses for the myCloudData.net service and actually only scratches the surface of possibilities. The use of myCloudData.net for the functionality demonstrated here is completely free, as in free beer. Only when using large amounts of data or blob fields, a subscription to myCloudData.net is required.
Our TMS Cloud Pack component TAdvmyCloudData makes it completely abstract and dead-simple to use the underlying REST API from the service for VCL applications and this component is also available with exactly the same interface for FireMonkey applications and Lazarus applications.

We are eager to learn about your creative and innovative applications using these components and service and in what further directions this can be developed to satisfy your needs!

The full source code of this sample application can be downloaded here.

Rest Server Authentication using JWT (JSON Web Token)

$
0
0

When building HTTP Servers with TMS Business frameworks - more specifically Rest/JSON servers with TMS XData - you can use the builtin authentication and authorization mechanism to protect server resources/requests. Such mechanism is actually implemented in our lower-level framework TMS Sparkle and can be used for any types of HTTP server, not only XData. This blog post will show you how to use authorization and authentication using JSON Web Tokens.

JSON Web Token (JWT)

From Wikipedia: "JSON Web Token (JWT) is a JSON-based open standard (RFC 7519) for passing claims between parties in web application environment". That doesn't say much if we have never heard about it before. There is plenty of information out there to read more in details about JWT, so here I'm going directly to the point in a very summarized practical way.

A JWT is a string with this format:

aaaaaaaaaaa.bbbbbbbbbb.cccccccccc

It's just three sections in string separated by dots. Each section is a text encoded using base64-url:

(base64url-encoded header).(base64url-encoded claims).(base64url-encoded signature)

So a real JWT looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidG1zdXNlciIsImlzcyI6IlRNUyBYRGF0YSBTZXJ2ZXIiLCJhZG1pbiI6dHJ1ZX0.
pb-4JAajpYxTsDTqWtgyIgpoqCQH8wlHl4RoTki8kpQ

If we decode each part of the JWT separately (remember, we have three parts separated by dots), this is what we would have from part one (spaces and returns added to make it more readable). It's the header:

{
  "alg":"HS256", 
  "typ":"JWT"
}

And this is part two decoded, which is the payload or claims set:

{
  "name":"tmsuser",
  "iss":"TMS XData Server",
  "admin":true
}

Finally the third part is the signature. It makes no sense to decode it here since it's just a bunch of bytes that represent the hash of three things: the header, the payload, and the secret that only the generator of the JWT knows.

The payload is the JSON object that "matters", it's the actual content that end-user applications will read to perform actions. The header contains meta information of the token, mostly the hashing algorithm used to generate the signature, also present in the token. So, we could say that a JWT is just an alternative way to represent a JSON object, but with a signature attached to it.

What does it has to do with authentication and authorization? Well, you can think of the JWT as a "session" or "context" for an user accessing your server. The JSON object in the payload will contain arbitrary information that you are going to put in there, like permissions, user name, etc. This token will be generated by your server upon some event (for example, an user "login"), and then the client will resend the token to the server whenever he wants to perform any operation. This would be the basic workflow:

1. Client performs "login" in the server by passing regular user credentials (user name and password for example)
2. The server validates the credentials, generates a JWT with relevant info, using the secret, and sends the JWT back to the client
3. The client sends the JWT in next requests, passing the JWT again to the server
4. When processing each request, the server checks if the JWT signature is valid. If it is, then it can trust that the JSON Object in payload is valid and can proceed normally

Since only the server has the secret, there is no way the client can change the payload, adding false information to it - for example, change the user name or permissions. When the server receives the modified JWT, the signature will not match and the token will be rejected by the server.

For more detailed information on JSON Web Tokens (JWT) you can refer to https://jwt.io, the Wikipedia article or just the official specification. It's also worth mentioning that for handling JWT internally, either to create or validate the tokens, TMS XData uses under the hood the open source Delphi JOSE and JWT library.

JWT Authentication with TMS XData

Enough of theory, the next steps will show you how to implement authentication/authorization using JWT in TMS XData. This is just a suggestion of implementation, and it's up to you to define with more details how your system will work. In this example we will create a login service, add the middleware and use XData server-side events to implement authorization.

User Login and JWT Generation

We're going to create a service operation to allow users to perform login. Our service contract will look like this:

  [ServiceContract]
  ILoginService = interface(IInvokable)
  ['{BAD477A2-86EC-45B9-A1B1-C896C58DD5E0}']
    function Login(const UserName, Password: string): string;
  end;

Users will send user name and password, and receive the token. Delphi applications can invoke this method using the TXDataClient, or invoke it using regular HTTP, performing a POST request passing user name and password parameters in the body request in JSON format. Nevertheless, be aware that you should always use a secure connection (HTTPS) in your server to protect such requests.

The implementation of such service operation would be something like this:

uses {...}, Bcl.Jose.Core.JWT, Bcl.Jose.Core.Builder;
 
function TLoginService.Login(const UserName, Password: string): string;
var
  JWT: TJWT;
  Role: string;
  IsAdmin: Boolean;
begin
  { check if UserName and Password are valid, retrieve User data from database, 
   add relevant claims to JWT and return it. In this example, we will only
   add two claims: Role and IsAdmin.  }
  
  // Now that application specific logic is finished, generate the token 
  JWT := TJWT.Create(TJWTClaims);
  try
    JWT.Claims.SetClaimOfType<string>('role', Role);
    JWT.Claims.SetClaimOfType<string>('isadmin', IsAdmin);
    JWT.Claims.Issuer := 'XData Server';
    Result := TJOSE.SHA256CompactToken('secret', JWT);
  finally
    JWT.Free;
  end;
end;

Now, users can simply login to the server by performing a request like this (some headers removed):

POST /loginservice/login HTTP/1.1
content-type: application/json
 
{
  "UserName": "tmsuser",
  "Password": "tmsuser"
}

And the response will be a JSON object containing the JSON Web Token (some headers removed and JWT modified for better readibility):

HTTP/1.1 200 OK
Content-Type: application/json
 
{
    "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6InRtc3VzZXIiLCJpc3MiOiJYRGF0YSBTZXJ2ZXIifQ.CAxxa3aizZheG3VXmBoXtfdg3N5jN9tNAZHEV7R-W4Q"
}

For further requests, clients just need to add that token in the request using the authorization header by indicating it's a bearer token. For example:

GET /customers?$orderby=Name HTTP/1.1
content-type: application/json
authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6InRtc3VzZXIiLCJpc3MiOiJYRGF0YSBTZXJ2ZXIifQ.CAxxa3aizZheG3VXmBoXtfdg3N5jN9tNAZHEV7R-W4Q

Adding TJwtMiddleware to process tokens in requests

The second step is to add the TJwtMiddleware to our XData server module. It's done just once, before starting up the server with the module. All we need to inform is the secret our middleware will use to validate the signature of the received tokens:

uses {...}, Sparkle.Middleware.Jwt;
 
{...}
  Module.AddMiddleware(TJwtMiddleware.Create('secret'));

That's it. What this will do? It will automatically check for the token in the authorization header. If it does exist and signature is valid, it will create the IUserIdentity interface, set its Claims based on the claims in the JWT, and set such interface to the User property of THttpRequest object. Regardless if the token exists or not and the User property is set or not, the middleware will forward the processing of the request to your server anyway. It's up to you to check if user is present in the request and perform the correct actions. The only situation where the middleware will return immediately is if the token is present and is invalid (bad format or wrong signature). In this case it will return an error to the client immediately and your server code will not be executed.

Authorizing the requests

Finally, in your application code, all you have to do is check for such User property and take actions based on it. For example, suppose you have a service operation DoSomething that runs some arbitrary code. You don't want to allow anonymous requests (not authenticated) to execute that operation. And you will also only execute it if authenticated user is an administrator. This is how you would implement it:

uses {...}, Sparkle.Security, XData.Sys.Exceptions;
 
procedure TMyService.DoSomething;
var
  User: IUserIdentity;
begin
  User := TXDataOperationContext.Current.Request.User;
  if User = nil then
    raise EXDataHttpUnauthorized.Create('User not authenticated');
  if not (User.Claims.Exists('isadmin') and User.Claims['isadmin'].AsBoolean) then
    raise EXDataHttpForbidden.Create('Not enough privileges');
  
  // if code reachs here, user is authenticated and is administrador
  // execute the action
end;

Server-side events

The above code is valid for service operations. But you can also use server-side events to protect the entity sets published by XData. For example, you can use the OnEntityDeleting event to not allow non-admin users to delete resources. The event handler implementation would be pretty much the same as the code above (Module refers to a TXDataServerModule object):

  Module.Events.OnEntityDeleting.Subscribe(procedure(Args: TEntityDeletingArgs)
    var User: IUserIdentity;
    begin
      User := TXDataOperationContext.Current.Request.User;
      if User = nil then
        raise EXDataHttpUnauthorized.Create('User not authenticated');
      if not (User.Claims.Exists('isadmin') and User.Claims['isadmin'].AsBoolean) then
        raise EXDataHttpForbidden.Create('Not enough privileges');
    end
  );

That applies to all entities published by the server. Of course, if you want to restrict the code to some entities, you can check the Args.Entity property to verify the class of object being deleted and perform actions accordingly.

Finally, another nice example for authorization and server-side events. Suppose that every entity in your application has a property named "Protected" which means only admin users can see those entities. You can use a code similar to the one above to refuse requests that try to modify, create or retrieve a protected entity if the requesting user is not admin.

But what about complex queries that return multiple entities? In this case you can use the OnEntityList event, which will provide you with the Aurelius criteria (TCriteria) that will be used to retrieve the entities. You can then modify such criteria depending on user permissions:

  Module.Events.OnEntityList.Subscribe(procedure(Args: TEntityListArgs)
    var 
      User: IUserIdentity;
      IsAdmin: Boolean;
    begin
      User := Args.Handler.Request.User;
      IsAdmin := (User <> nil) and User.Claims.Exists('isadmin') and User.Claims['isadmin'].AsBoolean;
      if not IsAdmin then
        Args.Criteria.Add(TLinq.EqualsTo('Protected', false));
    end
  );

The code above simply checks if the requesting user has elevated privileges. If it does not, then it adds an extra condition to the criteria (whatever the criteria is) which filters only the entities that are not protected. So non-admin users will not see the protected entities in the server response.


For more information about the subject, here is a summary of the links related to the topics covered in this post:
TMS Business Subscription Page
TMS XData Product Page
TMS Sparkle Product Page
TMS XData Online Documentation
TMS Sparkle Online Documentation
JSON Web Token Web Site
Wikipedia article about JSON Web Token
JSON Web Token Specification - RFC-7519
Delphi JOSE and JWT library



Developing your first FNC custom control

$
0
0

Some weeks ago, we released the TMS FNC UI Pack, a set of Framework Neutral Components (FNC), i.e. UI controls that can be used from VCL Windows applications, FireMonkey (FMX) Windows, Mac OS-X, iOS, Android applications and LCL framework based Lazarus applications for Windows, Linux, Mac OS-X,..
The TMS FNC UI Pack contains highly complex & feature-rich components such as grid, planner, rich editor, treeview, toolbars. To create such complex components that work under 3 frameworks and a myriad of operating systems is not a trivial excercise and requires intricate knowledge about the VCL, FMX and LCL frameworks as well as the operating systems the controls need to work under.
To help ourselves and the users of the TMS FNC UI Pack, we have introduced several abstractions that facilitate creating framework neutral components and this is what we want to cover in this brief introduction to developing FNC custom controls.

FNC custom control basics

The structure of the FNC custom control we want to present is this of a classic UI control. The control is responsible for painting itself and interacts with keyboard and/or mouse. The control has several properties to control its appearance and behavior. If we look at this concept from the perspective of implementing this for 3 different frameworks, the biggest challenges faced are:

1) abstractions in the code for dealing with graphics: especially VCL and FMX are quite different in this respect, so abstraction is welcome.
2) abstractions in keyboard and mouse handling: also here there are differences, although subtle, between VCL, FMX and LCL.
3) abstractions in types: types such as font, color, rectangles, points are different in FMX and VCL.

So, we'll cover what is included in FNC to facilitate dealing with this.

Unit organisation

Perhaps the biggest stumbling block is unit organisation. As it is desirable to create components that can be installed in the IDE, (which means for Delphi for both the VCL and FireMonkey framework simultanously) we'll need units for VCL and units for FireMonkey. Although we can use the same class name, the class hierarchy for the VCL control and the FMX control will be different. The VCL FNC control will descend from the VCL TCustomControl and the FMX FNC control will descend from the FMX TControl. In Lazarus, the FNC control will descend from the LCL TCustomControl. In a nutshell, to solve this, we create 3 units for a component that will be nearly identical and we provide a conversion step to allow you to write the code in one unit and automatically generate the other units for the other frameworks. For this example, in total, we'll have 6 units: 3 units with the code for the control for the 3 supported frameworks and 3 units for the component registration in the IDE:

// Units for the VCL variant of the FNC control
VCL.TMSFNCCust.pas
VCL.TMSFNCCustReg.pas

// Units for the FMX variant of the FNC control
FMX.TMSFNCCust.pas
FMX.TMSFNCCustReg.pas

// Units for the LCL variant of the FNC control
LCLTMSFNCCust.pas
LCLTMSFNCCustReg.pas
We'll also use 3 packages: a package for VCL, a package for FMX and a package for LCL. We can install the VCL & FMX package simultanously in the Delphi IDE and the LCL package in the Lazarus IDE. The package for the custom control will have a dependency to the framework plus to the TMS FNC UI Pack for the framework. The structure of the packages in the Delphi 10.1 Berlin IDE is:


and in Lazarus, this is:

Getting to grips

Ok, now that the unit structure is setup, we can focus on writing the code. To write this code, we'll use 3 TMS FNC UI Pack units with abstractions: xxx.TMSFNCCustomControl, xxx.TMSFNCGraphics and xxx.TMSFNCTypes (with xxx = the framework). In this example, we'll write the code in VCL and automatically generate the FMX & LCL equivalents from it, so the uses list becomes:

for FMX
uses
  Classes, Types, FMX.TMSFNCCustomControl, FMX.TMSFNCGraphics, FMX.TMSFNCTypes;
for LCL
uses
  Classes, Types, LCLTMSFNCCustomControl, LCLTMSFNCGraphics, LCLTMSFNCTypes;
The FNC control we'll write here is very rudimentary for the sake of focusing on the abstractions. It is basically a gauge control that can be clicked with the mouse to set the gauge value, has a gauge value property to set it and can use the key up & down to change the gauge value. So, we need painting of the gauge, we need some gauge line color & font settings and we need handling of the mouse click and keyboard.


Control initialization

The control descends from the abstract FNC class TTMSFNCCustomControl and exposes one extra color property for the gauge line color (note that this are of the abstract type TTMSFNCGraphicsColor), a font (of the abstract type TTMSFNCGraphicsFont, that has also a font color in all frameworks) and a gauge value property of the type TControlValue. Note that the Stroke & Fill properties are published. This contains the control border & control background color and even enables things such as setting a background gradient, a border with a specific pen style etc...

  TControlValue = 0..100;

  TTMSFNCCustomControlSample = class(TTMSFNCCustomControl)
  private
    FLineColor: TTMSFNCGraphicsColor;
    FFont: TTMSFNCGraphicsFont;
    FValue: TControlValue;
  protected
    procedure Draw({%H-}AGraphics: TTMSFNCGraphics; {%H-}ARect: TRectF); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Stroke;
    property Fill;
    property Font: TTMSFNCGraphicsFont read FFont write SetFont;
    property LineColor: TTMSFNCGraphicsColor read FLineColor write SetLineColor default gcRed;
    property Value: TControlValue read FValue write SetValue default 0;
  end;
In the constructor, initialization of the line color property value is done as well as the border color and the font is created. This is of the type TTMSFNCGraphicsFont and the main purpose is to have a font with font color in all frameworks. There is one more thing particular in the constructor and that is the assignment of the Font.OnChange event handler. In Delphi Pascal, we can simply assign the object method but for the FPC compiler, this needs to be prefixed with the @ symbol. Fortunately, the Lazarus environment has the LCL define we can use to handle this.

{ TTMSFNCCustomControlSample }

constructor TTMSFNCCustomControlSample.Create(AOwner: TComponent);
begin
  inherited;
  Stroke.Color := gcBlack;
  FLineColor := gcRed;
  FFont := TTMSFNCGraphicsFont.Create;
  FFont.OnChanged := {$IFDEF LCL}@{$ENDIF}FontChanged;
  Width := 100;
  Height := 100; 
end;

destructor TTMSFNCCustomControlSample.Destroy;
begin
  FFont.Free;
  inherited;
end;
Painting

The control descends from the abstract FNC class TTMSFNCCustomControl and exposes 3 color properties, the border, background and gauge line color (note that these are of the type TTMSFNCGraphicsColor), a font (of the type TTMSFNCGraphicsFont, that has also a font color in all frameworks) and a gauge value property of the type TControlValue.

Painting is done in the override of the Draw() method that has 2 parameters: AGraphics: TTMSFNCGraphics, a framework neutral graphics library and the rectangle of the control via ARect of the type TRectF. In VCL and LCL only the not fractional part of the floating point numbers is used but of course in the FireMonkey framework, this can use the fractional parts as well.

The painting code itself is:

procedure TTMSFNCCustomControlSample.Draw(AGraphics: TTMSFNCGraphics;
  ARect: TRectF);
var
  angle: double;
  lf,tf: TPointF;
  w: single;
begin
  inherited;

  angle := Value/High(Value)*2*PI;

  w := Min(ARect.Right - ARect.Left, ARect.Bottom - ARect.Top) / 2;

  lf.X := (ARect.Right - ARect.Left)/2;
  lf.Y := (ARect.Bottom - ARect.Top)/2;

  tf.X := lf.X + Cos(angle) * w;
  tf.Y := lf.Y - Sin(angle) * w;

  AGraphics.Stroke.Color := LineColor;

  AGraphics.DrawLine(lf,tf);

  AGraphics.Font.Assign(Font);
  AGraphics.DrawText(ARect, InttoStr(Value),false, gtaCenter, gtaCenter);
end;

Mouse and keyboard handling

The mouse and keyboard handling is via the HandleKeyDown and HandleMouseDown virtual method overrides:

  TTMSFNCCustomControlSample = class(TTMSFNCCustomControl)
  protected
    procedure HandleKeyDown(var {%H-}Key: Word; {%H-}Shift: TShiftState); override;
    procedure HandleMouseDown({%H-}Button: TMouseButton; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Single); override;
  end;
The implementation is straightforward. One thing that is noteworthy, is that the unit xxx.TTMSFNCTypes has a list of keys in a framework and operating system neutral way, i.e. we can check for the arrow up and arrow down keys with the constants KEY_DOWN and KEY_UP.

procedure TTMSFNCCustomControlSample.HandleKeyDown(var Key: Word;
  Shift: TShiftState);
begin
  inherited;

  if Key = KEY_DOWN then
  begin
    if Value > Low(Value) then
      Value := Value - 1;
  end;

  if Key = KEY_UP then
  begin
    if Value < High(Value) then
      Value := Value + 1;
  end;
end;
In the mouse down handler, we set the focus to the control when possible and then calculate the value of the gauge that matches with the mouse click position. Note that the X,Y values are of the type single to accomodate FMX and will contain non fractional values in VCL and LCL.

procedure TTMSFNCCustomControlSample.HandleMouseDown(Button: TTMSFNCMouseButton;
  Shift: TShiftState; X, Y: Single);
var
  angle: single;
  dx,dy: single;

begin
  inherited;

  if AllowFocus then
    SetFocus;

  dx := x - (Width/2);
  dy := - y + (Height/2);

  if dx = 0 then
    angle := sign(dy) * PI / 2
  else
    angle := ArcTan(dy/dx);

  if dx < 0 then
    angle := PI + angle;

  if angle < 0 then
    angle := angle + 2 * PI;

  Value := Round((angle / 2 / PI) * High(Value));
end;
Creating the units for FMX and LCL

Now that we have the VCL framework FNC component ready that contains 100% framework neutral code, let's create automatically the FMX and LCL units from this. 3 steps are needed:

1) Rename the unit VCL.TMSFNCCust.pas to FMX.TMSFNCCust.pas and LCLTMSFNCCust.pas
2) Change in the unit .PAS file the unit name, i.e. replace VCL.TMSFNCCust by FMX.TMSFNCCust and LCLTMSFNCCust respectively
3) Change the unit references in the uses list from
VCL.TMSFNCCustomControl, VCL.TMSFNCGraphics, VCL.TMSFNCTypes;
to
FMX.TMSFNCCustomControl, FMX.TMSFNCGraphics, FMX.TMSFNCTypes;
or
LCLTMSFNCCustomControl, LCLTMSFNCGraphics, LCLTMSFNCTypes;

To accomplish this, we call a simple powershell script that performs text replacement from VCL.TMS to FMX.TMS or LCLTMS respectively:

powershell -command "(gc VCL.TMSFNCCust.pas) -replace 'VCL.TMS','LCLTMS' |Out-file LCLTMSFNCCust.pas -Encoding utf8"
powershell -command "(gc VCL.TMSFNCCust.pas) -replace 'VCL.TMS','FMX.TMS' |Out-file FMX.TMSFNCCust.pas -Encoding utf8"
With these units created, we can compile the packages and install our FNC custom control for use from VCL, FMX and LCL applications in Lazarus.


The full source of this sample FNC custom control can be downloaded here. This sample can be used with the latest version of the TMS FNC UI Pack.

We hope this already whets your appetite for exploring FNC and the power of writing code for UI controls once for use in 3 frameworks. See also this blog article for a more general coverage of what is available in the TMS FNC UI Pack. In a next article, we'll go deeper in compound control creation and also the TTMSFNCGraphics library that offers a vast range of functions, going from drawing text, polygons, polylines, images in various formats, controls like checkboxes, radiobuttons, buttons, ... and much more.


Presenting the TMS software website redesign

$
0
0

Since 1995, the year tmssoftware.com started focussing on offering components & tools for developers, it was time for a fifth major overhaul of our website. Since the beginning of this year, a lot work went into this overhaul behind the scenes for this overhaul. Some of the main goals of this website overhaul we set at the beginning of this project were:

  • Improved product discoverability
  • Graphically evolve into a more modern style, influenced by Apple, Google material design, Windows 10 style
  • Improved organisation of products by technologies and overview of product structure
  • Find sufficient differentiating and recognizable style elements
  • Move to responsive design for optimal UX for mobile, tablet, desktop
  • Improved readability of information with more space, more contrast, more consistent and better structure of product pages
  • Make the site more consistent with common web practices
Taking all these goals in consideration, you will understand the project was quite challenging. What's more, we wanted to respect the motto: evolution instead of revolution to also keep a high degree of familiarity for customers who know and use our website for all these years. Fortunately, we got the help from the company Well Communications. Here's a break-down of how all these goals were achieved:

Improved product discoverability

There is product discoverability from website top down navigation and from entering the website via search. Instead of showing the latest product releases, the home page is now centered around products. Products can be discovered from the main "Products" menu with a popup showing an overview of our most popular products organised by product type.



. At the same time, there is an overview of technologies/frameworks our products are based on and from there, the visitor is guided to the products per technology. Within the technology boxes on the main page, we have also featured our most popular products within this technology and the visitor can navigate to it directly by clicking it.



Equally important is discoverability from entering the website via searches. Here we have on product pages immediately references to equivalent products for different technologies. If you'd search for a cross platform scheduling/planner components via Google and were directed to the VCL product page, you can immediately see in the top right corner that we have the product also available for other technologies/frameworks.




Graphically evolve into a more modern style

Where the previous website was still more influenced by the Office 2007 style, early iOS style, it was a choice to move to a more minimal style typically used these days. We removed the old DVD box analogy representing the software, we removed gradients, grayish & soft elements and now have a more sharp, slightly harder graphical design.

Improved organisation of products

There is a strong focus on organisation of products by technology or framework. This is already visible on the main page where we have currently 8 boxes representing 8 technologies/frameworks we have products for. Each technology or framework is represented by a specific color and these colors should help increase the awareness under what technology a product can be used.



Some products support multiple technologies/frameworks. Whereas this was not possible in the former website, now, products supporting multiple frameworks at the same time will be listed under each technology/framework these support. As such, our ORM product TMS Aurelius will appear under VCL, FMX and the business logic category as it falls into each of these categories.

Differentiating and recognizable style elements

Our logo has been slightly restyled with the underlining of the part 'tmssoftware' and ending the line with a corner under the dot. Where a square logo is needed like for avatars, this is reduced to 'tms' underlined ending under the dot.






This line & dot element is repeated for various boxes on our website and subtle animations stress this line & dot element.



We also like to think of the dot on the line a kind of similar to putting the dot on the i, hinting at our drive and continuous striving for delivering our products and support with the highest possible quality.

Responsive design

We had noticed that about 20% of our website traffic was coming from mobile devices & tablets, so we considered this important enough to also focus on offering a good UX for users on mobile phones and tablets. Our website will now automatically adapt to have a good usability on all these different browser form factors.



Improved readability

Where in our former website, the product information or blogs were often squeezed between the left sidebar and the right-side download box or blog history & selector, we removed the boxes at the right side of these pages and we add the capability to hide the left sidebar. This way, the full page can be used to read our product information and blogs and have this space available for bigger screenshots as well.

Consistency with common web practices

We paid attention to keep our website consistent with typical & common web practices these days. You'll find the website sign-in button in the top right corner of the page. You find the company information, social media links, website sitemap and commonly used page references always the bottom of the website in the footer area. In a similar way, after login the website will always display your user account name on top as well as the main menu in a fixed non-scrolling area. A search functionality across our product pages, web forum and FAQs is also available and always visible in the top right corner.




In a nutshell, we hope you will not only find our v5.0 website more visually pleasing but also easier to navigate and discover products. While v5 was tested and approved by a small group of people in the past couple of weeks, as of today, it is available for everyone. This means that from today, we're eager to learn from you how you experience the v5 website and more importantly, if you have more ideas and suggestions to improve it even further. With your inputs and help, we can continue to strive for perfection or "to put the dot on the line" in our case. But at the same time,keeping the famous words of painter Salvador Dali in mind: "Have no fear of perfection, you will never reach it".



TMS software teams up with Malcolm Groves' new Code Partners company

$
0
0

  -  
It's with a lot of enthusiasm that we'd like to announce today that TMS software teams up with Malcolm Groves' new Code-Partners company. With this partnership, Code Partners becomes our exclusive representative for Australia, Japan and East-Asia. Code Partners will offer a full scale of services to assist you with your needs for application development and in relationship to TMS software products specifically, this includes:

  • help you choosing the TMS components and tools for your tasks at hand
  • offer expert-level consulting for integrating our components in your applications with a direct line to our R&D team
  • handle selling of our products in the Australia, Japan and East-Asia region
  • offer training on our components and tools
  • make extra information, insights, tips & tricks available on TMS components online via blog, videos, webinars,..

We're convinced that Malcolm's deep technical product knowledge, long-time experience with Delphi and software development in general and strong motivation is an excellent choice for a successful & long-term partnership that strives for a win³ goal:

  • a win for our customers
  • a win for Code Partners
  • a win for us
Visit Code Partners today and stay tuned!

Almost 8 years at tmssoftware

$
0
0



I remember my first day at tmssoftware. Some guys behind their computer, busy toggling on their keyboard, all focused on that screen.
The first job Bruno gave me: here is our website, can you put all our products in a database, together with supported IDE's, frameworks, Windows versions. OK, not the nicest job... but afterwards I understood Bruno just wanted me to learn as fast as possible the entire product range. And yes, our product range is quite large.

The second job was to go through all product demo projects ... here we go again, this kept me busy for a few weeks. After this 'product' introduction, I finally was ready to take the challenge to enter the world of 'support'.

Now 8 years later, support is a daily routine, most of our customers know me, and more than that, most of the emails in the info@tmssoftware.com inbox are starting with 'hi Nancy'.

What also keeps fascinating me in this Delphi world is how everything evolves so incredibly fast. 8 years ago we were not even talking about FireMonkey, cross-platform development, components for accessing cloud services, testing on multiple devices like iPad, Android, Linux, Raspbian, ... It also seems like the Delphi world has a small group of real die-hard fans , always the same people you see in the Delphi Google communities, on forums, at conferences, ... Or is it just my imagination?

Beside all these new products we released over the 8 years, we also moved to a new office this year and last month we had a major redesign of our website (thanks again for all the positive comment we received!).

Can you imagine how curious I am about what new technologies, new challenges, new products, new exciting things I would write about in a similar blog post after another 8 years at tmssoftware?!

After these 8 years I can say I'm still glad to help our customers as good as I can and as fast as I can.

---
Kind regards,
Nancy Lescouhier
TMS software team


TMS teams up with Roman Yankovsky to bring TMS FixInsight, the static code analysis tool

$
0
0

We're pleased and honoured to announce that Roman Yankovsky joins the TMS family and the excellent static code analysis tool FixInsight is from now on available as TMS FixInsight product. Roman Yankovsky is the architect and developer of the highly respected FixInsight tool for Delphi. We're proud and excited that the high expertise and talent of Roman is now part of the TMS family! First of all, some details about what FixInsight can do for you:

FixInsight is an IDE plugin from where you can start a static code analysis or you can configure FixInsight to run as part of the build process. This code analysis helps you identify all potential issues in the code and coding convention compliance. This let's you trace bugs before you or your customers do. At this time, FixInsight has 29 specific code warnings, like for example a warning about objects created in the Try block or destructors without override directive... It has 10 code convention warnings and 2 code optimization hints.



FixInsight is all about watching over the quality of the code from early on in the development process and by that reducing the number of bugs and reduce support.

With FixInsight being part of the TMS family now, we'll work together with Roman and we look forward to bring future versions of TMS FixInsight that make it even more feature-rich than today. We of course also welcome & look forward to all feedback, comments, needs from you about FixInsight.

FixInsight is available in two editions: TMS FixInsight Personal and TMS FixInsight Pro. The difference between the two is that TMS FixInsight Pro comes with a command-line tool to integrate running TMS FixInsight from various automation / build processes.
Discover FixInsight now by loading the trial version from here

New cryptography solutions from TMS software in partnership with Cyberens

$
0
0

In the past couple of months, TMS software formed a partnership with the company Cyberens with the goal to jointly develop cryptography solutions for Delphi, C++, C users. With this partnership, Bernard Roussely and Marion Candau from Cyberens bring in the high expertise in cryptography algorithms with the expertise of TMS software to bring this in intuitive and easy to use classes for Delphi and C++Builder users. Bernard Roussely and Marion Candau have years of experience with cryptography, developed cryptography based solutions for various high-profile security projects in companies and governments and have completed all the administrative work to satisfy EU export control requirements.

The first result coming forth out of this partnership is the TMS Cryptography Pack. This is a bundle of advanced and up-to-date cryptography algorithms such as AES, SPECK, SALSA20, ECIES encryption, SHA2 and SHA3 hash generation, RSA, EdDSA signing ...



You can discover the TMS Cryptography Pack here and download a fully functional trial version for Delphi XE2 to Delphi 10.1 Berlin.

We're excited to work together with Bernard Roussely & Marion Candau to build out a strong line of cryptography based solutions for software developers in the coming years and we're eager to learn about your needs, comments, wishes in this area.

Viewing all 1008 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>