Komunikace s PLC pres TCP/IP

Slavomir Skopalik skopalik na elektlabs.cz
Čtvrtek Prosinec 26 17:26:22 CET 2013


Dobry den,
pokud chcete pouzit TCP server misto jednoho konce seriove linky, tak Vas
ceka nekolik
nesnazi:

1. TCP server obecne muze mit navazano vice spojeni, nez jedno.
  Resi se to tak, ze je jeden thread, kde je tzv. listener, jenz prijima a
spojeni a predava jej do tzv. workeru, jenz
  pak resi vlastni komunikaci. Neco jako inetd v linuxu.

2. TCP ma delsi odezvy, nez RSxxx, je treba s tim pocitat

3. Doporucuji vsechny TCP funkce volat s vlastnim TimeOutem, aby recovery
spojeni bylo pod kontrolou.

4. Zvazit nastaveni NoDelay, viz kod nize

Ukazka meho kodu (pouziva synapse + vlastni RS232):

Berte to prosim jen jako nastrel toho, jak to muze vypadat, slozitost je
dana pozadavku na univerzalnost
knihoven (RS, TCP, UDP, server x klient).

function TComThread.WriteBuf(const Data; Count: Integer;
TCPTimeOut:integer=500): Integer;
var
  B: DWORD; s:AnsiString;
  tp: TThreadPriority;
begin
  Result := -1;
  if FTCP then begin
    if TCPConnect then begin
      FSocket.SetSendTimeout(TCPTimeOut);
      result:=FSocket.SendBuffer(@Data,Count);
      if FSocket.LastError<>0 then begin
        DoLog('TCP: Write:'+AnsiString(FSocket.LastErrorDesc),lgTx);
        TCPClose;
      end;
    end;
   end else begin
    if FDefered then DeferedConnect;
    if FRTSIsDirection then
      EscapeCommFunction(ComHandle,SETRTS);
    if WriteFile(FComHandle, Data, Count, B, nil) then Result := B else
DeferedClose;
    if FRTSIsDirection then begin
      tp:=Priority;
      Priority:=tpTimeCritical;
      FlushFileBuffers(ComHandle);
 
Sleep(Max(1,Ceil((1000*BitsPerChar/TcmBaudrateToBaudRate[BaudRate])))); //
wait for last char in Tx Buffer
      EscapeCommFunction(ComHandle,CLRRTS);
      Priority:=tp;
    end;
  end;
  if (result=Count)and EnableLog then begin
    SetLength(s,result);
    move(Data,s[1],result);
    DoLog('Tx:'+s,lgTx);
  end;
end;

function TComThread.TryReadBuf(var Data; Count:
Integer;TCPTimeOut:integer=1000): Integer;
var
  B: DWORD; s:AnsiString;
begin
  if FTCP then begin
     TCPConnect;
     result:=FSocket.RecvBufferEx(@data,Count,TCPTimeOut);
   end else begin
    if ReadFile(FComHandle, Data, Count, B, nil) then Result := B
     else Result := -1;
  end;
  if EnableLog then begin
    if result>0 then begin
      SetLength(s,result);
      move(Data,s[1],result);
      DoLog(Format('Rx(%d):',[result])+string(s),lgRx);
     end;
  end;
end;

function TComThread.SetNoDelay(NoDelay:boolean): boolean;
var flag:LongInt;
begin
  result:=false;
  if not TCP then exit;
  flag:=ord(Nodelay);
 
result:=synsock.SetSockOpt(Socket.Socket,IPPROTO_TCP,TCP_NODELAY, na flag,sizeo
f(flag))=0;
end;

A jeste ukazka listeneru:

procedure TComThread.MultiServerListener;
type TCTC=class of TComThread;
var p:TComThread; c:TCTC;
{$ifdef synapse}
  s,prot,user,pass,host,port,path,para:string;
  sock:TTCPBlockSocket;
  hSock:TSocket;
{$endif}
begin
  SetThreadName('Listener:'+ClassName+':'+DeviceName);
 
s:=copy(FDeviceName,pos(_TCPServer,FDeviceName)+Length(_TCPServer),MaxInt);
  ParseURL(s,prot,user,pass,host,port,path,para);
  while not Terminated do begin
    Event.ResetEvent;
    if FServerSocket.Socket=INVALID_SOCKET then begin
      FServerSocket.Bind(host,port);
      if FServerSocket.LastError<>0 then begin
        DoLog(Format('Binding socket %s:%s error:
%s',[host,port,FServerSocket.LastErrorDesc]),lgErrorLevel1);
        FServerSocket.CloseSocket;
        Event.WaitFor(2000);
        continue;
      end;
      FServerSocket.Listen;
    end;
    if FServerSocket.CanRead(500) then begin
      c:=TCTC(self.ClassType);
      hSock:=FServerSocket.Accept;
      if FWorkersList.Count>80 then begin
        sock:=TTCPBlockSocket.create;
        sock.Socket:=hSock;
        sock.AbortSocket;
        sock.Free;
        DoLog('Maximum worker threads reached, incoming connection
Aborted',lgErrorLevel1);
      end else begin
        p:=c.Create(_TCPSocket+IntToStr(hSock));
        p.Assign(self);   // copy of communication setup
        SO.Enter;
        FWorkersList.Add(p);
        SO.Leave;
        p.Event.SetEvent;
        sleep(20);        // prevet too many thread start in one moment
        DoLog('TCP Accept
connection:'+AnsiString(p.Socket.GetRemoteSinIP+':'+IntToStr(p.Socket.GetRem
oteSinPort)),lgConnect);
      end;
    end;
    SO.Enter;
    try
      FWorkersList.FreeTerminated;
      FConnected:=FWorkersList.Count>0;
    finally
      SO.Leave;
    end;
  end;
end;

Slavek

Ing. Slavomir Skopalik
Jednatel spolecnosti
Elekt Labs s.r.o.
Sber a vyhodnoceni dat ze stroju a laboratori 
systemem MASA (http://www.elektlabs.cz/m2demo)
----------------------------------------------
Adresa:
Elekt Labs s.r.o.
Chaloupky 158
783 72 Velky Tynec
Czech Republic
----------------------------------------------
Mobil: +420 724 207 851
icq:199 118 333
skype:skopaliks
e-mail:skopalik na elektlabs.cz
http://www.elektlabs.cz
 





> -----Original Message-----
> From: Hw-list [mailto:hw-list-bounces na list.hw.cz] On Behalf 
> Of Milan Kratochvíl
> Sent: Thursday, December 26, 2013 4:28 PM
> To: HW-news
> Subject: Re: Komunikace s PLC pres TCP/IP
> 
> 
> Jsem doma takže upřesnění, místo třídy TBlockSerial použít třídu 
> TSocket. Krom nastavení mají tyto třídy hodně stejných funkcí 
> takže je 
> nebudete muset přepisovat. Krásný příklad serveru je v Synapsi přímo 
> Synapse\source\demo\httpserv kde se nastaví na poslouchání na nějakém 
> portu (v demu 80) a potom samostatné thready obsluhují jednotlivá 
> příchozí spojení. Pokud z toho threadu vyhážete tu obsluhu html 
> protokolu a dáte tam to co máte na sériovém portu tak by to 
> mohlo chodit. Milan
> 
> Dne 26.12.2013 10:35, Milan Kratochvíl napsal(a):
> > Tak to by mělo být pro vás velice jednoduché. Autor někde píše, že
> > stačí nahradit třídu TSerial třídou THttp (jestli si ty třídy dobře 
> > pamatuji). Nemám to u sebe a tak to píšu z hlavy. Jinak když to 
> > stahnete tak je tam pár příkladů podle kterých se to dá udělat.
> > Milan
> >
> > Dne 26.12.2013 8:47, Martin Záruba napsal(a):
> >> Ja v to doufal, pouze naprosto nevim jak. Nemate nejaky priklad? Ma
> >> se to
> >> chovat uplne stejne jako puvodni seriovy port, pouze bych 
> potreboval, 
> >> aby v
> >> rezimu server umel dat informaci, ze klient se pokousi o navazani 
> >> spojeni.
> >>
> >> Martin Zaruba
> >> ----- Original Message -----
> >> From: "Milan Kratochvíl" <krata.milan na seznam.cz>
> >> To: "HW-news" <hw-list na list.hw.cz>
> >> Sent: Thursday, December 26, 2013 7:23 AM
> >> Subject: Re: Komunikace s PLC pres TCP/IP
> >>
> >>
> >> Zdravim
> >> Použijte opět projekt Synapse. Umí jak klienta tak server, má dost 
> >> příkladů a v Lazaru funguje. Milan
> >>
> >> Dne 26.12.2013 6:53, Martin Záruba napsal(a):
> >>> Pred rokem mi pan Zdenek Aster velmi pomohl s informaci, jak v 
> >>> Delphi/Lazarus udelat komunikaci s PLC pres seriovy port pomoci 
> >>> knihovny synaser. Vse krasne funguje, ale nyni se ukazuje nutna 
> >>> komunikaci na vetsi
> >>> vzdalenost. Chci k tomu pouzit XPort nebo WiPort. Na 
> strane PLC mi vse
> >>> funguje a pokud na strane PC pouziji portredirector, funguje to 
> >>> (vetsinou)
> >> i
> >>> na strane PC. Zkusil jsem nekolik portredirectoru, ten od 
> Lantronixu 
> >>> je zcela nepouzitelny, nektere dalsi s obtizemi (PLC musi 
> pracovat 
> >>> jako klient - nema verejnou IP). Nejradeji bych proto komumikoval 
> >>> primo z vlastniho programu bez portredirectoru. To umi napr. 
> >>> vyvojove prostredi k PLC, kde si lze zvolit bud seriovy port nebo 
> >>> Ethernet a prislusny port. V
> >>> rezimu server pak program ceka na pozadavek od klienta, 
> vypise jeho 
> >>> IP a
> >> pak
> >>> jiz staci jen povolit komunikaci. Zjevne je to zcela standardni,
> >>> protoze i
> >>> kdyz je vyvojove prostredi k PLC, navazalo komunikaci s WiPortem 
> >>> Lantronix
> >>> bez problemu.
> >>> Jak ale tuto komunikaci udelat ve vlastnim programu? 
> Jakou knihovnu pro
> >>> komunikaci pres IP pouzit v Delphi/Lazarus?
> >>>
> >>> Martin Zaruba
> >>>
> >>> _______________________________________________
> >>> HW-list mailing list  -  sponsored by www.HW.cz Hw-list na list.hw.cz
> >>> http://list.hw.cz/mailman/listinfo/hw-list
> >>>
> >> _______________________________________________
> >> HW-list mailing list  -  sponsored by www.HW.cz Hw-list na list.hw.cz
> >> http://list.hw.cz/mailman/listinfo/hw-list
> >>
> >> _______________________________________________
> >> HW-list mailing list  -  sponsored by www.HW.cz Hw-list na list.hw.cz
> >> http://list.hw.cz/mailman/listinfo/hw-list
> >>
> >
> > _______________________________________________
> > HW-list mailing list  -  sponsored by www.HW.cz Hw-list na list.hw.cz
> > http://list.hw.cz/mailman/listinfo/hw-list
> >
> 
> _______________________________________________
> HW-list mailing list  -  sponsored by www.HW.cz 
> Hw-list na list.hw.cz http://list.hw.cz/mailman/listinfo/hw-list
> 



Další informace o konferenci Hw-list