cgse 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cgse-0.1.0/.gitignore +39 -0
- cgse-0.1.0/NOTES.md +101 -0
- cgse-0.1.0/PKG-INFO +38 -0
- cgse-0.1.0/README.md +27 -0
- cgse-0.1.0/TODO.md +3 -0
- cgse-0.1.0/bump.py +85 -0
- cgse-0.1.0/conftest.py +30 -0
- cgse-0.1.0/docs/custom_theme/main.html +32 -0
- cgse-0.1.0/docs/dev_guide/coding_style.md +95 -0
- cgse-0.1.0/docs/dev_guide/docs.md +53 -0
- cgse-0.1.0/docs/dev_guide/index.md +6 -0
- cgse-0.1.0/docs/dev_guide/installation.md +2 -0
- cgse-0.1.0/docs/dev_guide/plugins.md +147 -0
- cgse-0.1.0/docs/dev_guide/uv.md +152 -0
- cgse-0.1.0/docs/getting_started.md +116 -0
- cgse-0.1.0/docs/help.md +9 -0
- cgse-0.1.0/docs/images/icons/cgse-logo-blue.svg +180 -0
- cgse-0.1.0/docs/images/icons/cgse-logo.svg +365 -0
- cgse-0.1.0/docs/index.md +30 -0
- cgse-0.1.0/docs/initialize.md +99 -0
- cgse-0.1.0/docs/libs/cgse-common/images/load_methods.png +0 -0
- cgse-0.1.0/docs/libs/cgse-common/index.md +37 -0
- cgse-0.1.0/docs/libs/cgse-common/settings.md +253 -0
- cgse-0.1.0/docs/libs/cgse-common/setup.md +1 -0
- cgse-0.1.0/docs/libs/cgse-coordinates/index.md +2 -0
- cgse-0.1.0/docs/libs/cgse-core/index.md +1 -0
- cgse-0.1.0/docs/libs/cgse-gui/index.md +1 -0
- cgse-0.1.0/docs/libs/index.md +11 -0
- cgse-0.1.0/docs/package_list.md +16 -0
- cgse-0.1.0/docs/projects/cgse-tools.md +2 -0
- cgse-0.1.0/docs/projects/index.md +16 -0
- cgse-0.1.0/docs/projects/symetrie-hexapod.md +2 -0
- cgse-0.1.0/docs/stylesheets/custom.css +97 -0
- cgse-0.1.0/docs/tutorial.md +10 -0
- cgse-0.1.0/docs/user_guide/index.md +3 -0
- cgse-0.1.0/libs/cgse-common/README.md +39 -0
- cgse-0.1.0/libs/cgse-common/pyproject.toml +84 -0
- cgse-0.1.0/libs/cgse-common/src/cgse_common/__init__.py +0 -0
- cgse-0.1.0/libs/cgse-common/src/cgse_common/settings.yaml +10 -0
- cgse-0.1.0/libs/cgse-common/src/egse/bits.py +543 -0
- cgse-0.1.0/libs/cgse-common/src/egse/calibration.py +250 -0
- cgse-0.1.0/libs/cgse-common/src/egse/command.py +680 -0
- cgse-0.1.0/libs/cgse-common/src/egse/config.py +415 -0
- cgse-0.1.0/libs/cgse-common/src/egse/control.py +448 -0
- cgse-0.1.0/libs/cgse-common/src/egse/decorators.py +428 -0
- cgse-0.1.0/libs/cgse-common/src/egse/device.py +272 -0
- cgse-0.1.0/libs/cgse-common/src/egse/env.py +814 -0
- cgse-0.1.0/libs/cgse-common/src/egse/exceptions.py +88 -0
- cgse-0.1.0/libs/cgse-common/src/egse/hk.py +792 -0
- cgse-0.1.0/libs/cgse-common/src/egse/metrics.py +106 -0
- cgse-0.1.0/libs/cgse-common/src/egse/mixin.py +472 -0
- cgse-0.1.0/libs/cgse-common/src/egse/monitoring.py +96 -0
- cgse-0.1.0/libs/cgse-common/src/egse/observer.py +41 -0
- cgse-0.1.0/libs/cgse-common/src/egse/obsid.py +161 -0
- cgse-0.1.0/libs/cgse-common/src/egse/persistence.py +58 -0
- cgse-0.1.0/libs/cgse-common/src/egse/plugin.py +159 -0
- cgse-0.1.0/libs/cgse-common/src/egse/process.py +460 -0
- cgse-0.1.0/libs/cgse-common/src/egse/protocol.py +607 -0
- cgse-0.1.0/libs/cgse-common/src/egse/proxy.py +522 -0
- cgse-0.1.0/libs/cgse-common/src/egse/reload.py +122 -0
- cgse-0.1.0/libs/cgse-common/src/egse/resource.py +438 -0
- cgse-0.1.0/libs/cgse-common/src/egse/response.py +102 -0
- cgse-0.1.0/libs/cgse-common/src/egse/services.py +215 -0
- cgse-0.1.0/libs/cgse-common/src/egse/services.yaml +51 -0
- cgse-0.1.0/libs/cgse-common/src/egse/settings.py +448 -0
- cgse-0.1.0/libs/cgse-common/src/egse/settings.yaml +5 -0
- cgse-0.1.0/libs/cgse-common/src/egse/setup.py +1239 -0
- cgse-0.1.0/libs/cgse-common/src/egse/state.py +173 -0
- cgse-0.1.0/libs/cgse-common/src/egse/system.py +1537 -0
- cgse-0.1.0/libs/cgse-common/src/egse/version.py +180 -0
- cgse-0.1.0/libs/cgse-common/src/egse/zmq_ser.py +69 -0
- cgse-0.1.0/libs/cgse-common/tests/conftest.py +4 -0
- cgse-0.1.0/libs/cgse-common/tests/data/CSL/conf/SETUP_CSL_00028_201028_155259.yaml +110 -0
- cgse-0.1.0/libs/cgse-common/tests/data/CSL/conf/SETUP_CSL_00029_201028_155331.yaml +111 -0
- cgse-0.1.0/libs/cgse-common/tests/data/CSL/conf/SETUP_CSL_00030_210311_134043.yaml +196 -0
- cgse-0.1.0/libs/cgse-common/tests/data/CSL/conf/SETUP_CSL_00080_210917_105245.yaml +372 -0
- cgse-0.1.0/libs/cgse-common/tests/data/CSL/conf/SETUP_CSL_00081_210922_142259.yaml +375 -0
- cgse-0.1.0/libs/cgse-common/tests/data/CSL/conf/SETUP_CSL_00082_210923_094458.yaml +377 -0
- cgse-0.1.0/libs/cgse-common/tests/data/CSL1/README.md +4 -0
- cgse-0.1.0/libs/cgse-common/tests/data/CSL2/README.md +4 -0
- cgse-0.1.0/libs/cgse-common/tests/data/IAS/README.md +4 -0
- cgse-0.1.0/libs/cgse-common/tests/data/INTA/README.md +4 -0
- cgse-0.1.0/libs/cgse-common/tests/data/LAB23/README.md +4 -0
- cgse-0.1.0/libs/cgse-common/tests/data/SRON/conf/SETUP_SRON_00027_211119_140441.yaml +430 -0
- cgse-0.1.0/libs/cgse-common/tests/data/SRON/conf/SETUP_SRON_00028_211119_160406.yaml +430 -0
- cgse-0.1.0/libs/cgse-common/tests/data/SRON/conf/SETUP_SRON_00029_211119_172918.yaml +431 -0
- cgse-0.1.0/libs/cgse-common/tests/data/SRON/conf/SETUP_SRON_00030_211122_103604.yaml +432 -0
- cgse-0.1.0/libs/cgse-common/tests/data/SRON/conf/SETUP_SRON_00031_211123_124900.yaml +433 -0
- cgse-0.1.0/libs/cgse-common/tests/data/common/telemetry/tm-dictionary-default.csv +3 -0
- cgse-0.1.0/libs/cgse-common/tests/data/conf/SETUP_20250114_1519.yaml +24 -0
- cgse-0.1.0/libs/cgse-common/tests/data/conf/config-file.toml +0 -0
- cgse-0.1.0/libs/cgse-common/tests/data/data/cal_coeff_1234.csv +4 -0
- cgse-0.1.0/libs/cgse-common/tests/data/data/calibration.csv +4 -0
- cgse-0.1.0/libs/cgse-common/tests/data/data/calibration.yaml +10 -0
- cgse-0.1.0/libs/cgse-common/tests/data/data/command.yaml +5 -0
- cgse-0.1.0/libs/cgse-common/tests/data/data/corrupt.yaml +11 -0
- cgse-0.1.0/libs/cgse-common/tests/data/data/data-file.txt +44 -0
- cgse-0.1.0/libs/cgse-common/tests/data/data/empty_data_file.txt +0 -0
- cgse-0.1.0/libs/cgse-common/tests/data/data/empty_yaml_file.yaml +0 -0
- cgse-0.1.0/libs/cgse-common/tests/data/data/local_settings.yaml +3 -0
- cgse-0.1.0/libs/cgse-common/tests/data/data/test_setup.yaml +7 -0
- cgse-0.1.0/libs/cgse-common/tests/data/icons/keyboard.png +0 -0
- cgse-0.1.0/libs/cgse-common/tests/data/lib/dev1/shared-lib.so +0 -0
- cgse-0.1.0/libs/cgse-common/tests/data/lib/dev2/shared-lib.so +0 -0
- cgse-0.1.0/libs/cgse-common/tests/dummy.py +220 -0
- cgse-0.1.0/libs/cgse-common/tests/fixtures/default_env.py +38 -0
- cgse-0.1.0/libs/cgse-common/tests/fixtures/helpers.py +237 -0
- cgse-0.1.0/libs/cgse-common/tests/test_bits.py +266 -0
- cgse-0.1.0/libs/cgse-common/tests/test_command.py +304 -0
- cgse-0.1.0/libs/cgse-common/tests/test_config.py +177 -0
- cgse-0.1.0/libs/cgse-common/tests/test_control.py +0 -0
- cgse-0.1.0/libs/cgse-common/tests/test_decorators.py +355 -0
- cgse-0.1.0/libs/cgse-common/tests/test_device.py +21 -0
- cgse-0.1.0/libs/cgse-common/tests/test_env.py +232 -0
- cgse-0.1.0/libs/cgse-common/tests/test_fixtures.py +12 -0
- cgse-0.1.0/libs/cgse-common/tests/test_hk.py +133 -0
- cgse-0.1.0/libs/cgse-common/tests/test_mixin.py +358 -0
- cgse-0.1.0/libs/cgse-common/tests/test_resource.py +220 -0
- cgse-0.1.0/libs/cgse-common/tests/test_response.py +109 -0
- cgse-0.1.0/libs/cgse-common/tests/test_settings.py +127 -0
- cgse-0.1.0/libs/cgse-common/tests/test_setup.py +1042 -0
- cgse-0.1.0/libs/cgse-common/tests/test_system.py +703 -0
- cgse-0.1.0/libs/cgse-coordinates/README.md +1 -0
- cgse-0.1.0/libs/cgse-coordinates/pyproject.toml +53 -0
- cgse-0.1.0/libs/cgse-coordinates/src/egse/coordinates/__init__.py +531 -0
- cgse-0.1.0/libs/cgse-coordinates/src/egse/coordinates/avoidance.py +103 -0
- cgse-0.1.0/libs/cgse-coordinates/src/egse/coordinates/cslmodel.py +127 -0
- cgse-0.1.0/libs/cgse-coordinates/src/egse/coordinates/laser_tracker_to_dict.py +120 -0
- cgse-0.1.0/libs/cgse-coordinates/src/egse/coordinates/point.py +707 -0
- cgse-0.1.0/libs/cgse-coordinates/src/egse/coordinates/pyplot.py +195 -0
- cgse-0.1.0/libs/cgse-coordinates/src/egse/coordinates/referenceFrame.py +1279 -0
- cgse-0.1.0/libs/cgse-coordinates/src/egse/coordinates/refmodel.py +737 -0
- cgse-0.1.0/libs/cgse-coordinates/src/egse/coordinates/rotationMatrix.py +85 -0
- cgse-0.1.0/libs/cgse-coordinates/src/egse/coordinates/transform3d_addon.py +419 -0
- cgse-0.1.0/libs/cgse-core/.envrc.disabled +9 -0
- cgse-0.1.0/libs/cgse-core/README.md +1 -0
- cgse-0.1.0/libs/cgse-core/pyproject.toml +86 -0
- cgse-0.1.0/libs/cgse-core/src/cgse_core/__init__.py +0 -0
- cgse-0.1.0/libs/cgse-core/src/cgse_core/settings.yaml +35 -0
- cgse-0.1.0/libs/cgse-core/src/egse/confman/__init__.py +1081 -0
- cgse-0.1.0/libs/cgse-core/src/egse/confman/__main__.py +0 -0
- cgse-0.1.0/libs/cgse-core/src/egse/confman/confman.yaml +67 -0
- cgse-0.1.0/libs/cgse-core/src/egse/confman/confman_cs.py +237 -0
- cgse-0.1.0/libs/cgse-core/src/egse/logger/__init__.py +261 -0
- cgse-0.1.0/libs/cgse-core/src/egse/logger/__main__.py +12 -0
- cgse-0.1.0/libs/cgse-core/src/egse/logger/log_cs.py +310 -0
- cgse-0.1.0/libs/cgse-core/src/egse/procman/__init__.py +816 -0
- cgse-0.1.0/libs/cgse-core/src/egse/procman/procman.yaml +49 -0
- cgse-0.1.0/libs/cgse-core/src/egse/procman/procman_cs.py +201 -0
- cgse-0.1.0/libs/cgse-core/src/egse/storage/__init__.py +1090 -0
- cgse-0.1.0/libs/cgse-core/src/egse/storage/__main__.py +3 -0
- cgse-0.1.0/libs/cgse-core/src/egse/storage/persistence.py +537 -0
- cgse-0.1.0/libs/cgse-core/src/egse/storage/storage.yaml +72 -0
- cgse-0.1.0/libs/cgse-core/src/egse/storage/storage_cs.py +198 -0
- cgse-0.1.0/libs/cgse-core/src/scripts/_start.py +41 -0
- cgse-0.1.0/libs/cgse-core/src/scripts/_status.py +81 -0
- cgse-0.1.0/libs/cgse-core/src/scripts/_stop.py +41 -0
- cgse-0.1.0/libs/cgse-core/src/scripts/cgse.py +100 -0
- cgse-0.1.0/libs/cgse-core/src/scripts/services.py +49 -0
- cgse-0.1.0/libs/cgse-core/tests/conftest.py +5 -0
- cgse-0.1.0/libs/cgse-core/tests/data/local_settings.yaml +8 -0
- cgse-0.1.0/libs/cgse-core/tests/fixtures/default_env.py +38 -0
- cgse-0.1.0/libs/cgse-core/tests/fixtures/helpers.py +237 -0
- cgse-0.1.0/libs/cgse-core/tests/fixtures/services.py +201 -0
- cgse-0.1.0/libs/cgse-core/tests/test_confman_setups.py +35 -0
- cgse-0.1.0/libs/cgse-core/tests/test_logger.py +83 -0
- cgse-0.1.0/libs/cgse-core/tests/test_settings_core.py +92 -0
- cgse-0.1.0/libs/cgse-gui/README.md +1 -0
- cgse-0.1.0/libs/cgse-gui/pyproject.toml +37 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/__init__.py +56 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/buttons.py +378 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/aeu-cs-start.svg +117 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/aeu-cs-stop.svg +118 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/aeu-cs.svg +107 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/aeu_cs-started.svg +112 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/aeu_cs-stopped.svg +112 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/aeu_cs.svg +55 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/alert.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/arrow-double-left.png +0 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/arrow-double-right.png +0 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/arrow-up.svg +11 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/backward.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/busy.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/cleaning.svg +115 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/color-scheme.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/cs-connected-alert.svg +91 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/cs-connected-disabled.svg +43 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/cs-connected.svg +89 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/cs-not-connected.svg +44 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/double-left-arrow.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/double-right-arrow.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/erase-disabled.svg +19 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/erase.svg +59 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/fitsgen-start.svg +47 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/fitsgen-stop.svg +48 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/fitsgen.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/forward.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/fov-hk-start.svg +33 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/fov-hk-stop.svg +37 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/fov-hk.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/front-desk.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/home-actioned.svg +15 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/home-disabled.svg +15 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/home.svg +13 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/info.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/invalid.png +0 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/led-green.svg +20 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/led-grey.svg +20 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/led-orange.svg +20 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/led-red.svg +20 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/led-square-green.svg +134 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/led-square-grey.svg +134 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/led-square-orange.svg +134 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/led-square-red.svg +134 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/limit-switch-all-green.svg +115 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/limit-switch-all-red.svg +117 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/limit-switch-el+.svg +116 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/limit-switch-el-.svg +117 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/location-marker.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/logo-dpu.svg +48 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/logo-gimbal.svg +112 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/logo-huber.svg +23 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/logo-ogse.svg +31 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/logo-puna.svg +92 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/logo-tcs.svg +29 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/logo-zonda.svg +66 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/maximize.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/meter.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/more.svg +45 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/n-fee-hk-start.svg +24 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/n-fee-hk-stop.svg +25 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/n-fee-hk.svg +83 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/observing-off.svg +46 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/observing-on.svg +46 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/open-document-hdf5.png +0 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/open-document-hdf5.svg +21 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/ops-mode.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/play-green.svg +17 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/plugged-disabled.svg +27 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/plugged.svg +21 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/pm_ui.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/power-button-green.svg +27 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/power-button-red.svg +27 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/power-button.svg +27 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/radar.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/radioactive.svg +2 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/reload.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/remote-control-off.svg +28 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/remote-control-on.svg +28 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/repeat-blue.svg +15 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/repeat.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/settings.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/shrink.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/shutter.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/sign-off.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/sign-on.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/sim-mode.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/small-buttons-go.svg +20 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/small-buttons-minus.svg +51 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/small-buttons-plus.svg +51 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/sponge.svg +220 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/start-button-disabled.svg +84 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/start-button.svg +50 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/stop-button-disabled.svg +84 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/stop-button.svg +50 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/stop-red.svg +17 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/stop.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/switch-disabled-square.svg +87 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/switch-disabled.svg +15 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/switch-off-square.svg +87 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/switch-off.svg +72 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/switch-on-square.svg +87 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/switch-on.svg +61 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/temperature-control.svg +44 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/th_ui_logo.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/unplugged.svg +23 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/unvalid.png +0 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/user-interface.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/vacuum.svg +1 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/valid.png +0 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/zoom-to-pixel-dark.svg +64 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/icons/zoom-to-pixel-white.svg +36 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/led.py +162 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/limitswitch.py +143 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/states.py +148 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/styles/dark.qss +343 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/styles/default.qss +48 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/styles.qss +48 -0
- cgse-0.1.0/libs/cgse-gui/src/egse/gui/switch.py +108 -0
- cgse-0.1.0/mkdocs.yml +116 -0
- cgse-0.1.0/projects/generic/cgse-tools/README.md +1 -0
- cgse-0.1.0/projects/generic/cgse-tools/pyproject.toml +73 -0
- cgse-0.1.0/projects/generic/cgse-tools/src/cgse_tools/__init__.py +0 -0
- cgse-0.1.0/projects/generic/cgse-tools/src/cgse_tools/cgse_clock.py +47 -0
- cgse-0.1.0/projects/generic/cgse-tools/src/cgse_tools/cgse_commands.py +203 -0
- cgse-0.1.0/projects/generic/cgse-tools/src/cgse_tools/cgse_services.py +28 -0
- cgse-0.1.0/projects/generic/cgse-tools/src/egse/tools/status.py +86 -0
- cgse-0.1.0/projects/generic/keithley-tempcontrol/README.md +9 -0
- cgse-0.1.0/projects/generic/keithley-tempcontrol/pyproject.toml +65 -0
- cgse-0.1.0/projects/generic/keithley-tempcontrol/src/egse/tempcontrol/keithley/__init__.py +4 -0
- cgse-0.1.0/projects/generic/keithley-tempcontrol/src/egse/tempcontrol/keithley/daq6510.py +723 -0
- cgse-0.1.0/projects/generic/keithley-tempcontrol/src/egse/tempcontrol/keithley/daq6510.yaml +102 -0
- cgse-0.1.0/projects/generic/keithley-tempcontrol/src/egse/tempcontrol/keithley/daq6510_cs.py +197 -0
- cgse-0.1.0/projects/generic/keithley-tempcontrol/src/egse/tempcontrol/keithley/daq6510_devif.py +359 -0
- cgse-0.1.0/projects/generic/keithley-tempcontrol/src/egse/tempcontrol/keithley/daq6510_protocol.py +105 -0
- cgse-0.1.0/projects/generic/keithley-tempcontrol/src/keithley_tempcontrol/__init__.py +0 -0
- cgse-0.1.0/projects/generic/keithley-tempcontrol/src/keithley_tempcontrol/cgse_services.py +34 -0
- cgse-0.1.0/projects/generic/keithley-tempcontrol/src/keithley_tempcontrol/settings.yaml +22 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/README.md +1 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/pyproject.toml +64 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/__init__.py +32 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/__init__.py +167 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/alpha.py +859 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/dynalpha.py +1383 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/hexapod_ui.py +1511 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/joran.py +266 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/joran.yaml +62 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/joran_cs.py +184 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/joran_protocol.py +128 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/joran_ui.py +449 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/pmac.py +1010 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/pmac_regex.py +83 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/puna.py +1167 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/puna.yaml +193 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/puna_cs.py +197 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/puna_protocol.py +131 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/puna_ui.py +432 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/punaplus.py +107 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/zonda.py +872 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/zonda.yaml +337 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/zonda_cs.py +173 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/zonda_devif.py +415 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/zonda_protocol.py +119 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/egse/hexapod/symetrie/zonda_ui.py +448 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/symetrie_hexapod/__init__.py +0 -0
- cgse-0.1.0/projects/generic/symetrie-hexapod/src/symetrie_hexapod/cgse_services.py +34 -0
- cgse-0.1.0/projects/plato/plato-fits/README.md +1 -0
- cgse-0.1.0/projects/plato/plato-fits/pyproject.toml +55 -0
- cgse-0.1.0/projects/plato/plato-fits/src/egse/plugins/storage/fits.py +1221 -0
- cgse-0.1.0/projects/plato/plato-hdf5/README.md +0 -0
- cgse-0.1.0/projects/plato/plato-hdf5/pyproject.toml +55 -0
- cgse-0.1.0/projects/plato/plato-hdf5/src/egse/plugins/storage/hdf5.py +154 -0
- cgse-0.1.0/projects/plato/plato-spw/README.md +0 -0
- cgse-0.1.0/projects/plato/plato-spw/pyproject.toml +50 -0
- cgse-0.1.0/projects/plato/plato-spw/src/egse/spw.py +1480 -0
- cgse-0.1.0/pyproject.toml +62 -0
- cgse-0.1.0/ruff.toml +25 -0
cgse-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Python versions and environment
|
|
2
|
+
|
|
3
|
+
__pycache__
|
|
4
|
+
.python-version
|
|
5
|
+
.envrc
|
|
6
|
+
|
|
7
|
+
# Build systems
|
|
8
|
+
|
|
9
|
+
build
|
|
10
|
+
dist
|
|
11
|
+
**/*.egg-info
|
|
12
|
+
|
|
13
|
+
# Apple specific
|
|
14
|
+
|
|
15
|
+
.DS_Store
|
|
16
|
+
|
|
17
|
+
# Unit testing
|
|
18
|
+
|
|
19
|
+
.pytest_cache
|
|
20
|
+
.coverage
|
|
21
|
+
htmlcov
|
|
22
|
+
|
|
23
|
+
# Virtual environments
|
|
24
|
+
|
|
25
|
+
.env
|
|
26
|
+
.venv
|
|
27
|
+
venv
|
|
28
|
+
|
|
29
|
+
# PyCharm IDE
|
|
30
|
+
|
|
31
|
+
.idea
|
|
32
|
+
|
|
33
|
+
# MKDOCS documentation site
|
|
34
|
+
|
|
35
|
+
/site
|
|
36
|
+
|
|
37
|
+
# Packaging
|
|
38
|
+
|
|
39
|
+
uv.lock
|
cgse-0.1.0/NOTES.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# The monorepo structure
|
|
2
|
+
|
|
3
|
+
Currently, the structure starts with two folders in the root, i.e. `libs` and `projects`. Where _libs_ contains library type packages like common modules, small generic gui functions, reference frames, ... and _projects_ contain packages that build upon these libraries and can be device drivers or stand-alone applications.
|
|
4
|
+
|
|
5
|
+
There is one package that I think doesn't fit into this picture, that is `cgse-core`. This is not a library, but a – collection of – service(s). So, we might want to add a third top-level folder `services` but I also fear that this again more complicates the monorepo.
|
|
6
|
+
|
|
7
|
+
Anyway, the overall structure of the monorepo is depicted below:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
cgse/
|
|
11
|
+
│── pyproject.toml
|
|
12
|
+
├── libs/
|
|
13
|
+
│ ├── cgse-common/
|
|
14
|
+
│ │ ├── src/
|
|
15
|
+
│ │ ├── tests/
|
|
16
|
+
│ │ └── pyproject.toml
|
|
17
|
+
│ ├── cgse-core/
|
|
18
|
+
│ ├── cgse-coordinates/
|
|
19
|
+
│ └── cgse-gui/
|
|
20
|
+
│
|
|
21
|
+
└── projects/
|
|
22
|
+
├── generic/
|
|
23
|
+
│ ├── cgse-tools/
|
|
24
|
+
│ └── symetrie-hexapod/
|
|
25
|
+
└── plato/
|
|
26
|
+
├── plato-spw/
|
|
27
|
+
├── plato-fits/
|
|
28
|
+
└── plato-hdf5/
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
We will discuss the structure of individual packages in a later section, for now let's look at the root of the monorepo. The root also contains a `pyproject.toml` file although this is not a package that will be build and published. The purpose of this root `pyproject.toml` file is to define properties that are used to build the full repo or any individual package in it. In the root folder we will also put some maintenance/management scripts to help you maintain and bump versions of the projects, build and publish all projects, create and maintain a changelog etc.
|
|
32
|
+
|
|
33
|
+
# Package Structure
|
|
34
|
+
|
|
35
|
+
We try to keep the package structure as standard as possible and consistent over the whole monorepo. The structure currently is as follows (example from cgse-common):
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
├── README.md
|
|
39
|
+
├── dist
|
|
40
|
+
│ ├── cgse_common-2023.1.4-py3-none-any.whl
|
|
41
|
+
│ └── cgse_common-2023.1.4.tar.gz
|
|
42
|
+
├── pyproject.toml
|
|
43
|
+
├── src/
|
|
44
|
+
│ └── egse/ # namespace
|
|
45
|
+
│ ├── modules (*.py)
|
|
46
|
+
│ └── <sub-packages>/
|
|
47
|
+
└── tests/
|
|
48
|
+
├── data
|
|
49
|
+
└── pytest modules (test_*.py)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Note that each library or project is a standalone Poetry package with its own `pyproject.toml` file, source code and unit tests.
|
|
53
|
+
|
|
54
|
+
# Package versions
|
|
55
|
+
|
|
56
|
+
All packages in the monorepo will have the same version. This can be maintained with the `bump.py` script. This script will read the version from the `pyproject.toml` file at the root of the monorepo and propagate the version to all libs and projects in the monorepo. Note that you –for now– will have to update the version number in the `pyproject.toml` file located at the monorepo root folder manually.
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# Build and Publish
|
|
60
|
+
|
|
61
|
+
Building a source distribution and a wheel for your project is as easy as running the following command:
|
|
62
|
+
```
|
|
63
|
+
$ pipx run build
|
|
64
|
+
* Creating isolated environment: venv+pip...
|
|
65
|
+
* Installing packages in isolated environment:
|
|
66
|
+
- hatchling
|
|
67
|
+
* Getting build dependencies for sdist...
|
|
68
|
+
* Building sdist...
|
|
69
|
+
* Building wheel from sdist
|
|
70
|
+
* Creating isolated environment: venv+pip...
|
|
71
|
+
* Installing packages in isolated environment:
|
|
72
|
+
- hatchling
|
|
73
|
+
* Getting build dependencies for wheel...
|
|
74
|
+
* Building wheel...
|
|
75
|
+
```
|
|
76
|
+
Make sure you have updated/bumped the version number in the `pyproject.toml`. Publishing your package on PyPI needs some more preparation, since you need to prepare a token that allows you to upload your project to PyPI. Publishing itself is a peace of cake when the credentials have been configured correctly. Poetry will also automatically take the latest version to publish.
|
|
77
|
+
```
|
|
78
|
+
$ pipx run twine publish
|
|
79
|
+
Publishing cgse-common (2023.1.5) to PyPI
|
|
80
|
+
- Uploading cgse_common-2023.1.5-py3-none-any.whl 100%
|
|
81
|
+
- Uploading cgse_common-2023.1.5.tar.gz 100%
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
# The egse namespace
|
|
85
|
+
|
|
86
|
+
You might have notices that all packages in this monorepo have a `src/egse` folder in which they maintain their source code, preferably in a sub-package. Note that the `egse` folder is not a normal Python package but a namespace. There are two important facts you need to remember about namespaces:
|
|
87
|
+
|
|
88
|
+
1. A namespace package **does not** contain an `__init__.py` module, never, in any of the packages in this or any other repo. If you place an `__init__.py` module in one of your `egse` package folders, you will break the namespace and therefore also the external contributions in plugins etc.
|
|
89
|
+
2. A namespace package is spread out over several directories that can reside in different packages as distributed by PyPI.
|
|
90
|
+
|
|
91
|
+
# Questions
|
|
92
|
+
|
|
93
|
+
What is the meaning of the common egse root folder in this monorepo and when the packages are installed through PyPI?
|
|
94
|
+
|
|
95
|
+
* do we still need `get_common_egse_root()`
|
|
96
|
+
|
|
97
|
+
NO, this has nothing to do with a generic common egse and also the concept of a project root folder doesn't really work for PyPI projects, especially in a monorepo. What might work in this specific case is to use the egse namespace as a root and find folders and files in the folders that make up the namespace.
|
|
98
|
+
|
|
99
|
+
* what is the Projects root directory?
|
|
100
|
+
|
|
101
|
+
This folder basically contains device implementations and plugins for e.g. the storage manager.
|
cgse-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cgse
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Generic Common-EGSE: Commanding and monitoring lab equipment
|
|
5
|
+
Author-email: Rik Huygen <rik.huygen@kuleuven.be>, Sara Regibo <sara.regibo@kuleuven.be>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Requires-Dist: rich>=13.9.4
|
|
9
|
+
Requires-Dist: tomlkit>=0.13.2
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# Common-EGSE
|
|
13
|
+
|
|
14
|
+
This is a monorepo with all the code and documentation for the common egse framework.
|
|
15
|
+
|
|
16
|
+
This repository is organized in two main areas, `libs` and `projects`. The `libs` folder contains library type
|
|
17
|
+
packages like common modules, small generic gui functions, reference frames, etc. and `projects` contains packages
|
|
18
|
+
that build upon these libraries and can be device drivers or stand-alone applications.
|
|
19
|
+
|
|
20
|
+
In the `libs` folder, we have the following packages:
|
|
21
|
+
|
|
22
|
+
- `cgse-common`: Common modules and functions for the EGSE framework.
|
|
23
|
+
- `cgse-coordinates`: Coordinate systems and transformations.
|
|
24
|
+
- `cgse-core`: Core services for the EGSE framework.
|
|
25
|
+
- `cgse-gui`: GUI functions for the EGSE framework.
|
|
26
|
+
|
|
27
|
+
The `projects` folder contains generic and project specific packages.
|
|
28
|
+
|
|
29
|
+
We have the following generic packages:
|
|
30
|
+
|
|
31
|
+
- `cgse-tools`: Tools for the `cgse` command.
|
|
32
|
+
- `symetrie-hexapod`: The Symétrie Hexapod drivers. We put this in the generic folder because it is a generic device driver that can be used by different projects.
|
|
33
|
+
|
|
34
|
+
We have the following project specific packages:
|
|
35
|
+
|
|
36
|
+
- `plato-spw`: The PLATO SpaceWire drivers.
|
|
37
|
+
- `plato-fits`: The PLATO FITS plugins.
|
|
38
|
+
- `plato-hdf5`: The PLATO HDF5 plugins.
|
cgse-0.1.0/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Common-EGSE
|
|
2
|
+
|
|
3
|
+
This is a monorepo with all the code and documentation for the common egse framework.
|
|
4
|
+
|
|
5
|
+
This repository is organized in two main areas, `libs` and `projects`. The `libs` folder contains library type
|
|
6
|
+
packages like common modules, small generic gui functions, reference frames, etc. and `projects` contains packages
|
|
7
|
+
that build upon these libraries and can be device drivers or stand-alone applications.
|
|
8
|
+
|
|
9
|
+
In the `libs` folder, we have the following packages:
|
|
10
|
+
|
|
11
|
+
- `cgse-common`: Common modules and functions for the EGSE framework.
|
|
12
|
+
- `cgse-coordinates`: Coordinate systems and transformations.
|
|
13
|
+
- `cgse-core`: Core services for the EGSE framework.
|
|
14
|
+
- `cgse-gui`: GUI functions for the EGSE framework.
|
|
15
|
+
|
|
16
|
+
The `projects` folder contains generic and project specific packages.
|
|
17
|
+
|
|
18
|
+
We have the following generic packages:
|
|
19
|
+
|
|
20
|
+
- `cgse-tools`: Tools for the `cgse` command.
|
|
21
|
+
- `symetrie-hexapod`: The Symétrie Hexapod drivers. We put this in the generic folder because it is a generic device driver that can be used by different projects.
|
|
22
|
+
|
|
23
|
+
We have the following project specific packages:
|
|
24
|
+
|
|
25
|
+
- `plato-spw`: The PLATO SpaceWire drivers.
|
|
26
|
+
- `plato-fits`: The PLATO FITS plugins.
|
|
27
|
+
- `plato-hdf5`: The PLATO HDF5 plugins.
|
cgse-0.1.0/TODO.md
ADDED
cgse-0.1.0/bump.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This script bumps the version of all libs and projects in this monorepo to the version that
|
|
3
|
+
is currently in the `pyproject.toml` file in the root folder of the monorepo.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
$ python bump.py
|
|
7
|
+
|
|
8
|
+
Note:
|
|
9
|
+
You are expected to be in the minimal virtual environment associated with this monorepo,
|
|
10
|
+
being a `pyenv` or Poetry environment, or your global environment shall include the tomlkit
|
|
11
|
+
and the rich package.
|
|
12
|
+
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
# /// script
|
|
16
|
+
# requires-python = ">=3.9"
|
|
17
|
+
# dependencies = [
|
|
18
|
+
# "tomlkit",
|
|
19
|
+
# "rich",
|
|
20
|
+
# ]
|
|
21
|
+
# ///
|
|
22
|
+
import os
|
|
23
|
+
import pathlib
|
|
24
|
+
|
|
25
|
+
import rich
|
|
26
|
+
import tomlkit
|
|
27
|
+
import tomlkit.exceptions
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_master_version(master_pyproject_path):
|
|
31
|
+
"""Returns the version number of the master project, i.e. cgse."""
|
|
32
|
+
|
|
33
|
+
with open(master_pyproject_path, "r") as file:
|
|
34
|
+
data = tomlkit.parse(file.read())
|
|
35
|
+
|
|
36
|
+
return data["project"]["version"]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def update_project_version(project_dir, new_version):
|
|
40
|
+
"""Updates the version of the subproject."""
|
|
41
|
+
|
|
42
|
+
os.chdir(project_dir)
|
|
43
|
+
|
|
44
|
+
# Check if the Poetry version is defined, otherwise print a message.
|
|
45
|
+
|
|
46
|
+
with open("pyproject.toml", "r") as file:
|
|
47
|
+
data = tomlkit.parse(file.read())
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
data["project"]["version"] = new_version
|
|
51
|
+
|
|
52
|
+
with open("pyproject.toml", "w") as file:
|
|
53
|
+
tomlkit.dump(data, file)
|
|
54
|
+
|
|
55
|
+
except tomlkit.exceptions.NonExistentKey:
|
|
56
|
+
rich.print(f"[red]\[project.version] is not defined in pyproject.toml in {project_dir}[/]")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def update_all_projects_in_monorepo(root_dir):
|
|
60
|
+
"""Updates all pyproject.toml files with the master version number."""
|
|
61
|
+
|
|
62
|
+
excluded_subdirs = ["__pycache__", ".venv", ".git", ".idea", "cgse/build", "cgse/dist"]
|
|
63
|
+
|
|
64
|
+
master_version = get_master_version(os.path.join(root_dir, "pyproject.toml"))
|
|
65
|
+
|
|
66
|
+
rich.print(f"Projects will be bumped to version {master_version}")
|
|
67
|
+
|
|
68
|
+
for subdir, dirs, files in os.walk(root_dir):
|
|
69
|
+
if subdir == "." or subdir == ".." or any(excluded in subdir for excluded in excluded_subdirs):
|
|
70
|
+
# rich.print(f"rejected {subdir = }")
|
|
71
|
+
continue
|
|
72
|
+
if "pyproject.toml" in files and subdir != str(root_dir): # Skip the master pyproject.toml
|
|
73
|
+
print(f"Updating version for project in {subdir}")
|
|
74
|
+
update_project_version(subdir, master_version)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
monorepo_root = pathlib.Path(__file__).parent.resolve()
|
|
79
|
+
|
|
80
|
+
cwd = os.getcwd()
|
|
81
|
+
os.chdir(monorepo_root)
|
|
82
|
+
|
|
83
|
+
update_all_projects_in_monorepo(monorepo_root)
|
|
84
|
+
|
|
85
|
+
os.chdir(cwd)
|
cgse-0.1.0/conftest.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from glob import glob
|
|
2
|
+
|
|
3
|
+
import rich
|
|
4
|
+
|
|
5
|
+
# Quick hack to load plugins from different places.
|
|
6
|
+
# This doesn't work, I get an import error.
|
|
7
|
+
|
|
8
|
+
# def refactor(string: str) -> str:
|
|
9
|
+
# entry = string.replace("/", ".").replace("\\", ".").replace(".py", "")
|
|
10
|
+
# rich.print(f"[green]{entry = }[/]")
|
|
11
|
+
# return entry
|
|
12
|
+
#
|
|
13
|
+
# pytest_plugins = [
|
|
14
|
+
# refactor(conftest)
|
|
15
|
+
# for conftest in glob("**/tests/fixtures/*.py", recursive=True)
|
|
16
|
+
# if "__" not in conftest
|
|
17
|
+
# ]
|
|
18
|
+
|
|
19
|
+
# Doesn't work, with the following error:
|
|
20
|
+
#
|
|
21
|
+
# File "/Users/rik/github/cgse/libs/cgse-common/tests/fixtures/default_env.py", line 7, in <module>
|
|
22
|
+
# from fixtures.helpers import setup_data_storage_layout, teardown_data_storage_layout
|
|
23
|
+
# ImportError: Error importing plugin "libs.cgse-common.tests.fixtures.default_env": No module named 'fixtures'
|
|
24
|
+
#
|
|
25
|
+
|
|
26
|
+
# pytest_plugins = [
|
|
27
|
+
# "libs.cgse-common.tests.fixtures.helpers",
|
|
28
|
+
# "libs.cgse-common.tests.fixtures.default_env",
|
|
29
|
+
# "libs.cgse-core.tests.fixtures.services",
|
|
30
|
+
# ]
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
|
|
3
|
+
{% block extrahead %}
|
|
4
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/fira_code.min.css" integrity="sha512-MbysAYimH1hH2xYzkkMHB6MqxBqfP0megxsCLknbYqHVwXTCg9IqHbk+ZP/vnhO8UEW6PaXAkKe2vQ+SWACxxA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
|
5
|
+
<!-- Fathom - beautiful, simple website analytics -->
|
|
6
|
+
<script src="https://cdn.usefathom.com/script.js" data-site="TAUKXRLQ" defer></script>
|
|
7
|
+
<!-- / Fathom -->
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
<meta property="og:title" content="CGSE - {{ page.title }}">
|
|
11
|
+
<meta property="og:type" content="article">
|
|
12
|
+
<meta property="og:url" content="{{ page.canonical_url | url }}">
|
|
13
|
+
<meta property="og:site_name" content="CGSE Documentation">
|
|
14
|
+
<meta property="og:description" content="CGSE is an instrument test framework for Python.">
|
|
15
|
+
<meta property="og:image" content="https://raw.githubusercontent.com/IvS-KULeuven/cgse/refs/heads/main/docs/images/icons/cgse-logo.svg">
|
|
16
|
+
|
|
17
|
+
<style>
|
|
18
|
+
|
|
19
|
+
@font-face {
|
|
20
|
+
font-family: "Virgil";
|
|
21
|
+
src: url("https://unpkg.com/@excalidraw/excalidraw@0.12.0/dist/excalidraw-assets/Virgil.woff2");
|
|
22
|
+
}
|
|
23
|
+
@font-face {
|
|
24
|
+
font-family: "Cascadia";
|
|
25
|
+
src: url("https://unpkg.com/@excalidraw/excalidraw@0.12.0/dist/excalidraw-assets/Cascadia.woff2");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
</style>
|
|
31
|
+
|
|
32
|
+
{% endblock %}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Style Guide
|
|
2
|
+
|
|
3
|
+
This part of the developer guide contains instructions for coding styles that are adopted for this project.
|
|
4
|
+
|
|
5
|
+
The style guide that we use for this project is [PEP8](https://www.python.org/dev/peps/pep-0008/). This is the standard for Python code and all IDEs, parsers and code formatters understand and work with this standard. PEP8 leaves room for project specific styles. A good style guide that we can follow is the [Google Style Guide](https://google.github.io/styleguide/pyguide.html).
|
|
6
|
+
|
|
7
|
+
The following sections will give the most used conventions with a few examples of good and bad.
|
|
8
|
+
|
|
9
|
+
## TL;DR
|
|
10
|
+
|
|
11
|
+
| Type | Style | Example |
|
|
12
|
+
|------|-------|---------|
|
|
13
|
+
| Classes | CapWords | ProcessManager, ImageViewer, CommandList, Observation, MetaData |
|
|
14
|
+
| Methods & Functions | lowercase with underscores | get_value, set_mask, create_image |
|
|
15
|
+
| Variables | lowercase with underscores | key, last_value, model, index, user_info |
|
|
16
|
+
| Constants | UPPERCASE with underscores | MAX_LINES, BLACK, COMMANDING_PORT |
|
|
17
|
+
| Modules & packages | lowercase **no** underscores | dataset, commanding, multiprocessing |
|
|
18
|
+
|
|
19
|
+
## General
|
|
20
|
+
|
|
21
|
+
* Name the class or variable or function with what it is, what it does or what it contains. A variable named `user_list` might look good at first, but what if at some point you want to change the list to a set so it can not contain duplicates. Are you going to rename everything into `user_set` or would `user_info` be a better name?
|
|
22
|
+
* Never use dashes in any name, they will raise a `SyntaxError: invalid syntax`.
|
|
23
|
+
* We introduce a number of relaxations to not break backward compatibility for the sake of a naming convention. As described in [A Foolish Consistency is the Hobgoblin of Little Minds](https://legacy.python.org/dev/peps/pep-0008/#a-foolish-consistency-is-the-hobgoblin-of-little-minds): _Consistency with this style guide is important. Consistency within a project is more important. Consistency within one module or function is the most important. [...] do not break backwards compatibility just to comply with this PEP!_
|
|
24
|
+
|
|
25
|
+
## Classes
|
|
26
|
+
|
|
27
|
+
Always use CamelCase (Python uses CapWords) for class names. When using acronyms, keep them all UPPER case.
|
|
28
|
+
|
|
29
|
+
* Class names should be nouns, like Observation
|
|
30
|
+
* Make sure to name classes distinctively
|
|
31
|
+
* Stick to one word for a concept when naming classes, i.e. words like `Manager` or `Controller` or `Organizer` all mean similar things. Choose one word for the concept and stick to it.
|
|
32
|
+
* If a word is already part of a package or module, don't use the same word in the class name again.
|
|
33
|
+
|
|
34
|
+
Good names are: `Observation`, `CalibrationFile`, `MetaData`, `Message`, `ReferenceFrame`, `URLParser`.
|
|
35
|
+
|
|
36
|
+
## Methods and Functions
|
|
37
|
+
|
|
38
|
+
A function or a method does something (and should only do one thing, SRP=Single Responsibility Principle), it is an action, so the name should reflect that action.
|
|
39
|
+
|
|
40
|
+
Always use lowercase words separated with underscores.
|
|
41
|
+
|
|
42
|
+
Good names are: `get_time_in_ms()`, `get_commanding_port()`, `is_connected()`, `parse_time()`, `setup_mask()`.
|
|
43
|
+
|
|
44
|
+
When working with legacy code or code from another project, names may be in camelCase (with the first letter a lower case letter). So we can in this case use also `getCommandPort()` or `isConnected()` as method and function names.
|
|
45
|
+
|
|
46
|
+
## Variables
|
|
47
|
+
|
|
48
|
+
Use the same naming convention as functions and methods, i.e. lowercase with underscores.
|
|
49
|
+
|
|
50
|
+
Good names are: `key`, `value`, `user_info`, `model`, `last_value`
|
|
51
|
+
|
|
52
|
+
Bad names: `NSegments`, `outNoise`
|
|
53
|
+
|
|
54
|
+
Take care not to use builtins: `list`, `type`, `filter`, `lambda`, `map`, `dict`, ...
|
|
55
|
+
|
|
56
|
+
Private variables (for classes) start with an underscore: `_name` or `_total_n_args`.
|
|
57
|
+
|
|
58
|
+
In the same spirit as method and function names, the variables can also be in camelCase for specific cases.
|
|
59
|
+
|
|
60
|
+
## CONSTANTS
|
|
61
|
+
|
|
62
|
+
Use ALL_UPPER_CASE with underscores for constants. Use constants always within a name space, not globally.
|
|
63
|
+
|
|
64
|
+
Good names: `MAX_LINES`, `BLACK`, `YELLOW`, `ESL_LINK_MODE_DISABLED`
|
|
65
|
+
|
|
66
|
+
## Modules and Packages
|
|
67
|
+
|
|
68
|
+
Use simple words for modules, preferably just one word like `datasets` or `commanding` or `storage` or `extensions`. If two words are unavoidable, just concatenate them, like `multiprocessing` or `sampledata` or `testdata`. If needed for readability, use an underscore to separate the words, e.g. `image_analysis`.
|
|
69
|
+
|
|
70
|
+
## Import Statements
|
|
71
|
+
|
|
72
|
+
* Group and sort import statements
|
|
73
|
+
* Never use the form `from <module> import *`
|
|
74
|
+
* Always use absolute imports in scripts
|
|
75
|
+
|
|
76
|
+
Be careful that you do not name any modules the same as a module in the Python standard library. This can result in strange effects and may result in an `AttributeError`. Suppose you have named a module `math` in the `egse` directory and it is imported and used further in the code as follows:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from egse import math
|
|
80
|
+
|
|
81
|
+
# in some expression further down the code you might use
|
|
82
|
+
|
|
83
|
+
math.exp(a)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
This will result in the following runtime error:
|
|
87
|
+
|
|
88
|
+
```text
|
|
89
|
+
File "some_module.py", line 8, in <module>
|
|
90
|
+
print(math.exp(a))
|
|
91
|
+
AttributeError: module 'egse.math' has no attribute 'exp'
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Of course this is an obvious example, but it might be more obscure like e.g. in this [GitHub issue](https://github.com/ParmEd/ParmEd/issues/148): 'module'
|
|
95
|
+
object has no attribute 'Cmd'.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
|
|
2
|
+
# Building the documentation
|
|
3
|
+
|
|
4
|
+
- Make sure you are in a virtual environment with Python 3.10+
|
|
5
|
+
- Run the `mkdocs serve` from the project root older
|
|
6
|
+
- Create new pages by adding folder and Markdown files inside `docs/*`
|
|
7
|
+
|
|
8
|
+
## Set up your environment
|
|
9
|
+
|
|
10
|
+
I created a virtual environment using `pyenv` and when I'm working on the documentation, I start up a shell with
|
|
11
|
+
this environment. Currently, only `mkdocs` and `mkdocs-material` are needed. Of course, you need to install these
|
|
12
|
+
only once.
|
|
13
|
+
|
|
14
|
+
```shell
|
|
15
|
+
$ pyenv virtualenv 3.10 cgse-doc-3.10
|
|
16
|
+
$ pyenv shell cgse-doc-3.10
|
|
17
|
+
$ pip install --upgrade pip setuptools wheel
|
|
18
|
+
$ pip install mkdocs
|
|
19
|
+
$ pip install mkdocs-material
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
From this shell, navigate to the project root folder and start the _live-reload_ server of `mkdocs`.
|
|
23
|
+
|
|
24
|
+
```shell
|
|
25
|
+
$ cd ~/github/cgse
|
|
26
|
+
$ mkdocs serve
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Now you can update files, create new folders in `docs/*`, create new Markdown files and all changes will be reloaded
|
|
30
|
+
live in the browser.
|
|
31
|
+
|
|
32
|
+
When you are ready with updating, you will need to build the site and publish it on GitHub pages:
|
|
33
|
+
|
|
34
|
+
```shell
|
|
35
|
+
$ mkdocs build
|
|
36
|
+
$ mkdocs gh-deploy -r upstream -m "documentation update on .."
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Commands
|
|
40
|
+
|
|
41
|
+
- `mkdocs serve` — start the live-reloading docs server
|
|
42
|
+
- `mkdocs build` — build the documentation site
|
|
43
|
+
- `mkdocs deploy` — publish your documentation on GitHub pages
|
|
44
|
+
- `mkdocs -h` — print a help message for more options
|
|
45
|
+
|
|
46
|
+
## Project layout
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
mkdocs.yml # the mkdocs configuration file
|
|
50
|
+
docs/
|
|
51
|
+
index.md # the documentation homepage
|
|
52
|
+
... # other markdown pages, image, folders, ...
|
|
53
|
+
```
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Developer Guide
|
|
2
|
+
|
|
3
|
+
Welcome to the CGSE developer guide! An in-depth reference on how to contribute to the CGSE.
|
|
4
|
+
|
|
5
|
+
First thing to know is that this repository is actually a monorepo, meaning it contains a bunch of related but
|
|
6
|
+
self-standing packages with a minimum of interdependencies. These packages are
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Plugins
|
|
2
|
+
|
|
3
|
+
The CGSE is designed to be extensible and uses a few plugin mechanisms to extend its functionally
|
|
4
|
+
with external contributions. Also within the `cgse` monorepo we use the plugin mechanism at several
|
|
5
|
+
places. The following entry-points are currently defined:
|
|
6
|
+
|
|
7
|
+
* `cgse.version`: Each package that provides functionality within the CGSE or adds a device driver
|
|
8
|
+
registers itself to provide version information.
|
|
9
|
+
* `cgse.command.plugins`: Packages can add commands or sub-commands to the `cgse` app to manage
|
|
10
|
+
their functionality from within the `cgse` app, e.g. to start or stop the service or to report on
|
|
11
|
+
its status.
|
|
12
|
+
* `cgse.settings`: Package can add their own settings.
|
|
13
|
+
* `cgse.resource`: Packages can register resources.
|
|
14
|
+
|
|
15
|
+
Each of the entry-points knows how to load a module or object and each entry-point group is
|
|
16
|
+
connected to a specific action or plugin hook like, e.g. add a command or command group to the
|
|
17
|
+
`cgse` app, add package specific settings to the global settings.
|
|
18
|
+
|
|
19
|
+
## Version discovery
|
|
20
|
+
|
|
21
|
+
When you write a package that you want to integrate with the CGSE, provide a `cgse.version`
|
|
22
|
+
entry-point. The name of the entry-point shall match the package name and is used to read the
|
|
23
|
+
version from the importlib metadata. The entry-point value is currently not used. The entry-point
|
|
24
|
+
value can optionally provide additional information about the package, but that is currently not
|
|
25
|
+
specified.
|
|
26
|
+
|
|
27
|
+
Add the following to your `pyproject.toml` file in your project's root folder, replacing
|
|
28
|
+
_package-name_ with the name of your project. You can leave the entry-point value empty since it is
|
|
29
|
+
currently not used.
|
|
30
|
+
|
|
31
|
+
```toml
|
|
32
|
+
[project.entry-points."cgse.version"]
|
|
33
|
+
package-name = ''
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Extending the `cgse` app
|
|
37
|
+
|
|
38
|
+
### Add a Command
|
|
39
|
+
|
|
40
|
+
If your package provides specific functionality that can be added as a command or a command group to
|
|
41
|
+
the `cgse` app, use the `cgse.command` entry-point group. Since the `cgse` app uses
|
|
42
|
+
the [Typer](https://typer.tiangolo.com) package to build its commandline interface, adding a command
|
|
43
|
+
is as simple as writing a function. The function will be added to the `cgse` app using
|
|
44
|
+
the `app.command()` function of `Typer`, making the function a top-level command of the `cgse`
|
|
45
|
+
app. The function can be defined as a plain function or with Typer's `@app.command` decorator.
|
|
46
|
+
|
|
47
|
+
In the `pyproject.toml` file of your project, add the following lines to add the CGSE command:
|
|
48
|
+
|
|
49
|
+
```toml
|
|
50
|
+
[project.entry-points."cgse.command"]
|
|
51
|
+
name = 'module:object'
|
|
52
|
+
```
|
|
53
|
+
Where:
|
|
54
|
+
|
|
55
|
+
- `name` is the name of the command
|
|
56
|
+
- `module` is a fully qualified module name for your package, a module that can be imported
|
|
57
|
+
- `object` is the name of the function that you want to add as a command
|
|
58
|
+
|
|
59
|
+
As an example, for the `cgse-tools` package, the `init` command of the `cgse` app is listed in
|
|
60
|
+
the `pyproject.toml` file as follows:
|
|
61
|
+
|
|
62
|
+
```toml
|
|
63
|
+
[project.entry-points."cgse.command"]
|
|
64
|
+
init = 'cgse_tools.cgse_commands:init'
|
|
65
|
+
```
|
|
66
|
+
The `init` function is defined in the `cgse_commands.py` module which is located in the
|
|
67
|
+
`cgse_tools` module in the `src` folder of the package:
|
|
68
|
+
|
|
69
|
+
```text
|
|
70
|
+
src
|
|
71
|
+
├── cgse_tools
|
|
72
|
+
│ ├── __init__.py
|
|
73
|
+
│ ├── cgse_commands.py
|
|
74
|
+
...
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Add a Command group
|
|
78
|
+
|
|
79
|
+
Some commands are more complicated and define a number of sub-commands. An example is the `show`
|
|
80
|
+
command where you currently have the sub-commands `env` and `settings`
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
$ cgse show --help
|
|
84
|
+
|
|
85
|
+
Usage: cgse show [OPTIONS] COMMAND [ARGS]...
|
|
86
|
+
|
|
87
|
+
Show information about settings, environment, setup, ...
|
|
88
|
+
|
|
89
|
+
╭─ Options ─────────────────────────────────────────────────────────────────────────────╮
|
|
90
|
+
│ --help Show this message and exit. │
|
|
91
|
+
╰───────────────────────────────────────────────────────────────────────────────────────╯
|
|
92
|
+
╭─ Commands ────────────────────────────────────────────────────────────────────────────╮
|
|
93
|
+
│ settings Show the settings that are defined by the installed packages. │
|
|
94
|
+
│ env Show the environment variables that are defined for the project. │
|
|
95
|
+
╰───────────────────────────────────────────────────────────────────────────────────────╯
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The `show` command is defined as a `typer.Typer()` object where `env` and `settings` are added
|
|
99
|
+
using the decorator `@<app>.command()`.
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
import typer
|
|
103
|
+
|
|
104
|
+
show = typer.Typer(help="Show information about settings, environment, setup, ...")
|
|
105
|
+
|
|
106
|
+
@show.command(name="settings")
|
|
107
|
+
def show_settings():
|
|
108
|
+
...
|
|
109
|
+
|
|
110
|
+
@show.command(name="env")
|
|
111
|
+
def show_env():
|
|
112
|
+
...
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
To add this command group to the `cgse` app, the following entry was used in the `pyproject.
|
|
116
|
+
toml` file of the `cgse-tools` project. Notice the `[group]` at the end of the entry which
|
|
117
|
+
indicates this is a command group instead of a single command.
|
|
118
|
+
|
|
119
|
+
```toml
|
|
120
|
+
[project.entry-points."cgse.command"]
|
|
121
|
+
show = 'cgse_tools.cgse_commands:show[group]'
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
### Add a Service
|
|
126
|
+
|
|
127
|
+
If your package provides a device driver or a specific service, use the `cgse.service`
|
|
128
|
+
entry-point group. Service entry-points follow the same scheme as command groups, i.e. they are
|
|
129
|
+
added to the `cgse` app as a `Typer()` object. Use the following entry in your `pyproject.toml`
|
|
130
|
+
file:
|
|
131
|
+
|
|
132
|
+
```toml
|
|
133
|
+
[project.entry-points."cgse.service"]
|
|
134
|
+
name = 'module:object'
|
|
135
|
+
```
|
|
136
|
+
where:
|
|
137
|
+
|
|
138
|
+
- `name` is the name of the service or device driver
|
|
139
|
+
- `module` is a fully qualified module name for your package, a module that can be imported
|
|
140
|
+
- `object` is the name of the `Typer()` object that you want to add as a service
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
## Register resources
|
|
144
|
+
|
|
145
|
+
TODO: what if two packages provide a resource `icons` ?
|
|
146
|
+
|
|
147
|
+
- known resources: icons, styles
|