Thursday, July 9, 2015

Python for LXI Instrument Control



Summary

LXI instruments often provide an interface to their parsers via telnet and/or sockets.  These connections do not require a VISA library as the communication layer.  On systems that don’t support VISA or users who just prefer to not use the VISA library, sockets is a good choice for connecting to and controlling instruments.  Python is a programming language that is easy to learn and is supported across many different operating systems.  Python is especially popular on UNIX-like OS’s like Linux and HP-UX.  This article will give a quick introduction on how to use Python for controlling your LXI instrument.

LXI Instruments

First let’s take a look at the LXI instrument.  There are two ports registered for SCPI services with the Internet Assigned Numbers Authority (IANA) for SCPI LAN services.   Those ports are 5024 for scpi-telnet and port 5025 for scpi-raw.   These are optional services that LXI instrument can implement.  The LXI Specification 1.4 has these services as a recommendation (See section 10.4.3.12).  Not all venders used the IANA reserved ports for these services, so you will need to review your specific instruments documentation to determine if these services are available and what port numbers to use.

What are these services?  Telnet is a simple protocol for connecting to a remote computer via a shell like a command prompt.  The default port for Telnet is 23 and typically does not need to be specified when starting a Telnet session.  The scpi-telnet port is 5024.  Scpi-telnet is a connection between a computer and a remote LXI instrument using a SCPI parser.   Scpi-telnet is useful for systems that don’t have any I/O Libraries installed, but have a LAN connection to their instrument.

Figure 1

Figure 1 is an example scpi-telnet session by using a command prompt.  The command used to start this session is ‘telnet 192.168.0.97 5024’.  Once there is an instrument prompt SCPI can be entered and responses display in the command window.

Scpi-raw is a connection between a computer and a LXI instrument using raw sockets.  This connection does not have the addition of the MEPE protocol that HiSlip or VXI-11 have.  If you desire or need MEPE you can use the PyVISA contribution for a Python application.

Python and Instruments

There is a PyVISA Python package and this enables the user to control instruments with any I/O that the instrument supports and is appropriate to use if you need to control an instrument over USB, VXI-11, HiSlip, or GPIB.  This package does require an installation of VISA exist on the controller to be able to open a session.  There is not a great deal of value in using the PyVISA package if using Sockets as your transport layer, plus you lose the  portability of moving to a system that does not have VISA.
Python has a low-level networking module that provides access to the BSD socket interface.   This article is not a tutorial assumes that the reader is familiar with sockets.  Here is a link to the Python documentation for socket programming.  Below is the code for starting the client side connects to an instrument:

def SocketConnect():
    try:
        #create an AF_INET, STREAM socket (TCP)
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        #NODELAY turns off Nagle to improves chatty performance
        s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 0)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, 0)
    except socket.error:
        print ('Failed to create socket.')
        sys.exit();
    try:
        #Connect to remote server
        s.connect((remote_ip, port))
        #print ('Socket Connected to ip ' + remote_ip)
    except socket.error:
        print ('failed to connect to ip ' + remote_ip)
    return s

The open is has two parts.  The first part is creating the socket.  After creating the socket I have set two options.  The first option is turning off NODELAY.  This turns off the Nagle algorithm which attempts to accumulate multiple send messages in a single packet.  This means with the Nagle ON a send will be scheduled to be sent a few milliseconds after the request waiting for other send requests that could be sent together saving some bandwidth.  Instrument SCPI tends to be chatty with lots of short strings waiting for responses, so with this ON it will increase the send latency adding time to the overall configuration of an instrument.  The second options is turning off LINGER.  When socket connections close they wait around for several minutes to process any stray packets that my still be in the system.  With LINGER off, it turns this wait time to 0 and so when the connection is closed it doesn’t wait around.  This really is not a good practice but I have seen many systems where they do a lot of open/closes which will result in the system eventually running out of resources and hanging on an open.  It’s better to re-architecture the system to reuse open sessions.
The second part connects to the remote instrument server.  This requires the IP Address or the hostname and the port number of the instrument service (default 5025).  Once the connection to the instrument service is successfully completed, we are ready to use the socket.
I have written a short script that open a socket, sends a query, and closes the socket.  It does this loop for 100 times.   Below is the core of this script:

def SocketQuery(Sock, cmd):
    try :
        #Send cmd string
        Sock.sendall(cmd)
    except socket.error:
        #Send failed
        print ('Send failed')
        sys.exit()
    reply = Sock.recv(4096)
    return reply

def SocketClose(Sock):
    Sock.close()
    #Sleep paces the rate of open/close.  Warning: This time worked for
    #the instrument I was using, mileage may vary.
    time.sleep(.300)
   
# Body
for i in range(100):
    s = SocketConnect()
    qStr = SocketQuery(s, b':DIAG:SYST:MEM?\n')
    SocketClose(s)
    print (str(count) + ":: " + str(qStr))
    count = count + 1

I wrote a function to return the response from a query.  It’s nice to have a set of functions like this so that your main control loop is a straight forward as possible.  I also wrote a close function.  This allows me to do any actions that might be needed to disconnect from the instrument service. 

Conclusion

Python is an interpreted programming language that lets you work quickly and is very portable.   LXI provides the transport and specifications for LXI instruments. Python scripts can be written for sockets to do a variety of test and measurements tasks.


---Full Source of Test Script ---
#!/usr/bin/env python
import socket   # for sockets
import sys  # for exit
import time # for sleep
import getopt

remote_ip = "156.140.113.41"
port = 5025
count = 0

def SocketConnect():
    try:
        #create an AF_INET, STREAM socket (TCP)
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        #NODELAY turns of Nagle improves chatty performance
        s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 0)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, 0)
    except socket.error:
        print ('Failed to create socket.')
        sys.exit();
    try:
        #Connect to remote server
        s.connect((remote_ip , port))
        #print ('Socket Connected to ip ' + remote_ip)
    except socket.error:
        print ('failed to connect to ip ' + remote_ip)
    return s

def SocketQuery(Sock, cmd):
    try :
        #Send cmd string
        Sock.sendall(cmd)
    except socket.error:
        #Send failed
        print ('Send failed')
        sys.exit()
    reply = Sock.recv(4096)
    return reply

def SocketClose(Sock):
    Sock.close()
    #Sleep paces the rate of open/close.  Warning: This time worked for 
    #the instrument I was using, milage may vary.
    time.sleep(.300)

def Usage():
    print('mySocket -a <ipaddress> -p <portnumber default=5025>')
    sys.exit();

def main(argv):
    global remote_ip
    global port
    global count
    try:
        opts, args = getopt.getopt(argv, "ha:p:",["address=","port="])
    except getopt.GetopError:
        Usage()
    for opt, arg in opts:
        if opt == '-h':
            Usage()
        elif opt in ("-a", "--addr"):
            remote_ip = arg
        elif opt in ("-p", "--port"):
            port = arg
    # Body
    for i in range(100):
        s = SocketConnect()
        qStr = SocketQuery(s, b':DIAG:SYST:MEM?\n')
        SocketClose(s)
        print (str(count) + ":: " + str(qStr))
        count = count + 1
main(sys.argv[1:])

3 comments:

  1. For anyone that is interested I do have an example of a script that works with telnet. It's a little trickier because you have to read off the welcome message and the prompt. It would have to be modified for each individual instruments welcome message and prompt, but it could be useful in the case of an instrument that implemented scpi-telnet but not scpi-sockets.

    ReplyDelete
  2. The python programming help is really helpful to us. I am sure that It can also helpful to the students. Very amazing post.

    ReplyDelete

  3. للحصول علي خدمة نظافة جيدة تاكد انك تتعامل مع شركة تنظيف بالرياض لديها باع طويل في مجال النظافة العامة فتعاملك مع شركة متميزة فانك ستحصل علي اد اء متميزة فشركات النظافة عديدة فمثلا عندما تبحث عن شركة تنظيف فلل بالرياض ستلاقيي شركة ركن البيت التي تمتلك امكانيات جيدة في اداء الخدمات المنزلية الجيدة بادوات حديثة ومواد مستوردة تقوم باستعمالها فمعك ايضا شركة تنظيف خزانات بالرياض اتي تقوم باداء جيدة لتسليمك خزان تمتلك به مياه نظيفة خالية من الرواسب والبكتريا كما سنمدك باعمال شركة تنظيف مسابح بالرياض التي تعطيك كل الامكانيات في امتلاك مسبح نظيف يحافظ علي اسرتك واطفالك بدون وجود اي اوساخ به ونقدم لكم الخدمة الخاصة بالمنازل والبيوت الصغيرة من خلال البحث عن شركة تنظيف منازل بالرياض التي تستطيع ان تحل لك كل مشاكل التنظيف المنزلي المتعبة التي تعاني منها واليكم خدمة القضاء علي الحشرات بواسطة مبيدات عالية الجودة تقوم بالقضاء علي جميع الحشرات الصغيرة والكبيرة بواسطة شركة رش مبيدات بالرياض التي تقضي علي جميع الحشرات

    ReplyDelete