Driver for Unrecognized Switch

FlexOTO Instance:
Hardware Diagram

This is an example of a switch driver written in Python. This example driver allows FlexOTO to use a DiCon GP600 which FlexOTO already supports! But, writing a driver for a supported switch that you might be familiar with is good technique for learning how to create and test your script. This driver connects the switch using the PC's RS-232 port (USB) port, so you would pass the COM port to the driver using the Instrument Connection Setup dialog. For example, COM4.

MIT License. Copyright(c) 2023 Keysight Technologies. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Copy
Custom GP600 as Generic Switch
import pyvisa as visa
import pyvisa.constants
DONE = 'DONE'


def get_command_line_argument():
    """ Reads command-line argument COM port for serial connection sent from FlexOTO's Switch Connection Setup dialog.
    argv[0] will be this script's name. Subsequent argv values will be the command line arguments from FlexOTO.

    Returns
        sys.argv[1]: str
    """
    import sys
    if len(sys.argv) != 2:
        print('Invalid: Only one command line argument allowed, for example "COM4".')
        print(DONE)
        return None
    if 'com' not in sys.argv[1].lower():
        print('Invalid: argument must be a COM port, for example "COM4".')
        print(DONE)
        return None
    return sys.argv[1]


def connect_to_switch(comport):
    """ Opens a connection to the optical switch.

        Args
            comport: str
        Returns
            ser: visa.ResourceManager object of switch
    """
    visa_library = r'C:\WINDOWS\system32\visa64.dll'
    com_address = {'COM1': 'ASRL1::INSTR',
                   'COM2': 'ASRL2::INSTR',
                   'COM3': 'ASRL3::INSTR',
                   'COM4': 'ASRL4::INSTR',
                   'COM5': 'ASRL5::INSTR',
                   'COM6': 'ASRL6::INSTR',
                   'COM7': 'ASRL7::INSTR',
                   'COM8': 'ASRL8::INSTR',
                   }
    try:
        rm = visa.ResourceManager()
        ser = rm.open_resource(com_address[comport])
        ser.timeout = 1000  # (seconds)
        ser.read_termination = '\r'
        ser.write_termination = '\r'
        ser.set_visa_attribute(visa.constants.VI_ATTR_ASRL_BAUD, 115200)
        ser.set_visa_attribute(visa.constants.VI_ATTR_ASRL_DATA_BITS, 8)
        ser.set_visa_attribute(visa.constants.VI_ATTR_ASRL_STOP_BITS, visa.constants.VI_ASRL_STOP_ONE)
        ser.set_visa_attribute(visa.constants.VI_ATTR_ASRL_PARITY, visa.constants.VI_ASRL_PAR_NONE)
    except:
        print('Unable to open port: ' + comport)
        print(DONE)
        return None
    # Do a query to make sure the connection is set up correctly.
    sw_name = ser.query('*IDN?')
    if not sw_name:
        print("Couldn't connect to switch.")
        print(DONE)
        return None
    # Successfully connected
    print(DONE)
    return ser


def get_description(oswitch):
    """ Responds to FlexOTO get_description query by returning a JSON formatted string that describes the switch.
    Groups represent one or more internal switch modules.
    Args
        oswitch: visa.ResourceManager object of switch
    stdout
        description: JSON str
        DONE: str
    Returns
        None
    """

    s = oswitch.query('*IDN?')
    id = s.split(',')
    serial_number = id[2].strip()
    description = """
{
    "ModelNumber": "DiCon GP600",
    "SerialNumber": "%s",
    "SettlingTimeSeconds": 50e-3,
    "Groups": [
        {
            "Name": "M1",
            "InputPorts": ["1","2","3","4","5","6","7","8"],
            "OutputPorts": ["IN"]
        },
        {
            "Name": "X1",
            "InputPorts": ["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19",
                            "20","21","22","23","24","25","26","27","28","29","30","31","32"],
            "OutputPorts": ["IN 1","IN 2","IN 3","IN 4","IN 5","IN 6","IN 7","IN 8"]
        }
    ]
} """ % serial_number  # Insert the queried serial number
    print(description)
    print(DONE)


def convert_port_names_to_arguments(route):
    """ The set routes commands uses the name of the ports as configured in the get_description JSON string.
    However, the arguments sent to the hardware switch have different order and strings. In FlexOTO, the
    GP600 input ports are used as output and the GP600 output ports are used as inputs.
    Args:
        route: str (comma delimited)
    Returns
        cmd_parts: str
    """
    s = route.replace('"', '')
    cmd_parts = s.split(', ')
    group = cmd_parts[0]
    flexoto_in_port = cmd_parts[1]
    flexoto_out_port = cmd_parts[2]
    if 'X1' in group:
        gp600_in_port = flexoto_out_port.replace('IN ', '')
    elif 'M1' in cmd_parts[0]:
        gp600_in_port = flexoto_out_port.replace('IN', '1')
    else:
        print('Unknown group name: ' + group)
        return
    gp600_out_port = flexoto_in_port
    cmd_parts[1] = gp600_in_port
    cmd_parts[2] = gp600_out_port
    return cmd_parts


def set_routes(oswitch, args):
    """ While the Test Plans are running, FlexOTO's Sessions configure one or more switch routes (paths) through
    the optical switch FlexOTO with this command. Each route is defined by the following three fields:
    name, switch block input port, and switch block output port.
    Example of a routes string for GP600:
        "X1, 4, IN 1" "X1, 5, IN 2"
    resulting in these commands to switch hardware:
        oswitch.write('X1 CH 1 4')
        oswitch.write('X1 CH 2 5')

    Args
        oswitch: visa.ResourceManager object of switch
        args: str (comma delimited)
    stdout
        DONE: str
    Returns
        None
    """
    routes = args.split('" "')
    for route in routes:
        cmd_parts = convert_port_names_to_arguments(route)
        group = cmd_parts[0]
        gp600_in_port = cmd_parts[1]
        gp600_out_port = cmd_parts[2]
        cmd = group + ' CH ' + gp600_in_port + ', ' + gp600_out_port
        oswitch.write(cmd)
        errcode = oswitch.query("SYST:ERR?")
        if '+0' in errcode:
            continue
        else:
            print('Error code: {} ("{}")'.format(errcode, cmd))
    print(DONE)


def set_wavelength(oswitch, grp_and_wavelength):
    """ Specifies the wavelength setting for a switch group. If switch does not support wavelength settings
    do nothing and still send DONE to stdout.
    Args
        oswitch: visa.ResourceManager object of switch
        grp_and_wavelength: str (comma delimited)
    stdout
        DONE: str
    Returns
        None
    """
    # Does nothing.
    print(DONE)


# Main loop

com_port = get_command_line_argument()
switch = connect_to_switch(com_port)
switch.write('*RST')
if switch:
    while True:
        # Loop until FlexOTO sends 'exit'.
        fromFlexOTO = input()  # stdin from FlexOTO
        if 'get_description' in fromFlexOTO:
            get_description(switch)
        elif 'set_routes' in fromFlexOTO:
            set_routes(switch, fromFlexOTO.replace('set_routes ', ''))
        elif 'set_wavelength' in fromFlexOTO:
            set_wavelength(switch, fromFlexOTO.replace('set_wavelength ', ''))
        elif 'exit' in fromFlexOTO:
            break