Skip to content

Commit

Permalink
V1.3.0 (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
matesh authored Dec 27, 2024
2 parents 8c8ef00 + dcab16b commit 68ba611
Show file tree
Hide file tree
Showing 14 changed files with 516 additions and 63 deletions.
2 changes: 1 addition & 1 deletion modterm/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.2.0"
__version__ = "1.3.0"
76 changes: 70 additions & 6 deletions modterm/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@
"""

import curses
import os.path
import sys
import time
from os import environ, path
import logging
from logging.handlers import RotatingFileHandler
Expand All @@ -32,9 +30,11 @@
from modterm.components.read_registers_menu import ReadRegistersMenu
from modterm.components.write_registers_menu import WriteRegistersMenu
from modterm.components.unit_sweep_menu import UnitSweepMenu
from modterm.components.ip_sweep_menu import IpSweepMenu
from modterm.components.popup_message import show_popup_message
from modterm.components.export_menu import ExportMenu
from modterm.components.analyse_window import AnalyseWindow
from modterm.components.modbus_handler import HistoryItem

logger = logging.getLogger("ModTerm")
logger.setLevel('INFO')
Expand All @@ -61,7 +61,18 @@
"r - Read registers",
"w - Write registers",
"s - Sweep modbus units with register reads",
"e - Export register data"
"e - Export register data",
"i - IP address sweep",
"h - Result history"
"",
"Column titles",
"Idx - Index in the list Addr - Address",
"HAddr - Address in hexadecimal HexV - Value in hexadecimal",
"U16 - Unsigned 16 bit integer I16 - Signed 16 bit integer",
"HexV - Value in hexadecimal U16 - Unsigned 16 bit integer",
"I16 - Signe 16 bit integer U32 - Unsigned 32 bit integer",
"I32 - Signed 32 bit integer F32 - 32 bit floating point",
"St - string representation Bits - the register bits"
# "i - Sweep IP addresses with register reads",
]

Expand All @@ -73,6 +84,7 @@ def app(screen):
normal_text = curses.A_NORMAL
curses.curs_set(0)
screen.refresh()
screen_size = screen.getmaxyx()
menu = HeaderMenu(screen, normal_text, highlighted_text)
data_window = ScrollableList(screen.getmaxyx()[0] - 5,
screen.getmaxyx()[1],
Expand All @@ -85,13 +97,36 @@ def app(screen):
x = screen.getch()
modbus_handler = None
logger.info("ModTerm started up")
history: dict[str, HistoryItem] = {}
while x != curses.KEY_F10:
if (x == curses.KEY_RESIZE and curses.is_term_resized(screen_size[0], screen_size[1])) or \
curses.is_term_resized(screen_size[0], screen_size[1]):
curses.resizeterm(screen.getmaxyx()[0],
screen.getmaxyx()[1])
screen_size = screen.getmaxyx()
new_header_menu = HeaderMenu(screen, normal_text, highlighted_text)
new_header_menu.configuration = menu.configuration
menu = new_header_menu
new_data_window = ScrollableList(screen.getmaxyx()[0] - 5,
screen.getmaxyx()[1],
5,
0,
normal_text,
highlighted_text)
new_data_window.data_rows = data_window.data_rows
new_data_window.title = data_window.title
new_data_window.added_border = data_window.added_border
new_data_window.header = data_window.header
new_data_window.empty_list_message = data_window.empty_list_message
data_window = new_data_window

data_window.check_navigate(x)
if menu.check_navigate(x):
if modbus_handler is not None and modbus_handler is not None:
if modbus_handler is not None:

table_data = modbus_handler.process_words(modbus_config=menu.configuration,
read_config=load_read_config())
table_data.title = data_window.title
data_window.draw(table_data)

if x == curses.KEY_F1:
Expand All @@ -103,6 +138,9 @@ def app(screen):
if table_data is not None:
data_window.draw(table_data)
modbus_handler = read_registers_menu.modbus_handler
history = {**{table_data.title: HistoryItem(table_content=table_data,
modbus_handler=modbus_handler)},
**history}
save_modbus_config(menu.configuration)
if x == ord("w"):
write_registers_menu = WriteRegistersMenu(screen, normal_text, highlighted_text, menu.configuration)
Expand All @@ -115,7 +153,15 @@ def app(screen):
table_data = unit_sweep_menu.get_result()
if table_data is not None:
data_window.draw(table_data)
modbus_handler = unit_sweep_menu.modbus_handler
modbus_handler = None
save_modbus_config(menu.configuration)
if x == ord("i"):
ip_sweep_menu = IpSweepMenu(screen, normal_text, highlighted_text, menu.configuration)
if ip_sweep_menu.is_valid:
table_data = ip_sweep_menu.get_result()
if table_data is not None:
data_window.draw(table_data)
modbus_handler = None
save_modbus_config(menu.configuration)
if x == ord("e"):
if data_window.header is None or len(data_window.data_rows) == 0:
Expand All @@ -125,9 +171,25 @@ def app(screen):
export_menu = ExportMenu(screen, normal_text, highlighted_text, data_window.header, data_window.data_rows)
if export_menu.is_valid:
export_menu.get_result()
if x == ord("h"):
if len(history.keys()) != 0:
selection_window = SelectWindow(screen,
screen.getmaxyx()[0] - 20,
screen.getmaxyx()[1] - 30,
12,
10,
normal_text,
highlighted_text,
list(history.keys()),
title="Select previous result",
added_border=True)
selection = selection_window.get_selection()
if selection is not None:
data_window.draw(history[selection].table_content)
modbus_handler = history[selection].modbus_handler
if x == ord('\n'):
if len(data_window.data_rows) != 0:
if data_window.bar_position is not None:
if data_window.bar_position is not None and 2 < len(data_window.get_current_row_data()):
row_position = data_window.bar_position + 6
command_list = ["Close", "Write register", "Analyse"]
if row_position > screen.getmaxyx()[0] - len(command_list) - 3:
Expand Down Expand Up @@ -176,6 +238,8 @@ def main():
except Exception as e:
logger.critical("Critical error in main", exc_info=True)
rc = 1
except KeyboardInterrupt:
pass
finally:
if 'stdscr' in locals():
stdscr.keypad(False)
Expand Down
23 changes: 19 additions & 4 deletions modterm/components/config_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from pathlib import Path
from json import loads, dumps
from modterm.components.definitions import CONFIG_DIR, ConfigType, ModbusConfig, ReadConfig, WriteConfig, \
UnitSweepConfig, ExportConfig
UnitSweepConfig, ExportConfig, IpSweepConfig


class ConfigOperation(Enum):
Expand Down Expand Up @@ -61,16 +61,19 @@ def config_file_manager(action: ConfigOperation,
Type[ReadConfig],
Type[WriteConfig],
Type[UnitSweepConfig],
Type[ExportConfig]]] = None,
Type[ExportConfig],
Type[IpSweepConfig]]] = None,
config_to_save: Optional[Union[ModbusConfig,
ReadConfig,
WriteConfig,
UnitSweepConfig,
ExportConfig]] = None) -> Optional[Union[ModbusConfig,
ExportConfig,
IpSweepConfig]] = None) -> Optional[Union[ModbusConfig,
ReadConfig,
WriteConfig,
UnitSweepConfig,
ExportConfig]]:
ExportConfig,
IpSweepConfig]]:

if (config_dir := get_project_dir()) is None:
# TODO log error
Expand Down Expand Up @@ -146,6 +149,18 @@ def save_unit_sweep_config(config: UnitSweepConfig):
config_to_save=config)


def load_ip_sweep_config() -> IpSweepConfig:
return config_file_manager(action=ConfigOperation.LOAD,
config_type=ConfigType.IpSweepConfig,
config_class=IpSweepConfig)


def save_ip_sweep_config(config: IpSweepConfig):
return config_file_manager(action=ConfigOperation.SAVE,
config_type=ConfigType.IpSweepConfig,
config_to_save=config)


def save_export_config(config: ExportConfig):
return config_file_manager(action=ConfigOperation.SAVE,
config_type=ConfigType.ExportConfig,
Expand Down
22 changes: 22 additions & 0 deletions modterm/components/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class ConfigType(Enum):
WriteConfig = "write_config.conf"
UnitSweepConfig = "scan_config.conf"
ExportConfig = "export.conf"
IpSweepConfig = "ip_sweep_config.conf"


@dataclass
Expand Down Expand Up @@ -141,7 +142,28 @@ def from_dict(cls, config_dict):
})


@dataclass
class IpSweepConfig:
subnet: str = "192.168.0"
start_address: int = 1
end_address: int = 255
port: int = 502
command: str = HOLDING
start_register: int = 0
number_of_registers: int = 1
unit_id: int = 1
timeout: float = 0.2

@classmethod
def from_dict(cls, config_dict):
return cls(**{
k: v for k, v in config_dict.items()
if k in inspect.signature(cls).parameters
})


@dataclass
class TableContents:
header: Optional[List[str]]
rows: List[List[str]]
title: str = ""
16 changes: 2 additions & 14 deletions modterm/components/header_menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from modterm.components.scrollable_list import SelectWindow
from modterm.components.serial_interface_scan import get_serial_interfaces
from modterm.components.definitions import BigEndian, LittleEndian, TCP, RTU
from modterm.components.hepers import get_text_input, CancelInput
from modterm.components.hepers import get_text_input, CancelInput, validate_ip
from modterm.components.config_handler import load_modbus_config
from modterm import __version__
from socket import gethostbyname
Expand All @@ -31,21 +31,9 @@
230400, 460800, 500000, 921600]


def validate_ip(s: str) -> bool:
a = s.split('.')
if len(a) != 4:
return False
for x in a:
if not x.isdigit():
return False
i = int(x)
if i < 0 or i > 255:
return False
return True


class HeaderMenu:
def __init__(self, screen, normal_text: curses.color_pair, highlighted_text: curses.color_pair):
self.screen = screen
self.window = curses.newwin(5, screen.getmaxyx()[1], 0, 0)
self.normal_text = normal_text
self.highlighted_text = highlighted_text
Expand Down
2 changes: 1 addition & 1 deletion modterm/components/help.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@


def display_help(screen, help_text_rows):
help_window = WindowBase(screen, len(help_text_rows) + 5, 80, title=f"Help - {help_text_rows[0]}")
help_window = WindowBase(screen, len(help_text_rows) + 5, 80, title=f"Help - {help_text_rows[0]}", added_border=True)
help_window.draw_window()
idx = 0
for idx, row in enumerate(help_text_rows[1:], start=2):
Expand Down
19 changes: 18 additions & 1 deletion modterm/components/hepers.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,29 @@ def get_text_input(window, width, y, x, default):
tb = Textbox(number_entry, insert_mode=True)
curses.curs_set(1)
window.refresh()
tb.edit(validate_text_edit_keys)
try:
tb.edit(validate_text_edit_keys)
except CancelInput:
curses.curs_set(0)
raise
content = tb.gather().strip()
curses.curs_set(0)
return content


def validate_ip(s: str) -> bool:
a = s.split('.')
if len(a) != 4:
return False
for x in a:
if not x.isdigit():
return False
i = int(x)
if i < 0 or i > 255:
return False
return True


def wrap_text(text, width):
return textwrap.fill(text, width)

Expand Down
Loading

0 comments on commit 68ba611

Please sign in to comment.