• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2017 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Interface for SCPI Protocol.
6
7Helper module to communicate with devices that uses SCPI protocol.
8
9https://en.wikipedia.org/wiki/Standard_Commands_for_Programmable_Instruments
10
11This will be used by RF Switch that was designed to connect WiFi AP and
12WiFi Clients RF enclosures for interoperability testing.
13
14"""
15
16from __future__ import print_function
17
18import logging
19import six
20import socket
21import sys
22
23
24class ScpiException(Exception):
25    """Exception for SCPI Errors."""
26
27    def __init__(self, msg=None, cause=None):
28        messages = []
29        if msg:
30            messages.append(msg)
31        if cause:
32            messages.append('Wrapping exception: %s: %s' % (
33                type(cause).__name__, str(cause)))
34        super(ScpiException, self).__init__(', '.join(messages))
35
36
37class Scpi(object):
38    """Controller for devices using SCPI protocol."""
39
40    SCPI_PORT = 5025
41    DEFAULT_READ_LEN = 4096
42
43    CMD_IDENTITY = '*IDN?'
44    CMD_RESET = '*RST'
45    CMD_STATUS = '*STB?'
46    CMD_ERROR_CHECK = 'SYST:ERR?'
47
48    def __init__(self, host, port=SCPI_PORT):
49        """
50        Controller for devices using SCPI protocol.
51
52        @param host: hostname or IP address of device using SCPI protocol
53        @param port: Int SCPI port number (default 5025)
54
55        @raises SCPIException: on error connecting to device
56
57        """
58        self.host = host
59        self.port = port
60
61        # Open a socket connection for communication with chassis.
62        try:
63            self.socket = socket.socket()
64            self.socket.connect((host, port))
65        except (socket.error, socket.timeout) as e:
66            logging.error('Error connecting to SCPI device.')
67            six.reraise(ScpiException(cause=e), None, sys.exc_info()[2])
68
69    def close(self):
70        """Close the connection."""
71        if hasattr(self, 'socket'):
72            self.socket.close()
73            del self.socket
74
75    def write(self, data):
76        """Send data to socket.
77
78        @param data: Data to send
79
80        @returns number of bytes sent
81
82        """
83        return self.socket.send(data)
84
85    def read(self, buffer_size=DEFAULT_READ_LEN):
86        """Safely read the query response.
87
88        @param buffer_size: Int max data to read at once (default 4096)
89
90        @returns String data read from the socket
91
92        """
93        return str(self.socket.recv(buffer_size))
94
95    def query(self, data, buffer_size=DEFAULT_READ_LEN):
96        """Send the query and get response.
97
98        @param data: data (Query) to send
99        @param buffer_size: Int max data to read at once (default 4096)
100
101        @returns String data read from the socket
102
103        """
104        self.write(data)
105        return self.read(buffer_size)
106
107    def info(self):
108        """Get Chassis Info.
109
110        @returns dictionary information of Chassis
111
112        """
113        # Returns a comma separated text as below converted to dict.
114        # 'VTI Instruments Corporation,EX7200-S-11539,138454,3.13.8\n'
115        return dict(
116            zip(('Manufacturer', 'Model', 'Serial', 'Version'),
117                self.query('%s\n' % self.CMD_IDENTITY)
118                .strip().split(',', 3)))
119
120    def reset(self):
121        """Reset the chassis.
122
123        @returns number of bytes sent
124        """
125        return self.write('%s\n' % self.CMD_RESET)
126
127    def status(self):
128        """Get status of relays.
129
130        @returns Int status of relays
131
132        """
133        return int(self.query('%s\n' % self.CMD_STATUS))
134
135    def error_query(self):
136        """Check for any error.
137
138        @returns tuple of error code and error message
139
140        """
141        code, msg = self.query('%s\n' % self.CMD_ERROR_CHECK).split(', ')
142        return int(code), msg.strip().strip('"')
143