Recent

Author Topic: Nodave 0.8.4.4 on Lazarus  (Read 18865 times)

arwen

  • Full Member
  • ***
  • Posts: 101
    • http://freeflow.awardspace.com
Nodave 0.8.4.4 on Lazarus
« on: February 18, 2008, 08:25:27 am »
I try to install the LibNodave component in Lazarus on Linux but I can't .
The origina library can be download at :
http://sourceforge.net/projects/libnodave/

This library is used to communicate with Siemens PLC S7

The installation of Nodave component in Lazarus0.9.24 on Windows work .
Someone was able to install nodave in Lazarus0.9.24 on Linux?  
If yes such corrections to the package I must make?
 
Thanks to all .

Leledumbo

  • Hero Member
  • *****
  • Posts: 8114
  • Programming + Glam Metal + Tae Kwon Do = Me
RE: Nodave 0.8.4.4 on Lazarus
« Reply #1 on: February 19, 2008, 05:50:48 am »
Quote
I try to install the LibNodave component in Lazarus on Linux but I can't .

Would you mind explaining "I can't" (e.g. error messages)?

arwen

  • Full Member
  • ***
  • Posts: 101
    • http://freeflow.awardspace.com
Nodave 0.8.4.4 on Lazarus
« Reply #2 on: February 23, 2008, 11:39:18 am »
Leledumbo Sorry if I have responded so late :oops:

In Linux

1 -  I must rename same pas file because Linux is CaseSensitive

2 - This is the errors :

Code: [Select]

/home/franaria/Projects/lazarus/components/nodave-0.8.4.4/DelphiComponent/NoDaveComponent.pas(547,5) Error: Identifier not found "DeallocateHWnd"
/home/franaria/Projects/lazarus/components/nodave-0.8.4.4/DelphiComponent/NoDaveComponent.pas(547,28) Error: Illegal expression
/home/franaria/Projects/lazarus/components/nodave-0.8.4.4/DelphiComponent/NoDaveComponent.pas(598,55) Hint: Conversion between ordinals and pointers is not portable
/home/franaria/Projects/lazarus/components/nodave-0.8.4.4/DelphiComponent/NoDaveComponent.pas(598,47) Hint: Conversion between ordinals and pointers is not portable
/home/franaria/Projects/lazarus/components/nodave-0.8.4.4/DelphiComponent/NoDaveComponent.pas(601,56) Hint: Conversion between ordinals and pointers is not portable
/home/franaria/Projects/lazarus/components/nodave-0.8.4.4/DelphiComponent/NoDaveComponent.pas(601,48) Hint: Conversion between ordinals and pointers is not portable
/home/franaria/Projects/lazarus/components/nodave-0.8.4.4/DelphiComponent/NoDaveComponent.pas(605,43) Hint: Conversion between ordinals and pointers is not portable
/home/franaria/Projects/lazarus/components/nodave-0.8.4.4/DelphiComponent/NoDaveComponent.pas(769,90) Hint: Conversion between ordinals and pointers is not portable
/home/franaria/Projects/lazarus/components/nodave-0.8.4.4/DelphiComponent/NoDaveComponent.pas(769,82) Hint: Conversion between ordinals and pointers is not portable
/home/franaria/Projects/lazarus/components/nodave-0.8.4.4/DelphiComponent/NoDaveComponent.pas(800,91) Hint: Conversion between ordinals and pointers is not portable
/home/franaria/Projects/lazarus/components/nodave-0.8.4.4/DelphiComponent/NoDaveComponent.pas(800,83) Hint: Conversion between ordinals and pointers is not portable
/home/franaria/Projects/lazarus/components/nodave-0.8.4.4/DelphiComponent/NoDaveComponent.pas(852,27) Hint: Conversion between ordinals and pointers is not portable
/home/franaria/Projects/lazarus/components/nodave-0.8.4.4/DelphiComponent/NoDaveComponent.pas(852,19) Hint: Conversion between ordinals and pointers is not portable
/home/franaria/Projects/lazarus/components/nodave-0.8.4.4/DelphiComponent/NoDaveComponent.pas(886,58) Hint: Type size mismatch, possible loss of data / range check error
/home/franaria/Projects/lazarus/components/nodave-0.8.4.4/DelphiComponent/NoDaveComponent.pas(941,34) Error: Identifier not found "AllocateHwnd"
/home/franaria/Projects/lazarus/components/nodave-0.8.4.4/DelphiComponent/NoDaveComponent.pas(957,59) Hint: Type size mismatch, possible loss of data / range check error
/home/franaria/Projects/lazarus/components/nodave-0.8.4.4/DelphiComponent/NoDaveComponent.pas(971,59) Hint: Type size mismatch, possible loss of data / range check error
/home/franaria/Projects/lazarus/components/nodave-0.8.4.4/DelphiComponent/NoDaveComponent.pas(992,3) Hint: Local variable "List" does not seem to be initialized
/home/franaria/Projects/lazarus/components/nodave-0.8.4.4/DelphiComponent/NoDaveComponent.pas(1488,3) Hint: Local variable "Buffer" does not seem to be initialized
/home/franaria/Projects/lazarus/components/nodave-0.8.4.4/DelphiComponent/NoDaveComponent.pas(1567,23) Hint: Conversion between ordinals and pointers is not portable


In Windows works  :cry:

Thanks to all

Leledumbo

  • Hero Member
  • *****
  • Posts: 8114
  • Programming + Glam Metal + Tae Kwon Do = Me
Nodave 0.8.4.4 on Lazarus
« Reply #3 on: February 25, 2008, 11:05:27 am »
Both AllocateHwnd and DeallocateHwnd are Windows specific. Would you mind posting the code that has calls to these functions? Hope we can make a linux version (or even better, a cross-platform version) of it.

arwen

  • Full Member
  • ***
  • Posts: 101
    • http://freeflow.awardspace.com
Nodave 0.8.4.4 on Lazarus
« Reply #4 on: March 04, 2008, 08:11:46 am »
This is the Unit .
The complete library can be download at :
http://sourceforge.net/projects/libnodave/

Thanks to all

// NoDaveComponent.pas
//
// A unit implementing a wrapper component for easy access to a S7-PLC with
// the libnodave.dll of Thomas Hergenhahn (http://libnodave.sourceforge.net)
//
// (C) 2005, 2006 Gebr. Schmid GmbH + Co., Freudenstadt, Germany
//
// Author: Axel Kinting (akinting@schmid-online.de)
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

{$IFDEF FPC}

  {$MODE Delphi}

{$ENDIF}


unit NoDaveComponent;

//The Unit NoDaveComponent implements the class TNoDave, which encapsulates the access to the libnodave.dll.
//With TNoDave and libnodave.dll it is very easy to read and write data from and to a S7 PLC.

//Simatic, Simatic S5, Simatic S7, S7-200, S7-300, S7-400 are registered Trademarks of Siemens Aktiengesellschaft, Berlin und Muenchen.
//~Author Axel Kinting - Gebr. Schmid GmbH + Co.
//~todo Before Installation:

//Please copy the file \pascal\nodave.pas into the directory, where the file nodavecomponent.pas is located !

//Delphi-Installation:

//1. Select Component - Install in the Delphi-menu
//2. Select Add... button
//3. Select Browse
//4. Select NoDaveComponent.pas
//5. Select OK

//Lazarus-Installation:
//1. Select Components - Open package file
//2. Select nodavepackage.lpk
//3. Select Open
//4. Select Compile
//5. Select Install
//6. Select Yes

interface

uses

  SysUtils, Classes, NoDave, SyncObjs, Controls, {$IFDEF FPC} LCLIntf, LResources {$ELSE} Windows {$ENDIF};

type

  TNoDaveArea = (                                               //The area of the PLC-Data for the TNoDave-Component.

                  daveSysInfo,                                  //System information of 200 family
                  daveSysFlags,                                 //System flag area of 200 family
                  daveAnaIn,                                    //Analog input words of 200 family
                  daveAnaOut,                                   //Analog output words of 200 family
                  daveInputs,                                   //Input memory image
                  daveOutputs,                                  //Output memory image
                  daveFlags,                                    //Flags/Markers
                  daveDB,                                       //Data Blocks (global data)
                  daveDI,                                       //Data Blocks (instance data) ?
                  daveLocal,                                    //Data Blocks (local data) ?
                  daveV,                                        //unknown Area
                  daveCounter,                                  //Counter
                  daveTimer,                                    //Timer
                  daveP                                         //Peripherie Input/Output

  );



  TNoDaveDebugOption = (                                        //The debug-options for the libnodave.dll

                         daveDebugRawRead,
                         daveDebugSpecialChars,
                         daveDebugRawWrite,
                         daveDebugListReachables,
                         daveDebugInitAdapter,
                         daveDebugConnect,
                         daveDebugPacket,
                         daveDebugByte,
                         daveDebugCompare,
                         daveDebugExchange,
                         daveDebugPDU,
                         daveDebugUpload,
                         daveDebugMPI,
                         daveDebugPrintErrors,
                         daveDebugPassive
  );



  TNoDaveDebugOptions = Set of TNoDaveDebugOption;



  TNoDaveProtocol = (                                           //The type of the communication-protocol for the TNoDave-Component.

                      daveProtoMPI,                             //MPI-Protocol
                      daveProtoMPI2,                            //MPI-Protocol (Andrew's version without STX)
                      daveProtoMPI3,                            //MPI-Protocol (Step 7 Version version)
                      daveProtoMPI4,                            //MPI-Protocol (Andrew's version with STX)
                      daveProtoPPI,                             //PPI-Protocol
                      daveProtoISOTCP,                          //ISO over TCP
                      daveProtoISOTCP243,                       //ISO over TCP (for CP243)
                      daveProtoIBH,                             //IBH-Link TCP/MPI-Adapter
                      daveProtoIBH_PPI,                         //IBH-Link TCP/MPI-Adapter with PPI-Protocol
                      daveProtoS7Online,                        //use S7Onlinx.dll for transport via Siemens CP
                      daveProtoAS511,                           //S5 via programmer-port
                      daveProtoNLPro                            //Deltalogic NetLink-PRO TCP/MPI-Adapter

  );



  TNoDaveSpeed = (                                              //The speed of the MPI-protocol for the TNoDave-Component.

                   daveSpeed9k,
                   daveSpeed19k,
                   daveSpeed187k,
                   daveSpeed500k,
                   daveSpeed1500k,
                   daveSpeed45k,
                   daveSpeed93k
  );



  TNoDaveComSpeed = (                                           //The speed of the COM-Port for the TNoDave-Component.

                      daveComSpeed9_6k,
                      daveComSpeed19_2k,
                      daveComSpeed38_4k,
                      daveComSpeed57_6k,
                      daveComSpeed115_2k
  );



//This is the type of the Event-Handler for the OnError-Event of the TNoDave component.
//~param Sender The TNoDave-instance which is the source of the event.
//~param ErrorMsg A clear text message describing the error.
  TNoDaveOnErrorEvent = procedure(Sender: TObject; ErrorMsg: String) of Object;



//List of reachable Partners in the MPI-Network, True = Station is available at this address.

  TNoDaveReachablePartnersMPI = Array [0..126] of Boolean;

//The Class TNoDave encapsulates the access to the libnodave.dll of Thomas Hergenhahn.
//All the settings for the communication are available in the properties of TNoDave.

  TNoDave = class(TComponent)

  private

    FActive: Boolean;
    FArea: TNoDaveArea;
    FBuffer: Pointer;
    FBufLen: Integer;
    FBufOffs: Integer;
    FComPort: String;
    FCpuRack: Integer;
    FCpuSlot: Integer;
    FCycleTime: Cardinal;
    FDBNumber: Integer;
    FDebugOptions: Integer;
    FInterval: Cardinal;
    FIntfName: String;
    FIntfTimeout: Integer;
    FIPAddress: String;
    FIPPort: Integer;
    FMPILocal: Integer;
    FMPIRemote: Integer;
    FMPISpeed: TNoDaveSpeed;
    FLastError: Integer;
    FOnConnect: TNotifyEvent;
    FOnDisconnect: TNotifyEvent;
    FOnError: TNoDaveOnErrorEvent;
    FOnRead: TNotifyEvent;
    FOnWrite: TNotifyEvent;
    FProtocol: TNoDaveProtocol;
    FSZLBuffer: Pointer;
    FComSpeed: TNoDaveComSpeed;
    FHandle: THandle;
    function GetActive: Boolean;
    function GetBuffer: String;
    function GetDebugOptions: TNoDaveDebugOptions;
    function GetLastErrMsg: String;
    procedure SetActive(V: Boolean);
    procedure SetArea(V: TNoDaveArea);
    procedure SetBufLen(V: Integer);
    procedure SetBufOffs(V: Integer);
    procedure SetComPort(V: String);
    procedure SetCpuRack(V: Integer);
    procedure SetCpuSlot(V: Integer);
    procedure SetDBNumber(V: Integer);
    procedure SetDebugOptions(V: TNoDaveDebugOptions);
    procedure SetInterval(V: Cardinal);
    procedure SetIntfName(V: String);
    procedure SetIntfTimeout(V: Integer);
    procedure SetIPAddress(V: String);
    procedure SetIPPort(V: Integer);
    procedure SetMPILocal(V: Integer);
    procedure SetMPIRemote(V: Integer);
    procedure SetMPISpeed(V: TNoDaveSpeed);
    procedure SetProtocol(V: TNoDaveProtocol);
    function GetSZLCount: Integer;
    function GetSZLItemSize: Integer;
    function GetSZLItem(Index: Integer): Pointer;
    function GetMaxPDUData: Integer;
    procedure SetComSpeed(V: TNoDaveComSpeed);
    function GetHandle: THandle;
  protected

    ConnectPending: Boolean;
    DaveFDS: _daveOSSerialType;
    DaveConn: PDaveConnection;
    DaveIntf: PDaveInterface;
    LockNoDave: TCriticalSection;
    ReadThread: TThread;
    procedure DoConnect(OnlyIntf: Boolean = False);
    procedure DoOnConnect;
    procedure DoOnDisconnect;
    procedure DoOnError(ErrorMsg: String);
    procedure DoOnRead;
    procedure DoOnWrite;
    procedure DoReadBytes(Area: TNoDaveArea; DB, Start, Size: Integer; Buffer: Pointer = Nil);
    procedure DoWriteBytes(Area: TNoDaveArea; DB, Start, Size: Integer; Buffer: Pointer = Nil);
    procedure DoWriteValue(Address, Size: Integer; Value: Pointer);
    procedure Loaded; override;
    procedure WriteBit(Area: TNoDaveArea; DB, Address, Bit: Integer; Value: Boolean); overload;
    function AreaCode(Area: TNoDaveArea): Integer;
    function ProtCode(Prot: TNoDaveProtocol): Integer;
    function BufferAt(Address: Integer; Size: Integer = 1; Buffer: Pointer = Nil; BufOffs: Integer = 0; BufLen: Integer = 0): Pointer;

  public

    AreaID: Integer;                                                                        //S7-ID of the selected ~[link .Area Area]

    constructor Create(aOwner: TComponent); override;
    destructor Destroy; override;
    procedure Connect(Wait: Boolean = True);
    procedure Disconnect;
    procedure Lock;
    procedure Unlock;
    procedure ResetInterface;
    procedure DoSetDebug(Options: Integer);
    procedure ReadBytes(Buffer: Pointer = Nil); overload;
    Procedure WriteBytes(Buffer: Pointer = Nil); overload;
    procedure ReadBytes(Area: TNoDaveArea; DB, Start, Size: Integer; Buffer: Pointer = Nil); overload;
    procedure WriteBytes(Area: TNoDaveArea; DB, Start, Size: Integer; Buffer: Pointer = Nil); overload;
    procedure WriteBit(Address, Bit: Integer; Value: Boolean); overload;
    procedure WriteByte(Address: Integer; Value: Byte);
    procedure WriteWord(Address: Integer; Value: Word);
    procedure WriteInt(Address: Integer; Value: SmallInt);
    procedure WriteDWord(Address: Integer; Value: LongWord);
    procedure WriteDInt(Address: Integer; Value: LongInt);
    procedure WriteFloat(Address: Integer; Value: Single);
    function GetBit(Address, Bit: Integer; Buffer: Pointer = Nil; BufOffs: Integer = 0; BufLen: Integer = 0): Boolean;
    function GetByte(Address: Integer; Buffer: Pointer = Nil; BufOffs: Integer = 0; BufLen: Integer = 0): Byte;
    function GetWord(Address: Integer; Buffer: Pointer = Nil; BufOffs: Integer = 0; BufLen: Integer = 0): Word;
    function GetInt(Address: Integer; Buffer: Pointer = Nil; BufOffs: Integer = 0; BufLen: Integer = 0): SmallInt;
    function GetDWord(Address: Integer; Buffer: Pointer = Nil; BufOffs: Integer = 0; BufLen: Integer = 0): LongWord;
    function GetDInt(Address: Integer; Buffer: Pointer = Nil; BufOffs: Integer = 0; BufLen: Integer = 0): LongInt;
    function GetFloat(Address: Integer; Buffer: Pointer = Nil; BufOffs: Integer = 0; BufLen: Integer = 0): Double;
    function GetDBSize(DB: Integer): Integer;
    function ListReachablePartners: TNoDaveReachablePartnersMPI;
    function Swap16(Value: SmallInt): SmallInt;
    function Swap32(Value: Integer): Integer;
    function GetErrorMsg(Error: Integer): String;
    function ReadSZL(ID, Index: Integer): Integer;
    property SZLCount: Integer read GetSZLCount;                                            //Property for the number of items in the internal SZL-Buffer
    property SZLItem[Index: Integer]: Pointer read GetSZLItem;                              //Property for the items in the internal SZL-Buffer
    property SZLItemSize: Integer read GetSZLItemSize;                                      //Property for the size of one item in the internal SZL-Buffer
    property MaxPDUData: Integer read GetMaxPDUData;                                        //Property for the maximum datasize of one read-request
    property Handle: THandle read GetHandle;                                                //Property for the Windows-Handle used for system-calls

  published

    property Active: Boolean read GetActive write SetActive;                                //Property for the connection-status.
    property Area: TNoDaveArea read FArea write SetArea;                                    //Property for the PLC-area
    property Buffer: String read GetBuffer;                                                 //Property for the pointer to the internal buffer memory.
    property BufLen: Integer read FBufLen write SetBufLen;                                  //Property for the length of the buffer.
    property BufOffs: Integer read FBufOffs write SetBufOffs;                               //Property for the offset of the buffer within the address-range of the PLC.
    property COMPort: String read FComPort write SetComPort;                                //Property for the name of the COM-Port used for the serial-to-MPI adapter.
    property COMSpeed: TNoDaveComSpeed read FComSpeed write SetComSpeed;                    //Property for the speed of the COM-Port used for the serial-to-MPI adapter.
    property CPURack: Integer read FCpuRack write SetCpuRack;                               //Property for the number of the rack containing the CPU of the PLC.
    property CPUSlot: Integer read FCpuSlot write SetCpuSlot;                               //Property for the number of the slot containing the CPU of the PLC.
    property CycleTime: Cardinal read FCycleTime;                                           //Property for the duration in ms of the last communication cycle.
    property DBNumber: Integer read FDBNumber write SetDBNumber;                            //Property for the number of the datablock in the PLC.
    property DebugOptions: TNoDaveDebugOptions read GetDebugOptions write SetDebugOptions;  //Property for the debug-options.
    property Interval: Cardinal read FInterval write SetInterval;                           //Property for the minimal round-trip cycle time for the background-communication with the PLC in milliseconds.
    property IntfName: String read FIntfName write SetIntfName;                             //Property for the symbolic name of the interface.
    property IntfTimeout: Integer read FIntfTimeout write SetIntfTimeout;                   //Property for the timeout of the interface in milliseconds.
    property IPAddress: String read FIPAddress write SetIPAddress;                          //Property for the IP-address or name of the TCP/IP partner station.
    property IPPort: Integer read FIPPort write SetIPPort;                                  //Property for the IP-port of the TCP/IP partner station.
    property LastError: Integer read FLastError;                                            //Property for the return-code of the last call of a communication-method.
    property LastErrMsg: String read GetLastErrMsg;                                         //Property for the text describing the code in ~[link .LastError LastError].
    property MPILocal: Integer read FMPILocal write SetMPILocal;                            //Property for the local MPI-address used for the MPI-communication.
    property MPIRemote: Integer read FMPIRemote write SetMPIRemote;                         //Property for the remote MPI-address used for the MPI-communication.
    property MPISpeed: TNoDaveSpeed read FMPISpeed write SetMPISpeed;                       //Property for the MPI-speed used for the MPI-communication.
    property OnConnect: TNotifyEvent read FOnConnect write FOnConnect;                      //Property for the OnConnect-eventhandler
    property OnDisconnect: TNotifyEvent read FOnDisconnect write FOnDisconnect;             //Property for the OnDisconnect-eventhandler
    property OnError: TNoDaveOnErrorEvent read FOnError write FOnError;                     //Property for the OnError-eventhandler
    property OnRead: TNotifyEvent read FOnRead write FOnRead;                               //Property for the OnRead-eventhandler
    property OnWrite: TNotifyEvent read FOnWrite write FOnWrite;                            //Property for the OnWrite-eventhandler
    property Protocol: TNoDaveProtocol read FProtocol write SetProtocol;                    //Property for the Protocol used for the communication with the PLC.

  end;


  TSzlLed = (NONE, SF, INTF, EXTF, RUN, STOP, FRCE, CRST, BAF, USR, USR1, BUS1F, BUS2F, REDF, MSTR, RACK0, RACK1, RACK2, IFM1F, IFM2F);

  TSzlBGIdent = Record
    Index:      Word;
    MlfB:       packed Array[1..20] of Char;
    BGTyp:      Word;
    AusBG:      packed Array[1..4] of Byte;

  end;

  PSzlBGIdent = ^TSzlBGIdent;
  TSzlUserMemory = Record
    Index:      Word;
    Code:       Word;
    Size:       LongWord;
    Mode:       Word;
    Granu:      Word;
    Area1:      LongWord;
    Used1:      LongWord;
    Free1:      LongWord;
    Area2:      LongWord;
    Used2:      LongWord;
    Free2:      LongWord;

  end;

  PSzlUserMemory = ^TSzlUserMemory;

  TSzlSystemMemory = Record
    Index:      Word;
    Code:       Word;
    Count:      Word;
    Reman:      Word;

  end;

  PSzlSystemMemory = ^TSzlSystemMemory;

  TSzlBlockType = Record
    Index:      Word;
    Count:      Word;
    Size:       Word;
    MemSize:    LongWord;

  end;

  PSzlBlockType = TSzlBlockType;

  TSzlLedState = packed Record
    Index:      Word;
    State:      Byte;
    Blink:      Byte;

  end;

  PSzlLedState = ^TSzlLedState;

  TSzlBGState = Record
    Addr1:      Word;
    Addr2:      Word;
    LogAddr:    Word;
    ConfTyp:    Word;
    RealTyp:    Word;
    Count:      Word;
    EAStatus:   Word;
    Area:       Word;

  end;

  PSzlBGState = ^TSzlBGState;

  TSzlStationState = Record
    Status:     packed Array[0..15] of Byte;

  end;

  PSzlStationState = ^TSzlStationState;


  TSzlDiagMessage = packed Record
    ID:         Word;
    Info:       Array[1..5] of Word;
    Year:       Byte;
    Month:      Byte;
    Day:        Byte;
    Hour:       Byte;
    Minute:     Byte;
    Second:     Byte;
    MSec:       Word;

  end;

  PSzlDiagMessage = ^TSzlDiagMessage;

  TSzlBGDiagInfo = Record

    Info:       packed Array[1..4] of Byte;
   end;
  PSzlBGDiagInfo = ^TSzlBGDiagInfo;

procedure Register;

implementation

type

//Worker-thread for asynchronous connecting with the PLC.
  TNoDaveConnectThread = class(TThread)

  private

    NoDave: TNoDave;
    ErrMsg: String;

  protected

    procedure DoOnError;
    procedure DoOnConnect;
    procedure Execute; override;

  public

    constructor Create(Target: TNoDave);

  end;



//Worker-thread for the background-communication with the PLC.

  TNoDaveReadThread = class(TThread)

  private

    NoDave: TNoDave;
    ErrMsg: String;

  protected

    procedure DoOnError;
    procedure DoOnRead;
    procedure Execute; override;

  public

    constructor Create(Target: TNoDave);

  end;



//Installation of TNoDave in the component palette

procedure Register;
begin

  RegisterComponents('System',[TNoDave]);

end;



{ TNoDaveConnectThread }



//Create the worker-thread for asynchronous connecting with the PLC.
//~param Target The TNoDave-instance to connect with the PLC.

constructor TNoDaveConnectThread.Create(Target: TNoDave);

begin

  inherited Create(False);
  NoDave:=Target;
  FreeOnTerminate:=True;

end;



//Synchronization-method for calling the OnConnect-Event of the TNoDave-instance.

procedure TNoDaveConnectThread.DoOnConnect;

begin

  NoDave.DoOnConnect;

end;



//Synchronization-method for calling the OnError-Event of the TNoDave-instance.

procedure TNoDaveConnectThread.DoOnError;

begin

  NoDave.DoOnError(ErrMsg);

end;



//Open the connection to the PLC. If successfull then call the OnConnect-Event else call the OnError-Event of the TNoDave-instance.

procedure TNoDaveConnectThread.Execute;

begin

  try

    NoDave.DoConnect;

    If NoDave.Active then Synchronize(DoOnConnect);

  except

    on E: Exception do

    begin

      ErrMsg:='Error in function TNoDaveConnectThread.Execute: ' + E.Message;

      Synchronize(DoOnError);

    end;

  end;

  NoDave.ConnectPending:=False;

end;



{ TNoDaveReadThread }



//Create the worker-thread for the background-communication with the PLC.

//~param Target The TNoDave-instance for the communication with the PLC.

constructor TNoDaveReadThread.Create(Target: TNoDave);

begin

  inherited Create(False);

  NoDave:=Target;

end;



//Synchronization-method for calling the OnError-Event of the TNoDave-instance.

procedure TNoDaveReadThread.DoOnError;

begin

  NoDave.DoOnError(ErrMsg);

end;



//Synchronization-method for calling the OnRead-Event of the TNoDave-instance.

procedure TNoDaveReadThread.DoOnRead;

begin

  NoDave.DoOnRead;

end;



//Read the data from the PLC, call the OnRead-Event of the TNoDave-instance, wait until the round-trip cycle time is reached and

//then start again from the beginning until the Connection of the TNoDave-instance is active. Disconnect the TNoDave-instance if

//the connection is not longer valid.

procedure TNoDaveReadThread.Execute;

var

  StartTime: Cardinal;

  NextTime: Cardinal;

begin

  While NoDave.Active and (NoDave.Interval > 0) do

  begin

    StartTime:=GetTickCount;

    NextTime:=StartTime + NoDave.Interval;

    try

      NoDave.ReadBytes;

      If NoDave.LastError = 0 then Synchronize(DoOnRead) else

      begin

        If NoDave.LastError = -1 then NoDave.Disconnect else

        begin

          ErrMsg:=NoDave.LastErrMsg;

          Synchronize(DoOnError);

        end;

      end;

    except

      on E: Exception do

      begin

        ErrMsg:='Error in function TNoDaveReadThread.Execute: ' + E.Message;

        Synchronize(DoOnError);

      end;

    end;

    While GetTickCount < NextTime do Sleep(1);

  end;

end;



{ TNoDave }



//Initialize a new instance of the TNoDave component.

//~param aOwner Owner of the created instance.

constructor TNoDave.Create(aOwner: TComponent);

begin

  inherited;

  AreaID:=-1;

  FActive:=False;

  FArea:=daveDB;

  FBuffer:=Nil;

  FBufLen:=0;

  FBufOffs:=0;

  FComPort:='COM1:';

  FComSpeed:=daveComSpeed38_4k;

  FCpuRack:=0;

  FCpuSlot:=2;

  FDBNumber:=1;

  FDebugOptions:=0;

  FIntfName:='IF1';

  FIntfTimeout:=1500000;

  FIPAddress:='';

  FIPPort:=102;

  FMPILocal:=0;

  FMPIRemote:=2;

  FMPISpeed:=daveSpeed187k;

  FLastError:=0;

  FProtocol:=daveProtoMPI;

  LockNoDave:=TCriticalSection.Create;

  daveSetDebug(FDebugOptions);

end;



//Close an active connection and call the inherited Destroy method.

destructor TNoDave.Destroy;

begin

  Disconnect;

  If Assigned(FBuffer) then

  try

    FreeMem(FBuffer);

  except

  end;

  If FHandle <> 0 then

  try

    DeallocateHWnd(FHandle);

  except

  end;

  FHandle:=0;

  LockNoDave.Free;

  inherited;

end;



//Determine the S7-ID of an Area.

//~param Area Requested Area.

//~result S7-ID of the Area.

function TNoDave.AreaCode(Area: TNoDaveArea): Integer;

begin

  Result:=AreaID;

  If Result = -1 then

  begin

    Case Area of

      daveSysInfo:    Result:=3;

      daveSysFlags:   Result:=5;

      daveAnaIn:      Result:=6;

      daveAnaOut:     Result:=7;

      daveCounter:    Result:=28;

      daveTimer:      Result:=29;

      daveP:          Result:=128;

      daveInputs:     Result:=129;

      daveOutputs:    Result:=130;

      daveFlags:      Result:=131;

      daveDB:         Result:=132;

      daveDI:         Result:=133;

      daveLocal:      Result:=134;

      daveV:          Result:=135;

    end;

  end;

end;



//Return a Pointer to the requested PLC-data point within the buffer.

//~param Address PLC-Address of  the datapoint.

//~param Size Size of the datapoint in bytes.

//~param Buffer Pointer to the buffer holding the PLC-data. The internal buffer is used, if Nil (default).

//~param BufOffs Offset-address of the buffer within the address-range of the PLC.

//~param BufLen Length of the buffer in bytes.

//~result Pointer to the requested data point if the address is located in the buffer, else Nil.

function TNoDave.BufferAt(Address: Integer; Size: Integer; Buffer: Pointer; BufOffs: Integer; BufLen: Integer): Pointer;

var

  Offset: Integer;

begin

  Result:=Nil;

  try

    If Assigned(Buffer) then

    begin

      Offset:=Address - BufOffs;

      If Offset + Size <= BufLen then Result:=Pointer(Integer(Buffer) +  Offset);

    end else begin

      Offset:=Address - FBufOffs;

      If Offset + Size <= FBufLen then Result:=Pointer(Integer(FBuffer) + Offset);

    end;

  except

    On E: Exception do DoOnError('Error in function TNoDave.BufferAt(' + IntToStr(Address) + ', ' + IntToStr(Size) + ', $' +

                                 IntToHex(Integer(Buffer), 8) + ', ' + IntToStr(BufOffs) + ', ' + IntToStr(BufLen) + '): ' +

                                 E.Message);

  end;

end;



//Open the connection to the PLC.

//~param Wait If False the connection is opened asyncronous in a separate thread. Default is True.

procedure TNoDave.Connect(Wait: Boolean = True);

begin

  If not FActive and not ConnectPending then

  begin

    ConnectPending:=True;

    If Wait then

    begin

      try

        DoConnect;

        If Active and not (csLoading in ComponentState) then DoOnConnect;

      except

        On E: Exception do DoOnError('Error in function TNoDave.Connect: ' + E.Message);

      end;

      ConnectPending:=False;

    end else begin

      TNoDaveConnectThread.Create(Self);

    end;

  end;

end;



//Close the connection to the PLC.

procedure TNoDave.Disconnect;

begin

  If Active then

  begin

    try

      daveDisconnectPLC(DaveConn);

      daveFree(DaveConn);

      daveDisconnectAdapter(DaveIntf);

      daveFree(DaveIntf);

      If FProtocol <> daveProtoS7Online then closePort(DaveFDS.rfd) else closeS7online(DaveFDS.rfd);

    except

      On E: Exception do DoOnError('Error in function TNoDave.Disconnect: ' + E.Message);

    end;

    FActive:=False;

    DoOnDisconnect;

  end;

end;



//Open the connection to the PLC specified by the properties ~[link .Protocol Protocol], ~[link .CPURack CPURack], ~[link .CPUSlot CPUSlot],

//~[link .COMPort COMPort], ~[link .IPAddress IPAddress], ~[link .IPPort IPPort], ~[link .MPILocal MPILocal], ~[link .MPIRemote MPIRemote]

// and/or ~[link .MPISpeed MPISpeed]

//~param OnlyIntf Open only the interface, don't connect to the PLC

procedure TNoDave.DoConnect(OnlyIntf: Boolean = False);

var

  Address: String;

  Speed: PChar;

begin

  If not FActive then

  begin

    If not (csLoading in ComponentState) then

    begin

      Case FProtocol of

        daveProtoMPI, daveProtoMPI2, daveProtoMPI3, daveProtoMPI4, daveProtoPPI, daveProtoAS511:

          begin

            Address:=FComPort + #0;

            Case ComSpeed of

              daveComSpeed9_6k:   Speed:='9600';

              daveComSpeed19_2k:  Speed:='19200';

              daveComSpeed38_4k:  Speed:='38400';

              daveComSpeed57_6k:  Speed:='57600';

              daveComSpeed115_2k: Speed:='115200';

              else                Speed:='38400';

            end;

            DaveFDS.rfd:=SetPort(@Address[1], Speed, 'O');

          end;

        daveProtoISOTCP, daveProtoISOTCP243, daveProtoIBH, daveProtoIBH_PPI, daveProtoNLPro:

          begin

            Address:=FIPAddress + #0;

            DaveFDS.rfd:=OpenSocket(FIPPort, @Address[1]);

          end;

        daveProtoS7Online:

          begin

            Address:=FComPort + #0;

            DaveFDS.rfd:=OpenS7Online(@Address[1], Handle);

          end;

      end;

      DaveFDS.wfd:=DaveFDS.rfd;

      If (DaveFDS.rfd > 0) or ((DaveFDS.rfd = 0) and (FProtocol = daveProtoS7Online)) then

      begin

        Address:=FIntfName + #0;

        DaveIntf:=daveNewInterface(DaveFDS, @Address[1], Ord(FMPIlocal), ProtCode(FProtocol), Ord(FMPISpeed));

        DaveIntf^.timeout:=FIntfTimeout;

        If not OnlyIntf then

        begin

          FLastError:=daveInitAdapter(DaveIntf);

          If FLastError = 0 then

          begin

            DaveConn:=daveNewConnection(DaveIntf, FMPIRemote, FCpuRack, FCpuSlot);

            FLastError:=daveConnectPLC(DaveConn);

            FActive:=(FLastError = 0);

            If Active then ReadBytes else DoOnError(daveStrerror(FLastError));

          end;

        end;

      end;

    end else FActive:=True;

  end;

end;



//Create the worker-thread for cyclic reading if neccessary and call the OnConnect-eventhandler if specified.

procedure TNoDave.DoOnConnect;

begin

  If (FInterval > 0) and not Assigned(ReadThread) and not (csDesigning in ComponentState)

    then ReadThread:=TNoDaveReadThread.Create(Self);

  If Assigned(OnConnect) then OnConnect(Self);

end;



//Stop and Destroy the worker-thread for cyclic reading if neccessary and call the OnDisconnect-eventhandler if specified.

procedure TNoDave.DoOnDisconnect;

begin

  If Assigned(ReadThread) then

  begin

    ReadThread.WaitFor;

    FreeAndNil(ReadThread);

  end;

  If Assigned(OnDisconnect) then OnDisconnect(Self);

end;



//Call the OnError-eventhandler if specified.

//~param ErrorMsg The text-message for the OnError-event

procedure TNoDave.DoOnError(ErrorMsg: String);

begin

  If Assigned(OnError) then OnError(Self, ErrorMsg);

end;



//Call the OnRead-eventhandler if specified.

procedure TNoDave.DoOnRead;

begin

  If Assigned(OnRead) then OnRead(Self);

end;



//Call the OnWrite-eventhandler if specified.

procedure TNoDave.DoOnWrite;

begin

  If Assigned(OnWrite) then OnWrite(Self);

end;



//Read the PLC-data into the buffer.

//~param Area Requested PLC-area.

//~param DB Number of requested datablock. Only used, if reading from Datablocks in the PLC.

//~param Start Start-address of the requested data within the address-range of the PLC.

//~param Size Length of the requested PLC-data in bytes.

//~param Buffer Pointer to the buffer. The internal buffer of the instance is used, if Nil (default).

procedure TNoDave.DoReadBytes(Area: TNoDaveArea; DB, Start, Size: Integer; Buffer: Pointer);

var

  Index, Length, MaxLen: Integer;

  StartTime: Cardinal;

begin

  Index:=0;

  StartTime:=GetTickCount;

  MaxLen:=MaxPDUData;

  While (Index < Size) and (MaxLen > 0) do

  begin

    Length:=(Size - Index);

    If Length > MaxLen then Length:=MaxLen;

    try

      LockNoDave.Enter;

      FLastError:=daveReadBytes(DaveConn, AreaCode(Area), DB, Index+Start, Size, Pointer(Integer(Buffer) + Index));

    except

      On E: Exception do DoOnError('Error in function TNoDave.DoReadBytes: ' + E.Message);

    end;

    LockNoDave.Leave;

    Inc(Index, Length);

  end;

  try

    FCycleTime:=GetTickCount - StartTime;

  except

  end;

end;



//Write the Buffer-data into the PLC.

//~param Area Requested PLC-area.

//~param DB Number of requested datablock. Only used, if reading from Datablocks in the PLC.

//~param Start Start-address of the requested data within the address-range of the PLC.

//~param Size Length of the requested PLC-data in bytes.

//~param Buffer Pointer to the buffer. The internal buffer of the instance is used, if Nil (default).

procedure TNoDave.DoWriteBytes(Area: TNoDaveArea; DB, Start, Size: Integer; Buffer: Pointer);

var

  Index, Length, MaxLen: Integer;

begin

  Index:=0;

  MaxLen:=MaxPDUData-4;

  While (Index < Size) and (MaxLen > 0) do

  begin

    Length:=(Size - Index);

    If Length > MaxLen then Length:=MaxLen;

    try

      LockNoDave.Enter;

      FLastError:=daveWriteBytes(DaveConn, AreaCode(Area), DB, Index+Start, Size, Pointer(Integer(Buffer) + Index));

    except

      On E: Exception do DoOnError('Error in function TNoDave.DoWriteBytes: ' + E.Message);

    end;

    LockNoDave.Leave;

    Inc(Index, Length);

  end;

end;



//Write a single value into the specified address of the PLC without changing the properties of the TNoDave-instance.

//~param Address PLC-Address of the data point.

//~param Size Size in bytes of the value.

//~param Value The value to be written.

procedure TNoDave.DoWriteValue(Address, Size: Integer; Value: Pointer);

begin

  try

    LockNoDave.Enter;

    FLastError:=daveWriteBytes(DaveConn, AreaCode(FArea), FDBNumber, Address, Size, Value);

  except

    On E: Exception do DoOnError('Error in function TNoDave.DoWriteValue: ' + E.Message);

  end;

  LockNoDave.Leave;

end;



//Set the debug-options of the libnodave.dll

//~param Options Value of the debug-options.

procedure TNoDave.DoSetDebug(Options: Integer);

begin

  daveSetDebug(Options);

  FDebugOptions:=Options;

end;



//Return the state of the connection to the PLC.

//~result True, if connection is open, else False.

function TNoDave.GetActive: Boolean;

begin

  Result:=FActive and not (csLoading in ComponentState);

end;



//Return a textual representation of the content of the internal buffer.

//~result String with the hexadecimal value of the bytes in the buffer.

function TNoDave.GetBuffer: String;

var

  Index: Integer;

  Value: Byte;

begin

  Result:='';

  If Assigned(FBuffer) then

  begin

    Index:=0;

    While Index < FBufLen do

    begin

      Value:=Byte(Pointer(Integer(FBuffer) + Index)^);

      If Result <> '' then Result:=Result + ' ';

      Result:=Result + IntToHex(Value, 2);

      Inc(Index);

    end;

  end;

end;



//Return the Bit-value read last from the PLC at the specified address.

//~param Address Byte-address of the requested value

//~param Bit Bit-address of the requested value

//~param Buffer Pointer to the buffer holding the PLC-data. The internal buffer is used, if Nil (default).

//~param BufOffs Offset-address of the buffer within the address-range of the PLC.

//~param BufLen Length of the buffer in bytes.

//~result The requested value or False, if the requested address was not found within the buffer.

function TNoDave.GetBit(Address, Bit: Integer; Buffer: Pointer; BufOffs: Integer; BufLen: Integer): Boolean;

var

  BufPtr: Pointer;

begin

  BufPtr:=BufferAt(Address, 1, Buffer, BufOffs, BufLen);

  If Assigned(BufPtr) then Result:=(daveGetU8From(BufPtr) and (1 shl Bit)) <> 0 else Result:=False;

end;



//Return the Byte-value read last from the PLC at the specified address.

//~param Address Address of the requested value

//~param Buffer Pointer to the buffer holding the PLC-data. The internal buffer is used, if Nil (default).

//~param BufOffs Offset-address of the buffer within the address-range of the PLC.

//~param BufLen Length of the buffer in bytes.

//~result The requested value or 0, if the requested address was not found within the buffer.

function TNoDave.GetByte(Address: Integer; Buffer: Pointer; BufOffs: Integer; BufLen: Integer): Byte;

var

  BufPtr: Pointer;

begin

  BufPtr:=BufferAt(Address, 1, Buffer, BufOffs, BufLen);

  If Assigned(BufPtr) then Result:=daveGetU8From(BufPtr) else Result:=0;

end;



//Return the LongInt-value read last from the PLC at the specified address.

//~param Address Address of the requested value

//~param Buffer Pointer to the buffer holding the PLC-data. The internal buffer is used, if Nil (default).

//~param BufOffs Offset-address of the buffer within the address-range of the PLC.

//~param BufLen Length of the buffer in bytes.

//~result The requested value or 0, if the requested address was not found within the buffer.

function TNoDave.GetDInt(Address: Integer; Buffer: Pointer; BufOffs: Integer; BufLen: Integer): LongInt;

var

  BufPtr: Pointer;

begin

  BufPtr:=BufferAt(Address, 4, Buffer, BufOffs, BufLen);

  If Assigned(BufPtr) then Result:=daveGetS32From(BufPtr) else Result:=0;

end;



//Return the LongWord-value read last from the PLC at the specified address.

//~param Address Address of the requested value

//~param Buffer Pointer to the buffer holding the PLC-data. The internal buffer is used, if Nil (default).

//~param BufOffs Offset-address of the buffer within the address-range of the PLC.

//~param BufLen Length of the buffer in bytes.

//~result The requested value or 0, if the requested address was not found within the buffer.

function TNoDave.GetDWord(Address: Integer; Buffer: Pointer; BufOffs: Integer; BufLen: Integer): LongWord;

var

  BufPtr: Pointer;

begin

  BufPtr:=BufferAt(Address, 4, Buffer, BufOffs, BufLen);

  If Assigned(BufPtr) then Result:=daveGetU32From(BufPtr) else Result:=0;

end;



//Return the Float-value read last from the PLC at the specified address.

//~param Address Address of the requested value

//~param Buffer Pointer to the buffer holding the PLC-data. The internal buffer is used, if Nil (default).

//~param BufOffs Offset-address of the buffer within the address-range of the PLC.

//~param BufLen Length of the buffer in bytes.

//~result The requested value or 0, if the requested address was not found within the buffer.

function TNoDave.GetFloat(Address: Integer; Buffer: Pointer; BufOffs: Integer; BufLen: Integer): Double;

var

  BufPtr: Pointer;

begin

  BufPtr:=BufferAt(Address, 4, Buffer, BufOffs, BufLen);

  If Assigned(BufPtr) then Result:=daveGetFloatFrom(BufPtr) else Result:=0;

end;



//Return the Window-Handle (HWND) used for system-calls.

//~result The Window-Handle (HWND).

function TNoDave.GetHandle: THandle;

var

  Parent: TComponent;

begin

  Parent:=Self;

  while Assigned(Parent.Owner) and not (Parent is TWinControl) do Parent:=Parent.Owner;

  if Parent is TWinControl then Result:=TWinControl(Parent).Handle else

  begin

    If FHandle = 0 then FHandle:=AllocateHwnd(Nil);

    Result:=FHandle;

  end;

end;



//Return the SmallInt-value read last from the PLC at the specified address.

//~param Address Address of the requested value

//~param Buffer Pointer to the buffer holding the PLC-data. The internal buffer is used, if Nil (default).

//~param BufOffs Offset-address of the buffer within the address-range of the PLC.

//~param BufLen Length of the buffer in bytes.

//~result The requested value or 0, if the requested address was not found within the buffer.

function TNoDave.GetInt(Address: Integer; Buffer: Pointer; BufOffs: Integer; BufLen: Integer): SmallInt;

var

  BufPtr: Pointer;

begin

  BufPtr:=BufferAt(Address, 2, Buffer, BufOffs, BufLen);

  If Assigned(BufPtr) then Result:=daveGetS16From(BufPtr) else Result:=0;

end;



//Return the Word-value read last from the PLC at the specified address.

//~param Address Address of the requested value

//~param Buffer Pointer to the buffer holding the PLC-data. The internal buffer is used, if Nil (default).

//~param BufOffs Offset-address of the buffer within the address-range of the PLC.

//~param BufLen Length of the buffer in bytes.

//~result The requested value or 0, if the requested address was not found within the buffer.

function TNoDave.GetWord(Address: Integer; Buffer: Pointer; BufOffs: Integer; BufLen: Integer): Word;

var

  BufPtr: Pointer;

begin

  BufPtr:=BufferAt(Address, 2, Buffer, BufOffs, BufLen);

  If Assigned(BufPtr) then Result:=daveGetU16From(BufPtr) else Result:=0;

end;



//Return the description of the return-code in ~[link .LastError LastError].

//~result The description of the return-code.

function TNoDave.GetLastErrMsg: String;

begin

  If FLastError = 0 then Result:='' else Result:=GetErrorMsg(FLastError);

end;



//Scan the MPI-bus for all reachable partners

//~result List with True for available partners and False for unavailable partners.

function TNoDave.ListReachablePartners: TNoDaveReachablePartnersMPI;

type

  TByteList = Array [0..126] of Byte;

  PByteList = ^TByteList;

var

  List: Pointer;

  WasActive: Boolean;

  Index: Integer;

begin

  GetMem(List, SizeOf(TByteList));

  try

    WasActive:=Active;

    Active:=True;

    If Active then

    begin

      daveListReachablePartners(DaveIntf, List);

      Index:=0;

      While Index < 127 do

      begin

        Result[Index]:=(PByteList(List)^[Index] = daveMPIReachable);

        Inc(Index);

      end;

    end else begin

      Index:=0;

      While Index < 127 do

      begin

        Result[Index]:=False;

        Inc(Index);

      end;

    end;

    Active:=WasActive;

  finally

    FreeMem(List);

  end;

end;



//Open the connection to the PLC after the instance is completely loaded from the stream and if Active is True.

procedure TNoDave.Loaded;

begin

  inherited;

  If FActive then

  begin

    FActive:=False;

    Connect;

  end;

end;



//Lock the communication-routines for the current tread.

procedure TNoDave.Lock;

begin

  LockNoDave.Enter;

end;



//Determine the libnodave.dll-code of a protocol

//~param Prot The requested protocol

//~result The libnodave.dll code for the protocol

function TNoDave.ProtCode(Prot: TNoDaveProtocol): Integer;

begin

  Result:=-1;

  Case Prot of

    daveProtoMPI:       Result:=0;

    daveProtoMPI2:      Result:=1;

    daveProtoMPI3:      Result:=2;

    daveProtoMPI4:      Result:=3;

    daveProtoPPI:       Result:=10;

    daveProtoAS511:     Result:=20;

    daveProtoS7Online:  Result:=50;

    daveProtoISOTCP:    Result:=122;

    daveProtoISOTCP243: Result:=123;

    daveProtoIBH:       Result:=223;

    daveProtoIBH_PPI:   Result:=224;

    daveProtoNLPro:     Result:=230;

  end;

end;



//Read the Data specified by the properties ~[link .Area Area], ~[link .DBNumber DBNumber], ~[link .BufOffs BufOffs]

//and ~[link .BufLen BufLen] from the PLC into the buffer.

//~param Buffer Pointer to the buffer for PLC-data. The internal buffer is used, if Nil (default).

procedure TNoDave.ReadBytes(Buffer: Pointer);

begin

  If not Assigned(Buffer) then Buffer:=FBuffer;

  If Active then

  begin

    DoReadBytes(FArea, FDBNumber, FBufOffs, FBufLen, Buffer);

    DoOnRead;

  end;

end;



//Read the specified Data from the PLC into the buffer.

//~param Area Requested PLC-area.

//~param DB Number of requested datablock. Only used, if reading from Datablocks in the PLC.

//~param Start Start-address of the requested data within the address-range of the PLC.

//~param Size Length of the requested PLC-data in bytes.

//~param Buffer Pointer to the buffer for PLC-data. The internal buffer is used, if Nil (default).

procedure TNoDave.ReadBytes(Area: TNoDaveArea; DB, Start, Size: Integer; Buffer: Pointer);

begin

  If not Assigned(Buffer) then

  begin

    FArea:=Area;

    FDBNumber:=DB;

    FBufOffs:=Start;

    FBufLen:=Size;

    ReadBytes(Buffer);

  end else begin

    DoReadBytes(Area, DB, Start, Size, Buffer);

  end;

end;



//Set the property ~[link .Active Active] and call either ~[link .Connect Connect] or ~[link .Disconnect Disconnect].

//depending on the requested value.

//~param V The requested state.

procedure TNoDave.SetActive(V: Boolean);

begin

  If V then Connect else Disconnect;

end;



//Set the property ~[link .Area Area].

//~param V The ~[link TNoDaveArea PLC-Area].

procedure TNoDave.SetArea(V: TNoDaveArea);

begin

  FArea:=V;

  If V in [daveDB, daveDI, daveLocal] then

  begin

    If FDBNumber = 0 then FDBNumber:=1;

  end else FDBNumber:=0;

  If Active and (csDesigning in ComponentState) then ReadBytes;

end;



//Set the property ~[link .BufLen BufLen] and reserve the required memory.

//~param V The length of the buffer in bytes.

procedure TNoDave.SetBufLen(V: Integer);

begin

  If Assigned(FBuffer) then

  try

    FreeMem(FBuffer);

  except

  end;

  FBufLen:=V;

  GetMem(FBuffer, FBufLen);

  If Active and (csDesigning in ComponentState) then ReadBytes;

end;



//Set the property ~[link .BufOffs BufOffs].

//~param V The address within the address-range of the PLC.

procedure TNoDave.SetBufOffs(V: Integer);

begin

  FBufOffs:=V;

  If Active and (csDesigning in ComponentState) then ReadBytes;

end;



//Set the property ~[link .ComPort ComPort].

//~param V The Name of the ComPort.

procedure TNoDave.SetComPort(V: String);

begin

  If FProtocol in [daveProtoMPI, daveProtoMPI2, daveProtoMPI3, daveProtoMPI4, daveProtoPPI, daveProtoAS511] then Disconnect;

  FComPort:=V;

end;



//Set the property ~[link .ComSpeed ComComSpeed].

//~param V The Name of the ComPort.

procedure TNoDave.SetComSpeed(V: TNoDaveComSpeed);

begin

  If FProtocol in [daveProtoMPI, daveProtoMPI2, daveProtoMPI3, daveProtoMPI4, daveProtoPPI, daveProtoAS511] then Disconnect;

  FComSpeed:=V;

end;



//Set the property ~[link .CpuRack CpuRack].

//~param V The rack of the PLC containing the CPU.

procedure TNoDave.SetCpuRack(V: Integer);

begin

  Disconnect;

  FCpuRack:=V;

end;



//Set the property ~[link .CpuSlot CpuSlot].

//~param V The Slot of the PLC containing the CPU.

procedure TNoDave.SetCpuSlot(V: Integer);

begin

  Disconnect;

  FCpuSlot:=V;

end;



//Set the property ~[link .DBNumber DBNumber].

//~param V The requested number of the datablock.

procedure TNoDave.SetDBNumber(V: Integer);

begin

  FDBNumber:=V;

  If Active and (csDesigning in ComponentState) then ReadBytes;

end;



//Set the property ~[link .Interval Interval].

//~param V The Interval for the background-communication in milliseconds. 0 if no background-communication is desired.

procedure TNoDave.SetInterval(V: Cardinal);

begin

  FInterval:=V;

  If Active then

  begin

    If (FInterval > 0) then

    begin

      If not Assigned(ReadThread) and not (csDesigning in ComponentState) then ReadThread:=TNoDaveReadThread.Create(Self);

    end else begin

      If Assigned(ReadThread) then

      begin

        ReadThread.WaitFor;

        FreeAndNil(ReadThread);

      end;

    end;

  end;

end;



//Set the property ~[link .IntfName IntfName].

//~param V The symbolic name of the interface.

procedure TNoDave.SetIntfName(V: String);

begin

  Disconnect;

  FIntfName:=V;

end;



//Set the property ~[link .IntfTimeout IntfTimeout].

//~param V The timeout of the interface in milliseconds.

procedure TNoDave.SetIntfTimeout(V: Integer);

begin

  FIntfTimeout:=V;

  If Active then DaveIntf^.timeout:=FIntfTimeout;

end;



//Set the property ~[link .IPAddress IPAddress].

//~param V The IP-address or name of the TCP/IP partner station.

procedure TNoDave.SetIPAddress(V: String);

begin

  If FProtocol in [daveProtoISOTCP, daveProtoISOTCP243, daveProtoIBH, daveProtoIBH_PPI, daveProtoNLPro] then Disconnect;

  FIPAddress:=V;

end;



//Set the property ~[link .IPPort IPPort].

//~param V The IP-Port of the TCP/IP parter station.

procedure TNoDave.SetIPPort(V: Integer);

begin

  If FProtocol in [daveProtoISOTCP, daveProtoISOTCP243, daveProtoIBH, daveProtoIBH_PPI, daveProtoNLPro] then Disconnect;

  FIPPort:=V;

end;



//Set the property ~[link .MPILocal MPILocal].

//~param V The local MPI-address for the MPI communication.

procedure TNoDave.SetMPILocal(V: Integer);

begin

  If FProtocol in [daveProtoMPI, daveProtoMPI2, daveProtoMPI3, daveProtoMPI4, daveProtoPPI, daveProtoIBH, daveProtoIBH_PPI, daveProtoNLPro] then Disconnect;

  FMPILocal:=V;

end;



//Set the property ~[link .MPIRemote MPIRemote].

//~param V The remote MPI-address for the MPI communication.

procedure TNoDave.SetMPIRemote(V: Integer);

begin

  If FProtocol in [daveProtoMPI, daveProtoMPI2, daveProtoMPI3, daveProtoMPI4, daveProtoPPI, daveProtoIBH, daveProtoIBH_PPI, daveProtoNLPro] then Disconnect;

  FMPIRemote:=V;

end;



//Set the property ~[link .MPISpeed MPISpeed].

//~param V The ~[link TNoDaveSpeed speed] of the MPI-bus.

procedure TNoDave.SetMPISpeed(V: TNoDaveSpeed);

begin

  If FProtocol in [daveProtoMPI, daveProtoMPI2, daveProtoMPI3, daveProtoMPI4, daveProtoPPI, daveProtoIBH, daveProtoIBH_PPI, daveProtoNLPro] then Disconnect;

  FMPISpeed:=V;

end;



//Set the property ~[link .Protocol Protocol].

//~param V The requested ~[link TNoDaveProtocol Protocol].

procedure TNoDave.SetProtocol(V: TNoDaveProtocol);

begin

  Disconnect;

  FProtocol:=V;

  If FProtocol in [daveProtoIBH, daveProtoIBH_PPI] then FIPPort:=1099;

  If FProtocol in [daveProtoNLPro] then FIPPort:=7777;

  If FProtocol in [daveProtoISOTCP, daveProtoISOTCP243] then FIPPort:=102;

end;



//Swap the byte-order in a 16-bit value.

//~param Value The value for the conversion.

//~result The converted value.

function TNoDave.Swap16(Value: SmallInt): SmallInt;

begin

  Result:=daveSwapIed_16(Value);

end;



//Swap the byte-order in a 32-bit value.

//~param Value The value for the conversion.

//~result The converted value.

function TNoDave.Swap32(Value: Integer): Integer;

begin

  Result:=daveSwapIed_32(Value);

end;



//Unlock the communication-routines for other threads.

procedure TNoDave.Unlock;

begin

  LockNoDave.Leave;

end;



//Write a Bit-value into the PLC at the specified address without changing the properties of the TNoDave-instance.

//~param Area Requested PLC-area.

//~param DB Number of requested datablock. Only used, if writing into datablocks of the PLC.

//~param Address Byte-address of the value

//~param Bit Bit-address of the value

//~param Value Value to write into the PLC.

procedure TNoDave.WriteBit(Area: TNoDaveArea; DB, Address, Bit: Integer; Value: Boolean);

var

  Output: LongInt;

begin

  try

    LockNoDave.Enter;

    If Value then Output:=1 else Output:=0;

    FLastError:=daveWriteBits(DaveConn, AreaCode(Area), DB, (Address*8)+Bit, 1, @Output);

  except

    On E: Exception do DoOnError('Error in function TNoDave.WriteBit: ' + E.Message);

  end;

  LockNoDave.Leave;

end;



//Write the buffer into the PLC at the address specified by the properties ~[link .Area Area], ~[link .DBNumber DBNumber],

//~[link .BufOffs BufOffs] and ~[link .BufLen BufLen].

//~param Buffer Pointer to the buffer for PLC-data. The internal buffer is used, if Nil (default).

procedure TNoDave.WriteBytes(Buffer: Pointer);

begin

  If not Assigned(Buffer) then Buffer:=FBuffer;

  If Active then

  begin

    DoWriteBytes(FArea, FDBNumber, FBufOffs, FBufLen, Buffer);

    DoOnWrite;

  end;

end;



//Write the buffer into the PLC at the specified address after setting up the properties with the given values.

//~param Area Requested PLC-area. Changes the property ~[link .Area Area].

//~param DB Number of requested datablock. Changes the property ~[link .DBNumber DBNumber]. Only used, if writing into datablocks of the PLC.

//~param Start Start-address of the buffer within the address-range of the PLC. Changes the property ~[link BufOffs].

//~param Size Length of the buffer in bytes. Changes the property ~[link .BufLen BufLen].

//~param Buffer Pointer to the buffer for PLC-data. The internal buffer is used, if Nil (default).

procedure TNoDa