def _transmit(self, request, retlen, last_try=False): response = '' try: self._connection.sendall(request) self.log.debug('request sent') for i in range(self.comtries): self.log.debug('waiting for response, try %d/%d', i, self.comtries) p = select.select([self._connection], [], [], self.bustimeout) if self._connection in p[0]: data = self._connection.recv(20) # more than enough! if not data: raise CommunicationError(self, 'no reply from recv') response += data if response[-1] in (EOT, DC1, DC2, DC3, ACK, NAK): return response except OSError as err: if last_try: raise CommunicationError(self, 'tcp connection failed: %s' % err) from err # try reopening connection self.log.warning('tcp connection failed, retrying', exc=1) self.doReset() return self._transmit(request, retlen, last_try=True) else: return response
def _HW_writeParameter(self, index, value, store2eeprom=False): if index not in self.HW_writeable_Params: raise UsageError('Writing not possible for parameter index %s' % index) index = self.HW_writeable_Params.get(index, index) self.log.debug('writeParameter %d:0x%04x', index, value) if store2eeprom: if self._HW_readStatusWord() & (1 << 6) == 0: # target reached not set -> problem raise UsageError( self, 'Param acces no possible until target ' 'reached') self.log.warning('writeParameter stores to eeprom !') if self._readStatusWord() & (1 << 7): raise UsageError( self, 'Can not access Parameters while Motor is ' 'moving, please stop it first!') # wait for inactive ACK/NACK self.log.debug('Wait for idle ACK/NACK bits') for _ in range(1000): if self._readStatusWord() & (3 << 14) == 0: break else: raise CommunicationError( self, 'HW still busy, can not write ' 'Parameter, please retry later') self._writeDestination(value) if store2eeprom: # store to eeprom # index goes to bits 8..15, also set bit1 (2) = eeprom and # bit0 (1) = set_parameter self._writeUpperControlWord((index << 8) | 3) else: # store to volatile memory # index goes to bits 8..15, also set bit0 (1) = set_parameter self._writeUpperControlWord((index << 8) | 1) self.log.debug('Wait for ACK/NACK bits') for _ in range(1000): if self._readStatusWord() & (3 << 14) != 0: break else: raise CommunicationError( self, 'WritePar command not recognized ' 'by HW, please retry later....') if self._readStatusWord() & (1 << 14): raise CommunicationError( self, 'Writing of Parameter %r failed, ' 'got a NACK' % index) return self._readReturn()
def doSetPosition(self, value): for _ in range(100): if self._readStatusWord() & (1 << 7): continue break else: raise UsageError( self, 'Can not set position while motor is ' 'moving, please stop it first!') was_on = self._readControlBit(0) if was_on: self._HW_disable() # wait for inactive ACK/NACK for _ in range(1000): if self._readStatusWord() & (3 << 14) == 0: break else: raise CommunicationError( self, 'HW still busy, can not set ' 'position, please retry later.') loops = 10 for loop in range(loops): self.log.debug('setPosition: loop %d of %d', loop, loops) self._writeDestination(self._phys2steps(value)) # index=1: update current position self._writeUpperControlWord((1 << 8) | 1) # Wait for ACK/NACK bits for _ in range(100): last_sw = self._readStatusWord() if last_sw & (3 << 14) != 0: break else: self.log.warning( 'SetPosition command not recognized, retrying') if last_sw & (2 << 14) != 0: self.log.debug('setPosition: got ACK') break elif last_sw & (1 << 14): self.log.debug('setPosition: got NACK') raise CommunicationError( self, 'Setting position failed, ' 'got a NACK!') else: raise CommunicationError( self, 'setPosition command not ' 'recognized by HW, please retry later.') if was_on: self._HW_enable()
def doRead(self, maxage=0): if self._cache: try: time, ttl, val = self._cache.get_explicit(self, 'value') except CacheError: raise CommunicationError(self, CACHE_NOVALUE_STRING) if time and ttl and time + ttl < currenttime(): # Note: this will only be reached if self.maxage is expired as well self.log.warning('value timed out in cache, this should be ' 'considered as an error!') return val raise CommunicationError( self, '%s: no cache found' % self.__class__.__name__)
def connect_pv(self, pvname, timeout): # Check pv is available try: _CONTEXT.get(pvname, timeout=timeout) except TimeoutError: raise CommunicationError(f'could not connect to PV {pvname}') return pvname
def _current(self): # return the current in Amperes resp = self._taco_guard(self._dev.communicate, 'I%d' % self.channel) # format is MMMM-E (mantissa/exponent) if len(resp) != 6: raise CommunicationError(self, 'invalid current readout %r' % resp) return float(resp[:4] + 'e' + resp[4:])
def _get_pv(self, pvparam, field='value', as_string=False): """ Uses pvaccess to obtain a field from a PV. The default field is value, so that val = self._get_pv('readpv') retrieves the value of the PV. To obtain alarm or other status information, the field parameter can be specified: alarm = self._get_pv('readpv', field='alarm') Args: pvparam: The PV parameter to be queried. Is translated to a PV name internally. field: Field of the PV to obtain, default is value. Returns: Value of the queried PV field. """ # result = self._pvs[pvparam].get(field).toDict().get(field) result = _pvget(self._pvs[pvparam], field, as_string) if result is None: # timeout or channel not connected raise CommunicationError(self, 'timed out getting PV %r from EPICS' % self._get_pv_name(pvparam)) return result
def _getRaw(self): if parse is None: raise NicosError( self, 'cannot parse web page, lxml is not ' 'installed on this system') url = 'http://%s/web?group=%d' % (self.hostname, self.group) try: tree = parse(url) names = [ name.text.strip() for name in tree.findall('//font[@class="tagname"]') ] values = [ value.text.strip() for value in tree.findall('//font[@class="pv"]') ] info = dict(zip(names, values)) if self.valuename not in info: raise ConfigurationError( self, 'value %r not found on ' 'Memograph page (valid values are %s)' % (self.valuename, ', '.join(map(repr, names)))) return info[self.valuename] except ConfigurationError: # pass through error raised above raise except Exception as err: raise CommunicationError( self, 'Memograph not responding or ' 'changed format: %s' % err)
def doPreinit(self, mode): # Don't create PVs in simulation mode self._pvs = {} self._pvctrls = {} if mode != SIMULATION: # in case we get started in a thread, make sure to use the global # CA context in that thread if epics.ca.current_context() is None: epics.ca.use_initial_context() # When there are standard names for PVs (see motor record), the PV # names may be derived from some prefix. To make this more flexible, # the pv_parameters are obtained via a method that can be overridden # in subclasses. pv_parameters = self._get_pv_parameters() for pvparam in pv_parameters: # Retrieve the actual PV-name from (potentially overridden) method pvname = self._get_pv_name(pvparam) if not pvname: raise ConfigurationError(self, 'PV for parameter %s was ' 'not found!' % pvparam) pv = self._pvs[pvparam] = epics.pv.PV( pvname, connection_timeout=self.epicstimeout) pv.connect() if not pv.wait_for_connection(timeout=self.epicstimeout): raise CommunicationError(self, 'could not connect to PV %r' % pvname) self._pvctrls[pvparam] = pv.get_ctrlvars() or {} else: for pvparam in self._get_pv_parameters(): self._pvs[pvparam] = HardwareStub(self) self._pvctrls[pvparam] = {}
def _send_command(self, prefix, command, retry=None): try: # Connect once for each request to avoid problems with overlapping # commands/responses to/from the Serial->Ethernet device. ip, port = self.host.split(':') sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((ip, int(port))) message = '{}{}{}\r'.format(prefix, self.index, command) self.log.debug('Sending message: ' + message) sock.send(message) self.log.debug('Message sent, waiting for reply') reply = sock.recv(1024) self.log.debug('Got reply: ' + str(len(reply)) + ' ' + str(reply)) sock.close() try: if reply.endswith('\x06'): return reply[:-1] return reply except TypeError: return None except OSError: if retry is not None: self.log.debug('Retry unsuccessful, raising error.') raise CommunicationError( 'Communication error with device.') from None self.log.debug('Socket error, retrying after 0.1 second sleep.') sleep(0.1) return self._send_command(prefix, command, True)
def doInit(self, mode): if mode != SIMULATION: if not self.comm('IVR').startswith('MCC'): raise CommunicationError(self, 'No Response from Phytron, ' 'please check!') self._pushParams() self.doReset()
def writeParameter(self, index, value, store2eeprom=False): self.log.debug('writeParameter %d:0x%04x', index, value) if store2eeprom: self.log.warning('writeParameter stores to eeprom !') try: index = int(self._paridx.get(index, index)) except ValueError: UsageError(self, 'Unknown parameter %r' % index) if self._readStatusWord() & (1 << 7): raise UsageError( self, 'Can not access Parameters while Motor is ' 'moving, please stop it first!') if self.power == 'on': self.power = 'off' # wait for inactive ACK/NACK self.log.debug('Wait for idle ACK/NACK bits') for _ in range(1000): if self._readStatusWord() & (3 << 14) == 0: break else: raise CommunicationError( self, 'HW still busy, can not write ' 'Parameter, please retry later.') self._writeDestination(value) if store2eeprom: # store to eeprom self._writeUpperControlWord((index << 8) | 3) else: # store to volatile memory self._writeUpperControlWord((index << 8) | 1) self.log.debug('Wait for ACK/NACK bits') for _ in range(1000): if self._readStatusWord() & (3 << 14) != 0: break else: raise CommunicationError( self, 'WritePar command not recognized ' 'by HW, please retry later.') if self._readStatusWord() & (1 << 14): raise CommunicationError( self, 'Writing of Parameter %r failed, ' 'got a NACK' % index) return self._readReturn()
def doWriteRamp(self, ramp): ramp = int(ramp / 60.) if ramp == 0: raise ConfigurationError( self, 'ramp is too small; must be at ' 'least 60 V/min') resp = self._taco_guard(self._dev.communicate, 'V%d=%03d' % (self.channel, ramp)) if resp: raise CommunicationError( self, 'could not write ramp, reply from ' 'device: %r' % resp) resp = self._taco_guard(self._dev.communicate, 'A%d=01' % self.channel) if resp: raise CommunicationError( self, 'could not save ramp, reply from ' 'device: %r' % resp) self.log.info('ramp set to %d V/min and stored in EEPROM', ramp * 60)
def _rest_request(self, endpoint): response = requests.get('http://{}/{}/{}'.format( self.host, endpoint, self.service)) jsondata = response.json() if 'data' in jsondata: return jsondata['data'] raise CommunicationError('Error: {}'.format(jsondata['error']))
def doRead(self, maxage=0): if epics.ca.current_context() is None: epics.ca.use_initial_context() result = self._pvs['readpv'].get(timeout=self.epicstimeout, count=self.count) if result is None: # timeout raise CommunicationError( self, 'timed out getting PV %r from EPICS' % self._get_pv_name('readpv')) return result
def _execute(self, q, t=5, wait=0): try: os.write(self._io, q.encode() + b'\n') except OSError as err: if t == 0: raise CommunicationError(self, 'error executing: %s' % err) session.delay(0.5) self.doReset() self._execute(q, t - 1) session.delay(wait) self._check_status()
def doInit(self, mode): if mode == SIMULATION: return self._dev = serial.Serial(self.port) # sanity check: try to ask the device for its identification and # make sure a suitable string is received self._dev.write('*IDN?\n') reply = self._dev.readline() if not reply.startswith('<some string>'): raise CommunicationError('wrong identification: %r' % reply)
def _get_pv(self, pvparam, as_string=False): # since NICOS devices can be accessed from any thread, we have to # ensure that the same context is set on every thread if epics.ca.current_context() is None: epics.ca.use_initial_context() result = self._pvs[pvparam].get(timeout=self.epicstimeout, as_string=as_string) if result is None: # timeout raise CommunicationError(self, 'timed out getting PV %r from EPICS' % self._get_pv_name(pvparam)) return result
def readParameter(self, index): self.log.debug('readParameter %d', index) try: index = int(self._paridx.get(index, index)) except ValueError: raise UsageError( self, 'Unknown parameter %r, try one of %s' % (index, ', '.join(self._paridx))) from None if self._readStatusWord() & (1 << 7): raise UsageError( self, 'Can not access Parameters while Motor is ' 'moving, please stop it first!') if self.power == 'on': self.power = 'off' # wait for inactive ACK/NACK self.log.debug('Wait for idle ACK/NACK bits') for _ in range(1000): if self._readStatusWord() & (3 << 14) == 0: break else: raise CommunicationError( self, 'HW still busy, can not read ' 'Parameter, please retry later.') self._writeUpperControlWord((index << 8) | 4) self.log.debug('Wait for ACK/NACK bits') for _ in range(1000): if self._readStatusWord() & (3 << 14) != 0: break else: raise CommunicationError( self, 'ReadPar command not recognized by ' 'HW, please retry later.') if self._readStatusWord() & (1 << 14): raise CommunicationError( self, 'Reading of Parameter %r failed, ' 'got a NACK' % index) return self._readReturn()
def check_tango_host_connection(address, timeout=3.0): """Check pure network connection to the tango host.""" tango_host = os.environ.get('TANGO_HOST', 'localhost:10000') if address.startswith('tango://'): tango_host = address.split('/')[2] try: with tcpSocketContext(tango_host, 10000, timeout=timeout): pass except socket.error as e: raise CommunicationError(str(e))
def _getRaw(self): url = 'http://%s/Single%s' % (self.hostname, self.port) try: response = urllib.request.urlopen(url) html = response.read() return html except ConfigurationError: # pass through error raised above raise except Exception as err: raise CommunicationError( self, 'wut-box not responding or changed format: %s' % err) from err
def _getRaw(self): url = 'http://%s/Single%s' % (self.hostname, self.port) try: response = urllib.request.urlopen(url) # pylint:disable=consider-using-with html = response.read().decode('utf-8') return str(html) except ConfigurationError: # pass through error raised above raise except Exception as err: raise CommunicationError( self, 'wut-box not responding or changed format: %s' % err ) from err
def _get_value(maxage=None): # This is the original _get_pv. Unfortunately this class doesn't # derive from EpicsDevice, hence I can't use something like # EpicsDevice._get_pv(self, ...) if epics.ca.current_context() is None: epics.ca.use_initial_context() result = self._pvs[pvparam].get(timeout=self.epicstimeout, as_string=as_string) if result is None: # timeout raise CommunicationError( self, 'timed out getting PV %r from ' 'EPICS' % self._get_pv_name(pvparam)) return result
def _put_pv_blocking(self, pvparam, value, update_rate=0.1, timeout=60): if epics.ca.current_context() is None: epics.ca.use_initial_context() pv = self._pvs[pvparam] pv.put(value, use_complete=True) start = currenttime() while not pv.put_complete: if currenttime() - start > timeout: raise CommunicationError('Timeout in setting %s' % pv.pvname) session.delay(update_rate)
def _endatclearalarm(self): """Clear alarm for a binary-endat encoder.""" if self._type is not None and 'endat-protocol' not in self._type: return try: self._attached_bus.send(self.addr, 155, 185, 3) session.delay(0.5) self._attached_bus.send(self.addr, 157, 0, 3) session.delay(0.5) self.doReset() except Exception as err: raise CommunicationError(self, 'cannot clear alarm for encoder') from err
def communicate(self, msg, dest, expect_ok=False, expect_hex=0): msg = '%02X%02X%s' % (dest, self.source, msg) msg += self._crc(msg) ret = self._dev.communicate(msg) # check reply for validity crc = self._crc(ret[:-2]) if (len(ret) < 7 or ret[4] != '>' or ret[-2:] != crc or ret[:2] != '%02X' % self.source or ret[2:4] != '%02X' % dest): raise CommunicationError(self, 'garbled reply: %r' % ret) resp = ret[5:-2] if expect_ok and resp != 'OK': raise CommunicationError(self, 'unexpected reply: %r' % resp) if expect_hex: if len(resp) != expect_hex: raise CommunicationError(self, 'response invalid: %r' % resp) try: value = int(resp, 16) except ValueError: raise CommunicationError(self, 'invalid hex number: %r' % resp) return value return resp
def _getRaw(self): if urlopen is None: raise NicosError(self, 'cannot parse web page, urllib2 is not ' 'installed on this system') url = 'http://%s/Single%s' % (self.hostname, self.port) try: response = urlopen(url) html = response.read() return html except ConfigurationError: # pass through error raised above raise except Exception as err: raise CommunicationError(self, 'wut-box not responding or ' 'changed format: %s' % err)
def _check_status(self, t=3): try: os.write(self._io, b'*STB?\n') status = os.read(self._io, 100).rstrip().decode() except OSError as err: if t == 0: raise CommunicationError(self, 'error getting status: %s' % err) from err session.delay(0.5) self.doReset() return self._check_status(t - 1) if status != '0': self.log.warning('status byte = %s, clearing', status) os.write(self._io, b'*CLS\n')
def doStart(self, value): url = 'http://%s/outputaccess%s?PW=sans1&State=%s&' % ( self.hostname, self.port, value) try: response = urllib.request.urlopen(url, timeout=1) html = response.read() self.log.debug(html) except ConfigurationError: # pass through error raised above raise except Exception as err: raise CommunicationError( self, 'wut-box not responding or changed format: %s' % err) from err
def _transmit(self, request, retlen, last_try=False): response = '' self._dev.write(request) for _i in range(self.comtries): session.delay(self.bustimeout) try: data = self._dev.read() except Exception: data = '' if not data: continue response += data if response[-1] in (EOT, DC1, DC2, DC3, ACK, NAK): return response raise CommunicationError(self, 'no response')