def setUp(self): """ Sets up and connects the _client. """ TrhphClientTest._end_client_if_any() super(TrhphClientTest, self).setUp() host = self.device_address port = self.device_port self._samples_recd = 0 outfile = file('trhph_output.txt', 'w') prefix_state = True _client = TrhphClient(host, port, outfile, prefix_state) # set the class and instance variables to refer to this object: TrhphClientTest._trhph_client = self._client = _client # prepare client including going to the main menu _client.set_data_listener(self._data_listener) _client.set_generic_timeout(self._timeout) log.info("connecting") _client.connect()
class TrhphInstrumentProtocol(InstrumentProtocol): """ The protocol for the TrhphChannel.INSTRUMENT channel. """ def __init__(self, evt_callback=None): """ Creates and initializes a protocol object to the UNCOFIGURED state. """ InstrumentProtocol.__init__(self, evt_callback) self.trhph_client = None self._host = None self._port = None self._state = DriverState.UNCONFIGURED # TODO probably promote this convenience to super-class? self._timeout = 30 """Default timeout value for operations accepting an optional timeout argument.""" def get_current_state(self): """ Gets the current state of the protocol. """ return self._state def _assert_state(self, obj): """ Asserts that the current state is the same as the one given (if not a list) or that is one of the elements of the given list. """ cs = self.get_current_state() if isinstance(obj, list): if cs in obj: return else: raise AssertionError("current state=%s, expected one of %s" % (cs, str(obj))) state = obj if cs != state: raise AssertionError("current state=%s, expected=%s" % (cs, state)) def initialize(self, *args, **kwargs): """ Resets the protocol to the UNCONFIGURED state. """ if log.isEnabledFor(logging.DEBUG): log.debug("args=%s kwargs=%s" % (str(args), str(kwargs))) self._assert_state([DriverState.UNCONFIGURED, DriverState.DISCONNECTED]) super(TrhphInstrumentProtocol, self).initialize(*args, **kwargs) self._state = DriverState.UNCONFIGURED def configure(self, config, *args, **kwargs): """ Sets _host/_port according to the given config object and sets the current state as DriverState.DISCONNECTED. Note that super.configure is not called but instead and adjusted version of it. """ if log.isEnabledFor(logging.DEBUG): log.debug("config=%s args=%s kwargs=%s" % (str(config), str(args), str(kwargs))) self._assert_state(DriverState.UNCONFIGURED) # Parameters for TrhphClient: # with direct access to the instrument: # self._host = config['device_addr'] # self._port = config['device_port'] # but we're now connecting through the EthernetDeviceLogger: self._host = config['server_addr'] self._port = config['server_port'] self._base_configure(config, *args, **kwargs) self._state = DriverState.DISCONNECTED return InstErrorCode.OK def _base_configure(self, config, *args, **kwargs): """ This is as super.configure but with adjustments so we actually use TrhphClient (in attach) instead of LoggerClient. The base class is under changes so this all may change as well. """ log.info('Configuring for device comms.') method = config['method'] if method == 'ethernet': device_addr = config['device_addr'] device_port = config['device_port'] #server_addr = config['server_addr'] server_port = config['server_port'] self._logger = EthernetDeviceLogger(device_addr, device_port, server_port) #self._logger_client = LoggerClient(server_addr, server_port) elif method == 'serial': # The config dict does not have a valid connection method. raise InstrumentConnectionException() else: # The config dict does not have a valid connection method. raise InstrumentConnectionException() return InstErrorCode.OK def connect(self, *args, **kwargs): """ It calls super.connect, which calls attach, where the actual connection happens, and sets _state to DriverState.CONNECTED. """ if log.isEnabledFor(logging.DEBUG): log.debug("args=%s kwargs=%s" % (str(args), str(kwargs))) self._assert_state([DriverState.UNCONFIGURED, DriverState.DISCONNECTED]) # TODO Note that super.connect calls attach; it would be better that # the attach call be done by client code so for example it is # possible to separate the intervening states (DISCONNECTED, # CONNECTED, ATTACHED, DETACHED). super(TrhphInstrumentProtocol, self).connect(*args, **kwargs) self._state = DriverState.CONNECTED def _data_listener(self, sample): """ The callback used to announce a DATA_RECEIVED event to the driver. """ log.info("_data_listener: sample = %s" % str(sample)) # log.debug("announcing to driver: %s" % str(sample)) # self.announce_to_driver(DriverAnnouncement.DATA_RECEIVED, # data_sample=sample) def disconnect(self, *args, **kwargs): """ Calls super.disconnect and sets state to DriverState.DISCONNECTED """ if log.isEnabledFor(logging.DEBUG): log.debug("args=%s kwargs=%s" % (str(args), str(kwargs))) super(TrhphInstrumentProtocol, self).disconnect(*args, **kwargs) self._state = DriverState.DISCONNECTED def attach(self, *args, **kwargs): """ Sets up the trhph_client and connects to the instrument. Note that super.attach is not called. """ if log.isEnabledFor(logging.DEBUG): log.debug("args=%s kwargs=%s" % (str(args), str(kwargs))) # TODO better handling of the intervening states related with # connection and attachment -- see comment above in connect(). # self._assert_state(DriverState.CONNECTED) self._setup_trhph_client() return InstErrorCode.OK def _setup_trhph_client(self): """ Called by attach() to create the supporting TrhphClient object and do the connection. A file trhph_output.txt in the current directory is specified for the TrhphClient to log out all received messages from the instrument. """ host = self._host port = self._port outfile = file('trhph_output.txt', 'w') log.info("setting TrhphClient to connect to %s:%s" % (host, port)) self.trhph_client = TrhphClient(host, port, outfile, True) self.trhph_client.set_data_listener(self._data_listener) self.trhph_client.connect() def detach(self, *args, **kwargs): """ Ends the TrhphClient and returns InstErrorCode.OK. Note that super.detach is not called. """ if log.isEnabledFor(logging.DEBUG): log.debug("args=%s kwargs=%s" % (str(args), str(kwargs))) self.trhph_client.end() self.trhph_client = None return InstErrorCode.OK ######################################################################## # Channel command interface. ######################################################################## def get(self, params, *args, **kwargs): """ Gets the value of the requested parameters. It handles TIME_BETWEEN_BURSTS, VERBOSE_MODE. """ if log.isEnabledFor(logging.DEBUG): log.debug("params=%s args=%s kwargs=%s" % (str(params), str(args), str(kwargs))) assert isinstance(params, list) # TODO actually it should be ATTACHED if we want consistency with the # states related with connection and attachment. self._assert_state(DriverState.CONNECTED) timeout = kwargs.get('timeout', self._timeout) # gather data collection params if any of them are requested: data_coll_params = {} if TrhphParameter.TIME_BETWEEN_BURSTS in params or \ TrhphParameter.VERBOSE_MODE in params: seconds, is_data_only = self._get_data_collection_params(timeout) data_coll_params[TrhphParameter.TIME_BETWEEN_BURSTS] = seconds data_coll_params[TrhphParameter.VERBOSE_MODE] = is_data_only params = list(set(params)) # remove any duplicates result = {} for param in params: if param == TrhphParameter.TIME_BETWEEN_BURSTS or \ param == TrhphParameter.VERBOSE_MODE: value = data_coll_params[param] else: value = InstErrorCode.INVALID_PARAMETER result[param] = value return result def _get_data_collection_params(self, timeout): """ Gets the get_data_collection_params. """ try: return self.trhph_client.get_data_collection_params(timeout) except TimeoutException, e: raise InstrumentTimeoutException(str(e)) except TrhphClientException, e: return InstErrorCode.MESSAGING_ERROR
class TrhphInstrumentDriver(InstrumentDriver): """ TRHPH driver """ def __init__(self, evt_callback): """ Constructor. @param evt_callback Driver process event callback. """ InstrumentDriver.__init__(self, evt_callback) # _trhph_client created in configure() self._trhph_client = None self._state = TrhphDriverState.UNCONFIGURED # TODO probably promote this convenience to super-class? self._timeout = 30 """Default timeout value for operations accepting an optional timeout argument.""" def _assert_state(self, obj): """ Asserts that the current state is either the same as the one given (if not a list) or one of the elements of the given list. @raises StateError if the assertion fails """ cs = self.get_current_state() if isinstance(obj, list): if cs in obj: return # OK else: raise StateError( "current state=%s not one of %s" % (cs, str(obj))) state = obj if cs != state: raise StateError( "current state=%s, expected=%s" % (cs, state)) ############################################################# # Device connection interface. ############################################################# def initialize(self, *args, **kwargs): """ Initialize driver connection, bringing communications parameters into unconfigured state (no connection object). @raises StateError if command not allowed in current state """ # print # print("trhph driver: args=%s kwargs=%s" % (str(args), str(kwargs))) # print("trhph driver: state=%s" % str(self._state)) # print("trhph driver: _trhph_client=%s" % str(self._trhph_client)) # print if log.isEnabledFor(logging.DEBUG): log.debug("args=%s kwargs=%s" % (str(args), str(kwargs))) if self._state == TrhphDriverState.UNCONFIGURED: assert self._trhph_client is None return # assert self._trhph_client is not None # try: # self._trhph_client.end() # finally: # self._trhph_client = None if self._trhph_client is not None: try: self._trhph_client.end() finally: self._trhph_client = None self._state = TrhphDriverState.UNCONFIGURED def configure(self, *args, **kwargs): """ Configure the driver for communications with the device via port agent / logger (valid but unconnected connection object). @param config comms config dict. @raises StateError if command not allowed in current state @throws ParameterError if missing comms or invalid config dict. """ if log.isEnabledFor(logging.DEBUG): log.debug("args=%s kwargs=%s" % (str(args), str(kwargs))) self._assert_state(TrhphDriverState.UNCONFIGURED) config = kwargs.get('config', None) if config is None: # raise ParameterError("'config' parameter required") config = args[0] # Verify dict and construct connection client. try: addr = config['addr'] port = config['port'] if isinstance(addr, str) and \ isinstance(port, int) and len(addr) > 0: # self._connection = LoggerClient(addr, port) def _data_listener(sample): log.info("_data_listener: sample = %s" % str(sample)) host = addr outfile = file('trhph_output.txt', 'w') log.info("setting TrhphClient to connect to %s:%s" % (host, port)) self.trhph_client = TrhphClient(host, port, outfile, True) self.trhph_client.set_data_listener(_data_listener) else: raise ParameterError('Invalid comms config dict') except (TypeError, KeyError): raise ParameterError('Invalid comms config dict.') self._state = TrhphDriverState.DISCONNECTED def connect(self, *args, **kwargs): """ Establish communications with the device via port agent / logger (connected connection object). @raises StateError if command not allowed in current state @throws InstrumentConnectionException if the connection failed. """ if log.isEnabledFor(logging.DEBUG): log.debug("args=%s kwargs=%s" % (str(args), str(kwargs))) self._assert_state(TrhphDriverState.DISCONNECTED) self.trhph_client.connect() self._state = TrhphDriverState.CONNECTED def disconnect(self, *args, **kwargs): """ Disconnect from device via port agent / logger. @raises StateError if command not allowed in current state """ if log.isEnabledFor(logging.DEBUG): log.debug("args=%s kwargs=%s" % (str(args), str(kwargs))) self._assert_state(TrhphDriverState.CONNECTED) self.trhph_client.end() self._state = TrhphDriverState.DISCONNECTED ############################################################# # Command and control interface. ############################################################# def get(self, *args, **kwargs): """ Retrieve device parameters. @param params DriverParameter.ALL or a list of parameters to retrieve. @param timeout Timeout for each involved instrument interation, self._timeout by default. @retval parameter : value dict. @raises ParameterError if missing or invalid get parameters. @raises StateError if command not allowed in current state """ if log.isEnabledFor(logging.DEBUG): log.debug("args=%s kwargs=%s" % (str(args), str(kwargs))) self._assert_state(TrhphDriverState.CONNECTED) params = kwargs.get('params', None) if params is None: raise ParameterError( "'params' parameter required") if params == DriverParameter.ALL: params = TrhphParameter.list() elif not isinstance(params, (list, tuple)): raise ParameterError( 'params must be list or tuple.') timeout = kwargs.get('timeout', self._timeout) # gather data collection params if any of them are requested: data_collec_params = {} if TrhphParameter.TIME_BETWEEN_BURSTS in params or \ TrhphParameter.VERBOSE_MODE in params: seconds, is_data_only = self._get_data_collection_params(timeout) verbose = not is_data_only data_collec_params[TrhphParameter.TIME_BETWEEN_BURSTS] = seconds data_collec_params[TrhphParameter.VERBOSE_MODE] = verbose params = list(set(params)) # remove any duplicates result = {} for param in params: if param == TrhphParameter.TIME_BETWEEN_BURSTS or \ param == TrhphParameter.VERBOSE_MODE: value = data_collec_params[param] else: # value = InstErrorCode.INVALID_PARAMETER raise ParameterError('invalid parameter %s' % param) result[param] = value return result def _get_data_collection_params(self, timeout): """ Gets the get_data_collection_params. """ try: return self.trhph_client.get_data_collection_params(timeout) except TimeoutException, e: raise TimeoutError(str(e)) except TrhphClientException, e: raise InstrumentException(str(e))