44from functools import partial
55from typing import List , Literal , Optional , Tuple , Type , Union
66
7+ from asyncua import ua
78from asyncua .client import Client as AsyncClient
89from asyncua .sync import Client , sync_async_client_method
10+ from asyncua .ua import VariantType
911from asyncua .ua .uaerrors import BadNoMatch , BadTypeMismatch , BadUserAccessDenied
1012from fastapi import BackgroundTasks
1113from pydantic import ConfigDict , Field
1214
15+
16+ class UnsupportedTypeError (Exception ):
17+ """Raised when an unsupported value type is specified"""
18+
19+ pass
20+
21+
1322from inference .core .workflows .execution_engine .entities .base import OutputDefinition
1423from inference .core .workflows .execution_engine .entities .types import (
1524 BOOLEAN_KIND ,
@@ -171,11 +180,25 @@ class BlockManifest(WorkflowBlockManifest):
171180 )
172181 value_type : Union [
173182 Selector (kind = [STRING_KIND ]),
174- Literal ["Boolean" , "Float" , "Integer" , "String" ],
183+ Literal [
184+ "Boolean" ,
185+ "Double" ,
186+ "Float" ,
187+ "Int16" ,
188+ "Int32" ,
189+ "Int64" ,
190+ "Integer" ,
191+ "SByte" ,
192+ "String" ,
193+ "UInt16" ,
194+ "UInt32" ,
195+ "UInt64" ,
196+ ],
175197 ] = Field (
176198 default = "String" ,
177- description = "The type of the value to be written to the target variable on the OPC UA server." ,
178- examples = ["Boolean" , "Float" , "Integer" , "String" ],
199+ description = "The type of the value to be written to the target variable on the OPC UA server. "
200+ "Supported types: Boolean, Double, Float, Int16, Int32, Int64, Integer (Int64 alias), SByte, String, UInt16, UInt32, UInt64." ,
201+ examples = ["Boolean" , "Double" , "Float" , "Int32" , "Int64" , "String" ],
179202 json_schema_extra = {
180203 "always_visible" : True ,
181204 },
@@ -256,7 +279,20 @@ def run(
256279 object_name : str ,
257280 variable_name : str ,
258281 value : Union [str , bool , float , int ],
259- value_type : Literal ["Boolean" , "Float" , "Integer" , "String" ] = "String" ,
282+ value_type : Literal [
283+ "Boolean" ,
284+ "Double" ,
285+ "Float" ,
286+ "Int16" ,
287+ "Int32" ,
288+ "Int64" ,
289+ "Integer" ,
290+ "SByte" ,
291+ "String" ,
292+ "UInt16" ,
293+ "UInt32" ,
294+ "UInt64" ,
295+ ] = "String" ,
260296 timeout : int = 2 ,
261297 fire_and_forget : bool = True ,
262298 disable_sink : bool = False ,
@@ -285,28 +321,16 @@ def run(
285321 "message" : "Sink cooldown applies" ,
286322 }
287323
288- value_str = str (value )
289- logging .debug (f"OPC Writer converting value '{ value_str } ' to type { value_type } " )
290- try :
291- if value_type in [BOOLEAN_KIND , "Boolean" ]:
292- decoded_value = value_str .strip ().lower () in ("true" , "1" )
293- elif value_type in [FLOAT_KIND , "Float" ]:
294- decoded_value = float (value_str )
295- elif value_type in [INTEGER_KIND , "Integer" ]:
296- decoded_value = int (value_str )
297- elif value_type in [STRING_KIND , "String" ]:
298- decoded_value = value_str
299- else :
300- raise ValueError (f"Unsupported value type: { value_type } " )
301- logging .debug (f"OPC Writer successfully converted value to { decoded_value } " )
302- except ValueError as exc :
303- logging .error (f"OPC Writer failed to convert value: { exc } " )
304- return {
305- "disabled" : False ,
306- "error_status" : True ,
307- "throttling_status" : False ,
308- "message" : f"Failed to convert value: { exc } " ,
309- }
324+ if value_type in [BOOLEAN_KIND , "Boolean" ] and isinstance (value , str ):
325+ # handle boolean conversion explicitly if value is a string
326+ decoded_value = value .strip ().lower () in ("true" , "1" )
327+ else :
328+ # Use value directly - OPC UA library will convert based on type specification
329+ decoded_value = value
330+
331+ logging .debug (
332+ f"OPC Writer prepared value '{ decoded_value } ' for type { value_type } "
333+ )
310334
311335 opc_writer_handler = partial (
312336 opc_connect_and_write_value ,
@@ -317,6 +341,7 @@ def run(
317341 object_name = object_name ,
318342 variable_name = variable_name ,
319343 value = decoded_value ,
344+ value_type = value_type ,
320345 timeout = timeout ,
321346 node_lookup_mode = node_lookup_mode ,
322347 )
@@ -376,6 +401,18 @@ def safe_disconnect(client: Client) -> None:
376401 logging .debug (f"OPC Writer disconnect error (non-fatal): { exc } " )
377402
378403
404+ def get_node_data_type (var ) -> str :
405+ """
406+ Get the data type of an OPC UA node.
407+ Returns a string representation of the type, or "Unknown" if unable to read.
408+ """
409+ try :
410+ return str (var .read_data_type_as_variant_type ())
411+ except Exception as exc :
412+ logging .info (f"Unable to read node data type: { exc } " )
413+ return "Unknown"
414+
415+
379416def opc_connect_and_write_value (
380417 url : str ,
381418 namespace : str ,
@@ -386,6 +423,7 @@ def opc_connect_and_write_value(
386423 value : Union [bool , float , int , str ],
387424 timeout : int ,
388425 node_lookup_mode : Literal ["hierarchical" , "direct" ] = "hierarchical" ,
426+ value_type : str = "String" ,
389427) -> Tuple [bool , str ]:
390428 logging .debug (
391429 f"OPC Writer attempting to connect and write value={ value } to { url } /{ object_name } /{ variable_name } "
@@ -401,6 +439,7 @@ def opc_connect_and_write_value(
401439 value = value ,
402440 timeout = timeout ,
403441 node_lookup_mode = node_lookup_mode ,
442+ value_type = value_type ,
404443 )
405444 logging .debug (
406445 f"OPC Writer successfully wrote value to { url } /{ object_name } /{ variable_name } "
@@ -424,6 +463,7 @@ def _opc_connect_and_write_value(
424463 value : Union [bool , float , int , str ],
425464 timeout : int ,
426465 node_lookup_mode : Literal ["hierarchical" , "direct" ] = "hierarchical" ,
466+ value_type : str = "String" ,
427467):
428468 logging .debug (f"OPC Writer creating client for { url } with timeout={ timeout } " )
429469 client = Client (url = url , sync_wrapper_timeout = timeout )
@@ -514,15 +554,50 @@ def _opc_connect_and_write_value(
514554 raise Exception (f"UNHANDLED ERROR: { type (exc )} { exc } " )
515555
516556 try :
517- logging .debug (f"OPC Writer writing value '{ value } ' to variable" )
518- var .write_value (value )
557+ logging .debug (
558+ f"OPC Writer writing value '{ value } ' to variable with type '{ value_type } '"
559+ )
560+ # Use proper OPC UA type specification using VariantType enum
561+ if value_type in [BOOLEAN_KIND , "Boolean" ]:
562+ var .set_value (value , VariantType .Boolean )
563+ elif value_type == "Double" :
564+ var .set_value (value , VariantType .Double )
565+ elif value_type in [FLOAT_KIND , "Float" ]:
566+ var .set_value (value , VariantType .Float )
567+ elif value_type == "Int16" :
568+ var .set_value (value , VariantType .Int16 )
569+ elif value_type == "Int32" :
570+ var .set_value (value , VariantType .Int32 )
571+ elif value_type in ["Int64" , INTEGER_KIND , "Integer" ]:
572+ var .set_value (value , VariantType .Int64 )
573+ elif value_type == "SByte" :
574+ var .set_value (value , VariantType .SByte )
575+ elif value_type in [STRING_KIND , "String" ]:
576+ var .set_value (value , VariantType .String )
577+ elif value_type == "UInt16" :
578+ var .set_value (value , VariantType .UInt16 )
579+ elif value_type == "UInt32" :
580+ var .set_value (value , VariantType .UInt32 )
581+ elif value_type == "UInt64" :
582+ var .set_value (value , VariantType .UInt64 )
583+ else :
584+ logging .error (f"OPC Writer unsupported value type: { value_type } " )
585+ safe_disconnect (client )
586+ raise UnsupportedTypeError (f"Value type '{ value_type } ' is not supported." )
519587 logging .info (
520588 f"OPC Writer successfully wrote '{ value } ' to variable at { object_name } /{ variable_name } "
521589 )
590+ except UnsupportedTypeError :
591+ raise
522592 except BadTypeMismatch as exc :
523- logging .error (f"OPC Writer type mismatch error: { exc } " )
593+ node_type = get_node_data_type (var )
594+ logging .error (
595+ f"OPC Writer type mismatch: tried to write value '{ value } ' (type: { type (value ).__name__ } ) to node with data type { node_type } . Error: { exc } "
596+ )
524597 safe_disconnect (client )
525- raise Exception (f"WRONG TYPE ERROR: { exc } " )
598+ raise Exception (
599+ f"WRONG TYPE ERROR: Tried to write value '{ value } ' (type: { type (value ).__name__ } ) but node expects type { node_type } . { exc } "
600+ )
526601 except Exception as exc :
527602 logging .error (f"OPC Writer unhandled write error: { type (exc )} { exc } " )
528603 safe_disconnect (client )
0 commit comments