async def main(): server = Server() await server.init() server.set_endpoint('opc.tcp://192.168.0.100:4840/opcua/') server.set_server_name("OPC-UA Server") uri = 'http://devnetiot.com/opcua/' idx = await server.register_namespace(uri) obj_plc = await server.nodes.objects.add_object(idx, 'PLC') var_temperature = await obj_plc.add_variable(idx, 'temperature', 0) var_humidity = await obj_plc.add_variable(idx, 'humidity', 0) _logger.info('Starting server!') async with server: while True: humidity, temperature = Adafruit_DHT.read_retry( Adafruit_DHT.DHT11, DHT_DATA_PIN) if humidity is not None and temperature is not None: print('temperature: {0:0.1f}*C'.format(temperature)) print('humidity: {0:0.1f}%'.format(humidity)) await var_temperature.write_value(float(temperature)) await var_humidity.write_value(float(humidity)) else: print('Failed to get DHT11 reading, trying again in ', DHT_READ_TIMEOUT, 'seconds') await asyncio.sleep(DHT_READ_TIMEOUT)
class DemoServer: def __init__(self): self.server = Server() self.server.set_endpoint('opc.tcp://0.0.0.0:51210/UA/SampleServer') self.server.set_server_name('Custom structure demo server') # idx name will be used later for creating the xml used in data type dictionary self._idx_name = 'http://examples.freeopcua.github.io' self.idx = self.server.register_namespace(self._idx_name) self.dict_builder = DataTypeDictionaryBuilder(self.server, self.idx, self._idx_name, 'MyDictionary') def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): quit() def start_server(self): self.server.start() def create_structure(self, name): # save the created data type return self.dict_builder.create_data_type(name) def complete_creation(self): self.dict_builder.set_dict_byte_string()
async def main(): global rpm_is puls.when_pressed = callback_rpm # set callback # initialize Server server = Server() await server.init() server.set_endpoint('opc.tcp://pi.local:4840/freeopcua/server/') server.set_server_name("OPC UA Raspi Server") # all possible endpoint policies set for client connection server.set_security_policy([ ua.SecurityPolicyType.NoSecurity, ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt, ua.SecurityPolicyType.Basic256Sha256_Sign]) # setup namespace uri = "example-uri.edu" idx = await server.register_namespace(uri) # create Motor-node type for later use base_motor = await motor_object(idx, server) # populateing address space motor = await server.nodes.objects.add_object(idx, "Motor", base_motor) rpm_var = await motor.get_child([f"{idx}:RPM"]) # rounds per minute await rpm_var.set_writable() speed_var = await motor.get_child([f"{idx}:Speed"]) # motor speed await speed_var.set_writable() # Start! async with server: while True: rpm_is, speed_is = await asyncio.gather(*(get_rpm(rpm_var), set_speed(speed_var)))
class HelloServer: def __init__(self, endpoint, name, model_filepath): self.server = Server() # This need to be imported at the start or else it will overwrite the data self.server.import_xml(model_filepath) self.server.set_endpoint(endpoint) self.server.set_server_name(name) objects = self.server.get_objects_node() freeopcua_namespace = self.server.get_namespace_index( "urn:freeopcua:python:server") hellower = objects.get_child("0:Hellower") hellower_say_hello = hellower.get_child("0:SayHello") self.server.link_method(hellower_say_hello, say_hello_xml) hellower.add_method(freeopcua_namespace, "SayHello2", say_hello, [ua.VariantType.Boolean], [ua.VariantType.String]) hellower.add_method(freeopcua_namespace, "SayHelloArray", say_hello_array, [ua.VariantType.Boolean], [ua.VariantType.String]) def __enter__(self): self.server.start() return self.server def __exit__(self, exc_type, exc_val, exc_tb): self.server.stop()
class TestServer: def __init__(self): " Basic setup of server" # async def __init__ doesn't work self.server = Server() self.idx = None self.top = None self.vars = [] async def create_simulator(self): await self.server.init() self.server.set_endpoint(f'opc.tcp://{LOCAL_HOST}:{PORT}/{PATH}') self.server.set_server_name("OPC-UA Test Server") uri = "https://github.com/it-hms/my-opcua-test-server" self.idx = await self.server.register_namespace(uri) self.top = await self.server.nodes.objects.add_object( self.idx, 'Dynamic') for i in range(MAX_TAG + 1): t_var = await self.top.add_variable(self.idx, f"Random.Float{i}", 0) self.vars.append(t_var) async def run(self): async with self.server: while True: i = 0 for var in self.vars: await var.write_value(random.gauss(i * 10, 9)) i += 1 await asyncio.sleep(2)
async def main(): # Certificates folder certs_folder = os.environ.get('CERTS_FOLDER', os.path.dirname(os.path.realpath(__file__))) key_pem_path = os.path.join(certs_folder, "key.pem") cert_der_path = os.path.join(certs_folder, "certificate.der") server = Server() await server.init() await server.set_application_uri("urn:opcua:iom:server") server.set_endpoint('opc.tcp://0.0.0.0:4840/freeopcua/server/') server.set_server_name("IoM PLC Server Example") # Security await server.load_certificate(cert_der_path) await server.load_private_key(key_pem_path) server.set_security_policy([ ua.SecurityPolicyType.NoSecurity, ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt]) # setup our own namespace, not really necessary but should as spec idx = await server.register_namespace("https://tknika.eus/opcua/demo/plc") # get Objects node, this is where we should put our nodes objects = server.get_objects_node() # populating our address space plc_server = await objects.add_object(idx, 'PLC Server') bool_data = await plc_server.add_variable(idx, 'BooleanData', True, datatype=ua.NodeId(1, 0)) pos_data = await plc_server.add_variable(idx, 'PositiveTrendData', 0, datatype=ua.NodeId(11, 0)) neg_data = await plc_server.add_variable(idx, 'NegativeTrendData', 0, datatype=ua.NodeId(11, 0)) temp_data = await plc_server.add_variable(idx, 'TemperatureData', 18.5, datatype=ua.NodeId(11, 0)) hum_data = await plc_server.add_variable(idx, 'HumidityData', 60.2, datatype=ua.NodeId(11, 0)) cyc_data = await plc_server.add_variable(idx, 'CyclicData', 0, datatype=ua.NodeId(11, 0)) mirror_orig_data = await plc_server.add_variable(idx, 'MirrorDataOriginal', True, datatype=ua.NodeId(1, 0)) mirror_copy_data = await plc_server.add_variable(idx, 'MirrorDataCopy', True, datatype=ua.NodeId(1, 0)) latitude_data = await plc_server.add_variable(idx, "GPSLatitude", "", datatype=ua.NodeId(12, 0)) longitude_data = await plc_server.add_variable(idx, "GPSLongitude", "", datatype=ua.NodeId(12, 0)) latitude_longitude_data = await plc_server.add_variable(idx, "GPSLatitudeAndLongitude", "", datatype=ua.NodeId(12, 0)) logger.info('Starting OPC UA server!') bool_task = asyncio.Task(toggle_data(bool_data, refresh=10, init=True)) pos_task = asyncio.Task(periodic_data(pos_data, refresh=5)) neg_task = asyncio.Task(periodic_data(neg_data, refresh=5, increment=-2)) temp_task = asyncio.Task(random_data(temp_data, refresh=10, init=18.5, min=15, max=22)) hum_task = asyncio.Task(random_data(hum_data, refresh=10, init=60.2, min=0, max=100)) cyclic_task = asyncio.Task(cyclic_data(cyc_data, cycle_time=200, step=0.5, init=0, min=-100, max=100)) mirror_handler = MirrorHandler(server, mirror_orig_data, mirror_copy_data) await mirror_handler.start() tcx_update_handler = TCXUpdateHandler("circular-urnieta-aia-donosti-urnieta.tcx", latitude_data, longitude_data, latitude_longitude_data) tcx_task = asyncio.Task(tcx_update_handler.start()) async with server: await asyncio.gather(bool_task, pos_task, neg_task, temp_task, hum_task, cyclic_task, tcx_task)
async def main(): # optional: setup logging logging.basicConfig(level=logging.WARN) #logger = logging.getLogger("asyncua.address_space") # logger.setLevel(logging.DEBUG) #logger = logging.getLogger("asyncua.internal_server") # logger.setLevel(logging.DEBUG) #logger = logging.getLogger("asyncua.binary_server_asyncio") # logger.setLevel(logging.DEBUG) #logger = logging.getLogger("asyncua.uaprocessor") # logger.setLevel(logging.DEBUG) logger = logging.getLogger("asyncua.subscription_service") logger.setLevel(logging.DEBUG) # now setup our server server = Server() await server.init() #await server.disable_clock() #server.set_endpoint("opc.tcp://localhost:4840/freeopcua/server/") server.set_endpoint("opc.tcp://0.0.0.0:4840/freeopcua/server/") server.set_server_name("FreeOpcUa Example Server") # setup our own namespace uri = "http://examples.freeopcua.github.io" idx = await server.register_namespace(uri) # get Objects node, this is where we should put our custom stuff objects = server.nodes.objects # populating our address space myfolder = await objects.add_folder(idx, "myEmptyFolder") myobj = await objects.add_object(idx, "MyObject") myvar = await myobj.add_variable(idx, "MyVariable", 6.7) await myvar.set_writable() # Set MyVariable to be writable by clients # starting! await server.start() # Create Callback for item event server.subscribe_server_callback(CallbackType.ItemSubscriptionCreated, create_monitored_items) server.subscribe_server_callback(CallbackType.ItemSubscriptionModified, modify_monitored_items) server.subscribe_server_callback(CallbackType.ItemSubscriptionDeleted, delete_monitored_items) while True: await asyncio.sleep(1)
async def start_servers(): """ Spin up two servers with identical configurations """ ports = [4840, 4841] urls = [] loop = asyncio.get_event_loop() for port in ports: server = Server() await server.init() url = f"opc.tcp://0.0.0.0:{port}/freeopcua/server/" urls.append(url) server.set_endpoint(url) server.set_server_name("FreeOpcUa Example Server {port}") # setup our own namespace uri = "http://examples.freeopcua.github.io" idx = await server.register_namespace(uri) myobj = await server.nodes.objects.add_object(idx, "MyObject") myvar = await myobj.add_variable(idx, "MyVariable", 6.7) await server.start() loop.create_task(server_var_update(server, myvar)) return urls, myvar
async def main(): # Create Server server = Server() await server.init() # Set server configuration server.set_endpoint(_opc_ua_server) server.set_server_name(_opc_ua_server_name) server.set_security_policy([ ua.SecurityPolicyType.NoSecurity, ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt, ua.SecurityPolicyType.Basic256Sha256_Sign ]) # Set namespace idx = await server.register_namespace(_opc_ua_namespace) # Create Sensor object with two properties sensor = await server.nodes.base_object_type.add_object_type(idx, "Sensor") await (await sensor.add_variable(idx, "value", 0.0)).set_modelling_rule(True) # Populate the address space sensor0 = await server.nodes.objects.add_object(idx, "Sensor0", sensor) await (await sensor0.add_property(idx, "id", 0)).set_modelling_rule(True) await (await sensor0.add_property(idx, "name", "Sensor0")).set_modelling_rule(True) # Start Server async with server: # Retrieve Sensor0 value variable, in order to read/write it sensor0_value_var = await sensor0.get_child([f"{idx}:value"]) while True: # Generate a random float between 0.0 and 100.0 sensor0_value = uniform(0.0, 100.0) # Write the value to trigger data change await sensor0_value_var.write_value(sensor0_value) # Wait 5 seconds before triggering next event await asyncio.sleep(5)
async def main(): # setup our server server = Server() await server.init() server.set_endpoint('opc.tcp://127.0.0.1:4840/opcua/') server.set_server_name("DevNet OPC-UA Test Server") # setup our own namespace, not really necessary but should as spec uri = 'http://devnetiot.com/opcua/' idx = await server.register_namespace(uri) # populating our address space # server.nodes, contains links to very common nodes like objects and root obj_vplc = await server.nodes.objects.add_object(idx, 'vPLC1') var_temperature = await obj_vplc.add_variable(idx, 'temperature', 0) var_pressure = await obj_vplc.add_variable(idx, 'pressure', 0) var_pumpsetting = await obj_vplc.add_variable(idx, 'pumpsetting', 0) # Read Sensor Data from Kaggle df = pd.read_csv("sensor.csv") # Only use sensor data from 03 and 01 (preference) sensor_data = pd.concat([df["sensor_03"], df["sensor_01"]], axis=1) _logger.info('Starting server!') async with server: # run forever and iterate over the dataframe while True: for row in sensor_data.itertuples(): # if below the mean use different setting - just for testing if row[1] < df["sensor_03"].mean(): setting = "standard" else: setting = "speed" # Writing Variables await var_temperature.write_value(float(row[1])) await var_pressure.write_value(float(row[2])) await var_pumpsetting.write_value(str(setting)) await asyncio.sleep(1)
async def main(): _logger = logging.getLogger('Server') #logger = logging.getLogger("asyncua.address_space") #logger = logging.getLogger("asyncua.internal_server") #logger = logging.getLogger("asyncua.binary_server_asyncio") #logger = logging.getLogger("asyncua.uaprocessor") server = Server() await server.init() server.set_endpoint("opc.tcp://0.0.0.0:16703/") server.set_server_name("Servidor OPC eMPC MA") await server.set_application_uri(uri="http://servidor-eMPC-MA.com/test/") server.set_security_policy([ua.SecurityPolicyType.NoSecurity]) server._permission_ruleset = None server._policyIDs = ["Anonymous"] server.certificate = None uri = "Servidor OPC eMPC MA" idx = await server.register_namespace(uri) await server.import_xml("deck_opcua.xml") _logger.info("Iniciando servidor OPC-UA...") _logger.info("Escuchando en: opc.tcp://localhost:16703/") # Crear instancia del controlador controlador = clase_MPC.Controlador() # starting! async with server: while True: await asyncio.sleep(0.01) command_run = await server.get_node("ns=6;s=command_run" ).get_value() if command_run == 1: _logger.info( f' [{datetime.now().strftime("%H:%M:%S.%f")[:-3]}]\t Node read: command_run = {command_run}' ) _logger.info( f' [{datetime.now().strftime("%H:%M:%S.%f")[:-3]}]\t Executing...' ) await controlador.recibir_variables(server) controlador.actualizar_arrays() ControlFlag = await server.get_node("ns=4;s=ControlFlag" ).read_value() _logger.info( f' [{datetime.now().strftime("%H:%M:%S.%f")[:-3]}]\t Node read: Control_Flag = {ControlFlag}' ) if ControlFlag: controlador.ejecutar() await server.write_attribute_value( server.get_node("ns=4;s=uq[1]").nodeid, ua.DataValue(controlador.uq1)) _logger.info( f' [{datetime.now().strftime("%H:%M:%S.%f")[:-3]}]\t \ Node written: uq1 = {controlador.uq1:.3f}') await server.write_attribute_value( server.get_node("ns=4;s=uFr[1]").nodeid, ua.DataValue(controlador.uFr1)) _logger.info( f' [{datetime.now().strftime("%H:%M:%S.%f")[:-3]}]\t \ Node written: uFr1 = {controlador.uFr1:.3f}' ) # Falta escribir todas las variables del controlador al servidor await controlador.escribir_variables(server) await server.write_attribute_value( server.get_node("ns=6;s=command_run").nodeid, ua.DataValue(0)) _logger.info( f' [{datetime.now().strftime("%H:%M:%S.%f")[:-3]}]\t Node written: command_run = 0' )
async def main(): # optional: setup logging logging.basicConfig(level=logging.WARN) # logger = logging.getLogger("asyncua.address_space") # logger.setLevel(logging.DEBUG) # logger = logging.getLogger("asyncua.internal_server") # logger.setLevel(logging.DEBUG) # logger = logging.getLogger("asyncua.binary_server_asyncio") # logger.setLevel(logging.DEBUG) # logger = logging.getLogger("asyncua.uaprocessor") # logger.setLevel(logging.DEBUG) # logger = logging.getLogger("asyncua.subscription_service") # logger.setLevel(logging.DEBUG) # now setup our server server = Server() await server.init() # server.set_endpoint("opc.tcp://localhost:4840/freeopcua/server/") server.set_endpoint("opc.tcp://0.0.0.0:4840/freeopcua/server/") server.set_server_name("FreeOpcUa Example Server") # setup our own namespace uri = "http://examples.freeopcua.github.io" idx = await server.register_namespace(uri) # get Objects node, this is where we should put our custom stuff objects = server.get_objects_node() # populating our address space await objects.add_folder(idx, "myEmptyFolder") myobj = await objects.add_object(idx, "MyObject") myvar = await myobj.add_variable(idx, "MyVariable", 6.7) await myvar.set_writable() # Set MyVariable to be writable by clients myarrayvar = await myobj.add_variable(idx, "myarrayvar", [6.7, 7.9]) await myobj.add_variable(idx, "myStronglytTypedVariable", ua.Variant([], ua.VariantType.UInt32)) await myobj.add_property(idx, "myproperty", "I am a property") await myobj.add_method(idx, "mymethod", func, [ua.VariantType.Int64], [ua.VariantType.Boolean]) inargx = ua.Argument() inargx.Name = "x" inargx.DataType = ua.NodeId(ua.ObjectIds.Int64) inargx.ValueRank = -1 inargx.ArrayDimensions = [] inargx.Description = ua.LocalizedText("First number x") inargy = ua.Argument() inargy.Name = "y" inargy.DataType = ua.NodeId(ua.ObjectIds.Int64) inargy.ValueRank = -1 inargy.ArrayDimensions = [] inargy.Description = ua.LocalizedText("Second number y") outarg = ua.Argument() outarg.Name = "Result" outarg.DataType = ua.NodeId(ua.ObjectIds.Int64) outarg.ValueRank = -1 outarg.ArrayDimensions = [] outarg.Description = ua.LocalizedText("Multiplication result") await myobj.add_method(idx, "multiply", multiply, [inargx, inargy], [outarg]) await myobj.add_method(idx, "multiply_async", multiply_async, [inargx, inargy], []) await myobj.add_method(idx, "func_async", func_async, [ua.VariantType.Int64], []) async with server: while True: await asyncio.sleep(1)
# logger.setLevel(logging.DEBUG) #logger = logging.getLogger("asyncua.internal_server") # logger.setLevel(logging.DEBUG) #logger = logging.getLogger("asyncua.binary_server_asyncio") # logger.setLevel(logging.DEBUG) #logger = logging.getLogger("asyncua.uaprocessor") # logger.setLevel(logging.DEBUG) logger = logging.getLogger("asyncua.subscription_service") logger.setLevel(logging.DEBUG) # now setup our server server = Server() #server.disable_clock() #server.set_endpoint("opc.tcp://localhost:4840/freeopcua/server/") server.set_endpoint("opc.tcp://0.0.0.0:4840/freeopcua/server/") server.set_server_name("FreeOpcUa Example Server") # setup our own namespace uri = "http://examples.freeopcua.github.io" idx = server.register_namespace(uri) # get Objects node, this is where we should put our custom stuff objects = server.nodes.objects # populating our address space myfolder = objects.add_folder(idx, "myEmptyFolder") myobj = objects.add_object(idx, "MyObject") myvar = myobj.add_variable(idx, "MyVariable", 6.7) myvar.set_writable() # Set MyVariable to be writable by clients # starting!
async def start(self): node_obj = {} variable_obj = {} # Data Type Mappings (OPCUA Datatypes to IoT Central Datatypes) variant_type = VariantType(self.logger) # OPCUA Server Setup # Here we setup the Server and add discovery try: # configure the endpoint opc_url = self.config["ServerUrlPattern"].format(ip = self.config["IPAddress"], port = self.config["Port"]) if not self.whatif: # init the server opc_server = Server() await opc_server.init() # set the endpoint and name opc_server.set_endpoint(opc_url) opc_server.set_server_name(self.config["ServerDiscoveryName"]) # set discovery await opc_server.set_application_uri(self.config["ApplicationUri"]) log_msg = "[SERVER CONFIG] ENDPOINT: {ep} APPLICATION URI: {au} APPLICATION NAME: {an}" self.logger.info(log_msg.format(ep = opc_url, au = self.config["ApplicationUri"], an = self.config["ServerDiscoveryName"])) # Setup root for Map Telemetry self.map_telemetry = self.create_map_telemetry(self.config["NameSpace"], self.config["DeviceCapabilityModelId"]) except Exception as ex: self.logger.error("[ERROR] %s" % ex) self.logger.error("[TERMINATING] We encountered an error in OPCUA Server Setup" ) return # OPCUA Server Setup Nodes # Here we setup the Nodes and Variables try: # Set NameSpace namespace = self.config["NameSpace"] self.logger.info("[NAMESPACE] %s" % namespace) if not self.whatif: id_namespace = await opc_server.register_namespace(namespace) node_count = 0 # Create our Nodes and Parameters for node in self.nodes: # Add Node and Begin Populating our Address Space if not self.whatif: # Create Node node_obj[node["Name"]] = await opc_server.nodes.objects.add_object(id_namespace, node["Name"]) self.logger.info("[NODE ID] %s" % node_obj[node["Name"]]) # Setup nodes for Map Telemetry self.map_telemetry["Nodes"].append(self.create_map_telemetry_node(node["Name"], str(node_obj[node["Name"]]), node["InterfacelId"], node["InterfaceInstanceName"])) for variable in node["Variables"]: variable_name = variable["DisplayName"] telemetry_name = variable["TelemetryName"] range_value = variable["RangeValues"][0] opc_variant_type = variant_type.map_variant_type(variable["IoTCDataType"]) log_msg = "[SETUP VARIABLE] NODE NAME: {nn} DISPLAY NAME: {dn} TELEMETRY NAME: {tn} RANGE VALUE: {rv} " \ "IoTC TYPE: {it} OPC VARIANT TYPE {ovt} OPC DATA TYPE {odt}" self.logger.info(log_msg.format(nn = node["Name"], dn = variable["DisplayName"], vn = variable["TelemetryName"], \ tn = variable["TelemetryName"], rv = variable["RangeValues"][0], it = variable["IoTCDataType"], \ ovt = opc_variant_type, odt = opc_variant_type)) if not self.whatif: # Create Node Variable nodeObject = await node_obj[node["Name"]].add_variable(id_namespace, telemetry_name, range_value) await nodeObject.set_writable() variable_obj[telemetry_name] = nodeObject self.map_telemetry_nodes_variables.append(self.create_map_telemetry_variable(variable_name, telemetry_name, str(variable_obj[telemetry_name]), variable["IoTCDataType"])) if not self.whatif: self.map_telemetry["Nodes"][node_count]["Variables"] = copy.copy(self.map_telemetry_nodes_variables) self.logger.info("[MAP] %s" % self.map_telemetry) self.map_telemetry_nodes_variables = [] node_count += 1 if not self.whatif: self.update_map_telemetry() except Exception as ex: self.logger.error("[ERROR] %s" % ex) self.logger.error("[TERMINATING] We encountered an error in OPCUA Server Setup for the Nodes and Variables" ) return # Start the server and save the address space try: if not self.whatif and self.cache_addr_space == "save": filename = Path(self.config["CacheAddrSpaceFileName"]) opc_server.start() opc_server.iserver.dump_address_space(filename) opc_server.stop() self.logger.info("[CACHE ADDRESS SPACE] Saved and Server Terminated. Now run with -c load") return except Exception as ex: self.logger.error("[ERROR] %s" % ex) self.logger.error("[TERMINATING] We encountered an error in OPCUA Server Setup for the Nodes and Variables" ) return # We start the server loop if we are not in WhatIf and the CacheAddrSpace # is null (not set) or load. If it was load, we pulled it from the cache on # server init()... if not self.whatif and (self.cache_addr_space == None or self.cache_addr_space == "load"): self.logger.info("[STARTING SERVER] %s" % opc_url) if self.cache_addr_space == "load": filename = Path(self.config["CacheAddrSpaceFileName"]) opc_server.iserver.load_address_space(filename) self.logger.info("[CACHE ADDRESS SPACE] Loaded Address Space Cache from %s" % filename) opc_server.start() async with opc_server: while True: await asyncio.sleep(self.config["ServerFrequencyInSeconds"]) for node in self.nodes: temp_dict = self.nodes_dict[node["Name"]] temp_dict_counter = self.nodes_dict_counter[node["Name"]] for variable in node["Variables"]: count = temp_dict_counter[variable["TelemetryName"]] sequence_count = temp_dict[variable["TelemetryName"]] if count > (sequence_count - 1): count = 0 # Choose the next value in the telemetry sequence for the variable self.nodes_dict_counter[node["Name"]][variable["TelemetryName"]] = (count + 1) value = variable["RangeValues"][count] if not self.whatif: await variable_obj[variable["TelemetryName"]].write_value(value) log_msg = "[LOOP] {nn} {tn} {vw} {tc} SEQ({sc}) CUR({cc})" self.logger.info(log_msg.format(nn = node["Name"], tn = variable["TelemetryName"], vw = value, tc = count, sc = temp_dict[variable["TelemetryName"]], cc = temp_dict_counter[variable["TelemetryName"]])) else: while True: await asyncio.sleep(self.config["ServerFrequencyInSeconds"]) for node in self.nodes: temp_dict = self.nodes_dict[node["Name"]] temp_dict_counter = self.nodes_dict_counter[node["Name"]] for variable in node["Variables"]: count = temp_dict_counter[variable["TelemetryName"]] sequence_count = temp_dict[variable["TelemetryName"]] if count > (sequence_count - 1): count = 0 # Choose the next value in the telemetry sequence for the variable self.nodes_dict_counter[node["Name"]][variable["TelemetryName"]] = (count + 1) value = variable["RangeValues"][count] log_msg = "[LOOP] {nn} {tn} {vw} {tc} SEQ({sc}) CUR({cc})" self.logger.info(log_msg.format(nn = node["Name"], tn = variable["TelemetryName"], vw = value, tc = count, sc = temp_dict[variable["TelemetryName"]], cc = temp_dict_counter[variable["TelemetryName"]])) return
class OpcUaServer(): def __init__(self, Log, WhatIf): self.logger = Log self.whatif = WhatIf self.opcua_server_instance = None # Namespaces self.opcua_id_namespace_twins = None self.opcua_id_namespace_gateways = None self.opcua_id_namespace_devices = None # Load configuration self.config = [] self.load_config() # Load Device Mapping self.devicescache = [] self.load_devicescache() self.node_instances = {} self.variable_instances = {} # Telemetry Mapping self.map_telemetry = [] self.map_telemetry_devices = [] self.map_telemetry_interfaces = [] self.map_telemetry_interfaces_variables = [] # meta self.application_uri = None self.namespace = None self.device_capability_model_id = None self.device_capability_model = [] self.device_name_prefix = None self.ignore_interface_ids = [] # ------------------------------------------------------------------------------- # Function: run # Usage: The start function starts the OPC Server # ------------------------------------------------------------------------------- async def run(self): # OPCUA Server Run try: async with self.opcua_server_instance: while True: await asyncio.sleep(self.config["ServerFrequencyInSeconds"] ) self.logger.info("[SERVER LOOP] STARTING:") for device in self.map_telemetry["Devices"]: for interface in device["Interfaces"]: for variable in interface["Variables"]: # Get the values from our ranges we are writing value = variable["RangeValues"][ int(variable["RangeValueCurrent"]) - 1] self.logger.info("[SERVER LOOP] VALUE: %s" % value) # Update our iterator and boundaries if int(variable["RangeValueCurrent"]) < int( variable["RangeValueCount"]): variable["RangeValueCurrent"] = int( variable["RangeValueCurrent"]) + 1 else: variable["RangeValueCurrent"] = 1 print("int(variable[RangeValueCurrent]) %s" % int(variable["RangeValueCurrent"])) print("int(variable[RangeValueCount]) %s" % int(variable["RangeValueCount"])) variable_node_instance = self.opcua_server_instance.get_node( variable["NodeId"]) await variable_node_instance.write_value(value) return except Exception as ex: self.logger.error("[ERROR] %s" % ex) self.logger.error( "[TERMINATING] We encountered an error in OpcUaServer::run()") # ------------------------------------------------------------------------------- # Function: stop # Usage: The start function starts the OPC Server # ------------------------------------------------------------------------------- async def stop(self): await self.opcua_server_instance.stop() return # ------------------------------------------------------------------------------- # Function: setup # Usage: The setup function preps the configuration for the OPC Server # ------------------------------------------------------------------------------- async def setup(self): # OPCUA Server Setup try: # configure the endpoint opc_url = self.config["ServerUrlPattern"].format( ip=self.config["IPAddress"], port=self.config["Port"]) # init the server self.opcua_server_instance = Server() await self.opcua_server_instance.init() self.opcua_server_instance.set_endpoint(opc_url) self.opcua_server_instance.set_server_name( self.config["ServerDiscoveryName"]) await self.opcua_server_instance.set_application_uri( self.config["ApplicationUri"]) self.logger.info("[SERVER CONFIG] ENDPOINT: %s" % opc_url) self.logger.info("[SERVER CONFIG] APPLICATION URI: %s" % self.config["ApplicationUri"]) self.logger.info("[SERVER CONFIG] APPLICATION NAME: %s" % self.config["ServerDiscoveryName"]) # Set NameSpace(s) for pattern in self.config["IoTCentralPatterns"]: if pattern["ModelType"] == "Twins": self.opcua_id_namespace_twins = await self.opcua_server_instance.register_namespace( pattern["NameSpace"]) if pattern["ModelType"] == "Gateways": self.opcua_id_namespace_gateways = await self.opcua_server_instance.register_namespace( pattern["NameSpace"]) if pattern["ModelType"] == "Devices": self.opcua_id_namespace_devices = await self.opcua_server_instance.register_namespace( pattern["NameSpace"]) self.logger.info("[SERVER CONFIG] NAMESPACE TWINS: %s" % self.opcua_id_namespace_twins) self.logger.info("[SERVER CONFIG] NAMESPACE TWINS: %s" % self.opcua_id_namespace_gateways) self.logger.info("[SERVER CONFIG] NAMESPACE DEVICES: %s" % self.opcua_id_namespace_devices) except Exception as ex: self.logger.error("[ERROR] %s" % ex) self.logger.error( "[TERMINATING] We encountered an error in OPCUA Server Setup::setup()" ) return # ------------------------------------------------------------------------------- # Function: load_nodes_from_devicecache # Usage: The load_nodes_from_devicecache function enumerates the # devicescache.json and creates a node for each kind of # Iot Central Device. It looks at a Twin and registers all # of the interfaces and for devices, registers the interface # ------------------------------------------------------------------------------- async def load_nodes_from_devicecache(self): # OPCUA Server Setup try: # Setup root for map telemetry configuration file self.logger.info("[SERVER] INITIATED MAP TELEMETRY FILE: %s" % self.map_telemetry) self.map_telemetry = self.create_map_telemetry_root( self.config["NameSpace"]) # Data Type Mappings (OPCUA Datatypes to IoT Central Datatypes) variant_type = VariantType(self.logger) device_count = 0 for device in self.devicescache["Devices"]: self.logger.info("[SERVER] DEVICE TYPE: %s" % device["DeviceType"]) self.logger.info("[SERVER] DEVICE NAME: %s" % device["Name"]) # DEVICE PER NODE namespace_id = None if device["DeviceType"] == "Twins": namespace_id = self.opcua_id_namespace_twins elif device["DeviceType"] == "Gateways": namespace_id = self.opcua_id_namespace_devices elif device["DeviceType"] == "Devices": namespace_id = self.opcua_id_namespace_devices self.logger.info("[SERVER] DEVICE TYPE: %s" % device["DeviceType"]) self.logger.info("[SERVER] DEVICE NAME: %s" % device["Name"]) self.node_instances[device[ "Name"]] = await self.opcua_server_instance.nodes.objects.add_object( namespace_id, device["Name"]) self.logger.info("[SERVER] NODE ID: %s" % self.node_instances[device["Name"]]) # Add the device info to the map telemetry file self.map_telemetry_devices.append( self.create_map_telemetry_device( device["Name"], str(self.node_instances[device["Name"]]), device["DeviceType"], device["DeviceCapabilityModelId"])) self.logger.info( "[SERVER] ADDED DEVICE TO MAP TELEMETRY FILE: %s" % self.map_telemetry_devices) interface_count = 0 for interface in device["Interfaces"]: # Add the interface info to the map telemetry file self.map_telemetry_interfaces.append( self.create_map_telemetry_interface( interface["Name"], interface["InterfacelId"], interface["InterfaceInstanceName"])) self.logger.info( "[SERVER] ADDED INTERFACE TO MAP TELEMETRY FILE: %s" % self.map_telemetry_interfaces) config_interface = [ obj for obj in self.config["Nodes"] if obj["InterfaceInstanceName"] == interface["InterfaceInstanceName"] ] for variable in config_interface[0]["Variables"]: variable_name = variable["DisplayName"] telemetry_name = variable["TelemetryName"] range_value = variable["RangeValues"][0] opc_variant_type = variant_type.map_variant_type( variable["IoTCDataType"]) # Log Verbose Feedback log_msg = "[SERVER] SETUP VARIABLES: *DISPLAY NAME: {dn} TELEMETRY NAME: {tn} *RANGE VALUE: {rv} *IoTC TYPE: {it} *OPC VARIANT TYPE {ovt} *OPC DATA TYPE {odt}" self.logger.info( log_msg.format(dn=variable["DisplayName"], vn=variable["TelemetryName"], tn=variable["TelemetryName"], rv=variable["RangeValues"][0], it=variable["IoTCDataType"], ovt=opc_variant_type, odt=opc_variant_type)) # Create Node Variable nodeObject = await self.node_instances[ device["Name"] ].add_variable(namespace_id, telemetry_name, range_value) await nodeObject.set_writable() self.variable_instances[telemetry_name] = nodeObject # Append the variables to the Interfaces collection for the map telemetry file self.map_telemetry_interfaces_variables.append( self.create_map_telemetry_variable( variable_name, str(self.variable_instances[telemetry_name]), telemetry_name, variable["IoTCDataType"], variable["Frequency"], variable["OnlyOnValueChange"], variable["RangeValues"])) self.logger.info( "[SERVER] MAP TELEMETRY VARIABLES APPEND: %s" % self.map_telemetry_interfaces[interface_count]) # Save the variables to the Map Telemetry [Interface] Collection self.map_telemetry_interfaces[interface_count][ "Variables"] = self.map_telemetry_interfaces_variables self.logger.info( "[SERVER] MAP TELEMETRY INTERFACES APPEND: %s" % self.map_telemetry_interfaces[interface_count]) interface_count = interface_count + 1 self.map_telemetry_interfaces_variables = [] # Append the Interfaces to the Devices collection for the map telemetry file self.map_telemetry_devices[device_count][ "Interfaces"] = self.map_telemetry_interfaces device_count = device_count + 1 self.map_telemetry_interfaces = [] # Append the Devices to the Root collection for the map telemetry file self.map_telemetry["Devices"] = self.map_telemetry_devices self.logger.info("[SERVER] MAP TELEMETRY: %s" % self.map_telemetry) self.update_map_telemetry() except Exception as ex: self.logger.error("[ERROR] %s" % ex) self.logger.error( "[TERMINATING] We encountered an error in OPCUA Server Setup::load_nodes_from_devicecache()" ) return # ------------------------------------------------------------------------------- # Function: load_config # Usage: Loads the configuration from file # ------------------------------------------------------------------------------- def load_config(self): config = Config(self.logger) self.config = config.data return # ------------------------------------------------------------------------------- # Function: load_devicescache # Usage: Loads the Devices that have been registered and provisioned. # This file is generated from the as-is state of the system # when the OpcUaServer is started. # ------------------------------------------------------------------------------- def load_devicescache(self): devicescache = DevicesCache(self.logger) self.devicescache = devicescache.data return # ------------------------------------------------------------------------------- # Function: create_map_telemetry_root # Usage: Sets the root for the Map Telemetry configuration file # ------------------------------------------------------------------------------- def create_map_telemetry_root(self, NameSpace): mapTelemetry = { "NameSpace": NameSpace, "Created": str(datetime.datetime.now()), "Devices": [] } return mapTelemetry # ------------------------------------------------------------------------------- # Function: create_map_telemetry_device # Usage: Adds a device to the map telemetry configuration file # ------------------------------------------------------------------------------- def create_map_telemetry_device(self, Name, OpcUaNodeId, DeviceType, DeviceCapabilityModelId): mapTelemetry = { "Name": Name, "Connected": False, "ConnectedDateTime": "", "NodeId": OpcUaNodeId, "DeviceType": DeviceType, "DeviceCapabilityModelId": DeviceCapabilityModelId, "Interfaces": [] } return mapTelemetry # ------------------------------------------------------------------------------- # Function: create_map_telemetry_interface # Usage: Sets the node for the Map Telemetry configuration file # ------------------------------------------------------------------------------- def create_map_telemetry_interface(self, Name, InterfacelId, InterfaceInstanceName): mapTelemetry = { "Name": Name, "InterfacelId": InterfacelId, "InterfaceInstanceName": InterfaceInstanceName, "Variables": [] } return mapTelemetry # ------------------------------------------------------------------------------- # Function: create_map_telemetry_variable # Usage: Sets the variable for the Map Telemetry configuration file # ------------------------------------------------------------------------------- def create_map_telemetry_variable(self, DisplayName, OpcUaNodeId, TelemetryName, IoTCDataType, Frequency, OnlyOnValueChange, RangeValues): mapTelemetry = { "DisplayName": DisplayName, "NodeId": OpcUaNodeId, "TelemetryName": TelemetryName, "IoTCDataType": IoTCDataType, "Frequency": Frequency, "OnlyOnValueChange": OnlyOnValueChange, "RangeValueCount": len(RangeValues), "RangeValueCurrent": 1, "RangeValues": RangeValues } return mapTelemetry # ------------------------------------------------------------------------------- # Function: update_map_telemetry # Usage: Saves the generated Map Telemetry File # ------------------------------------------------------------------------------- def update_map_telemetry(self): map_telemetry_file = MapTelemetry(self.logger) map_telemetry_file.update_file(self.map_telemetry) return
async def main(): server = Server() await server.init() server.set_endpoint('opc.tcp://0.0.0.0:4840/UA/SampleServer') server.set_server_name('Custom structure demo server') # idx name will be used later for creating the xml used in data type dictionary url = 'http://examples.freeopcua.github.io' idx = await server.register_namespace(url) dict_builder = DataTypeDictionaryBuilder(server, idx, url, 'MyDictionary') await dict_builder.init() # add one basic structure basic_struct_name = 'BasicStructure' basic_struct = await dict_builder.create_data_type(basic_struct_name) basic_struct.add_field('ID', ua.VariantType.Int32) basic_struct.add_field('Gender', ua.VariantType.Boolean) basic_struct.add_field('Comments', ua.VariantType.String) # add an advance structure which uses our basic structure nested_struct_name = 'NestedStructure' nested_struct = await dict_builder.create_data_type(nested_struct_name) nested_struct.add_field('Name', ua.VariantType.String) nested_struct.add_field('Surname', ua.VariantType.String) # add a list of simple structure as field nested_struct.add_field('StuffArray', basic_struct, is_array=True) # this operation will write the OPC dict string to our new data type dictionary # namely the 'MyDictionary' await dict_builder.set_dict_byte_string() # get the working classes await server.load_type_definitions() # Create one test structure in our address space basic_var = await server.nodes.objects.add_variable( idx, 'BasicStruct', None, datatype=basic_struct.data_type, ) await basic_var.set_writable() var = ua.BasicStructure() var.ID = 3 var.Gender = True var.Comments = 'Test string' await basic_var.write_value(var) # Create one advance test structure nested_var = await server.nodes.objects.add_variable( idx, 'NestedStruct', None, datatype=nested_struct.data_type, ) await nested_var.set_writable() var2 = ua.NestedStructure() var2.StuffArray = [var, var] var2.Name = 'Max' var2.Surname = 'Karl' await nested_var.write_value(var2) async with server: # see the xml value in our customized dictionary 'MyDictionary', only for debugging use print(getattr(dict_builder, '_type_dictionary').get_dict_value()) # values can be write back and retrieved with the codes below. v1 = await basic_var.read_value() v2 = await nested_var.read_value() #embed() while True: await asyncio.sleep(1)
async def _uaserver(): parser = argparse.ArgumentParser( description= "Run an example OPC-UA server. By importing xml definition and using uawrite command line, it is even possible to expose real data using this server" ) # we setup a server, this is a bit different from other tool so we do not reuse common arguments parser.add_argument( "-u", "--url", help="URL of OPC UA server, default is opc.tcp://0.0.0.0:4840", default='opc.tcp://0.0.0.0:4840', metavar="URL") parser.add_argument( "-v", "--verbose", dest="loglevel", choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], default='WARNING', help="Set log level") parser.add_argument( "-x", "--xml", metavar="XML_FILE", help="Populate address space with nodes defined in XML") parser.add_argument("-p", "--populate", action="store_true", help="Populate address space with some sample nodes") parser.add_argument( "-c", "--disable-clock", action="store_true", help= "Disable clock, to avoid seeing many write if debugging an application" ) parser.add_argument( "-s", "--shell", action="store_true", help="Start python shell instead of randomly changing node values") parser.add_argument("--certificate", help="set server certificate") parser.add_argument("--private_key", help="set server private key") args = parser.parse_args() logging.basicConfig(format="%(levelname)s: %(message)s", level=getattr(logging, args.loglevel)) server = Server() server.set_endpoint(args.url) if args.certificate: server.load_certificate(args.certificate) if args.private_key: server.load_private_key(args.private_key) server.disable_clock(args.disable_clock) server.set_server_name("FreeOpcUa Example Server") if args.xml: server.import_xml(args.xml) if args.populate: @uamethod def multiply(parent, x, y): print("multiply method call with parameters: ", x, y) return x * y uri = "http://examples.freeopcua.github.io" idx = server.register_namespace(uri) objects = server.nodes.objects myobj = objects.add_object(idx, "MyObject") mywritablevar = myobj.add_variable(idx, "MyWritableVariable", 6.7) mywritablevar.set_writable( ) # Set MyVariable to be writable by clients myvar = myobj.add_variable(idx, "MyVariable", 6.7) myarrayvar = myobj.add_variable(idx, "MyVarArray", [6.7, 7.9]) myprop = myobj.add_property(idx, "MyProperty", "I am a property") mymethod = myobj.add_method( idx, "MyMethod", multiply, [ua.VariantType.Double, ua.VariantType.Int64], [ua.VariantType.Double]) try: await server.init() async with server: if args.shell: embed() elif args.populate: count = 0 while True: await asyncio.sleep(1) myvar.write_value(math.sin(count / 10)) myarrayvar.write_value( [math.sin(count / 10), math.sin(count / 100)]) count += 1 else: while True: await asyncio.sleep(1) except OSError as e: print(e) sys.exit(1) except KeyboardInterrupt: pass sys.exit(0)
async def main(): # setup our server server = Server() await server.init() server.set_endpoint('opc.tcp://0.0.0.0:4840') # server.set_endpoint('opc.tcp://192.168.100.170:4840') # setup our own namespace, not really necessary but should as spec server.set_server_name("SU OPC UA Server read test") # server.set_security_policy([ # ua.SecurityPolicyType.NoSecurity, # ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt, # ua.SecurityPolicyType.Basic256Sha256_Sign]) uri = 'http://examples.freeopcua.github.io' idx = await server.register_namespace(uri) await server.import_xml('kuka2.xml') ## get Objects node, this is where we should put our nodes # await server.start() # root = server.get_root_node() # # _logger.info("Root node is: %r", root) # objects = server.get_objects_node() # # _logger.info("Objects node is: %r", objects) # # print(server.get_namespace_index()) # # myvar = await root.get_child(["0:Objects","3:KUKA_1",'0:x']) # # bo=await objects.get_children() # myvar = await objects.get_children() # # myvar. # # myvar(['0:actuator1']) # print(myvar) # root = server.get_root_node() # actuator1 = await root.get_child(["0:Objects", "0:Actuator", '0:actuator1']) # actuator2 = await root.get_child(["0:Objects", "0:Actuator", '0:actuator2']) # actuator3 = await root.get_child(["0:Objects", "0:Actuator", '0:actuator3']) # actuator4 = await root.get_child(["0:Objects", "0:Actuator", '0:actuator4']) # degree1 = await root.get_child(["0:Objects", "0:Drulling_Robot", '0:Degree', '0:degree1']) # degree2 = await root.get_child(["0:Objects", "0:Drulling_Robot", '0:Degree', '0:degree2']) # degree3 = await root.get_child(["0:Objects", "0:Drulling_Robot", '0:Degree', '0:degree3']) # degree4 = await root.get_child(["0:Objects", "0:Drulling_Robot", '0:Degree', '0:degree4']) # degree5 = await root.get_child(["0:Objects", "0:Drulling_Robot", '0:Degree', '0:degree5']) # degree6 = await root.get_child(["0:Objects", "0:Drulling_Robot", '0:Degree', '0:degree6']) # degree7 = await root.get_child(["0:Objects", "0:Riveting_Robot", '0:Degree_R', '0:degree7']) # degree8 = await root.get_child(["0:Objects", "0:Riveting_Robot", '0:Degree_R', '0:degree8']) # degree9 = await root.get_child(["0:Objects", "0:Riveting_Robot", '0:Degree_R', '0:degree9']) # degree10 = await root.get_child(["0:Objects", "0:Riveting_Robot", '0:Degree_R', '0:degree10']) # degree11 = await root.get_child(["0:Objects", "0:Riveting_Robot", '0:Degree_R', '0:degree11']) # degree12 = await root.get_child(["0:Objects", "0:Riveting_Robot", '0:Degree_R', '0:degree12']) # x1 = await root.get_child(["0:Objects", "0:Drulling_Robot", '0:Joint', '0:x1']) # x2 = await root.get_child(["0:Objects", "0:Riveting_Robot", '0:Joint_R', '0:x2']) # y1 = await root.get_child(["0:Objects", "0:Drulling_Robot", '0:Joint', '0:y1']) # y2 = await root.get_child(["0:Objects", "0:Riveting_Robot", '0:Joint_R', '0:y2']) # z1 = await root.get_child(["0:Objects", "0:Drulling_Robot", '0:Joint', '0:z1']) # z2 = await root.get_child(["0:Objects", "0:Riveting_Robot", '0:Joint_R', '0:z2']) # a1 = await root.get_child(["0:Objects", "0:Drulling_Robot", '0:Joint', '0:a1']) # a2 = await root.get_child(["0:Objects", "0:Riveting_Robot", '0:Joint_R', '0:a2']) # b1 = await root.get_child(["0:Objects", "0:Drulling_Robot", '0:Joint', '0:b1']) # b2 = await root.get_child(["0:Objects", "0:Riveting_Robot", '0:Joint_R', '0:b2']) # c1 = await root.get_child(["0:Objects", "0:Drulling_Robot", '0:Joint', '0:c1']) # c2 = await root.get_child(["0:Objects", "0:Riveting_Robot", '0:Joint_R', '0:c2']) # normal1 = await root.get_child(["0:Objects", "0:Normals",'0:normal1']) # normal2 = await root.get_child(["0:Objects", "0:Normals",'0:normal2']) # normal3 = await root.get_child(["0:Objects", "0:Normals",'0:normal3']) # data=[actuator1,actuator2,actuator3,actuator4,degree1,degree2,degree3,degree4, # degree5,degree6,degree7,degree8,degree9,degree10,degree11,degree12,x1,x2, # y1,y2,z1,z2,a1,a2,b1,b2,c1,c2,normal1,normal2,normal3] # print(f'{bo}') # aa=await server.get_objects_node().get_child( # ["0:Objects", "0:Server"]).nodeid # print(aa) # myobject2_type_nodeid = server.get_root_node().get_child( # ["0:Types", "0:ObjectTypes", "0:BaseObjectType", "%d:StateType" % idx]).nodeid # myobject3_type_nodeid = server.get_node(ua.ObjectIds.BaseObjectType).get_child( # ["%d:MyCustomObjectType" % idx]).nodeid # # # Node objects have methods to read and write node attributes as well as browse or populate address space # print("Children of root are: %r", await objects.get_children()) # objects = server.get_objects_node() # aa = objects.get_child([f"{idx}:sss"]) # aa.write_value(True) # print(aa) # todo 对于变量固定的 适合用xml生成信息模型 # kukaobj1=await objects.add_object(idx,'KUKA_ROBOT_1') # kukafloder1=await kukaobj1.add_folder(idx,'Axis') # kukavar1 = kukafloder1.add_variable() # kukavar2 = kukafloder1.add_variable() # kukavar3 = kukafloder1.add_variable() # kukavar4 = kukafloder1.add_variable() # kukavar5 = kukafloder1.add_variable() # kukavar6 = kukafloder1.add_variable() # kukafloder2 = await kukaobj1.add_folder(idx, 'Degree') # kukavar7 = kukafloder2.add_variable() # kukavar8 = kukafloder1.add_variable() # kukavar9 = kukafloder1.add_variable() # kukavar10 = kukafloder1.add_variable() # kukavar11 = kukafloder1.add_variable() # kukavar12 = kukafloder1.add_variable() # myobj = await objects.add_object(idx, 'MyObject') # # Set MyVariable to be writable by clients # await myvar.set_writable() # await objects.add_method( # ua.NodeId('ServerMethod', 2), ua.QualifiedName('ServerMethod', 2), # func, [ua.VariantType.Int64], [ua.VariantType.Int64] # ) _logger.info('Starting server!') async with server: count = 0 while True: await asyncio.sleep(1) count += 0.1
class DataServer: """ OPC-UA Asynchronous Data Server NOTE: attributes that are specified in the format 'Node, data_type' mean that the attribute is an OPC-UA node that holds the specified data type Attributes: ---------- __server_url: str IP and Port to which the OPC-UA server will send data to. Format: opc.tcp://[IP]:[PORT] __address_space_name: str OPC-UA server data address space name __server_name: str name of the OPC-UA server __server: Server asynchronous OPC-UA server __address_space: Node OPC-UA server address space __sensor: Node main objects that holds all variables shared by the server __voltage_ramp: Node, list list of voltages to apply to plasma sensor __voltage_sweep_time: Node, float wait time between measurements (in .1 seconds) __current_filter: Node, str Data filter to apply to final list of current Values: 'None' -> No filter applied 'SOS' -> SOS filter __sensor_to_use: Node, str Plasma sensor to use for measurements Values: 'SLP', 'DLP', 'HEA' SLP -> Single Langmuir Probe DLP -> Double Langmuir Probe HEA -> Hyperbolic Energy Analyzer __abort_measurements: Node, bool Signals the server to abort a measurement __is_client_data_fully_sent: Node, bool Client signal that it has finished sending measurement parameters __is_measurements_running: Node, bool Specifies if the server is running a measurement __is_measurements_finished: Node, bool Server signal that specifies that it has finished calculating measurements __current_measurements: Node, str Holds the calculated 'current' from the plasma sensor __time_measurements: Node, str Times in which the plasma sensor's current was calculated Methods: _______ __initialize_server(): Initializes the server and all nodes handled by the server __flush_measurement_parameters(): Flushes the values of a measurement so conflicts can be avoided when doing new measurements __check_client_data_received(): Checks if client data has been fully received __run_measurements(): Runs measurements with entered measurement parameters __run_SLP_measurements(): Runs measurements for a Single Langmuir Probe (SLP) plasma sensor __run_DLP_measurements(): Runs measurements for a Double Langmuir Probe (DLP) plasma sensor __run_HEA_measurements(): Runs measurements for a Hyperbolic Energy Analyzer (HEA) plasma sensor start_server(): Awaits for a client connection and serves server data indefinitely and asynchronously to server url. start_server_task(): Starts server synchronously (without using asyncio) close_server(): Stops server from serving data stop_measurements(): Signals the server to abort generating measurements set_server_url(server_url: str): Change server url to serve data on get_server_url(): Returns server url to serve data on get_server_name(): Returns server name Shares the following data with clients: -------------------------------------- class Sensor: voltage_ramp: list, writeable List specifying all voltages to read from a plasma sensor. Format: [2, 2.5, 3] -> all values are voltages voltage_sweep_time: int, writeable Wait time between measurements (in seconds) current_filter: str, writeable Data filter to apply to final list of current Values: 'None' -> No filter applied 'SOS' -> SOS filter sensor_to_use: str, writeable Plasma sensor to use for measurement Values: 'SLP', 'DLP', 'HEA' SLP -> Single Langmuir Probe DLP -> Double Langmuir Probe HEA -> Hyperbolic Energy Analyzer abort_measurements: bool, writeable if set to true, aborts running measurements is_client_data_fully_sent: bool, writeable if set to true, client data has been fully received and measurement can be run is_measurements_running: bool, not writeable if set to true, server is currently running measurements is_measurements_finished: bool, not writeable if set to true, server is done calculating measurements NOTE: the current measurements and time measurements holders are currently implemented as string data structures. this should be changed to use lists as it is more appropriate current_measurements: str, not writeable returns the values for all current measurements divided by commas ',' time_measurements: str, not writeable returns the values for all times in which measurements where taken divided by commas ',' """ __server_url = "" __address_space_name = "OPCUA_PLASMA_MEASUREMENTS_SERVER" __server_name = "Plasma Meter Server" __server = None __address_space = None __sensor = None __voltage_ramp = None __voltage_sweep_time = None __current_filter = None __sensor_to_use = None __abort_measurements = None __is_client_data_fully_sent = None __is_measurements_running = None __is_measurements_finished = None __current_measurements = None __time_measurements = None def __init__(self, server_url: str = "opc.tcp://127.0.0.1:9002"): """ Initialize with server url Default url is set to local loopback address :param server_url: IP and Port to which the OPC-UA server will send data to. Format: opc.tcp://[IP]:[PORT] """ self.set_server_url(server_url) async def __initialize_server(self): """ Initializes the server and all nodes handled by the server :return: None """ # instantiates server self.__server = Server() await self.__server.init() self.__server.set_endpoint(self.get_server_url()) self.__server.set_server_name(self.get_server_name()) # sets server address space self.__address_space = await self.__server.register_namespace(self.__address_space_name) # sensor object self.__sensor = await self.__server.nodes.objects.add_object(self.__address_space, 'sensor') # self.__sensor object variables (see method docstring for description of each) self.__voltage_ramp = await self.__sensor.add_variable(self.__address_space, "voltage_ramp", val='') self.__voltage_sweep_time = await self.__sensor.add_variable(self.__address_space, "voltage_sweep_time", 0.0) self.__current_filter = await self.__sensor.add_variable(self.__address_space, "current_filter", val='None') self.__sensor_to_use = await self.__sensor.add_variable(self.__address_space, "sensor_to_use", '') self.__abort_measurements = await self.__sensor.add_variable(self.__address_space, "stop_measurements", False) self.__is_client_data_fully_sent = await self.__sensor.add_variable(self.__address_space, "is_client_data_fully_sent", val=False) self. __is_measurements_running = await self.__sensor.add_variable(self.__address_space, "is_measurements_running", val=False) self.__is_measurements_finished = await self.__sensor.add_variable(self.__address_space, "is_measurements_finished", val=False) self.__current_measurements = await self.__sensor.add_variable(self.__address_space, "current_measurements", val='') self.__time_measurements = await self.__sensor.add_variable(self.__address_space, "time_measurements", val='') # makes the sensor object's variables that the client can modify writeable await self.__voltage_ramp.set_writable() await self.__voltage_sweep_time.set_writable() await self.__sensor_to_use.set_writable() await self.__abort_measurements.set_writable() await self.__is_client_data_fully_sent.set_writable() await self.__current_filter.set_writable() async def __flush_measurement_parameters(self): """ Flushes the values of a measurement so conflicts can be avoided when doing new measurements :return: None """ # flushes measurement parameters await self.__voltage_ramp.set_value('') await self.__voltage_sweep_time.set_value(0.0) await self.__sensor_to_use.set_value('') await self.__is_client_data_fully_sent.set_value(False) await self.__is_measurements_finished.set_value(False) await self.__abort_measurements.set_value(False) async def __check_client_data_received(self): """ Checks if client data has been fully received :return: True if client data has been fully received, false if not :rtype: bool """ if await self.__is_client_data_fully_sent.get_value(): return True else: return False async def __run_measurements(self): """ Runs measurements with entered measurement parameters :return: None """ await self.__is_measurements_running.set_value(True) # runs measurements for the selected plasma sensor if (await self.__sensor_to_use.get_value()) == 'SLP': await self.__run_SLP_measurements() elif (await self.__sensor_to_use.get_value()) == 'DLP': await self.__run_DLP_measurements() elif (await self.__sensor_to_use.get_value()) == 'HEA': await self.__run_HEA_measurements() else: print('SERVER ERROR: selected sensor not implemented') await self.__is_measurements_running.set_value(False) async def __run_SLP_measurements(self): """ Runs measurements for a Single Langmuir Probe (SLP) plasma sensor :return: None """ # variables to generate measurements measurement_voltage_sweep_time = await self.__voltage_sweep_time.get_value() measurement_voltage_ramp = await self.__voltage_ramp.get_value() number_of_measurements = float(len(measurement_voltage_ramp)) step_time = float(measurement_voltage_sweep_time) / number_of_measurements current_filter = await self.__current_filter.get_value() # current sensing resistor of the Single Langmuir Probe shunt_resistor = 200.0 # holds the generated current and time values calculated_current_measurements = '' calculated_time_measurements = '' voltages = [float(v) for v in (await self.__voltage_ramp.get_value()).split(',')] # runs measurements with no current filter if current_filter == 'None': # does measurements for each voltage for voltage in voltages: # stop measurements if abort flag is raised if await self.__abort_measurements.get_value(): break # NOTE: voltage must be sent to sensor before retrieving the current\ # NOTE: time.sleep was used instead of asyncio.sleep to preserve miliseconds needed for runtime await asyncio.sleep(step_time) current = (voltage * -2.0) / shunt_resistor # print(time.time()) # appends new current and time measurements # NOTE: join was used instead of string concatenation to preserve real time speed of application calculated_current_measurements = ''.join((calculated_current_measurements, str(current), ',')) calculated_time_measurements = ''.join((calculated_time_measurements, str(datetime.datetime.now()), ',')) # continuously updates current and time values await self.__current_measurements.set_value(calculated_current_measurements[:-1]) await self.__time_measurements.set_value(calculated_time_measurements[:-1]) # uses SOS filter for current elif current_filter == 'SOS': # does measurements for each voltage for voltage in voltages.split(','): # stop measurements if abort flag is raised if await self.__abort_measurements.get_value(): break # NOTE: voltage must be sent to sensor before retrieving the current await asyncio.sleep(step_time) current = (float(voltage) * 2.0) / shunt_resistor # appends new current and time measurements calculated_current_measurements += str(current) + ',' calculated_time_measurements += str(datetime.datetime.now()) + ',' # continuously updates current and time values await self.__current_measurements.set_value(calculated_current_measurements[:-1]) await self.__time_measurements.set_value(calculated_time_measurements[:-1]) else: print(f'SERVER ERROR: current filter not properly specified. If none is used, please select "None"') print(f'the current is {calculated_current_measurements}') print(f'the time is {calculated_time_measurements}') # transmits calculated data await self.__current_measurements.set_value(calculated_current_measurements[:-1]) await self.__time_measurements.set_value(calculated_time_measurements[:-1]) await self.__is_measurements_finished.set_value(True) print(f"SERVER RESULT TRANSMITTING: server transmitting data result on {self.get_server_url()}") await asyncio.sleep(10) async def __run_DLP_measurements(self): """ Runs measurements for a Double Langmuir Probe (DLP) plasma sensor :return: None """ pass async def __run_HEA_measurements(self): """ Runs measurements for a Hyperbolic Energy Analyzer (HEA) plasma sensor :return: None """ pass async def start_server(self): """ Awaits for a client connection and serves server data indefinitely and asynchronously to server url. Requires to be called as an asyncio function Requires voltage_ramp, voltage_sweep_time, sensor_to_use, and client_data_fully_sent to be sent by client to run :return: None """ await self.__initialize_server() # transmits that server is open to receive data print(f"SERVER STARTED: server open at {self.get_server_url()}") async with self.__server: # waits to receive server data while True: # print('SERVER STILL AWAITING') # client data received and measurements can start if await self.__check_client_data_received(): print(f"SERVER DATA RECEIVED: server received measurement data on {self.get_server_url()}") await self.__run_measurements() # flushes parameters after measurements have finished await self.__flush_measurement_parameters() # delay for server to check for data changes await asyncio.sleep(2) def start_server_task(self): """ Starts server synchronously (without using asyncio) :return: None """ asyncio.run(self.start_server()) async def close_server(self): """ Stops server from serving data :return: None """ print(f"CLOSE SERVER: closing server at {self.get_server_url()}") await self.__server.stop() async def stop_measurements(self): """ Signals the server to abort generating measurements :return: None """ # aborts measurements if measurements are running if await self.__is_measurements_running.get_value(): await self.__abort_measurements.set_value(True) else: print("SERVER ERROR: measurements couldn't be stopped as no measurement is running") def set_server_url(self, server_url: str): """ Change server url to serve data on :param server_url: IP and Port to which the OPC-UA server will send data to. Format: opc.tcp://[IP]:[PORT] :return: None """ self.__server_url = server_url def get_server_url(self): """ Returns server url to serve data on :return: IP and Port to which the OPC-UA server will send data to. Format: opc.tcp://[IP]:[PORT] :rtype: str """ return self.__server_url def get_server_name(self): """ Returns server name :return: string name of server :rtype: str """ return self.__server_name
async def main(): # optional: setup logging #logger = logging.getLogger("asyncua.address_space") # logger.setLevel(logging.DEBUG) #logger = logging.getLogger("asyncua.internal_server") # logger.setLevel(logging.DEBUG) #logger = logging.getLogger("asyncua.binary_server_asyncio") # logger.setLevel(logging.DEBUG) #logger = logging.getLogger("asyncua.uaprocessor") # logger.setLevel(logging.DEBUG) # now setup our server server = Server() await server.init() server.disable_clock() #for debuging #server.set_endpoint("opc.tcp://localhost:4840/freeopcua/server/") server.set_endpoint("opc.tcp://0.0.0.0:4840/freeopcua/server/") server.set_server_name("FreeOpcUa Example Server") # set all possible endpoint policies for clients to connect through server.set_security_policy([ ua.SecurityPolicyType.NoSecurity, ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt, ua.SecurityPolicyType.Basic256Sha256_Sign ]) # setup our own namespace uri = "http://examples.freeopcua.github.io" idx = await server.register_namespace(uri) # create a new node type we can instantiate in our address space dev = await server.nodes.base_object_type.add_object_type(idx, "MyDevice") await (await dev.add_variable(idx, "sensor1", 1.0)).set_modelling_rule(True) await (await dev.add_property(idx, "device_id", "0340")).set_modelling_rule(True) ctrl = await dev.add_object(idx, "controller") await ctrl.set_modelling_rule(True) await (await ctrl.add_property(idx, "state", "Idle")).set_modelling_rule(True) # populating our address space # First a folder to organise our nodes myfolder = await server.nodes.objects.add_folder(idx, "myEmptyFolder") # instanciate one instance of our device mydevice = await server.nodes.objects.add_object(idx, "Device0001", dev) mydevice_var = await mydevice.get_child( [f"{idx}:controller", f"{idx}:state"]) # get proxy to our device state variable # create directly some objects and variables myobj = await server.nodes.objects.add_object(idx, "MyObject") myvar = await myobj.add_variable(idx, "MyVariable", 6.7) await myvar.set_writable() # Set MyVariable to be writable by clients mystringvar = await myobj.add_variable(idx, "MyStringVariable", "Really nice string") await mystringvar.set_writable( ) # Set MyVariable to be writable by clients mydtvar = await myobj.add_variable(idx, "MyDateTimeVar", datetime.utcnow()) await mydtvar.set_writable() # Set MyVariable to be writable by clients myarrayvar = await myobj.add_variable(idx, "myarrayvar", [6.7, 7.9]) myarrayvar = await myobj.add_variable( idx, "myStronglytTypedVariable", ua.Variant([], ua.VariantType.UInt32)) myprop = await myobj.add_property(idx, "myproperty", "I am a property") mymethod = await myobj.add_method(idx, "mymethod", func, [ua.VariantType.Int64], [ua.VariantType.Boolean]) multiply_node = await myobj.add_method( idx, "multiply", multiply, [ua.VariantType.Int64, ua.VariantType.Int64], [ua.VariantType.Int64]) # import some nodes from xml await server.import_xml("custom_nodes.xml") # creating a default event object # The event object automatically will have members for all events properties # you probably want to create a custom event type, see other examples myevgen = await server.get_event_generator() myevgen.event.Severity = 300 # starting! async with server: print("Available loggers are: ", logging.Logger.manager.loggerDict.keys()) # enable following if you want to subscribe to nodes on server side #handler = SubHandler() #sub = server.create_subscription(500, handler) #handle = sub.subscribe_data_change(myvar) # trigger event, all subscribed clients wil receive it var = await myarrayvar.read_value( ) # return a ref to value in db server side! not a copy! var = copy.copy( var ) # WARNING: we need to copy before writting again otherwise no data change event will be generated var.append(9.3) await myarrayvar.write_value(var) await mydevice_var.write_value("Running") await myevgen.trigger(message="This is BaseEvent") await server.write_attribute_value( myvar.nodeid, ua.DataValue(0.9) ) # Server side write method which is a bit faster than using write_value while True: await asyncio.sleep(0.1) await server.write_attribute_value(myvar.nodeid, ua.DataValue(sin(time.time())))
class server: def __init__(self): logging.info("Creating a opc server") self._server = Server() self._config = False self.nsindex = "" async def NewOPCServer(self, IP): # now setup our server await self._server.init() self._server.disable_clock() #for debuging self.IP = IP.strip() self.mynodes = [] self._server.set_endpoint( self.IP ) #ip removing whitespace Address to pass "opc.tcp://0.0.0.0:4840/freeopcua/server/" self._server.set_server_name("OPC Server Name") # set all possible endpoint policies for clients to connect through self._server.set_security_policy([ ua.SecurityPolicyType.NoSecurity, ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt, ua.SecurityPolicyType.Basic256Sha256_Sign ]) def checkdata(self, data): if len(data) < 5: return False if len(data['measure']) < 1: return False #TODO implement more data check async def LoadConfig(self, path): with open(path) as f: data = json.load(f) if not self.checkdata(data): return False # setup our own namespace await self._server.register_namespace(self.nsindex) self._config = False self.scan = data['scaninterval'] self.nsindex = data['namespaceindex'] self.namespace = data['namespace'] self.devices = data['device'] # create a new node type we can instantiate in our address space dev = await self._server.nodes.base_object_type.add_object_type( int(self.nsindex), self.devices) #Creating the nodes i = 0 for element in data['measure']: self.mynodes.append(await dev.add_variable( ua.NodeId.from_string('ns=' + self.nsindex + ';s=' + self.namespace + '.' + self.devices + '.' + element['name']), element['name'], 0)) await self.mynodes[i].set_writable() i += 1 myevgen = await self._server.get_event_generator() myevgen.event.Severity = 300 logging.info("Configuration loaded") self._config = True async def Start(self): if self._config: async with self._server: #print("Available loggers are: ", logging.Logger.manager.loggerDict.keys()) logging.info("Server Started") while True: await asyncio.sleep(self.scan) for element in self.mynodes: #updating values await self._server.write_attribute_value( element.nodeid, ua.DataValue(sin(time.time()))) logging.warning("The server need to be configure it first") async def CreateXML(self): ipremove = self.IP.replace("opc.tcp://", "") port = ipremove[ipremove.index(":") + 1:] #creating the xml await self._server.export_xml_by_ns( "gen/Configuration" + port + ".xml", int(self.nsindex)) logging.info("XML File created successfully")