unit KYasPar;
{
   Parameters for Yaskawa inverter

   Author:  Vesa Lappalainen
   Date:    31.12.1996
   Changes: 05.04.1997
              + reset ottaa kaikki RunParametrin biti pois päältä aluksi
              (jos run-päällä tai Ext.error, ei muuten resetoi).
   Changes: 23.07.2001
     + modbusslave not a pointer any more (no use)
   Changes: 06.10.2001
     + StoreRamRegister + EEPROM
   Changes: 26.12.2003
     + SendRunParams ja RefreshRunParams käsittely ei peräkkäisille rekistereille.


  One example hierarcy
  =====================
                  0-n
     TParamValue -----<> TParamList                                  IniPara.pas
          |                  ^
          |           1      |                                      ------------
          |         |----TKParamList
          |         |
          ^         |    TComponent
          |         |        ^
          |         ---<>TParams                                     KParams.pas
          |                  ^                                      ------------
          |                  |
     YasParamValue       TYasParams                                  KYasPar.pas
           *
           |
TModBus ---
}

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  IniPara,KModbus,kparams;

const NotInUseSlave = -33;

type
  TYasParamValue = class(TParamValue)
  private
    ModBusSlave:TModBusSlave;
    StoreRamRegister : integer;
    StoreEEPROMRegister : integer;
    StoreEEPROMSequence : string;

  protected
  public
    function WriteFunction(d: double;noread:boolean): string; override;
    function ReadFunction: string; override;
    procedure StoreEEPROM; override;
    procedure StoreRAM; override;
  published
  end;

type
  TYasParams = class(TParams)
  private
    FYasForm : TForm;
    FModbusSlave:TModBusSlave;
    thz : Double;  // Temp for demos, where no modbus active

    FDirection : integer;
    FRun       : boolean;

    RunParam,
    DirectionParam,
    ResetParam,
    HzParam : TParamValue;

    FNameRun,
    FNameDirection,
    FNameReset,
    FNameHz,
    FNameMaxHz, //   = 'n012';
    FNameMinHz, //   = 'n017';
    FNameAccTime, // = 'n019';
    FNameDecTime, // = 'n020';
    FNameInvStatus : string; // = 'r044';
    FcInvFault : integer;
    FNameOutRead: string;
    FNameInRead: string;
    FNameOutWrite: string;
    FNameReadV: string;
    FNameFreqRef: string;
    FNameMaxV: string;
    FNameReadHz: string;
    FNameInWrite: string;
    FNameReadA: string;
    FNameMaxA: string;
    FcResetBitValue: integer;
    FcInCount: integer;
    FcInWriteStart: integer;
    FcOutReadShift: integer;
    FcOutCount: integer;
    FcOutWriteShift: integer;
    FcInReadShift: integer;
    FcInWriteShift: integer;
    FInputNames: string;
    FOutputNames: string;
    FcStoreRamRegister: integer;
    FcStoreEEPROMRegister: integer;
    FStoreEEPROMSequence : string;
    FcForwardRun: integer;
    FcReverseRun: integer; // = $4000;
    FcRunBit : integer;
    FcDirectionBit : integer;
    FcRunMask : integer;
    FcDirectionMask : integer;
    function GetEnabled: boolean;
    procedure SetEnabled(const Value: boolean);
    function ReadNames: boolean;

  protected
    procedure SetDirection(d:integer); virtual;
    function GetDirection:integer; virtual;
    function FindParams:boolean; virtual;
    procedure RefreshRunParams; virtual;
    procedure SendRunParams; virtual;

    function GetSlave:integer;  virtual;
    procedure SetSlave(s:integer); virtual;
    function GetModbus : TModbus;
    procedure SetModbus(mb:TModbus);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function CreateList: string; override;
    procedure WriteHz(hz:double); virtual;
    procedure WriteRunHz(hz:double); virtual;
    function ReadHz:double; virtual;
    function ReadMaxHz:double; virtual;
    function ReadMinHz:double; virtual;
        function WriteReadHz(hz:double):double; virtual;
    function AccAHz:double; virtual;
    function DecAHz:double; virtual;
    function InvOK : boolean; virtual;
    procedure Stop; virtual;
    function Reset : boolean; virtual;
    function NewParam(const name:string):TParamValue; override;
    procedure DoShowControl(modal:boolean);
    procedure ShowControl; virtual;
    procedure ShowControlModal; virtual;
    procedure ShowErrors; virtual;
    function GetLastError:string; virtual;
    procedure StoreEEPROM; override;
    procedure StoreRAM; override;
  published
    property ModbusSlave:TModBusSlave read FModbusSlave;
    property Modbus:TModBus read GetModBus write SetModbus;
    property Direction:integer read GetDirection write SetDirection;
    property Slave : integer read GetSlave write SetSlave;
    property Enabled : boolean read GetEnabled write SetEnabled;
//    property LastError : string; read GetLastError;
    property NameRun:string read FNameRun;
    property NameReset:string read FNameReset;
    property NameDirection:string read FNameDirection;
    property NameHz:string read FNameHz;
    property NameMaxHz:string read FNameMaxHz;
    property NameMinHz:string read FNameMinHz;
    property NameAccTime:string read FNameAccTime;
    property NameDecTime:string read FNameDecTime;
    property NameInvStatus:string read FNameInvStatus;
    property NameInRead:string read FNameInRead;
    property NameOutRead:string read FNameOutRead;
    property NameOutWrite:string read FNameOutWrite;
    property NameInWrite:string read FNameInWrite;
    property NameReadHz:string read FNameReadHz;
    property NameMaxA:string read FNameMaxA;
    property NameReadA:string read FNameReadA;
    property NameMaxV:string read FNameMaxV;
    property NameReadV:string read FNameReadV;
    property NameFreqRef:string read FNameFreqRef;
    property cInvFault:integer read FcInvFault;
    property ResetBitValue:integer read FcResetBitValue;
    property InCount:integer read FcInCount;
    property OutCount:integer read FcOutCount;
    property InReadShift:integer read FcInReadShift;
    property InWriteShift:integer read FcInWriteShift;
    property InWriteStart:integer read FcInWriteStart;
    property OutReadShift:integer read FcOutReadShift;
    property OutWriteShift:integer read FcOutWriteShift;
    property InputNames : string read FInputNames;
    property OutputNames : string read FOutputNames;
    property StoreRamRegister : integer read FcStoreRamRegister;
    property StoreEEPROMRegister : integer read FcStoreEEPROMRegister;
    property StoreEEPROMSequence : string read FStoreEEPROMSequence;
    property cForwardRun : integer read FcForwardRun;
    property cReverseRun : integer read FcReverseRun;
    property cRunBit : integer read FcRunBit;
    property cDirectionBit : integer read FcDirectionBit;
    property cRunMask : integer read FcRunMask;
    property cDirectionMask : integer read FcDirectionMask;
  end;

procedure Register;

implementation

uses YasForm,inifiles,kErrors;
var
  ceYasParamDebug : TCommError;


procedure TYasParamValue.StoreEEPROM;
begin
  if ( StoreEEPROMRegister <> 0 ) then begin
    ModbusSlave.SetReg(StoreEEPROMRegister,0);
    ceYasParamDebug.Add('Yaskawa: Write to EEPROM - '+Name+'='+AsString);
  end;

end;

procedure TYasParamValue.StoreRAM;
begin
  if ( StoreRAMRegister <> 0 ) then ModbusSlave.SetReg(StoreRAMRegister,0);
end;


{------------------------------------------------------------------------------}
{ Write value to the fysical device if one exist and change value to d         }
{ if success                                                                   }
{ Return error message as text.                                                }
function TYasParamValue.WriteFunction(d: double;noread:boolean): string;
var i:integer; oldvalue : double; err:string;
begin
  oldvalue := value;
  value := d;
  Result := 'No slave';
  if ( not ModbusSlave.ok ) then exit;
  ModbusSlave.SetReg(Ind,AsInteger);
  Result := ModbusSlave.LastError;
  if ( noread ) then exit;
  if ( ModbusSlave.ReadReg(Ind,i) ) then begin
    AsInteger := i;
    StoreRam;
  end
  else begin
    err := ModbusSlave.LastError;
    value := oldvalue;
    if ( Result = '' ) and ( err <> '' ) then Result := err;
  end;  
end;

{------------------------------------------------------------------------------}
{ Read value from fysical device and change the value if success               }
{ Return error message as text.                                                }
function TYasParamValue.ReadFunction: string;
var i:integer;
begin
  Result := 'No slave';
  if ( not ModbusSlave.ok ) then exit;
  if ( ModbusSlave.ReadReg(Ind,i) ) then begin
    AsInteger := i;
    Result := '';
  end
  else
    Result := ModbusSlave.LastError;
end;

{------------------------------------------------------------------------------}
{ Overdriven virtual to create right params for this Params-list               }
function TYasParams.NewParam(const name:string):TParamValue;
var p : TYasParamValue;
begin
  p := TYasParamValue.Create;
  p.ModbusSlave := ModbusSlave;
  Result := p;
  if ( Name <> '' ) and ( UpperCase(Name[1]) <> 'R' ) then begin
    p.StoreRamRegister := FcStoreRamRegister;
    p.StoreEEPROMRegister := FcStoreEEPROMRegister;
    p.StoreEEPROMSequence := FStoreEEPROMSequence;
  end;
end;

{------------------------------------------------------------------------------}
{ TYasParam -------------------------------------------------------------------}
{------------------------------------------------------------------------------}

{------------------------------------------------------------------------------}
function TYasParams.GetModbus : TModbus;
begin
  Result := ModbusSlave.Modbus;
end;

{------------------------------------------------------------------------------}
procedure TYasParams.SetModbus(mb:TModbus);
begin
  ModbusSlave.Modbus := mb;
end;

{------------------------------------------------------------------------------}
procedure TYasParams.SendRunParams;
var r,ihz,dir : integer;
begin
  if ( not ModbusSlave.ok ) then exit;
  r   := RunParam.AsInteger;
  ihz := abs(HzParam.AsInteger);
  if ( RunParam.Ind+1 = HzParam.Ind ) and ( RunParam.Ind = DirectionParam.Ind ) then begin
    // Use single write, if Hz is immediately after run.
    ModbusSlave.SetReg2(RunParam.Ind,r,ihz);
  end else begin
    if ( RunParam.Ind <> DirectionParam.Ind ) then begin
      dir := DirectionParam.AsInteger;
      ModbusSlave.SetReg(DirectionParam.Ind,dir);
    end;
    ModbusSlave.SetReg(HzParam.Ind,ihz);
    ModbusSlave.SetReg(RunParam.Ind,r);
  end;
end;

{------------------------------------------------------------------------------}
procedure TYasParams.RefreshRunParams;
var r,ihz : integer;
begin
  if ( not ModbusSlave.ok ) then exit;
  if ( RunParam.Ind+1 = HzParam.Ind ) then begin
    // Use single write, if Hz is immediately after run.
    if ( ModbusSlave.ReadReg2(RunParam.Ind,r,ihz) ) then begin
      RunParam.AsInteger := r;
      HzParam.AsInteger := ihz;
    end;
  end else begin
    if ( ModbusSlave.ReadReg(RunParam.Ind,r) ) then RunParam.AsInteger := r;
    if ( ModbusSlave.ReadReg(HzParam.Ind,ihz) ) then HzParam.AsInteger := ihz;
  end
end;

{------------------------------------------------------------------------------}
function TYasParams.FindParams:boolean;
begin
  RunParam       := FindParam(NameRun);
  ResetParam     := FindParam(NameReset);
  DirectionParam := FindParam(NameDirection);
  HzParam        := FindParam(NameHz);
  Result := (RunParam<>NIL) and (HzParam<>NIL);
end;

{------------------------------------------------------------------------------}
procedure TYasParams.SetDirection(d:integer);
begin
  Fdirection := d;
  if ( Frun ) then begin
    RunParam.SetBitMask(cRunBit,1,cRunMask);
    if Fdirection < 0 then begin
      DirectionParam.SetBitMask(cDirectionBit,FcReverseRun,cDirectionMask)
    end else begin
      DirectionParam.SetBitMask(cDirectionBit,FcForwardRun,cDirectionMask)
    end
  end else begin
      RunParam.SetBitMask(cRunBit,0,cRunMask);
  end
end;

{------------------------------------------------------------------------------}
function TYasParams.GetDirection:integer;
begin
//  Result := 1;
//  if ( RunParam.GetBit(1) )  then Result := -1;
  Result := fDirection;
end;

{------------------------------------------------------------------------------}
function TYasParams.ReadHz:double;
var hz:double;
begin
  Result := thz;
  if ( not ModbusSlave.Ok ) then exit;
  RefreshRunParams;
  hz := HzParam.Value;
  if ( direction = -1 ) then Hz := -Hz;
  Result := Hz;
  thz := hz;
end;

{------------------------------------------------------------------------------}
function TYasParams.ReadMaxHz:double;
begin
  if ( not ModbusSlave.Ok ) then Result := 60
  else Result := ReadValueByName(NameMaxHz);
end;

{------------------------------------------------------------------------------}
function TYasParams.ReadMinHz:double;
begin
  if ( not ModbusSlave.Ok ) then Result := 0
  else Result := ReadValueByName(NameMinHz);
end;

{------------------------------------------------------------------------------}
procedure TYasParams.WriteHz(hz:double);
begin
  thz := hz;
  if ( hz < 0 ) then direction := -1 else direction := 1;
  HzParam.Value := abs(hz);
  SendRunParams;
end;

{------------------------------------------------------------------------------}
procedure TYasParams.WriteRunHz(hz:double);
begin
  FRun := abs(hz)>0.01;
  WriteHz(hz);
end;

{------------------------------------------------------------------------------}
function TYasParams.WriteReadHz(hz:double):double;
begin
  WriteRunHz(hz);
  Result := ReadHz;
end;

{------------------------------------------------------------------------------}
function TYasParams.AccAHz:double;
var t : double;
begin
  t := ReadValueByName(NameAccTime);
  if ( t = 0 ) then t := 1;
  Result := ReadMaxHz/t;
end;

{------------------------------------------------------------------------------}
function TYasParams.DecAHz:double;
var t : double;
begin
  t := ReadValueByName(NameDecTime);
  if ( t = 0 ) then t := 1;
  Result := ReadMaxHz/t;
end;

{------------------------------------------------------------------------------}
procedure TYasParams.Stop;
begin
  FRun := false;
  RunParam.WriteBitMask(cRunBit,0,cRunMask);
end;

{------------------------------------------------------------------------------}
function TYasParams.Reset : boolean;
begin
  Result := true;
  FRun := false;
  if ( not ModbusSlave.OK ) then exit;
  Result := false;
  RunParam.WriteValue(0);
  if ( ModbusSlave.LastError <> '' ) then exit;
//  RunParam.WriteBit(3,true);
  ResetParam.WriteBitMask(0,ResetBitValue,$ffff);
  if ( ModbusSlave.LastError <> '' ) then exit;
  ResetParam.WriteValue(0);
  RunParam.WriteValue(0);
//  RunParam.WriteBitMask(0,0,$ffff);
//  RunParam.WriteBit(3,false);
  Result := ModbusSlave.LastError = '';
end;

{------------------------------------------------------------------------------}
function TYasParams.ReadNames : boolean;
var ini : TIniFile;
begin
  ini := nil;
  Result := true;
  try
    ini := TIniFile.Create(TypeIniName);
    FNameRun       := ini.ReadString('Names','NameRun','r001');
    FNameReset     := ini.ReadString('Names','NameReset',FNameRun);
    FNameDirection := ini.ReadString('Names','NameDirection',FNameRun);

    FNameHz        := ini.ReadString('Names','NameHz','r002');
    FNameMaxHz     := ini.ReadString('Names','NameMaxHz','n012');
    FNameMinHz     := ini.ReadString('Names','NameMinHz','n017');
    FNameAccTime   := ini.ReadString('Names','NameAccTime','n019');
    FNameDecTime   := ini.ReadString('Names','NameDecTime','n020');
    FNameOutWrite  := ini.ReadString('Names','NameOutWrite','r009');
    FNameOutRead   := ini.ReadString('Names','NameOutRead','r032');
    FNameInRead    := ini.ReadString('Names','NameInRead','r043');
    FNameInWrite   := ini.ReadString('Names','NameInWrite','r001');
    FNameInvStatus := ini.ReadString('Names','NameInvStatus','r044');
    FNameReadHz    := ini.ReadString('Names','NameReadHz','r036');
    FNameMaxA      := ini.ReadString('Names','NameMaxA','n032');
    FNameReadA     := ini.ReadString('Names','NameReadA','r039');
    FNameMaxV      := ini.ReadString('Names','NameMaxV','n013');
    FNameReadV     := ini.ReadString('Names','NameReadV','r040');
    FNameFreqRef   := ini.ReadString('Names','NameFreqRef','r035');

    FInputNames   := ini.ReadString('Names','InputNames','S1,S2,S3,S4,S5,S6');
    FOutputNames   := ini.ReadString('Names','OutputNames','MA-MC,M1-M2');

    FcInvFault := ini.ReadInteger('Names','cInvFault',$4000);
    FcResetBitValue := ini.ReadInteger('Names','cResetBitValue',$0008);
    FcInCount := ini.ReadInteger('Names','cInCount',6);
    FcInReadShift := ini.ReadInteger('Names','cInReadShift',0);
    FcInWriteShift := ini.ReadInteger('Names','cInWriteShift',4);
    FcInWriteStart := ini.ReadInteger('Names','cInWriteStart',2);
    FcOutCount := ini.ReadInteger('Names','cOutCount',2);
    FcOutReadShift := ini.ReadInteger('Names','cOutReadShift',5);
    FcOutWriteShift := ini.ReadInteger('Names','cOutWriteShift',0);
    FcStoreRamRegister := ini.ReadInteger('Names','cStoreRAMRegister',0);
    FcStoreEEPROMRegister := ini.ReadInteger('Names','cStoreEEPROMRegister',0);
    FStoreEEPROMSequence := ini.ReadString('Names','StoreEEPROMSequence','');
    FcForwardRun := ini.ReadInteger('Names','cForwardRun',$1);
    FcReverseRun := ini.ReadInteger('Names','cReverseRun',$3);
    FcRunBit := ini.ReadInteger('Names','cRunBit',0);
    FcDirectionBit := ini.ReadInteger('Names','cRunBit',FcRunBit);
    FcRunMask := ini.ReadInteger('Names','cRunMask',$3);
    FcDirectionMask := ini.ReadInteger('Names','cDirectionMask',FcRunMask);
  finally
    ini.Free;
  end;
end;

{------------------------------------------------------------------------------}
function TYasParams.CreateList: string;
begin
  ReadNames;
  Result := Inherited CreateList;
  if ( Result = '' ) and ( Not FindParams ) then Result := 'Param error';
end;

{------------------------------------------------------------------------------}
constructor TYasParams.Create(AOwner: TComponent);
begin
  FYasForm := NIL;
  FmodbusSlave := TModbusSlave.Create;
  FModbusSlave.slave := 1;
  thz := 0;
end;

{------------------------------------------------------------------------------}
procedure TYasParams.DoShowControl(modal:boolean);
//var os : integer;
begin
//  os := slave;
  if ( FYasForm = NIL ) then begin
    FYasForm := TFormYas.Create2(@FYasForm,Self);
    if ( FYasForm = NIL ) then exit;
{
    if ( os < 0 ) then begin
      (FYasForm as TFormYas).ParamSlave.Value := os;
      (FYasForm as TFormYas).ParamSlave.SaveToIni;
      Slave := os;
    end;
}
  end;
  if modal then FYasForm.ShowModal else FYasForm.Show;
end;

{------------------------------------------------------------------------------}
destructor TYasParams.Destroy;
begin
  If ( FYasForm <> nil ) then
    FYasForm.Free;
  inherited;
end;

{------------------------------------------------------------------------------}
procedure TYasParams.ShowControl;
begin
  DoShowControl(false);
end;

{------------------------------------------------------------------------------}
procedure TYasParams.ShowControlModal;
begin
  DoShowControl(true);
end;

{------------------------------------------------------------------------------}
procedure TYasParams.ShowErrors;
begin
  ModbusSlave.ShowErrors;
end;

{------------------------------------------------------------------------------}
function TYasParams.GetSlave:integer;
begin
  Result := ModbusSlave.Slave;
end;

{------------------------------------------------------------------------------}
procedure TYasParams.SetSlave(s:integer);
begin
  ModbusSlave.Slave := s;
end;


{------------------------------------------------------------------------------}
function TYasParams.InvOK : boolean;
var i : integer;
begin
  // ceYasParamDebug.Add('Before inv ok');
  Result := false;
  if ( not ModbusSlave.Ok ) then exit;
  // ceYasParamDebug.Add('MBok');
  i := Trunc(ReadValueByName(NameInvStatus));
  // ceYasParamDebug.Add(Format('Status: %s = %d',[NameInvStatus,i]));
  if ( ModbusSlave.LastError <> '' ) then exit;
  // ceYasParamDebug.Add(Format('Result: %d',[i and cInvFault]));
  Result := ( i and cInvFault ) = 0;
end;

{------------------------------------------------------------------------------}
function TYasParams.GetLastError:string;
begin
  Result := 'No slave';
  if ( not ModbusSlave.Ok ) then exit;
  Result := ModbusSlave.LastError;
end;

function TYasParams.GetEnabled: boolean;
begin
  Result := ModbusSlave.Enabled;
end;

procedure TYasParams.SetEnabled(const Value: boolean);
begin
  ModbusSlave.Enabled := Value;
end;

procedure TYasParams.StoreEEPROM;
var
  Param:TParamValue;
begin
  if ( FStoreEEPROMSequence<>'' ) then begin
    Param := FindParam('M10-00');
    Param.WriteValueNoSave(1000,true);
    Param := FindParam('M10-38');
    Param.WriteValueNoSave(100,true);
    ceYasParamDebug.Add('Write to EEPROM');
  end else if ( FcStoreEEPROMRegister <> 0 ) then begin
    ModbusSlave.SetReg(FcStoreEEPROMRegister,0);
    ceYasParamDebug.Add('Yaskawa: Write to EEPROM');
  end;
end;

procedure TYasParams.StoreRAM;
begin
  if ( FcStoreRamRegister <> 0 ) then ModbusSlave.SetReg(FcStoreRamRegister,0);
end;


procedure Register;
begin
end;



initialization begin
  RegisterError(ceYasParamDebug ,'yd', 'Yaskawa debug'           ,True);
end;

end.
