Adding simple database, excel export, and reports with D4 Pro

  • Follow


I would like to add some simple database functionality to a D4 application 
I have been developing for quite a while. I tried previously to use some of 
the TDB* components and I had some success but I found that the application 
worked differently on another computer, and I really don't think I need the 
full database functionality and corresponding complexity that go along with 
that.

Basically, what I would like to do is:

1. Create, edit, and use a database consisting of records with a unique 
text ID, and several data points of real numbers.

2. Use the application to read test data and compare it to the expected 
values as determined by standard values stored in the database.

3. Export the data and results of comparison to a file format that may be 
used by Excel or MS Access.

4. Make simple reports based on this data and information from other 
sources (databases).

The simple approach would be to use text records with fixed length fields 
which would be stored in a text file. The file could be read into a 
TStringList object which could be sorted and the desired record could be 
found by searching on the text ID. But for convenient editing it may be 
better to use a TStringGrid or a TDBGrid, but these add layers of 
complexity. I expect to have several hundred records with total length of 
about 200 characters per record.

For exporting the data, a simple approach would be a CSV format which is 
directly usable by Excel and Access. The other approach is to use 
Automation to access the OLE properties and methods of Excel. I have done 
this sort of thing with other applications but it adds a lot of complexity.

In the Help for D4 there is information on Quick Reports components, but 
this again adds complexity and it also probably limits the customer to a 
specific format which is hard coded in the application. It may be better to 
just export the data and allow the end users to design their own reports 
using Excel or Access or perhaps other applications. This may be more 
flexible as well as simpler.

I'm just in the conceptual stage of this at the moment, and I'd just like 
to solicit some opinions and advice to avoid going down the wrong path. I'm 
also not fully comfortable using D4 because it is a rather old version and 
it seems to be the first to incorporate database functionality. Thus it is 
more likely to have bugs and it may not play well with more modern 
incarnations of MS Access. I have used Access 97 for a long time but I 
recently purchased Access 2007 which seems to have major differences.

If anyone can offer their thoughts on how to proceed I'd greatly appreciate 
it.

Paul 


0
Reply Paul 10/26/2009 10:48:22 PM

Just to add a little information:

I used a TStringList object for storing, sorting, and accessing records, 
but I found a problem with finding records with a unique ID field. I use 
the first 15 characters of each string as an ID which must be unique, and 
the remainder of the string is a sequence of data values. But now I realize 
that the TStringList.Find function checks for an exact match. I had thought 
that it might only check for equality of the number of characters in the 
search string. So I had to create my own function.

I ran into a little unexpected problem when I used an integer variable 
"Index" which I used in a for-next loop, and when I used it later as a 
string index it was no longer defined. I suppose I could have used a 
"while" loop with an increment of the "Index" variable. The compiler did 
issue a warning.

Also I found that when I used the TStrings.SaveToStream (where stream is a 
file), I had to use Seek(0,0) before calling or else the updated StringList 
was appended to the file.

Something else I found difficult to do without writing a special procedure 
was to copy a string into an array of characters where I wanted the initial 
array to be kept as it was initialized with all spaces. This is what I did:

  TReclIndex = record                //All 15 characters comprise a unique 
key
    rType: Array[0..4] of Char;
    rCoil: Array[0..6] of Char;
    rCurve: Array[0..2] of Char;
    end;

  TReclCurveType = (Rec,Str);
  TReclCurveRec = record
    ReclIndex: TReclIndex;            //This is the key
    Time2x: Array[0..6] of Char;      //These are fields for curve data 
points
    Time3x: Array[0..6] of Char;
    Time4x: Array[0..6] of Char;
    Time6x: Array[0..6] of Char;
    Time8x: Array[0..6] of Char;
    Time10x: Array[0..6] of Char;
    TolMax: Array[0..3] of Char;      //Additional information (tolerances)
    TolMin: Array[0..3] of Char;
    end;

  TReclCurve = record
  case TReclCurveType of
    Rec: (R: TReclCurveRec);
    Str: (S: Array[0..sizeof(TReclCurveRec)-1] of Char;)    // should be ); 
?
    end;

I found this construct rather confusing and now that I copied it here I see 
an apparent error that seemed to work anyway. But I did not know just what 
to call this in Delphi. In "C" it would be a union. So the help file didn't 
find the proper information for this. It seems a bit strange. I refer to 
the individual record fields as .R and the entire record as .S, for 
instance:

    S := StringOfChar(' ', sizeof(ReclCurve));                //Initialize 
entire record with spaces
    StrToArray( S, ReclCurve.S );
    S := StringOfChar('_', sizeof(ReclCurve.R.ReclIndex));    //Initialize 
index fields to underlines
    StrToArray( S, ReclCurve.S );
    StrToArray(cbReclType.Text, ReclCurve.R.ReclIndex.rType);
    StrToArray(cbReclCoil.Text, ReclCurve.R.ReclIndex.rCoil);
    StrToArray('A', ReclCurve.R.ReclIndex.rCurve);

My special function is:

procedure  StrToArray( S: String; var A: Array of char );
var i: integer;
begin
 for i := 1 to length(S) do begin
   if (i > sizeof(A)-1) then
     exit;
   A[i-1] := S[i];
   end;
end;

This would all probably be much easier using database functions, but I had 
problems before and this seems like it will do the job. And maybe I just 
need to get some sleep...

Paul


0
Reply Paul 10/27/2009 10:51:27 AM


"Paul E. Schoen" <paul@peschoen.com> wrote in message
news:ZkAFm.15340$MZ1.6882@newsfe11.iad...

> I used a TStringList object for storing, sorting, and accessing records,
> but I found a problem with finding records with a unique ID field. I use
> the first 15 characters of each string as an ID which must be unique,
> and the remainder of the string is a sequence of data values. But now I
> realize that the TStringList.Find function checks for an exact match. I
> had thought that it might only check for equality of the number of
> characters in the search string. So I had to create my own function.

People really do make the oddest assumptions. What does the help say?
What would you think is reasonable for a 'list of strings'?

I looked over some old code that does essentially the same thing (use
a TStrings for an environment, a list of name-value pairs) and it
appears TStrings is (or was then) not sufficiently flexible to do it
properly - apart from plumbing to put integers and Booleans in the list,
there is a new IndexOfName method. No useful overrides.


> I ran into a little unexpected problem when I used an integer variable
> "Index" which I used in a for-next loop, and when I used it later as a
> string index it was no longer defined. I suppose I could have used a
> "while" loop with an increment of the "Index" variable. The compiler
> did issue a warning.

This is part of the documented semantics of for loops: the loop counter
is valid only within the loop, and is read-only to you. This allows the
compiler to use certain optimisations, and to correctly handle
'for i:=low(i) to high(i) do ...;' (think about it).

If you want a valid index after the loop, use a while loop.

If at all possible, fix warnings. Disable them only if you really must.
Never, never, NEVER ignore them.


> Also I found that when I used the TStrings.SaveToStream (where stream
> is a file), I had to use Seek(0,0) before calling or else the updated
> StringList was appended to the file.

It doesn't matter that the stream was a file; it could have happened
with any stream. Streams have a current position. It's a feature.

Seek takes a number as it second parameter but that's a mistake. It
should have been an enumeration (and in the 64-bit overload, it is).
Please use the named constants soFromBeginning/Current/End.


> Something else I found difficult to do without writing a special
> procedure was to copy a string into an array of characters where I
> wanted the initial array to be kept as it was initialized with all
> spaces.

There's nothing wrong with writing your own procedures for things.
It's what makes us programmers.


> This is what I did:
[...]

> TReclCurve = record
> case TReclCurveType of
>   Rec: (R: TReclCurveRec);
>   Str: (S: Array[0..sizeof(TReclCurveRec)-1] of Char;) // should be ); ?

I have no idea. Nor do I care much. If that's what the compiler wants,
that's what it gets.


>     end;
>
> I found this construct rather confusing and now that I copied it here I
> see an apparent error that seemed to work anyway. But I did not know
> just what to call this in Delphi. In "C" it would be a union.

The Pascal term is a 'variant record'. No relation with Variants.


> So the help file didn't
> find the proper information for this. It seems a bit strange.

They're a historical feature and I don't like them much. They're mostly
useful in performing disk I/O, which isn't often done with fixed-size
records anymore.


> I refer to
> the individual record fields as .R and the entire record as .S, for
> instance:

>
>     S := StringOfChar(' ', sizeof(ReclCurve));
> //Initialize entire record with spaces

You could just as easily use FillChar on .R. (In fact, you then wouldn't
even need .S, and could remove the .R indirection as well.)


>     StrToArray( S, ReclCurve.S );
>     S := StringOfChar('_', sizeof(ReclCurve.R.ReclIndex));
> //Initialize index fields to underlines
>     StrToArray( S, ReclCurve.S );
>     StrToArray(cbReclType.Text, ReclCurve.R.ReclIndex.rType);
>     StrToArray(cbReclCoil.Text, ReclCurve.R.ReclIndex.rCoil);
>     StrToArray('A', ReclCurve.R.ReclIndex.rCurve);
>
> My special function is:
>
> procedure  StrToArray( S: String; var A: Array of char );
> var i: integer;
> begin
>  for i := 1 to length(S) do begin
>    if (i > sizeof(A)-1) then
>      exit;
>    A[i-1] := S[i];
>    end;
> end;

I'm not sure SizeOf(A) will do what you want. Although it might; I just
don't trust it.

Anyway, this is what Move is for.

Were you never tempted to start your array indices at 1?


> This would all probably be much easier using database functions, but
> I had problems before and this seems like it will do the job. And
> maybe I just need to get some sleep...

And to read an old book on Pascal. Some things will be impressive for not
having changed in forty years, others for people ever having been forced
to actually use them at all. You'll come out with a new appreciation
for Strings and TStreams, and hopefully with an understanding of how
Pascal really works. From your questions over the last few years, I've
gotten an impression of you as a bit of the 'magic' type of programmer,
and that's really a pity. And quite unnecessary; it's all quite simple
once you know how (and why) it works underneath.

Groetjes,
Maarten Wiltink


0
Reply Maarten 10/27/2009 12:02:52 PM

Paul

For a small database I would use descendants of TCollection /
TCollectionItem. These ancestors provide the skeleton & glue to do
what they say (with Add, Items methods etc). One adds functionality as
one needs it. I've used these many times.

Here's a quickie of your basic needs (I think). It uses a TStringList
to add your storage in each TDataItem (descended from TCollectionItem)
and a TData to hold all the TDataItems (descended from
TCollectionItem) together with SaveTo & LoadFrom methods.

Any there are any elements which are new to you, feel free to email me
for assistance.

unit DataU;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Add: TButton;
    ListBox1: TListBox;
    Label1: TLabel;
    SaveBtn: TButton;
    LoadBtn: TButton;
    procedure FormCreate(Sender: TObject);
    procedure AddClick(Sender: TObject);
    procedure SaveBtnClick(Sender: TObject);
    procedure LoadBtnClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TDataItem = class;

  TData = class(TCollection)
  private
    function GetItem(Index : integer) : TDataItem;
  public
    constructor Create(ItemClass: TCollectionItemClass);
    destructor Destroy; override;
    function Add : TDataItem;
    procedure SaveToFile(fPN : string);
    procedure LoadFromFile(FPN : string);
    procedure SaveToStream(Stream : TStream);
    procedure LoadFromStream(Stream : TStream);
    property Items[Index : integer] : TDataItem read GetItem; default;
  end;

  TDataItemIndex = (diiTime2x, diiTime3x, diiTime4x, diiTime6x,
diiTime8x,
                    diiTime10x, diiTolMax, diiTolMin);

  TDataItem = class(TCollectionItem)
  private
    FDataSL : TStringList;
    function GetValue(AIndex : integer) : string;
    procedure SetValue(AIndex : integer; AValue : string);
  public
    constructor Create(Collection : TCollection); override;
    destructor Destroy; override;
    procedure SaveItemToStream(Stream : TStream);
    procedure LoadItemFromStream(Stream : TStream);
    property DataSL : TStringList read FDataSL;
    property Time2x : string index integer(diiTime2x)
                        read GetValue write SetValue;
    property Time3x : string index integer(diiTime3x)
                        read GetValue write SetValue;
    property Time4x : string index integer(diiTime4x)
                        read GetValue write SetValue;
    property Time6x : string index integer(diiTime6x)
                        read GetValue write SetValue;
    property Time8x : string index integer(diiTime8x)
                        read GetValue write SetValue;
    property Time10x : string index integer(diiTime10x)
                         read GetValue write SetValue;
    property TolMax : string index integer(diiTolMax)
                        read GetValue write SetValue;
    property TolMin : string index integer(diiTolMin)
                        read GetValue write SetValue;
  end;

var
  Form1: TForm1;
  MyData : TData;

implementation

{$R *.DFM}

// = = = = = = = = = = = = = = = = = = = = = = = = =
// S t r e a m   A c c e s s   M e t h o d s
// = = = = = = = = = = = = = = = = = = = = = = = = =
function ReadStreamInt(Stream : TStream) : integer;
{returns an integer from stream}
begin
  Stream.ReadBuffer(Result, SizeOf(Integer));
end;

function ReadStreamStr(Stream : TStream) : string;
{returns a string from the stream}
var
  LenStr : integer;
begin
  Result := '';
  {get length of string}
  LenStr := ReadStreamInt(Stream);
  {set string to get memory}
  SetLength(Result, LenStr);
  {read characters}
  Stream.Read(Result[1], LenStr);
end;

procedure WriteStreamInt(Stream : TStream; Num : integer);
{writes an integer to the stream}
begin
  Stream.WriteBuffer(Num, SizeOf(Integer));
end;

procedure WriteStreamStr(Stream : TStream; Str : string);
{writes a string to the stream}
var
  StrLen : integer;
begin
  {get length of string}
  StrLen := Length(Str);
  {write length of string}
  WriteStreamInt(Stream, StrLen);
  if StrLen > 0 then
    {write characters}
    Stream.Write(Str[1], StrLen);
end;

// = = = = = = = = = = = = = = = = = = = = = = = = =
// T D a t a
// = = = = = = = = = = = = = = = = = = = = = = = = =
constructor TData.Create(ItemClass: TCollectionItemClass);
begin
  inherited Create(ItemClass);
end;

function TData.Add : TDataItem;
begin
  Result := TDataItem(inherited Add);
end;

function TData.GetItem(Index : integer) : TDataItem;
{this function saves having to typecast every access of the
 collection item returned from inherited access to inherited Items}
begin
  Result := TDataItem(inherited Items[Index]);
end;

procedure TData.SaveToFile(FPN : string);
var
  FS : TFileStream;
begin
  if FileExists(FPN) then
    FS := TFileStream.Create(FPN, fmOpenReadWrite)
  else
    FS := TfileStream.Create(FPN, fmCreate);
  SaveToStream(FS);
  FS.Free;
end;

procedure TData.LoadFromFile(FPN : string);
var
  FS : TFileStream;
begin
  if FileExists(FPN) then begin
    Self.Clear; // clear DataItems
    FS := TFileStream.Create(FPN, fmOpenReadWrite);
    FS.Seek(0, soFromBeginning);
    LoadfromStream(FS);
    FS.Free;
  end;
end;

procedure TData.SaveToStream(Stream : TStream);
var
  I : integer;
begin
  Stream.Seek(0, soFromBeginning);
  WriteStreamInt(Stream, Count);
  for i := 0 to Count - 1 do
    Items[i].SaveItemToStream(Stream);
end;

procedure TData.LoadFromStream(Stream : TStream);
var
  ItemCount, i : integer;
  DI : TDataItem;
begin
  Stream.Seek(0, soFromBeginning);
  ItemCount := ReadStreamInt(Stream);
  for i := 0 to ItemCount - 1 do begin
    DI := Self.Add;
    DI.LoadItemFromStream(Stream);
  end;
end;

destructor TData.Destroy;
var
  i : integer;
begin
  for i := 0 to Self.Count - 1 do
    Self.Items[i].Free;
  inherited Destroy;
end;

// = = = = = = = = = = = = = = = = = = = = = = = = =
// T D a t a I t e m
// = = = = = = = = = = = = = = = = = = = = = = = = =
constructor TDataItem.Create(Collection : TCollection);
var
  i : integer;
begin
  inherited Create(Collection);
  FDataSL := TStringList.Create;
  for i := 0 to 7 do
    FDataSL.Add(' ');
end;

function TDataItem.GetValue(AIndex : integer) : string;
begin
  Result := DataSL.Strings[AIndex];
end;

procedure TDataItem.SetValue(AIndex : integer; AValue : string);
begin
  DataSL.Strings[AIndex] := AValue;
end;

procedure TDataItem.SaveItemToStream(Stream : TStream);
begin
  WriteStreamStr(Stream, Self.FDataSL.Text);
end;

procedure TDataItem.LoadItemFromStream(Stream : TStream);
begin
  Self.FDataSL.Text := ReadStreamStr(Stream);
end;

destructor TDataItem.Destroy;
begin
  DataSL.Free;
  inherited Destroy;
end;

// = = = = = = = = = = = = = = = = = = = = = = = = =
// T D a t a   /   T D a t a I t e m   U s a g e
// = = = = = = = = = = = = = = = = = = = = = = = = =
procedure TForm1.FormCreate(Sender: TObject);
begin
  MyData := TData.Create(TDataItem); // create collection
end;

procedure TForm1.AddClick(Sender: TObject);
var
  DI : TDataItem;
begin
  DI := MyData.Add;
  with DI do begin
    {any old test data - I don't know what yours is like}
    Time2x := 'One' + IntToStr(MyData.Count);
    Time3x := 'Two';
    Time4x := 'Three';
    Time6x := 'Four';
    Time8x := 'Five';
    Time10x := 'Six';
    TolMax := 'Max';
    TolMin := 'Min';
  end;
end;

procedure TForm1.SaveBtnClick(Sender: TObject);
begin
  MyData.SaveToFile('E:\AProject\Collection Data\TestData.dat');
end;

procedure TForm1.LoadBtnClick(Sender: TObject);
begin
  MyData.LoadFromFile('E:\AProject\Collection Data\TestData.dat');
end;

end.

Alan Lloyd
0
Reply alanglloyd 10/28/2009 7:54:24 AM

"Maarten Wiltink" <maarten@kittensandcats.net> wrote in message 
news:4ae6e171$0$83251$e4fe514c@news.xs4all.nl...
> "Paul E. Schoen" <paul@peschoen.com> wrote in message
> news:ZkAFm.15340$MZ1.6882@newsfe11.iad...
>
>> I used a TStringList object for storing, sorting, and accessing records,
>> but I found a problem with finding records with a unique ID field. I use
>> the first 15 characters of each string as an ID which must be unique,
>> and the remainder of the string is a sequence of data values. But now I
>> realize that the TStringList.Find function checks for an exact match. I
>> had thought that it might only check for equality of the number of
>> characters in the search string. So I had to create my own function.
>
> People really do make the oddest assumptions. What does the help say?
> What would you think is reasonable for a 'list of strings'?

After I went through the debugging process and created a function to do 
what I wanted, I had a sort of d'oh slap on the forehead moment. I think I 
may have been thinking of a database-oriented find function or possibly 
equality of strings in VB. The help does clearly state that its purpose is 
to avoid duplicate strings or to find an exact match.


> I looked over some old code that does essentially the same thing (use
> a TStrings for an environment, a list of name-value pairs) and it
> appears TStrings is (or was then) not sufficiently flexible to do it
> properly - apart from plumbing to put integers and Booleans in the list,
> there is a new IndexOfName method. No useful overrides.

Yes, it was probably more difficult forcing it to work than just learning 
how to use the database functions properly.


>> I ran into a little unexpected problem when I used an integer variable
>> "Index" which I used in a for-next loop, and when I used it later as a
>> string index it was no longer defined. I suppose I could have used a
>> "while" loop with an increment of the "Index" variable. The compiler
>> did issue a warning.
>
> This is part of the documented semantics of for loops: the loop counter
> is valid only within the loop, and is read-only to you. This allows the
> compiler to use certain optimisations, and to correctly handle
> 'for i:=low(i) to high(i) do ...;' (think about it).
>
> If you want a valid index after the loop, use a while loop.
>
> If at all possible, fix warnings. Disable them only if you really must.
> Never, never, NEVER ignore them.

Actually I only noticed the warning when I finally ran the program in the 
IDE. When I did the syntax check it did not show the warnings. It just 
seemed odd to me that having defined and initialized the loop counter as a 
var in the procedure before the loop, and also using it after the loop, 
would have caused the compiler to recognize my intention and bypass the 
optimization. But that was a bad ASSumption.
>
>
>> Also I found that when I used the TStrings.SaveToStream (where stream
>> is a file), I had to use Seek(0,0) before calling or else the updated
>> StringList was appended to the file.
>
> It doesn't matter that the stream was a file; it could have happened
> with any stream. Streams have a current position. It's a feature.
>
> Seek takes a number as it second parameter but that's a mistake. It
> should have been an enumeration (and in the 64-bit overload, it is).
> Please use the named constants soFromBeginning/Current/End.

Yes, that was just a lazy shortcut. Certainly not good programming 
practice. But it had been a long night.
>
>
>> Something else I found difficult to do without writing a special
>> procedure was to copy a string into an array of characters where I
>> wanted the initial array to be kept as it was initialized with all
>> spaces.
>
> There's nothing wrong with writing your own procedures for things.
> It's what makes us programmers.

Probably I was thinking of strings and character arrays in C, where they 
can be more freely mixed and there are functions I had been accustomed to 
that would do what I wanted.


>> This is what I did:
> [...]
>
>> TReclCurve = record
>> case TReclCurveType of
>>   Rec: (R: TReclCurveRec);
>>   Str: (S: Array[0..sizeof(TReclCurveRec)-1] of Char;) // should be ); ?
>
> I have no idea. Nor do I care much. If that's what the compiler wants,
> that's what it gets.

I tried it both ways with the same apparent result. No errors displayed 
anyway.


>> I found this construct rather confusing and now that I copied it here I
>> see an apparent error that seemed to work anyway. But I did not know
>> just what to call this in Delphi. In "C" it would be a union.
>
> The Pascal term is a 'variant record'. No relation with Variants.

Now I finally found it in help, as "Variant parts in records". It is not 
really the same as a "C" union, as the variant records may be of different 
size. In my case I had a block of memory (an array of characters) which I 
wanted to handle as a single block, or record, and also as fixed length 
fields.


>> So the help file didn't
>> find the proper information for this. It seems a bit strange.
>
> They're a historical feature and I don't like them much. They're mostly
> useful in performing disk I/O, which isn't often done with fixed-size
> records anymore.
>
>
>> I refer to
>> the individual record fields as .R and the entire record as .S, for
>> instance:
>
>>
>>     S := StringOfChar(' ', sizeof(ReclCurve));
>> //Initialize entire record with spaces
>
> You could just as easily use FillChar on .R. (In fact, you then wouldn't
> even need .S, and could remove the .R indirection as well.)
>
>
>>     StrToArray( S, ReclCurve.S );
>>     S := StringOfChar('_', sizeof(ReclCurve.R.ReclIndex));
>> //Initialize index fields to underlines
>>     StrToArray( S, ReclCurve.S );
>>     StrToArray(cbReclType.Text, ReclCurve.R.ReclIndex.rType);
>>     StrToArray(cbReclCoil.Text, ReclCurve.R.ReclIndex.rCoil);
>>     StrToArray('A', ReclCurve.R.ReclIndex.rCurve);
>>
>> My special function is:
>>
>> procedure  StrToArray( S: String; var A: Array of char );
>> var i: integer;
>> begin
>>  for i := 1 to length(S) do begin
>>    if (i > sizeof(A)-1) then
>>      exit;
>>    A[i-1] := S[i];
>>    end;
>> end;
>
> I'm not sure SizeOf(A) will do what you want. Although it might; I just
> don't trust it.
>
> Anyway, this is what Move is for.
>
> Were you never tempted to start your array indices at 1?

Probably another "C" throwback, where arrays of characters (and strings) 
are zero based. It was clearer to me to use the string array based at 1. I 
find the multitude of string-like types confusing, especially when some 
functions use String, PChar, and even TCaption for essentially the same 
purpose. Often I just try a typecast and if the compiler doesn't choke, I 
let it go. And probably sometimes it bites me in the butt.


>> This would all probably be much easier using database functions, but
>> I had problems before and this seems like it will do the job. And
>> maybe I just need to get some sleep...
>
> And to read an old book on Pascal. Some things will be impressive for not
> having changed in forty years, others for people ever having been forced
> to actually use them at all. You'll come out with a new appreciation
> for Strings and TStreams, and hopefully with an understanding of how
> Pascal really works. From your questions over the last few years, I've
> gotten an impression of you as a bit of the 'magic' type of programmer,
> and that's really a pity. And quite unnecessary; it's all quite simple
> once you know how (and why) it works underneath.

I'm sure I still have my first book on Pascal (and Turbo Pascal) that I 
bought in 1987 when I took a "structured programming" course. By that time 
I had been using BASIC and various forms of assembly for about 20 years. 
But that consisted of college computer courses from 1967 to 1970 (BASIC and 
a little Fortran and Assembly on mainframes using punch cards and paper 
tape on a teletype with acoustic modem), and then very little until around 
1980 when I got involved with CP/M and 8085 and Z80 assembly, then BASIC 
and a little assembly on early IBM PCs starting around 1982. But my 
principal skillset was (and still is) electronics design and high power 
electrical design, although after 1985 much instrumentation was based on 
microprocessers and controllers.

Not long after doing some simple programs in Borland Turbo Pascal I 
discovered C, and then I used Borland C for many MSDOS console 
applications, mostly with a lot of low-level I/O. It was probably around 
1997 that I bought both Borland Delphi 4 Pro and also Borland C++ Builder 
3, each for about $100 at a computer show. At first I just used the Borland 
C++ for its better Windows-based IDE but still for MSDOS console 
applications. I tried my hand at C++ and eventually used it around 1996 to 
make a very early Windows based version of my Ortmaster program which I 
originally released as an MSDOS app in 1994. As my first foray into Windows 
applications it was very crude and I lacked familiarity with OOP.

I'm not sure how I got started with Delphi. By 1999 I had made a demo 
Ortmaster application using Delphi, and by 2003 there were many customers 
who had purchased the old Ortmaster system and now their new computers 
would not run the application, so my distributor and I started work on a 
new design which would no longer require the parallel port for the hardware 
interface and true MSDOS to run. By 2005 I had a hardware and software 
solution using a serial port, but for various reasons the software being 
developed by my distributor proceeded sporadically. He decided to use 
VB.NET because one of his technicians learned it in school, but after a 
couple of years he quit, and finally he hired a professional programmer who 
had helped me with some basic concepts of Delphi.

At this point I have designed and built a USB version of the hardware and I 
have invested in 120 PC boards and parts, but the other half of the 
software (which is mostly data processing and reports) is still not ready, 
and I have decided that I must proceed with my own version so I can get 
something out to customers who have been screaming for this new release for 
several years. Thus my recent efforts as touched upon here.

So, my involvement in software is on an "ad hoc" basis, such that I do just 
enough programming to get this system (as well as others) working. I am 
also involved with the hardware design, board layout, assembly, 
prototyping, and production, as well as marketing, procurement, and 
accounting. This is now my major project, but I have been similarly 
involved in other projects. This project has been especially frustrating 
because I have had to work with my distributor and he has his own narrow 
set of ideas on how to structure the design effort. And he has refused to 
write any sort of software specification, claiming that he will do so 
*after* the program is complete, because he feels it is a waste of time to 
write it and then change it as the project takes shape. So the concept is 
still in his head except for bits and pieces that I get from his occasional 
emails.

I know this has been "too much information", but perhaps it begins to 
explain why I do not have the depth of experience and knowledge that most 
professional programmers have and need to succeed. I consider Delphi as one 
tool among many, and I use it because I find it very capable and actually 
fun to use. But I do not have the time or desire to do extensive learning 
about everything it can do. I try to write programs that are well 
structured and maintainable, but often I find that I must learn on-the-fly 
and often that means ugly code and a debugging nightmare. But I have many 
other priorities.

Whew, that's enough! And I appreciate all the help you've given me over the 
past few years.

Thanks,

Paul 


0
Reply Paul 10/28/2009 8:16:51 AM

"Paul E. Schoen" wrote:
> 
>  TReclIndex = record                //All 15 characters comprise a unique 
>key
>    rType: Array[0..4] of Char;
>    rCoil: Array[0..6] of Char;
>    rCurve: Array[0..2] of Char;
>    end;
...
 
> This would all probably be much easier using database functions, but I had
> problems before 


I wonder what those problems exactly were? I am almost 99% sure that
using some ready written Database that has been tested for years would
be the reason to your problem.

Now you have chosen to start writing your own database look-alike
solution. Of course that may be interesting for some time. 

But of course that is also not what you should do. Not even if you are a
part time programmer. Your first attempts will be much clumsier and have
tons of bugs, compared tothose finalized components. And there's no time
when someone asks for multi user, better reports etc.

So you have decided that your data should be saved to CSV semicolon
limited text file? Even then your better _not_ start writing that kind
of functionality yourself. All that already exists, tested with tens of
hundreds of people, and all sources are available for free.

Use for instance components TkbmMemTable and its counterpart
TkbmCSVStreamFormat. You'll be able to use your data indexed, sorted and
manipulated with ease. And you can save get your data to a CSV file.
 "@@FILE VERSION@@","251"
 "Name","Phone","Address",
 "Susan","888-777 890","Rubicon Lane 542",
 "Peter","990-67890","Parsons Street 7",

TkbmMemTable uses Delphi's standard TDataSet schema. Works nicely with
all Data Controls, DbGrids etc. You'll easily find code how to export
your data from TkbmMemTable to Excel format, or how to write simple
reports from your TkbmMemTable datasets etc.

> And maybe I just need to get some sleep...

All right then. And maybe I also just need to let you continue writing
your code and solve your problem exactly as you wish. 

Making your own small programming inventions and findings may be more
rewarding for some time (even some years..) than just struggling to make
your everyday living in programming. 

I do not have that kind of luxury any more. I just can't start writing
any bigger chunk of code that already exists and that is available for
me for free or costs some hundred bucks.
-AP
0
Reply AP 10/28/2009 9:08:46 AM

"Paul E. Schoen" <paul@peschoen.com> wrote in message
news:Q8TFm.15413$MZ1.12901@newsfe11.iad...
> "Maarten Wiltink" <maarten@kittensandcats.net> wrote in message
> news:4ae6e171$0$83251$e4fe514c@news.xs4all.nl...
>> "Paul E. Schoen" <paul@peschoen.com> wrote in message
>> news:ZkAFm.15340$MZ1.6882@newsfe11.iad...

[...]
>>> TReclCurve = record
>>> case TReclCurveType of
>>>   Rec: (R: TReclCurveRec);
>>>   Str: (S: Array[0..sizeof(TReclCurveRec)-1] of Char;) // should be ); ?
>>
>> I have no idea. Nor do I care much. If that's what the compiler wants,
>> that's what it gets.
>
> I tried it both ways with the same apparent result. No errors displayed
> anyway.

Probably it's yet another instance of the semicolon being a separator,
not a terminator, and empty parts being allowed. The effect is that you
don't need a semicolon after the last declaration, and can sprinkle them
anywhere you like.

So I use them as terminators except where expressly forbidden, as I
expect most Pascal programmers do.


[...]
>>> maybe I just need to get some sleep...
>>
>> And to read an old book on Pascal. ...
>
> I'm sure I still have my first book on Pascal (and Turbo Pascal) that I
> bought in 1987 when I took a "structured programming" course. By that
> time I had been using BASIC and various forms of assembly for about 20
> years.

<snip The Story of Paul's Professional Life>

> I know this has been "too much information", but perhaps it begins to
> explain why I do not have the depth of experience and knowledge that
> most professional programmers have and need to succeed. ...

No, it's not TMI; I like hearing this sort of stuff. It reminds me that
not everybody is a programmer and there are other worthy skills that I
don't have.

I'll readily admit that sometimes I'm disappointed by it, but I know it's
a reality that no, in fact most professional programmers manage to get
by with a disturbing (to me) _lack_ of experience and knowledge. As I get
older, it becomes easier to live with.

Working in assembly is usually a good preparation for higher-level
programming. It shows how a computer works on the level below third
generation languages, and takes the mystery out of it. Knowing what
Pascal needs to translate can explain many of the design choices made
in the language.

Groetjes,
Maarten Wiltink


0
Reply Maarten 10/28/2009 10:23:05 AM

<alanglloyd@aol.com> wrote in message 
news:eeb236c6-26ae-4391-b82e-d24ac17aa82f@v30g2000yqm.googlegroups.com...
> Paul
>
> For a small database I would use descendants of TCollection /
> TCollectionItem. These ancestors provide the skeleton & glue to do
> what they say (with Add, Items methods etc). One adds functionality as
> one needs it. I've used these many times.
>
> Here's a quickie of your basic needs (I think). It uses a TStringList
> to add your storage in each TDataItem (descended from TCollectionItem)
> and a TData to hold all the TDataItems (descended from
> TCollectionItem) together with SaveTo & LoadFrom methods.
>
> Any there are any elements which are new to you, feel free to email me
> for assistance.

Thanks for the code. I made an additional form in my Ortmaster project, 
inserted your code and placed buttons and the ListBox as components, and I 
added a little button to show the form. I realize that the example was only 
a framework, but looking at it more closely I don't think it will do what I 
want any better than what I already have, and it would take some work to 
get that far.

I think it will become important to manipulate and store the data in a true 
database. The curve data that I am working on now has fixed length fields, 
the first three of which constitute a searchable and unique key, based on 
model, coil size, and curve. The remaining data are times corresponding to 
preset multiples of current (2X, 3X, 10X, etc), and using a cubic spline 
function the time for any current within and somewhat beyond the limits may 
be determined. Thus test results may be compared to the standard curve 
points and the deviation may be compared to the tolerance and used for 
GO/NOGO results.

There will be another database which is indexed on a specific device serial 
number or other ID. This data will contain information on specific details, 
which include the model, coil size, and one or more curves that may apply 
depending on how it is configured.

A third database will consist of test results, which have a unique 
date/time stamp and also a Recloser ID which identifies the model, coil 
sizes, and curves. Each test result will have up to four current values, 
four trip time values, and four reclose intervals (time between current 
pulses). Each current/time pair correspond to a particular curve. There 
will also be a field for test type, which may be specified as 3X, 4X, 8X, 
etc. There is also a MPU test which will have only a current value which 
should be a certain multiple (2x or maybe 1.5x) of the coil size. And there 
is also a GND test which may be conducted at several multiples of a 
separate GND TRip coil.

You can see that this is rather complex, and there may be even more 
databases such as Technician and Company. The Ortmaster program is 
primarily for obtaining and storing test results, and determining GO/NOGO 
results. The curves and recloser databases may be maintained using an 
external database program, but they need to be readable with the Ortmaster 
program, and the test results need to be formatted for inclusion. The 
original concept used a separate program to do the database manipulation, 
and the Ortmaster program simply accepted a text file with information 
needed to set up the test, and the results were returned in a different 
text file for data analysis and storage, as well as reports and maintenance 
of the other supporting databases. Perhaps this may be the better approach.

I am now undertaking this part of the project because of difficulties 
working with someone else who had developed the previous MSDOS version of 
his TCC application that worked with my MSDOS ORTRUN program. We had been 
working on the Windows version of this project since about 2004, at which 
time I had a working version of the hardware and software. It went through 
many changes, including the use of a USB connection rather than a serial 
port, but the main problem as I see it was the development of the TCC 
program. The project was originally coded in VB .NET, and it was started by 
a student and then transferred to a professional programmer I recommended, 
who introduced me to Delphi but who also knows VB. However he is not 
familiar with the actual testing process and the entire concept of the TCC 
software exists only in the brain of the original developer, who has 
refused to write a software specification. He considers that a waste of 
time and says he will do it only when the program is finished. Kind of like 
having a house built and then building the foundation under it.

I'm thinking that I might need to upgrade from D4 to a newer version. I 
think D4 introduced database functionality but used the proprietary BDE. D5 
AIUI introduced ADO which is more universal and allows connection to a MS 
Access database, which I am fairly familiar with. I am also familiar with 
dBase III+ format which I used in another project almost 20 years ago 
(Nantucket Clipper). It would be possible to make (or perhaps 
find/purchase) a Delphi unit that could manipulate that format, as it is 
mostly text files. But using index files and building and maintaining 
relations is a bit trickier.

So, maybe upgrading would be the best option. I think there are upgrades 
available for about $500 or less to D7 and maybe even D2009 Pro. There may 
even be some D5 or D6 or D7 on eBay for cheap. I've certainly gotten my 
$100 worth from D4 pro, and having the latest and greatest may be well 
worth it. My main worry is getting used to the new IDE and making sure I 
don't introduce subtle bugs or compatibility problems.

I have an older version of the Ortmaster program and some additional 
information on my website www.Ortmaster.com if you would like to have a 
look. I will post a newer version (in setup format) that will actually 
install the program. It's a simple install and does not use the registry or 
take any other possibly dangerous action.

Suggestions welcome. Thanks!

Paul 


0
Reply Paul 10/30/2009 4:21:33 PM

If you're really  convinced to go a "real" database then what about
InterBase (IB) datasets, which respond to SQL. Or MySQL.

OTOH I've found TCollection/TCollectionItem work well with all simple
accesses (not more than a couple of criteria). The separation of
methods which "belong" to the whole data from that for a particular
item is most convenient.

I've gone the route I have because I am not totally familiar with SQL
language.

I think a detailed outline of what you want to do with the data, and
their relationships, would clarify your choice and be necessary anyway
whatever route you chose.

Alternateively 4th Dimension is a powerful, easy to start, but
somewhat quirky. It has powerful graphic IDE (from its Mac heritage
which it carried across to its Windows implementation). Its got good
documenting capabilities.

Alan Lloyd.
0
Reply alanglloyd 10/31/2009 10:23:09 AM

"Paul E. Schoen" <paul@peschoen.com> wrote in message
news:tpEGm.836$3P2.458@newsfe09.iad...
[...]
> I think it will become important to manipulate and store the data
> in a true database.

Are your data relational? If not, there are few advantages over
text files. (I'm very fond of text files myself because *I* can
parse them, too, and edit them if necessary.)


[...]
> There will be another database which is indexed on a specific device
> serial number or other ID. ...

Databases aren't indexed. Tables are indexed.


> A third database will consist of test results, which have a unique
> date/time stamp and also a Recloser ID which identifies the model,
> coil sizes, and curves. Each test result will have up to four current
> values, four trip time values, and four reclose intervals (time
> between current pulses). ...

Three databases? You may be thinking in the wrong direction. You can
create any number of tables all in the same database. You can have
tables linked into clusters through foreign keys, and you can have
several unrelated clusters (I tend to think of them as subsystems)
still in the same database.


> [...] the entire concept of the TCC software exists only in the brain
> of the original developer, who has refused to write a software
> specification. He considers that a waste of time and says he will do
> it only when the program is finished.

Hm. Optimised for job security... his.


> I'm thinking that I might need to upgrade from D4 to a newer version.
> I think D4 introduced database functionality but used the proprietary
> BDE. D5 AIUI introduced ADO which is more universal and allows
> connection to a MS Access database, ...

I was supplied with Delphi 2007 at work but we're _still_ stuck with
the BDE.

Can't you just import the right type libraries into D4? In my previous
job we worked with raw ADO and it was a breeze compared to the mess of
datamodules and dbgrids I get to maintain now.

Groetjes,
Maarten Wiltink


0
Reply Maarten 10/31/2009 2:13:54 PM

"Maarten Wiltink" <maarten@kittensandcats.net> wrote in message 
news:4aec4623$0$83235$e4fe514c@news.xs4all.nl...
> "Paul E. Schoen" <paul@peschoen.com> wrote in message
> news:tpEGm.836$3P2.458@newsfe09.iad...
> [...]
>> I think it will become important to manipulate and store the data
>> in a true database.
>
> Are your data relational? If not, there are few advantages over
> text files. (I'm very fond of text files myself because *I* can
> parse them, too, and edit them if necessary.)

I feel more comfortable with text files for similar reasons. I think I can 
use what I have concocted and maybe add an export utility for a CSV file 
that can be read by Excel or Access. I will probably want to keep my curves 
database separate from the test results, which will be used for reports.


> [...]
>> There will be another database which is indexed on a specific device
>> serial number or other ID. ...
>
> Databases aren't indexed. Tables are indexed.

That's just from my days working with dBase III+ which used separate DBF 
files for tables (and separate NDX and NTX files, among others. But, yes, I 
meant tables.


>> A third database will consist of test results, which have a unique
>> date/time stamp and also a Recloser ID which identifies the model,
>> coil sizes, and curves. Each test result will have up to four current
>> values, four trip time values, and four reclose intervals (time
>> between current pulses). ...
>
> Three databases? You may be thinking in the wrong direction. You can
> create any number of tables all in the same database. You can have
> tables linked into clusters through foreign keys, and you can have
> several unrelated clusters (I tend to think of them as subsystems)
> still in the same database.

Yes, I have worked with MS Access 97 for quite some time and now I have 
Access 2007. The new software will open old DBF files and old Access97 MDB 
files, but the new file format is ACCDB


>> [...] the entire concept of the TCC software exists only in the brain
>> of the original developer, who has refused to write a software
>> specification. He considers that a waste of time and says he will do
>> it only when the program is finished.
>
> Hm. Optimised for job security... his.

He and I worked together fairly well in the beginning when I just had to 
develop the hardware and software to interface to his original MSDOS 
application. He wrote the application himself, and it is amateurish, even 
for an MSDOS console application. The information must be entered 
sequentially in response to prompts, and you can't go back and edit if you 
make a mistake. But since that time he has left his job as a recloser test 
technician and he has been doing network installations (as well as 
operating a skeet shooting range and going hunting for deer and bear). He 
does not have the skills to do serious programming even at my fairly 
amateurish level, and the programmer he hired does not have much knowledge 
of the testing of reclosers. I got a patent for a recloser test set in 1980 
and I have fairly extensive experience in the field of electrical testing, 
and I offered to do the TCC software but he had already started it with a 
student programmer who used VB .NET and he didn't want to "waste" the code 
by using a different language like Delphi. And he claims to be working 
10-12 hours a day, 7 days a week, so I think he is sleep deprived and 
probably not thinking too clearly and having anger issues. So I am glad to 
be breaking away from his influence.


>> I'm thinking that I might need to upgrade from D4 to a newer version.
>> I think D4 introduced database functionality but used the proprietary
>> BDE. D5 AIUI introduced ADO which is more universal and allows
>> connection to a MS Access database, ...
>
> I was supplied with Delphi 2007 at work but we're _still_ stuck with
> the BDE.
>
> Can't you just import the right type libraries into D4? In my previous
> job we worked with raw ADO and it was a breeze compared to the mess of
> datamodules and dbgrids I get to maintain now.

I have searched for and found some Delphi components that do not use the 
BDE. Some are freeware and some cost $150 or so. But I'm not sure just what 
I need. Mostly I'd be happy if I could work with an Access database. There 
will be some fairly simple relationships that could be handled even with 
text files.

Several I've looked at are:
http://bde-replacements.aidaim.org/in-memory_sql_database_delphi.htm
http://www.componentace.com/bde_replacement_database_delphi_absolute_database.htm
http://www.freedownloadscenter.com/Programming/Delphi_Tools_and_Components/LMD_Tools_4_5X_for_Delphi_4.html
http://www.smartcode.com/downloads/delphi-personal-database-component.html
http://www.aptrio.com/Development/Databases-Networks/branko-s-database-components-1155.html

I've made some good progress with the program and now I just need to figure 
out exactly how some testing is done. The single phase hydraulic reclosers 
I am most familiar with just have a sequence of up to four operations which 
may be either fast trip or delayed according to preset time/current curves. 
Fast trip curves are designated as "A", and the delayed curves may be B, C, 
D, or E. Three phase reclosers just have three units ganged together, so 
the tests must specify phase A, B, or C. But some reclosers have more 
complicated curves designated as 1, 1-2, 1-3, 19, and others. Many of these 
are ground trip curves which can be set in various ways and the curves are 
sometimes shifted by a certain time period. I am working with someone at a 
large testing company who has agreed to help.

For more information on reclosers and their testing there is a lot of 
literature on the Cooper website:
http://www.cooperpower.com/Library/Literature/section.asp?ProductLineID=12

Thanks,

Paul 


0
Reply Paul 10/31/2009 3:31:35 PM

I just uploaded this new version of Ortmaster to my www.Ortmaster.com 
website. It is Ortmaster.zip, and you just run the "install.exe" program. 
When you run the Ortmaster.exe program, use the menu\view\enhanced mode to 
show the Recloser Data button. When you click on it, it opens the main data 
form where you select the first items in the combo boxes for a 3H type, 5 
amp, B or C curve. I have tabbed pages for the curve data. To run a test, 
select test type 6X, and click Run Test. On the main test form click start, 
and the program will simulate a test in real time from a saved waveform 
file. Once the operations are complete, the CheckTCC button will be 
enabled. That will show the results form, with the test results and 
minimum, maximum, and optimum values. You can also click on "Keyboard 
Input" and edit the test results, then click "Analyze" to check the new 
results. There is also a "View Curves" button that shows the curves 
graphically.

There is also a "View Waveform" menu item that is on the main test form and 
it shows the waveform of the current and allows measurements of portions 
like a digital storage oscilloscope.

I need to add the actual checking of trip times with the curves. That will 
take another day or two. The recloser curve database file is 
"Ortmaster.rcf" in the Ortmaster directory under App Data.

Paul 


0
Reply Paul 10/31/2009 3:56:24 PM

"Paul E. Schoen" <paul@peschoen.com> wrote in message
news:sMYGm.1375$XP2.292@newsfe17.iad...
[...]
> I have searched for and found some Delphi components that do not use
> the BDE. Some are freeware and some cost $150 or so. But I'm not sure
> just what I need. Mostly I'd be happy if I could work with an Access
> database.

I certainly don't know what exactly you need, but if you're not using
data-aware controls, you can probably get everything done with
manually declared Database and Recordset (interface) variables and
calling methods on them.

Groetjes,
Maarten Wiltink


0
Reply Maarten 10/31/2009 4:54:13 PM

I have just made a Delphi project to try and understand how to create and 
use the database controls and objects. I was (finally) able to create a 
dbTest.DBF file and insert one record of data. But I ran into problems when 
the file existed and I tried to open it. Here is my code:

procedure TForm1.btCreateClick(Sender: TObject);
var DBIresult: Word;
begin
  if DataSource1 = nil then
    DataSource1 := TDataSource.Create(self);
  if Table1 = nil then
    Table1 := TTable.Create(self);
  if FileExists('dbTest.dbf') then begin
    if not Table1.CheckOpen(DBIresult) then                //This gives me 
a BDE exception
      exit;
    If not Table1.Exists then begin                        // If I bypass 
the CheckOpen this gives me AVs
      Table1.FieldDefs.Clear;
      Table1.FieldDefs.Add('ITEM', ftString, 30, True);
      Table1.FieldDefs.Add('QTY', ftInteger, 0, True);
      Table1.CreateTable;
      Table1.Active := True;
      Table1.InsertRecord( ['ABC', 123]);
      end;
    end
  else begin                                                // When I 
delete the file, this works OK
    Table1.FieldDefs.Clear;
    Table1.FieldDefs.Add('ITEM', ftString, 30, True);
    Table1.FieldDefs.Add('QTY', ftInteger, 0, True);
    Table1.CreateTable;
    Table1.Active := True;
    Table1.InsertRecord( ['ABC', 123]);
    end;
  Table1.Active := True;
  dbTest1.Open;
  dbTest1.StartTransaction;
end;

On Form Close:
    if dbTest1.InTransaction then
      dbTest1.Commit;
    if dbTest1.Connected then
      dbTest1.Close;
    Table1.Close;
    Table1.Free;
    Table1 := nil;
    DataSource1.Free;
    DataSource1 := nil;

The database components are set up as follows:

Table1.DatabaseName := dbTest;
Table1.TableName := dbTest;
Table1.TableType := TTDbase;

dbTest1: TDatabase;
dbTest1.DatabaseName := dbTest;
dbTest1.DriverName := Microsoft Access dBASE Driver (;

DataSource1.DataSet := Table1;

When I close the application I can open the dbTest.DBF file in MS Access 
2007 and it seems fine. It also saves it as dbTest.accdb.

When I added dbGrid and dbNavigator components, and set the DataSources to 
DataSource1, I got an AV error and then when I tried again it went into an 
endless loop and the CPU window showed garbage. Previously I also sometimes 
got the SQL cursor but it was hung up. Sometimes I got an insufficient 
memory error and I had to restart Delphi.

I have another application that uses some of these same components but 
somehow I got it to run without serious errors, although it did not really 
run correctly. In that project I created the components rather than 
dropping them on the form. Here is some of the code:

    DataSourceOptions := TDataSource.Create(FormData);
    DataSource1 := TDataSource.Create(FormData);

  Table1 := TTable.Create(FormData);
  Table1.DatabaseName := 'dBase Files';
  Table1.TableType := ttdBase;
  Table1.FieldDefs.Clear;
    Table1.FieldDefs.Add('ITEM', ftInteger, 0, True);
    Table1.FieldDefs.Add('QTY', ftInteger, 0, True);
  Table1.TableName := 'BOMdata1';
  Table1.CachedUpdates := True;
  Table1.CreateTable;
  DataSource1.DataSet := Table1;
  DataSource1.AutoEdit := True;
  DataSource1.Enabled := True;

  DBGrid1.DataSource := DataSource1;
    DBGrid1.Columns.Add;
    DBGrid1.Columns[0].Field := Table1.FieldByName('ITEM');
    DBGrid1.Columns.Add;
    DBGrid1.Columns[1].Field := Table1.FieldByName('QTY');
  DBNavigator1.DataSource := DataSource1;
  DBGrid1.Enabled := True;

    Table1.Active := False;
    Table1.EmptyTable;
    Table1.Active := True;
        Table1.AppendRecord([CompName, CompType, CompDecal, PartNum, 
PartCost,
          PartDescr, PartValue, PartTolerance, PartPower, PartVoltage, 
PartCurrent,
          PartMfr1, PartMfrNum, AttrCount, AttrList]);
    Table1.Refresh; // Remove duplicates

This seems to work, but I was unable to save the database. I probably 
needed to add:

  dbTest1.Open;
  dbTest1.StartTransaction;
  dbTest1.Commit;
  dbTest1.Close;

Every time I think I understand it, something seems to go wrong. When I 
tried to use the Microsoft Access driver I got a log-on dialog and it would 
not accept blanks. I checked the driver setups and I changed the user name 
and password but it still would not accept them and gave errors. Either I 
am doing something very stupid or there is something wrong with the Delphi 
implementation. Maybe a combination of both.

If you can shed some light on any errors in the above code and fix it so I 
can just create or open this simple file and use the dbGrid and navigator, 
and save the file, that will probably be enough. But I really hope to 
understand why I get the severe errors I do. Perhaps there is a patch for 
D4 for that I don't have? There should be a stupid simple demo project that 
can be used to determine if there is a problem with the BDE or some of the 
database functions.

Ugh, another all-nighter...Thanks!

Paul




0
Reply Paul 11/1/2009 11:43:03 AM

I just tried to add a dbGrid component, and when I tried to use the object 
inspector to set the field names, the whole IDE crashed instantly. When I 
tried again, I got the SQL cursor forever until I ended the program. 
Something is very wrong....

Paul 


0
Reply Paul 11/1/2009 12:03:54 PM

"Paul E. Schoen" <paul@peschoen.com> wrote in message 
news:XPeHm.3884$Mg.2368@newsfe01.iad...
>I just tried to add a dbGrid component, and when I tried to use the object 
>inspector to set the field names, the whole IDE crashed instantly. When I 
>tried again, I got the SQL cursor forever until I ended the program. 
>Something is very wrong....
>
> Paul

I'm going to try the update packs #2 and #3 from Torry:
http://www.torry.net/pages.php?id=63

Here is a list of known issues and fixes:
http://info.borland.com/devsupport/delphi/fixes/delphi4/

Hopefully these will fix my problems. Perhaps I should also reinstall or 
repair Delphi from my original disk first. And hopefully these are genuine 
fixes and not something malicious. If anyone knows of other patches or if 
the above are OK then please let me know.

Thanks!

Paul 


0
Reply Paul 11/2/2009 6:30:54 PM

"Paul E. Schoen" <paul@peschoen.com> wrote in message 
news:pweHm.3882$Mg.1509@newsfe01.iad...
>I have just made a Delphi project to try and understand how to create and 
>use the database controls and objects. I was (finally) able to create a 
>dbTest.DBF file and insert one record of data. But I ran into problems when 
>the file existed and I tried to open it. Here is my code:
>
> procedure TForm1.btCreateClick(Sender: TObject);
> var DBIresult: Word;
> begin
>  if DataSource1 = nil then
>    DataSource1 := TDataSource.Create(self);
>  if Table1 = nil then
>    Table1 := TTable.Create(self);
>  if FileExists('dbTest.dbf') then begin
>    if not Table1.CheckOpen(DBIresult) then                //This gives me 
> a BDE exception

No surprise here. You create a tTable component but you have yet to assign 
it a Session, Database, or Tablename. It's no wonder that you cannot open 
the table.

If you have created a physical table why not keep the programming simple and 
drop a tTable component onto a form. Set the required properties 
appropriately and away you go for testing. For more versitile work, check 
out the BeforeOpen event.

0
Reply BRoberts 11/3/2009 1:00:48 AM

"BRoberts" <berdontemail@caneris.ca> wrote in message 
news:88807$4aef80d5$45c49ff1$24925@TEKSAVVY.COM-Free...
> "Paul E. Schoen" <paul@peschoen.com> wrote in message 
> news:pweHm.3882$Mg.1509@newsfe01.iad...
>>I have just made a Delphi project to try and understand how to create and 
>>use the database controls and objects. I was (finally) able to create a 
>>dbTest.DBF file and insert one record of data. But I ran into problems 
>>when the file existed and I tried to open it. Here is my code:
>>
>> procedure TForm1.btCreateClick(Sender: TObject);
>> var DBIresult: Word;
>> begin
>>  if DataSource1 = nil then
>>    DataSource1 := TDataSource.Create(self);
>>  if Table1 = nil then
>>    Table1 := TTable.Create(self);
>>  if FileExists('dbTest.dbf') then begin
>>    if not Table1.CheckOpen(DBIresult) then                //This gives 
>> me a BDE exception
>
> No surprise here. You create a tTable component but you have yet to 
> assign it a Session, Database, or Tablename. It's no wonder that you 
> cannot open the table.
>
> If you have created a physical table why not keep the programming simple 
> and drop a tTable component onto a form. Set the required properties 
> appropriately and away you go for testing. For more versitile work, check 
> out the BeforeOpen event.

I tried dropping a TTable component on the form and it still gave errors. 
And I had better luck previously (I think) by creating and setting up 
various database components dynamically at runtime. I've had nothing but 
problems, and now I think it must be because of a broken portion of the 
Delphi tool itself, or the BDE.

I am also confused about such things as the TDatabase.name and 
TDatabase.DatabaseName properties. And TTable.Name and TTable.TableName. It 
seems like the TableName is the filename that I am creating or wish to 
open.

And apparently when I reset the application after it hung up, the session 
remained active such that I could not set properties in the object 
inspector. So maybe there are threads running in the background even when I 
terminate Delphi, and I may have to reboot each time until I get it right. 
I probably had it right several times but the previous errors caused errors 
that would not have occurred if there had been no errors to begin with. 
Maybe that's the whole problem.

Thanks for the input, but I still don't know just what to do.

Paul 


0
Reply Paul 11/3/2009 6:07:50 AM

"Paul E. Schoen" wrote:
> 
> Thanks for the input, but I still don't know just what to do.
> 
http://101.lv/learn/delphi/ch16.htm
http://portal.aauj.edu/portal_resources/downloads/database/delphi_database_application_developers_book.pdf
http://delphi.about.com/od/database/a/databasecourse.htm
http://en.allexperts.com/q/Delphi-1595/become-professional-delphi-database.htm
http://www.ayton.id.au/gary/it/Delphi/D_db1.htm
http://www.pdf-search-engine.com/delphi-database-programming-pdf.html
http://www.freeprogrammingresources.com/delphi.html

Also, slightly adjust the thread's topic. Acting with databases, reports
etc., and turning them to a usable GUI-application never tend to be too
simple. Learning curve is not only a urban legend, it exists.
-AP
0
Reply AP 11/3/2009 9:16:03 AM

"AP" <ap.newsonly@nomails.please.com> wrote in message 
news:4AEFF4D3.D33BAD61@nomails.please.com...
> "Paul E. Schoen" wrote:
>>
>> Thanks for the input, but I still don't know just what to do.
>>
> http://101.lv/learn/delphi/ch16.htm
> http://portal.aauj.edu/portal_resources/downloads/database/delphi_database_application_developers_book.pdf
> http://delphi.about.com/od/database/a/databasecourse.htm
> http://en.allexperts.com/q/Delphi-1595/become-professional-delphi-database.htm
> http://www.ayton.id.au/gary/it/Delphi/D_db1.htm
> http://www.pdf-search-engine.com/delphi-database-programming-pdf.html
> http://www.freeprogrammingresources.com/delphi.html
>
> Also, slightly adjust the thread's topic. Acting with databases, reports
> etc., and turning them to a usable GUI-application never tend to be too
> simple. Learning curve is not only a urban legend, it exists.

I realize that database programming is not simple, but I have done it with 
dBase III+ (using Clipper) and also with MS Access (using VBA). I admit 
that there are many advanced concepts that I have not mastered or even have 
usable competency, but I should be able to create, open, edit, and save a 
simple table.

I tried the Chapter 16 of the first link and I found that I could not 
select the DBDEMOS alias. When I type it in I get an error. I located the 
demo database files in C:\Program Files\Common Files\Borland Shared\Data, 
but there is no file or folder called DBDEMOS. The Data folder has 
Animals.dbf and Clients.dbf. I was able to open Animals.dbf using the 
Database Desktop but I was unable to find them or any other DBF files with 
the Database Explorer even when I copied them into the working project 
directory. When I selected Text Files it found files with the .TXT 
extension.

I was able to find them and examine the fields when I closed and restarted 
Delphi, although it gave a warning about a Production Index Missing and I 
could open them only as Read Only. Then I copied the .MDX files over and I 
was able to use the Explorer.

I dropped a TTable, TDatabase, and TDataSource onto the form, and using the 
Object Inspector I was able to select one of the DBF files As the 
Table1.TableName. But it did not read the FieldDefs until I set the 
Table1.Active True. I dropped a TDBGrid and set the DataSource to 
DataSource1 and it showed the fields in the grid. I changed the 
Table1.TableName to another existing DBF file and when I made it active 
again it used the new table fields and records. And I added a TDBNavigator 
set the source, and ran the App. I was able to edit the field data and add 
a record. I also added the Table1.IndexName so that the added record would 
show in the Object inspector.

So now this appears to work. I did install the updates #2 and #3, and 
perhaps that helped.

OK. Thanks for your patience. I think I can take it from here.

I'll have to see if I can also create a new database at runtime rather than 
use one that already exists, although I will probably distribute my 
database files with my application. At this point I am OK with using dBase, 
as I do not appear to be able to use others in the dropdown list because I 
can't get past the login prompt.

Speaking of Login Prompt, I could not get the application to run without 
displaying the login prompt dialog, even though I set the 
DataBase1.LoginPrompt False. But I removed the DatabaseName from both the 
Table1 and Database1 components and now the app no longer prompts for 
login. IOn fact I was able to delete the TDatabase component and everything 
still seems to work. So here are the properties I used for the SimpleDB 
project (there is no code yet):

DataSource1.DataSet := DataSet1;
Table1.Active := True;
Table1.DatabaseName := '';
Table1.DefaultIndex := True;
Table1.IndexName := INDEX_65BC;    //Selected from list
Table1.TableName := 'BOMdata1';    //Selected from list of DBF files
Table1.TableType := ttDBase;

This may be stupid simple and obvious but at least I now have a starting 
point from which I can build. I'd like to use MS Access but that's not 
going to happen unless I get a later version of Delphi or do some of the 
tricks I've seen that might expose the elements of MSACC8.OLB or MSACC.OLB. 
But that is hardly trivial. I can live with a DBF database and I can import 
and export the format using MS Access 97.

Thanks,

Paul







0
Reply Paul 11/3/2009 8:03:33 PM

"Paul E. Schoen" wrote:
> 
> 
> I tried the Chapter 16 of the first link and I found that I could not
> select the DBDEMOS alias. 

Many of you earlier error messages and the overall diffculties even with
basic things just sound so strange. It should not be that difficult to
do start with the beginner DB demos there are descibed in manuals.
Now we at least know so much that you have some written example or
manual to follow.

The problem could be with your oldish Delphi version or maybe with the
operating system (Vista???) and some overly protected Program Files
folders. 
But I have even older version, D3, installed on my machine. And all the
demos that came with this Delphi Professional version work just fine on
XP.

Aliases. You can manage existing BDE Aliases and add new ones with
BdeAdmin.exe that you shoud be able to locate.

> When I type it in I get an error. I located the
> demo database files in C:\Program Files\Common Files\Borland Shared\Data,
> but there is no file or folder called DBDEMOS. 

DBDEMOS is not a folder, it's an Alias for BDE, and you can view them
with the tool mentioned earlier.

> OK. Thanks for your patience. I think I can take it from here.

Don't worry, we are only trying to save your time and maybe ours at the
same time:) Of course my approach may be killing this kind of live and
lenghy thread. With lot's of code snippets and other programming related
conversation. 

But on the other hand, if you want quickly to get something to work
also, and do not want to paint youself in to a corner or dead end on a
slightly longer period.
  
> I'll have to see if I can also create a new database at runtime rather than
> use one that already exists, although I will probably distribute my
> database files with my application. At this point I am OK with using dBase,
> as I do not appear to be able to use others in the dropdown list because I
> can't get past the login prompt.

Use Database Desktop to create all your tables, then just distribute
those tables with your installation. There's rarely need to start
creating tables from total nil within your application code.

> Speaking of Login Prompt, I could not get the application to run without
> displaying the login prompt dialog, even though I set the
> DataBase1.LoginPrompt False. 

I wonder if it is really worth to learn to use and deal with BDE, and
aliases ans everything such any more. I understood that you want to have
an easy applcaton, with easy installation. BDE always needs to be
installed to the target machines, and there are constant problems with
BDE NetDir errors etc.

If you have chosen that dBase will be your data format and D4 is the
version where you will stay, then mayne Halcyon with $100 would be a
suitable BDE replacement http://www.griffinsolutions.com/halcyon6.php

On the other hand, if you can get D5 or better, then your can install
free NexusDB version. that is available also to D5 and upwards.
http://www.torry.net/quicksearchd.php?String=nexus&Title=Yes
http://www.nexusdb.com/support/index.php?q=FreeEmbedded

These do NOT use dBase file format, but that should not be a problem. I
abandoned all dBase files already over decade ago.

> This may be stupid simple and obvious but at least I now have a starting
> point from which I can build. I'd like to use MS Access but that's not
> going to happen unless I get a later version of Delphi or do some of the
> tricks I've seen that might expose the elements of MSACC8.OLB or MSACC.OLB.
> But that is hardly trivial. I can live with a DBF database and I can import
> and export the format using MS Access 97.

You should be able to install full DAO data access to your D4 version
for free, and that way use also Access databases.
http://www.torry.net/pages.php?id=569#5157
Yet those DAO things are mostly grey area to me, and I do not know if
all those work or not.

It may well be that you can build your DB app totally without upgrading
anything or buying anything. There is a wide selecton of free code and
alternatives, and you may be lucky.

But If you do have something like $100 to spend, it also may be that
spending now that money wisely to right upgrades may save you from a lot
of troubles and questions in the future.
-AP
0
Reply AP 11/4/2009 11:26:59 AM

20 Replies
337 Views

(page loaded in 0.716 seconds)

Similiar Articles:


















7/28/2012 11:38:54 AM


Reply: