示例#1
0
def do_send_hex(data: str):
    """
    Send raw hex to serial device, ignoring spaces \\x and 0x.

    :PARAM: data: Raw hex to send.
    """
    assert (
        ctserial.session
    ), "There is not an open session.  Connect to one first."  # ToDo assert session type
    raw_hex = data.lower().replace("0x", "").replace("\\x",
                                                     "").replace(" ", "")
    assert re.match("^[0123456789abcdef]+$",
                    raw_hex), "Only hex characters allowed"
    # assert len(raw_hex) % 2 != 0, "You must send an even number of hex characters"
    tx_bytes = bytes.fromhex(raw_hex)
    ctserial.views["transactions"].append([
        "-->",
        common.bytes2hexstr(tx_bytes, group=8, sep=" ", line=35),
        common.bytes_decode(tx_bytes),
    ])
    rx_bytes = common.send_instruction(ctserial.session, tx_bytes)
    if rx_bytes:
        ctserial.views["transactions"].append([
            "<--",
            common.bytes2hexstr(rx_bytes, group=8, sep=" ", line=35),
            common.bytes_decode(rx_bytes),
        ])
    else:
        message_dialog("ERROR", "No response received")
    return tabulate(ctserial.views["transactions"],
                    tablefmt="plain",
                    headers="firstrow")
示例#2
0
 def do_connect(self, args, output_text):
     """Generate a session with a single serial device to interact with it."""
     parts = args.split()
     devices = [x.device for x in serial.tools.list_ports.comports()]
     if len(parts) > 0:
         device = parts[0]
         if len(parts) > 1:
             baudrate = parts[1]
         else:
             baudrate = 9600
         if device in devices:
             self.session = serial.Serial(port=device,
                                          baudrate=baudrate,
                                          parity=serial.PARITY_NONE,
                                          stopbits=serial.STOPBITS_ONE,
                                          bytesize=serial.EIGHTBITS)
             # initiate a serial session and return success message
             self.session.isOpen()
             output_text += 'Connect session opened with {}\n'.format(
                 device)
             return output_text
     # return list of devices if command incomplete or incorrect
     message = 'Valid devices:\n' + ', \n'.join(devices) + '\n'
     message_dialog(title='Invalid Device', text=message)
     return False
示例#3
0
def do_connect(device: str = "/dev/null/",
               baudrate: int = 9600,
               parity: str = "none"):
    """
    Connect to a serial device

    :PARAM: device: Serial device path or COM port
    :PARAM: baudrate: Defaults to 9600. Most standard rates from 75 to 4000000 supported.
    :PARAM: parity: Defaults to none. Can also be even, odd, mark, or space
    """
    assert (
        ctserial.session == None
    ), "Session already open.  Close first."  # ToDo assert session type
    valid_device = common.validate_serial_device(device)
    parities = {
        "none": PARITY_NONE,
        "even": PARITY_EVEN,
        "odd": PARITY_ODD,
        "mark": PARITY_MARK,
        "space": PARITY_SPACE,
    }
    assert (parity in parities.keys()
            ), "Parity must be none (default), even, odd, mark, or space"
    s = Serial(
        port=valid_device,
        baudrate=baudrate,
        parity=parities[parity],
        stopbits=STOPBITS_ONE,
        bytesize=EIGHTBITS,
    )
    assert s.isOpen(), f"Could not connect to {valid_device}"
    ctserial.session = s
    message_dialog("SUCCESS", f"ASCII session OPENED with {valid_device}")
    return
示例#4
0
 def do_write_coils(self, args, output_text):
     """Write to registers in format: <address> <int>..."""
     desc = 'Modbus Function 3, Read Holding Registers'
     start, values = args.split(maxsplit=1)
     assert (start.isdigit()), 'start must be an integer'
     int_start = int(start)
     try:
         list_bool_values = [bool(int(x)) for x in values.replace(' ', '')]
     except:
         raise AssertionError('List of values to write must be decimals')
     if len(list_bool_values) == 1:
         self.session.write_coil(int_start,
                                 list_bool_values[0],
                                 unit=self.unit_id)
         desc = 'Modbus Function 5, Write Single Coil'
     else:
         self.session.write_coils(int_start,
                                  list_bool_values,
                                  unit=self.unit_id)
         desc = 'Modbus Function 15, Write Multiple Coils'
     message_dialog(
         title='Success',
         text=f'Wrote {list_bool_values} starting at {int_start}')
     results = {}
     stop = int_start + len(list_bool_values)
     for i in range(len(list_bool_values)):
         results[int_start + i] = int(list_bool_values[i])
     output_text += self.log_and_output_bits(desc, int_start, stop, results)
     return output_text
示例#5
0
def do_debug(cmd: str):
    """
    Run a python command for debugging

    :PARAM: cmd: Python command to run in debug session
    """
    message_dialog(title="Debug", text=str(eval(cmd)))
示例#6
0
def summarize_word_responses(message, results):
    """
    Summarize word reseponses in message dialog

    :PARAM:
    """
    la, lr, fa = None, None, None  # last_addres, last_result, first_address
    table = [["Addr", "Int", "HEX", "UTF-8"]]
    for address, result in results.items():
        if address - 1 == la and result == lr:
            if fa == None:
                fa = la
        elif la != None:
            if fa == None:
                table.append([la, lr, "{:04x}".format(lr), str(chr(lr))])
                fa = None
            else:
                s = "{0}-{1}".format(fa, la)
                table.append([s, lr, "{:04x}".format(lr), str(chr(lr))])
                fa = None
        la, lr = address, result
    # Print final output from for loop
    if fa == None:
        table.append([la, lr, "{:04x}".format(lr), str(chr(lr))])
    else:
        s = "{0}-{1}".format(fa, la)
        table.append([s, lr, "{:04x}".format(lr), str(chr(lr))])
    message += tabulate(table, headers="firstrow", tablefmt="simple")
    message_dialog(title="Success", text=message)
示例#7
0
def do_send_utf8(data: str):
    """
    Send UTF-8 string to serial device.

    :PARAM: data: UTF-8 string to send.
    """
    assert (
        ctserial.session
    ), "There is not an open session.  Connect to one first."  # ToDo assert session type
    utf8_str = "".join(
        shlex.split(data))  # remove spaces not in quotes and format
    tx_bytes = bytes(utf8_str, encoding="utf-8")
    ctserial.views["transactions"].append([
        "-->",
        common.bytes2hexstr(tx_bytes, group=8, sep=" ", line=35),
        common.bytes_decode(tx_bytes),
    ])
    rx_bytes = common.send_instruction(ctserial.session, tx_bytes)
    if rx_bytes:
        ctserial.views["transactions"].append([
            "<--",
            common.bytes2hexstr(rx_bytes, group=8, sep=" ", line=35),
            common.bytes_decode(rx_bytes),
        ])
    else:
        message_dialog("ERROR", "No response received")
    return tabulate(ctserial.views["transactions"],
                    tablefmt="plain",
                    headers="firstrow")
示例#8
0
 def do_connect_udp(self, args, output_text):
     """Connect to a Modbus UDP device"""
     assert (self.session == None), 'Session already open.  Close first.'  # ToDo assert session type
     host, port = self.parse_ip_port(args)
     self.session = ModbusUdpClient(host, port)
     message_dialog(
         title = 'Success',
         text = 'Session opened with {}:{}'.format(host, port) )
示例#9
0
 def do_connect_rtu(self, args, output_text):
     """Connect to a Modbus RTU serial device"""
     assert (self.session == None), 'Session already open.  Close first.'  # ToDo assert session type
     device = self.validate_serial_device(args)
     self.session = ModbusSerialClient(method='rtu', port=device, timeout=1)
     message_dialog(
         title = 'Success',
         text = 'Session opened with {}'.format(device) )
示例#10
0
 def do_close(self, args, output_text):
     """Close a session."""
     assert (self.session), 'There is not an open session.  Connect to one first'  # ToDo assert session type
     self.session.close()
     message_dialog(
         title = 'Success',
         text = 'Session closed with {}'.format(self.session) )
     self.session = None
     return None
示例#11
0
 def do_project():
     """Information about the current project"""
     message = f'  Project Name:  {ctui.project_name}\n'
     message += f'  Project Path:  {ctui._project_path}\n'
     message += f'     File Size:  {Path(ctui._project_path).stat().st_size} KB\n'
     message += f' History Count:  {len(ctui.history)} records\n'
     message += f'Settings Count:  {len(ctui.settings)} records\n'
     message += f' Storage Count:  {len(ctui.storage)} records'
     message_dialog(title='Project Information', text=message)
示例#12
0
def do_connect():
    """Connect to modbus device/service or list suggestions"""
    output_text = "Connected Serial Devices\n"
    output_text += common.list_serial_devices()
    output_text += "\n\n\n"
    output_text += "Listening Services on Localhost\n"
    output_text += common.list_listening_ports()
    output_text += "                                                            \n"
    message_dialog(title="Suggestions", text=output_text)
示例#13
0
 def do_read_id(self, args, output_text):
     """Read device identification data."""
     assert (
         self.session
     ), 'There is not an open session.  Connect to one first'  # ToDo assert session type
     request = ReadDeviceInformationRequest(unit=1)
     response = self.session.execute(request)
     message_dialog(title="Response", text=str(reponse))
     return None
示例#14
0
    def do_history(count: int = 0):
        """
        Print history of commands entered

        :PARAM count: Optional number of last histories to print
        """
        message = tabulate(ctui.history.all()[-count:],
                           headers='keys',
                           tablefmt='simple')
        message_dialog(title='History', text=message)
示例#15
0
 def do_project_list():
     """List saved projects"""
     lines = []
     for file in list(Path(ctui._project_folder).glob(f'*.{ctui.name}')):
         lines.append({
             'Project': file.stem,
             'Size (KB)': file.stat().st_size
         })
     message = tabulate(lines, headers='keys', tablefmt='simple')
     message_dialog(title='Saved Projects', text=message, scrollbar=True)
示例#16
0
    def do_history_search(query: str):
        """
        Search history of commands ...

        :PARAM query: Regex string to search in history
        """
        History = Query()
        search_results = ctui.history.search(
            History.Command.matches('.*' + query))
        message = tabulate(search_results, headers='keys', tablefmt='simple')
        message_dialog(title='History Search Results', text=message)
示例#17
0
def do_close():
    """
    Close the open session
    """
    assert (
        ctserial.session
    ), "There is not an open session.  Connect to one first."  # ToDo assert session type
    dev = ctserial.session.port
    ctserial.session.close()
    ctserial.session = None
    message_dialog("SUCCESS", f"Session with {dev} CLOSED")
    return
示例#18
0
def send_instruction(session, tx_bytes):
    """Send data to serial device"""
    # clear out any leftover data
    try:
        rx_raw = bytes()
        if session.inWaiting() > 0:
            session.flushInput()
        session.write(tx_bytes)
        time.sleep(0.1)
        while session.inWaiting() > 0:
            rx_raw += session.read()
    except BaseException as e:
        message_dialog("ERROR", "\n\n{}".format(e))
    time.sleep(0.1)
    return rx_raw
示例#19
0
    def do_project_load(name: str):
        """
        Load saved project ...

        :PARAM name: Name of project to load
        """
        project_to_load_path = f'{ctui._project_folder}{name}.{ctui.name}'
        assert (Path(project_to_load_path).is_file()
                ), f'"{name}" is not a valid project'

        ctui.db.close()
        ctui.project_name = name
        ctui._init_db()

        message_dialog(title='Success', text=f'{name} project loaded.')
示例#20
0
    def do_project_export(filename: str):
        """
        Export the current project to ...

        :PARAM filename: Filename of file to export current project
        """
        export_file = Path(f'{filename}.{ctui.name}').expanduser()

        def project_export():
            export_file.write_bytes(Path(ctui._project_path).read_bytes())

        if export_file.is_file():
            yes_no_dialog(title='File Already Exists',
                          text='Overwrite file?',
                          yes_func=project_export)
        else:
            project_export()
            message_dialog(title='Success',
                           text=f'Project exported as:\n"{export_file}"')
示例#21
0
    def do_project_saveas(name: str):
        """
        Save current project as ...

        :PARAM name: New name of project you are saving
        """
        project_to_save_path = f'{ctui._project_folder}{name}.{ctui.name}'

        def project_saveas():
            ctui.db.close()
            old_project = Path(ctui._project_path)
            ctui.project_name = name
            Path(ctui._project_path).write_bytes(old_project.read_bytes())
            ctui._init_db()

        if Path(project_to_save_path).is_file():
            yes_no_dialog(title='Warning',
                          text=f'Project "{name}" already exists!  Overwrite?',
                          yes_func=project_saveas)
        else:
            project_saveas()
            message_dialog(text=f'Project saved as "{name}".')
示例#22
0
def do_write_coil(address: int, values: GreedyBin):
    """
    Write single coil in format: <address> <0 or 1>

    :PARAM: address: Modbus address to start writes
    :PARAM: values: Space separated list of True or False to write
    """
    assert ctmodbus.session, "There is not an open session.  Connect to one first."
    if len(values) == 1:
        ctmodbus.session.write_coil(address, values[0], unit=unit_id)
        desc = "Modbus Function 5, Write Single Coil"
    else:
        # This is currently not working, as GreedyBin doesn't pass on a list
        ctmodbus.session.write_coils(address, values, unit=unit_id)
        desc = "Modbus Function 15, Write Multiple Coils"
    message_dialog(title="Success",
                   text=f"Wrote {values} starting at {address}")
    results = {}
    stop = address + len(values)
    for i in range(len(values)):
        results[address + i] = int(values[i])
    output_text = ctmodbus.output_text
    output_text += common.log_and_output_bits(desc, address, stop, results)
    return output_text
示例#23
0
def do_write_register(address: int, values: GreedyInt):
    """
    Write single register in format: <address> <int>

    :PARAM: address: Modbus address to start writes
    :PARAM: values: Space separated integers to write
    """
    assert ctmodbus.session, "There is not an open session.  Connect to one first."
    if len(values) == 1:
        ctmodbus.session.write_register(address, values[0], unit=unit_id)
        desc = "Modbus Function 6, Write Single Register"
    else:
        # This is currently not working, as GreedyInt doesn't pass on a list
        ctmodbus.session.write_registers(address, values, unit=unit_id)
        desc = "Modbus Function 16, Write Multiple Registers"
    message_dialog(title="Success",
                   text=f"Wrote {values} starting at {address}")
    results = {}
    stop = address + len(values)
    for i in range(len(values)):
        results[address + i] = values[i]
    output_text = ctmodbus.output_text
    output_text += common.log_and_output_words(desc, address, stop, results)
    return output_text
示例#24
0
    def do_project_import(filename: str):
        """
        Import exported project from ...

        :PARAM filename: Exported filename to import
        """
        project_to_import_path = Path(filename).expanduser()
        if project_to_import_path.is_file() == False:
            project_to_import_path = Path(
                str(project_to_import_path) + f'.{ctui.name}')
        assert (project_to_import_path.is_file()), 'File does not exist'
        with open(project_to_import_path) as f:
            assert (f.read(14) == '{"_default": {'
                    ), 'Invald or corrupted project file'

        # build new project name and path
        if project_to_import_path.suffix[1:] == ctui.name:
            ctui.project_name = project_to_import_path.stem
        else:
            ctui.project_name = project_to_import_path.name

        def project_import():
            ctui.db.close()
            Path(ctui._project_path).write_bytes(
                project_to_import_path.read_bytes())
            ctui._init_db()

        if Path(ctui._project_path).is_file():
            yes_no_dialog(title='WARNING',
                          text='Project already exists.\nOverwrite project?',
                          yes_func=project_import)
            # TODO: use input_dialog to request new project name
        else:
            project_import()
            message_dialog(title='Success',
                           text=f'Project imported as "{ctui.project_name}"')
示例#25
0
 def response_message_dialog(self, message, results):
     la, lr, fa = None, None, None  #last_addres, last_result, first_address
     table = [['Addr', 'Int', 'HEX', 'ASCII']]
     for address, result in results.items():
         if address - 1 == la and result == lr:
             if fa == None:
                 fa = la
         elif la != None:
             if fa == None:
                 table.append([la, lr, '{:04x}'.format(lr), str(chr(lr))])
                 fa = None
             else:
                 s = '{0}-{1}'.format(fa, la)
                 table.append([s, lr, '{:04x}'.format(lr), str(chr(lr))])
                 fa = None
         la, lr = address, result
     # Print final output from for loop
     if fa == None:
         table.append([la, lr, '{:04x}'.format(lr), str(chr(lr))])
     else:
         s = '{0}-{1}'.format(fa, la)
         table.append([s, lr, '{:04x}'.format(lr), str(chr(lr))])
     message += tabulate(table, headers='firstrow', tablefmt='simple')
     message_dialog(title='Success', text=message)
示例#26
0
 def do_debug(self, args, output_text):
     message_dialog(title='Debug', text=str(eval(args)))