FlexEye testing: running FlexEye child processess

This Python example demonstates using FlexEye independent acquisition. The example works with FlexDCA offline and installs simulated modules. No hardware is required. For this example, two programs are used. One program is used to launch another program as a child process. The child process performs a measurement on a specific channels. To work, both programs must be located in the same folder. From the sidebar shown in this topic, you must save these two files:

  • FlexEye_sessions.py (parent process)
  • FlexEye_sessions_child.py (child process)

The child process, FlexEye_sessions_child.py, is run on five FlexEye sessions using Python's standard subprocess library.

The child process acquires 10000 waveforms and performs a risetime measurement on the resulting waveforms. Because of the very large number of acquisitions, the data takes long enough for the parent process to illustrate the different FlexEye states.

Parent Script (FlexEYE_sessions.py)

The arguments passed to subprocess.Popen() are passed as a list. When you run this program, you will see the entire list (converted to a string) that is passed to each call to subprocess.Popen(). The examine_FlexEye() function allows the user to make selections while the five child processes run and after they complete. This illustrates FlexEye in pause mode where the sessions can be viewed. When FlexEye is paused, notice that the acquisition tests stop and then resume when FlexEye is returned to run mode.

Copy

FlexEye_sessions.py

# -*- coding: utf-8 -*-
""" Launches subprocess: FlexEye_sessions_child.py
Demonstrates FlexEye independent eye acquisition.
As the child processes are running, the script prompts you
to make selections that show:
- The "sessions" dictionary, that is near the top of the script,
defines the FlexEye sessions to be created. Dictionary keys represent
FlexEye session numbers and the dictionary values represents
valid channels to measure for the session. The assignments show
the variety of channel combinations allowed for sessions.
- Each session will run as independent oscilloscope.
- Child processes can be paused and restarted.
This script uses two simulated modules. The child processes
do nothing but take a lot of time so that you can view different
tabs and pause FlexEye. No data is returned from the child processes.
The parent script prints VISA address and channels tested for each
session (child process). """

import subprocess
import sys
import pyvisa as visa  # import VISA library

IOTIMEOUT = 60000  # 60 seconds
ADDRESS = 'TCPIP0::localhost::hislip0,4880::INSTR'
SLOT5 = '5'
SLOT6 = '6'
# FlexEye channel assignments for sessions
sessions = {1: 'D5A',
            2: '5C,6A',
            3: '5D',
            4: '6B',
            5: '6C,6D',
            6: '', 7: '', 8: '', 9: '', 10: '', 11: '', 12: '', 13: '', 14: '', 15: ''}


def open_flexdca_connection(address):
    """ Opens visa connection to FlexFlexDCA. """
    print('Connecting to Flexdca ...')
    try:
        rm = visa.ResourceManager()
        connection = rm.open_resource(address)
        connection.timeout = 20000  # Set connection timeout to 20s
        connection.read_termination = '\n'
        connection.write_termination = '\n'
        inst_id = connection.query('*IDN?')
        print('\nFlexDCA connection established to:\n' + inst_id, flush=True)
    except (visa.VisaIOError, visa.InvalidSession):
        print('\nVISA ERROR: Cannot open instrument address.\n', flush=True)
        return None
    except Exception as other:
        print('\nVISA ERROR: Cannot connect to instrument:', other, flush=True)
        print('\n')
        return None
    return connection


def initialize_FlexDCA(flexdca):
    """ Place FlexDCA in a know starting state.
    Checks to see if FlexEye is already running. If so exit FlexEye. Perform
    a default setup.
    """
    flexeye_state = flexdca.query(':FEYE:STATe?')
    if 'RUN' in flexeye_state:
        flexdca.query(':FEYE:STATe PAUSe;*OPC?')
        flexdca.query(':FEYE:STATe STOP;*OPC?')
    elif 'PAUSe' in flexeye_state:
        flexdca.query(':FEYE:STATe STOP;*OPC?')
    flexdca.write('*CLS')
    flexdca.query(':SYSTem:DEFault;*OPC?')
    flexdca.query(':SYSTem:MODE EYE;*OPC?')


def install_simulated_module(flexdca, slot):
    """ Installs a simulated module in the first available slot.
    Second argument selects type of module based on arguments to
    :EMODules:SLOT:SELection command. If argument is missing, a
    quad-electrical module is installed. Returns a slot number (string). '0'
    if no available slots.
    """
    flexdca.write(':EMODules:SLOT'+slot+':SELection QEM')
    for c in ['A', 'B', 'C', 'D']:
        channel = slot + c
        flexdca.write(':SOURce'+channel+':FORMat NRZ')
        flexdca.write(':SOURce'+channel+':DRATe 9.95328e9')
        flexdca.write(':SOURce'+channel+':WTYPe DATA')
        flexdca.write(':SOURce'+channel+':PLENgth 127')
        # 90 mV amplitude
        flexdca.write(':SOURce'+channel+':AMPLitude 90E-3')
        # Add 3 uV random noise
        flexdca.write(':SOURce'+channel+':NOISe:RN 3.0E-6')
        # Add 4 ps random jitter
        flexdca.write(':SOURce'+channel+':JITTer:RJ 4.0E-12')
        print('Simulated module installed in slot ' + slot, flush=True)
        return slot


def all_channels_off(flexdca):
    """ Turns all available channels off. """
    for slot in '12345678':
        for letter in 'ABCD':
            channel = slot + letter
            flexdca.write(':CHANnel' + channel + ':DISPlay OFF')


def configure_FlexEye(flexdca, sessions):
    """ Configures each FlexEye session to the state defined in the
    sessions dictionary. Then, turns FlexEye on. This populates FlexDCA's
    FlexEye Streaming Setup dialog.
    """
    print('Configuring FlexEye. Please wait...', flush=True)
    for session in sessions:  # iterate through 15 session keys
        # create iterable list of channels from comma separated string
        channels = sessions[session].split(',')
        if channels[0]:  # if channels list has channels
            for ch in channels:  # run through channel tupple
                if ch[0] in 'D':  # diff channel, eg, D1A
                    channel = ch[1:3]   # eg, 1A
                    flexdca.write(':SOURce'+channel+':DIFFerential ON')
                    flexdca.query(':DIFF'+channel+':DMODe ON;*OPC?')
                    flexdca.write(':DIFF'+channel+':DISPlay ON')
                    flexdca.write(':FEYE:CHANnel' + channel +
                                  ':SID SESSion' + str(session))
                    flexdca.write(':FEYE:DIFF' + channel + ':ENABled ON')
                else:  # configure channel, eg, 1A
                    flexdca.write(':CHANnel' + ch + ':DISPlay ON')
                    flexdca.write(':FEYE:CHANnel' + ch + ':SID SESSion' +
                                  str(session))
                    flexdca.write(':FEYE:CHANnel' + ch + ':ENABled ON')
    flexdca.query(':FEYE:STATe RUN;*OPC?')
    print('Sessions are configured.', flush=True)
    sys.stdout.flush()  # Flush print message in stdout


def run_child_processes(primary_address, sessions):
    """ Starts the FlexEye child process for each session. Creates
    the session VISA address based on the address of the primary
    process. Passes the address and list of session channels as
    arguments to the child process.
    """
    print('Starting child processes.')
    child_processes = []  # list of child processes
    command = ['python', 'FlexEye_sessions_child.py', '', '']
    for session in sessions:  # iterate through 15 session keys
        channels = sessions[session]  # comma separated string of channels
        if channels:  # if valid session with channels
            command[2] = primary_address.replace('hislip0',
                                                 'hislip' + str(session))
            command[3] = channels
            p = subprocess.Popen(command,
                                 stdout=subprocess.PIPE,
                                 universal_newlines=True)
            child_processes.append(p)
            print('\nCommand arguments for Session ' + str(session) + ':')
            print('  VISA address: ' + command[2])
            print('  Channels: ' + command[3])
    print('\nChild processes are running.', flush=True)
    return


def examine_FlexEye(flexdca):
    """ Pauses FlexEye to allow user to click on Session tabs. User exits
        FlexEye when ready.
    """
    enabled_sessions = ['1', '2', '3', '4', '5']
    flexdca.write(':FEYE:VIEW ' + 'SESSion' + enabled_sessions[0])
    prompt = 'FlexEye is RUNNING! You can view Sessions ' \
             + 'in progress.\nSelect an action: '
    while True:
        print('-' * 50)
        print('"Enter" key toggles PAUSE/RUN modes.')
        print('Enter "q" exits FlexEye.')
        print('Enter number to select session:')
        print('  0. Primary')
        # print session selections for user
        i = 1
        for session in enabled_sessions:
            print('  ' + str(i) + '. Session ' + session)
            i += 1
        choice = input(prompt)
        if (choice == 'q' or choice == 'Q'):
            state = flexdca.query(':FEYE:STATe?')
            if 'PAUS' in state:
                flexdca.query(':FEYE:STATe RUN;*OPC?')
            flexdca.write(':FEYE:VIEW PRIMary')
            flexdca.query(':FEYE:STATe STOP;*OPC?')
            print('Script Finished.')
            return
        elif choice == '':
            prompt = toggle_FlexEye_Mode(flexdca, enabled_sessions)
        elif choice == '0':
            flexdca.write(':FEYE:VIEW PRIMary')
            prompt = 'Select an action: '
        else:
            try:
                n = int(choice) - 1
                flexdca.write(':FEYE:VIEW ' + 'SESSion' + enabled_sessions[n])
                prompt = 'Select an action: '
            except:
                prompt = 'Incorrect entry. Select an action: '
                continue


def toggle_FlexEye_Mode(flexdca, enabled_sessions):
    if 'RUN' in flexdca.query(':FEYE:STATe?'):
        flexdca.query(':FEYE:STATe PAUSe;*OPC?')
        flexdca.write(':FEYE:VIEW ' + enabled_sessions[0])
        return 'FlexEye is PAUSED!\nSelect an action: '
    else:
        flexdca.query(':FEYE:STATe RUN;*OPC?')
        flexdca.write(':FEYE:VIEW ' + enabled_sessions[0])
        return 'FlexEye is RUNNING! You can view Sessions \
in progress.\nSelect an action to end FlexEye: '


FlexDCA = open_flexdca_connection(ADDRESS)
FlexDCA.timeout = IOTIMEOUT
initialize_FlexDCA(FlexDCA)
install_simulated_module(FlexDCA, SLOT5)
install_simulated_module(FlexDCA, SLOT6)
all_channels_off(FlexDCA)
configure_FlexEye(FlexDCA, sessions)
run_child_processes(ADDRESS, sessions)
examine_FlexEye(FlexDCA)
FlexDCA.write(':SYSTem:GTLocal')
FlexDCA.close()

Child Script (FlexEye_sessions_child.py)

Copy

FlexEye_sessions_child.py

# -*- coding: utf-8 -*-
""" Subprocess: FlexEye_sessions_child.py launched by FlexEye_sessions.py
This program is designed to be a sub-process that is run by
FlexEye_sessions.py as an example of FlexEye independent acquisitions.
This program performs a Rise Time measurement on an eye diagram.
sys.argv[1] is the session's VISA address command-line argument
sys.argv[2] is a list of channels. """

import sys
import pyvisa as visa

IOTIMEOUT = 120000  # Set connection timeout to 2 minutes.


def initialize_setup(FlexDCA, channels):
    """ Places session in known state.
        Turns on and autoscales session channels.
    """
    FlexDCA.query(':SYSTem:DEFault;*OPC?')
    for ch in channels:
        if 'D' in ch[0]:  # differential channel. eg, D1A
            s = ch[1:3]
            FlexDCA.write(':CHAN'+s+':DISPlay OFF')
            # turn on differential channel
            FlexDCA.query(':DIFF'+s+':DMODe ON;*OPC?')
            FlexDCA.write(':DIFF'+s+':DISPlay ON')
        else:  # configure channel
            FlexDCA.write(':CHAN'+ch+':DISPlay ON')
    FlexDCA.query(':SYSTem:AUToscale;*OPC?')


def acquisition_limit_test(FlexDCA):
    """ A limit acquisition test ensures that enough waveform
        data is available to start measuring the eye. Captures
        10000 waveforms which will take long enough to show
        advantage of running parallel processes.
    """
    FlexDCA.write(':ACQuire:STOP')
    FlexDCA.write(':ACQuire:CDISplay')
    FlexDCA.write(':MEASure:EYE:LIST:CLEar')
    FlexDCA.write(':LTESt:ACQuire:CTYPe:WAVeforms 100')
    FlexDCA.write(':LTESt:ACQuire:STATe ON')
    FlexDCA.query(':ACQuire:RUN;*OPC?')
    FlexDCA.write(':LTESt:ACQuire:STATe OFF')


def make_measurements(FlexDCA, channels):
    """ Measure each channel's risetime. """
    for ch in channels:  # run through list of channels
        if 'D' in ch[0]:  # differential channel. eg, D1A
            s = ch[1:3]   # eg, 1A
            FlexDCA.write(':MEASure:EYE:RISetime:SOURce1 DIFF' + s)
        else:  # non-diff channel
            FlexDCA.write(':MEASure:EYE:RISetime:SOURce1 CHAN' + ch)
        FlexDCA.write(':MEASure:EYE:RISetime')


address = sys.argv[1]  # VISA address command-line argument
s = sys.argv[2]  # comma separated string of channels
channels = s.split(',')  # a list of channels
rm = visa.ResourceManager()
FlexDCA = rm.open_resource(address)
FlexDCA.timeout = IOTIMEOUT
FlexDCA.read_termination = '\n'
FlexDCA.write_termination = '\n'
initialize_setup(FlexDCA, channels)
acquisition_limit_test(FlexDCA)
make_measurements(FlexDCA, channels)
FlexDCA.write(':SYSTem:GTLocal')
FlexDCA.close()  # Close VISA session