# LibNoDave + S7Online



## Lazarus™ (7 April 2009)

Hallo,

ich habe folgende Routine:


```
procedure TfrmMain.ConnectPlc;
begin
  sbMain.Panels[0].Text := 'Verbindung wird hergestellt';
  sbMain.Update;
  DaveOsSerialType.RFD := {OpenSocket(7777, '10.90.0.243');} OpenS7Online('S7ONLINE', Self.Handle);
  DaveOsSerialType.WFD := DaveOsSerialType.RFD;
  if (DaveOsSerialType.RFD > 0) then begin
    DaveInterface := DaveNewInterface(DaveOsSerialType, 'IF1', LocalMPI, {DaveProtoNlPro} DaveProtoS7Online, DaveSpeed187K);
    DaveInterface^.Timeout := 20000;
    if (DaveInitAdapter(DaveInterface) = 0) then begin
      DaveConnection := DaveNewConnection(DaveInterface, PlcMPI, Rack, Slot);
      if (DaveConnectPLC(DaveConnection) = 0) then begin
        btnConnection.Caption := 'Von SPS trennen';
        sbMain.Panels[0].Text := 'Verbindung hergestellt';
        Connected := True;
       end else
        Disconnect;
    end else
      Disconnect;
  end;
end;
```

Mit der Ausgeklammerten Version (NLPro) geht es einwandfrei, aber mit der
S7Online Version nicht. Funktioniert das S7Online generell ???
Ich würde gerne das S7Online nehmen, damit das Tool dann allgemeingültig ist und immer zu verwenden, egal ob TCP/IP oder Adapter etc.

Wenn dieses problem gelöst ist, wird es bald ein DB-Backup :TOOL: hier zum Download geben, ich denke das kann man ab und an mal brauchen.


----------



## Ralle (7 April 2009)

Hier mal meine Routine zum Connect und Disconnect. Vielleicht kannst du da ja was rauslesen.  S7Online funktioniert bei mir. Step7 muß installiert sein. Allerdings hab ich das bisher immer nur mit einer SPS gleichzeitig hinbekiommen. Wollte ich 2 oder 3 SPS mit S7Online öffnen, bekam ich für jede SPS immer die Werte der ersten SPS, die Verbindung hergestellt hatte.


```
procedure Connect;
var
  Address: string;
  I, FIPPort: Integer;
begin
  //MPI libnodaveS7

  for I := 1 to SimDatenListe.SimN do
  begin
    if ((NOT DaveInterface.Remote[I].IsOpen) AND SimDatenListe.SimEintrag[I].ComSimState AND NOT (SimDatenListe.SimEintrag[I].Driver = AS511_DLL)) then
    begin
      Case SimDatenListe.SimEintrag[I].Protocol of
        Ord(daveProtoMPI), Ord(daveProtoMPI2), Ord(daveProtoMPI3), Ord(daveProtoMPI4), Ord(daveProtoPPI):
          begin
            Address:= ComPort_to_Str(SimDatenListe.SimEintrag[I].ComPort) + #0;
            DaveInterface.Remote[I].DaveFDS.rfd:=SetPort(@Address[1], '38400', 'O');
          end;
        Ord(daveProtoAS511):
          begin
            Address:= ComPort_to_Str(SimDatenListe.SimEintrag[I].ComPort) + #0;
            DaveInterface.Remote[I].DaveFDS.rfd:=SetPort(@Address[1], '9600', 'E');
            DaveInterface.Remote[I].CpuRack := 0;
            DaveInterface.Remote[I].CpuSlot := 0;
          end;
        Ord(daveProtoISOTCP), Ord(daveProtoISOTCP243), Ord(daveProtoIBH), Ord(daveProtoIBH_PPI), Ord(daveProtoNLPro):
          begin
            Address:= SimDatenListe.SimEintrag[I].IPAddress + #0;
            FIPPort := SetIPPort(SimDatenListe.SimEintrag[I].Protocol);
            DaveInterface.Remote[I].IPPort := FIPPort;
            DaveInterface.Remote[I].DaveFDS.rfd:=OpenSocket(FIPPort, @Address[1]);
          end;
       [COLOR="Red"] Ord(daveProtoS7Online):
          begin
            Address:= 'S7ONLINE'+ #0;
            DaveInterface.Remote[I].DaveFDS.rfd:=OpenS7Online(@Address[1], Application.MainForm.Handle);[/COLOR]
          end;
      end;
    end;

    if ((NOT DaveInterface.Remote[I].IsOpen) AND SimDatenListe.SimEintrag[I].ComSimState AND NOT (SimDatenListe.SimEintrag[I].Driver = AS511_DLL)) then
    begin
      DaveInterface.Remote[I].DaveFDS.wfd := DaveInterface.Remote[I].DaveFDS.rfd;
      if DaveInterface.Remote[I].DaveFDS.rfd >= 0 then
      begin
        Address:='IFS'+IntToStr(I) + #0;
        DaveInterface.MPIlocal := SimDatenListe.SimEintrag[I].MPILocal;
        DaveInterface.Remote[I].Protocol := TNodaveProtocol(SimDatenListe.SimEintrag[I].Protocol);
        DaveInterface.MPISpeed := TNodaveSpeed(SimDatenListe.SimEintrag[I].MPISpeed);
        DaveInterface.Remote[I].DaveIntf:=daveNewInterface(DaveInterface.Remote[I].DaveFDS, @Address[1], DaveInterface.MPILocal, ProtCode(DaveInterface.Remote[I].Protocol), Ord(DaveInterface.MPISpeed));
        DaveInterface.Remote[I].DaveIntf^.timeout:=SimDatenListe.SimEintrag[I].Timeout;
        DaveInterface.Remote[I].IntfError:=daveInitAdapter(DaveInterface.Remote[I].DaveIntf);
        if DaveInterface.Remote[I].IntfError = 0 then
        begin
          if ((not DaveInterface.Remote[I].RemoteActive) AND SimDatenListe.SimEintrag[I].ComSimState) then
          begin
            DaveInterface.Remote[I].DaveConn:=daveNewConnection(DaveInterface.Remote[I].DaveIntf, DaveInterface.Remote[I].MPIRemote, DaveInterface.Remote[I].CpuRack, DaveInterface.Remote[I].CpuSlot);
            DaveInterface.Remote[I].LastError:=daveConnectPLC(DaveInterface.Remote[I].DaveConn);
            DaveInterface.Remote[I].RemoteActive:=(DaveInterface.Remote[I].LastError = 0);
//            If DaveInterface.Remote[I].Active then
//              ReadBytes
//            else
//              DoOnError(daveStrerror(FLastError));
            Last_DaveError_Str := daveStrerror(DaveInterface.Remote[I].LastError);
            InitPG1Fehler := DaveInterface.Remote[I].LastError;
            DaveInterface.Remote[I].IntfActive := True;
          end;
        end;
      end
      else
      begin
        InitPG1Fehler := 1001;//DaveInterface.Remote[I].DaveFDS.rfd;
        DaveInterface.Remote[I].LastError := 1001;//DaveInterface.Remote[I].DaveFDS.rfd;
        Last_DaveError_Str := 'kein Interface';
      end;
    end
    else
    begin
//      InitPG1Fehler := DaveInterface.Remote[I].DaveFDS.rfd;
//      DaveInterface.Remote[I].LastError := DaveInterface.Remote[I].DaveFDS.rfd;
//      Last_DaveError_Str := daveStrerror(DaveInterface.Remote[I].LastError);
    end;
  end;
end;

//Close the connection to the PLC.
procedure Disconnect;
var
  I: Integer;
begin
  for I := 1 to SimDatenListe.SimN do
  begin
    if DaveInterface.Remote[I].IntfActive then
    begin
//      if DaveInterface.Remote[I].IntfError <> 0 then
      begin
        try
//          if DaveInterface.Remote[I].LastError = 0 then   //wurde mit der neuen Version von Axel mit Timout für TCP beseitigt!!!!!
            DaveInterface.Remote[I].IntfError := daveDisconnectPLC(DaveInterface.Remote[I].DaveConn);
          daveFree(DaveInterface.Remote[I].DaveConn);
          DaveInterface.Remote[I].IntfError := daveDisconnectAdapter(DaveInterface.Remote[I].DaveIntf);
          daveFree(DaveInterface.Remote[I].DaveIntf);
          if DaveInterface.Remote[I].Protocol <> daveProtoS7Online then
          begin
            if ((DaveInterface.Remote[I].Protocol = daveProtoISOTCP)
               or (DaveInterface.Remote[I].Protocol = daveProtoISOTCP243)
               or (DaveInterface.Remote[I].Protocol = daveProtoIBH)
               or (DaveInterface.Remote[I].Protocol = daveProtoIBH_PPI)
               or (DaveInterface.Remote[I].Protocol = daveProtoNLPro)) then
              closeSocket(DaveInterface.Remote[I].DaveFDS.rfd)
            else
              closePort(DaveInterface.Remote[I].DaveFDS.rfd);
          end
          else
          begin
            [COLOR="Red"]closeS7online(DaveInterface.Remote[I].DaveFDS.rfd);[/COLOR]
          end;
        except
//        On E: Exception do DoOnError('Error in function TNoDave.Disconnect: ' + E.Message);
        end;
        DaveInterface.Remote[I].IntfActive := False;
        DaveInterface.Remote[I].RemoteActive := False;
      end;
    end;
  end;
end;
```


----------



## Lazarus™ (17 April 2009)

@Ralle:  Yepp, das funktioniert. Meine Version war ja in etwa genau so gemacht ...    Das Problem lag woanders ...

@Zottel: Schlechte Nachrichten ...   Ich glaube mit Delphi 2009 funktioniert die Library nicht mehr. Ein Connect war absolut unmöglich.
Die selbe Applikation unter Delphi 2007 compiliert rennt :!:
Warum das so ist, kann ich nicht sagen, aber scheinbar ist der Compiler soweit verändert, das eben die LibNoDave nicht mehr geht.
Eventuell hat das damit zu tun, das einiges verändert wurde betreffend Unicode. Vielleicht haut das mit den Strings und PChars so nicht mehr hin...
Ist aber nur ein Verdacht


----------



## Question_mark (17 April 2009)

*Unicode*

Hallo,



			
				Lazarus schrieb:
			
		

> Eventuell hat das damit zu tun, das einiges verändert wurde betreffend Unicode. Vielleicht haut das mit den Strings und PChars so nicht mehr hin...
> Ist aber nur ein Verdacht



Und diesen Verdacht teile ich mit Dir 

Gruß

Question_mark


----------



## Lazarus™ (22 April 2009)

Hallo QM,

das du meinen Verdacht mit mir teilst, finde ich ja irgendwie doof 

Ich hoffte wirklich das es wie sonst auch an mir liegt, aber ich glaube diesmal eben auch nicht ...    Sehr schlecht ...

Werde dann wohl umsteigen/downgraden auf ein altes Delphi ...

Vielleicht gibt es ja bald mal eine neue Version von LibNoDave


----------



## Zottel (22 April 2009)

Lazarus™ schrieb:


> Vielleicht gibt es ja bald mal eine neue Version von LibNoDave


Habe kein Delphi, kann daher nicht nachprüfen.
Und was mache ich dann?
Umstellen auf neu und alle mit alt ärgern sich?
Oder 2 Versionen?
Es müßte meiner Meinung nach reichen, daß der Anwender nodave.pas neu kompiliert. Falls Pchars jetzt auf Unicode zeigen müßte man halt ein PByte definieren.


----------



## Question_mark (22 April 2009)

*Unicode*

Hallo,



			
				Zottel schrieb:
			
		

> Falls Pchars jetzt auf Unicode zeigen müßte man halt ein PByte definieren.



Naja, diese Auffassung kann ich nicht so richtig teilen. Unicode heisst zwei Byte pro Zeichen, im Gegensatz zum altbekannten Ansicode mit 1 Char = 1 Byte.
Viele Delphianer haben lautstark die Einführung von Unicode zur Internationalisierung Ihrer Anwendungen von Embarcadero (früher Borland und auch GodeGear) in den entsprechenden Foren eingefordert. Das ist mit D2009 nun endlich geschehen, als Folge davon darf ich nochmal einige tausend Euronen in aktualisierte Fremdkomponenten von Kassl, DevArt, Steema Software, TMS und Konsorten investieren...



			
				Lazarus schrieb:
			
		

> Ich hoffte wirklich das es wie sonst auch an mir liegt, aber ich glaube diesmal eben auch nicht



Nee, liegt nicht an Dir, die Umstellung von Delphi auf Unicode hat wirklich interessante Effekte  

Gruß

Question_mark


----------



## Lazarus™ (24 April 2009)

Zottel:  Danke, du hast recht. *ACK*

Ich habe in der NoDave.pas:

- ALLE pChar gegen pAnsiChar
- ALLE String gegen AnsiString
- ALLE Char gegen AnsiChar

getauscht und funzt ...

Das Selbe auch in der NoDaveComponent und alles rennt wieder ...


Danke dir ...


----------



## Lazarus™ (28 April 2009)

@Zottel: Ich habe das alles jetzt etwas genauer getestet. Mit den o.g. Änderungen läuft alles korrekt, soweit ich das getestet habe.
Eine Übersetzung mit einem "älteren" Delphi 2007 war genauso möglich.

Eventuell könnte man die Änderungen ja direkt mit in dein Librarypaket stecken. Zumindest habe ich die Dateien mal mit angehängt ...


----------



## afk (4 Mai 2009)

Lazarus™ schrieb:


> Eventuell könnte man die Änderungen ja direkt mit in dein Librarypaket stecken. Zumindest habe ich die Dateien mal mit angehängt ...


Danke, ich schau mir das mal an, und wenn möglich pflege ich es dann in meinen aktuellen Stand der Komponente ein.


Gruß Axel


----------



## marcengbarth (4 Mai 2009)

Hallo,

ich hänge mich hier einfach mal an.
Hab der Komponente auch noch 3 Kleinigkeiten hinzugefügt.

1. 

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

```
function TNoDave.GetString(Address: Integer; Buffer: Pointer; BufOffs: Integer; BufLen: Integer): AnsiString;
```
3.

```
procedure TNoDave.WriteString(Address: Integer; Value: AnsiString; Count: Byte);
```


----------



## Lazarus™ (5 Mai 2009)

Hallo ...

schön, das sich was tut.  Gibt es denn eventuell demnächst eine 
Aktualisierung der Komponente ????

Ein paar "neue" funktionen fehlen inzwischen in der Komponente.
Von daher wäre eine Überarbeitung schön. Wenn du (AFK) eventuell
etwas unterstützung brauchen kannst, dann sag bescheid...  Ich kann sicher 
etwas Zeit freischaufeln dafür 

Und vielen Dank für diese schöne Komponente...  Dieser Dank geht natürlich
auch an Zottel für die hervorragende Arbeit ...


----------



## afk (5 Mai 2009)

Lazarus™ schrieb:


> - ALLE pChar gegen pAnsiChar
> - ALLE String gegen AnsiString
> - ALLE Char gegen AnsiChar


Habe ich in meiner Version nachgezogen, danke für die Suche nach der Lösung.



marcengbarth schrieb:


> ```
> procedure TNoDave.ReadManyBytes(Area: TNoDaveArea; DB, Start, Size: Integer; Buffer: Pointer);
> function TNoDave.GetString(Address: Integer; Buffer: Pointer; BufOffs: Integer; BufLen: Integer): AnsiString;
> procedure TNoDave.WriteString(Address: Integer; Value: AnsiString; Count: Byte);
> ```


Der Aufruf von daveReadManyBytes in der der Methode DoReadManyBytes hat eigentlich keinen Sinn gemacht, da in der Methode DoReadManyBytes schon immer bei Bedarf in mehrere Blöcke geteilt wurde. Darum habe ich die Methoden vor geraumer Zeit wieder rausgeschmissen, ReadManyBytes macht daher auch keinen Sinn mehr.

GetString und WriteString hab ich mir angeschaut, habe mich dann aber entschlossen, das etwas anders zu implementieren:

```
function TNoDave.GetString(Address: Integer; Buffer: Pointer; BufOffs, BufLen: Integer): AnsiString;
var
  BufPtr: Pointer;
  MaxLen: Integer;
  ActLen: Integer;
  BufIdx: Integer;
begin
  Result:='';
  If BufOffs = -1 then BufOffs:=FBufOffs;
  If BufLen = -1 then BufLen:=FBufLen;
  BufPtr:=BufferAt(Address, 1, Buffer, BufOffs, BufLen);
  If Assigned(BufPtr) then MaxLen:=daveGetU8From(BufPtr) else MaxLen:=0;
  If MaxLen > 0 then
  begin
    BufPtr:=BufferAt(Address + 1, 1, Buffer, BufOffs, BufLen);
    If Assigned(BufPtr) then ActLen:=daveGetU8From(BufPtr) else ActLen:=0;
    If ActLen > MaxLen then Exit;
    BufIdx:=0;
    While BufIdx < ActLen do
    begin
      BufPtr:=BufferAt(Address + 2 + BufIdx, 1, Buffer, BufOffs, BufLen);
      If Assigned(BufPtr) then Result:=Result + Chr(daveGetU8From(BufPtr)) else
      begin
        Result:='';
        Exit;
      end;
      Inc(BufIdx);
    end;
  end;
end;

procedure TNoDave.WriteString(Address: Integer; Value: AnsiString);
var
  MaxLen: Byte;
  ActLen: Byte;
  Dummy: AnsiString;
begin
  MaxLen:=0;
  ReadBytes(FArea, FDBNumber, Address, 1, @MaxLen);
  ActLen:=Length(Value);
  If ActLen > MaxLen then ActLen:=MaxLen;
  Dummy:=Chr(ActLen) + Copy(Value, 1, ActLen);
  DoWriteValue(Address + 1, MaxLen + 1, @Dummy);
end;
```
Dadurch ist bei WriteString zwar ein zusätzlicher Lesezugriff erforderlich (langsamer), allerdings wird dafür verhindert, daß die Struktur eines Datenbausteins vom PC aus "kaputtgeschrieben" wird.

Ich hoffe, daß sich da kein Fehler eingeschlichen hat, da ich es bei mir hier nicht so auf die Schnelle testen kann.


Gruß Axel


----------



## afk (5 Mai 2009)

Lazarus™ schrieb:


> Gibt es denn eventuell demnächst eine
> Aktualisierung der Komponente ????


Ist schon in Arbeit.



Lazarus™ schrieb:


> Ein paar "neue" funktionen fehlen inzwischen in der Komponente.


Welche ? 



Lazarus™ schrieb:


> Wenn du (AFK) eventuell etwas unterstützung brauchen kannst, dann sag bescheid...


Fehler finden, Anregungen liefern, Tipps geben ...


Gruß Axel


----------



## Lazarus™ (5 Mai 2009)

afk schrieb:


> Fehler finden, Anregungen liefern, Tipps geben ...



Das geht los ...


----------

