Skip to content

Objects

Demonstrates how to organise a server's address space with object nodes, define a custom ObjectType as a reusable template, and declare a custom VariableType. The result is a small "Plant" hierarchy with two child devices and one sensor instantiated from the custom type.

Start any OPC UA client (e.g. C02_firststeps.py or C03_browsing.py) against this server to see the hierarchy.

Server.addObject() is the high-level way to add an Object to the address space. name is the displayed name and parent is the NodeId of the enclosing object. Passing an explicit nodeid keeps the layout predictable across runs — without it the server assigns numeric ids in the order calls are made.

1. Object hierarchy

Build a small "Plant" tree: two child devices (Oven and Conveyor) each with two Variable children. Every node gets an explicit nodeid in namespace 1 so client examples can reference them by string.

print("--- 1. Object Hierarchy ---")

plant = server.addObject(name="Plant", parent=server.objectsNode, nodeId="ns=1;i=100")

oven = server.addObject("Oven", plant, nodeId="ns=1;i=110")
oven_temp = server.addVariable("Temperature", oven, 180.0, nodeId="ns=1;i=111")
oven_heater = server.addVariable("HeaterOn", oven, True, nodeId="ns=1;i=112")

conveyor = server.addObject("Conveyor", plant, nodeId="ns=1;i=120")
conveyor_speed = server.addVariable("Speed", conveyor, 1.5, nodeId="ns=1;i=121")
conveyor_running = server.addVariable("IsRunning", conveyor, False, nodeId="ns=1;i=122")

2. Custom ObjectType

addObjectType() creates a type definition. Instances refer to it through typeDefinition; clients can inspect its declarations. Child declarations require modelling rules for automatic instantiation. This example explicitly creates the Value and Unit children on the instance.

print()
print("--- 2. Custom ObjectType ---")

sensor_type = server.addObjectType("SensorType", nodeId="ns=1;i=200")
server.addVariable("Value", sensor_type, 0.0, nodeId="ns=1;i=201")
server.addVariable("Unit", sensor_type, "", nodeId="ns=1;i=202", writable=False)

humidity_sensor = server.addObject(
    "HumiditySensor",
    oven,
    nodeId="ns=1;i=130",
    typeDefinition=sensor_type,
)
server.addVariable("Value", humidity_sensor, 45.0, nodeId="ns=1;i=131")
server.addVariable("Unit", humidity_sensor, "%RH", nodeId="ns=1;i=132", writable=False)

3. Custom VariableType

addVariableType() registers a new VariableType node. It is useful when a value needs units, range, or engineering-meta data attached. Clients can read this type by its NodeId.

print()
print("--- 3. Custom VariableType ---")

server.addVariableType(
    "TemperatureType",
    dataType="i=11",  # Double
    nodeId="ns=1;i=300",
)

4. Run and simulation loop

server.start() blocks until server.stop(). The wrapper assigns new values to the Variable handles once per second so any client can watch the values change live.

    print()
    print("--- 4. Run and Simulation Loop ---")

    server.start()
    print(f"Server running at {endpoint_url}")
    print("Press Ctrl+C to stop.")

    try:
        cycle = 0
        while True:
            cycle += 1
            oven_temp(180.0 + (cycle % 20) * 0.5)
            conveyor_speed(1.5 + (cycle % 10) * 0.1)
            conveyor_running(cycle % 15 != 0)

            if cycle % 10 == 0:
                print(
                    f"Cycle {cycle}: OvenTemp = {oven_temp():.1f}°C, "
                    f"ConvSpeed = {conveyor_speed():.1f}m/s"
                )

            time.sleep(1.0)

    except KeyboardInterrupt:
        print("Shutting down...")
    finally:
        server.stop()
        print("Server stopped.")


if __name__ == "__main__":
    main()

Source Code

Download S05_objects.py to run it locally.