Skip to content

Commit 1f8a308

Browse files
HugoCLSCRoberto
andauthored
USB tools (#193)
* Udisks2 dbus interfaces for File system and dev Block * Add Udisks2 Drive interface * Finalize UDisks2 Interfaces * Finished blocking udisks2 dbus interfaces and manager * Add initial async UDisks2 Dbus interfaces and manager * Added parallel monitoring for added/removed interfaces for Udisks2 signals, new properties on interfaces * WIP (switching laptops) * WIP Udev monitoring * WIP: Add UDisks2 Partition Interface, properties changed signal handling The properties of a device although uncommon may change, so a handler for the signal UDisks2 manager `properties_changed` was added. The logic is still missing here. The initial representation of a Device is added but not finished * Created a module for this usb storage devices Since code can be added in the future to increase the functionality of the usb storage devices communication, the different parts of the feature were seperated into different files in order to create a module that can later be called to handle all usb storage devices. This will make code additions and refactors cleaner in the future. The commit seperates the previous `usb_manager.py` into six different files: seperate wrappers for the sdbus UDisks2 service (blocking and async), usb storage asynchronous manager, and a WIP udev monitor that may be deleted in the future. This is still a work in progress so don't pay to much attention to what is done * Moved to module * WIP * Seperated Device definition * Rplaced string literals with Enum members, added new property to interface * WIP All phases are now in place, mounting and symlink creation next * WIP version and type checking * WIP print to udisks2 signals * Add symlink traking * WIP, mounting, symlink creation and removal, input validation, exception handling * WIP: changing laptops * Finish static typing for arguments * Finish static typing for arguments for blocking * Wip: remove comments and prints * add: added banner popup for usb * Del: debug prints * Remove useless comments * Finish available module signals * Make banner popup adhere to the same file naming * Format, remove deprecated typing.Deque, change var name * Changed var name for something more explicit * Fix typo from the prior commit * Del prints from debugging * Change method names, auto restart when usb monitor stops * Add active control flag, incomplete * Add new signals, finish restart logic * Finish restart logic, handle restart type * Finish restart type logic * gcodes_dir can also be none now * Add as a requirement * Instantiate USBManager * Add slot for .aboutToQuit the app, start transition to this cleanup * Fix bug TYPE_CHECKING = True, this broke everything * return fallback on no section * Call usb_manager, fetch gcode dir from configfile if available * Cleanup * Add default usb-manager config section * Call banner on hardware signals * Remove unused experiment * Add docstrings * Add docstrings to FileSystem async interface * Midway adding docstrings to Block device async interface * Finish Block method docstrings * Finish Drive interface methods docstrings * Finish adding documentation * Correctly import the storage package * Pass device name or id_label as the symlink name * Fix device initialization * Del dead code * Typo fix * Del unused var type * Add return types * Typo fixes * Rem unused imports * Add doctring to fire_n_forget * Fix banner parent, pass signals correctly * Only pass the parent if it exists * bugfix: black background * Add a 'USB' prefix to the symlink name so that the screen can assign a usb icon to the symlink * Del accidental import --------- Co-authored-by: Roberto <roberto.martins@blockstec.com>
1 parent 11cbfcf commit 1f8a308

11 files changed

Lines changed: 1688 additions & 41 deletions

File tree

BlocksScreen.cfg

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,7 @@ host: localhost
33
port: 7125
44

55
[screensaver]
6-
timeout: 5000
6+
timeout: 5000
7+
8+
[usb_manager]
9+
gcodes_dir: ~/printer_data/gcodes/

BlocksScreen/BlocksScreen.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import typing
44

55
from lib.panels.mainWindow import MainWindow
6-
from logger import setup_logging
6+
from logger import setup_logging, LogManager
77
from PyQt6 import QtCore, QtGui, QtWidgets
88

99
QtGui.QGuiApplication.setAttribute(
@@ -30,14 +30,19 @@ def show_splash(window: typing.Optional[QtWidgets.QWidget] = None):
3030
splash.finish(window)
3131

3232

33+
def on_quit() -> None:
34+
logging.info("Final exit cleanup")
35+
LogManager.shutdown()
36+
37+
3338
if __name__ == "__main__":
3439
setup_logging(
3540
filename="logs/BlocksScreen.log",
36-
level=logging.DEBUG, # File gets DEBUG+
37-
console_output=True, # Print to terminal
38-
console_level=logging.DEBUG, # Console gets DEBUG+
39-
capture_stderr=True, # Capture X11 errors
40-
capture_stdout=False, # Don't capture print()
41+
level=logging.DEBUG,
42+
console_output=True,
43+
console_level=logging.DEBUG,
44+
capture_stderr=True,
45+
capture_stdout=False,
4146
)
4247
_logger = logging.getLogger(__name__)
4348
_logger.info("============ BlocksScreen Initializing ============")
@@ -47,5 +52,6 @@ def show_splash(window: typing.Optional[QtWidgets.QWidget] = None):
4752
BlocksScreen.setDesktopFileName("BlocksScreen")
4853
main_window = MainWindow()
4954
BlocksScreen.processEvents()
55+
BlocksScreen.aboutToQuit.connect(on_quit)
5056
main_window.show()
5157
sys.exit(BlocksScreen.exec())

BlocksScreen/configfile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def get_section(
104104
) -> BlocksScreenConfig:
105105
"""Get configfile section"""
106106
if not self.config.has_section(section):
107-
raise configparser.NoSectionError(f"No section with name: {section}")
107+
return fallback
108108
return BlocksScreenConfig(self.configfile, section)
109109

110110
def get_options(self) -> list:
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from .usb_controller import USBManager
2+
3+
__doc__ = """
4+
5+
The storage package contains a tool that monitors
6+
pluggable usb devices via python-sdbus library.
7+
While offering an automounting option.
8+
The package is also capable of creating a symlink that
9+
points directly to the mounted usb drive on the gcodes
10+
directory.
11+
12+
13+
There is still a lot of functionality missing, that may
14+
be added in the future, but for now it just automounts,
15+
creates symlinks, cleans up broken symlinks on the
16+
gcodes directory.
17+
18+
19+
All tools related to storage devices should be contained
20+
in this package directory.
21+
"""
22+
__version__ = "0.0.1"
23+
__all__ = ["USBManager"]
24+
__name__ = "storage"
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
from .udisks2_dbus_async import (
2+
UDisks2BlockAsyncInterface,
3+
UDisks2DriveAsyncInterface,
4+
UDisks2PartitionAsyncInterface,
5+
UDisks2FileSystemAsyncInterface,
6+
UDisks2PartitionTableAsyncInterface,
7+
)
8+
9+
10+
class Device:
11+
def __init__(
12+
self,
13+
path: str,
14+
DriveInterface: UDisks2DriveAsyncInterface,
15+
symlink_path: str,
16+
) -> None:
17+
self.path: str = path
18+
self.symlink_path: str = symlink_path
19+
self.driver_interface: UDisks2DriveAsyncInterface = DriveInterface
20+
self.partitions: dict[str, UDisks2PartitionAsyncInterface] = {}
21+
self.raw_block: dict[str, UDisks2BlockAsyncInterface] = {}
22+
self.logical_blocks: dict[str, UDisks2BlockAsyncInterface] = {}
23+
self.file_systems: dict[str, UDisks2FileSystemAsyncInterface] = {}
24+
self.partition_tables: dict[str, UDisks2PartitionTableAsyncInterface] = {}
25+
self.symlinks: list[str] = []
26+
27+
def get_logical_blocks(self) -> dict[str, UDisks2BlockAsyncInterface]:
28+
"""The available logical blocks for the device"""
29+
return self.logical_blocks
30+
31+
def get_driver(self) -> UDisks2DriveAsyncInterface | None:
32+
"""Get current device driver"""
33+
if not self.driver_interface:
34+
return None
35+
return self.driver_interface
36+
37+
def update_file_system(
38+
self, path: str, data: UDisks2FileSystemAsyncInterface
39+
) -> None:
40+
"""Add or update a filesystem for this device
41+
42+
Args:
43+
path (str): filesystem path
44+
data (UDisks2FileSystemAsyncInterface): The interface
45+
"""
46+
self.file_systems.update({path: data})
47+
48+
def update_raw_block(self, path: str, block: UDisks2BlockAsyncInterface) -> None:
49+
"""Add or update a raw block for this device
50+
51+
Args:
52+
path (str): block path
53+
data (UDisks2BlockAsyncInterface): The blocks interface
54+
"""
55+
self.raw_block.update({path: block})
56+
57+
def update_logical_blocks(
58+
self, path: str, block: UDisks2BlockAsyncInterface
59+
) -> None:
60+
"""Add or update a logical block for this device
61+
62+
Args:
63+
path (str): block path
64+
data (UDisks2BlockAsyncInterface): The block interface
65+
"""
66+
self.logical_blocks.update({path: block})
67+
68+
def update_part_table(
69+
self, path: str, part: UDisks2PartitionTableAsyncInterface
70+
) -> None:
71+
"""Add or update partition table for this device
72+
73+
Args:
74+
path (str): Partition table path
75+
part (UDisks2PartitionTableAsyncInterface): The interface
76+
"""
77+
self.partition_tables.update({path: part})
78+
79+
def update_partitions(
80+
self, path: str, block: UDisks2PartitionAsyncInterface
81+
) -> None:
82+
"""Add or update partitions for the current device
83+
84+
Args:
85+
path (str): the partition path
86+
data (UDisks2PartitionAsyncInterface): The partition interface
87+
"""
88+
self.partitions.update({path: block})
89+
90+
def kill(self) -> None:
91+
"""Delete the device and removes any track of it
92+
93+
Especially used when devices were removed unsafely
94+
"""
95+
self.delete()
96+
97+
def delete(self) -> None:
98+
"""Cleanup and delete this device"""
99+
del self.driver_interface
100+
self.partitions.clear()
101+
self.raw_block.clear()
102+
self.file_systems.clear()
103+
self.partition_tables.clear()
104+
self.symlinks.clear()

0 commit comments

Comments
 (0)