From 2265e8a1d16fc7f114cfe2370e9b7e5e504f88e2 Mon Sep 17 00:00:00 2001 From: Ice Date: Thu, 15 Feb 2024 13:43:33 -0500 Subject: [PATCH 01/43] [NOS3#234] Initial Igniter GUI Commit --- cfg/gui/cfg_gui.ui | 1156 +++++++++++++++++++++++++++++++++++++ cfg/gui/cfg_gui.ui.nIFTUJ | 1112 +++++++++++++++++++++++++++++++++++ cfg/gui/cfg_gui_main.py | 332 +++++++++++ cfg/gui/cfg_gui_ui.py | 399 +++++++++++++ 4 files changed, 2999 insertions(+) create mode 100644 cfg/gui/cfg_gui.ui create mode 100644 cfg/gui/cfg_gui.ui.nIFTUJ create mode 100644 cfg/gui/cfg_gui_main.py create mode 100644 cfg/gui/cfg_gui_ui.py diff --git a/cfg/gui/cfg_gui.ui b/cfg/gui/cfg_gui.ui new file mode 100644 index 00000000..09550594 --- /dev/null +++ b/cfg/gui/cfg_gui.ui @@ -0,0 +1,1156 @@ + + + Form + + + + 0 + 0 + 658 + 655 + + + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 127 + 127 + 127 + + + + + + + 170 + 170 + 170 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 127 + 127 + 127 + + + + + + + 170 + 170 + 170 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + + + 127 + 127 + 127 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 127 + 127 + 127 + + + + + + + 170 + 170 + 170 + + + + + + + 127 + 127 + 127 + + + + + + + 255 + 255 + 255 + + + + + + + 127 + 127 + 127 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 127 + 127 + 127 + + + + + + + 255 + 255 + 255 + + + + + + + + Form + + + + + 0 + 0 + 661 + 661 + + + + QTabWidget::Rounded + + + 0 + + + + Config + + + + + 130 + 20 + 421 + 26 + + + + Qt::AlignCenter + + + true + + + None + + + + + + 550 + 20 + 94 + 26 + + + + Browse... + + + + + + 10 + 20 + 121 + 21 + + + + Current Config: + + + + + + 10 + 260 + 631 + 321 + + + + Spacecraft Config + + + + + 0 + 20 + 631 + 301 + + + + + + + + 0 + 0 + + + + true + + + Qt::ScrollBarAlwaysOn + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 613 + 297 + + + + + 0 + 0 + + + + + QLayout::SetDefaultConstraint + + + + + + + + + + + 150 + -1 + 48 + 21 + + + + 1 + + + + + + + 10 + 60 + 631 + 181 + + + + Master Config + + + + + 0 + 20 + 631 + 161 + + + + + + + false + + + + + + + + + + 220 + 590 + 94 + 26 + + + + Save + + + + + + 330 + 590 + 94 + 26 + + + + Save As... + + + + + + Build + + + + + 10 + 120 + 631 + 491 + + + + Console Output + + + Qt::AlignCenter + + + false + + + false + + + + + 0 + 20 + 631 + 471 + + + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + + + true + + + + + + + 110 + 20 + 421 + 81 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 79 + 0 + 341 + 80 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 0 + 0 + 340 + 80 + + + + + + + FSW + + + + + + + FSW + + + + + + + GSW + + + + + + + SIM + + + + + + + SIM + + + + + + + GSW + + + + + + + All + + + + + + + All + + + + + + + + + + 0 + 0 + 81 + 41 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 0 + 0 + 81 + 41 + + + + + + + Build + + + Qt::AlignCenter + + + + + + + + + + 0 + 40 + 81 + 41 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 0 + 0 + 81 + 41 + + + + + + + Clean + + + Qt::AlignCenter + + + + + + + + + + + Launch + + + + + 10 + 10 + 631 + 611 + + + + + + + + + 10 + 570 + 611 + 31 + + + + + 45 + + + + + Play + + + + ../../../../../../../../usr/share/icons/Humanity/actions/24/gtk-media-play-ltr.svg../../../../../../../../usr/share/icons/Humanity/actions/24/gtk-media-play-ltr.svg + + + + + + + Pause + + + + ../../../../../../../../usr/share/icons/Humanity/actions/24/media-playback-pause.svg../../../../../../../../usr/share/icons/Humanity/actions/24/media-playback-pause.svg + + + + + + + + + 10 + 10 + 611 + 41 + + + + + + + Launch + + + + + + + Stop + + + + + + + + + 10 + 60 + 611 + 451 + + + + NOS3 Time Driver + + + Qt::AlignCenter + + + false + + + false + + + + + 0 + 20 + 611 + 431 + + + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + + + true + + + + + + + 190 + 520 + 261 + 41 + + + + + + + + + + + + + Run For + + + + + Run Until + + + + + + + + + + + Qt::AlignCenter + + + + + + + + + + + + diff --git a/cfg/gui/cfg_gui.ui.nIFTUJ b/cfg/gui/cfg_gui.ui.nIFTUJ new file mode 100644 index 00000000..6af78023 --- /dev/null +++ b/cfg/gui/cfg_gui.ui.nIFTUJ @@ -0,0 +1,1112 @@ + + + Form + + + + 0 + 0 + 655 + 655 + + + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 127 + 127 + 127 + + + + + + + 170 + 170 + 170 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 127 + 127 + 127 + + + + + + + 170 + 170 + 170 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + + + 127 + 127 + 127 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 127 + 127 + 127 + + + + + + + 170 + 170 + 170 + + + + + + + 127 + 127 + 127 + + + + + + + 255 + 255 + 255 + + + + + + + 127 + 127 + 127 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 127 + 127 + 127 + + + + + + + 255 + 255 + 255 + + + + + + + + Form + + + + + 0 + 0 + 661 + 661 + + + + QTabWidget::Rounded + + + 0 + + + + Config + + + + + 130 + 20 + 421 + 26 + + + + Qt::AlignCenter + + + true + + + None + + + + + + 550 + 20 + 94 + 26 + + + + Browse... + + + + + + 10 + 20 + 121 + 21 + + + + Current Config: + + + + + + 10 + 300 + 631 + 281 + + + + Spacecraft Config + + + + + 0 + 20 + 631 + 261 + + + + + + + QTextEdit::NoWrap + + + false + + + + + + + + + 150 + -1 + 48 + 21 + + + + 1 + + + + + + + 10 + 60 + 631 + 231 + + + + Master Config + + + + + 0 + 20 + 631 + 211 + + + + + + + false + + + + + + + + + + 220 + 590 + 94 + 26 + + + + Save + + + + + + 330 + 590 + 94 + 26 + + + + Save As... + + + + + + Build + + + + + 10 + 120 + 631 + 491 + + + + Console Output + + + false + + + false + + + + + 0 + 20 + 631 + 471 + + + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + + + true + + + + + + + 110 + 20 + 421 + 81 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 79 + 0 + 341 + 80 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 0 + 0 + 340 + 80 + + + + + + + FSW + + + + + + + FSW + + + + + + + GSW + + + + + + + SIM + + + + + + + SIM + + + + + + + GSW + + + + + + + All + + + + + + + All + + + + + + + + + + 0 + 0 + 81 + 41 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 0 + 0 + 81 + 41 + + + + + + + Build + + + Qt::AlignCenter + + + + + + + + + + 0 + 40 + 81 + 41 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 0 + 0 + 81 + 41 + + + + + + + Clean + + + Qt::AlignCenter + + + + + + + + + + + Launch + + + + + 10 + 10 + 631 + 611 + + + + + + + + + 10 + 570 + 611 + 31 + + + + + 45 + + + + + Play + + + + ../../../../../../../../usr/share/icons/Humanity/actions/24/gtk-media-play-ltr.svg../../../../../../../../usr/share/icons/Humanity/actions/24/gtk-media-play-ltr.svg + + + + + + + Pause + + + + ../../../../../../../../usr/share/icons/Humanity/actions/24/media-playback-pause.svg../../../../../../../../usr/share/icons/Humanity/actions/24/media-playback-pause.svg + + + + + + + + + 10 + 10 + 611 + 41 + + + + + + + Launch + + + + + + + Stop + + + + + + + + + 10 + 60 + 611 + 451 + + + + Console Output + + + false + + + false + + + + + 0 + 20 + 611 + 431 + + + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + + + true + + + + + + + 200 + 530 + 93 + 26 + + + + + Run For + + + + + Run Until + + + + + + + 290 + 530 + 181 + 26 + + + + + + + Qt::AlignCenter + + + + + + + + + diff --git a/cfg/gui/cfg_gui_main.py b/cfg/gui/cfg_gui_main.py new file mode 100644 index 00000000..c52c629e --- /dev/null +++ b/cfg/gui/cfg_gui_main.py @@ -0,0 +1,332 @@ +from pathlib import Path +from PySide6.QtWidgets import QWidget, QApplication, QFileDialog, QTextEdit, QPushButton, QDateTimeEdit, QLabel, QCheckBox, QVBoxLayout, QSizePolicy, QDoubleSpinBox, QLayout +from PySide6.QtCore import QProcess, QDateTime +from PySide6.QtGui import QTextCharFormat + +import sys, re, xmltodict, datetime, threading +import xml.etree.ElementTree as ET + +from cfg_gui_ui import Ui_Form + +class cfg_gui(QWidget): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.ui = Ui_Form() + self.ui.setupUi(self) + self.setFixedSize(655, 655) + self.setWindowTitle("NOS3 Igniter - Version 0.0.1") + + self.dateTimeEdit = QDateTimeEdit() + self.scConfigs = {} # Stores child configs {'filename' : "filetext"} + self.prevButtonPressed = None # Tracks the last button pressed, used in buttonColor() + self.defaultStyleSheet = self.ui.pushButton_buildAll.styleSheet() # Saves default stylesheet to return button color to normal, buttonColor() + self.setup = 0 # Allows for switchConfig() to initially be called without calling saveText() + self.configNumTrack = 0 # Tracks the index of the previous SC config when switching to another index + + # Config Tab + self.ui.pushButton_browse.clicked.connect(self.browseConfig) + self.ui.pushButton_save.clicked.connect(lambda: self.saveXML("save")) + self.ui.pushButton_saveAs.clicked.connect(lambda: self.saveXML("saveAs")) + self.ui.spinBox_configNumber.valueChanged.connect(lambda: self.switchConfig(self.ui.spinBox_configNumber.value())) + + # Build Tab + self.ui.pushButton_buildAll.clicked.connect(lambda: self.build("all", self.ui.pushButton_buildAll)) + self.ui.pushButton_fswBuild.clicked.connect(lambda: self.build("fsw", self.ui.pushButton_fswBuild)) + self.ui.pushButton_gswBuild.clicked.connect(lambda: self.build("gsw", self.ui.pushButton_gswBuild)) + self.ui.pushButton_simBuild.clicked.connect(lambda: self.build("sim", self.ui.pushButton_simBuild)) + self.ui.pushButton_cleanAll.clicked.connect(lambda: self.clean("all", self.ui.pushButton_cleanAll)) + self.ui.pushButton_fswClean.clicked.connect(lambda: self.clean("fsw", self.ui.pushButton_fswClean)) + self.ui.pushButton_gswClean.clicked.connect(lambda: self.clean("gsw", self.ui.pushButton_gswClean)) + self.ui.pushButton_simClean.clicked.connect(lambda: self.clean("sim", self.ui.pushButton_simClean)) + + # Launch Tab + self.ui.pushButton_play.clicked.connect(lambda: self.startBashProcess(self.ui.textEdit_launchConsole, ["-lc", "echo '>> Starting NOS3 Time Driver'"])) + self.ui.pushButton_stop.clicked.connect(lambda: self.gnome_terminal(self.ui.textEdit_launchConsole, "make stop")) + self.ui.pushButton_pause.clicked.connect(lambda: self.startBashProcess(self.ui.textEdit_launchConsole, ["-lc", "echo '>> Pausing NOS3 Time Driver'"])) + self.ui.pushButton_launch.clicked.connect(lambda: self.gnome_terminal(self.ui.textEdit_launchConsole, "make launch")) + self.ui.comboBox_run.currentIndexChanged.connect(self.run_ForUntil) + + # Replaces the textbox on launch tab with a date/time box and vice versa + def run_ForUntil(self): + index = self.ui.comboBox_run.currentIndex() + if index == 0: + self.ui.horizontalLayout_runForUntil.itemAt(1).widget().setParent(None) + self.ui.horizontalLayout_runForUntil.insertWidget(1, self.ui.lineEdit_secondsEntry) + self.ui.lineEdit_secondsEntry.setPlaceholderText("") + elif index == 1: + self.ui.horizontalLayout_runForUntil.itemAt(1).widget().setParent(None) + self.ui.horizontalLayout_runForUntil.insertWidget(1, self.ui.lineEdit_secondsEntry) + self.ui.lineEdit_secondsEntry.setPlaceholderText("Seconds") + elif index == 2: + self.ui.horizontalLayout_runForUntil.itemAt(1).widget().setParent(None) + self.currentTime = datetime.datetime.now() + self.dateTimeEdit.setMinimumDateTime(QDateTime(self.currentTime.year, self.currentTime.month, self.currentTime.day, self.currentTime.hour, self.currentTime.minute, self.currentTime.second, 0, 0)) + self.ui.horizontalLayout_runForUntil.insertWidget(1, self.dateTimeEdit) + + # Overwrites the currently saved text for the currently selected spacecraft config when edited + def saveText(self, layout:QLayout, config_value:int): + child = self.scConfigs[config_value] + text = child + filename = text.split('\n')[0] + childXml = text.split('\n', 2)[2] + childXml = xmltodict.parse(childXml) + + applications = ['cf', 'ds', 'fm', 'lc', 'sc'] + components = ['adcs', 'cam', 'css', 'eps', 'fss', 'gps', 'imu', 'mag', 'radio', 'rw', 'sample', 'st', 'torquer'] + + i = 0 + while layout.itemAt(i) != None: + widget = layout.itemAt(i).widget() + if isinstance(widget, QCheckBox): + #print(child.text()) + text = widget.text().split(' ')[0] + if text in applications: + childXml['sc-1-config']['applications'][text]['enable'] = str(widget.isChecked()).lower() + elif text in components: + childXml['sc-1-config']['components'][text]['enable'] = str(widget.isChecked()).lower() + elif text == 'gui': + childXml['sc-1-config'][text]['enable'] = str(widget.isChecked()).lower() + elif isinstance(widget, QDoubleSpinBox): + #print(child.prefix()) + prefix = widget.prefix().split(' ')[0] + childXml['sc-1-config']['orbit'][prefix] = str(widget.value()) + i += 1 + + combined = filename + '\n\n' + xmltodict.unparse(childXml) + self.scConfigs[config_value] = combined + + # Saves the master/child XML's edited in the GUI + def saveXML(self, saveType:str): + # saveType = "save" (overwrite) or "saveAs" (new) + + if saveType == "saveAs": + savePath, _ = QFileDialog.getSaveFileName(None, 'Directory', './cfg/custom') + elif saveType == "save": + savePath = self.config_path + + # Grab master and save to xml + masterXml = xmltodict.parse(self.ui.textEdit_masterConfig.toPlainText()) + self.convert2xml(masterXml, savePath) + + # TODO: Now handle children, currently placeholder until checkboxes are added (update in progress) + self.saveText(self.layout_, self.ui.spinBox_configNumber.value()-1) + + for child in self.scConfigs: + text = str(self.scConfigs[child]) + filename = text.split('\n')[0].split(' ')[1] + childXml = text.split('\n', 2)[2] + childXml = xmltodict.parse(childXml) + + # save under same directory as masterXml using filename parsed from textEdit + self.convert2xml(childXml, savePath.rsplit('/', 1)[0]+f'/{filename}') + + # Loads the child config into the Spacecraft Config textbox + def switchConfig(self, value:int): + # value : index of spacecraft config in the order listed in the master XML + # Note: Parameter indexing starts at 1 + + # save edits made to config before viewing next one + if self.setup == 1: + self.saveText(self.layout_, self.configNumTrack) + self.configNumTrack = value-1 + + self.setup = 1 + self.ui.scrollArea.setWidgetResizable(True) + self.ui.scrollAreaWidgetContents.setLayout(QVBoxLayout().layout()) + self.layout_ = self.ui.scrollAreaWidgetContents.layout() + self.layout_.setSpacing(12) + + # remove all items from SC Config window when switching index + while self.layout_.itemAt(0) != None: + child = self.layout_.itemAt(0).widget().setParent(None) + + + value = value-1 + if value in self.scConfigs: + fileName = self.scConfigs[value].split('\n')[0] + childXML = self.scConfigs[value].split('\n')[2::] + childXML = ''.join(childXML) + childDict = xmltodict.parse(childXML) + + policy = self.ui.scrollAreaWidgetContents.sizePolicy() + + for child in childDict: + configTag = QLabel() + configTag.setText((fileName)) + configTag.setMinimumHeight(18) + self.layout_.addWidget(configTag) + for child2 in childDict[child]: + tag = QLabel() + tag.setText(child2.upper()+": ") + format = QTextCharFormat() + format.setFontUnderline(True) + tag.setFont(format.font()) + tag.setMinimumHeight(18) + self.layout_.addWidget(tag) + if child2 in ['applications', 'components']: + for child3 in childDict[child][child2]: + enableTag = QCheckBox() + enableTag.setText(child3 + " enable ") + enableTag.setChecked(childDict[child][child2][child3]['enable'] == 'true') + enableTag.setMinimumHeight(18) + enableTag.sizePolicy().setVerticalPolicy(QSizePolicy.Expanding) + self.layout_.addWidget(enableTag) + elif child2 == 'gui': + enableTag = QCheckBox() + enableTag.setText(child2 + " enable ") + enableTag.setChecked(childDict[child][child2]['enable'] == 'true') + self.layout_.addWidget(enableTag) + elif child2 == 'orbit': + for child3 in childDict[child][child2]: + orbitSpinBox = QDoubleSpinBox() + orbitSpinBox.setMinimum(-99.00) + orbitSpinBox.setMaximum(99.00) + orbitSpinBox.setValue(float(childDict[child][child2][child3])) + orbitSpinBox.setPrefix(f'{child3} = ') + self.layout_.addWidget(orbitSpinBox) + else: + tag = QLabel() + tag.setText("*ERROR*\n\nMake sure you chose a master configuration file\n\n*ERROR*") + self.layout_.addWidget(tag) + + # Converts a dictionary to XML file, saved under the given filename/path + def convert2xml(self, attrDict:dict, fileName:[str, Path]): + # ensure file is saved as xml + if fileName[-4::] != ".xml": + fileName += ".xml" + + # unparse dictionary to xml + with open(fileName, "w") as f: + xmltodict.unparse(attrDict, f, pretty=True) + f.close() + + # Opens file selection menu and calls parseXML() on the selected file + def browseConfig(self): + self.config_path, _ = QFileDialog.getOpenFileName(None, 'File', './cfg', "XML Files [ *.xml ]") + if self.config_path != "": + self.config_name = self.config_path.split("/")[-1] + self.ui.lineEdit_curConfig.setText(self.config_name) + self.parseXml(self.config_path) + + # Parses Master and child XML files from the given file, updates text boxes accordingly + def parseXml(self, config_path): + + # Master + with open(config_path, 'r') as f: + self.ui.textEdit_masterConfig.setText(f.read()) + f.close() + + i = 1 + childDict = {} + root = ET.parse(config_path).getroot() + for child in root: + if child.tag == "number-spacecraft": + self.ui.spinBox_configNumber.setMaximum(int(child.text)) + if re.match("sc-[0-9]+-cfg", child.tag): + childDict[child.tag] = child.text + i+=1 + + # Children + config_dir = str(config_path.rsplit('/', 1)[0]) + for i, child in enumerate(childDict): + if Path(f'{config_dir}/{childDict[child]}').is_file(): + filePath = f'{config_dir}/{childDict[child]}' + elif Path(f'./cfg/{childDict[child]}').is_file(): + filePath = f'./cfg/{childDict[child]}' + elif Path(f'./cfg/custom/{childDict[child]}').is_file(): + filePath = f'./cfg/custom/{childDict[child]}' + else: + raise FileNotFoundError(childDict[child]) + + with open(filePath, 'r') as f: + self.scConfigs[i] = f'Filename: {childDict[child]}\n\n{f.read()}' + f.close() + + # Update Spacecraft Config Text + self.switchConfig(1) + + # Starts a Bash process to execute args, redirects output to given textbox, not used for now + def startBashProcess(self, textbox:QTextEdit, args:list): + process = QProcess() + process.start("bash", [item for item in args]) + + process.readyReadStandardOutput.connect(lambda: textbox.append(process.readAllStandardOutput().data().decode())) + process.readyReadStandardError.connect(lambda: textbox.append(process.readAllStandardError().data().decode())) + + process.waitForFinished() + process.close() + + # Test for gnome-terminal instead of bash, also uses startCommand() instead of start() + def gnome_terminal(self, textbox:QTextEdit, command:str): + process = QProcess() + print(command) + process.startCommand(f'gnome-terminal -- {command}') + + process.readyReadStandardOutput.connect(lambda: textbox.append(process.readAllStandardOutput().data().decode())) + process.readyReadStandardError.connect(lambda: textbox.append(process.readAllStandardError().data().decode())) + + process.waitForFinished(msecs=-1) + textbox.append(f'>> {command}...') + #process.close() + + # Placeholder clean command + def clean(self, software:str, button:QPushButton): + textbox = self.ui.textEdit_buildConsole + if software == 'all': + command = f'make clean' + else: + command = f'make clean-{software}' + + self.buttonColor(button) + t1 = threading.Thread(target=self.thread_Bash(textbox, button, command), name='t1') + t1.start() + + # Placeholder build command, assumes make prep already ran, same with clean commands + def build(self, software:str, button:QPushButton): + textbox = self.ui.textEdit_buildConsole + command = f'make {software}' + + self.buttonColor(button) + t1 = threading.Thread(target=self.thread_Bash(textbox, button, command), name='t1') + t1.start() + + # Button/Bash function wrapper for threads + def thread_Bash(self, textbox:QTextEdit, button:QPushButton, command:str): + self.disableButtons(button) + self.gnome_terminal(textbox, command) + self.enableButtons(button) + + # Changes the color of the most recently pressed button to green + def buttonColor(self, button:QPushButton): + if self.prevButtonPressed is not None: + self.prevButtonPressed.setStyleSheet(self.defaultStyleSheet) + button.setStyleSheet('QPushButton {background-color: green;}') + self.prevButtonPressed = button + + # Disable build/clean buttons while another is being ran + def disableButtons(self, button:QPushButton): + index = self.ui.gridLayout_buildCleanButtons.count()-1 + while index >= 0: + widget = self.ui.gridLayout_buildCleanButtons.itemAt(index).widget() + if widget != button: + widget.setDisabled(1) + index -= 1 + + # Enable build/clean buttons after process is done running + def enableButtons(self, button:QPushButton): + index = self.ui.gridLayout_buildCleanButtons.count()-1 + while index >= 0: + widget = self.ui.gridLayout_buildCleanButtons.itemAt(index).widget() + if widget != button: + widget.setEnabled(1) + index -= 1 + + +def main(): + app = QApplication(sys.argv) + win = cfg_gui() + win.show() + sys.exit(app.exec()) + +main() \ No newline at end of file diff --git a/cfg/gui/cfg_gui_ui.py b/cfg/gui/cfg_gui_ui.py new file mode 100644 index 00000000..913952bb --- /dev/null +++ b/cfg/gui/cfg_gui_ui.py @@ -0,0 +1,399 @@ +# -*- coding: utf-8 -*- + +################################################################################ +## Form generated from reading UI file 'cfg_gui.ui' +## +## Created by: Qt User Interface Compiler version 6.6.1 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, + QMetaObject, QObject, QPoint, QRect, + QSize, QTime, QUrl, Qt) +from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, + QFont, QFontDatabase, QGradient, QIcon, + QImage, QKeySequence, QLinearGradient, QPainter, + QPalette, QPixmap, QRadialGradient, QTransform) +from PySide6.QtWidgets import (QApplication, QComboBox, QFrame, QGridLayout, + QGroupBox, QHBoxLayout, QLabel, QLineEdit, + QPushButton, QScrollArea, QSizePolicy, QSpinBox, + QTabWidget, QTextEdit, QVBoxLayout, QWidget) + +class Ui_Form(object): + def setupUi(self, Form): + if not Form.objectName(): + Form.setObjectName(u"Form") + Form.resize(655, 655) + palette = QPalette() + brush = QBrush(QColor(0, 0, 0, 255)) + brush.setStyle(Qt.SolidPattern) + palette.setBrush(QPalette.Active, QPalette.WindowText, brush) + brush1 = QBrush(QColor(255, 255, 255, 255)) + brush1.setStyle(Qt.SolidPattern) + palette.setBrush(QPalette.Active, QPalette.Button, brush1) + palette.setBrush(QPalette.Active, QPalette.Light, brush1) + palette.setBrush(QPalette.Active, QPalette.Midlight, brush1) + brush2 = QBrush(QColor(127, 127, 127, 255)) + brush2.setStyle(Qt.SolidPattern) + palette.setBrush(QPalette.Active, QPalette.Dark, brush2) + brush3 = QBrush(QColor(170, 170, 170, 255)) + brush3.setStyle(Qt.SolidPattern) + palette.setBrush(QPalette.Active, QPalette.Mid, brush3) + palette.setBrush(QPalette.Active, QPalette.Text, brush) + palette.setBrush(QPalette.Active, QPalette.BrightText, brush1) + palette.setBrush(QPalette.Active, QPalette.ButtonText, brush) + palette.setBrush(QPalette.Active, QPalette.Base, brush1) + palette.setBrush(QPalette.Active, QPalette.Window, brush1) + palette.setBrush(QPalette.Active, QPalette.Shadow, brush) + palette.setBrush(QPalette.Active, QPalette.AlternateBase, brush1) + brush4 = QBrush(QColor(255, 255, 220, 255)) + brush4.setStyle(Qt.SolidPattern) + palette.setBrush(QPalette.Active, QPalette.ToolTipBase, brush4) + palette.setBrush(QPalette.Active, QPalette.ToolTipText, brush) + brush5 = QBrush(QColor(0, 0, 0, 127)) + brush5.setStyle(Qt.SolidPattern) +#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + palette.setBrush(QPalette.Active, QPalette.PlaceholderText, brush5) +#endif + palette.setBrush(QPalette.Active, QPalette.Accent, brush1) + palette.setBrush(QPalette.Inactive, QPalette.WindowText, brush) + palette.setBrush(QPalette.Inactive, QPalette.Button, brush1) + palette.setBrush(QPalette.Inactive, QPalette.Light, brush1) + palette.setBrush(QPalette.Inactive, QPalette.Midlight, brush1) + palette.setBrush(QPalette.Inactive, QPalette.Dark, brush2) + palette.setBrush(QPalette.Inactive, QPalette.Mid, brush3) + palette.setBrush(QPalette.Inactive, QPalette.Text, brush) + palette.setBrush(QPalette.Inactive, QPalette.BrightText, brush1) + palette.setBrush(QPalette.Inactive, QPalette.ButtonText, brush) + palette.setBrush(QPalette.Inactive, QPalette.Base, brush1) + palette.setBrush(QPalette.Inactive, QPalette.Window, brush1) + palette.setBrush(QPalette.Inactive, QPalette.Shadow, brush) + palette.setBrush(QPalette.Inactive, QPalette.AlternateBase, brush1) + palette.setBrush(QPalette.Inactive, QPalette.ToolTipBase, brush4) + palette.setBrush(QPalette.Inactive, QPalette.ToolTipText, brush) +#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + palette.setBrush(QPalette.Inactive, QPalette.PlaceholderText, brush5) +#endif + palette.setBrush(QPalette.Inactive, QPalette.Accent, brush1) + palette.setBrush(QPalette.Disabled, QPalette.WindowText, brush2) + palette.setBrush(QPalette.Disabled, QPalette.Button, brush1) + palette.setBrush(QPalette.Disabled, QPalette.Light, brush1) + palette.setBrush(QPalette.Disabled, QPalette.Midlight, brush1) + palette.setBrush(QPalette.Disabled, QPalette.Dark, brush2) + palette.setBrush(QPalette.Disabled, QPalette.Mid, brush3) + palette.setBrush(QPalette.Disabled, QPalette.Text, brush2) + palette.setBrush(QPalette.Disabled, QPalette.BrightText, brush1) + palette.setBrush(QPalette.Disabled, QPalette.ButtonText, brush2) + palette.setBrush(QPalette.Disabled, QPalette.Base, brush1) + palette.setBrush(QPalette.Disabled, QPalette.Window, brush1) + palette.setBrush(QPalette.Disabled, QPalette.Shadow, brush) + palette.setBrush(QPalette.Disabled, QPalette.AlternateBase, brush1) + palette.setBrush(QPalette.Disabled, QPalette.ToolTipBase, brush4) + palette.setBrush(QPalette.Disabled, QPalette.ToolTipText, brush) + brush6 = QBrush(QColor(127, 127, 127, 127)) + brush6.setStyle(Qt.SolidPattern) +#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + palette.setBrush(QPalette.Disabled, QPalette.PlaceholderText, brush6) +#endif + palette.setBrush(QPalette.Disabled, QPalette.Accent, brush1) + Form.setPalette(palette) + self.tabWidget = QTabWidget(Form) + self.tabWidget.setObjectName(u"tabWidget") + self.tabWidget.setGeometry(QRect(0, 0, 661, 661)) + self.tabWidget.setTabShape(QTabWidget.Rounded) + self.tab = QWidget() + self.tab.setObjectName(u"tab") + self.lineEdit_curConfig = QLineEdit(self.tab) + self.lineEdit_curConfig.setObjectName(u"lineEdit_curConfig") + self.lineEdit_curConfig.setGeometry(QRect(130, 20, 421, 26)) + self.lineEdit_curConfig.setAlignment(Qt.AlignCenter) + self.lineEdit_curConfig.setReadOnly(True) + self.pushButton_browse = QPushButton(self.tab) + self.pushButton_browse.setObjectName(u"pushButton_browse") + self.pushButton_browse.setGeometry(QRect(550, 20, 94, 26)) + self.label_curConfig = QLabel(self.tab) + self.label_curConfig.setObjectName(u"label_curConfig") + self.label_curConfig.setGeometry(QRect(10, 20, 121, 21)) + self.groupBox_scConfig = QGroupBox(self.tab) + self.groupBox_scConfig.setObjectName(u"groupBox_scConfig") + self.groupBox_scConfig.setGeometry(QRect(10, 260, 631, 321)) + self.horizontalLayoutWidget_6 = QWidget(self.groupBox_scConfig) + self.horizontalLayoutWidget_6.setObjectName(u"horizontalLayoutWidget_6") + self.horizontalLayoutWidget_6.setGeometry(QRect(0, 20, 631, 301)) + self.horizontalLayout_6 = QHBoxLayout(self.horizontalLayoutWidget_6) + self.horizontalLayout_6.setObjectName(u"horizontalLayout_6") + self.horizontalLayout_6.setContentsMargins(0, 0, 0, 0) + self.scrollArea = QScrollArea(self.horizontalLayoutWidget_6) + self.scrollArea.setObjectName(u"scrollArea") + self.scrollArea.setAutoFillBackground(True) + self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) + self.scrollArea.setWidgetResizable(False) + self.scrollAreaWidgetContents = QWidget() + self.scrollAreaWidgetContents.setObjectName(u"scrollAreaWidgetContents") + self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 627, 297)) + self.scrollArea.setWidget(self.scrollAreaWidgetContents) + + self.horizontalLayout_6.addWidget(self.scrollArea, 0, Qt.AlignTop) + + self.spinBox_configNumber = QSpinBox(self.groupBox_scConfig) + self.spinBox_configNumber.setObjectName(u"spinBox_configNumber") + self.spinBox_configNumber.setGeometry(QRect(150, -1, 48, 21)) + self.spinBox_configNumber.setMinimum(1) + self.groupBox_masterConfig = QGroupBox(self.tab) + self.groupBox_masterConfig.setObjectName(u"groupBox_masterConfig") + self.groupBox_masterConfig.setGeometry(QRect(10, 60, 631, 181)) + self.horizontalLayoutWidget_5 = QWidget(self.groupBox_masterConfig) + self.horizontalLayoutWidget_5.setObjectName(u"horizontalLayoutWidget_5") + self.horizontalLayoutWidget_5.setGeometry(QRect(0, 20, 631, 161)) + self.horizontalLayout_5 = QHBoxLayout(self.horizontalLayoutWidget_5) + self.horizontalLayout_5.setObjectName(u"horizontalLayout_5") + self.horizontalLayout_5.setContentsMargins(0, 0, 0, 0) + self.textEdit_masterConfig = QTextEdit(self.horizontalLayoutWidget_5) + self.textEdit_masterConfig.setObjectName(u"textEdit_masterConfig") + self.textEdit_masterConfig.setReadOnly(False) + + self.horizontalLayout_5.addWidget(self.textEdit_masterConfig) + + self.pushButton_save = QPushButton(self.tab) + self.pushButton_save.setObjectName(u"pushButton_save") + self.pushButton_save.setGeometry(QRect(220, 590, 94, 26)) + self.pushButton_saveAs = QPushButton(self.tab) + self.pushButton_saveAs.setObjectName(u"pushButton_saveAs") + self.pushButton_saveAs.setGeometry(QRect(330, 590, 94, 26)) + self.tabWidget.addTab(self.tab, "") + self.tab_2 = QWidget() + self.tab_2.setObjectName(u"tab_2") + self.groupBox_8 = QGroupBox(self.tab_2) + self.groupBox_8.setObjectName(u"groupBox_8") + self.groupBox_8.setGeometry(QRect(10, 120, 631, 491)) + self.groupBox_8.setAlignment(Qt.AlignCenter) + self.groupBox_8.setFlat(False) + self.groupBox_8.setCheckable(False) + self.textEdit_buildConsole = QTextEdit(self.groupBox_8) + self.textEdit_buildConsole.setObjectName(u"textEdit_buildConsole") + self.textEdit_buildConsole.setGeometry(QRect(0, 20, 631, 471)) + palette1 = QPalette() + palette1.setBrush(QPalette.Active, QPalette.Text, brush1) + palette1.setBrush(QPalette.Active, QPalette.Base, brush) + palette1.setBrush(QPalette.Inactive, QPalette.Text, brush1) + palette1.setBrush(QPalette.Inactive, QPalette.Base, brush) + self.textEdit_buildConsole.setPalette(palette1) + self.textEdit_buildConsole.setReadOnly(True) + self.frame_2 = QFrame(self.tab_2) + self.frame_2.setObjectName(u"frame_2") + self.frame_2.setGeometry(QRect(110, 20, 421, 81)) + self.frame_2.setFrameShape(QFrame.StyledPanel) + self.frame_2.setFrameShadow(QFrame.Raised) + self.frame = QFrame(self.frame_2) + self.frame.setObjectName(u"frame") + self.frame.setGeometry(QRect(79, 0, 341, 80)) + self.frame.setFrameShape(QFrame.StyledPanel) + self.frame.setFrameShadow(QFrame.Raised) + self.gridLayoutWidget_2 = QWidget(self.frame) + self.gridLayoutWidget_2.setObjectName(u"gridLayoutWidget_2") + self.gridLayoutWidget_2.setGeometry(QRect(0, 0, 340, 80)) + self.gridLayout_buildCleanButtons = QGridLayout(self.gridLayoutWidget_2) + self.gridLayout_buildCleanButtons.setObjectName(u"gridLayout_buildCleanButtons") + self.gridLayout_buildCleanButtons.setContentsMargins(0, 0, 0, 0) + self.pushButton_fswClean = QPushButton(self.gridLayoutWidget_2) + self.pushButton_fswClean.setObjectName(u"pushButton_fswClean") + + self.gridLayout_buildCleanButtons.addWidget(self.pushButton_fswClean, 1, 1, 1, 1) + + self.pushButton_fswBuild = QPushButton(self.gridLayoutWidget_2) + self.pushButton_fswBuild.setObjectName(u"pushButton_fswBuild") + + self.gridLayout_buildCleanButtons.addWidget(self.pushButton_fswBuild, 0, 1, 1, 1) + + self.pushButton_gswClean = QPushButton(self.gridLayoutWidget_2) + self.pushButton_gswClean.setObjectName(u"pushButton_gswClean") + + self.gridLayout_buildCleanButtons.addWidget(self.pushButton_gswClean, 1, 2, 1, 1) + + self.pushButton_simClean = QPushButton(self.gridLayoutWidget_2) + self.pushButton_simClean.setObjectName(u"pushButton_simClean") + + self.gridLayout_buildCleanButtons.addWidget(self.pushButton_simClean, 1, 0, 1, 1) + + self.pushButton_simBuild = QPushButton(self.gridLayoutWidget_2) + self.pushButton_simBuild.setObjectName(u"pushButton_simBuild") + + self.gridLayout_buildCleanButtons.addWidget(self.pushButton_simBuild, 0, 0, 1, 1) + + self.pushButton_gswBuild = QPushButton(self.gridLayoutWidget_2) + self.pushButton_gswBuild.setObjectName(u"pushButton_gswBuild") + + self.gridLayout_buildCleanButtons.addWidget(self.pushButton_gswBuild, 0, 2, 1, 1) + + self.pushButton_buildAll = QPushButton(self.gridLayoutWidget_2) + self.pushButton_buildAll.setObjectName(u"pushButton_buildAll") + + self.gridLayout_buildCleanButtons.addWidget(self.pushButton_buildAll, 0, 3, 1, 1) + + self.pushButton_cleanAll = QPushButton(self.gridLayoutWidget_2) + self.pushButton_cleanAll.setObjectName(u"pushButton_cleanAll") + + self.gridLayout_buildCleanButtons.addWidget(self.pushButton_cleanAll, 1, 3, 1, 1) + + self.frame_3 = QFrame(self.frame_2) + self.frame_3.setObjectName(u"frame_3") + self.frame_3.setGeometry(QRect(0, 0, 81, 41)) + self.frame_3.setFrameShape(QFrame.StyledPanel) + self.frame_3.setFrameShadow(QFrame.Raised) + self.verticalLayoutWidget = QWidget(self.frame_3) + self.verticalLayoutWidget.setObjectName(u"verticalLayoutWidget") + self.verticalLayoutWidget.setGeometry(QRect(0, 0, 81, 41)) + self.verticalLayout = QVBoxLayout(self.verticalLayoutWidget) + self.verticalLayout.setObjectName(u"verticalLayout") + self.verticalLayout.setContentsMargins(0, 0, 0, 0) + self.label_4 = QLabel(self.verticalLayoutWidget) + self.label_4.setObjectName(u"label_4") + self.label_4.setAlignment(Qt.AlignCenter) + + self.verticalLayout.addWidget(self.label_4) + + self.frame_4 = QFrame(self.frame_2) + self.frame_4.setObjectName(u"frame_4") + self.frame_4.setGeometry(QRect(0, 40, 81, 41)) + self.frame_4.setFrameShape(QFrame.StyledPanel) + self.frame_4.setFrameShadow(QFrame.Raised) + self.verticalLayoutWidget_2 = QWidget(self.frame_4) + self.verticalLayoutWidget_2.setObjectName(u"verticalLayoutWidget_2") + self.verticalLayoutWidget_2.setGeometry(QRect(0, 0, 81, 41)) + self.verticalLayout_2 = QVBoxLayout(self.verticalLayoutWidget_2) + self.verticalLayout_2.setObjectName(u"verticalLayout_2") + self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) + self.label_5 = QLabel(self.verticalLayoutWidget_2) + self.label_5.setObjectName(u"label_5") + self.label_5.setAlignment(Qt.AlignCenter) + + self.verticalLayout_2.addWidget(self.label_5) + + self.tabWidget.addTab(self.tab_2, "") + self.tab_3 = QWidget() + self.tab_3.setObjectName(u"tab_3") + self.groupBox_control = QGroupBox(self.tab_3) + self.groupBox_control.setObjectName(u"groupBox_control") + self.groupBox_control.setGeometry(QRect(10, 10, 631, 611)) + self.horizontalLayoutWidget = QWidget(self.groupBox_control) + self.horizontalLayoutWidget.setObjectName(u"horizontalLayoutWidget") + self.horizontalLayoutWidget.setGeometry(QRect(10, 570, 611, 31)) + self.horizontalLayout = QHBoxLayout(self.horizontalLayoutWidget) + self.horizontalLayout.setSpacing(45) + self.horizontalLayout.setObjectName(u"horizontalLayout") + self.horizontalLayout.setContentsMargins(0, 0, 0, 0) + self.pushButton_play = QPushButton(self.horizontalLayoutWidget) + self.pushButton_play.setObjectName(u"pushButton_play") + icon = QIcon() + icon.addFile(u"../../../../../../../../usr/share/icons/Humanity/actions/24/gtk-media-play-ltr.svg", QSize(), QIcon.Normal, QIcon.Off) + self.pushButton_play.setIcon(icon) + + self.horizontalLayout.addWidget(self.pushButton_play) + + self.pushButton_pause = QPushButton(self.horizontalLayoutWidget) + self.pushButton_pause.setObjectName(u"pushButton_pause") + icon1 = QIcon() + icon1.addFile(u"../../../../../../../../usr/share/icons/Humanity/actions/24/media-playback-pause.svg", QSize(), QIcon.Normal, QIcon.Off) + self.pushButton_pause.setIcon(icon1) + + self.horizontalLayout.addWidget(self.pushButton_pause) + + self.horizontalLayoutWidget_2 = QWidget(self.groupBox_control) + self.horizontalLayoutWidget_2.setObjectName(u"horizontalLayoutWidget_2") + self.horizontalLayoutWidget_2.setGeometry(QRect(10, 10, 611, 41)) + self.horizontalLayout_2 = QHBoxLayout(self.horizontalLayoutWidget_2) + self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") + self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) + self.pushButton_launch = QPushButton(self.horizontalLayoutWidget_2) + self.pushButton_launch.setObjectName(u"pushButton_launch") + + self.horizontalLayout_2.addWidget(self.pushButton_launch) + + self.pushButton_stop = QPushButton(self.horizontalLayoutWidget_2) + self.pushButton_stop.setObjectName(u"pushButton_stop") + + self.horizontalLayout_2.addWidget(self.pushButton_stop) + + self.groupBox_9 = QGroupBox(self.groupBox_control) + self.groupBox_9.setObjectName(u"groupBox_9") + self.groupBox_9.setGeometry(QRect(10, 60, 611, 451)) + self.groupBox_9.setAlignment(Qt.AlignCenter) + self.groupBox_9.setFlat(False) + self.groupBox_9.setCheckable(False) + self.textEdit_launchConsole = QTextEdit(self.groupBox_9) + self.textEdit_launchConsole.setObjectName(u"textEdit_launchConsole") + self.textEdit_launchConsole.setGeometry(QRect(0, 20, 611, 431)) + palette2 = QPalette() + palette2.setBrush(QPalette.Active, QPalette.Text, brush1) + palette2.setBrush(QPalette.Active, QPalette.Base, brush) + palette2.setBrush(QPalette.Inactive, QPalette.Text, brush1) + palette2.setBrush(QPalette.Inactive, QPalette.Base, brush) + self.textEdit_launchConsole.setPalette(palette2) + self.textEdit_launchConsole.setReadOnly(True) + self.horizontalLayoutWidget_3 = QWidget(self.groupBox_control) + self.horizontalLayoutWidget_3.setObjectName(u"horizontalLayoutWidget_3") + self.horizontalLayoutWidget_3.setGeometry(QRect(190, 520, 261, 41)) + self.horizontalLayout_runForUntil = QHBoxLayout(self.horizontalLayoutWidget_3) + self.horizontalLayout_runForUntil.setObjectName(u"horizontalLayout_runForUntil") + self.horizontalLayout_runForUntil.setContentsMargins(0, 0, 0, 0) + self.comboBox_run = QComboBox(self.horizontalLayoutWidget_3) + self.comboBox_run.addItem("") + self.comboBox_run.addItem("") + self.comboBox_run.addItem("") + self.comboBox_run.setObjectName(u"comboBox_run") + + self.horizontalLayout_runForUntil.addWidget(self.comboBox_run) + + self.lineEdit_secondsEntry = QLineEdit(self.horizontalLayoutWidget_3) + self.lineEdit_secondsEntry.setObjectName(u"lineEdit_secondsEntry") + self.lineEdit_secondsEntry.setAlignment(Qt.AlignCenter) + + self.horizontalLayout_runForUntil.addWidget(self.lineEdit_secondsEntry) + + self.tabWidget.addTab(self.tab_3, "") + + self.retranslateUi(Form) + + self.tabWidget.setCurrentIndex(0) + + + QMetaObject.connectSlotsByName(Form) + # setupUi + + def retranslateUi(self, Form): + Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None)) + self.lineEdit_curConfig.setPlaceholderText(QCoreApplication.translate("Form", u"None", None)) + self.pushButton_browse.setText(QCoreApplication.translate("Form", u"Browse...", None)) + self.label_curConfig.setText(QCoreApplication.translate("Form", u"Current Config:", None)) + self.groupBox_scConfig.setTitle(QCoreApplication.translate("Form", u"Spacecraft Config", None)) + self.groupBox_masterConfig.setTitle(QCoreApplication.translate("Form", u"Master Config", None)) + self.pushButton_save.setText(QCoreApplication.translate("Form", u"Save", None)) + self.pushButton_saveAs.setText(QCoreApplication.translate("Form", u"Save As...", None)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), QCoreApplication.translate("Form", u"Config", None)) + self.groupBox_8.setTitle(QCoreApplication.translate("Form", u"Console Output", None)) + self.pushButton_fswClean.setText(QCoreApplication.translate("Form", u"FSW", None)) + self.pushButton_fswBuild.setText(QCoreApplication.translate("Form", u"FSW", None)) + self.pushButton_gswClean.setText(QCoreApplication.translate("Form", u"GSW", None)) + self.pushButton_simClean.setText(QCoreApplication.translate("Form", u"SIM", None)) + self.pushButton_simBuild.setText(QCoreApplication.translate("Form", u"SIM", None)) + self.pushButton_gswBuild.setText(QCoreApplication.translate("Form", u"GSW", None)) + self.pushButton_buildAll.setText(QCoreApplication.translate("Form", u"All", None)) + self.pushButton_cleanAll.setText(QCoreApplication.translate("Form", u"All", None)) + self.label_4.setText(QCoreApplication.translate("Form", u"Build", None)) + self.label_5.setText(QCoreApplication.translate("Form", u"Clean", None)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), QCoreApplication.translate("Form", u"Build", None)) + self.groupBox_control.setTitle("") + self.pushButton_play.setText(QCoreApplication.translate("Form", u"Play", None)) + self.pushButton_pause.setText(QCoreApplication.translate("Form", u"Pause", None)) + self.pushButton_launch.setText(QCoreApplication.translate("Form", u"Launch", None)) + self.pushButton_stop.setText(QCoreApplication.translate("Form", u"Stop", None)) + self.groupBox_9.setTitle(QCoreApplication.translate("Form", u"NOS3 Time Driver", None)) + self.comboBox_run.setItemText(0, "") + self.comboBox_run.setItemText(1, QCoreApplication.translate("Form", u"Run For", None)) + self.comboBox_run.setItemText(2, QCoreApplication.translate("Form", u"Run Until", None)) + + self.lineEdit_secondsEntry.setText("") + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), QCoreApplication.translate("Form", u"Launch", None)) + # retranslateUi + From cd716bc4654d382bcdef5c3b3b4d94c75428ec05 Mon Sep 17 00:00:00 2001 From: "Lucas, John P" Date: Fri, 15 Mar 2024 09:24:40 -0400 Subject: [PATCH 02/43] [nasa/nos3#202] Updates to include CryptoLib in FSW and CryptoLib Standalone for GSW - errors getting data back from FSW; --- Makefile | 8 +++ cfg/nos3_defs/cpu1_cfe_es_startup.scr | 2 +- cfg/nos3_defs/targets.cmake | 2 +- cfg/nos3_defs/toolchain-amd64-linux-gnu.cmake | 4 +- cfg/sims/nos3-simulator.xml | 59 ++++++++++--------- components/cryptolib | 2 +- gsw/cosmos | 2 +- scripts/docker_build_cryptolib.sh | 31 ++++++++++ scripts/docker_launch.sh | 3 +- scripts/env.sh | 2 +- 10 files changed, 77 insertions(+), 38 deletions(-) create mode 100644 scripts/docker_build_cryptolib.sh diff --git a/Makefile b/Makefile index 0852ced2..341c1790 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ BUILDTYPE ?= debug INSTALLPREFIX ?= exe FSWBUILDDIR ?= $(CURDIR)/fsw/build +GSWBUILDDIR ?= $(CURDIR)/gsw/build SIMBUILDDIR ?= $(CURDIR)/sims/build export CFS_APP_PATH = ../components @@ -45,6 +46,11 @@ all: $(MAKE) sim $(MAKE) gsw +build-cryptolib: + mkdir -p $(GSWBUILDDIR) + cd $(GSWBUILDDIR) && cmake $(PREP_OPTS) -DSUPPORT=1 ../../components/cryptolib + $(MAKE) --no-print-directory -C $(GSWBUILDDIR) + build-fsw: mkdir -p $(FSWBUILDDIR) cd $(FSWBUILDDIR) && cmake $(PREP_OPTS) ../cfe @@ -72,6 +78,7 @@ clean-sim: rm -rf sims/build clean-gsw: + rm -rf gsw/build rm -rf gsw/cosmos/build config: @@ -84,6 +91,7 @@ fsw: ./scripts/docker_build_fsw.sh gsw: + ./scripts/docker_build_cryptolib.sh ./cfg/build/gsw_build.sh launch: diff --git a/cfg/nos3_defs/cpu1_cfe_es_startup.scr b/cfg/nos3_defs/cpu1_cfe_es_startup.scr index 324d5265..2b1520f7 100644 --- a/cfg/nos3_defs/cpu1_cfe_es_startup.scr +++ b/cfg/nos3_defs/cpu1_cfe_es_startup.scr @@ -1,5 +1,6 @@ CFE_LIB, hwlib, hwlib_Init, HW_LIB, 0, 0, 0x0, 0; CFE_LIB, io_lib, IO_LibInit, IO_LIB, 0, 0, 0x0, 0; +CFE_LIB, crypto, Crypto_TC_Init, CRYPTO, 0, 0, 0x0, 0; CFE_APP, sch, SCH_AppMain, SCH, 40, 16384, 0x0, 0; CFE_APP, ci, CI_AppMain, CI, 41, 16384, 0x0, 0; @@ -33,7 +34,6 @@ CFE_APP, generic_st, ST_AppMain, ST, CFE_APP, syn, SYN_AppMain, SYN, 72, 32768, 0x0, 0; CFE_LIB, cfs_lib, CFS_LibInit, CFS_LIB, 0, 0, 0x0, 0; -CFE_LIB, crypto, Crypto_TC_Init, CRYPTO, 0, 0, 0x0, 0; CFE_APP, cs, CS_AppMain, CS, 55, 16384, 0x0, 0; CFE_APP, hk, HK_AppMain, HK, 63, 16384, 0x0, 0; CFE_APP, hs, HS_AppMain, HS, 56, 16384, 0x0, 0; diff --git a/cfg/nos3_defs/targets.cmake b/cfg/nos3_defs/targets.cmake index ddc442dd..4f4b8e76 100644 --- a/cfg/nos3_defs/targets.cmake +++ b/cfg/nos3_defs/targets.cmake @@ -84,7 +84,7 @@ list(APPEND MISSION_GLOBAL_APPLIST # # Libraries # - #cryptolib + cryptolib hwlib io_lib # diff --git a/cfg/nos3_defs/toolchain-amd64-linux-gnu.cmake b/cfg/nos3_defs/toolchain-amd64-linux-gnu.cmake index a062e7fc..b0656fc0 100644 --- a/cfg/nos3_defs/toolchain-amd64-linux-gnu.cmake +++ b/cfg/nos3_defs/toolchain-amd64-linux-gnu.cmake @@ -30,5 +30,5 @@ add_definitions(-DBYTE_ORDER_LE) add_definitions(-D_LINUX_OS_) set(CMAKE_POSITION_INDEPENDENT_CODE ON) -set(CI_TRANSPORT udp) -set(TO_TRANSPORT udp) +set(CI_TRANSPORT udp_tf) +set(TO_TRANSPORT udp_tf) diff --git a/cfg/sims/nos3-simulator.xml b/cfg/sims/nos3-simulator.xml index 4c0b08dd..2cd2e2f1 100644 --- a/cfg/sims/nos3-simulator.xml +++ b/cfg/sims/nos3-simulator.xml @@ -134,8 +134,8 @@ SAMPLE_42_PROVIDER fortytwo 4242 - 20 - 5 + 30 + 1 0 @@ -158,8 +158,8 @@ GPS42SOCKET fortytwo 4245 - 20 - 5 + 30 + 1 0 0 37 @@ -218,8 +218,8 @@ GENERIC_EPS_42_PROVIDER fortytwo 4283 - 20 - 5 + 30 + 1 0 @@ -299,8 +299,8 @@ fortytwo 4277 4278 - 20 - 5 + 30 + 1 0 0 @@ -325,8 +325,8 @@ fortytwo 4377 4378 - 20 - 5 + 30 + 1 0 1 @@ -351,8 +351,8 @@ fortytwo 4477 4478 - 20 - 5 + 30 + 1 0 2 @@ -376,8 +376,8 @@ GENERIC_CSS_42_PROVIDER fortytwo 4227 - 20 - 5 + 30 + 1 0 <42-css-scale-factor>1.0 @@ -405,8 +405,8 @@ GENERIC_TORQUER_42_PROVIDER fortytwo 4279 - 20 - 5 + 30 + 1 3 @@ -437,8 +437,8 @@ TRUTH42PROVIDER fortytwo 9999 - 20 - 5 + 30 + 1 0 0 @@ -462,8 +462,8 @@ GENERIC_FSS_42_PROVIDER fortytwo 4281 - 20 - 5 + 30 + 1 0 @@ -495,15 +495,16 @@ gsw - cosmos + - prox @@ -540,8 +541,8 @@ GENERIC_IMU_42_PROVIDER fortytwo 4280 - 20 - 5 + 30 + 1 0 @@ -567,8 +568,8 @@ GENERIC_MAG_42_PROVIDER fortytwo 4234 - 20 - 5 + 30 + 1 0 @@ -594,8 +595,8 @@ GENERIC_STAR_TRACKER_42_PROVIDER fortytwo 4282 - 20 - 5 + 30 + 1 0 0 diff --git a/components/cryptolib b/components/cryptolib index 9fe8788c..174e896c 160000 --- a/components/cryptolib +++ b/components/cryptolib @@ -1 +1 @@ -Subproject commit 9fe8788cb487e5d883f4c8db68b607197cdc089f +Subproject commit 174e896c0f1587f4b4a0b0ed21d567a6ba2496d6 diff --git a/gsw/cosmos b/gsw/cosmos index 3b229c95..979bbf4d 160000 --- a/gsw/cosmos +++ b/gsw/cosmos @@ -1 +1 @@ -Subproject commit 3b229c951b122320c7b0ac6618cc9e7c25ba3c07 +Subproject commit 979bbf4d73e60e4a69ef310b6567524bae78d2a7 diff --git a/scripts/docker_build_cryptolib.sh b/scripts/docker_build_cryptolib.sh new file mode 100644 index 00000000..63126da2 --- /dev/null +++ b/scripts/docker_build_cryptolib.sh @@ -0,0 +1,31 @@ +#!/bin/bash -i +# +# Convenience script for NOS3 development +# Use with the Dockerfile in the deployment repository +# https://github.com/nasa-itc/deployment +# + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +source $SCRIPT_DIR/env.sh + +# Check that local NOS3 directory exists +if [ ! -d $USER_NOS3_DIR ]; then + echo "" + echo " Need to run make prep first!" + echo "" + exit 1 +fi + +# Check that configure build directory exists +if [ ! -d $BASE_DIR/cfg/build ]; then + echo "" + echo " Need to run make config first!" + echo "" + exit 1 +fi + +# Make ground software build directory +mkdir -p $BASE_DIR/gsw/build + +# Build +$DFLAGS_CPUS -v $BASE_DIR:$BASE_DIR --name "nos_build_cryptolib" -w $BASE_DIR $DBOX make -j$NUM_CPUS build-cryptolib diff --git a/scripts/docker_launch.sh b/scripts/docker_launch.sh index 5243c866..1eb1eb05 100755 --- a/scripts/docker_launch.sh +++ b/scripts/docker_launch.sh @@ -127,8 +127,7 @@ do gnome-terminal --tab --title=$SC_NUM" - StarTrk Sim" -- $DFLAGS -v $SIM_DIR:$SIM_DIR --name $SC_NUM"_startrk_sim" --network=$SC_NETNAME -w $SIM_BIN $DBOX ./nos3-single-simulator $SC_CFG_FILE generic_star_tracker_sim gnome-terminal --tab --title=$SC_NUM" - Torquer Sim" -- $DFLAGS -v $SIM_DIR:$SIM_DIR --name $SC_NUM"_torquer_sim" --network=$SC_NETNAME -w $SIM_BIN $DBOX ./nos3-single-simulator $SC_CFG_FILE generic_torquer_sim - # Note: Can keep open if desired after a new gnome-profile is manually created - #gnome-terminal --window-with-profile=KeepOpen --tab --title=$SC_NUM" - Sample Sim" -- $DFLAGS -v $SIM_DIR:$SIM_DIR --name $SC_NUM"_sample_sim" --network=$SC_NETNAME -w $SIM_BIN $DBOX ./nos3-single-simulator $SC_CFG_FILE sample_sim + gnome-terminal --window-with-profile=KeepOpen --title=$SC_NUM" - CryptoLib" -- $DFLAGS -v $BASE_DIR:$BASE_DIR --name $SC_NUM"_cryptolib" --network=$SC_NETNAME --network-alias=cryptolib -w $BASE_DIR/gsw/build $DBOX ./support/standalone echo "" done diff --git a/scripts/env.sh b/scripts/env.sh index 0fb42b28..6bf3791a 100755 --- a/scripts/env.sh +++ b/scripts/env.sh @@ -40,7 +40,7 @@ OPENC3_PATH=$OPENC3_DIR/openc3.sh DNETWORK="docker network" #fi -DBOX="ivvitc/nos3-64:20240306" +DBOX="ivvitc/nos3-64:dev" # Debugging #echo "Script directory = " $SCRIPT_DIR From b4d2f4dd01f5676b0a87bfdc26abc201b55a450c Mon Sep 17 00:00:00 2001 From: "Lucas, John P" Date: Wed, 27 Mar 2024 08:49:01 -0400 Subject: [PATCH 03/43] [nasa/nos3#202] TC clear and encrypted working again after submodule updates, disabled debug CI prints; --- components/cryptolib | 2 +- fsw/apps/ci | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/cryptolib b/components/cryptolib index 174e896c..56e99b94 160000 --- a/components/cryptolib +++ b/components/cryptolib @@ -1 +1 @@ -Subproject commit 174e896c0f1587f4b4a0b0ed21d567a6ba2496d6 +Subproject commit 56e99b94ed9eba44b812a0b1279b584641fe1c5a diff --git a/fsw/apps/ci b/fsw/apps/ci index 10c30a77..153db705 160000 --- a/fsw/apps/ci +++ b/fsw/apps/ci @@ -1 +1 @@ -Subproject commit 10c30a772c718bc38f3f522fcab137135603e61c +Subproject commit 153db705202edec3391c7b0dab11be0cd9cb128d From e90175c8ee3bbafc8a6635c752f078f4a1c23a4e Mon Sep 17 00:00:00 2001 From: "Lucas, John P" Date: Thu, 28 Mar 2024 10:54:40 -0400 Subject: [PATCH 04/43] [nasa/nos3#202] Updates in attempt to get both TC and TM initialized - failing still; --- cfg/nos3_defs/cpu1_cfe_es_startup.scr | 2 +- components/cryptolib | 2 +- fsw/apps/io_lib | 2 +- fsw/apps/to | 2 +- gsw/cosmos | 2 +- scripts/docker_launch.sh | 7 ++++--- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/cfg/nos3_defs/cpu1_cfe_es_startup.scr b/cfg/nos3_defs/cpu1_cfe_es_startup.scr index 2b1520f7..7a21cddb 100644 --- a/cfg/nos3_defs/cpu1_cfe_es_startup.scr +++ b/cfg/nos3_defs/cpu1_cfe_es_startup.scr @@ -1,6 +1,6 @@ +CFE_LIB, crypto, Crypto_SC_Init, CRYPTO, 0, 0, 0x0, 0; CFE_LIB, hwlib, hwlib_Init, HW_LIB, 0, 0, 0x0, 0; CFE_LIB, io_lib, IO_LibInit, IO_LIB, 0, 0, 0x0, 0; -CFE_LIB, crypto, Crypto_TC_Init, CRYPTO, 0, 0, 0x0, 0; CFE_APP, sch, SCH_AppMain, SCH, 40, 16384, 0x0, 0; CFE_APP, ci, CI_AppMain, CI, 41, 16384, 0x0, 0; diff --git a/components/cryptolib b/components/cryptolib index 56e99b94..a344ffdd 160000 --- a/components/cryptolib +++ b/components/cryptolib @@ -1 +1 @@ -Subproject commit 56e99b94ed9eba44b812a0b1279b584641fe1c5a +Subproject commit a344ffdd75c3275c756688da38fa73e303d1e518 diff --git a/fsw/apps/io_lib b/fsw/apps/io_lib index 31a03167..92cf7e1e 160000 --- a/fsw/apps/io_lib +++ b/fsw/apps/io_lib @@ -1 +1 @@ -Subproject commit 31a0316787506cc664c4771f4f61cf6030351afb +Subproject commit 92cf7e1eed42205365992c3a988438f0a8051b77 diff --git a/fsw/apps/to b/fsw/apps/to index 42bf2418..28aaae22 160000 --- a/fsw/apps/to +++ b/fsw/apps/to @@ -1 +1 @@ -Subproject commit 42bf24186615e524a188efb7c067f34e44e75171 +Subproject commit 28aaae22688c442cdb15de99dd9670ff4eae1108 diff --git a/gsw/cosmos b/gsw/cosmos index 979bbf4d..5daf14f4 160000 --- a/gsw/cosmos +++ b/gsw/cosmos @@ -1 +1 @@ -Subproject commit 979bbf4d73e60e4a69ef310b6567524bae78d2a7 +Subproject commit 5daf14f454f5da88b9d7b4b6599378526b78b931 diff --git a/scripts/docker_launch.sh b/scripts/docker_launch.sh index 1eb1eb05..a5a8cc19 100755 --- a/scripts/docker_launch.sh +++ b/scripts/docker_launch.sh @@ -85,6 +85,10 @@ do $DNETWORK connect $SC_NETNAME cosmos_openc3-operator_1 --alias cosmos echo "" + echo $SC_NUM " - CryptoLib..." + gnome-terminal --window-with-profile=KeepOpen --title=$SC_NUM" - CryptoLib" -- $DFLAGS -v $BASE_DIR:$BASE_DIR --name $SC_NUM"_cryptolib" --network=$SC_NETNAME --network-alias=cryptolib -w $BASE_DIR/gsw/build $DBOX ./support/standalone + echo "" + echo $SC_NUM " - 42..." rm -rf $USER_NOS3_DIR/42/NOS3InOut cp -r $BASE_DIR/cfg/build/InOut $USER_NOS3_DIR/42/NOS3InOut @@ -126,9 +130,6 @@ do gnome-terminal --tab --title=$SC_NUM" - Sample Sim" -- $DFLAGS -v $SIM_DIR:$SIM_DIR --name $SC_NUM"_sample_sim" --network=$SC_NETNAME -w $SIM_BIN $DBOX ./nos3-single-simulator $SC_CFG_FILE sample_sim gnome-terminal --tab --title=$SC_NUM" - StarTrk Sim" -- $DFLAGS -v $SIM_DIR:$SIM_DIR --name $SC_NUM"_startrk_sim" --network=$SC_NETNAME -w $SIM_BIN $DBOX ./nos3-single-simulator $SC_CFG_FILE generic_star_tracker_sim gnome-terminal --tab --title=$SC_NUM" - Torquer Sim" -- $DFLAGS -v $SIM_DIR:$SIM_DIR --name $SC_NUM"_torquer_sim" --network=$SC_NETNAME -w $SIM_BIN $DBOX ./nos3-single-simulator $SC_CFG_FILE generic_torquer_sim - - gnome-terminal --window-with-profile=KeepOpen --title=$SC_NUM" - CryptoLib" -- $DFLAGS -v $BASE_DIR:$BASE_DIR --name $SC_NUM"_cryptolib" --network=$SC_NETNAME --network-alias=cryptolib -w $BASE_DIR/gsw/build $DBOX ./support/standalone - echo "" done echo "NOS Time Driver..." From 35c8c9fdf7d32050e264bd0309b5486e45353650 Mon Sep 17 00:00:00 2001 From: "Lucas, John P" Date: Tue, 2 Apr 2024 15:52:51 -0400 Subject: [PATCH 05/43] [nos3#202] Debugging TM clear mode now; --- components/cryptolib | 2 +- fsw/apps/io_lib | 2 +- fsw/apps/to | 2 +- gsw/cosmos | 2 +- scripts/docker_launch.sh | 9 +++++---- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/components/cryptolib b/components/cryptolib index a344ffdd..30e457d6 160000 --- a/components/cryptolib +++ b/components/cryptolib @@ -1 +1 @@ -Subproject commit a344ffdd75c3275c756688da38fa73e303d1e518 +Subproject commit 30e457d62bdbc561f622a8d620121e4fd556af30 diff --git a/fsw/apps/io_lib b/fsw/apps/io_lib index 92cf7e1e..f9c841b4 160000 --- a/fsw/apps/io_lib +++ b/fsw/apps/io_lib @@ -1 +1 @@ -Subproject commit 92cf7e1eed42205365992c3a988438f0a8051b77 +Subproject commit f9c841b42dec4b485cb02dbf5a682e974461606c diff --git a/fsw/apps/to b/fsw/apps/to index 28aaae22..5741c305 160000 --- a/fsw/apps/to +++ b/fsw/apps/to @@ -1 +1 @@ -Subproject commit 28aaae22688c442cdb15de99dd9670ff4eae1108 +Subproject commit 5741c3051b3ee0ccea0526bba7482d7cefe10a5f diff --git a/gsw/cosmos b/gsw/cosmos index 5daf14f4..fd47c51d 160000 --- a/gsw/cosmos +++ b/gsw/cosmos @@ -1 +1 @@ -Subproject commit 5daf14f454f5da88b9d7b4b6599378526b78b931 +Subproject commit fd47c51da784167318bee3a2bc8533ab11628a03 diff --git a/scripts/docker_launch.sh b/scripts/docker_launch.sh index a5a8cc19..38149b8a 100755 --- a/scripts/docker_launch.sh +++ b/scripts/docker_launch.sh @@ -85,10 +85,6 @@ do $DNETWORK connect $SC_NETNAME cosmos_openc3-operator_1 --alias cosmos echo "" - echo $SC_NUM " - CryptoLib..." - gnome-terminal --window-with-profile=KeepOpen --title=$SC_NUM" - CryptoLib" -- $DFLAGS -v $BASE_DIR:$BASE_DIR --name $SC_NUM"_cryptolib" --network=$SC_NETNAME --network-alias=cryptolib -w $BASE_DIR/gsw/build $DBOX ./support/standalone - echo "" - echo $SC_NUM " - 42..." rm -rf $USER_NOS3_DIR/42/NOS3InOut cp -r $BASE_DIR/cfg/build/InOut $USER_NOS3_DIR/42/NOS3InOut @@ -105,6 +101,10 @@ do # Debugging # Replace `--tab` with `--window-with-profile=KeepOpen` once you've created this gnome-terminal profile manually + echo $SC_NUM " - CryptoLib..." + gnome-terminal --window-with-profile=KeepOpen --title=$SC_NUM" - CryptoLib" -- $DFLAGS -v $BASE_DIR:$BASE_DIR --name $SC_NUM"_cryptolib" --network=$SC_NETNAME --network-alias=cryptolib -w $BASE_DIR/gsw/build $DBOX ./support/standalone + echo "" + echo $SC_NUM " - Simulators..." cd $SIM_BIN gnome-terminal --tab --title=$SC_NUM" - NOS Engine Server" -- $DFLAGS -v $SIM_DIR:$SIM_DIR --name $SC_NUM"_nos_engine_server" -h nos_engine_server --network=$SC_NETNAME -w $SIM_BIN $DBOX /usr/bin/nos_engine_server_standalone -f $SIM_BIN/nos_engine_server_config.json @@ -130,6 +130,7 @@ do gnome-terminal --tab --title=$SC_NUM" - Sample Sim" -- $DFLAGS -v $SIM_DIR:$SIM_DIR --name $SC_NUM"_sample_sim" --network=$SC_NETNAME -w $SIM_BIN $DBOX ./nos3-single-simulator $SC_CFG_FILE sample_sim gnome-terminal --tab --title=$SC_NUM" - StarTrk Sim" -- $DFLAGS -v $SIM_DIR:$SIM_DIR --name $SC_NUM"_startrk_sim" --network=$SC_NETNAME -w $SIM_BIN $DBOX ./nos3-single-simulator $SC_CFG_FILE generic_star_tracker_sim gnome-terminal --tab --title=$SC_NUM" - Torquer Sim" -- $DFLAGS -v $SIM_DIR:$SIM_DIR --name $SC_NUM"_torquer_sim" --network=$SC_NETNAME -w $SIM_BIN $DBOX ./nos3-single-simulator $SC_CFG_FILE generic_torquer_sim + echo "" done echo "NOS Time Driver..." From 0e98f8543793a51d1089f7cf1c758a6ce0d938d2 Mon Sep 17 00:00:00 2001 From: Donnie-Ice Date: Thu, 4 Apr 2024 16:34:59 +0000 Subject: [PATCH 06/43] Added .gitignore file --- cfg/gui/.gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 cfg/gui/.gitignore diff --git a/cfg/gui/.gitignore b/cfg/gui/.gitignore new file mode 100644 index 00000000..cb8a7669 --- /dev/null +++ b/cfg/gui/.gitignore @@ -0,0 +1,3 @@ +*.ui +*.ui.* +__pycache__ \ No newline at end of file From 8263e9bc7db72939f620301375aef57ff8fe65c3 Mon Sep 17 00:00:00 2001 From: Ice Date: Thu, 11 Apr 2024 10:56:53 -0400 Subject: [PATCH 07/43] QOL and Bug Fixes --- cfg/gui/cfg_gui_main.py | 119 ++++++++++++++++++++++++++++++---------- 1 file changed, 89 insertions(+), 30 deletions(-) diff --git a/cfg/gui/cfg_gui_main.py b/cfg/gui/cfg_gui_main.py index c52c629e..67722e05 100644 --- a/cfg/gui/cfg_gui_main.py +++ b/cfg/gui/cfg_gui_main.py @@ -1,5 +1,5 @@ from pathlib import Path -from PySide6.QtWidgets import QWidget, QApplication, QFileDialog, QTextEdit, QPushButton, QDateTimeEdit, QLabel, QCheckBox, QVBoxLayout, QSizePolicy, QDoubleSpinBox, QLayout +from PySide6.QtWidgets import QWidget, QApplication, QFileDialog, QTextEdit, QPushButton, QDateTimeEdit, QLabel, QCheckBox, QVBoxLayout, QSizePolicy, QDoubleSpinBox, QLayout, QMessageBox from PySide6.QtCore import QProcess, QDateTime from PySide6.QtGui import QTextCharFormat @@ -8,6 +8,10 @@ from cfg_gui_ui import Ui_Form + +# TODO: Update master xml sc-x-cfg filename if modified in sc config (in progress) + + class cfg_gui(QWidget): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -17,8 +21,9 @@ def __init__(self, *args, **kwargs): self.setFixedSize(655, 655) self.setWindowTitle("NOS3 Igniter - Version 0.0.1") + # globals self.dateTimeEdit = QDateTimeEdit() - self.scConfigs = {} # Stores child configs {'filename' : "filetext"} + self.scConfigs = {} # Stores child configs {'index' : "filetext"} self.prevButtonPressed = None # Tracks the last button pressed, used in buttonColor() self.defaultStyleSheet = self.ui.pushButton_buildAll.styleSheet() # Saves default stylesheet to return button color to normal, buttonColor() self.setup = 0 # Allows for switchConfig() to initially be called without calling saveText() @@ -64,22 +69,32 @@ def run_ForUntil(self): self.dateTimeEdit.setMinimumDateTime(QDateTime(self.currentTime.year, self.currentTime.month, self.currentTime.day, self.currentTime.hour, self.currentTime.minute, self.currentTime.second, 0, 0)) self.ui.horizontalLayout_runForUntil.insertWidget(1, self.dateTimeEdit) - # Overwrites the currently saved text for the currently selected spacecraft config when edited + # Updates the currently saved xml dictionary (not actual xml file) for the currently selected spacecraft config when edited. TODO: change name of function def saveText(self, layout:QLayout, config_value:int): - child = self.scConfigs[config_value] - text = child + text = self.scConfigs[config_value] filename = text.split('\n')[0] - childXml = text.split('\n', 2)[2] - childXml = xmltodict.parse(childXml) + childXml = xmltodict.parse(text.split('\n', 2)[2]) + # TODO: change to dynamically pull apps/components from xml file or directory applications = ['cf', 'ds', 'fm', 'lc', 'sc'] components = ['adcs', 'cam', 'css', 'eps', 'fss', 'gps', 'imu', 'mag', 'radio', 'rw', 'sample', 'st', 'torquer'] i = 0 while layout.itemAt(i) != None: widget = layout.itemAt(i).widget() - if isinstance(widget, QCheckBox): - #print(child.text()) + + # Handle filename + if isinstance(widget, QTextEdit): + widget:QTextEdit + filename = widget.toPlainText() + if "Filename:" not in filename: + filename = f'Filename: {filename}' + if "Filename: " not in filename: + filename = f'Filename: {filename.split(":")[1]}' + + # Handle checkboxes + elif isinstance(widget, QCheckBox): + widget:QCheckBox text = widget.text().split(' ')[0] if text in applications: childXml['sc-1-config']['applications'][text]['enable'] = str(widget.isChecked()).lower() @@ -87,10 +102,14 @@ def saveText(self, layout:QLayout, config_value:int): childXml['sc-1-config']['components'][text]['enable'] = str(widget.isChecked()).lower() elif text == 'gui': childXml['sc-1-config'][text]['enable'] = str(widget.isChecked()).lower() + + # Handle orbits elif isinstance(widget, QDoubleSpinBox): - #print(child.prefix()) + widget:QDoubleSpinBox prefix = widget.prefix().split(' ')[0] childXml['sc-1-config']['orbit'][prefix] = str(widget.value()) + + # Increment index i += 1 combined = filename + '\n\n' + xmltodict.unparse(childXml) @@ -101,7 +120,7 @@ def saveXML(self, saveType:str): # saveType = "save" (overwrite) or "saveAs" (new) if saveType == "saveAs": - savePath, _ = QFileDialog.getSaveFileName(None, 'Directory', './cfg/custom') + savePath, _ = QFileDialog.getSaveFileName(None, 'Directory', './cfg') elif saveType == "save": savePath = self.config_path @@ -109,7 +128,7 @@ def saveXML(self, saveType:str): masterXml = xmltodict.parse(self.ui.textEdit_masterConfig.toPlainText()) self.convert2xml(masterXml, savePath) - # TODO: Now handle children, currently placeholder until checkboxes are added (update in progress) + # Now handle children self.saveText(self.layout_, self.ui.spinBox_configNumber.value()-1) for child in self.scConfigs: @@ -121,6 +140,8 @@ def saveXML(self, saveType:str): # save under same directory as masterXml using filename parsed from textEdit self.convert2xml(childXml, savePath.rsplit('/', 1)[0]+f'/{filename}') + self.reloadConfig(self.config_path) + # Loads the child config into the Spacecraft Config textbox def switchConfig(self, value:int): # value : index of spacecraft config in the order listed in the master XML @@ -130,8 +151,10 @@ def switchConfig(self, value:int): if self.setup == 1: self.saveText(self.layout_, self.configNumTrack) self.configNumTrack = value-1 + else: + self.setup = 1 - self.setup = 1 + # setup layout to add item to self.ui.scrollArea.setWidgetResizable(True) self.ui.scrollAreaWidgetContents.setLayout(QVBoxLayout().layout()) self.layout_ = self.ui.scrollAreaWidgetContents.layout() @@ -141,21 +164,22 @@ def switchConfig(self, value:int): while self.layout_.itemAt(0) != None: child = self.layout_.itemAt(0).widget().setParent(None) - + # Now parse the xml and convert to widgets value = value-1 if value in self.scConfigs: fileName = self.scConfigs[value].split('\n')[0] childXML = self.scConfigs[value].split('\n')[2::] childXML = ''.join(childXML) childDict = xmltodict.parse(childXML) - - policy = self.ui.scrollAreaWidgetContents.sizePolicy() + # child = sc-xxx-cfg for child in childDict: - configTag = QLabel() + configTag = QTextEdit() configTag.setText((fileName)) - configTag.setMinimumHeight(18) + configTag.setMinimumHeight(30) self.layout_.addWidget(configTag) + + # child2 = applications | components | gui | orbit for child2 in childDict[child]: tag = QLabel() tag.setText(child2.upper()+": ") @@ -164,7 +188,10 @@ def switchConfig(self, value:int): tag.setFont(format.font()) tag.setMinimumHeight(18) self.layout_.addWidget(tag) + if child2 in ['applications', 'components']: + + # child 3 = cf | ds | ... | adcs | cam | ... for child3 in childDict[child][child2]: enableTag = QCheckBox() enableTag.setText(child3 + " enable ") @@ -172,12 +199,16 @@ def switchConfig(self, value:int): enableTag.setMinimumHeight(18) enableTag.sizePolicy().setVerticalPolicy(QSizePolicy.Expanding) self.layout_.addWidget(enableTag) + elif child2 == 'gui': enableTag = QCheckBox() enableTag.setText(child2 + " enable ") enableTag.setChecked(childDict[child][child2]['enable'] == 'true') self.layout_.addWidget(enableTag) + elif child2 == 'orbit': + + # child3 = tipoff_x/y/z for child3 in childDict[child][child2]: orbitSpinBox = QDoubleSpinBox() orbitSpinBox.setMinimum(-99.00) @@ -186,12 +217,13 @@ def switchConfig(self, value:int): orbitSpinBox.setPrefix(f'{child3} = ') self.layout_.addWidget(orbitSpinBox) else: + # No SC configs in master XML file, or selected a SC XML as master tag = QLabel() tag.setText("*ERROR*\n\nMake sure you chose a master configuration file\n\n*ERROR*") self.layout_.addWidget(tag) # Converts a dictionary to XML file, saved under the given filename/path - def convert2xml(self, attrDict:dict, fileName:[str, Path]): + def convert2xml(self, attrDict:dict, fileName:str): # ensure file is saved as xml if fileName[-4::] != ".xml": fileName += ".xml" @@ -203,39 +235,63 @@ def convert2xml(self, attrDict:dict, fileName:[str, Path]): # Opens file selection menu and calls parseXML() on the selected file def browseConfig(self): + + # Clear SC Config window when selecting a new master config + if "layout_" in self.__dict__: + while self.layout_.itemAt(0) != None: + self.layout_.itemAt(0).widget().setParent(None) + self.config_path, _ = QFileDialog.getOpenFileName(None, 'File', './cfg', "XML Files [ *.xml ]") if self.config_path != "": self.config_name = self.config_path.split("/")[-1] self.ui.lineEdit_curConfig.setText(self.config_name) self.parseXml(self.config_path) + # Reloads the whole config after clicking save, allows you to change "sc-x-cfg" xml file + def reloadConfig(self, config_path): + if "layout_" in self.__dict__: + while self.layout_.itemAt(0) != None: + self.layout_.itemAt(0).widget().setParent(None) + + if config_path != "": + config_name = config_path.split("/")[-1] + self.ui.lineEdit_curConfig.setText(config_name) + self.parseXml(config_path) + + self.ui.spinBox_configNumber.setValue(1) + # Parses Master and child XML files from the given file, updates text boxes accordingly def parseXml(self, config_path): - # Master + # Read Master with open(config_path, 'r') as f: self.ui.textEdit_masterConfig.setText(f.read()) f.close() + # Parse number of SC and SC filenames from master i = 1 + self.sc_cfg_files = [] childDict = {} - root = ET.parse(config_path).getroot() - for child in root: + self.master_root = ET.parse(config_path).getroot() + for child in self.master_root: if child.tag == "number-spacecraft": self.ui.spinBox_configNumber.setMaximum(int(child.text)) if re.match("sc-[0-9]+-cfg", child.tag): childDict[child.tag] = child.text + + # Check for duplicate xml's + if child.text in self.sc_cfg_files: + QMessageBox.critical(self, "Error", "Using duplicate Spacecraft Config Files, Changes will not be saved correctly") + print("Duplicate SC Config file") + + self.sc_cfg_files.append(child.text) i+=1 - # Children + # Read Children config_dir = str(config_path.rsplit('/', 1)[0]) for i, child in enumerate(childDict): if Path(f'{config_dir}/{childDict[child]}').is_file(): filePath = f'{config_dir}/{childDict[child]}' - elif Path(f'./cfg/{childDict[child]}').is_file(): - filePath = f'./cfg/{childDict[child]}' - elif Path(f'./cfg/custom/{childDict[child]}').is_file(): - filePath = f'./cfg/custom/{childDict[child]}' else: raise FileNotFoundError(childDict[child]) @@ -243,7 +299,7 @@ def parseXml(self, config_path): self.scConfigs[i] = f'Filename: {childDict[child]}\n\n{f.read()}' f.close() - # Update Spacecraft Config Text + # Update Spacecraft Config Text to first SC config listed in master config self.switchConfig(1) # Starts a Bash process to execute args, redirects output to given textbox, not used for now @@ -285,7 +341,10 @@ def clean(self, software:str, button:QPushButton): # Placeholder build command, assumes make prep already ran, same with clean commands def build(self, software:str, button:QPushButton): textbox = self.ui.textEdit_buildConsole - command = f'make {software}' + if software == 'all': + command = f'make' + else: + command = f'make {software}' self.buttonColor(button) t1 = threading.Thread(target=self.thread_Bash(textbox, button, command), name='t1') @@ -297,7 +356,7 @@ def thread_Bash(self, textbox:QTextEdit, button:QPushButton, command:str): self.gnome_terminal(textbox, command) self.enableButtons(button) - # Changes the color of the most recently pressed button to green + # Changes the color of the most recently pressed button to green and the last pressed button to default def buttonColor(self, button:QPushButton): if self.prevButtonPressed is not None: self.prevButtonPressed.setStyleSheet(self.defaultStyleSheet) From 9e774bfe6cda854f7e503aa213a36de966ee53ca Mon Sep 17 00:00:00 2001 From: Ice Date: Thu, 11 Apr 2024 14:49:03 -0400 Subject: [PATCH 08/43] Removed Bash Process function and calls to it --- cfg/gui/cfg_gui_main.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/cfg/gui/cfg_gui_main.py b/cfg/gui/cfg_gui_main.py index 67722e05..84e99fe1 100644 --- a/cfg/gui/cfg_gui_main.py +++ b/cfg/gui/cfg_gui_main.py @@ -46,9 +46,9 @@ def __init__(self, *args, **kwargs): self.ui.pushButton_simClean.clicked.connect(lambda: self.clean("sim", self.ui.pushButton_simClean)) # Launch Tab - self.ui.pushButton_play.clicked.connect(lambda: self.startBashProcess(self.ui.textEdit_launchConsole, ["-lc", "echo '>> Starting NOS3 Time Driver'"])) + #self.ui.pushButton_play.clicked.connect(lambda: self.startBashProcess(self.ui.textEdit_launchConsole, ["-lc", "echo '>> Starting NOS3 Time Driver'"])) self.ui.pushButton_stop.clicked.connect(lambda: self.gnome_terminal(self.ui.textEdit_launchConsole, "make stop")) - self.ui.pushButton_pause.clicked.connect(lambda: self.startBashProcess(self.ui.textEdit_launchConsole, ["-lc", "echo '>> Pausing NOS3 Time Driver'"])) + #self.ui.pushButton_pause.clicked.connect(lambda: self.startBashProcess(self.ui.textEdit_launchConsole, ["-lc", "echo '>> Pausing NOS3 Time Driver'"])) self.ui.pushButton_launch.clicked.connect(lambda: self.gnome_terminal(self.ui.textEdit_launchConsole, "make launch")) self.ui.comboBox_run.currentIndexChanged.connect(self.run_ForUntil) @@ -303,20 +303,20 @@ def parseXml(self, config_path): self.switchConfig(1) # Starts a Bash process to execute args, redirects output to given textbox, not used for now - def startBashProcess(self, textbox:QTextEdit, args:list): - process = QProcess() - process.start("bash", [item for item in args]) + #def startBashProcess(self, textbox:QTextEdit, args:list): + #process = QProcess() + #process.start("bash", [item for item in args]) - process.readyReadStandardOutput.connect(lambda: textbox.append(process.readAllStandardOutput().data().decode())) - process.readyReadStandardError.connect(lambda: textbox.append(process.readAllStandardError().data().decode())) + #process.readyReadStandardOutput.connect(lambda: textbox.append(process.readAllStandardOutput().data().decode())) + #process.readyReadStandardError.connect(lambda: textbox.append(process.readAllStandardError().data().decode())) - process.waitForFinished() - process.close() + #process.waitForFinished() + #process.close() # Test for gnome-terminal instead of bash, also uses startCommand() instead of start() def gnome_terminal(self, textbox:QTextEdit, command:str): process = QProcess() - print(command) + #print(command) process.startCommand(f'gnome-terminal -- {command}') process.readyReadStandardOutput.connect(lambda: textbox.append(process.readAllStandardOutput().data().decode())) From d6ce6a55b24d5d2dd8916e6afaf9f1673d21c308 Mon Sep 17 00:00:00 2001 From: Donnie-Ice Date: Mon, 15 Apr 2024 13:57:46 -0400 Subject: [PATCH 09/43] Modified terminals to stay open after executing command --- cfg/gui/cfg_gui_main.py | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/cfg/gui/cfg_gui_main.py b/cfg/gui/cfg_gui_main.py index 84e99fe1..bbf6983f 100644 --- a/cfg/gui/cfg_gui_main.py +++ b/cfg/gui/cfg_gui_main.py @@ -47,8 +47,10 @@ def __init__(self, *args, **kwargs): # Launch Tab #self.ui.pushButton_play.clicked.connect(lambda: self.startBashProcess(self.ui.textEdit_launchConsole, ["-lc", "echo '>> Starting NOS3 Time Driver'"])) + self.ui.pushButton_play.setDisabled(1) self.ui.pushButton_stop.clicked.connect(lambda: self.gnome_terminal(self.ui.textEdit_launchConsole, "make stop")) #self.ui.pushButton_pause.clicked.connect(lambda: self.startBashProcess(self.ui.textEdit_launchConsole, ["-lc", "echo '>> Pausing NOS3 Time Driver'"])) + self.ui.pushButton_pause.setDisabled(1) self.ui.pushButton_launch.clicked.connect(lambda: self.gnome_terminal(self.ui.textEdit_launchConsole, "make launch")) self.ui.comboBox_run.currentIndexChanged.connect(self.run_ForUntil) @@ -77,7 +79,7 @@ def saveText(self, layout:QLayout, config_value:int): # TODO: change to dynamically pull apps/components from xml file or directory applications = ['cf', 'ds', 'fm', 'lc', 'sc'] - components = ['adcs', 'cam', 'css', 'eps', 'fss', 'gps', 'imu', 'mag', 'radio', 'rw', 'sample', 'st', 'torquer'] + components = ['adcs', 'cam', 'css', 'eps', 'fss', 'gps', 'imu', 'mag', 'radio', 'rw', 'sample', 'st', 'syn', 'torquer'] i = 0 while layout.itemAt(i) != None: @@ -302,29 +304,16 @@ def parseXml(self, config_path): # Update Spacecraft Config Text to first SC config listed in master config self.switchConfig(1) - # Starts a Bash process to execute args, redirects output to given textbox, not used for now - #def startBashProcess(self, textbox:QTextEdit, args:list): - #process = QProcess() - #process.start("bash", [item for item in args]) - - #process.readyReadStandardOutput.connect(lambda: textbox.append(process.readAllStandardOutput().data().decode())) - #process.readyReadStandardError.connect(lambda: textbox.append(process.readAllStandardError().data().decode())) - - #process.waitForFinished() - #process.close() - # Test for gnome-terminal instead of bash, also uses startCommand() instead of start() def gnome_terminal(self, textbox:QTextEdit, command:str): process = QProcess() - #print(command) - process.startCommand(f'gnome-terminal -- {command}') + process.startCommand(f'gnome-terminal --tab -- bash -c "{command}; read line" ') process.readyReadStandardOutput.connect(lambda: textbox.append(process.readAllStandardOutput().data().decode())) process.readyReadStandardError.connect(lambda: textbox.append(process.readAllStandardError().data().decode())) process.waitForFinished(msecs=-1) textbox.append(f'>> {command}...') - #process.close() # Placeholder clean command def clean(self, software:str, button:QPushButton): From 75fb4f21d842a97399cb92cc656a2ce88eb88116 Mon Sep 17 00:00:00 2001 From: Donnie-Ice Date: Mon, 15 Apr 2024 14:29:34 -0400 Subject: [PATCH 10/43] Added Igniter to make prep --- scripts/prepare.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/prepare.sh b/scripts/prepare.sh index ac9cf8a1..69bca9e4 100755 --- a/scripts/prepare.sh +++ b/scripts/prepare.sh @@ -39,3 +39,10 @@ cd $USER_NOS3_DIR/42 $DFLAGS_CPUS -v $BASE_DIR:$BASE_DIR -v $USER_NOS3_DIR:$USER_NOS3_DIR -w $USER_NOS3_DIR/42 --name "nos3_42_build" $DBOX make echo "" echo "" + +echo "Prepare Igniter..." +pip3 install pyside6 +cd $BASE_DIR +python3 $BASE_DIR/cfg/gui/cfg_gui_main.py & +echo "" +echo "" From 834edfcef52f10ec2f727750f270cb3f90bbfe9e Mon Sep 17 00:00:00 2001 From: Donnie-Ice Date: Mon, 15 Apr 2024 14:52:38 -0400 Subject: [PATCH 11/43] Added a 'Done' echo to let you know when the terminal is safe to close --- cfg/gui/cfg_gui_main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cfg/gui/cfg_gui_main.py b/cfg/gui/cfg_gui_main.py index bbf6983f..870f2d7e 100644 --- a/cfg/gui/cfg_gui_main.py +++ b/cfg/gui/cfg_gui_main.py @@ -307,7 +307,7 @@ def parseXml(self, config_path): # Test for gnome-terminal instead of bash, also uses startCommand() instead of start() def gnome_terminal(self, textbox:QTextEdit, command:str): process = QProcess() - process.startCommand(f'gnome-terminal --tab -- bash -c "{command}; read line" ') + process.startCommand(f'gnome-terminal --tab -- bash -c "{command}; echo Done; read line" ') process.readyReadStandardOutput.connect(lambda: textbox.append(process.readAllStandardOutput().data().decode())) process.readyReadStandardError.connect(lambda: textbox.append(process.readAllStandardError().data().decode())) From 7796987a2e1149237fbcd1ffd3e24e28a6de82d3 Mon Sep 17 00:00:00 2001 From: Donnie-Ice Date: Thu, 18 Apr 2024 09:34:33 -0400 Subject: [PATCH 12/43] [nasa/nos3#279] Updated Igniter Dependencies --- scripts/prepare.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/prepare.sh b/scripts/prepare.sh index 69bca9e4..d73ae7b3 100755 --- a/scripts/prepare.sh +++ b/scripts/prepare.sh @@ -41,7 +41,7 @@ echo "" echo "" echo "Prepare Igniter..." -pip3 install pyside6 +pip3 install pyside6 xmltodict cd $BASE_DIR python3 $BASE_DIR/cfg/gui/cfg_gui_main.py & echo "" From c5467e2b110d04e69d5391ae2f61cfbd3fe4f079 Mon Sep 17 00:00:00 2001 From: Donnie-Ice Date: Thu, 18 Apr 2024 09:41:40 -0400 Subject: [PATCH 13/43] [nasa/nos3#279] Added ability to make igniter --- Makefile | 3 +++ scripts/igniter_launch.sh | 13 +++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 scripts/igniter_launch.sh diff --git a/Makefile b/Makefile index c8c74906..f449bd85 100644 --- a/Makefile +++ b/Makefile @@ -109,3 +109,6 @@ stop: stop-gsw: ./scripts/stop_gsw.sh + +igniter: + ./scripts/igniter_launch.sh diff --git a/scripts/igniter_launch.sh b/scripts/igniter_launch.sh new file mode 100644 index 00000000..bbe225d1 --- /dev/null +++ b/scripts/igniter_launch.sh @@ -0,0 +1,13 @@ +#!/bin/bash -i +# +# Convenience script for NOS# development + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +source $SCRIPT_DIR/env.sh +echo "" +echo "" + +cd $BASE_DIR +python3 $BASE_DIR/cfg/gui/cfg_gui_main.py & +echo "" +echo "" \ No newline at end of file From 7e13f23359410955b58cf1139180557ad8e8657f Mon Sep 17 00:00:00 2001 From: Donnie-Ice Date: Thu, 18 Apr 2024 10:32:54 -0400 Subject: [PATCH 14/43] Updated TODO's, Comments --- cfg/gui/cfg_gui_main.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/cfg/gui/cfg_gui_main.py b/cfg/gui/cfg_gui_main.py index 870f2d7e..e8a30b0c 100644 --- a/cfg/gui/cfg_gui_main.py +++ b/cfg/gui/cfg_gui_main.py @@ -2,15 +2,12 @@ from PySide6.QtWidgets import QWidget, QApplication, QFileDialog, QTextEdit, QPushButton, QDateTimeEdit, QLabel, QCheckBox, QVBoxLayout, QSizePolicy, QDoubleSpinBox, QLayout, QMessageBox from PySide6.QtCore import QProcess, QDateTime from PySide6.QtGui import QTextCharFormat - +from cfg_gui_ui import Ui_Form import sys, re, xmltodict, datetime, threading import xml.etree.ElementTree as ET -from cfg_gui_ui import Ui_Form - - # TODO: Update master xml sc-x-cfg filename if modified in sc config (in progress) - +# TODO: disableButtons(), enableButtons() not working as intended due to the gnome-terminal handling the command externally class cfg_gui(QWidget): def __init__(self, *args, **kwargs): @@ -45,7 +42,7 @@ def __init__(self, *args, **kwargs): self.ui.pushButton_gswClean.clicked.connect(lambda: self.clean("gsw", self.ui.pushButton_gswClean)) self.ui.pushButton_simClean.clicked.connect(lambda: self.clean("sim", self.ui.pushButton_simClean)) - # Launch Tab + # Launch Tab (Time Driver controls disabled) #self.ui.pushButton_play.clicked.connect(lambda: self.startBashProcess(self.ui.textEdit_launchConsole, ["-lc", "echo '>> Starting NOS3 Time Driver'"])) self.ui.pushButton_play.setDisabled(1) self.ui.pushButton_stop.clicked.connect(lambda: self.gnome_terminal(self.ui.textEdit_launchConsole, "make stop")) @@ -77,7 +74,7 @@ def saveText(self, layout:QLayout, config_value:int): filename = text.split('\n')[0] childXml = xmltodict.parse(text.split('\n', 2)[2]) - # TODO: change to dynamically pull apps/components from xml file or directory + # TODO: change to dynamically pull apps/components from xml file or directory, but how? applications = ['cf', 'ds', 'fm', 'lc', 'sc'] components = ['adcs', 'cam', 'css', 'eps', 'fss', 'gps', 'imu', 'mag', 'radio', 'rw', 'sample', 'st', 'syn', 'torquer'] @@ -307,6 +304,8 @@ def parseXml(self, config_path): # Test for gnome-terminal instead of bash, also uses startCommand() instead of start() def gnome_terminal(self, textbox:QTextEdit, command:str): process = QProcess() + + # `read line` is to hold the terminal open after execution, allows errors to be seen process.startCommand(f'gnome-terminal --tab -- bash -c "{command}; echo Done; read line" ') process.readyReadStandardOutput.connect(lambda: textbox.append(process.readAllStandardOutput().data().decode())) @@ -324,7 +323,7 @@ def clean(self, software:str, button:QPushButton): command = f'make clean-{software}' self.buttonColor(button) - t1 = threading.Thread(target=self.thread_Bash(textbox, button, command), name='t1') + t1 = threading.Thread(target=self.thread_gnome(textbox, button, command), name='t1') t1.start() # Placeholder build command, assumes make prep already ran, same with clean commands @@ -336,11 +335,11 @@ def build(self, software:str, button:QPushButton): command = f'make {software}' self.buttonColor(button) - t1 = threading.Thread(target=self.thread_Bash(textbox, button, command), name='t1') + t1 = threading.Thread(target=self.thread_gnome(textbox, button, command), name='t1') t1.start() # Button/Bash function wrapper for threads - def thread_Bash(self, textbox:QTextEdit, button:QPushButton, command:str): + def thread_gnome(self, textbox:QTextEdit, button:QPushButton, command:str): self.disableButtons(button) self.gnome_terminal(textbox, command) self.enableButtons(button) From 2acafb8ed9223a3e4f383813f91143b8c267717f Mon Sep 17 00:00:00 2001 From: Zemerick Date: Sun, 21 Apr 2024 19:24:55 -0400 Subject: [PATCH 15/43] fixed the link to the NASA Contributor's PDF Form --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 951660b3..06078d9e 100644 --- a/README.md +++ b/README.md @@ -59,5 +59,12 @@ This project is licensed under the NOSA (NASA Open Source Agreement) License. # Issues and Features Please report issues and request features on the GitHub tracking system - [NOS3 Issues](https://www.github.com/nasa/nos3/issues). +## Contributions +If you would like to contribute to the repository, please complete this [NASA Form][def] + and submit it to gsfc-softwarerequest@mail.nasa.gov with John.P.Lucas@nasa.gov CC'ed. Next, please create an issue describing the work to be performed noting that you intend to work it, create a related branch, and submit a pull request when ready. When complete, we will review and work to get it integrated. + ## Support If this project interests you or if you have any questions, please feel free to contact any developer directly or email `support@nos3.org`. + + +[def]: https://github.com/nasa/nos3/files/14578604/NOS3_Invd_CLA.pdf "NOS3 NASA Contributor Form PDF" \ No newline at end of file From d06c08ee2f33456b58fc4097f005d18e18a0be7b Mon Sep 17 00:00:00 2001 From: "Lucas, John P" Date: Tue, 23 Apr 2024 07:38:41 -0400 Subject: [PATCH 16/43] [nasa/nos3#202] TM clear mode appears functional, but breaks FECF on TC side (cryptolib error -12) once started; --- components/cryptolib | 2 +- fsw/apps/to | 2 +- scripts/docker_launch.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/cryptolib b/components/cryptolib index 30e457d6..f62cf1b4 160000 --- a/components/cryptolib +++ b/components/cryptolib @@ -1 +1 @@ -Subproject commit 30e457d62bdbc561f622a8d620121e4fd556af30 +Subproject commit f62cf1b428016cbf33c66fe8875bbb2cff369dd4 diff --git a/fsw/apps/to b/fsw/apps/to index 5741c305..a3b534de 160000 --- a/fsw/apps/to +++ b/fsw/apps/to @@ -1 +1 @@ -Subproject commit 5741c3051b3ee0ccea0526bba7482d7cefe10a5f +Subproject commit a3b534dedd2e212d0eb2a500b2eac67cd30c497a diff --git a/scripts/docker_launch.sh b/scripts/docker_launch.sh index 38149b8a..5d1caec7 100755 --- a/scripts/docker_launch.sh +++ b/scripts/docker_launch.sh @@ -102,7 +102,7 @@ do # Replace `--tab` with `--window-with-profile=KeepOpen` once you've created this gnome-terminal profile manually echo $SC_NUM " - CryptoLib..." - gnome-terminal --window-with-profile=KeepOpen --title=$SC_NUM" - CryptoLib" -- $DFLAGS -v $BASE_DIR:$BASE_DIR --name $SC_NUM"_cryptolib" --network=$SC_NETNAME --network-alias=cryptolib -w $BASE_DIR/gsw/build $DBOX ./support/standalone + gnome-terminal --tab --title=$SC_NUM" - CryptoLib" -- $DFLAGS -v $BASE_DIR:$BASE_DIR --name $SC_NUM"_cryptolib" --network=$SC_NETNAME --network-alias=cryptolib -w $BASE_DIR/gsw/build $DBOX ./support/standalone echo "" echo $SC_NUM " - Simulators..." From 9280ebf2fb9c367d59d891f7cc6f0fffb9a860ce Mon Sep 17 00:00:00 2001 From: "Lucas, John P" Date: Thu, 25 Apr 2024 09:16:38 -0400 Subject: [PATCH 17/43] [nasa/nos3#202] Removed badFECF option in CryptoLib - likely was getting set due to an issue elsewhere; --- components/cryptolib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/cryptolib b/components/cryptolib index f62cf1b4..393b6214 160000 --- a/components/cryptolib +++ b/components/cryptolib @@ -1 +1 @@ -Subproject commit f62cf1b428016cbf33c66fe8875bbb2cff369dd4 +Subproject commit 393b621486a5dcd9474065bfb39e88c77099f21f From e2ca5db0acca880de618b296d8d32c204b2c95d0 Mon Sep 17 00:00:00 2001 From: Donnie-Ice Date: Thu, 25 Apr 2024 10:06:27 -0400 Subject: [PATCH 18/43] Disabled components that don't currently work --- cfg/gui/cfg_gui_main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cfg/gui/cfg_gui_main.py b/cfg/gui/cfg_gui_main.py index e8a30b0c..ef662a2d 100644 --- a/cfg/gui/cfg_gui_main.py +++ b/cfg/gui/cfg_gui_main.py @@ -49,7 +49,9 @@ def __init__(self, *args, **kwargs): #self.ui.pushButton_pause.clicked.connect(lambda: self.startBashProcess(self.ui.textEdit_launchConsole, ["-lc", "echo '>> Pausing NOS3 Time Driver'"])) self.ui.pushButton_pause.setDisabled(1) self.ui.pushButton_launch.clicked.connect(lambda: self.gnome_terminal(self.ui.textEdit_launchConsole, "make launch")) - self.ui.comboBox_run.currentIndexChanged.connect(self.run_ForUntil) + #self.ui.comboBox_run.currentIndexChanged.connect(self.run_ForUntil) + self.ui.comboBox_run.setDisabled(1) + self.ui.lineEdit_secondsEntry.setDisabled(1) # Replaces the textbox on launch tab with a date/time box and vice versa def run_ForUntil(self): From d6330b8eb351c9a5c2a6e7e4a71f269006f0d1d4 Mon Sep 17 00:00:00 2001 From: Donnie-Ice Date: Thu, 25 Apr 2024 10:45:48 -0400 Subject: [PATCH 19/43] Added default config to load on start --- cfg/gui/cfg_gui_main.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cfg/gui/cfg_gui_main.py b/cfg/gui/cfg_gui_main.py index ef662a2d..5148400a 100644 --- a/cfg/gui/cfg_gui_main.py +++ b/cfg/gui/cfg_gui_main.py @@ -5,9 +5,10 @@ from cfg_gui_ui import Ui_Form import sys, re, xmltodict, datetime, threading import xml.etree.ElementTree as ET +import os # TODO: Update master xml sc-x-cfg filename if modified in sc config (in progress) -# TODO: disableButtons(), enableButtons() not working as intended due to the gnome-terminal handling the command externally +# TODO: disableButtons(), enableButtons() not working as intended due to the gnome-terminal thread handling the commands externally class cfg_gui(QWidget): def __init__(self, *args, **kwargs): @@ -53,6 +54,9 @@ def __init__(self, *args, **kwargs): self.ui.comboBox_run.setDisabled(1) self.ui.lineEdit_secondsEntry.setDisabled(1) + # Load Default Config + self.reloadConfig(f'{os.path.dirname(os.path.abspath(__file__))}/../nos3-mission.xml') + # Replaces the textbox on launch tab with a date/time box and vice versa def run_ForUntil(self): index = self.ui.comboBox_run.currentIndex() @@ -353,7 +357,7 @@ def buttonColor(self, button:QPushButton): button.setStyleSheet('QPushButton {background-color: green;}') self.prevButtonPressed = button - # Disable build/clean buttons while another is being ran + # Disable build/clean buttons while another is being ran (not working) def disableButtons(self, button:QPushButton): index = self.ui.gridLayout_buildCleanButtons.count()-1 while index >= 0: @@ -362,7 +366,7 @@ def disableButtons(self, button:QPushButton): widget.setDisabled(1) index -= 1 - # Enable build/clean buttons after process is done running + # Enable build/clean buttons after process is done running (not working) def enableButtons(self, button:QPushButton): index = self.ui.gridLayout_buildCleanButtons.count()-1 while index >= 0: From 5f4539247ad316c3deaa710422d437a305856bd2 Mon Sep 17 00:00:00 2001 From: "Lucas, John P" Date: Thu, 25 Apr 2024 12:13:22 -0400 Subject: [PATCH 20/43] [nasa/nos3#282] Added include APPLICATION_PLATFORM_INC_LIST to the DS CMakeList.txt to allow easy reconfiguration per targets in use for build; --- cfg/nos3_defs/tables/ds_filter_tbl.c | 9 +++++---- fsw/apps/ds | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cfg/nos3_defs/tables/ds_filter_tbl.c b/cfg/nos3_defs/tables/ds_filter_tbl.c index c595d42a..de5a3afd 100644 --- a/cfg/nos3_defs/tables/ds_filter_tbl.c +++ b/cfg/nos3_defs/tables/ds_filter_tbl.c @@ -45,11 +45,12 @@ #include "cfe_msgids.h" #include "ds_msgids.h" +#include "sample_msgids.h" + /* #include "ci_lab_msgids.h" */ /* #include "to_lab_msgids.h" */ /* #include "cs_msgids.h" */ -/* #include "ds_msgids.h" */ /* #include "fm_msgids.h" */ /* #include "hk_msgids.h" */ /* #include "hs_msgids.h" */ @@ -196,11 +197,11 @@ DS_FilterTable_t DS_FilterTable = { {DS_UNUSED, DS_UNUSED, DS_UNUSED, DS_UNUSED, DS_UNUSED}, {DS_UNUSED, DS_UNUSED, DS_UNUSED, DS_UNUSED, DS_UNUSED}}}, /* Packet Index 014 */ - {/* .MessageID = */ CFE_SB_MSGID_RESERVED, + {/* .MessageID = */ CFE_SB_MSGID_WRAP_VALUE(SAMPLE_REQ_HK_MID), /* .Filter = */ {/* File table index, filter type, N, X, O */ - {DS_UNUSED, DS_UNUSED, DS_UNUSED, DS_UNUSED, DS_UNUSED}, - {DS_UNUSED, DS_UNUSED, DS_UNUSED, DS_UNUSED, DS_UNUSED}, + {FILE_ALL_APP_HK_PKTS, DS_BY_COUNT, 1, 1, 0}, + {FILE_ALL_APP_TLM_PKTS, DS_BY_COUNT, 1, 1, 0}, {DS_UNUSED, DS_UNUSED, DS_UNUSED, DS_UNUSED, DS_UNUSED}, {DS_UNUSED, DS_UNUSED, DS_UNUSED, DS_UNUSED, DS_UNUSED}}}, /* Packet Index 015 */ diff --git a/fsw/apps/ds b/fsw/apps/ds index 2a6c6a2d..3f5eeeea 160000 --- a/fsw/apps/ds +++ b/fsw/apps/ds @@ -1 +1 @@ -Subproject commit 2a6c6a2df5b2f00915a06f0a5ac30b9c327c725d +Subproject commit 3f5eeeea37ebcf464945d1d2204f57409beb6ab0 From 6766d1eb28912c9f59434b58efbfae5e32a76718 Mon Sep 17 00:00:00 2001 From: "Lucas, John P" Date: Thu, 25 Apr 2024 14:13:54 -0400 Subject: [PATCH 21/43] [nasa/nos3#282] Updated DS submodule after pull request; --- fsw/apps/ds | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fsw/apps/ds b/fsw/apps/ds index 3f5eeeea..3f273f6a 160000 --- a/fsw/apps/ds +++ b/fsw/apps/ds @@ -1 +1 @@ -Subproject commit 3f5eeeea37ebcf464945d1d2204f57409beb6ab0 +Subproject commit 3f273f6a552c1fb408b989ac0d78743452d3dbc3 From 03acc1eba83ca34d6d6c6d69341e9fd91909c3da Mon Sep 17 00:00:00 2001 From: "Lucas, John P" Date: Fri, 26 Apr 2024 07:24:11 -0400 Subject: [PATCH 22/43] [nasa/nos3#288] Added additional folders to APPLICATION_PLATFORM_INC_LIST and defaulted to using top level *_app.h file for RTS; --- cfg/nos3_defs/tables/sc_rts005.c | 13 +++++++++---- cfg/nos3_defs/targets.cmake | 7 ++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/cfg/nos3_defs/tables/sc_rts005.c b/cfg/nos3_defs/tables/sc_rts005.c index e2da32b8..a1d85837 100644 --- a/cfg/nos3_defs/tables/sc_rts005.c +++ b/cfg/nos3_defs/tables/sc_rts005.c @@ -8,10 +8,9 @@ #include "sc_msg.h" /* defines SC message structures */ /* Command Includes */ -#include "generic_radio_msg.h" -#include "generic_radio_msgids.h" -#include "sample_msg.h" -#include "sample_msgids.h" +#include "cam_app.h" +#include "generic_radio_app.h" +#include "sample_app.h" /* Custom table structure, modify as needed to add desired commands */ typedef struct @@ -20,6 +19,8 @@ typedef struct SAMPLE_Config_cmd_t cmd1; SC_RtsEntryHeader_t hdr2; GENERIC_RADIO_Proximity_cmd_t cmd2; + SC_RtsEntryHeader_t hdr3; + CAM_NoArgsCmd_t cmd3; } SC_RtsStruct005_t; /* Define the union to size the table correctly */ @@ -45,6 +46,10 @@ SC_RtsTable005_t SC_Rts005 = { .cmd2.CmdHeader = CFE_MSG_CMD_HDR_INIT(GENERIC_RADIO_CMD_MID, SC_MEMBER_SIZE(cmd2), GENERIC_RADIO_PROXIMITY_CC, 0x00), .cmd2.SCID = 0, .cmd2.Payload = {0x18, 0xA9, 0xC0, 0x00, 0x00, 0x05, 0x04, 0x00, 0x05, 0x00, 0x00, 0x00}, + + /* 3 - CAM NOOP */ + .hdr3.TimeTag = 1, + .cmd3.CmdHeader = CFE_MSG_CMD_HDR_INIT(CAM_CMD_MID, SC_MEMBER_SIZE(cmd3), CAM_NOOP_CC, 0x00), } }; diff --git a/cfg/nos3_defs/targets.cmake b/cfg/nos3_defs/targets.cmake index ddc442dd..26bfe3ae 100644 --- a/cfg/nos3_defs/targets.cmake +++ b/cfg/nos3_defs/targets.cmake @@ -121,12 +121,17 @@ list(APPEND MISSION_GLOBAL_APPLIST # Create Application Platform Include List FOREACH(X ${MISSION_GLOBAL_APPLIST}) + LIST(APPEND APPLICATION_PLATFORM_INC_LIST ${${X}_MISSION_DIR}/mission_inc) + LIST(APPEND APPLICATION_PLATFORM_INC_LIST ${${X}_MISSION_DIR}/fsw/mission_inc) + LIST(APPEND APPLICATION_PLATFORM_INC_LIST ${${X}_MISSION_DIR}/inc) LIST(APPEND APPLICATION_PLATFORM_INC_LIST ${${X}_MISSION_DIR}/fsw/inc) + LIST(APPEND APPLICATION_PLATFORM_INC_LIST ${${X}_MISSION_DIR}/platform_inc) LIST(APPEND APPLICATION_PLATFORM_INC_LIST ${${X}_MISSION_DIR}/fsw/platform_inc) LIST(APPEND APPLICATION_PLATFORM_INC_LIST ${${X}_MISSION_DIR}/platform_inc) + LIST(APPEND APPLICATION_PLATFORM_INC_LIST ${${X}_MISSION_DIR}/public_inc) LIST(APPEND APPLICATION_PLATFORM_INC_LIST ${${X}_MISSION_DIR}/fsw/public_inc) - LIST(APPEND APPLICATION_PLATFORM_INC_LIST ${${X}_MISSION_DIR}/fsw/src) LIST(APPEND APPLICATION_PLATFORM_INC_LIST ${${X}_MISSION_DIR}/src) + LIST(APPEND APPLICATION_PLATFORM_INC_LIST ${${X}_MISSION_DIR}/fsw/src) ENDFOREACH(X) # FT_INSTALL_SUBDIR indicates where the black box test data files (lua scripts) should From c028f2b211ccc011c96d59451c88c5b2b3977d3e Mon Sep 17 00:00:00 2001 From: Donnie-Ice Date: Fri, 26 Apr 2024 09:58:43 -0400 Subject: [PATCH 23/43] [nasa/nos3#234] Added build config button --- cfg/gui/cfg_gui.ui | 89 ++++++++++++++++-------------- cfg/gui/cfg_gui_main.py | 1 + cfg/gui/cfg_gui_ui.py | 117 +++++++++++++++++++++++----------------- 3 files changed, 118 insertions(+), 89 deletions(-) diff --git a/cfg/gui/cfg_gui.ui b/cfg/gui/cfg_gui.ui index 09550594..51651542 100644 --- a/cfg/gui/cfg_gui.ui +++ b/cfg/gui/cfg_gui.ui @@ -492,10 +492,10 @@ - QTabWidget::Rounded + QTabWidget::TabShape::Rounded - 0 + 1 @@ -511,7 +511,7 @@ - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter true @@ -568,7 +568,7 @@ - + @@ -580,10 +580,10 @@ true - Qt::ScrollBarAlwaysOn + Qt::ScrollBarPolicy::ScrollBarAlwaysOn - Qt::ScrollBarAlwaysOff + Qt::ScrollBarPolicy::ScrollBarAlwaysOff true @@ -593,8 +593,8 @@ 0 0 - 613 - 297 + 84 + 28 @@ -605,7 +605,7 @@ - QLayout::SetDefaultConstraint + QLayout::SizeConstraint::SetDefaultConstraint @@ -703,7 +703,7 @@ Console Output - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter false @@ -773,96 +773,103 @@ - 110 + 10 20 - 421 + 641 81 - QFrame::StyledPanel + QFrame::Shape::StyledPanel - QFrame::Raised + QFrame::Shadow::Raised 79 0 - 341 + 561 80 - QFrame::StyledPanel + QFrame::Shape::StyledPanel - QFrame::Raised + QFrame::Shadow::Raised 0 0 - 340 + 561 80 - - + + - FSW + All - + + + + All + + + + FSW - - + + - GSW + CFG - + SIM - + SIM - + GSW - - + + - All + FSW - + - All + GSW @@ -879,10 +886,10 @@ - QFrame::StyledPanel + QFrame::Shape::StyledPanel - QFrame::Raised + QFrame::Shadow::Raised @@ -900,7 +907,7 @@ Build - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter @@ -917,10 +924,10 @@ - QFrame::StyledPanel + QFrame::Shape::StyledPanel - QFrame::Raised + QFrame::Shadow::Raised @@ -938,7 +945,7 @@ Clean - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter @@ -1039,7 +1046,7 @@ NOS3 Time Driver - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter false @@ -1141,7 +1148,7 @@ - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter diff --git a/cfg/gui/cfg_gui_main.py b/cfg/gui/cfg_gui_main.py index 5148400a..07d3650f 100644 --- a/cfg/gui/cfg_gui_main.py +++ b/cfg/gui/cfg_gui_main.py @@ -35,6 +35,7 @@ def __init__(self, *args, **kwargs): # Build Tab self.ui.pushButton_buildAll.clicked.connect(lambda: self.build("all", self.ui.pushButton_buildAll)) + self.ui.pushButton_cfgBuild.clicked.connect(lambda: self.build("config", self.ui.pushButton_cfgBuild)) self.ui.pushButton_fswBuild.clicked.connect(lambda: self.build("fsw", self.ui.pushButton_fswBuild)) self.ui.pushButton_gswBuild.clicked.connect(lambda: self.build("gsw", self.ui.pushButton_gswBuild)) self.ui.pushButton_simBuild.clicked.connect(lambda: self.build("sim", self.ui.pushButton_simBuild)) diff --git a/cfg/gui/cfg_gui_ui.py b/cfg/gui/cfg_gui_ui.py index 913952bb..aac8d2a6 100644 --- a/cfg/gui/cfg_gui_ui.py +++ b/cfg/gui/cfg_gui_ui.py @@ -3,7 +3,7 @@ ################################################################################ ## Form generated from reading UI file 'cfg_gui.ui' ## -## Created by: Qt User Interface Compiler version 6.6.1 +## Created by: Qt User Interface Compiler version 6.7.0 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ @@ -16,15 +16,16 @@ QImage, QKeySequence, QLinearGradient, QPainter, QPalette, QPixmap, QRadialGradient, QTransform) from PySide6.QtWidgets import (QApplication, QComboBox, QFrame, QGridLayout, - QGroupBox, QHBoxLayout, QLabel, QLineEdit, - QPushButton, QScrollArea, QSizePolicy, QSpinBox, - QTabWidget, QTextEdit, QVBoxLayout, QWidget) + QGroupBox, QHBoxLayout, QLabel, QLayout, + QLineEdit, QPushButton, QScrollArea, QSizePolicy, + QSpinBox, QTabWidget, QTextEdit, QVBoxLayout, + QWidget) class Ui_Form(object): def setupUi(self, Form): if not Form.objectName(): Form.setObjectName(u"Form") - Form.resize(655, 655) + Form.resize(658, 655) palette = QPalette() brush = QBrush(QColor(0, 0, 0, 255)) brush.setStyle(Qt.SolidPattern) @@ -101,13 +102,13 @@ def setupUi(self, Form): self.tabWidget = QTabWidget(Form) self.tabWidget.setObjectName(u"tabWidget") self.tabWidget.setGeometry(QRect(0, 0, 661, 661)) - self.tabWidget.setTabShape(QTabWidget.Rounded) + self.tabWidget.setTabShape(QTabWidget.TabShape.Rounded) self.tab = QWidget() self.tab.setObjectName(u"tab") self.lineEdit_curConfig = QLineEdit(self.tab) self.lineEdit_curConfig.setObjectName(u"lineEdit_curConfig") self.lineEdit_curConfig.setGeometry(QRect(130, 20, 421, 26)) - self.lineEdit_curConfig.setAlignment(Qt.AlignCenter) + self.lineEdit_curConfig.setAlignment(Qt.AlignmentFlag.AlignCenter) self.lineEdit_curConfig.setReadOnly(True) self.pushButton_browse = QPushButton(self.tab) self.pushButton_browse.setObjectName(u"pushButton_browse") @@ -126,15 +127,29 @@ def setupUi(self, Form): self.horizontalLayout_6.setContentsMargins(0, 0, 0, 0) self.scrollArea = QScrollArea(self.horizontalLayoutWidget_6) self.scrollArea.setObjectName(u"scrollArea") + sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.scrollArea.sizePolicy().hasHeightForWidth()) + self.scrollArea.setSizePolicy(sizePolicy) self.scrollArea.setAutoFillBackground(True) - self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) - self.scrollArea.setWidgetResizable(False) + self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn) + self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + self.scrollArea.setWidgetResizable(True) self.scrollAreaWidgetContents = QWidget() self.scrollAreaWidgetContents.setObjectName(u"scrollAreaWidgetContents") - self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 627, 297)) + self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 84, 28)) + sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred) + sizePolicy1.setHorizontalStretch(0) + sizePolicy1.setVerticalStretch(0) + sizePolicy1.setHeightForWidth(self.scrollAreaWidgetContents.sizePolicy().hasHeightForWidth()) + self.scrollAreaWidgetContents.setSizePolicy(sizePolicy1) + self.verticalLayout_3 = QVBoxLayout(self.scrollAreaWidgetContents) + self.verticalLayout_3.setObjectName(u"verticalLayout_3") + self.verticalLayout_3.setSizeConstraint(QLayout.SizeConstraint.SetDefaultConstraint) self.scrollArea.setWidget(self.scrollAreaWidgetContents) - self.horizontalLayout_6.addWidget(self.scrollArea, 0, Qt.AlignTop) + self.horizontalLayout_6.addWidget(self.scrollArea, 0, Qt.AlignmentFlag.AlignTop) self.spinBox_configNumber = QSpinBox(self.groupBox_scConfig) self.spinBox_configNumber.setObjectName(u"spinBox_configNumber") @@ -167,7 +182,7 @@ def setupUi(self, Form): self.groupBox_8 = QGroupBox(self.tab_2) self.groupBox_8.setObjectName(u"groupBox_8") self.groupBox_8.setGeometry(QRect(10, 120, 631, 491)) - self.groupBox_8.setAlignment(Qt.AlignCenter) + self.groupBox_8.setAlignment(Qt.AlignmentFlag.AlignCenter) self.groupBox_8.setFlat(False) self.groupBox_8.setCheckable(False) self.textEdit_buildConsole = QTextEdit(self.groupBox_8) @@ -182,65 +197,70 @@ def setupUi(self, Form): self.textEdit_buildConsole.setReadOnly(True) self.frame_2 = QFrame(self.tab_2) self.frame_2.setObjectName(u"frame_2") - self.frame_2.setGeometry(QRect(110, 20, 421, 81)) - self.frame_2.setFrameShape(QFrame.StyledPanel) - self.frame_2.setFrameShadow(QFrame.Raised) + self.frame_2.setGeometry(QRect(10, 20, 641, 81)) + self.frame_2.setFrameShape(QFrame.Shape.StyledPanel) + self.frame_2.setFrameShadow(QFrame.Shadow.Raised) self.frame = QFrame(self.frame_2) self.frame.setObjectName(u"frame") - self.frame.setGeometry(QRect(79, 0, 341, 80)) - self.frame.setFrameShape(QFrame.StyledPanel) - self.frame.setFrameShadow(QFrame.Raised) + self.frame.setGeometry(QRect(79, 0, 561, 80)) + self.frame.setFrameShape(QFrame.Shape.StyledPanel) + self.frame.setFrameShadow(QFrame.Shadow.Raised) self.gridLayoutWidget_2 = QWidget(self.frame) self.gridLayoutWidget_2.setObjectName(u"gridLayoutWidget_2") - self.gridLayoutWidget_2.setGeometry(QRect(0, 0, 340, 80)) + self.gridLayoutWidget_2.setGeometry(QRect(0, 0, 561, 80)) self.gridLayout_buildCleanButtons = QGridLayout(self.gridLayoutWidget_2) self.gridLayout_buildCleanButtons.setObjectName(u"gridLayout_buildCleanButtons") self.gridLayout_buildCleanButtons.setContentsMargins(0, 0, 0, 0) - self.pushButton_fswClean = QPushButton(self.gridLayoutWidget_2) - self.pushButton_fswClean.setObjectName(u"pushButton_fswClean") + self.pushButton_cleanAll = QPushButton(self.gridLayoutWidget_2) + self.pushButton_cleanAll.setObjectName(u"pushButton_cleanAll") - self.gridLayout_buildCleanButtons.addWidget(self.pushButton_fswClean, 1, 1, 1, 1) + self.gridLayout_buildCleanButtons.addWidget(self.pushButton_cleanAll, 1, 0, 1, 1) + + self.pushButton_buildAll = QPushButton(self.gridLayoutWidget_2) + self.pushButton_buildAll.setObjectName(u"pushButton_buildAll") + + self.gridLayout_buildCleanButtons.addWidget(self.pushButton_buildAll, 0, 0, 1, 1) self.pushButton_fswBuild = QPushButton(self.gridLayoutWidget_2) self.pushButton_fswBuild.setObjectName(u"pushButton_fswBuild") - self.gridLayout_buildCleanButtons.addWidget(self.pushButton_fswBuild, 0, 1, 1, 1) + self.gridLayout_buildCleanButtons.addWidget(self.pushButton_fswBuild, 0, 2, 1, 1) - self.pushButton_gswClean = QPushButton(self.gridLayoutWidget_2) - self.pushButton_gswClean.setObjectName(u"pushButton_gswClean") + self.pushButton_cfgBuild = QPushButton(self.gridLayoutWidget_2) + self.pushButton_cfgBuild.setObjectName(u"pushButton_cfgBuild") - self.gridLayout_buildCleanButtons.addWidget(self.pushButton_gswClean, 1, 2, 1, 1) + self.gridLayout_buildCleanButtons.addWidget(self.pushButton_cfgBuild, 0, 1, 1, 1) self.pushButton_simClean = QPushButton(self.gridLayoutWidget_2) self.pushButton_simClean.setObjectName(u"pushButton_simClean") - self.gridLayout_buildCleanButtons.addWidget(self.pushButton_simClean, 1, 0, 1, 1) + self.gridLayout_buildCleanButtons.addWidget(self.pushButton_simClean, 1, 4, 1, 1) self.pushButton_simBuild = QPushButton(self.gridLayoutWidget_2) self.pushButton_simBuild.setObjectName(u"pushButton_simBuild") - self.gridLayout_buildCleanButtons.addWidget(self.pushButton_simBuild, 0, 0, 1, 1) + self.gridLayout_buildCleanButtons.addWidget(self.pushButton_simBuild, 0, 4, 1, 1) self.pushButton_gswBuild = QPushButton(self.gridLayoutWidget_2) self.pushButton_gswBuild.setObjectName(u"pushButton_gswBuild") - self.gridLayout_buildCleanButtons.addWidget(self.pushButton_gswBuild, 0, 2, 1, 1) + self.gridLayout_buildCleanButtons.addWidget(self.pushButton_gswBuild, 0, 3, 1, 1) - self.pushButton_buildAll = QPushButton(self.gridLayoutWidget_2) - self.pushButton_buildAll.setObjectName(u"pushButton_buildAll") + self.pushButton_fswClean = QPushButton(self.gridLayoutWidget_2) + self.pushButton_fswClean.setObjectName(u"pushButton_fswClean") - self.gridLayout_buildCleanButtons.addWidget(self.pushButton_buildAll, 0, 3, 1, 1) + self.gridLayout_buildCleanButtons.addWidget(self.pushButton_fswClean, 1, 2, 1, 1) - self.pushButton_cleanAll = QPushButton(self.gridLayoutWidget_2) - self.pushButton_cleanAll.setObjectName(u"pushButton_cleanAll") + self.pushButton_gswClean = QPushButton(self.gridLayoutWidget_2) + self.pushButton_gswClean.setObjectName(u"pushButton_gswClean") - self.gridLayout_buildCleanButtons.addWidget(self.pushButton_cleanAll, 1, 3, 1, 1) + self.gridLayout_buildCleanButtons.addWidget(self.pushButton_gswClean, 1, 3, 1, 1) self.frame_3 = QFrame(self.frame_2) self.frame_3.setObjectName(u"frame_3") self.frame_3.setGeometry(QRect(0, 0, 81, 41)) - self.frame_3.setFrameShape(QFrame.StyledPanel) - self.frame_3.setFrameShadow(QFrame.Raised) + self.frame_3.setFrameShape(QFrame.Shape.StyledPanel) + self.frame_3.setFrameShadow(QFrame.Shadow.Raised) self.verticalLayoutWidget = QWidget(self.frame_3) self.verticalLayoutWidget.setObjectName(u"verticalLayoutWidget") self.verticalLayoutWidget.setGeometry(QRect(0, 0, 81, 41)) @@ -249,15 +269,15 @@ def setupUi(self, Form): self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.label_4 = QLabel(self.verticalLayoutWidget) self.label_4.setObjectName(u"label_4") - self.label_4.setAlignment(Qt.AlignCenter) + self.label_4.setAlignment(Qt.AlignmentFlag.AlignCenter) self.verticalLayout.addWidget(self.label_4) self.frame_4 = QFrame(self.frame_2) self.frame_4.setObjectName(u"frame_4") self.frame_4.setGeometry(QRect(0, 40, 81, 41)) - self.frame_4.setFrameShape(QFrame.StyledPanel) - self.frame_4.setFrameShadow(QFrame.Raised) + self.frame_4.setFrameShape(QFrame.Shape.StyledPanel) + self.frame_4.setFrameShadow(QFrame.Shadow.Raised) self.verticalLayoutWidget_2 = QWidget(self.frame_4) self.verticalLayoutWidget_2.setObjectName(u"verticalLayoutWidget_2") self.verticalLayoutWidget_2.setGeometry(QRect(0, 0, 81, 41)) @@ -266,7 +286,7 @@ def setupUi(self, Form): self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) self.label_5 = QLabel(self.verticalLayoutWidget_2) self.label_5.setObjectName(u"label_5") - self.label_5.setAlignment(Qt.AlignCenter) + self.label_5.setAlignment(Qt.AlignmentFlag.AlignCenter) self.verticalLayout_2.addWidget(self.label_5) @@ -318,7 +338,7 @@ def setupUi(self, Form): self.groupBox_9 = QGroupBox(self.groupBox_control) self.groupBox_9.setObjectName(u"groupBox_9") self.groupBox_9.setGeometry(QRect(10, 60, 611, 451)) - self.groupBox_9.setAlignment(Qt.AlignCenter) + self.groupBox_9.setAlignment(Qt.AlignmentFlag.AlignCenter) self.groupBox_9.setFlat(False) self.groupBox_9.setCheckable(False) self.textEdit_launchConsole = QTextEdit(self.groupBox_9) @@ -347,7 +367,7 @@ def setupUi(self, Form): self.lineEdit_secondsEntry = QLineEdit(self.horizontalLayoutWidget_3) self.lineEdit_secondsEntry.setObjectName(u"lineEdit_secondsEntry") - self.lineEdit_secondsEntry.setAlignment(Qt.AlignCenter) + self.lineEdit_secondsEntry.setAlignment(Qt.AlignmentFlag.AlignCenter) self.horizontalLayout_runForUntil.addWidget(self.lineEdit_secondsEntry) @@ -355,7 +375,7 @@ def setupUi(self, Form): self.retranslateUi(Form) - self.tabWidget.setCurrentIndex(0) + self.tabWidget.setCurrentIndex(1) QMetaObject.connectSlotsByName(Form) @@ -372,14 +392,15 @@ def retranslateUi(self, Form): self.pushButton_saveAs.setText(QCoreApplication.translate("Form", u"Save As...", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), QCoreApplication.translate("Form", u"Config", None)) self.groupBox_8.setTitle(QCoreApplication.translate("Form", u"Console Output", None)) - self.pushButton_fswClean.setText(QCoreApplication.translate("Form", u"FSW", None)) + self.pushButton_cleanAll.setText(QCoreApplication.translate("Form", u"All", None)) + self.pushButton_buildAll.setText(QCoreApplication.translate("Form", u"All", None)) self.pushButton_fswBuild.setText(QCoreApplication.translate("Form", u"FSW", None)) - self.pushButton_gswClean.setText(QCoreApplication.translate("Form", u"GSW", None)) + self.pushButton_cfgBuild.setText(QCoreApplication.translate("Form", u"CFG", None)) self.pushButton_simClean.setText(QCoreApplication.translate("Form", u"SIM", None)) self.pushButton_simBuild.setText(QCoreApplication.translate("Form", u"SIM", None)) self.pushButton_gswBuild.setText(QCoreApplication.translate("Form", u"GSW", None)) - self.pushButton_buildAll.setText(QCoreApplication.translate("Form", u"All", None)) - self.pushButton_cleanAll.setText(QCoreApplication.translate("Form", u"All", None)) + self.pushButton_fswClean.setText(QCoreApplication.translate("Form", u"FSW", None)) + self.pushButton_gswClean.setText(QCoreApplication.translate("Form", u"GSW", None)) self.label_4.setText(QCoreApplication.translate("Form", u"Build", None)) self.label_5.setText(QCoreApplication.translate("Form", u"Clean", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), QCoreApplication.translate("Form", u"Build", None)) From 85e7dcdf7a05303a8053f5aef4e9786962c463f0 Mon Sep 17 00:00:00 2001 From: Donnie-Ice Date: Fri, 26 Apr 2024 10:44:18 -0400 Subject: [PATCH 24/43] [nasa/nos3#234] Added JSTAR and Nos3 branding --- cfg/gui/cfg_gui.ui | 41 ++++++++++++++++++++++-- cfg/gui/cfg_gui_main.py | 6 +++- cfg/gui/cfg_gui_ui.py | 15 +++++++-- cfg/gui/resources/JSTAR-transparent.png | Bin 0 -> 96293 bytes cfg/gui/resources/nos3.png | Bin 0 -> 53018 bytes 5 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 cfg/gui/resources/JSTAR-transparent.png create mode 100644 cfg/gui/resources/nos3.png diff --git a/cfg/gui/cfg_gui.ui b/cfg/gui/cfg_gui.ui index 51651542..51c2dea8 100644 --- a/cfg/gui/cfg_gui.ui +++ b/cfg/gui/cfg_gui.ui @@ -495,7 +495,7 @@ QTabWidget::TabShape::Rounded - 1 + 0 @@ -593,8 +593,8 @@ 0 0 - 84 - 28 + 613 + 68 @@ -685,6 +685,41 @@ Save As... + + + + 30 + 585 + 161 + 41 + + + + + + + true + + + + + + 450 + 585 + 171 + 41 + + + + + + + true + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + diff --git a/cfg/gui/cfg_gui_main.py b/cfg/gui/cfg_gui_main.py index 07d3650f..cc52e5ea 100644 --- a/cfg/gui/cfg_gui_main.py +++ b/cfg/gui/cfg_gui_main.py @@ -1,7 +1,7 @@ from pathlib import Path from PySide6.QtWidgets import QWidget, QApplication, QFileDialog, QTextEdit, QPushButton, QDateTimeEdit, QLabel, QCheckBox, QVBoxLayout, QSizePolicy, QDoubleSpinBox, QLayout, QMessageBox from PySide6.QtCore import QProcess, QDateTime -from PySide6.QtGui import QTextCharFormat +from PySide6.QtGui import QTextCharFormat, QPixmap from cfg_gui_ui import Ui_Form import sys, re, xmltodict, datetime, threading import xml.etree.ElementTree as ET @@ -32,6 +32,10 @@ def __init__(self, *args, **kwargs): self.ui.pushButton_save.clicked.connect(lambda: self.saveXML("save")) self.ui.pushButton_saveAs.clicked.connect(lambda: self.saveXML("saveAs")) self.ui.spinBox_configNumber.valueChanged.connect(lambda: self.switchConfig(self.ui.spinBox_configNumber.value())) + pixmap = QPixmap('resources/JSTAR-transparent.png') + self.ui.label_jstarLogo.setPixmap(pixmap) + pixmap = QPixmap('resources/nos3.png') + self.ui.label_nos3Logo.setPixmap(pixmap) # Build Tab self.ui.pushButton_buildAll.clicked.connect(lambda: self.build("all", self.ui.pushButton_buildAll)) diff --git a/cfg/gui/cfg_gui_ui.py b/cfg/gui/cfg_gui_ui.py index aac8d2a6..5d31b05f 100644 --- a/cfg/gui/cfg_gui_ui.py +++ b/cfg/gui/cfg_gui_ui.py @@ -138,7 +138,7 @@ def setupUi(self, Form): self.scrollArea.setWidgetResizable(True) self.scrollAreaWidgetContents = QWidget() self.scrollAreaWidgetContents.setObjectName(u"scrollAreaWidgetContents") - self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 84, 28)) + self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 613, 68)) sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred) sizePolicy1.setHorizontalStretch(0) sizePolicy1.setVerticalStretch(0) @@ -176,6 +176,15 @@ def setupUi(self, Form): self.pushButton_saveAs = QPushButton(self.tab) self.pushButton_saveAs.setObjectName(u"pushButton_saveAs") self.pushButton_saveAs.setGeometry(QRect(330, 590, 94, 26)) + self.label_nos3Logo = QLabel(self.tab) + self.label_nos3Logo.setObjectName(u"label_nos3Logo") + self.label_nos3Logo.setGeometry(QRect(30, 585, 161, 41)) + self.label_nos3Logo.setScaledContents(True) + self.label_jstarLogo = QLabel(self.tab) + self.label_jstarLogo.setObjectName(u"label_jstarLogo") + self.label_jstarLogo.setGeometry(QRect(450, 585, 171, 41)) + self.label_jstarLogo.setScaledContents(True) + self.label_jstarLogo.setAlignment(Qt.AlignmentFlag.AlignLeading|Qt.AlignmentFlag.AlignLeft|Qt.AlignmentFlag.AlignVCenter) self.tabWidget.addTab(self.tab, "") self.tab_2 = QWidget() self.tab_2.setObjectName(u"tab_2") @@ -375,7 +384,7 @@ def setupUi(self, Form): self.retranslateUi(Form) - self.tabWidget.setCurrentIndex(1) + self.tabWidget.setCurrentIndex(0) QMetaObject.connectSlotsByName(Form) @@ -390,6 +399,8 @@ def retranslateUi(self, Form): self.groupBox_masterConfig.setTitle(QCoreApplication.translate("Form", u"Master Config", None)) self.pushButton_save.setText(QCoreApplication.translate("Form", u"Save", None)) self.pushButton_saveAs.setText(QCoreApplication.translate("Form", u"Save As...", None)) + self.label_nos3Logo.setText("") + self.label_jstarLogo.setText("") self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), QCoreApplication.translate("Form", u"Config", None)) self.groupBox_8.setTitle(QCoreApplication.translate("Form", u"Console Output", None)) self.pushButton_cleanAll.setText(QCoreApplication.translate("Form", u"All", None)) diff --git a/cfg/gui/resources/JSTAR-transparent.png b/cfg/gui/resources/JSTAR-transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..35568dba98b5e141573b36e17dd2be40dbbfdd60 GIT binary patch literal 96293 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 `oG4xP-kb@n%u;=vLp4sf?)Hs0e)brLpFo=t7 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 P;GeXFym+;!VZi?bX2u0% literal 0 HcmV?d00001 diff --git a/cfg/gui/resources/nos3.png b/cfg/gui/resources/nos3.png new file mode 100644 index 0000000000000000000000000000000000000000..87e246377d20d5daab582688ec5b17f601248624 GIT binary patch literal 53018 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 n{!n}F4t4 Date: Fri, 26 Apr 2024 10:51:38 -0400 Subject: [PATCH 25/43] [nasa/nos3#234] Fixed paths to logos --- cfg/gui/cfg_gui_main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cfg/gui/cfg_gui_main.py b/cfg/gui/cfg_gui_main.py index cc52e5ea..9dddb4dc 100644 --- a/cfg/gui/cfg_gui_main.py +++ b/cfg/gui/cfg_gui_main.py @@ -32,9 +32,9 @@ def __init__(self, *args, **kwargs): self.ui.pushButton_save.clicked.connect(lambda: self.saveXML("save")) self.ui.pushButton_saveAs.clicked.connect(lambda: self.saveXML("saveAs")) self.ui.spinBox_configNumber.valueChanged.connect(lambda: self.switchConfig(self.ui.spinBox_configNumber.value())) - pixmap = QPixmap('resources/JSTAR-transparent.png') + pixmap = QPixmap('./cfg/gui/resources/JSTAR-transparent.png') self.ui.label_jstarLogo.setPixmap(pixmap) - pixmap = QPixmap('resources/nos3.png') + pixmap = QPixmap('./cfg/gui/resources/nos3.png') self.ui.label_nos3Logo.setPixmap(pixmap) # Build Tab From 448c4e80dcbdf593926f35ccf94010ab90635696 Mon Sep 17 00:00:00 2001 From: Donnie-Ice Date: Fri, 26 Apr 2024 10:55:11 -0400 Subject: [PATCH 26/43] [nasa/nos3#234] Fixed logo paths again --- cfg/gui/cfg_gui_main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cfg/gui/cfg_gui_main.py b/cfg/gui/cfg_gui_main.py index 9dddb4dc..fc0a8254 100644 --- a/cfg/gui/cfg_gui_main.py +++ b/cfg/gui/cfg_gui_main.py @@ -32,9 +32,9 @@ def __init__(self, *args, **kwargs): self.ui.pushButton_save.clicked.connect(lambda: self.saveXML("save")) self.ui.pushButton_saveAs.clicked.connect(lambda: self.saveXML("saveAs")) self.ui.spinBox_configNumber.valueChanged.connect(lambda: self.switchConfig(self.ui.spinBox_configNumber.value())) - pixmap = QPixmap('./cfg/gui/resources/JSTAR-transparent.png') + pixmap = QPixmap(f'{os.path.dirname(os.path.abspath(__file__))}/resources/JSTAR-transparent.png') self.ui.label_jstarLogo.setPixmap(pixmap) - pixmap = QPixmap('./cfg/gui/resources/nos3.png') + pixmap = QPixmap(f'{os.path.dirname(os.path.abspath(__file__))}/resources/nos3.png') self.ui.label_nos3Logo.setPixmap(pixmap) # Build Tab From 992c100a81b6526a7fe7c4d21e4c84046bec4c52 Mon Sep 17 00:00:00 2001 From: Donnie-Ice Date: Fri, 26 Apr 2024 11:08:47 -0400 Subject: [PATCH 27/43] [nasa/nos3#234] Added terminal message to press enter when it is done running --- cfg/gui/cfg_gui_main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cfg/gui/cfg_gui_main.py b/cfg/gui/cfg_gui_main.py index fc0a8254..c6757185 100644 --- a/cfg/gui/cfg_gui_main.py +++ b/cfg/gui/cfg_gui_main.py @@ -317,7 +317,7 @@ def gnome_terminal(self, textbox:QTextEdit, command:str): process = QProcess() # `read line` is to hold the terminal open after execution, allows errors to be seen - process.startCommand(f'gnome-terminal --tab -- bash -c "{command}; echo Done; read line" ') + process.startCommand(f'gnome-terminal --tab -- bash -c "{command}; echo Done. Press ENTER to close.; read line" ') process.readyReadStandardOutput.connect(lambda: textbox.append(process.readAllStandardOutput().data().decode())) process.readyReadStandardError.connect(lambda: textbox.append(process.readAllStandardError().data().decode())) From c006ff6d648aee280b20c30ede0cec4d2e7e6f6e Mon Sep 17 00:00:00 2001 From: "Lucas, John P" Date: Fri, 26 Apr 2024 15:45:45 +0000 Subject: [PATCH 28/43] [nasa/nos3#234] Added +x to scripts; --- cfg/gui/resources/JSTAR-transparent.png | Bin cfg/gui/resources/nos3.png | Bin scripts/gsw_cosmos_build.sh | 0 scripts/gsw_cosmos_launch.sh | 0 scripts/gsw_openc3_build.sh | 0 scripts/gsw_openc3_launch.sh | 0 scripts/igniter_launch.sh | 0 7 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 cfg/gui/resources/JSTAR-transparent.png mode change 100644 => 100755 cfg/gui/resources/nos3.png mode change 100644 => 100755 scripts/gsw_cosmos_build.sh mode change 100644 => 100755 scripts/gsw_cosmos_launch.sh mode change 100644 => 100755 scripts/gsw_openc3_build.sh mode change 100644 => 100755 scripts/gsw_openc3_launch.sh mode change 100644 => 100755 scripts/igniter_launch.sh diff --git a/cfg/gui/resources/JSTAR-transparent.png b/cfg/gui/resources/JSTAR-transparent.png old mode 100644 new mode 100755 diff --git a/cfg/gui/resources/nos3.png b/cfg/gui/resources/nos3.png old mode 100644 new mode 100755 diff --git a/scripts/gsw_cosmos_build.sh b/scripts/gsw_cosmos_build.sh old mode 100644 new mode 100755 diff --git a/scripts/gsw_cosmos_launch.sh b/scripts/gsw_cosmos_launch.sh old mode 100644 new mode 100755 diff --git a/scripts/gsw_openc3_build.sh b/scripts/gsw_openc3_build.sh old mode 100644 new mode 100755 diff --git a/scripts/gsw_openc3_launch.sh b/scripts/gsw_openc3_launch.sh old mode 100644 new mode 100755 diff --git a/scripts/igniter_launch.sh b/scripts/igniter_launch.sh old mode 100644 new mode 100755 From 3b316f6c24a45a98105851e06ac00301e31de481 Mon Sep 17 00:00:00 2001 From: Donnie-Ice Date: Fri, 26 Apr 2024 11:56:32 -0400 Subject: [PATCH 29/43] [nasa/nos3#234] Fixed logo sizes --- cfg/gui/cfg_gui.ui | 8 ++++---- cfg/gui/cfg_gui_ui.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cfg/gui/cfg_gui.ui b/cfg/gui/cfg_gui.ui index 51c2dea8..02234ade 100644 --- a/cfg/gui/cfg_gui.ui +++ b/cfg/gui/cfg_gui.ui @@ -688,9 +688,9 @@ - 30 + 50 585 - 161 + 111 41 @@ -704,9 +704,9 @@ - 450 + 480 585 - 171 + 131 41 diff --git a/cfg/gui/cfg_gui_ui.py b/cfg/gui/cfg_gui_ui.py index 5d31b05f..32b50d49 100644 --- a/cfg/gui/cfg_gui_ui.py +++ b/cfg/gui/cfg_gui_ui.py @@ -178,11 +178,11 @@ def setupUi(self, Form): self.pushButton_saveAs.setGeometry(QRect(330, 590, 94, 26)) self.label_nos3Logo = QLabel(self.tab) self.label_nos3Logo.setObjectName(u"label_nos3Logo") - self.label_nos3Logo.setGeometry(QRect(30, 585, 161, 41)) + self.label_nos3Logo.setGeometry(QRect(50, 585, 111, 41)) self.label_nos3Logo.setScaledContents(True) self.label_jstarLogo = QLabel(self.tab) self.label_jstarLogo.setObjectName(u"label_jstarLogo") - self.label_jstarLogo.setGeometry(QRect(450, 585, 171, 41)) + self.label_jstarLogo.setGeometry(QRect(480, 585, 131, 41)) self.label_jstarLogo.setScaledContents(True) self.label_jstarLogo.setAlignment(Qt.AlignmentFlag.AlignLeading|Qt.AlignmentFlag.AlignLeft|Qt.AlignmentFlag.AlignVCenter) self.tabWidget.addTab(self.tab, "") From ca033447c6799483829826eb324e5a1e17eeee9c Mon Sep 17 00:00:00 2001 From: "Lucas, John P" Date: Fri, 26 Apr 2024 15:55:25 -0400 Subject: [PATCH 30/43] [nasa/nos3#202] Swapped to a hybrid setup with CI udp_tf (TC) and TO udp (SPP); --- cfg/nos3_defs/toolchain-amd64-linux-gnu.cmake | 2 +- cfg/sims/nos3-simulator.xml | 8 +++++++- fsw/apps/to | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/cfg/nos3_defs/toolchain-amd64-linux-gnu.cmake b/cfg/nos3_defs/toolchain-amd64-linux-gnu.cmake index b0656fc0..80f34130 100644 --- a/cfg/nos3_defs/toolchain-amd64-linux-gnu.cmake +++ b/cfg/nos3_defs/toolchain-amd64-linux-gnu.cmake @@ -31,4 +31,4 @@ add_definitions(-D_LINUX_OS_) set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CI_TRANSPORT udp_tf) -set(TO_TRANSPORT udp_tf) +set(TO_TRANSPORT udp) # Note udp_tf used for Transfer Frames required for CryptoLib diff --git a/cfg/sims/nos3-simulator.xml b/cfg/sims/nos3-simulator.xml index 2cd2e2f1..d15d315b 100644 --- a/cfg/sims/nos3-simulator.xml +++ b/cfg/sims/nos3-simulator.xml @@ -501,10 +501,16 @@ 6010 6011 --> - + + cosmos + 8010 + 6011 + + prox diff --git a/fsw/apps/to b/fsw/apps/to index a3b534de..d30dca15 160000 --- a/fsw/apps/to +++ b/fsw/apps/to @@ -1 +1 @@ -Subproject commit a3b534dedd2e212d0eb2a500b2eac67cd30c497a +Subproject commit d30dca1539175f3555ffad6289a5ef5008a49b20 From ac7f0299b4f73728bee36c73de20fc0da7981c90 Mon Sep 17 00:00:00 2001 From: Donnie-Ice Date: Mon, 29 Apr 2024 10:22:07 -0400 Subject: [PATCH 31/43] [nasa/nos3#290] Set PNGs to upload as binaries --- .gitattributes | 3 +++ cfg/gui/cfg_gui_main.py | 5 +++-- ...ent.png => JSTAR-transparent_original.png} | Bin 96293 -> 96294 bytes .../resources/{nos3.png => nos3_original.png} | Bin 53018 -> 53019 bytes 4 files changed, 6 insertions(+), 2 deletions(-) rename cfg/gui/resources/{JSTAR-transparent.png => JSTAR-transparent_original.png} (99%) mode change 100755 => 100644 rename cfg/gui/resources/{nos3.png => nos3_original.png} (99%) mode change 100755 => 100644 diff --git a/.gitattributes b/.gitattributes index 4e8a4875..b0c5febf 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,3 +3,6 @@ # Declare files that will always have LF line endings on checkout. *.sh text eol=lf + +# Handle PNGs as binary +*.png binary diff --git a/cfg/gui/cfg_gui_main.py b/cfg/gui/cfg_gui_main.py index c6757185..3fc9825e 100644 --- a/cfg/gui/cfg_gui_main.py +++ b/cfg/gui/cfg_gui_main.py @@ -18,6 +18,7 @@ def __init__(self, *args, **kwargs): self.ui.setupUi(self) self.setFixedSize(655, 655) self.setWindowTitle("NOS3 Igniter - Version 0.0.1") + self.setWindowIcon(QPixmap(f'{os.path.dirname(os.path.abspath(__file__))}/resources/nos3_original.png')) # globals self.dateTimeEdit = QDateTimeEdit() @@ -32,9 +33,9 @@ def __init__(self, *args, **kwargs): self.ui.pushButton_save.clicked.connect(lambda: self.saveXML("save")) self.ui.pushButton_saveAs.clicked.connect(lambda: self.saveXML("saveAs")) self.ui.spinBox_configNumber.valueChanged.connect(lambda: self.switchConfig(self.ui.spinBox_configNumber.value())) - pixmap = QPixmap(f'{os.path.dirname(os.path.abspath(__file__))}/resources/JSTAR-transparent.png') + pixmap = QPixmap(f'{os.path.dirname(os.path.abspath(__file__))}/resources/JSTAR-transparent_original.png') self.ui.label_jstarLogo.setPixmap(pixmap) - pixmap = QPixmap(f'{os.path.dirname(os.path.abspath(__file__))}/resources/nos3.png') + pixmap = QPixmap(f'{os.path.dirname(os.path.abspath(__file__))}/resources/nos3_original.png') self.ui.label_nos3Logo.setPixmap(pixmap) # Build Tab diff --git a/cfg/gui/resources/JSTAR-transparent.png b/cfg/gui/resources/JSTAR-transparent_original.png old mode 100755 new mode 100644 similarity index 99% rename from cfg/gui/resources/JSTAR-transparent.png rename to cfg/gui/resources/JSTAR-transparent_original.png index 35568dba98b5e141573b36e17dd2be40dbbfdd60..5a988078b02f277dfab5bc1538b95a448c12d5e9 GIT binary patch delta 19 acmZ4bfpysjR@Tk{KX=|nmaQy|iWLA(LI#ci delta 18 ZcmZ4XfpzHzR+i2HKlet~t*nfS6#z*x27&+p diff --git a/cfg/gui/resources/nos3.png b/cfg/gui/resources/nos3_original.png old mode 100755 new mode 100644 similarity index 99% rename from cfg/gui/resources/nos3.png rename to cfg/gui/resources/nos3_original.png index 87e246377d20d5daab582688ec5b17f601248624..a4a9a98d6814440184a418b76973cf9825c8e8c2 GIT binary patch delta 16 XcmbO=k9qbyX4cLCKX=}ZEMn&YG2aD} delta 15 WcmbO|k9pQSW|qzXKlhESV&?%Zumyzx From 0333916fbb855c0e8b4c3d035365c4c868fd8f58 Mon Sep 17 00:00:00 2001 From: "Lucas, John P" Date: Tue, 30 Apr 2024 19:50:43 -0400 Subject: [PATCH 32/43] [nasa/nos3#202] Updates after submodule pull requests; --- components/cryptolib | 2 +- fsw/apps/ci | 2 +- fsw/apps/io_lib | 2 +- fsw/apps/to | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/cryptolib b/components/cryptolib index 393b6214..fb72ee2e 160000 --- a/components/cryptolib +++ b/components/cryptolib @@ -1 +1 @@ -Subproject commit 393b621486a5dcd9474065bfb39e88c77099f21f +Subproject commit fb72ee2e836f39737e0df20d838bae56076d277b diff --git a/fsw/apps/ci b/fsw/apps/ci index 153db705..57189901 160000 --- a/fsw/apps/ci +++ b/fsw/apps/ci @@ -1 +1 @@ -Subproject commit 153db705202edec3391c7b0dab11be0cd9cb128d +Subproject commit 57189901d7eae241a90e4fc346748bf56a686856 diff --git a/fsw/apps/io_lib b/fsw/apps/io_lib index f9c841b4..523edf70 160000 --- a/fsw/apps/io_lib +++ b/fsw/apps/io_lib @@ -1 +1 @@ -Subproject commit f9c841b42dec4b485cb02dbf5a682e974461606c +Subproject commit 523edf70f8a620f4a7988eef3beb23713289d1a4 diff --git a/fsw/apps/to b/fsw/apps/to index d30dca15..db65b438 160000 --- a/fsw/apps/to +++ b/fsw/apps/to @@ -1 +1 @@ -Subproject commit d30dca1539175f3555ffad6289a5ef5008a49b20 +Subproject commit db65b4381160adbbd35b8b140b2e5a19cfab1c5a From e9ae3a3c763574c0dc3f3a7aba3ea98da8ea3050 Mon Sep 17 00:00:00 2001 From: "Lucas, John P" Date: Tue, 30 Apr 2024 20:08:27 -0400 Subject: [PATCH 33/43] [nasa/nos3#202] Updated docker container to ivvitc/nos3-64:20240430; --- scripts/env.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/env.sh b/scripts/env.sh index 6bf3791a..426d55f9 100755 --- a/scripts/env.sh +++ b/scripts/env.sh @@ -40,7 +40,7 @@ OPENC3_PATH=$OPENC3_DIR/openc3.sh DNETWORK="docker network" #fi -DBOX="ivvitc/nos3-64:dev" +DBOX="ivvitc/nos3-64:20240430" # Debugging #echo "Script directory = " $SCRIPT_DIR From c70fbe8274b9348cf9c007b5e17c0c4ce242a277 Mon Sep 17 00:00:00 2001 From: Donnie-Ice Date: Thu, 2 May 2024 12:46:52 -0400 Subject: [PATCH 34/43] [nasa/nos3#294] Fixed config_path in Igniter --- cfg/gui/cfg_gui_main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cfg/gui/cfg_gui_main.py b/cfg/gui/cfg_gui_main.py index 3fc9825e..232bfacb 100644 --- a/cfg/gui/cfg_gui_main.py +++ b/cfg/gui/cfg_gui_main.py @@ -27,6 +27,8 @@ def __init__(self, *args, **kwargs): self.defaultStyleSheet = self.ui.pushButton_buildAll.styleSheet() # Saves default stylesheet to return button color to normal, buttonColor() self.setup = 0 # Allows for switchConfig() to initially be called without calling saveText() self.configNumTrack = 0 # Tracks the index of the previous SC config when switching to another index + self.defaultConfig = f'{os.path.dirname(os.path.abspath(__file__))}/../nos3-mission.xml' + self.config_path = self.defaultConfig # Config Tab self.ui.pushButton_browse.clicked.connect(self.browseConfig) @@ -61,7 +63,7 @@ def __init__(self, *args, **kwargs): self.ui.lineEdit_secondsEntry.setDisabled(1) # Load Default Config - self.reloadConfig(f'{os.path.dirname(os.path.abspath(__file__))}/../nos3-mission.xml') + self.reloadConfig(self.defaultConfig) # Replaces the textbox on launch tab with a date/time box and vice versa def run_ForUntil(self): From b59559254f69b405845d36258f5a46067031115a Mon Sep 17 00:00:00 2001 From: "Lucas, John P" Date: Sun, 5 May 2024 14:21:30 -0400 Subject: [PATCH 35/43] [nasa/nos3#275] Updated sample checkout app to not force 32bit, swapped to USART 16, and renamed checkout script; --- Makefile | 2 +- cfg/sims/nos3-simulator.xml | 3 +++ components/sample | 2 +- scripts/{checkout.sh => docker_checkout.sh} | 9 ++++----- 4 files changed, 9 insertions(+), 7 deletions(-) rename scripts/{checkout.sh => docker_checkout.sh} (75%) mode change 100755 => 100644 diff --git a/Makefile b/Makefile index 5b93ca4c..95ea8b5b 100644 --- a/Makefile +++ b/Makefile @@ -62,7 +62,7 @@ build-sim: $(MAKE) --no-print-directory -C $(SIMBUILDDIR) install checkout: - ./scripts/checkout.sh + ./scripts/docker_checkout.sh clean: $(MAKE) clean-fsw diff --git a/cfg/sims/nos3-simulator.xml b/cfg/sims/nos3-simulator.xml index d15d315b..69461bce 100644 --- a/cfg/sims/nos3-simulator.xml +++ b/cfg/sims/nos3-simulator.xml @@ -131,12 +131,15 @@ + SAMPLE_PROVIDER + diff --git a/components/sample b/components/sample index 5382e509..3a9923e0 160000 --- a/components/sample +++ b/components/sample @@ -1 +1 @@ -Subproject commit 5382e5097253ed661f715caf6d9f046087454c3c +Subproject commit 3a9923e0505130465ced4b37ee43b9d5eb4cdfef diff --git a/scripts/checkout.sh b/scripts/docker_checkout.sh old mode 100755 new mode 100644 similarity index 75% rename from scripts/checkout.sh rename to scripts/docker_checkout.sh index 8cc3d7fb..54d39142 --- a/scripts/checkout.sh +++ b/scripts/docker_checkout.sh @@ -20,11 +20,10 @@ echo "" # Replace `--tab` with `--window-with-profile=KeepOpen` once you've created this gnome-terminal profile manually #echo "42..." -#cd /opt/nos3/42/ -#rm -rf NOS3InOut -#cp -r $BASE_DIR/sims/cfg/InOut /opt/nos3/42/NOS3InOut +#rm -rf $USER_NOS3_DIR/42/NOS3InOut +#cp -r $BASE_DIR/cfg/build/InOut $USER_NOS3_DIR/42/NOS3InOut #xhost +local:* -#gnome-terminal --window-with-profile=KeepOpen --title="42" -- $DFLAGS -e DISPLAY=$DISPLAY -v /opt/nos3/42/NOS3InOut:/opt/nos3/42/NOS3InOut -v /tmp/.X11-unix:/tmp/.X11-unix:ro --name $SC_NUM"_fortytwo" -h fortytwo --network=$SC_NETNAME -w /opt/nos3/42 -t $DBOX /opt/nos3/42/42 NOS3InOut +#gnome-terminal --tab --title=$SC_NUM" - 42" -- $DFLAGS -e DISPLAY=$DISPLAY -v $USER_NOS3_DIR:$USER_NOS3_DIR -v /tmp/.X11-unix:/tmp/.X11-unix:ro --name $SC_NUM"_fortytwo" -h fortytwo --network=$SC_NETNAME -w $USER_NOS3_DIR/42 -t $DBOX $USER_NOS3_DIR/42/42 NOS3InOut #echo "" echo "NOS Core..." @@ -44,6 +43,6 @@ gnome-terminal --tab --title="Sample Sim" -- $DFLAGS -v $SIM_DIR:$SIM_DIR --na # make # Rename for your checkout under test to allow checkout -gnome-terminal --title="Sample Sim" -- $DFLAGS -v $BASE_DIR:$BASE_DIR --name $SC_NUM"_sample_checkout" --network=$SC_NETNAME -w $BASE_DIR $DBOX ./components/sample/support/build/sample_checkout +gnome-terminal --title="Sample Checkout" -- $DFLAGS -v $BASE_DIR:$BASE_DIR --name $SC_NUM"_sample_checkout" --network=$SC_NETNAME -w $BASE_DIR $DBOX ./components/sample/support/build/sample_checkout echo "" From e527d51d73e1f3f907a1edf4ada308632cc18a89 Mon Sep 17 00:00:00 2001 From: "Lucas, John P" Date: Sun, 5 May 2024 23:37:24 +0000 Subject: [PATCH 36/43] [nasa/nos3#275] Removed sample from 42 Inp_IPC.txt and updated to default to TGTNAME=cpu1 --- cfg/InOut/Inp_IPC.txt | 12 +----------- components/sample | 2 +- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/cfg/InOut/Inp_IPC.txt b/cfg/InOut/Inp_IPC.txt index e7f7a534..1e2bc0d8 100644 --- a/cfg/InOut/Inp_IPC.txt +++ b/cfg/InOut/Inp_IPC.txt @@ -1,15 +1,5 @@ <<<<<<<<<<<<<<< 42: InterProcess Comm Configuration File >>>>>>>>>>>>>>>> -17 ! Number of Sockets -********************************** Sample IPC ***************************** -TX ! IPC Mode (OFF,TX,RX,TXRX,ACS,WRITEFILE,READFILE) -0 ! AC.ID for ACS mode -"State00.42" ! File name for WRITE or READ -SERVER ! Socket Role (SERVER,CLIENT,GMSEC_CLIENT) -fortytwo 4242 ! Server Host Name, Port -FALSE ! Allow Blocking (i.e. wait on RX) -FALSE ! Echo to stdout -1 ! Number of TX prefixes -"SC" ! Prefix 0 +16 ! Number of Sockets ********************************** RW 0 to 42 ***************************** RX ! IPC Mode (OFF,TX,RX,TXRX,ACS,WRITEFILE,READFILE) 0 ! AC.ID for ACS mode diff --git a/components/sample b/components/sample index 3a9923e0..6b519d95 160000 --- a/components/sample +++ b/components/sample @@ -1 +1 @@ -Subproject commit 3a9923e0505130465ced4b37ee43b9d5eb4cdfef +Subproject commit 6b519d95486ca84d91b2dcf470a5055451e062c8 From 6b3fe3e1797a05021aab7dacf4189886d3165338 Mon Sep 17 00:00:00 2001 From: "Lucas, John P" Date: Tue, 7 May 2024 19:01:38 +0000 Subject: [PATCH 37/43] [nasa/nos3#269] Updated the cosmos build script to copy targets into a COMPONENTS folder for use; --- gsw/cosmos | 2 +- scripts/docker-compose.yml | 112 ------------------------------ scripts/docker_build_cryptolib.sh | 0 scripts/docker_checkout.sh | 0 scripts/gsw_cosmos_build.sh | 17 ++--- 5 files changed, 8 insertions(+), 123 deletions(-) delete mode 100755 scripts/docker-compose.yml mode change 100644 => 100755 scripts/docker_build_cryptolib.sh mode change 100644 => 100755 scripts/docker_checkout.sh diff --git a/gsw/cosmos b/gsw/cosmos index fd47c51d..d72c4030 160000 --- a/gsw/cosmos +++ b/gsw/cosmos @@ -1 +1 @@ -Subproject commit fd47c51da784167318bee3a2bc8533ab11628a03 +Subproject commit d72c403091a35bea6d1b5acb4f3e7e67573a49cd diff --git a/scripts/docker-compose.yml b/scripts/docker-compose.yml deleted file mode 100755 index 953c6e72..00000000 --- a/scripts/docker-compose.yml +++ /dev/null @@ -1,112 +0,0 @@ -version: '3.3' -services: - nos_engine_server_standalone: - volumes: - - '$SIM_DIR:$SIM_DIR' - network_mode: host - image: nos3 - command: /usr/bin/nos_engine_server_standalone -f $SIM_BIN/nos_engine_server_config.json - working_dir: $SIM_BIN - tty: true - nos-time-driver: - volumes: - - '$SIM_DIR:$SIM_DIR' - network_mode: host - image: nos3 - command: $SIM_BIN/nos-time-driver - working_dir: $SIM_BIN - depends_on: - - nos_engine_server_standalone - nos3-simulator-terminal: - volumes: - - '$SIM_DIR:$SIM_DIR' - network_mode: host - image: nos3 - command: $SIM_BIN/nos3-simulator-terminal - working_dir: $SIM_BIN - depends_on: - - nos_engine_server_standalone - nos3-cam-simulator: - volumes: - - '$SIM_DIR:$SIM_DIR' - network_mode: host - image: nos3 - command: $SIM_BIN/nos3-cam-simulator - working_dir: $SIM_BIN - depends_on: - - nos_engine_server_standalone - nos3-generic-reactionwheel-simulator: - volumes: - - '$SIM_DIR:$SIM_DIR' - network_mode: host - image: nos3 - command: $SIM_BIN/nos3-generic-reactionwheel-simulator - working_dir: $SIM_BIN - depends_on: - - nos_engine_server_standalone - nos3-gps-simulator: - volumes: - - '$SIM_DIR:$SIM_DIR' - network_mode: host - image: nos3 - command: $SIM_BIN/nos3-gps-simulator - working_dir: $SIM_BIN - depends_on: - - nos_engine_server_standalone - nos3-sample-simulator: - volumes: - - '$SIM_DIR:$SIM_DIR' - network_mode: host - image: nos3 - command: $SIM_BIN/nos3-sample-simulator - working_dir: $SIM_BIN - depends_on: - - nos_engine_server_standalone - truth42sim: - volumes: - - '$SIM_DIR:$SIM_DIR' - network_mode: host - image: nos3 - command: $SIM_BIN/nos3-single-simulator truth42sim - working_dir: $SIM_BIN - depends_on: - - nos_engine_server_standalone - fsw: - volumes: - - '$FSW_BIN:$FSW_BIN' - network_mode: host - image: nos3 - command: $FSW_BIN/core-cpu1 -R PO - working_dir: $FSW_BIN - stdin_open: true - tty: true - sysctls: - - fs.mqueue.msg_max=500 - depends_on: - - nos_engine_server_standalone - fortytwo: - volumes: - - '/opt/nos3/42/NOS3InOut:/opt/nos3/42/NOS3InOut' - - '/tmp/.X11-unix:/tmp/.X11-unix:ro' - network_mode: host - image: nos3 - command: /opt/nos3/42/42 NOS3InOut - working_dir: /opt/nos3/42 - tty: true - environment: - - DISPLAY=$DISPLAY - depends_on: - - nos_engine_server_standalone - cosmos: - volumes: - - '/home/nos3/Desktop/github-nos3/gsw/cosmos:/cosmos/cosmos' - - '/home/nos3/Desktop/github-nos3/components/:/COMPONENTS' - - '/tmp/.X11-unix:/tmp/.X11-unix:ro' - network_mode: host - image: ballaerospace/cosmos - command: /bin/bash -c 'ruby Launcher -c nos3_launcher.txt --system nos3_system.txt && true' # true is necessary to avoid setpgrp error - working_dir: /cosmos/cosmos - tty: true - environment: - - DISPLAY=$DISPLAY - - QT_X11_NO_MITSHM=1 diff --git a/scripts/docker_build_cryptolib.sh b/scripts/docker_build_cryptolib.sh old mode 100644 new mode 100755 diff --git a/scripts/docker_checkout.sh b/scripts/docker_checkout.sh old mode 100644 new mode 100755 diff --git a/scripts/gsw_cosmos_build.sh b/scripts/gsw_cosmos_build.sh index 53085664..84d591ac 100755 --- a/scripts/gsw_cosmos_build.sh +++ b/scripts/gsw_cosmos_build.sh @@ -7,14 +7,11 @@ CFG_BUILD_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd SCRIPT_DIR=$CFG_BUILD_DIR/../../scripts source $SCRIPT_DIR/env.sh -# Debugging -#echo "Script directory = " $SCRIPT_DIR -#echo "Base directory = " $BASE_DIR -#exit - -#echo "Make /tmp folders..." -#mkdir /tmp/data 2> /dev/null -#mkdir /tmp/data/hk 2> /dev/null -#mkdir /tmp/uplink 2> /dev/null - echo "COSMOS build..." +mkdir $GSW_DIR/COMPONENTS 2> /dev/null +rm -r $GSW_DIR/COMPONENTS/* 2> /dev/null +for i in $(find $BASE_DIR/components/ -name "gsw" -type d) +do + #echo "$i" + cp -r $i/* $GSW_DIR/COMPONENTS/ +done From e7abfa8e9b58809dc1cda2262b4fea0cd5ba49a1 Mon Sep 17 00:00:00 2001 From: "Lucas, John P" Date: Wed, 8 May 2024 11:52:32 -0400 Subject: [PATCH 38/43] [nasa/nos3#269] Minor update to readme; --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f376f763..61a19ae8 100644 --- a/README.md +++ b/README.md @@ -7,15 +7,17 @@ The best source of documentation can be found at [the wiki](https://github.com/n ### Prerequisites Each of the applications listed below are required prior to performing the installation procedure: -* Option A +* Option A, you already use Linux * [Git 2.36+](https://git-scm.com/) * Linux with docker and docker compose installed -* Option B +* Option B, deployment of a virtual machine (VM) * [Git 2.36+](https://git-scm.com/) * [Vagrant 2.3.4+](https://www.vagrantup.com/) * [VirtualBox 7.0+](https://www.virtualbox.org/) ### Installing +Option B only. +Will provision a VM with all required packages installed to be used immediately. 1. Clone the repository `git clone https://github.com/nasa/nos3.git` 2. `cd nos3` 3. Clone the submodules `git submodule update --init --recursive` From 4704a5b32e384b918d1e8b0134842e6d74e326dd Mon Sep 17 00:00:00 2001 From: "Lucas, John P" Date: Wed, 8 May 2024 12:40:56 -0400 Subject: [PATCH 39/43] [nasa/nos3#269] Removed invalid git reset command in prepare script; --- scripts/prepare.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/prepare.sh b/scripts/prepare.sh index d73ae7b3..03371dc6 100755 --- a/scripts/prepare.sh +++ b/scripts/prepare.sh @@ -18,7 +18,6 @@ echo "" echo "Clone openc3-cosmos into local user directory..." cd $USER_NOS3_DIR git clone https://github.com/nasa-itc/openc3-nos3.git --depth 1 -b main $USER_NOS3_DIR/cosmos -git reset --hard echo "" echo "" From 037bc8cb2bf8070767499c4aff265e8db0b369ed Mon Sep 17 00:00:00 2001 From: "Lucas, John P" Date: Wed, 8 May 2024 14:28:35 -0400 Subject: [PATCH 40/43] [nasa/nos3#269] Updated cosmos submodule after merge; --- gsw/cosmos | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gsw/cosmos b/gsw/cosmos index d72c4030..50c889a4 160000 --- a/gsw/cosmos +++ b/gsw/cosmos @@ -1 +1 @@ -Subproject commit d72c403091a35bea6d1b5acb4f3e7e67573a49cd +Subproject commit 50c889a4f6ad21bd0dbc6e32254252a435e57770 From fc2674c29df6bc4e257a03ca2330cc6adad736af Mon Sep 17 00:00:00 2001 From: Mark Suder Date: Mon, 13 May 2024 16:07:06 -0400 Subject: [PATCH 41/43] [nasa/nos3#303] - Fix compiler warnings and errors. --- cfg/nos3_defs/arch_build_custom.cmake | 12 ++++++++---- components/arducam | 2 +- components/generic_adcs | 2 +- components/generic_css | 2 +- components/generic_eps | 2 +- components/generic_fss | 2 +- components/generic_imu | 2 +- components/generic_mag | 2 +- components/generic_star_tracker | 2 +- components/generic_torquer | 2 +- components/novatel_oem615 | 2 +- components/sample | 2 +- components/syn | 2 +- fsw/apps/hwlib | 2 +- 14 files changed, 21 insertions(+), 17 deletions(-) diff --git a/cfg/nos3_defs/arch_build_custom.cmake b/cfg/nos3_defs/arch_build_custom.cmake index e2af60d2..a2919a52 100644 --- a/cfg/nos3_defs/arch_build_custom.cmake +++ b/cfg/nos3_defs/arch_build_custom.cmake @@ -28,18 +28,22 @@ add_compile_options( #-std=c99 # Target the C99 standard (without gcc extensions) #-pedantic # Issue all the warnings demanded by strict ISO C - -Wall # Warn about most questionable operations + #-Wall # Warn about most questionable operations #-Wstrict-prototypes # Warn about missing prototypes - -Wwrite-strings # Warn if not treating string literals as "const" - -Wpointer-arith # Warn about suspicious pointer operations + #-Wwrite-strings # Warn if not treating string literals as "const" + #-Wpointer-arith # Warn about suspicious pointer operations #-Werror # Treat warnings as errors (code should be clean) - -Wno-address-of-packed-member + #-Wno-address-of-packed-member # Build Specific -DBYTE_ORDER_LE -D_LINUX_OS_ -D_DEFAULT_SOURCE ) +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wstrict-prototypes -pedantic -Werror") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wwrite-strings -Wpointer-arith -Wno-address-of-packed-member") +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") + if (CFE_SYSTEM_PSPNAME STREQUAL "nos-linux") # find itc cmake module path find_path(_ITC_CMAKE_MODULES_ diff --git a/components/arducam b/components/arducam index 88e946d5..b05c9746 160000 --- a/components/arducam +++ b/components/arducam @@ -1 +1 @@ -Subproject commit 88e946d54da6e0ed5e2084fd46e8476ff473b3c2 +Subproject commit b05c9746b7f49f852a79e05ccc7f01e043fca72f diff --git a/components/generic_adcs b/components/generic_adcs index 2755e61e..4911e51d 160000 --- a/components/generic_adcs +++ b/components/generic_adcs @@ -1 +1 @@ -Subproject commit 2755e61e66d847aada2bce545e537ea5606d9e27 +Subproject commit 4911e51d589623ab1c1899d1eac6ccde57fa40e1 diff --git a/components/generic_css b/components/generic_css index 1bbba058..448e0251 160000 --- a/components/generic_css +++ b/components/generic_css @@ -1 +1 @@ -Subproject commit 1bbba058117e2c8a3bf17229a1102eab7a67bee4 +Subproject commit 448e0251a2e5ff756c9c86142f17db6be45a7662 diff --git a/components/generic_eps b/components/generic_eps index 2c6d147e..c460ba5a 160000 --- a/components/generic_eps +++ b/components/generic_eps @@ -1 +1 @@ -Subproject commit 2c6d147eb6b7c4b6e43298ff8c4d743da9002d38 +Subproject commit c460ba5a6e9ab9eb9f0f04bd009517b7a0ad24c6 diff --git a/components/generic_fss b/components/generic_fss index a20aaa5f..29426d8d 160000 --- a/components/generic_fss +++ b/components/generic_fss @@ -1 +1 @@ -Subproject commit a20aaa5f0a4bd3c6ed9b76e088a172ac17f05847 +Subproject commit 29426d8d7d6d253272bda468076a1b6453327a47 diff --git a/components/generic_imu b/components/generic_imu index 6d1058e3..5e317f3b 160000 --- a/components/generic_imu +++ b/components/generic_imu @@ -1 +1 @@ -Subproject commit 6d1058e370f09ead713f03a8cee10d308549059c +Subproject commit 5e317f3b3734c6944843fb0afc06fc14099bac5a diff --git a/components/generic_mag b/components/generic_mag index a67fdade..c995ff79 160000 --- a/components/generic_mag +++ b/components/generic_mag @@ -1 +1 @@ -Subproject commit a67fdadea4663bebc308d88c974438c677aab8ba +Subproject commit c995ff79d2b49351761b255ab48de3121f52b634 diff --git a/components/generic_star_tracker b/components/generic_star_tracker index 6fb52ab3..bc075ea8 160000 --- a/components/generic_star_tracker +++ b/components/generic_star_tracker @@ -1 +1 @@ -Subproject commit 6fb52ab329c5ca783dcff2b904da1a3b3e19c876 +Subproject commit bc075ea8c1eaec71c613c8bb9e20582ae02c6430 diff --git a/components/generic_torquer b/components/generic_torquer index fda1fb7c..bd9242dc 160000 --- a/components/generic_torquer +++ b/components/generic_torquer @@ -1 +1 @@ -Subproject commit fda1fb7c323b5e66707e4c7acd8cc45d6213654f +Subproject commit bd9242dceba6c982191efdb9cb8a0aaedf4aad65 diff --git a/components/novatel_oem615 b/components/novatel_oem615 index 8d526247..ba08c053 160000 --- a/components/novatel_oem615 +++ b/components/novatel_oem615 @@ -1 +1 @@ -Subproject commit 8d526247d049ba1ec45f0eb108daff7f9e8e8003 +Subproject commit ba08c053c734ebdf94f4f3e3dc787a52b2a73a3f diff --git a/components/sample b/components/sample index 6b519d95..34260dd8 160000 --- a/components/sample +++ b/components/sample @@ -1 +1 @@ -Subproject commit 6b519d95486ca84d91b2dcf470a5055451e062c8 +Subproject commit 34260dd8a8d929b6d36c8805f08754aa847d6e97 diff --git a/components/syn b/components/syn index dcf625c3..b7d5cc8b 160000 --- a/components/syn +++ b/components/syn @@ -1 +1 @@ -Subproject commit dcf625c30879d129aaa25a36789cf676cce59973 +Subproject commit b7d5cc8bde0d88ba20700a02acb17cf95644de1a diff --git a/fsw/apps/hwlib b/fsw/apps/hwlib index 7f4fc717..cf82b544 160000 --- a/fsw/apps/hwlib +++ b/fsw/apps/hwlib @@ -1 +1 @@ -Subproject commit 7f4fc7177f7819d034d237c2a8a26a3cca319a98 +Subproject commit cf82b54413a007e79f534234733f113359660eb3 From 4114fe9b061b50e1b22983141247e11ead9f0cbd Mon Sep 17 00:00:00 2001 From: Mark Suder Date: Wed, 15 May 2024 12:16:15 -0400 Subject: [PATCH 42/43] Fix botched merge. --- fsw/psp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fsw/psp b/fsw/psp index 6699a3df..1159205e 160000 --- a/fsw/psp +++ b/fsw/psp @@ -1 +1 @@ -Subproject commit 6699a3df30985d825d0743f531eb710600f47bc8 +Subproject commit 1159205edc17db5806ec89c6c2d97125d294c836 From 5ec004cbb7fd3e84369e28cead736fdc44bad757 Mon Sep 17 00:00:00 2001 From: "John P. Lucas" Date: Fri, 14 Jun 2024 07:33:52 -0400 Subject: [PATCH 43/43] [nasa/nos3#216] Added CI to NOS3 for FSW and SIM (#316) * [nasa/nos3#216] First attempt at build workflow; * [nasa/nos3#216] Removed specific branches from build workflow; * [nasa/nos3#216] Removed sudo and added make prep; * [nasa/nos3#216] Added missing docker dependency; * [nasa/nos3#216] Break config and fsw into different runs in build.yml; * [nasa/nos3#216] Avoid docker in docker and run build-fsw; * [nasa/nos3#216] Attempt to get submodules recursively; * [nasa/nos3#216] Added sim job to build workflow; * [nasa/nos3#216] Attempt to use checkout@v4; --- .github/workflows/build.yml | 48 +++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..28419fdf --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,48 @@ +name: Build + +on: + push: + pull_request: + +jobs: + fsw: + runs-on: ubuntu-latest + container: + image: ivvitc/nos3-64:dev + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Update + run: apt-get update + - name: Install dependencies + run: apt-get install -y python3 docker docker.io + - name: prep + run: make prep + - name: config + run: make config + - name: build directory + run: mkdir ./fsw/build + - name: build + run: make build-fsw + + sim: + runs-on: ubuntu-latest + container: + image: ivvitc/nos3-64:dev + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + - name: Update + run: apt-get update + - name: Install dependencies + run: apt-get install -y python3 docker docker.io + - name: prep + run: make prep + - name: config + run: make config + - name: build directory + run: mkdir ./sims/build + - name: build + run: make build-sim