11"""TSM Context Wrapper"""
2-
2+ import ctypes . wintypes
33import time
44import typing
55
66import nitsm ._pinmapinterfaces
77import nitsm .enums
88import nitsm .pinquerycontexts
99import pythoncom
10+ import win32com .client
1011
1112__all__ = ["SemiconductorModuleContext" ]
1213
1819 import nifgen
1920 import niscope
2021 import niswitch
21- import win32com .client .dynamic
2222
2323 _Any = typing .Any
2424 _Tuple = typing .Tuple
3232 _CapabilityArg = _Union [nitsm .enums .Capability , str ]
3333 _PinsArg = _Union [str , _Sequence [str ]] # argument that accepts 1 or more pins
3434 _StringTuple = _Tuple [str , ...]
35+ _AnyTuple = _Tuple [_Any , ...]
3536
3637 _NIDigitalSingleSessionPpmuQuery = _Tuple [_PinQueryContext , nidigital .Session , str ]
3738 _NIDigitalMultipleSessionPpmuQuery = _Tuple [
6970 _PinQueryContext , _Tuple [niscope .Session , ...], _StringTuple
7071 ]
7172
73+ _SwitchQuery = _Tuple [_Tuple ["SemiconductorModuleContext" , ...], _AnyTuple , _StringTuple ]
74+
7275 _RelayDriverSingleSessionQuery = _Tuple [niswitch .Session , str ]
7376 _RelayDriverMultipleSessionQuery = _Tuple [_Tuple [niswitch .Session , ...], _StringTuple ]
7477
@@ -93,16 +96,15 @@ class SemiconductorModuleContext:
9396
9497 _sessions = {}
9598
96- def __init__ (self , tsm_com_obj : "_ISemiconductorModuleContext" ):
99+ def __init__ (self , tsm_dispatch : "_ISemiconductorModuleContext" ):
97100 """Wraps an instance of ISemiconductorModuleContext.
98101
99102 Args:
100- tsm_com_obj : The win32com.client.dynamic.CDispatch object provided by TestStand.
103+ tsm_dispatch : The win32com.client.dynamic.CDispatch object provided by TestStand.
101104 """
102- self ._context = nitsm ._pinmapinterfaces .ISemiconductorModuleContext (tsm_com_obj )
103- self ._context ._oleobj_ = tsm_com_obj ._oleobj_ .QueryInterface (
104- self ._context .CLSID , pythoncom .IID_IDispatch
105- )
105+ clsid = nitsm ._pinmapinterfaces .ISemiconductorModuleContext .CLSID
106+ interface = tsm_dispatch ._oleobj_ .QueryInterface (clsid , pythoncom .IID_IDispatch )
107+ self ._context = nitsm ._pinmapinterfaces .ISemiconductorModuleContext (interface )
106108
107109 # General and Advanced
108110
@@ -910,6 +912,116 @@ def pins_to_niscope_sessions(self, pins: "_PinsArg") -> "_NIScopeMultipleSession
910912 )
911913 return pin_query_context , sessions , channel_lists
912914
915+ # Switching
916+
917+ def get_all_switch_names (self , multiplexer_type_id : "_InstrTypeIdArg" ) -> "_StringTuple" :
918+ """Returns the names of all switches of the type specified by the multiplexer_type_id in the
919+ Semiconductor Module context. You can use switch names to open driver sessions.
920+
921+ Args:
922+ multiplexer_type_id: Specifies the type ID for the multiplexer in the pin map file. When
923+ you add a multiplexer to the pin map file, you can define a type ID for the
924+ multiplexer, such as the driver name. Multiplexers in the pin map that do not
925+ specify a type ID have a default ID of
926+ nitsm.enums.InstrumentTypeIdConstants.NI_GENERIC_MULTIPLEXER.
927+ """
928+ if isinstance (multiplexer_type_id , nitsm .enums .InstrumentTypeIdConstants ):
929+ multiplexer_type_id = multiplexer_type_id .value
930+ return self ._context .GetSwitchNames (multiplexer_type_id )
931+
932+ def set_switch_session (
933+ self , switch_name : str , session_data : "_Any" , multiplexer_type_id : "_InstrTypeIdArg"
934+ ) -> None :
935+ """Associates an open switch session with the switch_name for a multiplexer of type
936+ multiplexer_type_id.
937+
938+ Args:
939+ switch_name: The instrument name in the pin map file for the corresponding session_data.
940+ session_data: The instrument session for the corresponding switch_name and
941+ multiplexer_type_id.
942+ multiplexer_type_id: Specifies the type ID for the multiplexer in the pin map file. When
943+ you add a multiplexer to the pin map file, you can define a type ID for the
944+ multiplexer, such as the driver name. Multiplexers in the pin map that do not
945+ specify a type ID have a default ID of
946+ nitsm.enums.InstrumentTypeIdConstants.NI_GENERIC_MULTIPLEXER.
947+ """
948+ if isinstance (multiplexer_type_id , nitsm .enums .InstrumentTypeIdConstants ):
949+ multiplexer_type_id = multiplexer_type_id .value
950+ session_id = id (session_data )
951+ self ._sessions [session_id ] = session_data
952+ return self ._context .SetSwitchSession (multiplexer_type_id , switch_name , session_id )
953+
954+ def get_all_switch_sessions (self , multiplexer_type_id : "_InstrTypeIdArg" ) -> "_AnyTuple" :
955+ """Returns a tuple of all switch session data of the type specified by the
956+ multiplexer_type_id in the Semiconductor Module context.
957+
958+ Args:
959+ multiplexer_type_id: Specifies the type ID for the multiplexer in the pin map file. When
960+ you add a multiplexer to the pin map file, you can define a type ID for the
961+ multiplexer, such as the driver name. Multiplexers in the pin map that do not
962+ specify a type ID have a default ID of
963+ nitsm.enums.InstrumentTypeIdConstants.NI_GENERIC_MULTIPLEXER.
964+ """
965+ if isinstance (multiplexer_type_id , nitsm .enums .InstrumentTypeIdConstants ):
966+ multiplexer_type_id = multiplexer_type_id .value
967+ session_ids = self ._context .GetSwitchSessions (multiplexer_type_id )
968+ return tuple (map (SemiconductorModuleContext ._sessions .get , session_ids ))
969+
970+ def pin_to_switch_sessions (
971+ self , pin : str , multiplexer_type_id : "_InstrTypeIdArg"
972+ ) -> "_SwitchQuery" :
973+ """Returns the switch sessions, switch routes, and new Semiconductor Module context objects
974+ required to access the specified switched pin.
975+
976+ Args:
977+ pin: The name of the pin to translate to session data and switch routes.
978+ multiplexer_type_id: Specifies the type ID for the multiplexer in the pin map file. When
979+ you add a multiplexer to the pin map file, you can define a type ID for the
980+ multiplexer, such as the driver name. Multiplexers in the pin map that do not
981+ specify a type ID have a default ID of
982+ nitsm.enums.InstrumentTypeIdConstants.NI_GENERIC_MULTIPLEXER.
983+
984+ Returns:
985+ semiconductor_module_contexts: A tuple of Semiconductor Module context objects. Each
986+ element in the tuple represents a site that must be executed serially. Use each
987+ Semiconductor Module context object to query the pin map and publish data.
988+ session_data: A tuple of the session data required to access the switch that connects an
989+ instrument channel to the pin.
990+ switch_routes: The routes required to connect an instrument channel to the pin.
991+ """
992+ if isinstance (multiplexer_type_id , nitsm .enums .InstrumentTypeIdConstants ):
993+ multiplexer_type_id = multiplexer_type_id .value
994+ # We have to use DumbDispatch here because pywin32 fails to recognize
995+ # ISemiconductorModuleContext as deriving from IDispatch; most likely because it isn't
996+ # natively supported. So, we fetch it as IUnknown instead.
997+ vt_by_ref_array = pythoncom .VT_BYREF | pythoncom .VT_ARRAY
998+ site_contexts = win32com .client .VARIANT (vt_by_ref_array | pythoncom .VT_UNKNOWN , [])
999+ sessions = win32com .client .VARIANT (vt_by_ref_array | pythoncom .VT_VARIANT , [])
1000+ switch_routes = win32com .client .VARIANT (vt_by_ref_array | pythoncom .VT_BSTR , [])
1001+ dumb_context = win32com .client .dynamic .DumbDispatch (self ._context )
1002+ dumb_context .GetSwitchSessions_2 (
1003+ multiplexer_type_id , pin , site_contexts , sessions , switch_routes
1004+ )
1005+ # As of pywin32 303, there is a bug where SAFEARRAYs of IUnknown pointers leak references.
1006+ # See here: https://github.com/mhammond/pywin32/issues/1864
1007+ # As a work-around, we decrement the reference count until the only one left is held by
1008+ # Python. It will be released when the object is garbage collected.
1009+ add_ref = ctypes .WINFUNCTYPE (ctypes .wintypes .ULONG )(1 , "AddRef" )
1010+ release = ctypes .WINFUNCTYPE (ctypes .wintypes .ULONG )(2 , "Release" )
1011+ for site_context in site_contexts .value :
1012+ # address of IUnknown has to be parsed from the repr
1013+ # https://github.com/mhammond/pywin32/blob/main/com/win32com/src/PyIUnknown.cpp
1014+ address = ctypes .c_void_p (int (repr (site_context ).split ()[- 1 ][:- 1 ], 16 ))
1015+ # first add a reference in case the bug has been fixed; prevents count from reaching 0
1016+ add_ref (address )
1017+ # then release the reference until only one remains
1018+ while release (address ) > 1 :
1019+ pass
1020+ site_contexts = map (win32com .client .dynamic .DumbDispatch , site_contexts .value )
1021+ site_contexts = tuple (map (SemiconductorModuleContext , site_contexts ))
1022+ sessions = tuple (map (SemiconductorModuleContext ._sessions .get , sessions .value ))
1023+ return site_contexts , sessions , switch_routes .value
1024+
9131025 # Relay Driver
9141026
9151027 def get_relay_driver_module_names (self ) -> "_StringTuple" :
0 commit comments