Testserver

The testserver was created initially for internal testing. However, you can also use the testserver to test your application. Use it when no real ADS server is available, for example during continuous integration or when TwinCAT is not installed.

You can run a basic testserver with the command:

$ python -m pyads.testserver --handler basic

The handler type defaults to ‘advanced’.

This will create a new device on 127.0.0.1 port 48898. In the next step the route to the testserver needs to be added from another python console.

>>> import pyads
>>> pyads.add_route("127.0.0.1.1.1", "127.0.0.1")

Warning

The testserver functionality was originally intended only for internal testing. The documentation and any support are not guaranteed.

Handlers

The server is a socket.socket listener, that listens to ADS-like connections and sends responses to requests. AdsTestServer itself does not manage the requests and responses. Those are managed by handler classes. Currently there are two handlers available:

  • BasicHandler always returns the same static responses. No data can be saved, any returned values are always 0.

  • AdvancedHandler keeps a list of variables and allows for reading/writing variables. Variables need to be created upfront via add_variable().

Your requirements determine which handler is most suitable. You can also create your own handler by extending the AbstractHandler class. Typically, the basic handler will require the least amount of work.

A complete overview of the capabilities of the handlers is below. If a feature is mocked, it will do nothing but no error will be thrown when it is executed. If a feature is not implemented, an error will be thrown when an attempt is made to use the feature.

Handler implementations
Feature
(Methods from Connection)

BasicHandler

AdvancedHandler

read_state

Mocked

Mocked

write_control

Mocked

Mocked

read_device_info

Mocked

Mocked

read

Mocked

Implemented

write

Mocked

Implemented

read_by_name

Mocked

Implemented

read_by_name
(with handle)

Mocked

Implemented

write_by_name

Mocked

Implemented

write_by_name
(with handle)

Mocked

Implemented

get_symbol

Mocked (no info will
be found automatically)

Implemented

get_all_symbols

Mocked (list will
always be empty)

Implemented

get_handle

Mocked

Implemented

release_handle

Mocked

Mocked

read_list_by_name

Mocked

Implemented

write_list_by_name

Mocked

Implemented

read_structure_by_name

Mocked

Not implemented

write_structure_by_name

Mocked

Not implemented

add_device_notification

Mocked

Implemented

del_device_notification

Mocked

Implemented

Device notifications

Not implemented (callbacks
will never fire)

Implemented

Basic Handler

The BasicHandler just responds with 0x00 wherever possible. Trying to read any byte or integer will always always net 0. Trying to read an LREAL for example will give 2.09e-308, as that is the interpretation of all bits at 0.

Actions like writing to a variable or adding a notification will always be successful, but they won’t have any effect.

Advanced Handler

The AdvancedHandler keeps track of variables in an internal list. You can read from and write to those variables like you would with a real server, using either the indices, name or variable handle. Any notifications will be issued as expected too. The handler keeps a list of variables with the type PLCVariable. In order to address a variable you need to explicitly create it first:

# Server code

handler = AdvancedHandler()

test_var = PLCVariable(
    "Main.my_var", bytes(8), ads_type=constants.ADST_REAL64, symbol_type="LREAL"
)
handler.add_variable(test_var)
# Client code

with plc:
    sym = plc.get_symbol("Main.my_var")  # Already exists remotely
    print(sym)
    print(sym.read())