Locking Scheme for Scripting¶
Overview¶
When the user acquires and uses instruments in procedures'
functions that are called from a script, there is a subroutine that disabled access to those instruments and ‘locks’ the specified instruments for the duration of the script. When the script is finished running, the instruments will appear in the drop down menu again.
Usage¶
The workflow for locking an instrument occurs as follows:
- Every time the user runs a script a new instance of the
ScriptController
is instantiated to undertake the task. Each script contoller is associated with a uniqueID
. - The Script controller will serially go through the entries of the measurement script, executing the specified
procedure
function. - The graphical user interface is updated, blocking the locked instruments from the
Stages to Execute
option menu. - When an instrument request is made within a
procedure
function through theGlobal_MeasureHandler
, theGlobal_MeasureHandler
traverses the stack of the request, looking for theID
of the ScriptController that made this call. - When the
ID
is determined, theGlobal_MeasureHandler
can check if that instrument is available in the__locked
collection for that specificID
. - When the
Global_MeasureHandler
traverses the stack, it is looking for theID
of the instance ofScriptHandler
. It is not necessarily looking for a specificID
, but looking for theID
of the object that has invoked this exact instance of theget_instrument
call. Due to the design of the locking scheme, the only functions that could have invokedget_instrument
are stored in the listfunctions
. If theID
of the local namespace forself
, i.e. the object that invoked this function, appears in out__locked
dictionary’s keys, then it is in fact theID
of an instance of aScriptController
that locked the instrument during the initial setup.
- When the
- When an instrument request is made within a
functions = ['_structureProcedure','__executeCommand','_procedure']
for entry in inspect.stack(context=0):
if entry[3] in functions:
id_ = id(entry[0].f_locals['self'])
if (id_ in self.__locked.keys()):
- When the
procedure
function is done running, theGlobal_MeasureHandler
will release all of the locked instruments associated with thatID(
def release_current_user_instruments(self):
'''
Releases all current user instruments
Should be called at the end of a test entity and NOT normally
inside Measure functions
'''
owner_id = self._get_owner()
with self.__access_lock:
owned = self.__locked.get(owner_id)
chain = self.__chainSets.get(owner_id)
if owned is not None:
self.__locked[owner_id] = []
if chain is not None:
self.__chainSets[owner_id] = ChainList()
Snapshot¶
- Code that executes the locking scheme from the
Global_MeasureHandler
: - Note:
_look_for_obj
is just a convenience function that will return the first occurence of an element in a list, for which a lambda function, provided as an argument, returns true.
- Note:
def get_instrument(self, specifiedDevice, additional=False):
'''
Finds and returns an unactive instrument corresponding to the one specified
Returns None if such instrument was found/available.
'''
owner_id = self._get_owner() # This is where we get the ID
# serialize access to global ownership dictionary
with self.__access_lock:
if not additional:
# Try the owned instruments first
owned_list = self.__locked.get(owner_id)
sdebug('OwnedList<{}>: {}'.format(owner_id, owned_list))
if owned_list != None:
found = _look_for_obj(owned_list, lambda x: x.whoAmI() == specifiedDevice)
if found != None:
return found
# Then take a look for available instruments
used = [inst for sub_l in self.__locked.values() for inst in sub_l]
sdebug('used instruments: {}'.format(used))
def isUnused(instrument):
return instrument not in used and instrument.whoAmI() == specifiedDevice
found = _look_for_obj(self.Stages.values(), isUnused)
if found != None:
self._lock_instrument(found, owner_id)
# self._connect_to_chain(found, owner_id, fiber_id)
return found