Menu

Hello World! Log in to Edit

Jan Costermans
Attachments
HelloMessage.png (94291 bytes)
listen.py (1012 bytes)
opycua_ack.py (1116 bytes)
opycua_hello.py (1257 bytes)

Hello World!

Let's get started by sending an OPC UA TCP Hello message to localhost.

Setting up your system

  • We 'll be developping on Debian Squeeze with Python 2.6.6.
  • We'll be using Wireshark, with the OPC ua dissector to check our message.
  • Note: the opc ua dissector is not yet available in Wireshark 1.2.11.
    This is the version used on Debian squeeze.
    To solve this we run Wireshark 1.6.7 on Debian Sid inside a chroot.
    https://sourceforge.net/p/opycua/wiki/Schroot/
  • You should be able to run these snippets with newer versions of python and on other platforms as well.

The OPC UA Hello message

The hello message is used to negotiate buffer size, message size, and the maximum number of chunks per message to use.

It contains the following fields:

  • MessageType: HEL, from hello
  • IsFinal: F, from final because the hello message is sent in 1 chunk so it is the final chunk
  • MessageSize: 32 + the number of characters in the EndpointUrl
  • ProtocolVersion: 0, because our stack has not been certified it's version is 0
  • ReceiverBufferSize: 9000, just an example
  • SendBufferSize: 9000, just an example
  • MaxMessageSize: 0, this means unlimited
  • MaxChunkCount: 0, this means unlimited
  • EndpointUrl: opc.tcp://opycua:4841, just an example

Construct

Using Construct we can create a struct, containing these fields.

from construct import *
c = Struct('OPC UA TCP Hello Message',
        String('MessageType', 3),
        String('Reserved', 1),
        ULInt32('MessageSize'),
        ULInt32('ProtocolVersion'),
        ULInt32('ReceiveBufferSize'),
        ULInt32('SendBufferSize'),
        ULInt32('MaxMessageSize'),
        ULInt32('MaxChunkCount'),
        PascalString('EndpointUrl', length_field=ULInt32('length'), encoding='utf8'),
    )

Snippets

We 'll be using 2 snippets.

  • The first one will act as the OPC ua server. It simply waits and listens on a port. This is necessary because otherwise we cannot send a packet to this port.
  • The second one will act as the OPC ua client. It will encode the field of the OPC UA hello message as binary data and send it to the listening port.

Wireshark will also be running, so we can capture the message.

STEP 1: Start listening

import socket
import sys

from construct import *

c = Struct('OPC UA TCP Hello Message',
        String('MessageType', 3),
        String('Reserved', 1),
        ULInt32('MessageSize'),
        ULInt32('ProtocolVersion'),
        ULInt32('ReceiveBufferSize'),
        ULInt32('SendBufferSize'),
        ULInt32('MaxMessageSize'),
        ULInt32('MaxChunkCount'),
        PascalString('EndpointUrl', length_field=ULInt32('length'), encoding='utf8'),
    )

# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('localhost', 4840)
sock.bind(server_address)
sock.listen(1)

while True:
    print >>sys.stderr, '\nwaiting for a connection'
    connection, client_address = sock.accept()
    try:
        data = connection.recv(53)
        print >>sys.stderr, 'received "%s"' % data

        unpacked_data = c.parse(data)
        print >>sys.stderr, 'unpacked:', unpacked_data

    finally:
        connection.close()

STEP 2: start Wireshark

Listen on the loopback device (lo).

STEP 3: Send hello message

import socket
import sys
from construct import *

c = Struct('OPC UA TCP Hello Message',
        String('MessageType', 3),
        String('Reserved', 1),
        ULInt32('MessageSize'),
        ULInt32('ProtocolVersion'),
        ULInt32('ReceiveBufferSize'),
        ULInt32('SendBufferSize'),
        ULInt32('MaxMessageSize'),
        ULInt32('MaxChunkCount'),
        PascalString('EndpointUrl', length_field=ULInt32('length'), encoding='utf8'),
    )

x = Container(
        MessageType       = 'HEL' ,
        Reserved          = 'F'   ,
        MessageSize       =  53   ,
        ProtocolVersion   =   0   ,
        ReceiveBufferSize = 9000  ,
        SendBufferSize    = 9000  ,
        MaxMessageSize    =   0   ,
        MaxChunkCount     =   0   ,
        EndpointUrl       = 'opc.tcp://opycua:4841',
    )

packed_data = c.build(x)

# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('localhost', 4840)
sock.connect(server_address)
try:
    # Send data
    print >>sys.stderr, 'sending "%s"' % packed_data
    sock.sendall(packed_data)
finally:
    print >>sys.stderr, 'closing socket'
    sock.close()

Results

We hope this little example shows that it is a great idea to implement an OPC ua stack in python.

Anyway, here is a screenshot from Wireshark:

Wireshark - OPC ua hello message

Credit


Related

Wiki: Home
Wiki: uaArticles

MongoDB Logo MongoDB