where : ibrtses delphi

debugging real time Delphi applications

Debugging realtime applications pose a class of problems of their owm. Consider some machinery being under control of your PC, be it under direct control or with another system in between. This machinery has some time dependence and context, to stay formal for now.So your PC application has a mathematical model of the machinery included. Be that a set of differential equations, a set of state machines or both. It is paramount that the model in the PC is in sync with the state of the external system, otherwise the control can be considered to be lost. In this context it is of note that there are stable processes and unstable processes. The difference may be minute, but it is there. So a heater can be switched on and latch the command or it can be default off. The difference being that the first continues heating when the control is lost while the other switches off by itself. So a control system must be designed such that it goes to a stable state when the control is lost. The worst case study of loosing control defines the safety concept. There is still some time dependence involved, and this suggests that the model running on the PC cannot be single stepped, as this would break the timing of the model. Nor a breakpoint is not going to be placed. So how are we going to proceed ?

Requirements to the physical system viewed from the PC software

If the whole system includes some embedded system in front of the hardware, these requirements are valid for them too. These requirements suggest that there are no hidden states in the system. Yes, they indeed do. To clarify what is meant, consider a remote vehicle in a maze and the control link is lost. A reinitialized system has no idea about the location nor orientation of the vehicle. A backtrack algorithm would theoretically bring it back out there to the start to begin anew. Even though such systems exist, they are not the kind you want to debug. This case could be helped if the driven track could be read out at startup. Debugging has a lot to do with planning ahead. Loosing communication has to be anticipated and graceful restart has to be planned.

A simulation of the external world

To reduce the time dependence, and the hazards, one first needs a simulation of the external world. This simulation can be in the same application or in another application, even on another machine. The simulation behaves roughly as the reality what the focus of the problem concerns, but has at least two additions: The simulator should have exactly the same (software-)interface as the external world, plus have some visualization and perhaps some parameter editors. So the application software is first made to run with the simulator. Down to virtually zero bugs. Afterwards, each bug costs time. The time dependence of the external system being the frame.

Debugging options

Even though we designed the system to be inherently save when contol is lost, it may be wiser an perhaps much faster to bring it to a save or idle state under full control. Thus the application should be able to report the state as well as offer the necessary controls to change the states. The debugging is then reduced to loging the state and its variables. Since this additional task should not draw overly resources, the PC must not be fully loaded with application and debugging. The most trivial way to log the state is to use a memo and write to it.

A local memo

The next level is to use the debugwindow.

The debugwindow

Hardly know is the system procedure OutputDebugString(PCHAR(..)); Whatever written here appears in the global debug window. IMO, it is thread save, meaning any thread can write to it. And it doesn't need a user interface, meaning also a service application can be debugged with it.
 uses syncobjs, sysutils, Windows;

 var mystring:string;

 const CRLF = chr(10)+chr(13);
  
  mystring:='state= ....'+CRLF;
  OutputDebugString(PCHAR(mystring));

a bit more versatile, because it is configurable in a few ways :
uses syncobjs, sysutils, Windows;

const
 D_DEBUGSTRING =  $00000001;  // must be 1 for Eventlog
 D_LOGFILE =      $00000002;

type TICKTYPE = int64;

var 
 DUMP_MASK : longword;   //controls the behaviour of the Dump command


{#define MILLISECS(ticks) (ticks/10000) //100 nanosecond}
function TimeStampToMillisecs(ts:int64):longword;
begin
    Result:=ts div 10000;
end;

function TimeStamp:int64;
VAR h:filetime;
    u:int64 absolute h; //u:SystemTime;
begin
 GetSystemTimeAsFileTime(h);
 result:=u;
end;

procedure dump(mask:longword; lpOutput:pAnsiChar);
var
 str:string;
begin
 if (DUMP_MASK and mask)=mask then begin
  str:=lpOutput;
  if (DUMP_MASK and D_DEBUGSTRING) = D_DEBUGSTRING then begin
   str:=str+CRLF;
   OutputDebugString(PChar(str));
   end;
  if (DUMP_MASK and D_LOGFILE) = D_LOGFILE then
    writeToLog('holla', str);
  end;
end;

UDP messages

A rather generic way of debugging is the use of UDP packets. UDP is a stateless protocol that allows to have the loging and visualizing done on the same or on another machine. Such a setup needs just two parameters : the IP and the port. A client UDP socket is opened in the application to be debugged and the strings or structured varibles are sent in a similar manner as the strings to the memo. The log (debug window) is a standalone applicationon on the same machine or another with a UDP Server port listening on the specified port. Whatever comes there is written to a memo. That is basically it.
udp client :

uses IdBaseComponent, IdComponent, IdUDPBase, IdUDPClient,...

var (or form-var)
udpc: TIdUDPClient;
  

procedure TForm1.connect1Click(Sender: TObject);
begin
 udpc.port:= ...;
 udpc.host:= ...;
end;

procedure TForm1.send1Click(Sender: TObject);
begin
 udpc.Send(memo1.Text);
end;

udp server :
uses IdBaseComponent, IdComponent, IdUDPBase, IdUDPClient, IdUDPServer,IdSocketHandle

udps: TIdUDPServer;

procedure TForm1.Connect1Click(Sender: TObject);
begin
 udps.defaultport:=...;
 udps.active:=true;
end;

procedure TForm1.udpsUDPRead(Sender: TObject; AData: TStream;
  ABinding: TIdSocketHandle);
var s:string;
    h:integer;
begin
 h:=AData.Size;
 setlength(s,h);
 AData.Read(s,h);
 memo1.lines.add(s);
end;



To be continued...



Feedback is welcome





sponsored links





home
delphi pages

last updated 16.may.06 or perhaps later






Copyright (99,2006) Ing.Büro R.Tschaggelar