[C] PLC <--> Linux communicatie dmv Modbus/TCP

Pagina: 1
Acties:
  • 267 views sinds 30-01-2008
  • Reageer

  • MadMan81
  • Registratie: April 2000
  • Laatst online: 18-04 15:42
Ik ben voor mijn stage bezig een programma te maken dat kan communiceren met een PLC over Modbus/TCP. Het doel is dat PLC's er straks mee kunnen gaan verbinden om data naar toe te sturen. Het is me al gelukt om dmv het programma een register te schrijven op de PLC. Het lukt me alleen niet om de PLC een 'register' in mijn programma te laten schrijven. Ik ontvang wel de data van de PLC en kan die ook vertalen, maar het modbus protocol vereist dat ik daarna weer het zelfde pakket terug stuur om de andere kant te laten weten dat het gelukt is. En daar gaat het fout :|

Nu geeft de plc steeds error 6003, wat staat voor "Unexpected FIN or RST".

Als ik met een sniffer op de lijn kijk zie ik in het geval dat ik wat stuur:
code:
1
2
3
4
5
6
7
8
9
10
11
Bron      Doel: inhoud

Prog      PLC : [SYN]
PLC       Prog: [SYN, PSH, ACK]
Prog      PLC : [ACK]
Prog      PLC : Write multiple registers
PLC       Prog: Write multiple registers
Prog      PLC : [ACK]
Prog      PLC : [FIN, ACK]
PLC       Prog: [FIN, PSH, ACK]
Prog      PLC : [ACK]

Echter als ik de PLC in mijn programma laat schrijven, dan zie ik dit:
code:
1
2
3
4
5
6
7
8
9
10
Bron      Doel: inhoud

PLC       Prog: [SYN]
Prog      PLC : [SYN,ACK]
PLC       Prog: Write multiple registers
Prog      PLC : [ACK]
Prog      PLC : Write multiple registers
Prog      PLC : [FIN, ACK]
Prog      PLC : [TCP Retransmission] Write multiple registers
PLC       Prog: [RST, ACK]


Zoals te zien is wordt gelijk na de respons een FIN gestuurd naar de PLC, terwijl de PLC volgens mij eerst een ACK en daarna een FIN,ACK wil versturen.

Iemand enig idee wat het zou kunnen zijn? Of hoe ik die FIN/ACK etc kan beinvloeden? Ik heb het vermoede dat het te maken heeft met de mannier waarom de socket wordt aangemaakt..

Ik gebruik GCC versie 3.2.3 op een Fedora Core 1 bak met kernel 2.4.22

Cupra Born


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-05 16:53
Modbus/TCP is een normale TCP verbinding, met daarop een aangepaste Modbus versie.
Als het aan het TCP protocol zou liggen zou je ook met andere PC bijvoorbeeld geen verbinding kunnen maken.
Kan het zijn dat de PLC een bepaalde turnaround time heeft, dat je niet direct nadat je een frame ontvangt van de PLC een frame mag terugsturen, maar bv een sleep moet invoegen ?

Kijk eens naar de timeout instellingen van de PLC, als je daar invloed op hebt.

Btw, wat voor een PLC is het ?

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • MadMan81
  • Registratie: April 2000
  • Laatst online: 18-04 15:42
Het is een Schneider/Modicon Momentum, precieze type weet ik niet uit mijn hoofd, maar zal ik maandag wel ff posten.

In het eeste voorbeeld stuurt de PLC het antwoord bijna gelijk na het ontvangen van de "write multiple registers". Ik kan wel eens proberen om een sleep tussen het ontvangen en het versturen van het antwoord te plaatsen..

Maar wat mij hier zo verbaast is dat de hele opbouw anders is (de FIN, ACK en SYN dingen zeg maar).. Dat kan niet liggen aan de instellingen van mijn socket?

Cupra Born


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-05 16:53
Lijkt me eerder een timing probleem , of een verkeeerde instelling in de PLC. ( of een fout in de TCP stack van de PLC, maar die kans acht ik minder groot )

Is het een losse module of is het een ingebouwde ethernet poort ? Kun je vanuit Windows wel een correcte verbinding maken ?

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • MadMan81
  • Registratie: April 2000
  • Laatst online: 18-04 15:42
Het is een losse module die op de CPU zit. Ik zal maandag ff kijken wat er precies aan in te stellen is.

In het eerste blokje wordt vanuit een PHP file een register in de PLC geschreven en in het tweede verhaal wordt vanaf de PLC een register geschreven in mijn C programma. Beide draaien op de zelfde machine.

Cupra Born


  • MadMan81
  • Registratie: April 2000
  • Laatst online: 18-04 15:42
Het volledige type PLC is: Schneider/Modicon TSX Momentum 171ccc96030. Een gecombineerde CPU/Ethernet module met 1Mb aan geheugen.

Een sleep tussen het ontvangen en het versturen van het antwoord werkt helaas niet. :(

Behalve het IP, subnet en gateway valt er weinig aan de PLC in te stellen wat betreft het ethernet gebeuren. :/

Cupra Born


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-05 16:53
Probeer eens vanuit je c programma te schrijven naar de PLC . Gaat dat wel goed ?

Ik weet dat je bij een Siemens PLC perse op een bepaalde poort moet communiceren aan de PC kant ( dus geen partial address gebruiken bij het openen van je socket ) ( was wel ISO TCP )

[ Voor 54% gewijzigd door farlane op 19-04-2004 12:24 ]

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • MadMan81
  • Registratie: April 2000
  • Laatst online: 18-04 15:42
Het schrijven vanuit het C programma naar de PLC gaat wel goed. Ik heb verder nooit wat gezien over bepaalde poorten, behalve poort 502 voor modbus. Maar dat is aan de server kant. De PLC aan zijn kant kiest zelf een poort, daar heb ik verder geen invloed op..

Cupra Born


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-05 16:53
Kun je een stukje relevante C code posten van je Modbus/TCP slave, misschien valt er wat aan te ontdekken .....

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • MadMan81
  • Registratie: April 2000
  • Laatst online: 18-04 15:42
De code voor het aanmaken van de socket:
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); //Aanmaken van de socket

fcntl(sock, F_SETFD, 1);
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&sock_opt, sizeof(sock_opt));
sock_opt = 512; //SOCKBUFSIZE;
setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void *)&sock_opt, sizeof(sock_opt));

bzero(&serv_name, sizeof(serv_name)); //Vars instellen

serv_name.sin_family = AF_INET;
serv_name.sin_port = htons(PORT);
serv_name.sin_addr.s_addr = htonl(INADDR_ANY);
bind(sock, (struct sockaddr *)&serv_name, sizeof(serv_name));

listen(sock,MAX_CLIENTS);

connect_sock = accept(sock, (struct sockaddr *)&serv_name, &len); //Verbinden met de socket
ReadSocket(&connect_sock); //voor het uitlezen van het modbus request
close(connect_sock);


En de code van ReadSocket:
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//Transaction Identifier
  read(*sock,&param_buf,1);
  tmpPacket[0] = (int) param_buf;
  read(*sock,&param_buf,1);
  tmpPacket[1] = (int) param_buf;
//Protocol Identifier
  read(*sock,&param_buf,1);
  tmpPacket[2] = (int) param_buf;
  read(*sock,&param_buf,1);
  tmpPacket[3] = (int) param_buf;
//Datablock size
  read(*sock,&param_buf,1);
  tmpPacket[4] = (int) param_buf;
  read(*sock,&param_buf,1);
  tmpPacket[5] = (int) param_buf;
//Modbus Unit identifier
  read(*sock,&param_buf,1);
  tmpPacket[6] = (int) param_buf;
//Modbus function
  read(*sock,&param_buf,1);
  tmpPacket[7] = (int) param_buf;
//Modbus reference number
  read(*sock,&param_buf,1);
  tmpPacket[8] = (int) param_buf;
  read(*sock,&param_buf,1);
  tmpPacket[9] = (int) param_buf;
//Modbus word count
  read(*sock,&param_buf,1);
  tmpPacket[10] = (int) param_buf;
  read(*sock,&param_buf,1);
  tmpPacket[11] = (int) param_buf;
//Modbus byte count
  read(*sock,&param_buf,1);
  tmpPacket[12] = (int) param_buf;
//Modbus data
  for (i = 0; i< tmpPacket[12]; i++) {
    read(*sock,&param_buf,1);
    tmpPacket[13+i] = param_buf;
}


En aan daarna wordt het pakket weer terug gestuurd dmv:
C:
1
2
datasize = (int) tmpPacket[12];
write(*sock,tmpPacket,(size_t) 13+datasize);


Alle overige code heb ik voor het overzicht er uit gesloopt. Ook de error checks etc zijn er uit. Tot slot de code nog wat versimpeld door wat functies er uit te laten.

Cupra Born


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-05 16:53
Als je het Modbus protocol bekijkt, dan zie je dat er _altijd_ 4 bytes worden gestuurd, dus je kunt in een keer 4 bytes binnen halen.
Als het dan geen exception response is, heb je meteen het lengtebyte, en kun je de rest binnenharken.

Dit geldt ook voor Modbus/TCP, je moet alleen wat meer bytes aan header binnenhalen, in totaal 13 ofzow.

Je hoeft dus voor het lezen v/e frame maximaal 2 reads te doen !

Vervolgens vraag ik me af met welke functiecode de PLC probeert te schrijven : als dat namelijk met 16 is (0x10) Write multiple registers, stuur je het ding wel aardig over de toeren: het is bij dat commando namelijk helemaal niet de bedoeling dat je alle registers weer terugstuurt.

Ik krijg sterk het vermoeden dat je het protocol incorrect hebt geimplementeerd, en dat je daarmee de PLC over de toeren helpt, die vervolgens zijn socket reset ( dat staat namelijk ook in de standaard )

In het kort, back to the drawing board :)

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • MadMan81
  • Registratie: April 2000
  • Laatst online: 18-04 15:42
Na nog wat bestuderen van PDF-jes en Ethereal output heb ik het probleem kunnen oplossen.

@farlane, je had gelijk, je moet idd die dat niet meer mee terug sturen. Ook de byte-count moet niet mee terug, de word-count wel! Hier kwam ik al eerder achter omdat ethereal die dingen ook niet weergeeft, wel in het hex-vak, maar niet daarboven. Alleen het hielp niets.

Na nog maar eens de pdf te hebben doorgespit vond ik wat over byte 4 en 5, deze vormen samen nl de grote van het modbus deel van het Modbus/TCP pakket. En doordat de data en de byte-count mist moest deze natuurlijk ook nog ff aangepast worden |:(. Toen ik hier de goede waarde (6) heb gebruikt lukt de communicatie eindelijk :+

Beetje jammer alleen dat de foutcode van de PLC je op een verkeerde been zet. Een melding als "Corrupt packet" of zo zou duidelijker geweest zijn :! Bovendien is er al een code voor een foutieve lengte: "5051: Length error".

Hoofdzaak: het werkt :)

Bedankt voor jullie hulp _/-\o_

Cupra Born


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-05 16:53
MadMan81 schreef op 20 april 2004 @ 16:34:
Beetje jammer alleen dat de foutcode van de PLC je op een verkeerde been zet. Een melding als "Corrupt packet" of zo zou duidelijker geweest zijn :! Bovendien is er al een code voor een foutieve lengte: "5051: Length error".
Da kan niet. Als je volgens het Modbus protocol een 'corrupt' packet ontvangt, mag je niet antwoorden : de andere kant kan wel een compleet ander protocol gebruiken of er is onderweg iets fout gegaan.

Je zou dat wel kunnen doen als het complete bericht voldoet aan je protocol ( dus je hebt bepaald dat de andere kant ook Modbus praat ), en je hebt daar ongeldige parameters in zitten.

[ Voor 8% gewijzigd door farlane op 20-04-2004 16:59 ]

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • MadMan81
  • Registratie: April 2000
  • Laatst online: 18-04 15:42
Dat snap ik. Maar hier heeft de PLC het eerste wat verstuurd, en ook de socket opgezet. Als je dan op die socket een ongeldig pakket binnen krijgt zou ik zo'n error verwachten. Let wel, het zijn errors IN de plc! Het gaat hier om het Response pakket wat niet klopt, niet de query. Als die niet zou kloppen, tja jammer dan.. Dat kan de andere kant verder wel zien aan het feit dat hij geen response krijgt.

Cupra Born


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-05 16:53
Een duidelijke foutmelding in de PLC zou idd wel aardig zijn, maar mijn ervaringen zijn dat PLC's iha beperkte diagnose mogelijkheden hebben.

Het gedrag van de PLC is verder wel correct namelijk, Modbus/TCP schrijft voor dat als er een fout optreedt in de ontvangs of tijdens verzenden dat de socket geclosed moet worden en opnieuw geopend.

Ik zou trouwens als ik jouw was nog wel ff goed kijken of je je protocol misschien wat compacter kunt opzetten. Telkens 1 byte lezen is echt niet de manier. ;)

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • MadMan81
  • Registratie: April 2000
  • Laatst online: 18-04 15:42
LOL, nee dat is inmiddels al aangepast.

Maar de communicatie gaat nu prima. Dat heet, de plc geeft geen errors of zo. Maar als ik met de sniffer kijk zie ik steeds van dit soort dingen voorbij komen: "[TCP Out-Of-Order]" en "[TCP Dup ACK 479#1]". Wat zijn het? En moet ik er wat mee doen? Zo ja, wat dan?

Cupra Born


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-05 16:53
Out of order betekent dat de packets te laat komen, Dup acks zijn meerdere acks die verstuurd worden.

Lijkt me een timing issue eigenlijk. ( Of een bugje in de stack )

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • MadMan81
  • Registratie: April 2000
  • Laatst online: 18-04 15:42
Ook dat laaste probleem is nu opgelost:

ipv sleep(1) gebruik ik nu usleep(200000) tussen het sturen van de response en het sluiten van de socket.

Als je te kort wacht heeft de PLC niet genoeg tijd om te antwoorden. Als je te lang wacht krijg je die Out-Of-Orders..

Wel raar overigens wat ze raden je aan de socket gewoon open te houden, maar dat lijkt dus niet helemaal te gaan lukken.. Of ik moet hier iets missen :?

Cupra Born


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-05 16:53
MadMan81 schreef op 22 april 2004 @ 12:42:
Ook dat laaste probleem is nu opgelost:

ipv sleep(1) gebruik ik nu usleep(200000) tussen het sturen van de response en het sluiten van de socket.

Als je te kort wacht heeft de PLC niet genoeg tijd om te antwoorden. Als je te lang wacht krijg je die Out-Of-Orders..

Wel raar overigens wat ze raden je aan de socket gewoon open te houden, maar dat lijkt dus niet helemaal te gaan lukken.. Of ik moet hier iets missen :?
Socket moet je alleen closen als er iets fout gaat. Als het goed is blijft de socket nog in linger mode om eventuele data nog op te vangen ( Afhankelijk wat voor een socket options meegeeft )

Maar je programma doet dus 1 communicatieslag en exit' dan weer of blijft ie gewoon loopen totdat ie een stop commando krijgt ?

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • MadMan81
  • Registratie: April 2000
  • Laatst online: 18-04 15:42
Hij blijft gewoon loopen idd. En bij een accept forkt hij en handelt de modbus query af. Een redelijk standaard methode om te werken met sockets.

Cupra Born


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-05 16:53
MadMan81 schreef op 23 april 2004 @ 10:04:
Hij blijft gewoon loopen idd. En bij een accept forkt hij en handelt de modbus query af. Een redelijk standaard methode om te werken met sockets.
In dat geval sluit je je socket alleen als er iets mis gaat, het systeem zorgt er wel voor dat de onderliggende socket open blijft om eventuele data nog op te vangen en te discarden.

Het eea is dus afhankelijk van hoe je je linger options hebt ingesteld, en hoe je je socket sluit. Een graceful shutdown houdt in dat je eerst je socket sluit voor schrijven, , gaat lezen totdat je een 0 of een error terugkrijgt, en daarna kun je een close doen.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.

Pagina: 1