mirror of
https://github.com/apache/nuttx-apps.git
synced 2026-08-16 09:53:24 +00:00
This commit marks the end of my GSoC 2025 project in the NuttX section.
All changes:
- Silicon Heaven protocol (SHV) implementation:
The library is cloned from github.com/silicon-heaven/shv-libs4c
and compiled here. The library has out-of-the-box support
for NuttX and possibly all posix systems.
The library is compiled with CONFIG_SHV_LIBS4C_PLATFORM define
set to "nuttx". The library's dependancy is Pavel Pisa's ULUT
and originates from Michal Lenc's GSoC.
- examples/shv-nxboot-updater:
An example which constructs a SHV tree with which you can perform
firmware updates using a SHV "file node". The file node wraps
around NXBoot's update partition.
The application also allows for NXBoot confirmation of the images.
This application is to be meant used as a "background service",
started before any apps, possibly using rcS. The tree is allocated
as GAVL (see below).
- examples/shv-test:
An example which constructs a SHV tree and gives the user
the ability to choose which type of construction should be used,
either:
- GAVL: dynamic SHV tree allocation encapsulated within
an AVL tree.
- GSA: dynamic SHV tree allocation encapsulated within
a continuous array with binary search
- GSA_STATIC: SHV tree is defined as static const, this means
all the data structures are placed in .rodata.
Extremely beneficial for embedded systems,
as .rodata is located in flash and embedded
systems usually have more flash than sram,
thus reducing sram usage. The downside is that
the definitions are rather tedious, but can
be automated in case of some code generation
(like in pysimCoder).
All of it is places in a continuous array with
binary search.
Signed-off-by: Stepan Pressl <pressl.stepan@gmail.com>
236 lines
6.6 KiB
Python
Executable file
236 lines
6.6 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
|
|
############################################################################
|
|
# apps/examples/shv-nxboot-updater/update-script/gui.py
|
|
#
|
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
|
# contributor license agreements. See the NOTICE file distributed with
|
|
# this work for additional information regarding copyright ownership. The
|
|
# ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
# "License"); you may not use this file except in compliance with the
|
|
# License. You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
#
|
|
############################################################################
|
|
|
|
import argparse
|
|
import asyncio
|
|
import logging
|
|
import sys
|
|
from typing import Any
|
|
|
|
from PyQt6.QtCore import Qt, QThread
|
|
from PyQt6.QtWidgets import (
|
|
QApplication,
|
|
QComboBox,
|
|
QDialog,
|
|
QGridLayout,
|
|
QLabel,
|
|
QLineEdit,
|
|
QProgressBar,
|
|
QPushButton,
|
|
)
|
|
from shvconfirm import shv_confirm
|
|
from shvflasher import shv_flasher
|
|
|
|
log_levels = (
|
|
logging.DEBUG,
|
|
logging.INFO,
|
|
logging.WARNING,
|
|
logging.ERROR,
|
|
logging.CRITICAL,
|
|
)
|
|
|
|
PROGRESS_STYLE = """
|
|
QProgressBar{
|
|
border: 2px solid grey;
|
|
border-radius: 5px;
|
|
text-align: center
|
|
}
|
|
|
|
QProgressBar::chunk {
|
|
background-color: green;
|
|
}
|
|
"""
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
"""Parse passed arguments and return result."""
|
|
parser = argparse.ArgumentParser(
|
|
description="GUI application for NuttX firmware flash over SHV"
|
|
)
|
|
parser.add_argument(
|
|
"-v",
|
|
action="count",
|
|
default=0,
|
|
help="Increase verbosity level of logging",
|
|
)
|
|
parser.add_argument(
|
|
"-q",
|
|
action="count",
|
|
default=0,
|
|
help="Decrease verbosity level of logging",
|
|
)
|
|
parser.add_argument(
|
|
"-i",
|
|
"--image",
|
|
dest="image",
|
|
type=str,
|
|
default="nuttx.nximg",
|
|
help="Image path",
|
|
)
|
|
parser.add_argument(
|
|
"-m",
|
|
"--mount",
|
|
dest="target_mount",
|
|
type=str,
|
|
default="test/nuttxdevice",
|
|
help="Target mount location on the SHV broker",
|
|
)
|
|
parser.add_argument(
|
|
"-s",
|
|
"--server",
|
|
dest="shv_server",
|
|
type=str,
|
|
default="tcp://xyz@127.0.0.1:3755?password=xyz",
|
|
help="SHV server/broker",
|
|
)
|
|
return parser.parse_args()
|
|
|
|
|
|
async def flashThreadAsync(
|
|
url: str, img: str, path_to_root, progress_bar: QProgressBar
|
|
) -> None:
|
|
queue: asyncio.Queue = asyncio.Queue()
|
|
task = asyncio.create_task(shv_flasher(url, img, path_to_root, queue))
|
|
|
|
while True:
|
|
progressVal = await queue.get()
|
|
progress_bar.setValue(progressVal)
|
|
|
|
if progressVal == 100:
|
|
break
|
|
|
|
await task
|
|
|
|
|
|
class flashThread(QThread):
|
|
def __init__(
|
|
self,
|
|
parent: Any,
|
|
url: str,
|
|
img: str,
|
|
path_to_root: str,
|
|
progress_bar: QProgressBar,
|
|
) -> None:
|
|
QThread.__init__(self, parent)
|
|
self.url = url
|
|
self.img = img
|
|
self.path_to_root = path_to_root
|
|
self.progress_bar = progress_bar
|
|
|
|
def run(self) -> None:
|
|
asyncio.run(
|
|
flashThreadAsync(self.url, self.img, self.path_to_root, self.progress_bar)
|
|
)
|
|
|
|
|
|
class confirmThread(QThread):
|
|
def __init__(self, parent: Any, url: str, path_to_root: str) -> None:
|
|
QThread.__init__(self, parent)
|
|
self.url = url
|
|
self.path_to_root = path_to_root
|
|
|
|
def run(self) -> None:
|
|
asyncio.run(shv_confirm(self.url, self.path_to_root))
|
|
|
|
|
|
class ShvFlasherGui(QDialog):
|
|
def __init__(self, image=None, target_mount=None, shv_server=None) -> None:
|
|
super().__init__()
|
|
self.setWindowModality(Qt.WindowModality.ApplicationModal)
|
|
self.setWindowTitle("NuttX Firmware Flasher over SHV")
|
|
|
|
lbl_shv_url = QLabel("SHV RCP URL")
|
|
self.set_interface(shv_server)
|
|
|
|
lbl_image = QLabel("Image path")
|
|
self.image = QLineEdit(image if image is not None else "", self)
|
|
|
|
lbl_target_mount = QLabel("Device mount")
|
|
self.target_mount = QLineEdit(
|
|
target_mount if target_mount is not None else "", self
|
|
)
|
|
|
|
self.progress_bar = QProgressBar(self)
|
|
self.progress_bar.setValue(0)
|
|
self.progress_bar.setStyleSheet(PROGRESS_STYLE)
|
|
|
|
pb_flash = QPushButton("FLASH")
|
|
pb_confirm = QPushButton("CONFIRM")
|
|
pb_exit = QPushButton("EXIT")
|
|
grid = QGridLayout()
|
|
|
|
grid.addWidget(lbl_shv_url, 0, 0)
|
|
grid.addWidget(self.interface, 0, 1)
|
|
grid.addWidget(lbl_image, 1, 0)
|
|
grid.addWidget(self.image, 1, 1)
|
|
grid.addWidget(lbl_target_mount, 2, 0)
|
|
grid.addWidget(self.target_mount, 2, 1)
|
|
grid.addWidget(self.progress_bar, 3, 0, 1, 3)
|
|
grid.addWidget(pb_flash, 4, 0)
|
|
grid.addWidget(pb_confirm, 4, 1)
|
|
grid.addWidget(pb_exit, 4, 2)
|
|
|
|
pb_flash.clicked.connect(self.do_flash)
|
|
pb_confirm.clicked.connect(self.do_confirm)
|
|
pb_exit.clicked.connect(self.reject)
|
|
|
|
self.setLayout(grid)
|
|
|
|
def set_interface(self, shv_server=None) -> None:
|
|
self.interface = QComboBox()
|
|
self.interface.setEditable(True)
|
|
# tcp://user@localhost:3755?password=pass
|
|
self.interface.addItems([shv_server if shv_server is not None else ""])
|
|
|
|
def do_flash(self) -> None:
|
|
rpc_url = str(self.interface.currentText())
|
|
img_path = str(self.image.text())
|
|
path_to_root = str(self.target_mount.text())
|
|
workerFlash = flashThread(
|
|
self, rpc_url, img_path, path_to_root, self.progress_bar
|
|
)
|
|
workerFlash.start()
|
|
|
|
def do_confirm(self) -> None:
|
|
rpc_url = str(self.interface.currentText())
|
|
path_to_root = str(self.target_mount.text())
|
|
worker = confirmThread(self, rpc_url, path_to_root)
|
|
worker.start()
|
|
|
|
|
|
def main() -> None:
|
|
args = parse_args()
|
|
logging.basicConfig(
|
|
level=log_levels[sorted([1 - args.v + args.q, 0, len(log_levels) - 1])[1]],
|
|
format="[%(asctime)s] [%(levelname)s] - %(message)s",
|
|
)
|
|
|
|
_ = QApplication(sys.argv)
|
|
|
|
dialog = ShvFlasherGui(
|
|
image=args.image, target_mount=args.target_mount, shv_server=args.shv_server
|
|
)
|
|
dialog.exec()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|