cgse 2024.7.0__py3-none-any.whl → 2025.0.2__py3-none-any.whl
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.
- README.md +27 -0
- bump.py +85 -0
- cgse-2025.0.2.dist-info/METADATA +38 -0
- cgse-2025.0.2.dist-info/RECORD +5 -0
- {cgse-2024.7.0.dist-info → cgse-2025.0.2.dist-info}/WHEEL +1 -2
- cgse-2024.7.0.dist-info/COPYING +0 -674
- cgse-2024.7.0.dist-info/COPYING.LESSER +0 -165
- cgse-2024.7.0.dist-info/METADATA +0 -144
- cgse-2024.7.0.dist-info/RECORD +0 -660
- cgse-2024.7.0.dist-info/entry_points.txt +0 -75
- cgse-2024.7.0.dist-info/top_level.txt +0 -2
- egse/__init__.py +0 -12
- egse/__main__.py +0 -32
- egse/aeu/aeu.py +0 -5238
- egse/aeu/aeu_awg.yaml +0 -265
- egse/aeu/aeu_crio.yaml +0 -273
- egse/aeu/aeu_cs.py +0 -627
- egse/aeu/aeu_devif.py +0 -321
- egse/aeu/aeu_main_ui.py +0 -903
- egse/aeu/aeu_metrics.py +0 -131
- egse/aeu/aeu_protocol.py +0 -463
- egse/aeu/aeu_psu.yaml +0 -204
- egse/aeu/aeu_ui.py +0 -873
- egse/aeu/arbdata/FccdRead.arb +0 -2
- egse/aeu/arbdata/FccdRead_min_points.arb +0 -2
- egse/aeu/arbdata/HeaterSync_FccdRead.arb +0 -2
- egse/aeu/arbdata/HeaterSync_ccdRead25.arb +0 -2
- egse/aeu/arbdata/HeaterSync_ccdRead31_25.arb +0 -2
- egse/aeu/arbdata/HeaterSync_ccdRead37_50.arb +0 -2
- egse/aeu/arbdata/HeaterSync_ccdRead43_75.arb +0 -2
- egse/aeu/arbdata/HeaterSync_ccdRead50.arb +0 -2
- egse/aeu/arbdata/Heater_FccdRead_min_points.arb +0 -2
- egse/aeu/arbdata/ccdRead25.arb +0 -2
- egse/aeu/arbdata/ccdRead25_150ms.arb +0 -2
- egse/aeu/arbdata/ccdRead31_25.arb +0 -2
- egse/aeu/arbdata/ccdRead31_25_150ms.arb +0 -2
- egse/aeu/arbdata/ccdRead37_50.arb +0 -2
- egse/aeu/arbdata/ccdRead37_50_150ms.arb +0 -2
- egse/aeu/arbdata/ccdRead43_75.arb +0 -2
- egse/aeu/arbdata/ccdRead43_75_150ms.arb +0 -2
- egse/aeu/arbdata/ccdRead50.arb +0 -2
- egse/aeu/arbdata/ccdRead50_150ms.arb +0 -2
- egse/alert/__init__.py +0 -1049
- egse/alert/alertman.yaml +0 -37
- egse/alert/alertman_cs.py +0 -233
- egse/alert/alertman_ui.py +0 -600
- egse/alert/gsm/beaglebone.py +0 -138
- egse/alert/gsm/beaglebone.yaml +0 -51
- egse/alert/gsm/beaglebone_cs.py +0 -108
- egse/alert/gsm/beaglebone_devif.py +0 -122
- egse/alert/gsm/beaglebone_protocol.py +0 -46
- egse/bits.py +0 -318
- egse/camera.py +0 -44
- egse/collimator/__init__.py +0 -0
- egse/collimator/fcul/__init__.py +0 -0
- egse/collimator/fcul/ogse.py +0 -1077
- egse/collimator/fcul/ogse.yaml +0 -14
- egse/collimator/fcul/ogse_cs.py +0 -154
- egse/collimator/fcul/ogse_devif.py +0 -358
- egse/collimator/fcul/ogse_protocol.py +0 -132
- egse/collimator/fcul/ogse_sim.py +0 -431
- egse/collimator/fcul/ogse_ui.py +0 -1108
- egse/command.py +0 -699
- egse/config.py +0 -410
- egse/confman/__init__.py +0 -1058
- egse/confman/confman.yaml +0 -70
- egse/confman/confman_cs.py +0 -240
- egse/confman/confman_ui.py +0 -381
- egse/confman/setup_ui.py +0 -565
- egse/control.py +0 -632
- egse/coordinates/__init__.py +0 -534
- egse/coordinates/avoidance.py +0 -100
- egse/coordinates/cslmodel.py +0 -127
- egse/coordinates/laser_tracker_to_dict.py +0 -122
- egse/coordinates/point.py +0 -707
- egse/coordinates/pyplot.py +0 -194
- egse/coordinates/referenceFrame.py +0 -1279
- egse/coordinates/refmodel.py +0 -737
- egse/coordinates/rotationMatrix.py +0 -85
- egse/coordinates/transform3d_addon.py +0 -419
- egse/csl/__init__.py +0 -50
- egse/csl/commanding.py +0 -78
- egse/csl/icons/hexapod-connected-selected.svg +0 -30
- egse/csl/icons/hexapod-connected.svg +0 -30
- egse/csl/icons/hexapod-homing-selected.svg +0 -68
- egse/csl/icons/hexapod-homing.svg +0 -68
- egse/csl/icons/hexapod-retract-selected.svg +0 -56
- egse/csl/icons/hexapod-retract.svg +0 -51
- egse/csl/icons/hexapod-zero-selected.svg +0 -56
- egse/csl/icons/hexapod-zero.svg +0 -56
- egse/csl/icons/logo-puna.svg +0 -92
- egse/csl/icons/stop.svg +0 -1
- egse/csl/initialisation.py +0 -102
- egse/csl/mech_pos_settings.yaml +0 -18
- egse/das.py +0 -1240
- egse/das.yaml +0 -7
- egse/data/conf/SETUP_CSL_00000_170620_150000.yaml +0 -5
- egse/data/conf/SETUP_CSL_00001_170620_151010.yaml +0 -69
- egse/data/conf/SETUP_CSL_00002_170620_151020.yaml +0 -69
- egse/data/conf/SETUP_CSL_00003_170620_151030.yaml +0 -69
- egse/data/conf/SETUP_CSL_00004_170620_151040.yaml +0 -69
- egse/data/conf/SETUP_CSL_00005_170620_151050.yaml +0 -69
- egse/data/conf/SETUP_CSL_00006_170620_151060.yaml +0 -69
- egse/data/conf/SETUP_CSL_00007_170620_151070.yaml +0 -69
- egse/data/conf/SETUP_CSL_00008_170620_151080.yaml +0 -75
- egse/data/conf/SETUP_CSL_00010_210308_083016.yaml +0 -138
- egse/data/conf/SETUP_INTA_00000_170620_150000.yaml +0 -4
- egse/data/conf/SETUP_SRON_00000_170620_150000.yaml +0 -4
- egse/decorators.py +0 -514
- egse/device.py +0 -269
- egse/dpu/__init__.py +0 -2698
- egse/dpu/ccd_ui.py +0 -514
- egse/dpu/dpu.py +0 -783
- egse/dpu/dpu.yaml +0 -153
- egse/dpu/dpu_cs.py +0 -272
- egse/dpu/dpu_ui.py +0 -671
- egse/dpu/fitsgen.py +0 -2096
- egse/dpu/fitsgen_ui.py +0 -399
- egse/dpu/hdf5_model.py +0 -332
- egse/dpu/hdf5_ui.py +0 -277
- egse/dpu/hdf5_viewer.py +0 -506
- egse/dpu/hk_ui.py +0 -468
- egse/dpu_commands.py +0 -81
- egse/dsi/__init__.py +0 -33
- egse/dsi/_libesl.py +0 -232
- egse/dsi/constants.py +0 -296
- egse/dsi/esl.py +0 -630
- egse/dsi/rmap.py +0 -444
- egse/dsi/rmapci.py +0 -39
- egse/dsi/spw.py +0 -335
- egse/dsi/spw_state.py +0 -29
- egse/dummy.py +0 -318
- egse/dyndummy.py +0 -179
- egse/env.py +0 -278
- egse/exceptions.py +0 -88
- egse/fdir/__init__.py +0 -26
- egse/fdir/fdir_manager.py +0 -85
- egse/fdir/fdir_manager.yaml +0 -37
- egse/fdir/fdir_manager_controller.py +0 -136
- egse/fdir/fdir_manager_cs.py +0 -164
- egse/fdir/fdir_manager_interface.py +0 -15
- egse/fdir/fdir_remote.py +0 -73
- egse/fdir/fdir_remote.yaml +0 -30
- egse/fdir/fdir_remote_controller.py +0 -30
- egse/fdir/fdir_remote_cs.py +0 -94
- egse/fdir/fdir_remote_interface.py +0 -9
- egse/fdir/fdir_remote_popup.py +0 -26
- egse/fee/__init__.py +0 -106
- egse/fee/f_fee_register.yaml +0 -43
- egse/fee/feesim.py +0 -914
- egse/fee/n_fee_hk.py +0 -768
- egse/fee/nfee.py +0 -188
- egse/filterwheel/__init__.py +0 -4
- egse/filterwheel/eksma/__init__.py +0 -49
- egse/filterwheel/eksma/fw8smc4.py +0 -657
- egse/filterwheel/eksma/fw8smc4.yaml +0 -121
- egse/filterwheel/eksma/fw8smc4_cs.py +0 -144
- egse/filterwheel/eksma/fw8smc4_devif.py +0 -473
- egse/filterwheel/eksma/fw8smc4_protocol.py +0 -82
- egse/filterwheel/eksma/fw8smc4_ui.py +0 -940
- egse/filterwheel/eksma/fw8smc5.py +0 -115
- egse/filterwheel/eksma/fw8smc5.yaml +0 -105
- egse/filterwheel/eksma/fw8smc5_controller.py +0 -307
- egse/filterwheel/eksma/fw8smc5_cs.py +0 -141
- egse/filterwheel/eksma/fw8smc5_interface.py +0 -65
- egse/filterwheel/eksma/fw8smc5_simulator.py +0 -29
- egse/filterwheel/eksma/fw8smc5_ui.py +0 -1065
- egse/filterwheel/eksma/testpythonfw.py +0 -215
- egse/fov/__init__.py +0 -65
- egse/fov/fov_hk.py +0 -710
- egse/fov/fov_ui.py +0 -859
- egse/fov/fov_ui_controller.py +0 -140
- egse/fov/fov_ui_model.py +0 -200
- egse/fov/fov_ui_view.py +0 -345
- egse/gimbal/__init__.py +0 -32
- egse/gimbal/symetrie/__init__.py +0 -26
- egse/gimbal/symetrie/alpha.py +0 -586
- egse/gimbal/symetrie/generic_gimbal_ui.py +0 -1521
- egse/gimbal/symetrie/gimbal.py +0 -877
- egse/gimbal/symetrie/gimbal.yaml +0 -168
- egse/gimbal/symetrie/gimbal_cs.py +0 -183
- egse/gimbal/symetrie/gimbal_protocol.py +0 -138
- egse/gimbal/symetrie/gimbal_ui.py +0 -361
- egse/gimbal/symetrie/pmac.py +0 -1006
- egse/gimbal/symetrie/pmac_regex.py +0 -83
- egse/graph.py +0 -132
- egse/gui/__init__.py +0 -47
- egse/gui/buttons.py +0 -378
- egse/gui/focalplane.py +0 -1285
- egse/gui/formatter.py +0 -10
- egse/gui/led.py +0 -162
- egse/gui/limitswitch.py +0 -143
- egse/gui/mechanisms.py +0 -587
- egse/gui/states.py +0 -148
- egse/gui/stripchart.py +0 -729
- egse/gui/styles.qss +0 -48
- egse/gui/switch.py +0 -112
- egse/h5.py +0 -274
- egse/help/__init__.py +0 -0
- egse/help/help_ui.py +0 -126
- egse/hexapod/__init__.py +0 -32
- egse/hexapod/symetrie/__init__.py +0 -137
- egse/hexapod/symetrie/alpha.py +0 -874
- egse/hexapod/symetrie/dynalpha.py +0 -1387
- egse/hexapod/symetrie/hexapod_ui.py +0 -1516
- egse/hexapod/symetrie/pmac.py +0 -1010
- egse/hexapod/symetrie/pmac_regex.py +0 -83
- egse/hexapod/symetrie/puna.py +0 -1167
- egse/hexapod/symetrie/puna.yaml +0 -193
- egse/hexapod/symetrie/puna_cs.py +0 -195
- egse/hexapod/symetrie/puna_protocol.py +0 -134
- egse/hexapod/symetrie/puna_ui.py +0 -433
- egse/hexapod/symetrie/punaplus.py +0 -107
- egse/hexapod/symetrie/zonda.py +0 -872
- egse/hexapod/symetrie/zonda.yaml +0 -337
- egse/hexapod/symetrie/zonda_cs.py +0 -172
- egse/hexapod/symetrie/zonda_devif.py +0 -414
- egse/hexapod/symetrie/zonda_protocol.py +0 -123
- egse/hexapod/symetrie/zonda_ui.py +0 -449
- egse/hk.py +0 -791
- egse/icons/aeu-cs-start.svg +0 -117
- egse/icons/aeu-cs-stop.svg +0 -118
- egse/icons/aeu-cs.svg +0 -107
- egse/icons/aeu_cs-started.svg +0 -112
- egse/icons/aeu_cs-stopped.svg +0 -112
- egse/icons/aeu_cs.svg +0 -55
- egse/icons/alert.svg +0 -1
- egse/icons/arrow-double-left.png +0 -0
- egse/icons/arrow-double-right.png +0 -0
- egse/icons/arrow-up.svg +0 -11
- egse/icons/backward.svg +0 -1
- egse/icons/busy.svg +0 -1
- egse/icons/cleaning.svg +0 -115
- egse/icons/color-scheme.svg +0 -1
- egse/icons/cs-connected-alert.svg +0 -91
- egse/icons/cs-connected-disabled.svg +0 -43
- egse/icons/cs-connected.svg +0 -89
- egse/icons/cs-not-connected.svg +0 -44
- egse/icons/double-left-arrow.svg +0 -1
- egse/icons/double-right-arrow.svg +0 -1
- egse/icons/erase-disabled.svg +0 -19
- egse/icons/erase.svg +0 -59
- egse/icons/fitsgen-start.svg +0 -47
- egse/icons/fitsgen-stop.svg +0 -48
- egse/icons/fitsgen.svg +0 -1
- egse/icons/forward.svg +0 -1
- egse/icons/fov-hk-start.svg +0 -33
- egse/icons/fov-hk-stop.svg +0 -37
- egse/icons/fov-hk.svg +0 -1
- egse/icons/front-desk.svg +0 -1
- egse/icons/home-actioned.svg +0 -15
- egse/icons/home-disabled.svg +0 -15
- egse/icons/home.svg +0 -13
- egse/icons/info.svg +0 -1
- egse/icons/invalid.png +0 -0
- egse/icons/led-green.svg +0 -20
- egse/icons/led-grey.svg +0 -20
- egse/icons/led-orange.svg +0 -20
- egse/icons/led-red.svg +0 -20
- egse/icons/led-square-green.svg +0 -134
- egse/icons/led-square-grey.svg +0 -134
- egse/icons/led-square-orange.svg +0 -134
- egse/icons/led-square-red.svg +0 -134
- egse/icons/limit-switch-all-green.svg +0 -115
- egse/icons/limit-switch-all-red.svg +0 -117
- egse/icons/limit-switch-el+.svg +0 -116
- egse/icons/limit-switch-el-.svg +0 -117
- egse/icons/location-marker.svg +0 -1
- egse/icons/logo-dpu.svg +0 -48
- egse/icons/logo-gimbal.svg +0 -112
- egse/icons/logo-huber.svg +0 -23
- egse/icons/logo-ogse.svg +0 -31
- egse/icons/logo-puna.svg +0 -92
- egse/icons/logo-tcs.svg +0 -29
- egse/icons/logo-zonda.svg +0 -66
- egse/icons/maximize.svg +0 -1
- egse/icons/meter.svg +0 -1
- egse/icons/more.svg +0 -45
- egse/icons/n-fee-hk-start.svg +0 -24
- egse/icons/n-fee-hk-stop.svg +0 -25
- egse/icons/n-fee-hk.svg +0 -83
- egse/icons/observing-off.svg +0 -46
- egse/icons/observing-on.svg +0 -46
- egse/icons/open-document-hdf5.png +0 -0
- egse/icons/open-document-hdf5.svg +0 -21
- egse/icons/ops-mode.svg +0 -1
- egse/icons/play-green.svg +0 -17
- egse/icons/plugged-disabled.svg +0 -27
- egse/icons/plugged.svg +0 -21
- egse/icons/pm_ui.svg +0 -1
- egse/icons/power-button-green.svg +0 -27
- egse/icons/power-button-red.svg +0 -27
- egse/icons/power-button.svg +0 -27
- egse/icons/radar.svg +0 -1
- egse/icons/radioactive.svg +0 -2
- egse/icons/reload.svg +0 -1
- egse/icons/remote-control-off.svg +0 -28
- egse/icons/remote-control-on.svg +0 -28
- egse/icons/repeat-blue.svg +0 -15
- egse/icons/repeat.svg +0 -1
- egse/icons/settings.svg +0 -1
- egse/icons/shrink.svg +0 -1
- egse/icons/shutter.svg +0 -1
- egse/icons/sign-off.svg +0 -1
- egse/icons/sign-on.svg +0 -1
- egse/icons/sim-mode.svg +0 -1
- egse/icons/small-buttons-go.svg +0 -20
- egse/icons/small-buttons-minus.svg +0 -51
- egse/icons/small-buttons-plus.svg +0 -51
- egse/icons/sponge.svg +0 -220
- egse/icons/start-button-disabled.svg +0 -84
- egse/icons/start-button.svg +0 -50
- egse/icons/stop-button-disabled.svg +0 -84
- egse/icons/stop-button.svg +0 -50
- egse/icons/stop-red.svg +0 -17
- egse/icons/stop.svg +0 -1
- egse/icons/switch-disabled-square.svg +0 -87
- egse/icons/switch-disabled.svg +0 -15
- egse/icons/switch-off-square.svg +0 -87
- egse/icons/switch-off.svg +0 -72
- egse/icons/switch-on-square.svg +0 -87
- egse/icons/switch-on.svg +0 -61
- egse/icons/temperature-control.svg +0 -44
- egse/icons/th_ui_logo.svg +0 -1
- egse/icons/unplugged.svg +0 -23
- egse/icons/unvalid.png +0 -0
- egse/icons/user-interface.svg +0 -1
- egse/icons/vacuum.svg +0 -1
- egse/icons/valid.png +0 -0
- egse/icons/zoom-to-pixel-dark.svg +0 -64
- egse/icons/zoom-to-pixel-white.svg +0 -36
- egse/images/big-rotation-stage.png +0 -0
- egse/images/connected-100.png +0 -0
- egse/images/cross.svg +0 -6
- egse/images/disconnected-100.png +0 -0
- egse/images/gui-icon.png +0 -0
- egse/images/home.svg +0 -6
- egse/images/info-icon.png +0 -0
- egse/images/led-black.svg +0 -89
- egse/images/led-green.svg +0 -85
- egse/images/led-orange.svg +0 -85
- egse/images/led-red.svg +0 -85
- egse/images/load-icon.png +0 -0
- egse/images/load-setup.png +0 -0
- egse/images/load.png +0 -0
- egse/images/pause.png +0 -0
- egse/images/play-button.svg +0 -8
- egse/images/play.png +0 -0
- egse/images/process-status.png +0 -0
- egse/images/restart.png +0 -0
- egse/images/search.png +0 -0
- egse/images/sma.png +0 -0
- egse/images/start.png +0 -0
- egse/images/stop-button.svg +0 -8
- egse/images/stop.png +0 -0
- egse/images/switch-off.svg +0 -48
- egse/images/switch-on.svg +0 -48
- egse/images/undo.png +0 -0
- egse/images/update-button.svg +0 -11
- egse/imageviewer/exposureselection.py +0 -475
- egse/imageviewer/imageviewer.py +0 -198
- egse/imageviewer/matchfocalplane.py +0 -179
- egse/imageviewer/subfieldposition.py +0 -133
- egse/lampcontrol/__init__.py +0 -4
- egse/lampcontrol/beaglebone/beaglebone.py +0 -178
- egse/lampcontrol/beaglebone/beaglebone.yaml +0 -62
- egse/lampcontrol/beaglebone/beaglebone_cs.py +0 -106
- egse/lampcontrol/beaglebone/beaglebone_devif.py +0 -150
- egse/lampcontrol/beaglebone/beaglebone_protocol.py +0 -73
- egse/lampcontrol/energetiq/__init__.py +0 -22
- egse/lampcontrol/energetiq/eq99.yaml +0 -98
- egse/lampcontrol/energetiq/lampEQ99.py +0 -283
- egse/lampcontrol/energetiq/lampEQ99_cs.py +0 -128
- egse/lampcontrol/energetiq/lampEQ99_devif.py +0 -158
- egse/lampcontrol/energetiq/lampEQ99_encode_decode_errors.py +0 -73
- egse/lampcontrol/energetiq/lampEQ99_protocol.py +0 -71
- egse/lampcontrol/energetiq/lampEQ99_ui.py +0 -465
- egse/lib/CentOS-7/EtherSpaceLink_v34_86.dylib +0 -0
- egse/lib/CentOS-8/ESL-RMAP_v34_86.dylib +0 -0
- egse/lib/CentOS-8/EtherSpaceLink_v34_86.dylib +0 -0
- egse/lib/Debian/ESL-RMAP_v34_86.dylib +0 -0
- egse/lib/Debian/EtherSpaceLink_v34_86.dylib +0 -0
- egse/lib/Debian/libetherspacelink_v35_21.dylib +0 -0
- egse/lib/Linux/ESL-RMAP_v34_86.dylib +0 -0
- egse/lib/Linux/EtherSpaceLink_v34_86.dylib +0 -0
- egse/lib/Ubuntu-20/ESL-RMAP_v34_86.dylib +0 -0
- egse/lib/Ubuntu-20/EtherSpaceLink_v34_86.dylib +0 -0
- egse/lib/gssw/python3-gssw_2.2.3+31f63c9f-1_all.deb +0 -0
- egse/lib/ximc/__pycache__/pyximc.cpython-38 2.pyc +0 -0
- egse/lib/ximc/__pycache__/pyximc.cpython-38.pyc +0 -0
- egse/lib/ximc/libximc.framework/Frameworks/libbindy.dylib +0 -0
- egse/lib/ximc/libximc.framework/Frameworks/libxiwrapper.dylib +0 -0
- egse/lib/ximc/libximc.framework/Headers/ximc.h +0 -5510
- egse/lib/ximc/libximc.framework/Resources/Info.plist +0 -42
- egse/lib/ximc/libximc.framework/Resources/keyfile.sqlite +0 -0
- egse/lib/ximc/libximc.framework/libbindy.so +0 -0
- egse/lib/ximc/libximc.framework/libximc +0 -0
- egse/lib/ximc/libximc.framework/libximc.so +0 -0
- egse/lib/ximc/libximc.framework/libximc.so.7.0.0 +0 -0
- egse/lib/ximc/libximc.framework/libxiwrapper.so +0 -0
- egse/lib/ximc/pyximc.py +0 -922
- egse/listener.py +0 -179
- egse/logger/__init__.py +0 -243
- egse/logger/log_cs.py +0 -321
- egse/metrics.py +0 -102
- egse/mixin.py +0 -464
- egse/monitoring.py +0 -95
- egse/ni/alarms/__init__.py +0 -26
- egse/ni/alarms/cdaq9375.py +0 -300
- egse/ni/alarms/cdaq9375.yaml +0 -89
- egse/ni/alarms/cdaq9375_cs.py +0 -130
- egse/ni/alarms/cdaq9375_devif.py +0 -183
- egse/ni/alarms/cdaq9375_protocol.py +0 -48
- egse/obs_inspection.py +0 -165
- egse/observer.py +0 -41
- egse/obsid.py +0 -163
- egse/powermeter/__init__.py +0 -0
- egse/powermeter/ni/__init__.py +0 -38
- egse/powermeter/ni/cdaq9184.py +0 -224
- egse/powermeter/ni/cdaq9184.yaml +0 -73
- egse/powermeter/ni/cdaq9184_cs.py +0 -130
- egse/powermeter/ni/cdaq9184_devif.py +0 -201
- egse/powermeter/ni/cdaq9184_protocol.py +0 -48
- egse/powermeter/ni/cdaq9184_ui.py +0 -544
- egse/powermeter/thorlabs/__init__.py +0 -25
- egse/powermeter/thorlabs/pm100a.py +0 -380
- egse/powermeter/thorlabs/pm100a.yaml +0 -132
- egse/powermeter/thorlabs/pm100a_cs.py +0 -136
- egse/powermeter/thorlabs/pm100a_devif.py +0 -127
- egse/powermeter/thorlabs/pm100a_protocol.py +0 -80
- egse/powermeter/thorlabs/pm100a_ui.py +0 -725
- egse/process.py +0 -451
- egse/procman/__init__.py +0 -834
- egse/procman/cannot_start_process_popup.py +0 -43
- egse/procman/procman.yaml +0 -49
- egse/procman/procman_cs.py +0 -201
- egse/procman/procman_ui.py +0 -2081
- egse/protocol.py +0 -605
- egse/proxy.py +0 -531
- egse/randomwalk.py +0 -140
- egse/reg.py +0 -585
- egse/reload.py +0 -122
- egse/reprocess.py +0 -693
- egse/resource.py +0 -333
- egse/rmap.py +0 -406
- egse/rst.py +0 -135
- egse/search.py +0 -182
- egse/serialdevice.py +0 -190
- egse/services.py +0 -247
- egse/services.yaml +0 -68
- egse/settings.py +0 -379
- egse/settings.yaml +0 -980
- egse/setup.py +0 -1181
- egse/shutter/__init__.py +0 -0
- egse/shutter/thorlabs/__init__.py +0 -19
- egse/shutter/thorlabs/ksc101.py +0 -205
- egse/shutter/thorlabs/ksc101.yaml +0 -105
- egse/shutter/thorlabs/ksc101_cs.py +0 -136
- egse/shutter/thorlabs/ksc101_devif.py +0 -201
- egse/shutter/thorlabs/ksc101_protocol.py +0 -71
- egse/shutter/thorlabs/ksc101_ui.py +0 -548
- egse/shutter/thorlabs/sc10.py +0 -82
- egse/shutter/thorlabs/sc10.yaml +0 -52
- egse/shutter/thorlabs/sc10_controller.py +0 -81
- egse/shutter/thorlabs/sc10_cs.py +0 -108
- egse/shutter/thorlabs/sc10_interface.py +0 -25
- egse/shutter/thorlabs/sc10_simulator.py +0 -30
- egse/simulator.py +0 -41
- egse/slack.py +0 -61
- egse/socketdevice.py +0 -218
- egse/sockets.py +0 -218
- egse/spw.py +0 -1401
- egse/stages/__init__.py +0 -12
- egse/stages/aerotech/ensemble.py +0 -245
- egse/stages/aerotech/ensemble.yaml +0 -205
- egse/stages/aerotech/ensemble_controller.py +0 -275
- egse/stages/aerotech/ensemble_cs.py +0 -110
- egse/stages/aerotech/ensemble_interface.py +0 -132
- egse/stages/aerotech/ensemble_parameters.py +0 -433
- egse/stages/aerotech/ensemble_simulator.py +0 -27
- egse/stages/aerotech/mgse_sim.py +0 -188
- egse/stages/arun/smd3.py +0 -110
- egse/stages/arun/smd3.yaml +0 -68
- egse/stages/arun/smd3_controller.py +0 -470
- egse/stages/arun/smd3_cs.py +0 -112
- egse/stages/arun/smd3_interface.py +0 -53
- egse/stages/arun/smd3_simulator.py +0 -27
- egse/stages/arun/smd3_stop.py +0 -16
- egse/stages/huber/__init__.py +0 -49
- egse/stages/huber/smc9300.py +0 -920
- egse/stages/huber/smc9300.yaml +0 -63
- egse/stages/huber/smc9300_cs.py +0 -178
- egse/stages/huber/smc9300_devif.py +0 -345
- egse/stages/huber/smc9300_protocol.py +0 -113
- egse/stages/huber/smc9300_sim.py +0 -547
- egse/stages/huber/smc9300_ui.py +0 -973
- egse/state.py +0 -173
- egse/statemachine.py +0 -274
- egse/storage/__init__.py +0 -1067
- egse/storage/persistence.py +0 -2295
- egse/storage/storage.yaml +0 -79
- egse/storage/storage_cs.py +0 -231
- egse/styles/dark.qss +0 -343
- egse/styles/default.qss +0 -48
- egse/synoptics/__init__.py +0 -417
- egse/synoptics/syn.yaml +0 -9
- egse/synoptics/syn_cs.py +0 -195
- egse/system.py +0 -1611
- egse/tcs/__init__.py +0 -14
- egse/tcs/tcs.py +0 -879
- egse/tcs/tcs.yaml +0 -14
- egse/tcs/tcs_cs.py +0 -202
- egse/tcs/tcs_devif.py +0 -292
- egse/tcs/tcs_protocol.py +0 -180
- egse/tcs/tcs_sim.py +0 -177
- egse/tcs/tcs_ui.py +0 -543
- egse/tdms.py +0 -171
- egse/tempcontrol/__init__.py +0 -23
- egse/tempcontrol/agilent/agilent34970.py +0 -109
- egse/tempcontrol/agilent/agilent34970.yaml +0 -44
- egse/tempcontrol/agilent/agilent34970_cs.py +0 -114
- egse/tempcontrol/agilent/agilent34970_devif.py +0 -182
- egse/tempcontrol/agilent/agilent34970_protocol.py +0 -96
- egse/tempcontrol/agilent/agilent34972.py +0 -111
- egse/tempcontrol/agilent/agilent34972.yaml +0 -44
- egse/tempcontrol/agilent/agilent34972_cs.py +0 -115
- egse/tempcontrol/agilent/agilent34972_devif.py +0 -189
- egse/tempcontrol/agilent/agilent34972_protocol.py +0 -98
- egse/tempcontrol/beaglebone/beaglebone.py +0 -341
- egse/tempcontrol/beaglebone/beaglebone.yaml +0 -110
- egse/tempcontrol/beaglebone/beaglebone_cs.py +0 -117
- egse/tempcontrol/beaglebone/beaglebone_protocol.py +0 -134
- egse/tempcontrol/beaglebone/beaglebone_ui.py +0 -674
- egse/tempcontrol/digalox/digalox.py +0 -115
- egse/tempcontrol/digalox/digalox.yaml +0 -36
- egse/tempcontrol/digalox/digalox_cs.py +0 -108
- egse/tempcontrol/digalox/digalox_protocol.py +0 -56
- egse/tempcontrol/keithley/__init__.py +0 -33
- egse/tempcontrol/keithley/daq6510.py +0 -662
- egse/tempcontrol/keithley/daq6510.yaml +0 -105
- egse/tempcontrol/keithley/daq6510_cs.py +0 -163
- egse/tempcontrol/keithley/daq6510_devif.py +0 -343
- egse/tempcontrol/keithley/daq6510_protocol.py +0 -79
- egse/tempcontrol/keithley/daq6510_sim.py +0 -186
- egse/tempcontrol/lakeshore/__init__.py +0 -33
- egse/tempcontrol/lakeshore/lsci.py +0 -361
- egse/tempcontrol/lakeshore/lsci.yaml +0 -162
- egse/tempcontrol/lakeshore/lsci_cs.py +0 -174
- egse/tempcontrol/lakeshore/lsci_devif.py +0 -292
- egse/tempcontrol/lakeshore/lsci_protocol.py +0 -76
- egse/tempcontrol/lakeshore/lsci_ui.py +0 -387
- egse/tempcontrol/ni/__init__.py +0 -0
- egse/tempcontrol/spid/spid.py +0 -109
- egse/tempcontrol/spid/spid.yaml +0 -81
- egse/tempcontrol/spid/spid_controller.py +0 -279
- egse/tempcontrol/spid/spid_cs.py +0 -136
- egse/tempcontrol/spid/spid_protocol.py +0 -107
- egse/tempcontrol/spid/spid_ui.py +0 -723
- egse/tempcontrol/srs/__init__.py +0 -22
- egse/tempcontrol/srs/ptc10.py +0 -867
- egse/tempcontrol/srs/ptc10.yaml +0 -227
- egse/tempcontrol/srs/ptc10_cs.py +0 -128
- egse/tempcontrol/srs/ptc10_devif.py +0 -116
- egse/tempcontrol/srs/ptc10_protocol.py +0 -39
- egse/tempcontrol/srs/ptc10_ui.py +0 -906
- egse/ups/apc/apc.py +0 -236
- egse/ups/apc/apc.yaml +0 -45
- egse/ups/apc/apc_cs.py +0 -101
- egse/ups/apc/apc_protocol.py +0 -125
- egse/user.yaml +0 -7
- egse/vacuum/beaglebone/beaglebone.py +0 -149
- egse/vacuum/beaglebone/beaglebone.yaml +0 -44
- egse/vacuum/beaglebone/beaglebone_cs.py +0 -108
- egse/vacuum/beaglebone/beaglebone_devif.py +0 -159
- egse/vacuum/beaglebone/beaglebone_protocol.py +0 -192
- egse/vacuum/beaglebone/beaglebone_ui.py +0 -638
- egse/vacuum/instrutech/igm402.py +0 -91
- egse/vacuum/instrutech/igm402.yaml +0 -90
- egse/vacuum/instrutech/igm402_controller.py +0 -124
- egse/vacuum/instrutech/igm402_cs.py +0 -108
- egse/vacuum/instrutech/igm402_interface.py +0 -49
- egse/vacuum/instrutech/igm402_simulator.py +0 -36
- egse/vacuum/keller/kellerBus.py +0 -256
- egse/vacuum/keller/leo3.py +0 -100
- egse/vacuum/keller/leo3.yaml +0 -38
- egse/vacuum/keller/leo3_controller.py +0 -81
- egse/vacuum/keller/leo3_cs.py +0 -101
- egse/vacuum/keller/leo3_interface.py +0 -33
- egse/vacuum/mks/evision.py +0 -86
- egse/vacuum/mks/evision.yaml +0 -75
- egse/vacuum/mks/evision_cs.py +0 -101
- egse/vacuum/mks/evision_devif.py +0 -313
- egse/vacuum/mks/evision_interface.py +0 -60
- egse/vacuum/mks/evision_simulator.py +0 -24
- egse/vacuum/mks/evision_ui.py +0 -701
- egse/vacuum/pfeiffer/acp40.py +0 -87
- egse/vacuum/pfeiffer/acp40.yaml +0 -60
- egse/vacuum/pfeiffer/acp40_controller.py +0 -117
- egse/vacuum/pfeiffer/acp40_cs.py +0 -109
- egse/vacuum/pfeiffer/acp40_interface.py +0 -40
- egse/vacuum/pfeiffer/acp40_simulator.py +0 -37
- egse/vacuum/pfeiffer/tc400.py +0 -87
- egse/vacuum/pfeiffer/tc400.yaml +0 -83
- egse/vacuum/pfeiffer/tc400_controller.py +0 -136
- egse/vacuum/pfeiffer/tc400_cs.py +0 -109
- egse/vacuum/pfeiffer/tc400_interface.py +0 -70
- egse/vacuum/pfeiffer/tc400_simulator.py +0 -35
- egse/vacuum/pfeiffer/tpg261.py +0 -80
- egse/vacuum/pfeiffer/tpg261.yaml +0 -66
- egse/vacuum/pfeiffer/tpg261_controller.py +0 -150
- egse/vacuum/pfeiffer/tpg261_cs.py +0 -109
- egse/vacuum/pfeiffer/tpg261_interface.py +0 -59
- egse/vacuum/pfeiffer/tpg261_simulator.py +0 -23
- egse/version.py +0 -174
- egse/visitedpositions.py +0 -398
- egse/windowing.py +0 -213
- egse/zmq/__init__.py +0 -28
- egse/zmq/spw.py +0 -160
- egse/zmq_ser.py +0 -41
- scripts/alerts/cold.yaml +0 -278
- scripts/alerts/example_alerts.yaml +0 -54
- scripts/alerts/transition.yaml +0 -14
- scripts/alerts/warm.yaml +0 -49
- scripts/analyse_n_fee_hk_data.py +0 -52
- scripts/check_hdf5_files.py +0 -192
- scripts/check_register_sync.py +0 -47
- scripts/check_tcs_calib_coef.py +0 -90
- scripts/correct_ccd_cold_temperature_cal.py +0 -157
- scripts/create_hdf5_report.py +0 -293
- scripts/csl_model.py +0 -420
- scripts/csl_restore_setup.py +0 -229
- scripts/export-grafana-dashboards.py +0 -49
- scripts/fdir/cs_recovery/fdir_cs_recovery.py +0 -54
- scripts/fdir/fdir_table.yaml +0 -70
- scripts/fdir/fdir_test_recovery.py +0 -10
- scripts/fdir/hw_recovery/fdir_agilent_hw_recovery.py +0 -73
- scripts/fdir/limit_recovery/fdir_agilent_limit.py +0 -61
- scripts/fdir/limit_recovery/fdir_bb_heater_limit.py +0 -59
- scripts/fdir/limit_recovery/fdir_ensemble_limit.py +0 -33
- scripts/fdir/limit_recovery/fdir_pressure_limit_recovery.py +0 -71
- scripts/fix_csv.py +0 -80
- scripts/ias/correct_ccd_temp_cal_elfique.py +0 -43
- scripts/ias/correct_ccd_temp_cal_floreffe.py +0 -43
- scripts/ias/correct_trp_swap_achel.py +0 -199
- scripts/inta/correct_ccd_temp_cal_duvel.py +0 -43
- scripts/inta/correct_ccd_temp_cal_gueuze.py +0 -43
- scripts/n_fee_supply_voltage_calculation.py +0 -92
- scripts/playground.py +0 -30
- scripts/print_hdf5_hk_data.py +0 -68
- scripts/print_register_map.py +0 -43
- scripts/remove_lines_between_matches.py +0 -188
- scripts/sron/commanding/control_heaters.py +0 -44
- scripts/sron/commanding/pumpdown.py +0 -46
- scripts/sron/commanding/set_pid_setpoint.py +0 -19
- scripts/sron/commanding/shutdown_bbb_heaters.py +0 -10
- scripts/sron/commanding/shutdown_pumps.py +0 -33
- scripts/sron/correct_mgse_coordinates_brigand_chimay.py +0 -272
- scripts/sron/correct_trp_swap_brigand.py +0 -204
- scripts/sron/gimbal_conversions.py +0 -75
- scripts/sron/tm_gen/tm_gen_agilent.py +0 -37
- scripts/sron/tm_gen/tm_gen_heaters.py +0 -4
- scripts/sron/tm_gen/tm_gen_spid.py +0 -13
- scripts/update_operational_cgse.py +0 -268
- scripts/update_operational_cgse_old.py +0 -273
egse/spw.py
DELETED
|
@@ -1,1401 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
This module defines classes and functions to work with SpaceWire packets.
|
|
3
|
-
"""
|
|
4
|
-
import logging
|
|
5
|
-
import os
|
|
6
|
-
import struct
|
|
7
|
-
import textwrap
|
|
8
|
-
from enum import IntEnum
|
|
9
|
-
from typing import Tuple
|
|
10
|
-
from typing import Union
|
|
11
|
-
|
|
12
|
-
import numpy as np
|
|
13
|
-
|
|
14
|
-
import egse.rmap
|
|
15
|
-
from egse.bits import clear_bit
|
|
16
|
-
from egse.bits import crc_calc
|
|
17
|
-
from egse.bits import set_bit
|
|
18
|
-
from egse.exceptions import Error
|
|
19
|
-
from egse.setup import SetupError
|
|
20
|
-
from egse.state import GlobalState
|
|
21
|
-
|
|
22
|
-
MODULE_LOGGER = logging.getLogger(__name__)
|
|
23
|
-
|
|
24
|
-
try:
|
|
25
|
-
_ = os.environ["PLATO_CAMERA_IS_EM"]
|
|
26
|
-
MODULE_LOGGER.warning(
|
|
27
|
-
textwrap.dedent("""\
|
|
28
|
-
The PLATO_CAMERA_IS_EM environment variable is defined.
|
|
29
|
-
For the EM camera, image data and camera sensor data are interpreted as twos-complement and
|
|
30
|
-
converted accordingly. If the camera you are testing is not the EM camera, make sure the
|
|
31
|
-
PLATO_CAMERA_IS_EM environment variable is not defined when starting your control servers.
|
|
32
|
-
"""
|
|
33
|
-
)
|
|
34
|
-
)
|
|
35
|
-
TWOS_COMPLEMENT_OFFSET = 32768 if _.capitalize() in ("1", "True", "Yes") else 0
|
|
36
|
-
except KeyError:
|
|
37
|
-
TWOS_COMPLEMENT_OFFSET = 0
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class CheckError(Error):
|
|
41
|
-
"""
|
|
42
|
-
Raised when a check fails, and you want to pass a status values along with the message.
|
|
43
|
-
"""
|
|
44
|
-
|
|
45
|
-
def __init__(self, message, status):
|
|
46
|
-
self.message = message
|
|
47
|
-
self.status = status
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def update_transaction_identifier(tid: int) -> int:
|
|
51
|
-
"""
|
|
52
|
-
Updates the transaction identifier and returns the new value.
|
|
53
|
-
|
|
54
|
-
This identifier shall be incremented for each RMAP Request. The RMAP Request Reply packets
|
|
55
|
-
shall copy the transaction ID of the RMAP Request packet in their transaction ID field.
|
|
56
|
-
|
|
57
|
-
The transaction ID is a 16-bit field which is used to associate replies with the command
|
|
58
|
-
that caused the reply.
|
|
59
|
-
|
|
60
|
-
Args:
|
|
61
|
-
tid (int): The current transaction identifier
|
|
62
|
-
|
|
63
|
-
Returns:
|
|
64
|
-
the updated transaction identifier (int).
|
|
65
|
-
"""
|
|
66
|
-
tid = (tid + 1) & 0xFFFF
|
|
67
|
-
return tid
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
class PacketType(IntEnum):
|
|
71
|
-
"""Enumeration type that defines the SpaceWire packet type."""
|
|
72
|
-
|
|
73
|
-
DATA_PACKET = 0
|
|
74
|
-
OVERSCAN_DATA = 1
|
|
75
|
-
HOUSEKEEPING_DATA = 2 # N-FEE
|
|
76
|
-
DEB_HOUSEKEEPING_DATA = 2 # F-FEE
|
|
77
|
-
AEB_HOUSEKEEPING_DATA = 3 # F-FEE
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
class DataPacketType:
|
|
81
|
-
"""
|
|
82
|
-
Defines the Data Packet Field: Type, which is a bit-field of 16 bits.
|
|
83
|
-
|
|
84
|
-
Properties:
|
|
85
|
-
* value: returns the data type as an integer
|
|
86
|
-
* packet_type: the type of data packet, defined in PacketType enum.
|
|
87
|
-
* mode: the FEE mode, defined in n_fee_mode and f_fee_mode enum
|
|
88
|
-
* last_packet: flag which defines the last packet of a type in the current readout cycle
|
|
89
|
-
* ccd_side: 0 for E-side (left), 1 for F-side (right), see egse.fee.fee_side
|
|
90
|
-
* ccd_number: CCD number [0, 3]
|
|
91
|
-
* frame_number: the frame number after sync
|
|
92
|
-
"""
|
|
93
|
-
|
|
94
|
-
def __init__(self, data_type: int = 0):
|
|
95
|
-
self._data_type: int = data_type
|
|
96
|
-
# self.n_fee_side = GlobalState.setup.camera.fee.ccd_sides.enum
|
|
97
|
-
|
|
98
|
-
@property
|
|
99
|
-
def value(self) -> int:
|
|
100
|
-
"""Returns the data packet type as an int."""
|
|
101
|
-
return self._data_type
|
|
102
|
-
|
|
103
|
-
@property
|
|
104
|
-
def packet_type(self):
|
|
105
|
-
"""Returns the packet type: 0 = data packet, 1 = overscan data, 2 = housekeeping packet."""
|
|
106
|
-
return self._data_type & 0b0011
|
|
107
|
-
|
|
108
|
-
@packet_type.setter
|
|
109
|
-
def packet_type(self, value):
|
|
110
|
-
if not 0 <= value < 3:
|
|
111
|
-
raise ValueError(f"Packet Type can only have the value 0, 1, or 2, {value=} given.")
|
|
112
|
-
x = self._data_type
|
|
113
|
-
for idx, bit in enumerate([0, 1]):
|
|
114
|
-
x = set_bit(x, bit) if value & (1 << idx) else clear_bit(x, bit)
|
|
115
|
-
self._data_type = x
|
|
116
|
-
|
|
117
|
-
@property
|
|
118
|
-
def mode(self) -> int:
|
|
119
|
-
return (self._data_type & 0b1111_0000_0000) >> 8
|
|
120
|
-
|
|
121
|
-
@mode.setter
|
|
122
|
-
def mode(self, value: int):
|
|
123
|
-
x = self._data_type
|
|
124
|
-
for idx, bit in enumerate([8, 9, 10, 11]):
|
|
125
|
-
x = set_bit(x, bit) if value & (1 << idx) else clear_bit(x, bit)
|
|
126
|
-
self._data_type = x
|
|
127
|
-
|
|
128
|
-
@property
|
|
129
|
-
def last_packet(self) -> bool:
|
|
130
|
-
return bool(self._data_type & 0b1000_0000)
|
|
131
|
-
|
|
132
|
-
@last_packet.setter
|
|
133
|
-
def last_packet(self, flag: bool):
|
|
134
|
-
self._data_type = set_bit(self._data_type, 7) if flag else clear_bit(self._data_type, 7)
|
|
135
|
-
|
|
136
|
-
@property
|
|
137
|
-
def ccd_side(self) -> int:
|
|
138
|
-
return (self._data_type & 0b0100_0000) >> 6
|
|
139
|
-
|
|
140
|
-
@ccd_side.setter
|
|
141
|
-
def ccd_side(self, value: int):
|
|
142
|
-
self._data_type = set_bit(self._data_type, 6) if value & 0b0001 else clear_bit(self._data_type, 6)
|
|
143
|
-
|
|
144
|
-
@property
|
|
145
|
-
def ccd_number(self) -> int:
|
|
146
|
-
return (self._data_type & 0b0011_0000) >> 4
|
|
147
|
-
|
|
148
|
-
@ccd_number.setter
|
|
149
|
-
def ccd_number(self, value):
|
|
150
|
-
x = self._data_type
|
|
151
|
-
for idx, bit in enumerate([4, 5]):
|
|
152
|
-
x = set_bit(x, bit) if value & (1 << idx) else clear_bit(x, bit)
|
|
153
|
-
self._data_type = x
|
|
154
|
-
|
|
155
|
-
@property
|
|
156
|
-
def frame_number(self) -> int:
|
|
157
|
-
return (self._data_type & 0b1100) >> 2
|
|
158
|
-
|
|
159
|
-
@frame_number.setter
|
|
160
|
-
def frame_number(self, value):
|
|
161
|
-
x = self._data_type
|
|
162
|
-
for idx, bit in enumerate([2, 3]):
|
|
163
|
-
x = set_bit(x, bit) if value & (1 << idx) else clear_bit(x, bit)
|
|
164
|
-
self._data_type = x
|
|
165
|
-
|
|
166
|
-
def __str__(self) -> str:
|
|
167
|
-
from egse.fee import n_fee_mode
|
|
168
|
-
n_fee_side = GlobalState.setup.camera.fee.ccd_sides.enum
|
|
169
|
-
|
|
170
|
-
return (
|
|
171
|
-
f"mode:{n_fee_mode(self.mode).name}, last_packet:{self.last_packet}, "
|
|
172
|
-
f"CCD side:{n_fee_side(self.ccd_side).name}, CCD number:{self.ccd_number}, "
|
|
173
|
-
f"Frame number:{self.frame_number}, Packet Type:{PacketType(self.packet_type).name}"
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
def to_string(data: Union[DataPacketType]) -> str:
|
|
178
|
-
"""Returns a 'user-oriented' string representation of the SpW DataPacketType.
|
|
179
|
-
|
|
180
|
-
The purpose of this function is to represent the N-FEE information in a user-oriented way.
|
|
181
|
-
That means for certain values that they will be converted into the form the a user understands
|
|
182
|
-
and that may be different or reverse from the original N-FEE definition. An example is the
|
|
183
|
-
CCD number which is different from the user perspective with respect to the N-FEE.
|
|
184
|
-
|
|
185
|
-
If any other object type is passed, the data.__str__() method will be returned without
|
|
186
|
-
processing or conversion.
|
|
187
|
-
|
|
188
|
-
Args:
|
|
189
|
-
data: a DataPacketType
|
|
190
|
-
"""
|
|
191
|
-
from egse.fee import n_fee_mode
|
|
192
|
-
n_fee_side = GlobalState.setup.camera.fee.ccd_sides.enum
|
|
193
|
-
|
|
194
|
-
if isinstance(data, DataPacketType):
|
|
195
|
-
try:
|
|
196
|
-
ccd_bin_to_id = GlobalState.setup.camera.fee.ccd_numbering.CCD_BIN_TO_ID
|
|
197
|
-
except AttributeError:
|
|
198
|
-
raise SetupError("No entry in the setup for camera.fee.ccd_numbering.CCD_BIN_TO_ID")
|
|
199
|
-
return (
|
|
200
|
-
f"mode:{n_fee_mode(data.mode).name}, last_packet:{data.last_packet}, "
|
|
201
|
-
f"CCD side:{n_fee_side(data.ccd_side).name}, CCD number:"
|
|
202
|
-
f"{ccd_bin_to_id[data.ccd_number]}, "
|
|
203
|
-
f"Frame number:{data.frame_number}, Packet Type:{PacketType(data.packet_type).name}"
|
|
204
|
-
)
|
|
205
|
-
else:
|
|
206
|
-
return data.__str__()
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
class DataPacketHeader:
|
|
210
|
-
"""
|
|
211
|
-
Defines the header of a data packet.
|
|
212
|
-
|
|
213
|
-
The full header can be retrieved as a bytes object with the `data_as_bytes()` method.
|
|
214
|
-
|
|
215
|
-
Properties:
|
|
216
|
-
* logical_address: fixed value of 0x50
|
|
217
|
-
* protocol_id: fixed value of 0xF0
|
|
218
|
-
* length: length of the data part of the packet, i.e. the packet length - size of the header
|
|
219
|
-
* type: data packet type as defined by DataPacketType
|
|
220
|
-
* frame_counter:
|
|
221
|
-
* sequence_counter: a packet sequence counter per CCD
|
|
222
|
-
"""
|
|
223
|
-
def __init__(self, header_data: bytes = None):
|
|
224
|
-
self.header_data = bytearray(
|
|
225
|
-
header_data or bytes([0x50, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
|
|
226
|
-
|
|
227
|
-
if len(self.header_data) != 10:
|
|
228
|
-
raise ValueError(f"The length of the header for a data packet shall be 10 bytes, "
|
|
229
|
-
f"got {len(self.header_data)}.")
|
|
230
|
-
|
|
231
|
-
self.n_fee_side = GlobalState.setup.camera.fee.ccd_sides.enum
|
|
232
|
-
|
|
233
|
-
def data_as_bytes(self) -> bytes:
|
|
234
|
-
"""Returns the full header as a bytes object."""
|
|
235
|
-
return bytes(self.header_data)
|
|
236
|
-
|
|
237
|
-
@property
|
|
238
|
-
def logical_address(self) -> int:
|
|
239
|
-
return self.header_data[0]
|
|
240
|
-
|
|
241
|
-
@logical_address.setter
|
|
242
|
-
def logical_address(self, value: int):
|
|
243
|
-
self.header_data[0] = value
|
|
244
|
-
|
|
245
|
-
@property
|
|
246
|
-
def protocol_id(self) -> int:
|
|
247
|
-
return self.header_data[1]
|
|
248
|
-
|
|
249
|
-
@protocol_id.setter
|
|
250
|
-
def protocol_id(self, value: int):
|
|
251
|
-
self.header_data[1] = value
|
|
252
|
-
|
|
253
|
-
@property
|
|
254
|
-
def length(self) -> int:
|
|
255
|
-
return int.from_bytes(self.header_data[2:4], byteorder='big')
|
|
256
|
-
|
|
257
|
-
@length.setter
|
|
258
|
-
def length(self, value: int):
|
|
259
|
-
self.header_data[2:4] = value.to_bytes(2, 'big')
|
|
260
|
-
|
|
261
|
-
@property
|
|
262
|
-
def type(self):
|
|
263
|
-
return int.from_bytes(self.header_data[4:6], byteorder='big')
|
|
264
|
-
|
|
265
|
-
@type.setter
|
|
266
|
-
def type(self, value: Union[int, bytes, DataPacketType]):
|
|
267
|
-
if isinstance(value, bytes):
|
|
268
|
-
self.header_data[4:6] = value
|
|
269
|
-
elif isinstance(value, DataPacketType):
|
|
270
|
-
self.header_data[4:6] = value.value.to_bytes(2, 'big')
|
|
271
|
-
else:
|
|
272
|
-
self.header_data[4:6] = value.to_bytes(2, 'big')
|
|
273
|
-
|
|
274
|
-
@property
|
|
275
|
-
def type_as_object(self):
|
|
276
|
-
return DataPacketType(self.type)
|
|
277
|
-
|
|
278
|
-
@property
|
|
279
|
-
def packet_type(self):
|
|
280
|
-
return self.type_as_object.packet_type
|
|
281
|
-
|
|
282
|
-
@packet_type.setter
|
|
283
|
-
def packet_type(self, value: int):
|
|
284
|
-
type_obj = self.type_as_object
|
|
285
|
-
type_obj.packet_type = value
|
|
286
|
-
self.type = type_obj
|
|
287
|
-
|
|
288
|
-
@property
|
|
289
|
-
def last_packet(self):
|
|
290
|
-
return self.type_as_object.last_packet
|
|
291
|
-
|
|
292
|
-
@last_packet.setter
|
|
293
|
-
def last_packet(self, flag: bool):
|
|
294
|
-
type_obj = self.type_as_object
|
|
295
|
-
type_obj.last_packet = flag
|
|
296
|
-
self.type = type_obj
|
|
297
|
-
|
|
298
|
-
@property
|
|
299
|
-
def frame_counter(self):
|
|
300
|
-
return int.from_bytes(self.header_data[6:8], byteorder='big')
|
|
301
|
-
|
|
302
|
-
@frame_counter.setter
|
|
303
|
-
def frame_counter(self, value):
|
|
304
|
-
self.header_data[6:8] = value.to_bytes(2, 'big')
|
|
305
|
-
|
|
306
|
-
@property
|
|
307
|
-
def sequence_counter(self):
|
|
308
|
-
return int.from_bytes(self.header_data[8:10], byteorder='big')
|
|
309
|
-
|
|
310
|
-
@sequence_counter.setter
|
|
311
|
-
def sequence_counter(self, value):
|
|
312
|
-
self.header_data[8:10] = value.to_bytes(2, 'big')
|
|
313
|
-
|
|
314
|
-
def as_dict(self):
|
|
315
|
-
from egse.fee import n_fee_mode
|
|
316
|
-
|
|
317
|
-
data_packet_type = DataPacketType(self.type)
|
|
318
|
-
return dict(
|
|
319
|
-
logical_address=f"0x{self.logical_address:02X}",
|
|
320
|
-
protocol_id=f"0x{self.protocol_id:02X}",
|
|
321
|
-
length=self.length,
|
|
322
|
-
type=f"0x{self.type:04X}",
|
|
323
|
-
frame_counter=self.frame_counter,
|
|
324
|
-
sequence_counter=self.sequence_counter,
|
|
325
|
-
packet_type=data_packet_type.packet_type,
|
|
326
|
-
frame_number=data_packet_type.frame_number,
|
|
327
|
-
ccd_number=data_packet_type.ccd_number,
|
|
328
|
-
ccd_side=self.n_fee_side(data_packet_type.ccd_side).name,
|
|
329
|
-
last_packet=data_packet_type.last_packet,
|
|
330
|
-
mode=n_fee_mode(data_packet_type.mode).name,
|
|
331
|
-
)
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
class SpaceWirePacket:
|
|
335
|
-
"""Base class for any packet transmitted over a SpaceWire cable."""
|
|
336
|
-
|
|
337
|
-
# these settings are used by this class and its sub-classes to configure the print options
|
|
338
|
-
# for the numpy arrays.
|
|
339
|
-
|
|
340
|
-
_threshold = 300 # sys.maxsize
|
|
341
|
-
_edgeitems = 10
|
|
342
|
-
_linewidth = 120
|
|
343
|
-
|
|
344
|
-
def __init__(self, data: Union[bytes, np.ndarray]):
|
|
345
|
-
"""
|
|
346
|
-
Args:
|
|
347
|
-
data: a bytes object or a numpy array of type np.uint8 (not enforced)
|
|
348
|
-
"""
|
|
349
|
-
self._bytes = bytes(data)
|
|
350
|
-
|
|
351
|
-
def __repr__(self):
|
|
352
|
-
limit = 25
|
|
353
|
-
data_hex = ' '.join(f"{x:02x}" for x in self._bytes[:limit])
|
|
354
|
-
data_hex += '...' if len(self._bytes) > limit else ''
|
|
355
|
-
|
|
356
|
-
msg = (
|
|
357
|
-
f"{self.__class__.__name__}(0x{data_hex})"
|
|
358
|
-
)
|
|
359
|
-
return msg
|
|
360
|
-
|
|
361
|
-
def __len__(self):
|
|
362
|
-
return len(self._bytes)
|
|
363
|
-
|
|
364
|
-
@property
|
|
365
|
-
def packet_as_bytes(self):
|
|
366
|
-
return self._bytes
|
|
367
|
-
|
|
368
|
-
@property
|
|
369
|
-
def packet_as_ndarray(self):
|
|
370
|
-
return np.frombuffer(self._bytes, dtype=np.uint8)
|
|
371
|
-
|
|
372
|
-
@property
|
|
373
|
-
def logical_address(self):
|
|
374
|
-
# TODO: what about a timecode, that has no logical address?
|
|
375
|
-
return self._bytes[0]
|
|
376
|
-
|
|
377
|
-
@property
|
|
378
|
-
def protocol_id(self):
|
|
379
|
-
# TODO: what about a timecode, that has no protocol id?
|
|
380
|
-
return self._bytes[1]
|
|
381
|
-
|
|
382
|
-
def header_as_bytes(self) -> bytes:
|
|
383
|
-
# TODO: what about timecode, this has no header, except maybe the first byte: 0x91
|
|
384
|
-
raise NotImplementedError
|
|
385
|
-
|
|
386
|
-
@staticmethod
|
|
387
|
-
def create_packet(data: Union[bytes, np.ndarray]):
|
|
388
|
-
"""
|
|
389
|
-
Factory method that returns a SpaceWire packet of the correct type based on the information
|
|
390
|
-
in the header.
|
|
391
|
-
"""
|
|
392
|
-
# MODULE_LOGGER.info(f"{len(data) = }")
|
|
393
|
-
|
|
394
|
-
if TimecodePacket.is_timecode_packet(data):
|
|
395
|
-
return TimecodePacket(data)
|
|
396
|
-
if HousekeepingPacket.is_housekeeping_packet(data):
|
|
397
|
-
return HousekeepingPacket(data)
|
|
398
|
-
if DataDataPacket.is_data_data_packet(data):
|
|
399
|
-
return DataDataPacket(data)
|
|
400
|
-
if OverscanDataPacket.is_overscan_data_packet(data):
|
|
401
|
-
return OverscanDataPacket(data)
|
|
402
|
-
if WriteRequest.is_write_request(data):
|
|
403
|
-
return WriteRequest(data)
|
|
404
|
-
if WriteRequestReply.is_write_reply(data):
|
|
405
|
-
return WriteRequestReply(data)
|
|
406
|
-
if ReadRequest.is_read_request(data):
|
|
407
|
-
return ReadRequest(data)
|
|
408
|
-
if ReadRequestReply.is_read_reply(data):
|
|
409
|
-
return ReadRequestReply(data)
|
|
410
|
-
return SpaceWirePacket(data)
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
class ExtensionPacket:
|
|
414
|
-
def __init__(self):
|
|
415
|
-
pass
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
class DataPacket(SpaceWirePacket):
|
|
419
|
-
"""
|
|
420
|
-
Base class for proprietary SpaceWire data packets that are exchanged between FEE and DPU.
|
|
421
|
-
|
|
422
|
-
.. note::
|
|
423
|
-
This class should not be instantiated directly. Use the SpaceWirePacket.create_packet()
|
|
424
|
-
factory method or the constructors of one of the sub-classes of this DataPacket class.
|
|
425
|
-
"""
|
|
426
|
-
|
|
427
|
-
DATA_HEADER_LENGTH = 10
|
|
428
|
-
|
|
429
|
-
def __init__(self, data: Union[bytes, np.ndarray]):
|
|
430
|
-
"""
|
|
431
|
-
Args:
|
|
432
|
-
data: a bytes object or a numpy array
|
|
433
|
-
"""
|
|
434
|
-
if not self.is_data_packet(data):
|
|
435
|
-
raise ValueError(
|
|
436
|
-
f"Can not create a DataPacket from the given data {[f'0x{x:02x}' for x in data]}"
|
|
437
|
-
)
|
|
438
|
-
|
|
439
|
-
super().__init__(data)
|
|
440
|
-
|
|
441
|
-
if (data[2] == 0x00 and data[3] == 0x00) or len(data) == self.DATA_HEADER_LENGTH:
|
|
442
|
-
MODULE_LOGGER.warning(
|
|
443
|
-
f"SpaceWire data packet without data found, packet={[f'0x{x:02x}' for x in data]}"
|
|
444
|
-
)
|
|
445
|
-
|
|
446
|
-
self._length = (data[2] << 8) + data[3]
|
|
447
|
-
|
|
448
|
-
if len(data) != self._length + self.DATA_HEADER_LENGTH:
|
|
449
|
-
MODULE_LOGGER.warning(
|
|
450
|
-
f"The length of the data argument ({len(data)}) given to "
|
|
451
|
-
f"the constructor of {self.__class__.__name__} (or sub-classes) is inconsistent "
|
|
452
|
-
f"with the length data field ({self._length} + 10) in the packet header."
|
|
453
|
-
)
|
|
454
|
-
raise ValueError(
|
|
455
|
-
f"{self.__class__.__name__} header: data-length field ({self._length}) not "
|
|
456
|
-
f"consistent with packet length ({len(data)}). Difference should be "
|
|
457
|
-
f"{self.DATA_HEADER_LENGTH}."
|
|
458
|
-
)
|
|
459
|
-
|
|
460
|
-
self._type = DataPacketType((data[4] << 8) + data[5])
|
|
461
|
-
self._data = None # lazy loading of data from self._bytes
|
|
462
|
-
|
|
463
|
-
@property
|
|
464
|
-
def length(self) -> int:
|
|
465
|
-
"""Returns the data length in bytes.
|
|
466
|
-
|
|
467
|
-
.. note:: length == len(data_nd_array) * 2
|
|
468
|
-
This length property returns the length of the data area in bytes. This value is
|
|
469
|
-
taken from the header of the data packet. If you want to compare this with the size
|
|
470
|
-
of the data_as_ndarray property, multiply the length by 2 because the data is 16-bit
|
|
471
|
-
integers, not bytes.
|
|
472
|
-
|
|
473
|
-
Returns:
|
|
474
|
-
the size of the data area of the packet in bytes.
|
|
475
|
-
"""
|
|
476
|
-
return self._length
|
|
477
|
-
|
|
478
|
-
@property
|
|
479
|
-
def data_as_ndarray(self):
|
|
480
|
-
"""
|
|
481
|
-
Returns the data from this data packet as a 16-bit integer Numpy array.
|
|
482
|
-
|
|
483
|
-
.. note::
|
|
484
|
-
The data has been converted from the 8-bit packet data into 16-bit integers. That
|
|
485
|
-
means the length of this data array will be half the length of the data field the
|
|
486
|
-
packet, i.e. ``len(data) == length // 2``.
|
|
487
|
-
The reason for this is that pixel data has a size of 16-bit.
|
|
488
|
-
|
|
489
|
-
.. todo::
|
|
490
|
-
check if the data-length of HK packets should also be a multiple of 16.
|
|
491
|
-
|
|
492
|
-
Returns:
|
|
493
|
-
data: Numpy array with the data from this packet (type is np.uint16)
|
|
494
|
-
|
|
495
|
-
"""
|
|
496
|
-
|
|
497
|
-
# We decided to lazy load/construct the data array. The reason is that the packet may be
|
|
498
|
-
# created / transferred without the need to unpack the data field into a 16-bit numpy array.
|
|
499
|
-
|
|
500
|
-
if self._data is None:
|
|
501
|
-
# The data is in two's-complement. The most significant bit (msb) shall be inverted
|
|
502
|
-
# according to Sampie Smit. That is done in the following line where the msb in each
|
|
503
|
-
# byte on an even index is inverted.
|
|
504
|
-
|
|
505
|
-
# data = [toggle_bit(b, 7) if not idx % 2 else b for idx, b in enumerate(self._bytes)]
|
|
506
|
-
# data = bytearray(data)
|
|
507
|
-
# data_1 = np.frombuffer(data, offset=10, dtype='>u2')
|
|
508
|
-
|
|
509
|
-
# Needs further confirmation, but the following line should have the same effect as
|
|
510
|
-
# the previous three lines.
|
|
511
|
-
data_2 = np.frombuffer(self._bytes, offset=10, dtype='>i2') + TWOS_COMPLEMENT_OFFSET
|
|
512
|
-
|
|
513
|
-
# Test if the results are identical, left the code in until we are fully confident
|
|
514
|
-
# if diff := np.sum(np.cumsum(data_1 - data_2)):
|
|
515
|
-
# MODULE_LOGGER.info(f"cumsum={diff}")
|
|
516
|
-
|
|
517
|
-
self._data = data_2.astype('uint16')
|
|
518
|
-
return self._data
|
|
519
|
-
|
|
520
|
-
@property
|
|
521
|
-
def data(self) -> bytes:
|
|
522
|
-
return self._bytes[10: 10 + self._length]
|
|
523
|
-
|
|
524
|
-
@property
|
|
525
|
-
def type(self) -> DataPacketType:
|
|
526
|
-
return self._type
|
|
527
|
-
|
|
528
|
-
@property
|
|
529
|
-
def frame_counter(self):
|
|
530
|
-
return (self._bytes[6] << 8) + self._bytes[7]
|
|
531
|
-
|
|
532
|
-
@property
|
|
533
|
-
def sequence_counter(self):
|
|
534
|
-
return (self._bytes[8] << 8) + self._bytes[9]
|
|
535
|
-
|
|
536
|
-
@property
|
|
537
|
-
def header(self) -> DataPacketHeader:
|
|
538
|
-
return DataPacketHeader(self.header_as_bytes())
|
|
539
|
-
|
|
540
|
-
def header_as_bytes(self):
|
|
541
|
-
return self._bytes[:10]
|
|
542
|
-
|
|
543
|
-
@classmethod
|
|
544
|
-
def is_data_packet(cls, data: np.ndarray) -> bool:
|
|
545
|
-
if len(data) < 10 or data[0] != 0x50 or data[1] != 0xF0:
|
|
546
|
-
return False
|
|
547
|
-
return True
|
|
548
|
-
|
|
549
|
-
def __str__(self):
|
|
550
|
-
options = np.get_printoptions()
|
|
551
|
-
np.set_printoptions(
|
|
552
|
-
formatter={"int": lambda x: f"0x{x:04x}"},
|
|
553
|
-
threshold=super()._threshold,
|
|
554
|
-
edgeitems=super()._edgeitems,
|
|
555
|
-
linewidth=super()._linewidth,
|
|
556
|
-
)
|
|
557
|
-
limit = 50
|
|
558
|
-
header_hex = ' '.join(f'{byte:02X}' for byte in self.header_as_bytes()[:limit])
|
|
559
|
-
data_hex = ' '.join(f'{byte:02X}' for byte in self.data[:limit])
|
|
560
|
-
data_ascii = ''.join((chr(byte) if 32 <= byte <= 126 else '.') for byte in self.data[:limit])
|
|
561
|
-
|
|
562
|
-
msg = (
|
|
563
|
-
f"{self.__class__.__name__}:\n"
|
|
564
|
-
f" Logical Address = 0x{self.logical_address:02X}\n"
|
|
565
|
-
f" Protocol ID = 0x{self.protocol_id:02X}\n"
|
|
566
|
-
f" Length = {self.length}\n"
|
|
567
|
-
f" Type = {self._type}\n"
|
|
568
|
-
f" Frame Counter = {self.frame_counter}\n"
|
|
569
|
-
f" Sequence Counter = {self.sequence_counter}\n"
|
|
570
|
-
f" Header = {header_hex}\n"
|
|
571
|
-
f" Data HEX = {data_hex}\n"
|
|
572
|
-
f" Data ASC = {data_ascii}\n"
|
|
573
|
-
)
|
|
574
|
-
np.set_printoptions(**options)
|
|
575
|
-
return msg
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
class DataDataPacket(DataPacket):
|
|
579
|
-
"""Proprietary Data Packet for N-FEE and F-FEE CCD image data."""
|
|
580
|
-
|
|
581
|
-
@classmethod
|
|
582
|
-
def is_data_data_packet(cls, data: Union[bytes, np.ndarray]) -> bool:
|
|
583
|
-
if len(data) <= 10:
|
|
584
|
-
return False
|
|
585
|
-
if data[0] != 0x50:
|
|
586
|
-
return False
|
|
587
|
-
if data[1] != 0xF0:
|
|
588
|
-
return False
|
|
589
|
-
type_ = DataPacketType((data[4] << 8) + data[5])
|
|
590
|
-
if type_.packet_type == PacketType.DATA_PACKET:
|
|
591
|
-
return True
|
|
592
|
-
return False
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
class OverscanDataPacket(DataPacket):
|
|
596
|
-
"""Proprietary Overscan Data Packet for N-FEE and F-FEE CCD image data."""
|
|
597
|
-
|
|
598
|
-
@classmethod
|
|
599
|
-
def is_overscan_data_packet(cls, data: Union[bytes, np.ndarray]) -> bool:
|
|
600
|
-
if len(data) <= 10:
|
|
601
|
-
return False
|
|
602
|
-
if data[0] != 0x50:
|
|
603
|
-
return False
|
|
604
|
-
if data[1] != 0xF0:
|
|
605
|
-
return False
|
|
606
|
-
type_ = DataPacketType((data[4] << 8) + data[5])
|
|
607
|
-
if type_.packet_type == PacketType.OVERSCAN_DATA:
|
|
608
|
-
return True
|
|
609
|
-
return False
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
class HousekeepingPacket(DataPacket):
|
|
613
|
-
"""Proprietary Housekeeping data packet for the N-FEE and F-FEE."""
|
|
614
|
-
|
|
615
|
-
def __init__(self, data: Union[bytes, np.ndarray]):
|
|
616
|
-
"""
|
|
617
|
-
Args:
|
|
618
|
-
data: a numpy array of type np.uint8 (not enforced)
|
|
619
|
-
"""
|
|
620
|
-
if not self.is_housekeeping_packet(data):
|
|
621
|
-
raise ValueError(f"Can not create a HousekeepingPacket from the given data {data}")
|
|
622
|
-
|
|
623
|
-
# The __init__ method of DataPacket already checks e.g. data-length against packet length,
|
|
624
|
-
# so there is no need for these tests here.
|
|
625
|
-
|
|
626
|
-
super().__init__(data)
|
|
627
|
-
|
|
628
|
-
@classmethod
|
|
629
|
-
def is_housekeeping_packet(cls, data: Union[bytes, np.ndarray]) -> bool:
|
|
630
|
-
if len(data) <= 10:
|
|
631
|
-
return False
|
|
632
|
-
if data[0] != 0x50:
|
|
633
|
-
return False
|
|
634
|
-
if data[1] != 0xF0:
|
|
635
|
-
return False
|
|
636
|
-
type_ = DataPacketType((data[4] << 8) + data[5])
|
|
637
|
-
if type_.packet_type == PacketType.HOUSEKEEPING_DATA:
|
|
638
|
-
return True
|
|
639
|
-
return False
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
class TimecodePacket(SpaceWirePacket):
|
|
643
|
-
"""A Timecode Packet.
|
|
644
|
-
|
|
645
|
-
This packet really is an extended packet which is generated by the Diagnostic SpaceWire
|
|
646
|
-
Interface (DSI) to forward a SpaceWire timecode over the Ethernet connection.
|
|
647
|
-
"""
|
|
648
|
-
|
|
649
|
-
def __init__(self, data: Union[bytes, np.ndarray]):
|
|
650
|
-
super().__init__(data)
|
|
651
|
-
|
|
652
|
-
@property
|
|
653
|
-
def timecode(self) -> int:
|
|
654
|
-
return self._bytes[1] & 0x3F
|
|
655
|
-
|
|
656
|
-
def header_as_bytes(self) -> bytes:
|
|
657
|
-
return self._bytes[0:1]
|
|
658
|
-
|
|
659
|
-
@classmethod
|
|
660
|
-
def is_timecode_packet(cls, data: Union[bytes, np.ndarray]) -> bool:
|
|
661
|
-
return data[0] == 0x91
|
|
662
|
-
|
|
663
|
-
def __str__(self):
|
|
664
|
-
return f"Timecode Packet: timecode = 0x{self.timecode:02x} ({self.timecode:2d})"
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
class RMAPRequestMixin(SpaceWirePacket):
|
|
668
|
-
@property
|
|
669
|
-
def key(self):
|
|
670
|
-
"""Returns the key field."""
|
|
671
|
-
return get_key_field(self._bytes)
|
|
672
|
-
|
|
673
|
-
@property
|
|
674
|
-
def initiator_address(self):
|
|
675
|
-
"""Returns the initiator logical address."""
|
|
676
|
-
return self._bytes[4]
|
|
677
|
-
|
|
678
|
-
@property
|
|
679
|
-
def data_length(self):
|
|
680
|
-
return get_data_length(self._bytes)
|
|
681
|
-
|
|
682
|
-
@property
|
|
683
|
-
def address(self):
|
|
684
|
-
return get_address(self._bytes)
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
class RMAPRequestReplyMixin(SpaceWirePacket):
|
|
688
|
-
@property
|
|
689
|
-
def target_address(self):
|
|
690
|
-
"""Returns the target logical address."""
|
|
691
|
-
return self._bytes[4]
|
|
692
|
-
|
|
693
|
-
@property
|
|
694
|
-
def status(self):
|
|
695
|
-
"""Returns the status field."""
|
|
696
|
-
return self._bytes[3]
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
class RMAPPacket(SpaceWirePacket):
|
|
700
|
-
"""Base class for RMAP SpaceWire packets."""
|
|
701
|
-
|
|
702
|
-
def __init__(self, data: Union[bytes, np.ndarray]):
|
|
703
|
-
if not self.is_rmap_packet(data):
|
|
704
|
-
raise ValueError(f"Can not create a RMAPPacket from the given data {data}")
|
|
705
|
-
super().__init__(data)
|
|
706
|
-
|
|
707
|
-
def __str__(self):
|
|
708
|
-
msg = (
|
|
709
|
-
f"RMAP Base Packet ({len(self)} bytes)\n"
|
|
710
|
-
f"Logical address: 0x{self.logical_address:02x}\n"
|
|
711
|
-
f"Protocol ID: 0x{self.protocol_id:02x}\n"
|
|
712
|
-
f"Instruction: 0x{self.instruction:02x} (0o{self.instruction:08b})\n"
|
|
713
|
-
f"Transaction ID: 0x{self.transaction_id:04x} ({self.transaction_id})\n"
|
|
714
|
-
f"Data = {self._bytes}\n"
|
|
715
|
-
)
|
|
716
|
-
return msg
|
|
717
|
-
|
|
718
|
-
@property
|
|
719
|
-
def instruction(self):
|
|
720
|
-
return get_instruction_field(self._bytes)
|
|
721
|
-
|
|
722
|
-
@property
|
|
723
|
-
def transaction_id(self):
|
|
724
|
-
return get_transaction_identifier(self._bytes)
|
|
725
|
-
|
|
726
|
-
@property
|
|
727
|
-
def header_crc(self):
|
|
728
|
-
return get_header_crc(self._bytes)
|
|
729
|
-
|
|
730
|
-
@property
|
|
731
|
-
def data_crc(self):
|
|
732
|
-
return get_data_crc(self._bytes)
|
|
733
|
-
|
|
734
|
-
@classmethod
|
|
735
|
-
def is_rmap_packet(cls, data: Union[bytes, np.ndarray]):
|
|
736
|
-
if data[1] == 0x01: # Protocol ID
|
|
737
|
-
return True
|
|
738
|
-
return False
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
class WriteRequest(RMAPPacket, RMAPRequestMixin):
|
|
742
|
-
"""A Write Request SpaceWire RMAP Packet."""
|
|
743
|
-
|
|
744
|
-
def __init__(self, data: Union[bytes, np.ndarray]):
|
|
745
|
-
super().__init__(data)
|
|
746
|
-
|
|
747
|
-
def is_verified(self):
|
|
748
|
-
return self._bytes[2] == 0x7C
|
|
749
|
-
|
|
750
|
-
def is_unverified(self):
|
|
751
|
-
return self._bytes[2] == 0x6C
|
|
752
|
-
|
|
753
|
-
@property
|
|
754
|
-
def data_length(self):
|
|
755
|
-
return get_data_length(self._bytes)
|
|
756
|
-
|
|
757
|
-
@property
|
|
758
|
-
def data(self) -> bytes:
|
|
759
|
-
return get_data(self._bytes)
|
|
760
|
-
|
|
761
|
-
@classmethod
|
|
762
|
-
def is_write_request(cls, data: Union[bytes, np.ndarray]):
|
|
763
|
-
if not RMAPPacket.is_rmap_packet(data):
|
|
764
|
-
return False
|
|
765
|
-
if data[0] != 0x51:
|
|
766
|
-
return False
|
|
767
|
-
if (data[2] == 0x7C or data[2] == 0x6C) and data[3] == 0xD1:
|
|
768
|
-
return True
|
|
769
|
-
return False
|
|
770
|
-
|
|
771
|
-
def __str__(self):
|
|
772
|
-
prefix = "Verified" if self.is_verified() else "Unverified"
|
|
773
|
-
limit = 32
|
|
774
|
-
data_hex = ' '.join(f'{x:02X}' for x in self.data[:limit])
|
|
775
|
-
data_hex += ' ...' if len(self.data) > limit else ''
|
|
776
|
-
data_asc = ''.join((chr(byte) if 32 <= byte <= 126 else '.') for byte in self.data[:limit])
|
|
777
|
-
data_asc += ' ...' if len(self.data) > limit else ''
|
|
778
|
-
|
|
779
|
-
packet = self.packet_as_bytes
|
|
780
|
-
msg = (
|
|
781
|
-
f"RMAP {prefix} Write Request ({len(packet)} bytes)\n"
|
|
782
|
-
f"Target address: 0x{self.logical_address:02x}\n"
|
|
783
|
-
f"Protocol ID: 0x{self.protocol_id:02x}\n"
|
|
784
|
-
f"Instruction: 0x{self.instruction:02x} (0o{self.instruction:08b})\n"
|
|
785
|
-
f"Key: 0x{self.key:02x}\n"
|
|
786
|
-
f"Initiator address: 0x{self.initiator_address:02x}\n"
|
|
787
|
-
f"Transaction ID: 0x{self.transaction_id:04x} ({self.transaction_id})\n"
|
|
788
|
-
f"Address: 0x{self.address:08x}\n"
|
|
789
|
-
f"Data Length: {self.data_length}\n"
|
|
790
|
-
f"Header CRC: 0x{self.header_crc:02x}\n"
|
|
791
|
-
f"data (hex): 0x{data_hex}\n"
|
|
792
|
-
f"data (ascii): {data_asc}\n"
|
|
793
|
-
f"Data CRC: 0x{self.data_crc:02x}\n"
|
|
794
|
-
)
|
|
795
|
-
return msg
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
class WriteRequestReply(RMAPPacket, RMAPRequestReplyMixin):
|
|
799
|
-
"""An RMAP Reply packet to a Write Request."""
|
|
800
|
-
|
|
801
|
-
def __init__(self, data: Union[bytes, np.ndarray]):
|
|
802
|
-
super().__init__(data)
|
|
803
|
-
|
|
804
|
-
@classmethod
|
|
805
|
-
def is_write_reply(cls, data: Union[bytes, np.ndarray]):
|
|
806
|
-
if not RMAPPacket.is_rmap_packet(data):
|
|
807
|
-
return False
|
|
808
|
-
if data[0] != 0x50:
|
|
809
|
-
return False
|
|
810
|
-
if (data[2] == 0x3C or data[2] == 0x2C) and data[4] == 0x51:
|
|
811
|
-
return True
|
|
812
|
-
|
|
813
|
-
def __str__(self):
|
|
814
|
-
msg = (
|
|
815
|
-
f"Write Request Reply ({len(self)} bytes)\n"
|
|
816
|
-
f"Initiator address: 0x{self.logical_address:02x}\n"
|
|
817
|
-
f"Protocol ID: 0x{self.protocol_id:02x}\n"
|
|
818
|
-
f"Instruction: 0x{self.instruction:02x} (0o{self.instruction:08b})\n"
|
|
819
|
-
f"Status: 0x{self.status:02x}\n"
|
|
820
|
-
f"target address: 0x{self.target_address:02x}\n"
|
|
821
|
-
f"transaction ID: 0x{self.transaction_id:02x} ({self.transaction_id})\n"
|
|
822
|
-
f"Header CRC: 0x{self.header_crc:02x}\n"
|
|
823
|
-
)
|
|
824
|
-
|
|
825
|
-
return msg
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
class ReadRequest(RMAPPacket, RMAPRequestMixin):
|
|
829
|
-
"""A Read Request SpaceWire RMAP Packet."""
|
|
830
|
-
|
|
831
|
-
def __init__(self, data: Union[bytes, np.ndarray]):
|
|
832
|
-
super().__init__(data)
|
|
833
|
-
|
|
834
|
-
@classmethod
|
|
835
|
-
def is_read_request(cls, data: Union[bytes, np.ndarray]):
|
|
836
|
-
if not RMAPPacket.is_rmap_packet(data):
|
|
837
|
-
return False
|
|
838
|
-
if data[0] != 0x51:
|
|
839
|
-
return False
|
|
840
|
-
if data[2] == 0x4C and data[3] == 0xD1:
|
|
841
|
-
return True
|
|
842
|
-
return False
|
|
843
|
-
|
|
844
|
-
def __str__(self):
|
|
845
|
-
msg = (
|
|
846
|
-
f"RMAP Read Request ({len(self)} bytes)\n"
|
|
847
|
-
f"Target address: 0x{self.logical_address:02x}\n"
|
|
848
|
-
f"Protocol ID: 0x{self.protocol_id:02x}\n"
|
|
849
|
-
f"Instruction: 0x{self.instruction:02x} (0o{self.instruction:08b})\n"
|
|
850
|
-
f"Key: 0x{self.key:02x}\n"
|
|
851
|
-
f"Initiator address: 0x{self.initiator_address:02x}\n"
|
|
852
|
-
f"Transaction ID: 0x{self.transaction_id:04x} ({self.transaction_id})\n"
|
|
853
|
-
f"(Extended) Address: 0x{self.address:08x}\n"
|
|
854
|
-
f"Data Length: {self.data_length}\n"
|
|
855
|
-
f"Header CRC: 0x{self.header_crc:02x}\n"
|
|
856
|
-
)
|
|
857
|
-
|
|
858
|
-
return msg
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
class ReadRequestReply(RMAPPacket, RMAPRequestReplyMixin):
|
|
862
|
-
"""An RMAP Reply packet to a Read Request."""
|
|
863
|
-
|
|
864
|
-
def __init__(self, data: Union[bytes, np.ndarray]):
|
|
865
|
-
super().__init__(data)
|
|
866
|
-
|
|
867
|
-
@classmethod
|
|
868
|
-
def is_read_reply(cls, data: Union[bytes, np.ndarray]):
|
|
869
|
-
if not RMAPPacket.is_rmap_packet(data):
|
|
870
|
-
return False
|
|
871
|
-
if data[0] != 0x50:
|
|
872
|
-
return False
|
|
873
|
-
if data[2] == 0x0C and data[4] == 0x51:
|
|
874
|
-
return True
|
|
875
|
-
|
|
876
|
-
@property
|
|
877
|
-
def data(self) -> bytes:
|
|
878
|
-
return get_data(self._bytes)
|
|
879
|
-
|
|
880
|
-
@property
|
|
881
|
-
def data_length(self):
|
|
882
|
-
return get_data_length(self._bytes)
|
|
883
|
-
|
|
884
|
-
def __str__(self):
|
|
885
|
-
data_length = self.data_length
|
|
886
|
-
limit = 32
|
|
887
|
-
data_hex = ' '.join(f'{x:02X}' for x in self.data[:limit])
|
|
888
|
-
data_hex += ' ...' if len(self.data) > limit else ''
|
|
889
|
-
data_asc = ''.join((chr(byte) if 32 <= byte <= 126 else '.') for byte in self.data[:limit])
|
|
890
|
-
data_asc += ' ...' if len(self.data) > limit else ''
|
|
891
|
-
|
|
892
|
-
msg = (
|
|
893
|
-
f"Read Request Reply ({len(self)} bytes)\n"
|
|
894
|
-
f"Initiator address: 0x{self.logical_address:02x}\n"
|
|
895
|
-
f"Protocol ID: 0x{self.protocol_id:02x}\n"
|
|
896
|
-
f"Instruction: 0x{self.instruction:02x} (0o{self.instruction:08b})\n"
|
|
897
|
-
f"Status: 0x{self.status:02x}\n"
|
|
898
|
-
f"target address: 0x{self.target_address:02x}\n"
|
|
899
|
-
f"transaction ID: 0x{self.transaction_id:02x} ({self.transaction_id})\n"
|
|
900
|
-
f"Data Length: {self.data_length}\n"
|
|
901
|
-
f"Header CRC: 0x{self.header_crc:02x}\n"
|
|
902
|
-
f"Data (hex): 0x{data_hex}\n"
|
|
903
|
-
f"Data (ascii): {data_asc}\n"
|
|
904
|
-
f"Data CRC: 0x{self.data_crc:02x}\n"
|
|
905
|
-
)
|
|
906
|
-
|
|
907
|
-
return msg
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
class SpaceWireInterface:
|
|
911
|
-
"""
|
|
912
|
-
This interface defines methods that are used by the DPU to communicate with the FEE over
|
|
913
|
-
SpaceWire.
|
|
914
|
-
"""
|
|
915
|
-
|
|
916
|
-
def __enter__(self):
|
|
917
|
-
self.connect()
|
|
918
|
-
return self
|
|
919
|
-
|
|
920
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
921
|
-
self.disconnect()
|
|
922
|
-
|
|
923
|
-
def connect(self):
|
|
924
|
-
raise NotImplementedError
|
|
925
|
-
|
|
926
|
-
def disconnect(self):
|
|
927
|
-
raise NotImplementedError
|
|
928
|
-
|
|
929
|
-
def configure(self):
|
|
930
|
-
raise NotImplementedError
|
|
931
|
-
|
|
932
|
-
def flush(self):
|
|
933
|
-
raise NotImplementedError
|
|
934
|
-
|
|
935
|
-
def send_timecode(self, timecode: int):
|
|
936
|
-
"""Send a timecode over the transport layer."""
|
|
937
|
-
raise NotImplementedError
|
|
938
|
-
|
|
939
|
-
def read_packet(self, timeout: int = None) -> Tuple[int, bytes]:
|
|
940
|
-
"""
|
|
941
|
-
Read a full packet from the SpaceWire transport layer.
|
|
942
|
-
|
|
943
|
-
Args:
|
|
944
|
-
timeout (int): timeout in milliseconds [default=None]
|
|
945
|
-
Returns:
|
|
946
|
-
A tuple with the terminator value and a bytes object containing the packet.
|
|
947
|
-
"""
|
|
948
|
-
raise NotImplementedError
|
|
949
|
-
|
|
950
|
-
def write_packet(self, packet: bytes):
|
|
951
|
-
"""
|
|
952
|
-
Write a full packet to the SpaceWire transport layer.
|
|
953
|
-
|
|
954
|
-
Args:
|
|
955
|
-
packet (bytes): a bytes object containing the SpaceWire packet
|
|
956
|
-
|
|
957
|
-
Returns:
|
|
958
|
-
None.
|
|
959
|
-
"""
|
|
960
|
-
raise NotImplementedError
|
|
961
|
-
|
|
962
|
-
def read_register(self, address: int, length: int = 4, strict: bool = True) -> bytes:
|
|
963
|
-
"""
|
|
964
|
-
Reads the data for the given register from the FEE memory map.
|
|
965
|
-
|
|
966
|
-
This function sends an RMAP read request for the register to the FEE.
|
|
967
|
-
|
|
968
|
-
Args:
|
|
969
|
-
address: the start address (32-bit aligned) in the remote memory
|
|
970
|
-
length: the number of bytes to read from the remote memory [default = 4]
|
|
971
|
-
strict: perform strict checking of address and length
|
|
972
|
-
|
|
973
|
-
Returns:
|
|
974
|
-
data: the 32-bit data that was read from the FEE.
|
|
975
|
-
"""
|
|
976
|
-
raise NotImplementedError
|
|
977
|
-
|
|
978
|
-
def write_register(self, address: int, data: bytes):
|
|
979
|
-
"""
|
|
980
|
-
Writes the data from the given register to the N-FEE memory map.
|
|
981
|
-
|
|
982
|
-
The function reads the data for the registry from the local register map
|
|
983
|
-
and then sends an RMAP write request for the register to the N-FEE.
|
|
984
|
-
|
|
985
|
-
.. note:: it is assumed that the local register map is up-to-date.
|
|
986
|
-
|
|
987
|
-
Args:
|
|
988
|
-
address: the start address (32-bit aligned) in the remote memory
|
|
989
|
-
data: the data that will be written into the remote memory
|
|
990
|
-
|
|
991
|
-
Raises:
|
|
992
|
-
RMAPError: when data can not be written on the target, i.e. the N-FEE.
|
|
993
|
-
"""
|
|
994
|
-
|
|
995
|
-
raise NotImplementedError
|
|
996
|
-
|
|
997
|
-
def read_memory_map(self, address: int, size: int):
|
|
998
|
-
"""
|
|
999
|
-
Read (part of) the memory map from the N-FEE.
|
|
1000
|
-
|
|
1001
|
-
Args:
|
|
1002
|
-
address: start address
|
|
1003
|
-
size: number of bytes to read
|
|
1004
|
-
|
|
1005
|
-
Returns:
|
|
1006
|
-
a bytes object containing the requested memory map.
|
|
1007
|
-
"""
|
|
1008
|
-
|
|
1009
|
-
raise NotImplementedError
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
# General RMAP helper functions ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
1013
|
-
|
|
1014
|
-
def get_protocol_id(data: bytes) -> int:
|
|
1015
|
-
"""
|
|
1016
|
-
Returns the protocol identifier field. The protocol ID is 1 (0x01) for the RMAP protocol.
|
|
1017
|
-
"""
|
|
1018
|
-
return data[1]
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
def is_rmap(rx_buffer):
|
|
1022
|
-
"""
|
|
1023
|
-
Returns True if the buffer represents an RMAP packet, False otherwise.
|
|
1024
|
-
"""
|
|
1025
|
-
return get_protocol_id(rx_buffer) == egse.rmap.RMAP_PROTOCOL_ID
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
def get_reply_address_field_length(rx_buffer) -> int:
|
|
1029
|
-
"""Returns the size of reply address field.
|
|
1030
|
-
|
|
1031
|
-
This function returns the actual size of the reply address field. It doesn't return
|
|
1032
|
-
the content of the reply address length field. If you need that information, use the
|
|
1033
|
-
reply_address_length() function that work on the instruction field.
|
|
1034
|
-
|
|
1035
|
-
Returns:
|
|
1036
|
-
length: the size of the reply address field.
|
|
1037
|
-
"""
|
|
1038
|
-
instruction = get_instruction_field(rx_buffer)
|
|
1039
|
-
return reply_address_length(instruction) * 4
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
def get_data(rxbuf) -> bytes:
|
|
1043
|
-
"""
|
|
1044
|
-
Return the data from the RMAP packet.
|
|
1045
|
-
|
|
1046
|
-
Raises:
|
|
1047
|
-
ValueError: if there is no data section in the packet.
|
|
1048
|
-
"""
|
|
1049
|
-
instruction_field = get_instruction_field(rxbuf)
|
|
1050
|
-
|
|
1051
|
-
if is_write(instruction_field) and is_reply(instruction_field):
|
|
1052
|
-
raise ValueError("A WriteRequestReply packet doesn't contain a data section.")
|
|
1053
|
-
elif is_read(instruction_field) and is_command(instruction_field):
|
|
1054
|
-
raise ValueError("A ReadRequest packet doesn't contain a data section.")
|
|
1055
|
-
|
|
1056
|
-
address_length = get_reply_address_field_length(rxbuf)
|
|
1057
|
-
data_length = get_data_length(rxbuf)
|
|
1058
|
-
|
|
1059
|
-
offset = 12 if is_read(instruction_field) else 16
|
|
1060
|
-
|
|
1061
|
-
return rxbuf[offset + address_length:offset + address_length + data_length]
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
def check_data_crc(rxbuf):
|
|
1065
|
-
"""
|
|
1066
|
-
Verifies that the data CRC that is given in the packet matches the calculated data CRC.
|
|
1067
|
-
|
|
1068
|
-
The only packets that have a data CRC are: WriteRequest, ReadRequestReply, and an F-CAM DataPacket.
|
|
1069
|
-
|
|
1070
|
-
Raises:
|
|
1071
|
-
A CheckError when the provide and calculated CRC do not match.
|
|
1072
|
-
"""
|
|
1073
|
-
instruction_field = get_instruction_field(rxbuf)
|
|
1074
|
-
address_length = get_reply_address_field_length(rxbuf)
|
|
1075
|
-
data_length = get_data_length(rxbuf)
|
|
1076
|
-
|
|
1077
|
-
offset = 12 if is_read(instruction_field) else 16
|
|
1078
|
-
idx = offset + address_length
|
|
1079
|
-
|
|
1080
|
-
d_crc = rxbuf[idx + data_length]
|
|
1081
|
-
c_crc = crc_calc(rxbuf, idx, data_length) & 0xFF
|
|
1082
|
-
if d_crc != c_crc:
|
|
1083
|
-
raise CheckError(
|
|
1084
|
-
f"Data CRC doesn't match calculated CRC, d_crc=0x{d_crc:02X} & c_crc=0x{c_crc:02X}"
|
|
1085
|
-
)
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
def get_data_crc(rxbuf):
|
|
1089
|
-
"""
|
|
1090
|
-
Returns the data CRC of the RMAP packet.
|
|
1091
|
-
|
|
1092
|
-
The only packets that have a data CRC are: WriteRequest, ReadRequestReply, and an F-CAM DataPacket.
|
|
1093
|
-
"""
|
|
1094
|
-
instruction_field = get_instruction_field(rxbuf)
|
|
1095
|
-
address_length = get_reply_address_field_length(rxbuf)
|
|
1096
|
-
data_length = get_data_length(rxbuf)
|
|
1097
|
-
|
|
1098
|
-
offset = 12 if is_read(instruction_field) else 16
|
|
1099
|
-
idx = offset + address_length
|
|
1100
|
-
|
|
1101
|
-
d_crc = rxbuf[idx + data_length]
|
|
1102
|
-
|
|
1103
|
-
return d_crc
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
def check_header_crc(rxbuf):
|
|
1107
|
-
"""
|
|
1108
|
-
Verifies that the header CRC that is given in the packet matches the calculated header CRC.
|
|
1109
|
-
|
|
1110
|
-
Only RMAP Request and RequestReply packets have a header CRC, data packets not.
|
|
1111
|
-
|
|
1112
|
-
Raises:
|
|
1113
|
-
A CheckError when the provide and calculated CRC do not match.
|
|
1114
|
-
"""
|
|
1115
|
-
instruction_field = get_instruction_field(rxbuf)
|
|
1116
|
-
if is_command(instruction_field):
|
|
1117
|
-
offset = 15
|
|
1118
|
-
elif is_write(instruction_field):
|
|
1119
|
-
offset = 7
|
|
1120
|
-
else:
|
|
1121
|
-
offset = 11
|
|
1122
|
-
|
|
1123
|
-
idx = offset + get_reply_address_field_length(rxbuf)
|
|
1124
|
-
h_crc = rxbuf[idx]
|
|
1125
|
-
c_crc = crc_calc(rxbuf, 0, idx)
|
|
1126
|
-
if h_crc != c_crc:
|
|
1127
|
-
raise CheckError(
|
|
1128
|
-
f"Header CRC doesn't match calculated CRC, h_crc=0x{h_crc:02X} & c_crc=0x{c_crc:02X}"
|
|
1129
|
-
)
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
def get_header_crc(rxbuf):
|
|
1133
|
-
"""
|
|
1134
|
-
Returns the Header CRC of the RMAP packet.
|
|
1135
|
-
|
|
1136
|
-
Only RMAP Request and RequestReply packets have a header CRC, data packets not.
|
|
1137
|
-
"""
|
|
1138
|
-
instruction_field = get_instruction_field(rxbuf)
|
|
1139
|
-
if is_command(instruction_field):
|
|
1140
|
-
offset = 15
|
|
1141
|
-
elif is_write(instruction_field):
|
|
1142
|
-
offset = 7
|
|
1143
|
-
else:
|
|
1144
|
-
offset = 11
|
|
1145
|
-
|
|
1146
|
-
idx = offset + get_reply_address_field_length(rxbuf)
|
|
1147
|
-
h_crc = rxbuf[idx]
|
|
1148
|
-
|
|
1149
|
-
return h_crc
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
def get_data_length(rxbuf) -> int:
|
|
1153
|
-
"""
|
|
1154
|
-
Returns the length of the data part of an RMAP Request packet. The returned value
|
|
1155
|
-
is the number of bytes.
|
|
1156
|
-
|
|
1157
|
-
Raises:
|
|
1158
|
-
TypeError: when this method is used on a Write Request Reply packet (which has no
|
|
1159
|
-
data length).
|
|
1160
|
-
"""
|
|
1161
|
-
instruction_field = get_instruction_field(rxbuf)
|
|
1162
|
-
|
|
1163
|
-
if not is_command(instruction_field) and is_write(instruction_field):
|
|
1164
|
-
raise TypeError("There is no data length field for Write Request Reply packets, "
|
|
1165
|
-
"asking for the data length is an invalid operation.")
|
|
1166
|
-
|
|
1167
|
-
offset = 12 if is_command(instruction_field) else 8
|
|
1168
|
-
idx = offset + get_reply_address_field_length(rxbuf)
|
|
1169
|
-
|
|
1170
|
-
# We could use two alternative decoding methods here:
|
|
1171
|
-
|
|
1172
|
-
# data_length = int.from_bytes(rxbuf[idx:idx+3], byteorder='big') # (timeit=1.166s)
|
|
1173
|
-
data_length = struct.unpack('>L', b'\x00' + rxbuf[idx:idx + 3])[0] # (timeit=0.670s)
|
|
1174
|
-
|
|
1175
|
-
return data_length
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
def get_address(rxbuf) -> int:
|
|
1179
|
-
"""
|
|
1180
|
-
Returns the address field (including the extended address field if the address is 40-bits).
|
|
1181
|
-
|
|
1182
|
-
Raises:
|
|
1183
|
-
TypeError: when this method is used on a Reply packet (which has no address field).
|
|
1184
|
-
"""
|
|
1185
|
-
instruction_field = get_instruction_field(rxbuf)
|
|
1186
|
-
|
|
1187
|
-
if not is_command(instruction_field):
|
|
1188
|
-
raise TypeError("There is no address field for Reply packets, asking for the address is "
|
|
1189
|
-
"an invalid operation.")
|
|
1190
|
-
|
|
1191
|
-
idx = 7 + get_reply_address_field_length(rxbuf)
|
|
1192
|
-
extended_address = rxbuf[idx]
|
|
1193
|
-
idx += 1
|
|
1194
|
-
address = struct.unpack('>L', rxbuf[idx:idx + 4])[0]
|
|
1195
|
-
if extended_address:
|
|
1196
|
-
address = address + (extended_address << 32)
|
|
1197
|
-
return address
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
def get_instruction_field(rxbuf):
|
|
1201
|
-
"""
|
|
1202
|
-
Returns the instruction field of the RMAP packet.
|
|
1203
|
-
"""
|
|
1204
|
-
idx = 2
|
|
1205
|
-
return rxbuf[idx]
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
def get_key_field(rxbuf):
|
|
1209
|
-
"""
|
|
1210
|
-
Returns the 'Key' field of the RMAP packet.
|
|
1211
|
-
"""
|
|
1212
|
-
idx = 3
|
|
1213
|
-
return rxbuf[idx]
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
def check_instruction(rx_buffer) -> None:
|
|
1217
|
-
"""
|
|
1218
|
-
Check the instruction field for inconsistencies and report the values in the LOGGER at DEBUG
|
|
1219
|
-
level.
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
Args:
|
|
1223
|
-
rx_buffer (bytes): The read buffer which contains the SpW packet
|
|
1224
|
-
|
|
1225
|
-
Raises:
|
|
1226
|
-
CheckError: when the reserved bit is not zero,
|
|
1227
|
-
|
|
1228
|
-
Returns:
|
|
1229
|
-
None.
|
|
1230
|
-
"""
|
|
1231
|
-
from egse.rmap import RMAP_NOT_IMPLEMENTED_AUTHORISED
|
|
1232
|
-
|
|
1233
|
-
# The Instruction Field is the third byte (base=0) of the packet buffer.
|
|
1234
|
-
# Description of the Instruction Field can be found in ECSS-E-ST-50-52C.
|
|
1235
|
-
|
|
1236
|
-
instruction = get_instruction_field(rx_buffer)
|
|
1237
|
-
if is_reserved(instruction):
|
|
1238
|
-
raise CheckError(
|
|
1239
|
-
f"Instruction field [{instruction:08b}] reserved bit is not 0x00",
|
|
1240
|
-
RMAP_NOT_IMPLEMENTED_AUTHORISED
|
|
1241
|
-
)
|
|
1242
|
-
|
|
1243
|
-
msg = "RMAP Instruction Field: "
|
|
1244
|
-
msg += "Command; " if is_command(instruction) else "Reply; "
|
|
1245
|
-
msg += "write; " if is_write(instruction) else "read; "
|
|
1246
|
-
msg += "verify; " if is_verify(instruction) else "don't verify; "
|
|
1247
|
-
msg += "reply; " if is_reply_required(instruction) else "don't reply; "
|
|
1248
|
-
msg += "increment; " if is_increment(instruction) else "no increment; "
|
|
1249
|
-
|
|
1250
|
-
MODULE_LOGGER.debug(msg)
|
|
1251
|
-
if reply_address_length(instruction):
|
|
1252
|
-
MODULE_LOGGER.debug(f"Reply address length = {reply_address_length(instruction)} bytes.")
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
def check_protocol_id(rxbuf):
|
|
1256
|
-
from egse.rmap import RMAP_PROTOCOL_ID, RMAP_GENERAL_ERROR
|
|
1257
|
-
|
|
1258
|
-
idx = 1
|
|
1259
|
-
protocol_id = rxbuf[idx]
|
|
1260
|
-
if protocol_id != RMAP_PROTOCOL_ID:
|
|
1261
|
-
raise CheckError(
|
|
1262
|
-
f"Protocol id is not the expected value {protocol_id}, expected {RMAP_PROTOCOL_ID}",
|
|
1263
|
-
RMAP_GENERAL_ERROR)
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
def get_target_logical_address(rxbuf: bytes) -> int:
|
|
1267
|
-
"""
|
|
1268
|
-
The target logical address is always the FEE, i.e. 0x50. The location of the target logical
|
|
1269
|
-
address is different for Request and RequestReply packets.
|
|
1270
|
-
"""
|
|
1271
|
-
instruction = get_instruction_field(rxbuf)
|
|
1272
|
-
offset = 0 if is_command(instruction) else 4
|
|
1273
|
-
tla_idx = offset + get_reply_address_field_length(rxbuf)
|
|
1274
|
-
tla_rxbuf = rxbuf[tla_idx]
|
|
1275
|
-
return tla_rxbuf
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
def check_target_logical_address(rxbuf, tla):
|
|
1279
|
-
from egse.rmap import RMAP_GENERAL_ERROR
|
|
1280
|
-
|
|
1281
|
-
tla_rxbuf = get_target_logical_address(rxbuf)
|
|
1282
|
-
if tla != tla_rxbuf:
|
|
1283
|
-
raise CheckError(
|
|
1284
|
-
f"Target Logical Address doesn't match, tla=0x{tla:02X} & rxbuf[0]=0x{tla_rxbuf:02X}",
|
|
1285
|
-
RMAP_GENERAL_ERROR
|
|
1286
|
-
)
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
def get_initiator_logical_address(rxbuf):
|
|
1290
|
-
"""
|
|
1291
|
-
The initiator logical address is always the DPU, i.e. 0x51. The location of the initiator logical
|
|
1292
|
-
address is different for Request and RequestReply packets.
|
|
1293
|
-
"""
|
|
1294
|
-
instruction = get_instruction_field(rxbuf)
|
|
1295
|
-
offset = 4 if is_command(instruction) else 0
|
|
1296
|
-
idx = offset + get_reply_address_field_length(rxbuf)
|
|
1297
|
-
ila_rxbuf = rxbuf[idx]
|
|
1298
|
-
return ila_rxbuf
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
def check_initiator_logical_address(rxbuf, ila):
|
|
1302
|
-
ila_rxbuf = get_initiator_logical_address(rxbuf)
|
|
1303
|
-
if ila != ila_rxbuf:
|
|
1304
|
-
raise CheckError(
|
|
1305
|
-
f"Initiator Logical Address doesn't match, ila=0x{ila:02X} & ila_rxbuf=0x"
|
|
1306
|
-
f"{ila_rxbuf:02X}",
|
|
1307
|
-
egse.rmap.RMAP_GENERAL_ERROR
|
|
1308
|
-
)
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
def get_transaction_identifier(rxbuf):
|
|
1312
|
-
idx = 5 + get_reply_address_field_length(rxbuf)
|
|
1313
|
-
tid = struct.unpack('>h', rxbuf[idx:idx + 2])[0]
|
|
1314
|
-
return tid
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
def check_key(rxbuf, key):
|
|
1318
|
-
from egse.rmap import RMAP_INVALID_KEY
|
|
1319
|
-
|
|
1320
|
-
idx = 3
|
|
1321
|
-
key_rxbuf = rxbuf[idx]
|
|
1322
|
-
if key != key_rxbuf:
|
|
1323
|
-
raise CheckError(
|
|
1324
|
-
f"Key doesn't match, key={key} & key_rxbuf={key_rxbuf}", RMAP_INVALID_KEY
|
|
1325
|
-
)
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
# Functions to interpret the Instrument Field
|
|
1329
|
-
|
|
1330
|
-
def is_reserved(instruction):
|
|
1331
|
-
"""
|
|
1332
|
-
The reserved bit of the 2-bit packet type field from the instruction field.
|
|
1333
|
-
|
|
1334
|
-
For PLATO this bit shall be zero as the 0b10 and 0b11 packet field values are reserved.
|
|
1335
|
-
|
|
1336
|
-
Returns:
|
|
1337
|
-
The bit value: 1 or 0.
|
|
1338
|
-
"""
|
|
1339
|
-
return (instruction & 0b10000000) >> 7
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
def is_command(instruction):
|
|
1343
|
-
"""Returns True if the RMAP packet is a command packet."""
|
|
1344
|
-
return (instruction & 0b01000000) >> 6
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
def is_reply(instruction):
|
|
1348
|
-
"""Returns True if the RMAP packet is a reply to a previous command packet."""
|
|
1349
|
-
return not is_command(instruction)
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
def is_write(instruction):
|
|
1353
|
-
"""Returns True if the RMAP packet is a write request command packet."""
|
|
1354
|
-
return (instruction & 0b00100000) >> 5
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
def is_read(instruction):
|
|
1358
|
-
"""Returns True if the RMAP packet is a read request command packet."""
|
|
1359
|
-
return not is_write(instruction)
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
def is_verify(instruction):
|
|
1363
|
-
"""Returns True if the RMAP packet needs to do a verify before write."""
|
|
1364
|
-
return (instruction & 0b00010000) >> 4
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
def is_reply_required(instruction):
|
|
1368
|
-
"""Returns True if the reply bit is set in the instruction field.
|
|
1369
|
-
|
|
1370
|
-
Args:
|
|
1371
|
-
instruction (int): the instruction field of an RMAP packet
|
|
1372
|
-
|
|
1373
|
-
Note: the name of this function might be confusing.
|
|
1374
|
-
|
|
1375
|
-
This function does **not** test if the packet is a reply packet, but it checks
|
|
1376
|
-
if the command requests a reply from the target. If you need to test if the
|
|
1377
|
-
packet is a command or a reply, use the is_command() or is_reply() function.
|
|
1378
|
-
|
|
1379
|
-
"""
|
|
1380
|
-
return (instruction & 0b00001000) >> 3
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
def is_increment(instruction):
|
|
1384
|
-
"""Returns True if the data is written to sequential memory addresses."""
|
|
1385
|
-
return (instruction & 0b00000100) >> 2
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
def reply_address_length(instruction):
|
|
1389
|
-
"""Returns the content of the reply address length field.
|
|
1390
|
-
|
|
1391
|
-
The size of the reply address field is then decoded from the following table:
|
|
1392
|
-
|
|
1393
|
-
Address Field Length | Size of Address Field
|
|
1394
|
-
----------------------+-----------------------
|
|
1395
|
-
0b00 | 0 bytes
|
|
1396
|
-
0b01 | 4 bytes
|
|
1397
|
-
0b10 | 8 bytes
|
|
1398
|
-
0b11 | 12 bytes
|
|
1399
|
-
|
|
1400
|
-
"""
|
|
1401
|
-
return (instruction & 0b00000011) << 2
|