Forum > General

failed Memory update of a class object

(1/1)

mas steindorff:
I solved the following issue in my main code with workaround but I was wondering what I did wrong or if someone else has see this error?

I created a class object that basically reads a binary data from a "config file" and decodes it.  Within this class, I have a packed record that describes the memory layout of the various binary variables. I then created a ReadFromFile method that works just fine except I need to call it twice in order to make the new values "stick".  Otherwise the various object properties returns the previous values.  The object's properties read and write using procedures and functions that translate directly from the packed record memory and there is only one of these records.  There is only one instance of this config file reader object accessibly to the form but there are other instances on other forms within the program.  
When I place a breakpoint in the ReadFromFile code, I see the binary variables update but after the code returns to the form, the calls to the properties fetch old values!
Now the weird part: a second call to ReadFromFile right after the first one has no affect.  if I call a function that gets the value from all of the properties and then call the ReadFromFile, the next call to the get values get the correct values! (that is my fix)  All of my other code to set values, SaveToFile,  work just fine.
Ideas?

Marc:
Please provide some (compilable) sample code

mas steindorff:
OK, first the form code. I've included just my modifications to the automatically generated code.  this code expects the form to have a read button and 3 Tedit boxes not included in an attempt to keep this post for getting too big.

--- Code: ---...
  Public  { Public declarations }
     ConfigObj:TConfigObj;   // as part of the form
end;

Procedure TFrmConfig.FormCreate(Sender: TObject);
Begin
  ...
   ConfigObj := TConfigObj.Create;
end;

Procedure TFrmConfig.FormDestroy(Sender: TObject);
Begin
   ConfigObj.Free;
end; 

Procedure TFrmConfig.BtnReadSDClick(Sender: TObject);
var filename:string;
Begin
   filename := GetConfigFileName(SD_Device);  // "F:Config.dat
   // .. more file testing code removed
   StatusMessage2('reading...'+filename);
   if ConfigObj.ReadFromFile(filename) then
      begin
      PramToEdit();
      ConfigObj.ReadFromFile(filename);   // for some reason I need to do a display thing before the read takes hold
      PramToEdit();  // this one works
      end;
end;

procedure GetParam(var box:TEdit; AValue:double);
begin
  box.Text := format(%.2f',[Avalue]);
end;

procedure TFrmconfig.PramToEdit();
begin
  GetParam(ed_AcqTime,     ConfigObj.uiAcqTime);
  GetParam(ed_ProcTime,    ConfigObj.uiProcTime);
  GetParam(ed_AmpExcReg, ConfigObj.uiAmpDiffExReg);
end;


--- End code ---

========= The configobj is in another unit.  I'v reduced the number of parameters down to 3 (1 float and 2 words) but the others follow the same format


--- Code: ---type
  F32 = single;
  U16 = word;   
  U8 = byte;

  TConfigParmTable = packed record  // entree def
     prarmCode  :U8;
     byteoffset :U16;  // 0 to 511 (where to find parameters)
     size       :U16;
     end;

  TConfigFilePrams = packed record
     bidsmode  :U8;   
     PrarmCount:U8;
     // first table starts after these bytes
     PrarmTable:TConfigParmTable;     end;

  TConfigFile = record case integer of
     0:(Pram:TConfigFilePrams);
     1:(raw:array[1..(8*1024)] of U8);
     end;

 TConfigDAQType = packed record // def DAQ parameters
     AcqPts           :U16;   // rng=[10..500]
     ProcPts          :U16;   // rng=[10..500]
     AmpDiffExReg  :F32;  // rng=[0.0 .. 1.0]
     end;

 TConfigObj = class
      my : record
        Tag        :integer;
        CardType   :U8;
        DaqParam   :TConfigDAQType;   // Raw vars live here
        fileOK      :Boolean;
        ConfigFile :TConfigFile;   
        FileName   :string;
        end; // of record
   private { private declarations }
      procedure DecodeConfigTable(Index: Integer);
      Procedure SetU16Pts(value: double; Var saveto: U16; min,max:U16);
      procedure Showstr(Str: String);
   public  { public declarations }
     Constructor Create;
     property Tag     :integer   read my.tag          Write my.tag;
     Procedure SetAmpDiffExReg  (Const AValue: F32);
     Function  UIGet_ProcTime  : double;
     Procedure UISet_ProcTime (Const AValue: double);
     Function  UIGet_AcqTime   : double;
     Procedure UISet_AcqTime   (Const AValue: double);
     ...
     property uiAcqTime :double Read UIGet_AcqTime  Write UISet_AcqTime;
     property uiProcTime:double Read UIGet_ProcTime Write UISet_ProcTime;
     property uiAmpDiffExReg     :F32 Read my.DaqParam.AmpDiffExReg    Write SetAmpDiffExReg;
     function ReadFromFile(filename:string):Boolean;                                     
  End;

implementation 
 const
    sampfreq    = 50000.0;   

Constructor TConfigObj.Create;
Begin
   Inherited Create;
   with my do begin
     DaqParam.AcqPts  := 500;
     DaqParam.ProcPts := 500;
     DaqParam.AmpDiffExReg     := 0.00;
   end;
end;

Procedure TConfigObj.SetU16Pts(value:double; var saveto:U16; min,max:U16);
begin
  if (value < min) then      saveto := min
  else if (value > max) then saveto := max
  else                       saveto := round(value);
End;

Function TConfigObj.UIGet_AcqTime: double;  // checked
Begin
    result := my.DaqParam.AcqPts *1000.0/sampfreq;
End;

Procedure TConfigObj.UISet_AcqTime(Const AValue: double); // checked
var f:double;
Begin
    f := trunc(round(AValue*sampfreq/1000.0));
     SetU16Pts(f, my.DaqParam.AcqPts,    10, 500);   
end;

Function TConfigObj.UIGet_ProcTime: double;            // checked
Begin
      result := my.DaqParam.ProcPts *1000/sampfreq;
End;

Procedure TConfigObj.UISet_ProcTime(Const AValue: double);  // checked
var  f:double;
Begin
     f := AValue * sampfreq/1000;
     SetU16Pts(f, my.DaqParam.ProcPts , 10, 500);
End;

Procedure TConfigObj.SetAmpDiffExReg(Const AValue: F32);   
Begin
   if (Avalue < 0.0) then      my.DaqParam.AmpDiffExReg:=0.0
   else if (Avalue > 1.0) then my.DaqParam.AmpDiffExReg:=1.0
   else                        my.DaqParam.AmpDiffExReg:=Avalue;
end;

function Tconfigobj.Readfromfile(filename: String): Boolean;
 var fhandle:THandle;
     i:integer;
begin
  result := FALSE;
  my.fileOK:=FALSE;
  try
    fhandle:=FileOpen(fileName, fmOpenRead+ fmShareDenyNone);
    i := FileRead( Fhandle, my.ConfigFile, sizeof(TConfigFile));
    if (i < sizeof(my.ConfigFile)) then
       exit;
    my.fileOK   :=TRUE;
    my.FileName :=filename; // save just incase I need it
    my.CardType := my.ConfigFile.Pram.mode;

    for i:=1 to my.ConfigFile.Pram.PrarmCount do begin
       DecodeConfigTable(i);
       end;
    result := TRUE; // good read !! A breakpoint here shows my.DaqParam are equal to the file contents
  finally
    FileClose(fhandle);
  end;
end;

procedure Tconfigobj.BinGetDAQ_params(tbl:TConfigParmTable);
var p:^TConfigDAQType;
begin
  p := @my.ConfigFile.raw[tbl.byteoffset+1];
//  move(p^, my.DaqParam, sizeof(my.DaqParam)); 
  self.my.DaqParam := p^;     // and that all that it takes to read all 32 bytes
end;

procedure Tconfigobj.DecodeConfigTable(index:integer);
var tbl:^TConfigParmTable;
begin
   tbl := @my.ConfigFile.Pram.PrarmTable;
   inc(tbl, index-1);
   case tbl^.prarmCode of
    DAQ_param    : BinGetDAQ_params(tbl^);   // TConfigDAQType
    ....
    else ShowStr('  *** unknown type, no decoder yet ***');
    end;
end;

--- End code ---

thank you for looking.
mas

Navigation

[0] Message Index

Go to full version