Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
1cb7a99
refactor: rename existing state manager to better describing name - s…
Mar 17, 2026
a65bdc3
refactor: fixes
ArtemDychenko Mar 17, 2026
4403d0a
refactor: fix old name for error relate to selection
ArtemDychenko Mar 17, 2026
105b512
refactor: error handling changes, implement state design pattern
ArtemDychenko Mar 18, 2026
87f870c
refactor: remove unnecessary getters/setters in singletons
ArtemDychenko Mar 18, 2026
e2c7663
fix: initial state transtion
ArtemDychenko Mar 18, 2026
25c2a2d
refactor: split GUI state classes into separate files
ArtemDychenko Mar 19, 2026
3914b72
create ci workflow using github actions and add githooks using pre-co…
Mar 19, 2026
1e8e632
Change pre-push hook to pre-commit
ArtemDychenko Mar 19, 2026
82b5fbb
Run ruff check --fix on the existing codebase to clear initial debt a…
ArtemDychenko Mar 19, 2026
8de8b92
Merge pull request #21 from publictransitdata/20-add-github-actions-a…
ArtemDychenko Mar 19, 2026
4581963
Fix memory usage, remove prints in main
ArtemDychenko Mar 19, 2026
6610da5
refactor: ram usage optimalisation
ArtemDychenko Mar 19, 2026
2d12fff
refactor: lazy loading of code errors
ArtemDychenko Mar 22, 2026
e1a18e2
refactor: consolidation of state logic classes and functions in state…
Mar 23, 2026
e24c1d6
fix: recently added selection functions
ArtemDychenko Mar 23, 2026
288ff93
formatting of imports
ArtemDychenko Mar 24, 2026
d7f6f03
after refactor fixes
ArtemDychenko Mar 24, 2026
7cf02b8
ci improvements
ArtemDychenko Mar 24, 2026
c9cf722
format project files with flag --preview for consistency with ci form…
ArtemDychenko Mar 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: CI
on: pull_request
jobs:
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/ruff-action@v3
with:
args: "check --output-format=github"
- uses: astral-sh/ruff-action@v3
with:
args: "format --check --diff --preview"
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ config/*.txt

# Generated files
*.ndjson
.ruff_cache/
.ruff_cache/

# Virtual environment
.venv/
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.6
hooks:
- id: ruff-check
- id: ruff-format
64 changes: 48 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,52 +1,84 @@
# ISMU

Information system master unit

## How to Run the Project on Raspberry Pi Pico W Using VSCode

### Prerequisites

1. **Hardware:**
- Raspberry Pi Pico W.
- USB cable with data transfer capability.
- Raspberry Pi Pico W.
- USB cable with data transfer capability.

2. **Software:**
- [Visual Studio Code (VSCode)](https://code.visualstudio.com/) installed.
- VSCode Extension: MicroPico.
- Python 3.x (preferably version 3.11 or newer).
- MicroPython UF2 file installed on the Raspberry Pi Pico W.
> Follow the [official MicroPython setup guide](https://www.raspberrypi.com/documentation/microcontrollers/micropython.html) for installing it on your Pico.
2. **Software:** - [Visual Studio Code (VSCode)](https://code.visualstudio.com/) installed. - VSCode Extension: MicroPico. - Python 3.x (preferably version 3.11 or newer). - MicroPython UF2 file installed on the Raspberry Pi Pico W.
> Follow the [official MicroPython setup guide](https://www.raspberrypi.com/documentation/microcontrollers/micropython.html) for installing it on your Pico.

### Steps to Configure and Run the Project

#### 1. Install and Configure **MicroPico** Extension

- Open Visual Studio Code.
- Go to the **Extensions** panel and install the **MicroPico** extension.
- After installing, ensure your Raspberry Pi Pico is connected to your computer via USB.

#### 2. Clone the Project Repository

- Copy the repository to your local machine:
``` bash

```bash
git clone https://github.com/publictransitdata/ISMU.git
cd ISMU
```

#### 3. Make Sure Required Files Are in Place

- Ensure Python files for your project are located in a directory that will be synced to the Pico. Typically, this is the project’s root directory.

#### 4. Open the Project in VSCode

- Launch Visual Studio Code and open the project directory:
``` bash

```bash
code .
```
#### 5. Initialize MicroPico project

#### 5. Initialize virtual environment in project directory

```
python -m venv .venv
source .venv/bin/activate
```

#### 6. Install all required dependencies(if you want to develop project install also dev dependencies)

```
pip install -r requirements.txt
opptionally:
pip install -r requirements-dev.txt
```

#### 7. Set up the git hook scripts

```
pre-commit install
```

#### 8. Initialize MicroPico project

- **Right-click** on area in folder/project view.
- In the context menu that appears, select **Initialize MicroPico project**

#### 6. Toggle virtual MicroPico workspace
#### 9. Toggle virtual MicroPico workspace

- At the bottom of vs studio you will see a button with the same name and you have to click it

#### 7. Upload Code to Pico
#### 10. Upload Code to Pico

- To upload your code:
- **Right-click** on the file you want to upload in the side panel (or folder/project view).
- In the context menu that appears, select **Upload File to Pico**

#### 8. Run the Script
- **Right-click** on the file you want to run In Mpy Remote Workspace.
- In the context menu that appears, select **run current file on Pico**
#### 11. Run the Script

- **Right-click** on the file you want to run In Mpy Remote Workspace.
- In the context menu that appears, select **run current file on Pico**
4 changes: 2 additions & 2 deletions app/config_management/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .config_info import CurrentRouteTripSelection, SystemConfig
from .config_info import SystemConfig
from .config_manager import ConfigManager

__all__ = ["SystemConfig", "ConfigManager", "CurrentRouteTripSelection"]
__all__ = ["SystemConfig", "ConfigManager"]
236 changes: 16 additions & 220 deletions app/config_management/config_info.py
Original file line number Diff line number Diff line change
@@ -1,230 +1,26 @@
from app.routes_management import RoutesManager
from app.state_management import StateManager
from utils.singleton_decorator import singleton

AP_NAME = "ismu-hotspot"
AP_PASSWORD = "12345678"
AP_IP = "192.168.4.1"
VERSION = "1.0.0"


@singleton
class SystemConfig:
def __init__(self):
self._line_telegram: str = ""
self._destination_number_telegram: str = ""
self._destination_telegram: str = ""
self._show_start_and_end_stops: bool = False
self._force_short_names: bool = False
self._stop_board_telegram: str = ""
self._show_info_on_stop_board: bool = False
self._ap_name: str = AP_NAME
self._ap_password: str = AP_PASSWORD
self._ap_ip: str = AP_IP
self._baudrate: int = 1200
self._bits: int = 7
self._parity: int = 2
self._stop: int = 2
self._version: str = "1.0.0"

@property
def line_telegram(self):
return self._line_telegram

@line_telegram.setter
def line_telegram(self, value):
self._line_telegram = value

@property
def destination_number_telegram(self):
return self._destination_number_telegram

@destination_number_telegram.setter
def destination_number_telegram(self, value):
self._destination_number_telegram = value

@property
def destination_telegram(self):
return self._destination_telegram

@destination_telegram.setter
def destination_telegram(self, value):
self._destination_telegram = value

@property
def show_start_and_end_stops(self):
return self._show_start_and_end_stops

@show_start_and_end_stops.setter
def show_start_and_end_stops(self, value):
self._show_start_and_end_stops = value

@property
def force_short_names(self):
return self._force_short_names

@force_short_names.setter
def force_short_names(self, value):
self._force_short_names = value

@property
def stop_board_telegram(self):
return self._stop_board_telegram

@stop_board_telegram.setter
def stop_board_telegram(self, value):
self._stop_board_telegram = value

@property
def show_info_on_stop_board(self):
return self._show_info_on_stop_board

@show_info_on_stop_board.setter
def show_info_on_stop_board(self, value):
self._show_info_on_stop_board = value

@property
def ap_name(self):
return self._ap_name

@ap_name.setter
def ap_name(self, value):
self._ap_name = value

@property
def ap_password(self):
return self._ap_password

@ap_password.setter
def ap_password(self, value):
self._ap_password = value

@property
def ap_ip(self):
return self._ap_ip

@ap_ip.setter
def ap_ip(self, value):
self._ap_ip = value

@property
def version(self):
return self._version

@version.setter
def version(self, value):
self._version = value

@property
def baudrate(self):
return self._baudrate

@baudrate.setter
def baudrate(self, value):
self._baudrate = value

@property
def bits(self):
return self._bits

@bits.setter
def bits(self, value):
self._bits = value

@property
def parity(self):
return self._parity

@parity.setter
def parity(self, value):
self._parity = value

@property
def stop(self):
return self._stop

@stop.setter
def stop(self, value):
self._stop = value


class TripInfo:
def __init__(
self,
group_id: str,
point_id: str,
full_name: list[str],
short_name: list[str] | None,
):
self.group_id = group_id
self.point_id = point_id
self.full_name = full_name
self.short_name = short_name

@staticmethod
def trip_from_dict(d: dict | None):
if d is not None:
group_id = d.get("trip_id", "")
point_id = d.get("point_id", "")
full_name = d.get("full_name") or []
short_name = d.get("short_name") or []
return TripInfo(group_id, point_id, full_name, short_name)
else:
return None

def get_proper_trip_name(self) -> list[str]:
if SystemConfig().force_short_names:
if self.short_name:
return self.short_name
else:
return self.full_name
else:
return self.full_name


class CurrentRouteTripSelection:
def __init__(
self,
route_number: str | None = None,
trip: dict | None = None,
no_line_telegram: bool = False,
):
self.route_number = route_number
self.trip = TripInfo.trip_from_dict(trip)
self.no_line_telegram = no_line_telegram
self.is_updated = False

def load_from_saved_state(self):
state = StateManager().get_state()
route = RoutesManager().get_route_by_index(state["route_id"])

if route and route.get("dirs"):
self._route_number = route["route_number"]
dirs = route["dirs"]
trip_id = state.get("trip_id", 0)
if trip_id < len(dirs):
self._trip = TripInfo.trip_from_dict(dirs[trip_id])
self._no_line_telegram = route.get("no_line_telegram", False)

@property
def route_number(self):
return self._route_number

@route_number.setter
def route_number(self, value):
self._route_number = value

@property
def trip(self):
return self._trip

@trip.setter
def trip(self, value):
self._trip = value

@property
def no_line_telegram(self):
return self._no_line_telegram

@no_line_telegram.setter
def no_line_telegram(self, value):
self._no_line_telegram = value
self.line_telegram: str = ""
self.destination_number_telegram: str = ""
self.destination_telegram: str = ""
self.show_start_and_end_stops: bool = False
self.force_short_names: bool = False
self.stop_board_telegram: str = ""
self.show_info_on_stop_board: bool = False
self.ap_name: str = AP_NAME
self.ap_password: str = AP_PASSWORD
self.ap_ip: str = AP_IP
self.baudrate: int = 1200
self.bits: int = 7
self.parity: int = 2
self.stop: int = 2
self.version: str = VERSION
Loading
Loading