FUNCTION_BLOCK FB_SocketCreate
VAR_INPUT
xExecute : BOOL := FALSE;
bServer : BOOL := FALSE;
eProtocolType : ENUM_EthProtocolType := ENUM_TCP;
nEthComInstNo : INT := 0;
END_VAR
VAR_OUTPUT
xDone : BOOL := FALSE;
xError : BOOL := FALSE;
wErrorID : WORD := 16#0;
END_VAR
VAR
nState : INT := 0;
(* Create Socket *)
lOptLinger : SOCKET_LINGER;
dwValue : DWORD := 1;
nValue : DINT := 1;
(* Verwendete FBs aus SysSockAsync.lib *)
mySysSockCreateAsync : SysSockCreateAsync;
mySysSockSetOptionAsync : SysSockSetOptionAsync;
END_VAR
IF NOT xExecute THEN nState := 0;
END_IF;
CASE nState OF
0:
xDone := FALSE;
xError := FALSE;
wErrorID := 16#0;
nState := 10;
10:
IF xExecute THEN
nState :=20;
END_IF;
(* ---------------------------------------------------------- *)
20:(* create socket *)
mySysSockCreateAsync(bEnable := FALSE);
IF NOT mySysSockCreateAsync.bDone THEN
nState := nState + 10;
END_IF;
30:
IF eProtocolType = ENUM_TCP THEN
mySysSockCreateAsync.diType := SOCKET_STREAM;
mySysSockCreateAsync.diProtocol := SOCKET_IPPROTO_IP;
ELSE
mySysSockCreateAsync.diType := SOCKET_DGRAM;
mySysSockCreateAsync.diProtocol := SOCKET_IPPROTO_UDP;
END_IF;
FUN_EthComRegisterCallbacks();
mySysSockCreateAsync(
bEnable := TRUE,
diAddressFamily := SOCKET_AF_INET, (* AF_INET: IPv4 basierete Internet-Protokolle. TCP und UDP sind allgemein gebräuchliche Protokolle dieser Familie *)
diType := ,(* SOCK_STREAM: Stellt zuverlässige, sequentielle verbindungsorientierte Byte-Streams im Vollduplexmodus zur Verfügung.
Auch bandexterne Mechanismen zur Datenübertragung können unterstützt werden. Das TCP-Protokoll beruht auf diesem Socket.Typ. *)
diProtocol := ,(* IPPROTO_IP is for raw IP packets. IPPROTO_TCP and IPPROTO_UDP are protocols based on IP. If you open an raw IP socket,
you're responsible for assembling all the IP bytes yourself. You may need privileged access to open a raw IP socket. *)
sSocket => my_ST_EthCommunication[nEthComInstNo].diSocket (* erzeugten Socket in gobale Instanz übernehmen *)
);
IF mySysSockCreateAsync.bDone
AND NOT mySysSockCreateAsync.bError
THEN
(* Die Callbacks werden benötigt um den Socket bei Reset oder Onlinechange zu schließen sonst kann es zum Crash der PLC führen *)
nState := nState + 10;
ELSIF mySysSockCreateAsync.bError
AND mySysSockCreateAsync.bDone
THEN
mySysSockCreateAsync(
bEnable := FALSE);(*nState := 1000 + nState + 2; (* Socket konnte nicht erzeugt werden *)*)
END_IF;
40:
(* Callback Funktionen registrieren und Socket Optionen setzen *)
(*
SO_DEBUG Gibt an, ob Debug-Informationen mitgeschrieben wurden
SO_BROADCAST Gibt an, ob die Übermittlung von Rundsende-Nachrichten unterstützt wird.
SO_REUSEADDR Gibt an, ob die lokale Adresse wiederverwendet werden kann.
SO_KEEPALIVE Gibt an, ob Verbindungen durch periodische Aussendungen von Nachrichten aufrecht erhalten werden.
Falls der verbundene Socket nicht auf diese Nachrichten antworten kann, wird die Verbindung unterbrochen
und Prozesse, die auf diesen Socket schreiben, erhalten ein SIGPIPE Signal.
SO_LINGER Gibt an, ob der socket einen Aufruf von socket_close() verzögert, falls noch Daten
vorhanden sind. In der Standardeinstellung versucht ein Socket vor dem Schließen, alle noch nicht
gesendeten Daten zu übermitteln. Im Falle eines verbindungsorientierten Sockets, wartet socket_close() auf die
Empfangsbestätigung seiner Gegenstelle. Falls l_onoff von 0 verschieden und l_linger ist 0, werden bei einem
verbindungsorientierten Socket alle noch nicht gesendeten Daten verworfen und das Signal RST (Reset) an
die Gegenstelle gesendet. Andererseits, falls sowohl l_onoff als auch l_linger von 0 verschieden sind,
blockiert der Aufruf von socket_close() den Socket so lange, bis entweder alle Daten gesendet sind oder
das Zeitlimit erreicht ist, das in l_linger gesetzt wurde. Wenn der Socket auf nicht-blockieren gesetzt ist,
schläGT socket_close() fehl und gibt einen Fehler zurück. ARRAY. Das ARRAY enthäLT zwei Schlüssel: l_onoff AND l_linger.
SO_OOBINLINE Gibt an, ob socket die Übertragung von bandexternen Daten in der Reihenfolge überträGT, wie sie auftreten.
SO_SNDBUF Gibt die Grösse des Sendepuffers zurück.
SO_RCVBUF Gibt die Grösse des Empfangspuffers zurück.
SO_ERROR Gibt den Fehlerstatus zurück und löscht ihn. INT (dieser Wert kann nicht von socket_set_option() gesetzt werden.)
SO_TYPE Gibt den Typ von socket zurück (z. B. SOCK_STREAM). INT (dieser Wert kann nicht von socket_set_option() gesetzt werden.)
SO_DONTROUTE Gibt an, ob ausgehende Nachrichten das standardmässige Routing umgehen.
SO_RCVLOWAT Gibt die Mindestanzahl Bytes an, die socket bei einer Empfangsoperation verarbeiten soll.
(Der Vorgabewert ist 1.)
SO_RCVTIMEO Gibt den Wert der Zeitspanne bis zu einer Zeitüberschreitung bei Empfangsoperationen an. ARRAY. Das ARRAY
enthäLT zwei Schlüssel: sec, den Sekunden-Anteil der Zeitspanne und usec, den Mikrosekunden-Anteil der Zeitspanne
bis zu einer Zeitüberschreitung. SO_SNDTIMEO Gibt die Zeitspanne an, nach der eine Zeitüberschreitung bei
Sendeoperationen ausgelöST wird. Eine Ausgabefunktion darf während der angegebenen Zeitspanne blockieren,
falls die Ablaufsteuerung den Versand der Daten verhindert. ARRAY. Das ARRAY enthäLT zwei Schlüssel: sec
den Sekunden-Anteil der Zeitspanne und usec den Mikrosekunden-Anteil der Zeitspanne bis zu einer Zeitüberschreitung.
SO_SNDLOWAT Gibt die Mindestanzahl Bytes an, die socket bei einer Ausgabeoperation verarbeiten soll.
*)
mySysSockSetOptionAsync(bEnable := FALSE);
IF NOT mySysSockSetOptionAsync.bDone THEN
nState := nState + 10;
END_IF;
50: (* Socketoption setzen: Sendepuffergröße einstellen *)
mySysSockSetOptionAsync(
bEnable := TRUE,
diSocket := my_ST_EthCommunication[nEthComInstNo].diSocket, (* Socket übergeben *)
diLevel := SOCKET_SOL, (* Der Parameter level gibt die Protokollebene an, auf der diese Option gilt. Um beispielsweise Optionen auf dem Socket-Level abzufragen
würde man den Parameter level auf den Wert SOL_SOCKET setzen. Andere Ebenen, wie etwa TCP können benutzt werden, indem man die
Protokollnummer dieser Ebene benutzt. *)
diOption := SOCKET_SO_SNDBUF, (* Setzt die Größe des Sendepuffers fest *)
pOptionValue := ADR(ETH_COM_MAX_DATA_SIZE_SEND),
diOptionLength := SIZEOF(ETH_COM_MAX_DATA_SIZE_SEND)
);
IF mySysSockSetOptionAsync.bDone THEN
nState := nState + 10;
ELSIF mySysSockSetOptionAsync.bError THEN
nState := 1000 + nState + 2;
END_IF;
60:
mySysSockSetOptionAsync(bEnable := FALSE);
IF NOT mySysSockSetOptionAsync.bDone THEN
nState := nState + 10;
END_IF;
70: (* Socketoption setzen: Empfangspuffergröße einstellen *)
mySysSockSetOptionAsync(
bEnable := TRUE,
diSocket := my_ST_EthCommunication[nEthComInstNo].diSocket, (* Socket übergeben *)
diLevel := SOCKET_SOL, (* Der Parameter level gibt die Protokollebene an, auf der diese Option gilt. Um beispielsweise Optionen auf dem Socket-Level abzufragen
würde man den Parameter level auf den Wert SOL_SOCKET setzen. Andere Ebenen, wie etwa TCP können benutzt werden, indem man die
Protokollnummer dieser Ebene benutzt. *)
diOption := SOCKET_SO_RCVBUF, (* Setzt die Größe des Sendepuffers fest *)
pOptionValue := ADR(ETH_COM_MAX_DATA_SIZE_RECV),
diOptionLength := SIZEOF(ETH_COM_MAX_DATA_SIZE_RECV)
);
IF mySysSockSetOptionAsync.bDone THEN
nState := nState + 10;
ELSIF mySysSockSetOptionAsync.bError THEN
nState := 1000 + nState + 2;
END_IF;
80:
mySysSockSetOptionAsync(bEnable := FALSE);
IF NOT mySysSockSetOptionAsync.bDone THEN
nState := nState + 10;
END_IF;
90: (* Socketoption setzen: Adresse darf wiederverwendet werden nach Schließen des Sockets *)
dwValue := 1;
mySysSockSetOptionAsync(
bEnable := TRUE,
diSocket := my_ST_EthCommunication[nEthComInstNo].diSocket, (* Socket übergeben *)
diLevel := SOCKET_SOL, (* Der Parameter level gibt die Protokollebene an, auf der diese Option gilt. Um beispielsweise Optionen auf dem Socket-Level abzufragen
würde man den Parameter level auf den Wert SOL_SOCKET setzen. Andere Ebenen, wie etwa TCP können benutzt werden, indem man die
Protokollnummer dieser Ebene benutzt. *)
diOption := SOCKET_SO_REUSEADDR, (* Gibt an, dass Adresse wieder genutzt werden darf nach Schließen des Sockets! *)
pOptionValue := ADR(dwValue),
diOptionLength := SIZEOF(dwValue)
);
IF mySysSockSetOptionAsync.bDone THEN
nState := nState + 10;
ELSIF mySysSockSetOptionAsync.bError THEN
nState := 1000 + nState + 2;
END_IF;
100: mySysSockSetOptionAsync(bEnable := FALSE);
IF NOT mySysSockSetOptionAsync.bDone THEN
nState := nState + 10;
END_IF;
110: (* Socketoption setzen: Beim Schließen des Sockets kann durch Linger gewartet werden falls noch Daten im Sendepuffer vorliegen ==> Deaktivieren *)
lOptLinger.l_onoff := 0; (* Lingerfunktion deaktivieren! ==> Socket schließen bei SysSockClose auch wenn noch Daten im Ausgangspuffer vorliegen! *)
lOptLinger.l_linger := 0; (* Kein Linger nutzten! *)
mySysSockSetOptionAsync(
bEnable := TRUE,
diSocket := my_ST_EthCommunication[nEthComInstNo].diSocket, (* Socket übergeben *)
diLevel := SOCKET_SOL, (* Der Parameter level gibt die Protokollebene an, auf der diese Option gilt. Um beispielsweise Optionen auf dem Socket-Level abzufragen
würde man den Parameter level auf den Wert SOL_SOCKET setzen. Andere Ebenen, wie etwa TCP können benutzt werden, indem man die
Protokollnummer dieser Ebene benutzt. *)
diOption := SOCKET_SO_LINGER, (* Gibt an, dass Adresse wieder genutzt werden darf nach Schließen des Sockets! *)
pOptionValue := ADR(lOptLinger),
diOptionLength := SIZEOF(lOptLinger)
);
IF mySysSockSetOptionAsync.bDone THEN
nState := nState + 10;
ELSIF mySysSockSetOptionAsync.bError THEN
nState := 1000 + nState + 2;
END_IF;
120:
mySysSockSetOptionAsync(bEnable := FALSE);
IF NOT mySysSockSetOptionAsync.bDone THEN
nState := nState + 10;
END_IF;
130:
nValue := 1;
mySysSockSetOptionAsync(
bEnable := TRUE,
diSocket := my_ST_EthCommunication[nEthComInstNo].diSocket, (* Socket übergeben *)
diLevel := SOCKET_SOL, (* Gibt an, ob Verbindungen durch periodische Aussendungen von Nachrichten aufrecht erhalten werden.
Falls der verbundene Socket nicht auf diese Nachrichten antworten kann, wird die Verbindung unterbrochen
und Prozesse, die auf diesen Socket schreiben, erhalten ein SIGPIPE Signal. *)
diOption := SOCKET_SO_KEEPALIVE, (* Gibt an, dass Adresse wieder genutzt werden darf nach Schließen des Sockets! *)
pOptionValue := ADR(nValue),
diOptionLength := SIZEOF(nValue)
);
IF mySysSockSetOptionAsync.bDone THEN
nState := nState + 10;
ELSIF mySysSockSetOptionAsync.bError THEN
nState := 1000 + nState + 2;
END_IF;
140:
mySysSockSetOptionAsync(bEnable := FALSE);
IF NOT mySysSockSetOptionAsync.bDone THEN
nState := nState + 10;
END_IF;
150:
dwValue := 1;
mySysSockSetOptionAsync(
bEnable := TRUE,
diSocket := my_ST_EthCommunication[nEthComInstNo].diSocket, (* Socket übergeben *)
diLevel := SOCKET_SOL, (* Gibt an, ob Verbindungen durch periodische Aussendungen von Nachrichten aufrecht erhalten werden.
Falls der verbundene Socket nicht auf diese Nachrichten antworten kann, wird die Verbindung unterbrochen
und Prozesse, die auf diesen Socket schreiben, erhalten ein SIGPIPE Signal. *)
diOption := SOCKET_SO_REUSEPORT, (* Gibt an, dass Adresse wieder genutzt werden darf nach Schließen des Sockets! *)
pOptionValue := ADR(dwValue),
diOptionLength := SIZEOF(dwValue)
);
IF mySysSockSetOptionAsync.bDone THEN
nState := nState + 10;
ELSIF mySysSockSetOptionAsync.bError THEN
nState := 1000 + nState + 2;
END_IF;
160:
xDone := TRUE;
1000..1999:
;
END_CASE;