1*9c5db199SXin Li# Copyright 2021 The Chromium OS Authors. All rights reserved. 2*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be 3*9c5db199SXin Li# found in the LICENSE file. 4*9c5db199SXin Li"""Python module for Abstract Instrument Library.""" 5*9c5db199SXin Li 6*9c5db199SXin Liimport logging 7*9c5db199SXin Liimport requests 8*9c5db199SXin Liimport socket 9*9c5db199SXin Li 10*9c5db199SXin Li 11*9c5db199SXin Liclass SocketInstrumentError(Exception): 12*9c5db199SXin Li """Abstract Instrument Error Class, via Socket and SCPI.""" 13*9c5db199SXin Li 14*9c5db199SXin Li def __init__(self, error, command=None): 15*9c5db199SXin Li """Init method for Socket Instrument Error. 16*9c5db199SXin Li 17*9c5db199SXin Li Args: 18*9c5db199SXin Li error: Exception error. 19*9c5db199SXin Li command: Additional information on command, 20*9c5db199SXin Li Type, Str. 21*9c5db199SXin Li """ 22*9c5db199SXin Li super(SocketInstrumentError, self).__init__(error) 23*9c5db199SXin Li self._error_code = error 24*9c5db199SXin Li self._error_message = self._error_code 25*9c5db199SXin Li if command is not None: 26*9c5db199SXin Li self._error_message = 'Command {} returned the error: {}.'.format( 27*9c5db199SXin Li repr(command), repr(self._error_message)) 28*9c5db199SXin Li 29*9c5db199SXin Li def __str__(self): 30*9c5db199SXin Li return self._error_message 31*9c5db199SXin Li 32*9c5db199SXin Li 33*9c5db199SXin Liclass SocketInstrument(object): 34*9c5db199SXin Li """Abstract Instrument Class, via Socket and SCPI.""" 35*9c5db199SXin Li 36*9c5db199SXin Li def __init__(self, ip_addr, ip_port): 37*9c5db199SXin Li """Init method for Socket Instrument. 38*9c5db199SXin Li 39*9c5db199SXin Li Args: 40*9c5db199SXin Li ip_addr: IP Address. 41*9c5db199SXin Li Type, str. 42*9c5db199SXin Li ip_port: TCPIP Port. 43*9c5db199SXin Li Type, str. 44*9c5db199SXin Li """ 45*9c5db199SXin Li self._logger = logging.getLogger(__name__) 46*9c5db199SXin Li self._socket_timeout = 120 47*9c5db199SXin Li self._socket_buffer_size = 1024 48*9c5db199SXin Li 49*9c5db199SXin Li self._ip_addr = ip_addr 50*9c5db199SXin Li self._ip_port = ip_port 51*9c5db199SXin Li 52*9c5db199SXin Li self._escseq = '\n' 53*9c5db199SXin Li self._codefmt = 'utf-8' 54*9c5db199SXin Li 55*9c5db199SXin Li self._socket = None 56*9c5db199SXin Li 57*9c5db199SXin Li def _connect_socket(self): 58*9c5db199SXin Li """Init and Connect to socket.""" 59*9c5db199SXin Li try: 60*9c5db199SXin Li self._socket = socket.create_connection( 61*9c5db199SXin Li (self._ip_addr, self._ip_port), 62*9c5db199SXin Li timeout=self._socket_timeout) 63*9c5db199SXin Li 64*9c5db199SXin Li infmsg = 'Opened Socket connection to {}:{} with handle {}.'.format( 65*9c5db199SXin Li repr(self._ip_addr), repr(self._ip_port), 66*9c5db199SXin Li repr(self._socket)) 67*9c5db199SXin Li 68*9c5db199SXin Li except socket.timeout: 69*9c5db199SXin Li errmsg = 'Socket timeout while connecting to instrument.' 70*9c5db199SXin Li raise SocketInstrumentError(errmsg) 71*9c5db199SXin Li 72*9c5db199SXin Li except socket.error: 73*9c5db199SXin Li errmsg = 'Socket error while connecting to instrument.' 74*9c5db199SXin Li raise SocketInstrumentError(errmsg) 75*9c5db199SXin Li 76*9c5db199SXin Li def _send(self, cmd): 77*9c5db199SXin Li """Send command via Socket. 78*9c5db199SXin Li 79*9c5db199SXin Li Args: 80*9c5db199SXin Li cmd: Command to send, 81*9c5db199SXin Li Type, Str. 82*9c5db199SXin Li """ 83*9c5db199SXin Li if not self._socket: 84*9c5db199SXin Li self._connect_socket() 85*9c5db199SXin Li 86*9c5db199SXin Li cmd_es = cmd + self._escseq 87*9c5db199SXin Li 88*9c5db199SXin Li try: 89*9c5db199SXin Li self._socket.sendall(cmd_es.encode(self._codefmt)) 90*9c5db199SXin Li 91*9c5db199SXin Li except socket.timeout: 92*9c5db199SXin Li errmsg = ('Socket timeout while sending command {} ' 93*9c5db199SXin Li 'to instrument.').format(repr(cmd)) 94*9c5db199SXin Li raise SocketInstrumentError(errmsg) 95*9c5db199SXin Li 96*9c5db199SXin Li except socket.error: 97*9c5db199SXin Li errmsg = ('Socket error while sending command {} ' 98*9c5db199SXin Li 'to instrument.').format(repr(cmd)) 99*9c5db199SXin Li raise SocketInstrumentError(errmsg) 100*9c5db199SXin Li 101*9c5db199SXin Li except Exception as err: 102*9c5db199SXin Li errmsg = ('Error {} while sending command {} ' 103*9c5db199SXin Li 'to instrument.').format(repr(cmd), repr(err)) 104*9c5db199SXin Li raise SocketInstrumentError(errmsg) 105*9c5db199SXin Li 106*9c5db199SXin Li def _recv(self): 107*9c5db199SXin Li """Receive response via Socket. 108*9c5db199SXin Li 109*9c5db199SXin Li Returns: 110*9c5db199SXin Li resp: Response from Instrument via Socket, 111*9c5db199SXin Li Type, Str. 112*9c5db199SXin Li """ 113*9c5db199SXin Li if not self._socket: 114*9c5db199SXin Li self._connect_socket() 115*9c5db199SXin Li 116*9c5db199SXin Li resp = '' 117*9c5db199SXin Li 118*9c5db199SXin Li try: 119*9c5db199SXin Li while True: 120*9c5db199SXin Li resp_tmp = self._socket.recv(self._socket_buffer_size) 121*9c5db199SXin Li resp_tmp = resp_tmp.decode(self._codefmt) 122*9c5db199SXin Li resp += resp_tmp 123*9c5db199SXin Li if len(resp_tmp) < self._socket_buffer_size: 124*9c5db199SXin Li break 125*9c5db199SXin Li 126*9c5db199SXin Li except socket.timeout: 127*9c5db199SXin Li errmsg = 'Socket timeout while receiving response from instrument.' 128*9c5db199SXin Li raise SocketInstrumentError(errmsg) 129*9c5db199SXin Li 130*9c5db199SXin Li except socket.error: 131*9c5db199SXin Li errmsg = 'Socket error while receiving response from instrument.' 132*9c5db199SXin Li raise SocketInstrumentError(errmsg) 133*9c5db199SXin Li 134*9c5db199SXin Li except Exception as err: 135*9c5db199SXin Li errmsg = ('Error {} while receiving response ' 136*9c5db199SXin Li 'from instrument').format(repr(err)) 137*9c5db199SXin Li raise SocketInstrumentError(errmsg) 138*9c5db199SXin Li 139*9c5db199SXin Li resp = resp.rstrip(self._escseq) 140*9c5db199SXin Li 141*9c5db199SXin Li return resp 142*9c5db199SXin Li 143*9c5db199SXin Li def _close_socket(self): 144*9c5db199SXin Li """Close Socket Instrument.""" 145*9c5db199SXin Li if not self._socket: 146*9c5db199SXin Li return 147*9c5db199SXin Li 148*9c5db199SXin Li try: 149*9c5db199SXin Li self._socket.shutdown(socket.SHUT_RDWR) 150*9c5db199SXin Li self._socket.close() 151*9c5db199SXin Li self._socket = None 152*9c5db199SXin Li 153*9c5db199SXin Li except Exception as err: 154*9c5db199SXin Li errmsg = 'Error {} while closing instrument.'.format(repr(err)) 155*9c5db199SXin Li raise SocketInstrumentError(errmsg) 156*9c5db199SXin Li 157*9c5db199SXin Li def _query(self, cmd): 158*9c5db199SXin Li """query instrument via Socket. 159*9c5db199SXin Li 160*9c5db199SXin Li Args: 161*9c5db199SXin Li cmd: Command to send, 162*9c5db199SXin Li Type, Str. 163*9c5db199SXin Li 164*9c5db199SXin Li Returns: 165*9c5db199SXin Li resp: Response from Instrument via Socket, 166*9c5db199SXin Li Type, Str. 167*9c5db199SXin Li """ 168*9c5db199SXin Li self._send(cmd + ';*OPC?') 169*9c5db199SXin Li resp = self._recv() 170*9c5db199SXin Li return resp 171*9c5db199SXin Li 172*9c5db199SXin Li 173*9c5db199SXin Liclass RequestInstrument(object): 174*9c5db199SXin Li """Abstract Instrument Class, via Request.""" 175*9c5db199SXin Li 176*9c5db199SXin Li def __init__(self, ip_addr): 177*9c5db199SXin Li """Init method for request instrument. 178*9c5db199SXin Li 179*9c5db199SXin Li Args: 180*9c5db199SXin Li ip_addr: IP Address. 181*9c5db199SXin Li Type, Str. 182*9c5db199SXin Li """ 183*9c5db199SXin Li self._request_timeout = 120 184*9c5db199SXin Li self._request_protocol = 'http' 185*9c5db199SXin Li self._ip_addr = ip_addr 186*9c5db199SXin Li self._escseq = '\r\n' 187*9c5db199SXin Li 188*9c5db199SXin Li def _query(self, cmd): 189*9c5db199SXin Li """query instrument via request. 190*9c5db199SXin Li 191*9c5db199SXin Li Args: 192*9c5db199SXin Li cmd: Command to send, 193*9c5db199SXin Li Type, Str. 194*9c5db199SXin Li 195*9c5db199SXin Li Returns: 196*9c5db199SXin Li resp: Response from Instrument via request, 197*9c5db199SXin Li Type, Str. 198*9c5db199SXin Li """ 199*9c5db199SXin Li request_cmd = '{}://{}/{}'.format(self._request_protocol, 200*9c5db199SXin Li self._ip_addr, cmd) 201*9c5db199SXin Li resp_raw = requests.get(request_cmd, timeout=self._request_timeout) 202*9c5db199SXin Li 203*9c5db199SXin Li resp = resp_raw.text 204*9c5db199SXin Li for char_del in self._escseq: 205*9c5db199SXin Li resp = resp.replace(char_del, '') 206*9c5db199SXin Li 207*9c5db199SXin Li return resp 208