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 )
@@ -398,6 +423,7 @@ def opc_connect_and_write_value(
398423 value : Union [bool , float , int , str ],
399424 timeout : int ,
400425 node_lookup_mode : Literal ["hierarchical" , "direct" ] = "hierarchical" ,
426+ value_type : str = "String" ,
401427) -> Tuple [bool , str ]:
402428 logging .debug (
403429 f"OPC Writer attempting to connect and write value={ value } to { url } /{ object_name } /{ variable_name } "
@@ -413,6 +439,7 @@ def opc_connect_and_write_value(
413439 value = value ,
414440 timeout = timeout ,
415441 node_lookup_mode = node_lookup_mode ,
442+ value_type = value_type ,
416443 )
417444 logging .debug (
418445 f"OPC Writer successfully wrote value to { url } /{ object_name } /{ variable_name } "
@@ -436,6 +463,7 @@ def _opc_connect_and_write_value(
436463 value : Union [bool , float , int , str ],
437464 timeout : int ,
438465 node_lookup_mode : Literal ["hierarchical" , "direct" ] = "hierarchical" ,
466+ value_type : str = "String" ,
439467):
440468 logging .debug (f"OPC Writer creating client for { url } with timeout={ timeout } " )
441469 client = Client (url = url , sync_wrapper_timeout = timeout )
@@ -526,11 +554,41 @@ def _opc_connect_and_write_value(
526554 raise Exception (f"UNHANDLED ERROR: { type (exc )} { exc } " )
527555
528556 try :
529- logging .debug (f"OPC Writer writing value '{ value } ' to variable" )
530- 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." )
531587 logging .info (
532588 f"OPC Writer successfully wrote '{ value } ' to variable at { object_name } /{ variable_name } "
533589 )
590+ except UnsupportedTypeError :
591+ raise
534592 except BadTypeMismatch as exc :
535593 node_type = get_node_data_type (var )
536594 logging .error (
0 commit comments