Skip to content
This repository has been archived by the owner on Apr 1, 2024. It is now read-only.

Commit

Permalink
Merge pull request #37 from oresat/3.2.0-ui_rework
Browse files Browse the repository at this point in the history
3.2.0 UI rework
  • Loading branch information
dmitri-mcguckin authored Jan 5, 2021
2 parents e60d9c5 + ce235fb commit 7b15d38
Show file tree
Hide file tree
Showing 8 changed files with 467 additions and 254 deletions.
25 changes: 19 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ A utility for displaying and tracking activity over the CAN bus.

`$` `pip install -e .[dev]`

*(The `-e` flag creates a live-link to your local development version, so there's no need to uninstall and reinstall every time. Set it and forget it.)*
*(The `-e` flag creates a symbolic-link to your local development version, so there's no need to uninstall and reinstall every time. Set it and forget it.)*

## Create or Update Manifest

Expand Down Expand Up @@ -74,27 +74,40 @@ A set of devices configs including a list of CAN Buses that CAN Monitor will try
"data": [
{
"capacity": null,
"fields": [],
"fields": {
"COB ID": "arb_id",
"Node Name": "node_name",
"Interface": "interface",
"State": "status",
"Status": "parsed_msg"
},
"frame_types": [
"HEARTBEAT"
],
"name": "Hearbeats",
"type": "heartbeat_table"
"type": "message_table"
},
{
"capacity": null,
"fields": [],
"frame_types": [],
"name": "Info",
"type": "info_table"
"type": "message_table"
}
],
"split": "vertical",
"type": "grid"
},
{
"capacity": null,
"fields": [],
"fields": {
"COB ID": "arb_id",
"Node Name": "node_name",
"Interface": "interface",
"Type": "message_type",
"Time Stamp": "timestamp",
"Message": "parsed_msg"
},
"frame_types": [
"NMT",
"SYNC",
Expand All @@ -113,7 +126,7 @@ A set of devices configs including a list of CAN Buses that CAN Monitor will try
"UKNOWN"
],
"name": "Misc",
"type": "misc_table"
"type": "message_table"
}
],
"split": "horizontal",
Expand Down
5 changes: 3 additions & 2 deletions canopen_monitor/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import os

MAJOR = 3
MINOR = 1
PATCH = 4
MINOR = 2
PATCH = 0

APP_NAME = 'canopen-monitor'
APP_DESCRIPTION \
Expand All @@ -13,6 +13,7 @@
APP_URL = "https://github.com/oresat/CANopen-monitor"
APP_LICENSE = 'GPL-3.0'

CONFIG_FORMAT_VERSION = 1
CONFIG_DIR = os.path.expanduser('~/.config/{}'.format(APP_NAME)) + os.sep
CACHE_DIR = os.path.expanduser('~/.cache/{}'.format(APP_NAME)) + os.sep
ASSETS_DIR \
Expand Down
4 changes: 3 additions & 1 deletion canopen_monitor/canmsgs/canmsg.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ def __init__(self,
node_name = MessageType.cob_id_to_node_id(src.arb_id)
self.node_name = hex(node_name) \
if node_name is not None else hex(src.arb_id)
self.parsed_msg = ""

def __str__(self):
"""
Expand All @@ -138,7 +139,7 @@ def __str__(self):
attrs += ['{}={}'.format(k, v)]
return "<CANMsg {} {} {}>".format(self.message_type,
self.arb_id,
self.status())
self.status)

def __le__(self, operand) -> bool:
"""
Expand All @@ -153,6 +154,7 @@ def __le__(self, operand) -> bool:
"""
return self.arb_id <= operand.arb_id

@property
def status(self) -> str:
"""
Returns
Expand Down
104 changes: 85 additions & 19 deletions canopen_monitor/canmsgs/canmsg_table.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,99 @@
from __future__ import annotations
from .canmsg import CANMsg
import typing
from collections.abc import Iterable, Iterator


class CANMsgTable:
def __init__(self,
name: str = "message_table",
capacity: int = None):
self.name = name
self.message_table = {}
class CANMsgTable(Iterable):
"""
Table of CAN Messages
"""

def __init__(self: CANMsgTable, capacity: int = None):
self.__message_table = {}
self.capacity = capacity

def add(self, frame: CANMsg) -> None:
if(self.capacity is not None):
if(len(self.message_table) < self.capacity
or (self.message_table.get(frame.arb_id) is not None)):
self.message_table[frame.arb_id] = frame
def __sort__(self: CANMsgTable) -> [int]:
"""
Overloaded sort function
Sort by COB-ID
Returns
--------
[int]: List of keys sorted
"""
return sorted(self.__message_table.keys())

def __add__(self: CANMsgTable, frame: CANMsg) -> CANMsgTable:
"""
Overloaded add operator
allows for following:
CANMsgTable += CANMsg
Arguments
----------
frame: CANMsg to be added
Returns
--------
CANMsgTable: returns self after adding message
"""
if self.capacity is not None:
if (len(self.__message_table) < self.capacity
or (self.__message_table.get(frame.arb_id) is not None)):
self.__message_table[frame.arb_id] = frame
else:
self.message_table[frame.arb_id] = frame
self.__message_table[frame.arb_id] = frame

return self

def ids(self) -> [int]:
return sorted(self.message_table.keys())
def __len__(self: CANMsgTable) -> int:
"""
Overloaded len function
def __len__(self) -> int:
return len(self.message_table)
Returns
--------
int: Number of CANMsg records in table
"""
return len(self.__message_table)

def __str__(self) -> str:
def __str__(self: CANMsgTable) -> str:
"""
Overloaded str function
Returns
--------
str: String representation of CANMsgTable
"""
attrs = []
for k, v in self.__dict__.items():
attrs += ['{}={}'.format(k, v)]
return 'CANMsgTable {}\n\n'.format(', '.join(attrs))

def __getitem__(self, key: int) -> CANMsg:
return self.message_table.get(key)
def __getitem__(self: CANMsgTable, key: typing.Union[int, str]) -> \
typing.Union[CANMsg, None]:
"""
Overloaded getitem operator
Example: CANMsgTable[0x40]
Arguments
----------
key: int or string representation of node COB-ID
Returns
--------
CANMsg: last message added for the provided COB-ID
None: None will be returned if no messages exist for provided COB-ID
"""
sub_key = int(key, 16) if type(key) is str else key
return self.__message_table.get(sub_key)

def __iter__(self: CANMsgTable) -> Iterator[CANMsg]:
"""
Overloaded iter function
Returns
--------
Iterator[CANMsg]: iterator for contained messages
"""
return self.__message_table.__iter__()
14 changes: 9 additions & 5 deletions canopen_monitor/canmsgs/magic_can_bus.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,19 @@ class MagicCANBus:
Parameters
----------
interfaces `[pyvit.bus.Bus]`: A list of Bus objects that the Magic CAN Bus
will monitor.
will monitor.
frames `queue.Queue`: The thread-safe queue of CANMsg objects to pull from.
failed_interfaces `[str]`: A list of interface names that the Magic CAN Bus
failed to connect to.
failed to connect to.
stop_listening `threading.Event`: A thread-safe event that triggers when
it's time to shut down all of the bus
listeners.
it's time to shut down all of the bus listeners.
block `bool`: A flag for determining whether or not the Magic CAN Bus
should block when checking for CAN messages.
should block when checking for CAN messages.
threads `[threading.Thread]`: A list of bus-listener worker threads.
"""

Expand Down
29 changes: 11 additions & 18 deletions canopen_monitor/monitor_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import curses
import threading
import canopen_monitor
from .ui.pane import HeartBeatPane, InfoPane, Pane
from .ui.pane import CANMsgPane
from .ui.windows import PopupWindow
from .ui.grid import Grid, Split
from .parser.canopen import CANOpenParser
Expand All @@ -12,9 +12,6 @@
class MonitorApp:
"""The top-level application of Can Monitor that manages the middleware
resoruces and the UI elements.
Attributes
---------
"""

def __init__(self,
Expand Down Expand Up @@ -159,6 +156,10 @@ def read_input(self):
self.selected.scroll_up(rate=10)
elif(key == 526 or key == 561): # Ctrl+Down or Ctrl+Right
self.selected.scroll_down(rate=10)
elif(key == curses.KEY_LEFT):
self.selected.scroll_left()
elif(key == curses.KEY_RIGHT):
self.selected.scroll_right()

def draw_banner(self):
_, width = self.screen.getmaxyx()
Expand Down Expand Up @@ -210,20 +211,12 @@ def construct_grid(self, schema, parent=None):

for entry in data:
self.construct_grid(entry, component)
elif(type == 'heartbeat_table'):
component = HeartBeatPane(name,
self.parser,
capacity=capacity,
fields=fields,
frame_types=frame_types)
elif(type == 'misc_table'):
component = Pane(name,
self.parser,
capacity=capacity,
fields=fields,
frame_types=frame_types)
elif(type == 'info_table'):
component = InfoPane(name, self.parser)
elif(type == 'message_table'):
component = CANMsgPane(name,
self.parser,
capacity=capacity,
fields=fields,
frame_types=frame_types)
else:
raise ValueError('Failed to parse layout! Invalid table type: {}'
.format(type))
Expand Down
5 changes: 4 additions & 1 deletion canopen_monitor/parser/sdo.py
Original file line number Diff line number Diff line change
Expand Up @@ -1105,7 +1105,10 @@ def __parse_block_no_data(self, data):
def __parse_block_end_data(self, data):
# download_segment = SDOBlockEndData(data) # Unused

return self.__inProgressName + " 100%"
if self.__inProgressName is not None:
return self.__inProgressName + " 100%"
else:
return "100%"

def __parse_block_end_no_data(self, data):
download_segment = SDOBlockEndNoData(data)
Expand Down
Loading

0 comments on commit 7b15d38

Please sign in to comment.