cgse 2023.38.0__py3-none-any.whl → 2024.1.3__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 +77 -0
- cgse-2024.1.3.dist-info/METADATA +41 -0
- cgse-2024.1.3.dist-info/RECORD +5 -0
- {cgse-2023.38.0.dist-info → cgse-2024.1.3.dist-info}/WHEEL +1 -2
- cgse-2023.38.0.dist-info/COPYING +0 -674
- cgse-2023.38.0.dist-info/COPYING.LESSER +0 -165
- cgse-2023.38.0.dist-info/METADATA +0 -144
- cgse-2023.38.0.dist-info/RECORD +0 -649
- cgse-2023.38.0.dist-info/entry_points.txt +0 -75
- cgse-2023.38.0.dist-info/top_level.txt +0 -2
- egse/__init__.py +0 -12
- egse/__main__.py +0 -32
- egse/aeu/aeu.py +0 -5235
- egse/aeu/aeu_awg.yaml +0 -265
- egse/aeu/aeu_crio.yaml +0 -273
- egse/aeu/aeu_cs.py +0 -626
- egse/aeu/aeu_devif.py +0 -321
- egse/aeu/aeu_main_ui.py +0 -912
- 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 -234
- egse/alert/alertman_ui.py +0 -603
- 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 -130
- egse/alert/gsm/beaglebone_protocol.py +0 -48
- 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 -129
- 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 -1015
- egse/confman/confman.yaml +0 -67
- egse/confman/confman_cs.py +0 -239
- egse/confman/confman_ui.py +0 -381
- egse/confman/setup_ui.py +0 -565
- egse/control.py +0 -442
- egse/coordinates/__init__.py +0 -531
- egse/coordinates/avoidance.py +0 -103
- egse/coordinates/cslmodel.py +0 -127
- egse/coordinates/laser_tracker_to_dict.py +0 -120
- egse/coordinates/point.py +0 -707
- egse/coordinates/pyplot.py +0 -195
- 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 -1247
- 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 -415
- egse/device.py +0 -269
- egse/dpu/__init__.py +0 -2681
- egse/dpu/ccd_ui.py +0 -508
- egse/dpu/dpu.py +0 -786
- egse/dpu/dpu.yaml +0 -153
- egse/dpu/dpu_cs.py +0 -272
- egse/dpu/dpu_ui.py +0 -668
- egse/dpu/fitsgen.py +0 -2077
- egse/dpu/fitsgen_test.py +0 -752
- 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/constants.py +0 -220
- egse/dsi/esl.py +0 -870
- egse/dsi/rmap.py +0 -1042
- egse/dsi/rmapci.py +0 -37
- egse/dsi/spw.py +0 -154
- egse/dsi/spw_state.py +0 -29
- egse/dummy.py +0 -258
- egse/dyndummy.py +0 -179
- egse/env.py +0 -278
- egse/exceptions.py +0 -88
- egse/fdir/__init__.py +0 -28
- egse/fdir/fdir_manager.py +0 -85
- egse/fdir/fdir_manager.yaml +0 -51
- egse/fdir/fdir_manager_controller.py +0 -228
- egse/fdir/fdir_manager_cs.py +0 -164
- egse/fdir/fdir_manager_interface.py +0 -25
- egse/fdir/fdir_remote.py +0 -73
- egse/fdir/fdir_remote.yaml +0 -37
- egse/fdir/fdir_remote_controller.py +0 -50
- egse/fdir/fdir_remote_cs.py +0 -97
- egse/fdir/fdir_remote_interface.py +0 -14
- egse/fdir/fdir_remote_popup.py +0 -31
- egse/fee/__init__.py +0 -114
- egse/fee/f_fee_register.yaml +0 -43
- egse/fee/fee.py +0 -631
- egse/fee/feesim.py +0 -750
- egse/fee/n_fee_hk.py +0 -761
- egse/fee/nfee.py +0 -187
- egse/filterwheel/__init__.py +0 -4
- egse/filterwheel/eksma/__init__.py +0 -24
- egse/filterwheel/eksma/fw8smc4.py +0 -661
- 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 -81
- egse/filterwheel/eksma/fw8smc4_ui.py +0 -940
- egse/filterwheel/eksma/fw8smc5.py +0 -111
- 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 -1068
- egse/filterwheel/eksma/testpythonfw.py +0 -215
- egse/fov/__init__.py +0 -65
- egse/fov/fov_hk.py +0 -712
- egse/fov/fov_ui.py +0 -861
- 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 -135
- 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 -1281
- egse/gui/formatter.py +0 -10
- egse/gui/led.py +0 -162
- egse/gui/limitswitch.py +0 -143
- egse/gui/mechanisms.py +0 -588
- egse/gui/states.py +0 -148
- egse/gui/stripchart.py +0 -729
- 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 -138
- 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 -196
- egse/hexapod/symetrie/puna_protocol.py +0 -131
- egse/hexapod/symetrie/puna_ui.py +0 -434
- 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 -415
- egse/hexapod/symetrie/zonda_protocol.py +0 -119
- egse/hexapod/symetrie/zonda_ui.py +0 -449
- egse/hk.py +0 -765
- 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 -69
- 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/macOS/ESL-RMAP_v34_86.dylib +0 -0
- egse/lib/macOS/EtherSpaceLink_v34_86.dylib +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 -73
- egse/logger/__init__.py +0 -243
- egse/logger/log_cs.py +0 -321
- egse/metrics.py +0 -98
- 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 -163
- 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 -811
- 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 -603
- egse/proxy.py +0 -522
- egse/randomwalk.py +0 -140
- egse/reg.py +0 -585
- egse/reload.py +0 -122
- egse/reprocess.py +0 -675
- egse/resource.py +0 -333
- egse/rst.py +0 -135
- egse/search.py +0 -182
- egse/serialdevice.py +0 -190
- egse/services.py +0 -212
- egse/services.yaml +0 -51
- egse/settings.py +0 -379
- egse/settings.yaml +0 -980
- egse/setup.py +0 -1180
- 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 -69
- 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 -1479
- egse/stages/__init__.py +0 -12
- egse/stages/aerotech/ensemble.py +0 -247
- 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 -193
- egse/stages/arun/smd3.py +0 -111
- egse/stages/arun/smd3.yaml +0 -68
- egse/stages/arun/smd3_controller.py +0 -472
- 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 -904
- 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 -111
- 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 -1004
- egse/storage/persistence.py +0 -2295
- egse/storage/storage.yaml +0 -72
- egse/storage/storage_cs.py +0 -214
- egse/styles/dark.qss +0 -343
- egse/styles/default.qss +0 -48
- egse/synoptics/__init__.py +0 -412
- egse/synoptics/syn.yaml +0 -9
- egse/synoptics/syn_cs.py +0 -195
- egse/system.py +0 -1408
- egse/tcs/__init__.py +0 -14
- egse/tcs/tcs.py +0 -874
- 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 -177
- 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 -116
- egse/tempcontrol/agilent/agilent34970_devif.py +0 -182
- egse/tempcontrol/agilent/agilent34970_protocol.py +0 -99
- egse/tempcontrol/agilent/agilent34972.py +0 -111
- egse/tempcontrol/agilent/agilent34972.yaml +0 -44
- egse/tempcontrol/agilent/agilent34972_cs.py +0 -117
- egse/tempcontrol/agilent/agilent34972_devif.py +0 -189
- egse/tempcontrol/agilent/agilent34972_protocol.py +0 -101
- egse/tempcontrol/beaglebone/beaglebone.py +0 -342
- egse/tempcontrol/beaglebone/beaglebone.yaml +0 -110
- egse/tempcontrol/beaglebone/beaglebone_cs.py +0 -117
- egse/tempcontrol/beaglebone/beaglebone_protocol.py +0 -135
- egse/tempcontrol/beaglebone/beaglebone_ui.py +0 -681
- egse/tempcontrol/digalox/digalox.py +0 -107
- egse/tempcontrol/digalox/digalox.yaml +0 -36
- egse/tempcontrol/digalox/digalox_cs.py +0 -112
- egse/tempcontrol/digalox/digalox_protocol.py +0 -55
- 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 -78
- 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 -73
- egse/tempcontrol/lakeshore/lsci_ui.py +0 -389
- 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 -727
- egse/tempcontrol/srs/__init__.py +0 -22
- egse/tempcontrol/srs/ptc10.py +0 -875
- egse/tempcontrol/srs/ptc10.yaml +0 -227
- egse/tempcontrol/srs/ptc10_cs.py +0 -128
- egse/tempcontrol/srs/ptc10_devif.py +0 -118
- egse/tempcontrol/srs/ptc10_protocol.py +0 -42
- 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 -164
- egse/vacuum/beaglebone/beaglebone_protocol.py +0 -193
- egse/vacuum/beaglebone/beaglebone_ui.py +0 -638
- egse/vacuum/instrutech/igm402.py +0 -92
- egse/vacuum/instrutech/igm402.yaml +0 -90
- egse/vacuum/instrutech/igm402_controller.py +0 -128
- 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 -102
- egse/vacuum/keller/leo3.yaml +0 -38
- egse/vacuum/keller/leo3_controller.py +0 -83
- 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 -316
- egse/vacuum/mks/evision_interface.py +0 -60
- egse/vacuum/mks/evision_simulator.py +0 -24
- egse/vacuum/mks/evision_ui.py +0 -704
- 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 -39
- egse/vacuum/pfeiffer/tc400.py +0 -113
- egse/vacuum/pfeiffer/tc400.yaml +0 -83
- egse/vacuum/pfeiffer/tc400_controller.py +0 -140
- egse/vacuum/pfeiffer/tc400_cs.py +0 -109
- egse/vacuum/pfeiffer/tc400_interface.py +0 -70
- egse/vacuum/pfeiffer/tc400_simulator.py +0 -24
- egse/vacuum/pfeiffer/tpg261.py +0 -81
- 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 -60
- egse/vacuum/pfeiffer/tpg261_simulator.py +0 -24
- 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 -44
- scripts/check_hdf5_files.py +0 -192
- scripts/check_register_sync.py +0 -47
- scripts/create_hdf5_report.py +0 -295
- scripts/csl_model.py +0 -436
- scripts/csl_restore_setup.py +0 -230
- scripts/export-grafana-dashboards.py +0 -50
- scripts/fdir/cs_recovery/fdir_cs_recovery.py +0 -59
- scripts/fdir/fdir_table.yaml +0 -70
- scripts/fdir/fdir_test_recovery.py +0 -11
- scripts/fdir/hw_recovery/fdir_agilent_hw_recovery.py +0 -73
- scripts/fdir/limit_recovery/fdir_agilent_limit.py +0 -64
- scripts/fdir/limit_recovery/fdir_bb_heater_limit.py +0 -61
- 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/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/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/tm_gen/tm_gen_agilent.py +0 -38
- 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/alert/__init__.py
DELETED
|
@@ -1,1049 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import threading
|
|
3
|
-
import ssl
|
|
4
|
-
import time
|
|
5
|
-
import importlib
|
|
6
|
-
import yaml
|
|
7
|
-
import numpy as np
|
|
8
|
-
import zmq
|
|
9
|
-
import pickle
|
|
10
|
-
from smtplib import SMTP
|
|
11
|
-
from collections import namedtuple
|
|
12
|
-
|
|
13
|
-
from egse.setup import load_setup
|
|
14
|
-
from egse.zmq_ser import connect_address, bind_address
|
|
15
|
-
from egse.device import DeviceInterface
|
|
16
|
-
from egse.command import ClientServerCommand
|
|
17
|
-
from egse.proxy import Proxy
|
|
18
|
-
from egse.protocol import CommandProtocol
|
|
19
|
-
from egse.decorators import dynamic_interface
|
|
20
|
-
from egse.settings import Settings
|
|
21
|
-
from egse.system import replace_environment_variable, find_class
|
|
22
|
-
from egse.hk import get_housekeeping, HKError
|
|
23
|
-
from egse.system import format_datetime, EPOCH_1958_1970
|
|
24
|
-
from egse.control import ControlServer, is_control_server_active
|
|
25
|
-
from egse.alert.gsm.beaglebone import BeagleboneProxy
|
|
26
|
-
from egse.fdir.fdir_manager import FdirManagerProxy
|
|
27
|
-
from egse.procman import ProcessManagerProxy
|
|
28
|
-
|
|
29
|
-
logger = logging.getLogger(__name__)
|
|
30
|
-
|
|
31
|
-
CTRL_SETTINGS = Settings.load("Alert Manager Control Server")
|
|
32
|
-
DEVICE_SETTINGS = Settings.load(filename='alertman.yaml')
|
|
33
|
-
|
|
34
|
-
CS_STATUS = ['Offline', 'Controller disconnected', 'Online']
|
|
35
|
-
|
|
36
|
-
LIMIT = 0
|
|
37
|
-
SETPOINT = 1
|
|
38
|
-
RATEOFCHANGE = 2
|
|
39
|
-
MASK = 3
|
|
40
|
-
|
|
41
|
-
class AlertException(BaseException):
|
|
42
|
-
"""
|
|
43
|
-
Base class for all expcetion related to the Alerts
|
|
44
|
-
"""
|
|
45
|
-
pass
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
class AlertManagerCommand(ClientServerCommand):
|
|
50
|
-
"""
|
|
51
|
-
Command (client-server) for process management.
|
|
52
|
-
"""
|
|
53
|
-
pass
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def load_alert_configuration(phase):
|
|
58
|
-
""" Load the alert configuration for the configration file.
|
|
59
|
-
|
|
60
|
-
Returns all the alerts defined in the given file. Uses defaults for missing fields.
|
|
61
|
-
"""
|
|
62
|
-
AlertConfiguration = namedtuple('AlertConfiguration', ['name',
|
|
63
|
-
'metric',
|
|
64
|
-
'timeout',
|
|
65
|
-
'unit',
|
|
66
|
-
'actions',
|
|
67
|
-
'maximum_age',
|
|
68
|
-
'fdir_code',
|
|
69
|
-
'sample_rate',
|
|
70
|
-
'alert_type',
|
|
71
|
-
'limits'])
|
|
72
|
-
|
|
73
|
-
try:
|
|
74
|
-
configuration = load_setup().gse.alert_manager.configuration[phase]
|
|
75
|
-
except AttributeError as ex:
|
|
76
|
-
raise AlertException("Could not find a configuration for the {} phase in the setup".format(phase)) from ex
|
|
77
|
-
else:
|
|
78
|
-
alerts = {}
|
|
79
|
-
|
|
80
|
-
for name, config in configuration['alerts'].items():
|
|
81
|
-
try:
|
|
82
|
-
if 'operational_limit' in config:
|
|
83
|
-
alert_type = LIMIT
|
|
84
|
-
|
|
85
|
-
limits = {
|
|
86
|
-
'min' : config['operational_limit'].get('min', None),
|
|
87
|
-
'max' : config['operational_limit'].get('max', None)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
elif 'rate_of_change' in config:
|
|
91
|
-
alert_type = RATEOFCHANGE
|
|
92
|
-
|
|
93
|
-
limits = {
|
|
94
|
-
'limit' : config['rate_of_change'].get('limit', None),
|
|
95
|
-
'time_window' : config['rate_of_change'].get('time_window', 60)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if limits['limit'] is None:
|
|
99
|
-
raise AlertException(\
|
|
100
|
-
"Invalid configuration: No limit defined in the configuration")
|
|
101
|
-
|
|
102
|
-
elif 'mask' in config:
|
|
103
|
-
alert_type = MASK
|
|
104
|
-
limits = {'mask': config['mask']}
|
|
105
|
-
|
|
106
|
-
elif 'setpoint' in config:
|
|
107
|
-
alert_type = SETPOINT
|
|
108
|
-
|
|
109
|
-
limits = {
|
|
110
|
-
'metric' : None,
|
|
111
|
-
'value' : None,
|
|
112
|
-
'offset' : config['setpoint'].get('offset', 2)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if 'setpoint' in config:
|
|
116
|
-
if 'sensor' not in config['setpoint'] and \
|
|
117
|
-
'value' not in config['setpoint']:
|
|
118
|
-
raise AlertException(\
|
|
119
|
-
"Invalid configuration: No metric or value defined in configuration")
|
|
120
|
-
elif 'sensor' in config['setpoint'] and \
|
|
121
|
-
'value' in config['setpoint']:
|
|
122
|
-
raise AlertException(\
|
|
123
|
-
"Invalid configuration: Can not have both a metric and value in configuration")
|
|
124
|
-
else:
|
|
125
|
-
if 'sensor' in config['setpoint']:
|
|
126
|
-
limits['metric'] = config['setpoint']['sensor']
|
|
127
|
-
if 'value' in config['setpoint']:
|
|
128
|
-
limits['value'] = config['setpoint']['value']
|
|
129
|
-
else:
|
|
130
|
-
raise AlertException("Invalid configuration: No alert type was defined in the configuration")
|
|
131
|
-
|
|
132
|
-
alerts[name] = AlertConfiguration(
|
|
133
|
-
name,
|
|
134
|
-
config['sensor'],
|
|
135
|
-
config.get('timeout', 5),
|
|
136
|
-
config.get('unit', ''),
|
|
137
|
-
config.get('actions', []),
|
|
138
|
-
config.get('maxAge', 30),
|
|
139
|
-
config.get('fdir', ''),
|
|
140
|
-
config.get('sampleRate', 5),
|
|
141
|
-
alert_type,
|
|
142
|
-
limits
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
except Exception as ex:
|
|
146
|
-
logger.warning("Could not create configuration for alert '%s' : %s" % (name, ex))
|
|
147
|
-
|
|
148
|
-
return configuration, alerts
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
class Alert:
|
|
153
|
-
"""
|
|
154
|
-
Alert class representing a single alert
|
|
155
|
-
|
|
156
|
-
An alert has one main function self.update() which retrieves data from housekeeping and compares
|
|
157
|
-
it to the limits defined in the configuration. The Alert Manager Controller runs this method in
|
|
158
|
-
a thread. Sanity checks have been added to prevent false positives.
|
|
159
|
-
"""
|
|
160
|
-
def __init__(self, configuration, notification_worker):
|
|
161
|
-
""" Initialization of a new alert, to be used by the Alert Manager
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
Args:
|
|
165
|
-
configuration (_type_): _description_
|
|
166
|
-
notification_worker (_type_): _description_
|
|
167
|
-
"""
|
|
168
|
-
self.configuration = configuration
|
|
169
|
-
self.notification_worker = notification_worker
|
|
170
|
-
self.value = 0.0
|
|
171
|
-
self.triggered = False
|
|
172
|
-
self.notified = False
|
|
173
|
-
self.active = False
|
|
174
|
-
self.timed_out = 0.0
|
|
175
|
-
self.cycle_check = False
|
|
176
|
-
self.cycle_timeout = 0.0
|
|
177
|
-
self.lastTimestamp = 0.0
|
|
178
|
-
self.last_update = 0.0
|
|
179
|
-
self.thread = threading.Thread()
|
|
180
|
-
|
|
181
|
-
def metric_is_available(self):
|
|
182
|
-
""" Check whether a metric is available in the HK files """
|
|
183
|
-
try:
|
|
184
|
-
timestamp, _ = get_housekeeping(hk_name=self.configuration.metric)
|
|
185
|
-
if time.time() - (float(timestamp) - EPOCH_1958_1970) > self.configuration.maximum_age:
|
|
186
|
-
raise
|
|
187
|
-
except:
|
|
188
|
-
logger.warning(f"Alert '{self.configuration.name}' : " \
|
|
189
|
-
f"Metric: '{self.configuration.metric}' is currently unavailable." \
|
|
190
|
-
f"Please restart this alert when metric is available.")
|
|
191
|
-
return False
|
|
192
|
-
else:
|
|
193
|
-
return True
|
|
194
|
-
|
|
195
|
-
def daily_cycle_check(self):
|
|
196
|
-
""" When a metric is not found in housekeeping, keeping trying for 60 seconds.
|
|
197
|
-
This makes sure that alerts don't crash during the daily cycle of housekeeping files
|
|
198
|
-
|
|
199
|
-
Returns:
|
|
200
|
-
bool: Whether the file has cycled or not
|
|
201
|
-
"""
|
|
202
|
-
try:
|
|
203
|
-
_, _ = get_housekeeping(hk_name=self.configuration.metric)
|
|
204
|
-
except HKError as ex:
|
|
205
|
-
if not self.cycle_check:
|
|
206
|
-
self.cycle_check = True
|
|
207
|
-
self.cycle_timeout = time.time()
|
|
208
|
-
|
|
209
|
-
if (time.time() - self.cycle_timeout) > 300:
|
|
210
|
-
logger.info(f"Alert '{self.configuration.name}' : No daily file found after 5 minutes. Housekeeping is unavailable: {ex}")
|
|
211
|
-
self.active = False
|
|
212
|
-
self.triggered = False
|
|
213
|
-
self.timed_out = 0.0
|
|
214
|
-
|
|
215
|
-
return False
|
|
216
|
-
else:
|
|
217
|
-
logger.debug(f"Alert '{self.configuration.name}' : Daily file has been cycled")
|
|
218
|
-
self.cycle_check = False
|
|
219
|
-
return True
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
def generate_alert_message(self):
|
|
223
|
-
""" Generates the email body for the alert based on the alert type
|
|
224
|
-
"""
|
|
225
|
-
body = ""
|
|
226
|
-
|
|
227
|
-
fdir_code = self.configuration.fdir_code
|
|
228
|
-
actions = self.configuration.actions
|
|
229
|
-
|
|
230
|
-
if self.configuration.alert_type == LIMIT:
|
|
231
|
-
if self.configuration.limits['min'] != None and self.configuration.limits['max'] != None:
|
|
232
|
-
body += f"- {self.configuration.name} exceeded its operational limits of {float(self.configuration.limits['min']):.3e} & {float(self.configuration.limits['max']):.3e}. Current value: {float(self.value):.3e}\n"
|
|
233
|
-
elif self.configuration.limits['min'] != None:
|
|
234
|
-
body += f"- {self.configuration.name} exceeded its operatinoal limits of {float(self.configuration.limits['min']):.3e}. Current value: {float(self.value):.3e}\n"
|
|
235
|
-
elif self.configuration.limits['max'] != None:
|
|
236
|
-
body += f"- {self.configuration.name} exceeded its operational limits of {float(self.configuration.limits['max']):.3e}. Current value: {float(self.value):.3e}\n"
|
|
237
|
-
elif self.configuration.alert_type is SETPOINT:
|
|
238
|
-
body += f"- {self.configuration.name} setpoint offset was larger than {float(self.configuration.limits['offset']):.3e}. Current value: {float(self.value):.3e}\n"
|
|
239
|
-
elif self.configuration.alert_type is MASK:
|
|
240
|
-
body += f"- {self.configuration.name} is {bool(self.value)}"
|
|
241
|
-
elif self.configuration.alert_type is RATEOFCHANGE:
|
|
242
|
-
body += f"- {self.configuration.name} has exceeded its RoC limit of {float(self.configuration.limits['limit']):.3e}. Current value: {float(self.value):.3e}\n"
|
|
243
|
-
|
|
244
|
-
if fdir_code:
|
|
245
|
-
body += f"\t- FDIR code: '{self.configuration.fdir_code}' was fired\n"
|
|
246
|
-
|
|
247
|
-
if actions:
|
|
248
|
-
body += f"\t- Corrective actions:\n"
|
|
249
|
-
for action in actions:
|
|
250
|
-
body += f"\t\t\t- {action}\n"
|
|
251
|
-
|
|
252
|
-
return body
|
|
253
|
-
|
|
254
|
-
def trigger_alert(self):
|
|
255
|
-
""" Send a notifcation and FDIR signal, also ensures that alerts are only triggered once
|
|
256
|
-
"""
|
|
257
|
-
if not self.notified:
|
|
258
|
-
self.triggered = True
|
|
259
|
-
logger.critical(f"Alert '{self.configuration.name}' : triggered!")
|
|
260
|
-
|
|
261
|
-
self.notification_worker.add_notification(0, self.generate_alert_message())
|
|
262
|
-
|
|
263
|
-
self.signal_fdir()
|
|
264
|
-
|
|
265
|
-
self.notified = True
|
|
266
|
-
|
|
267
|
-
def clear_alert(self):
|
|
268
|
-
""" Resets the alert after it being triggered
|
|
269
|
-
"""
|
|
270
|
-
if self.triggered:
|
|
271
|
-
logger.info(f"Alert '{self.configuration.name}' : Alert status has been reset.")
|
|
272
|
-
self.triggered = False
|
|
273
|
-
self.timed_out = 0.0
|
|
274
|
-
self.notified = False
|
|
275
|
-
|
|
276
|
-
def signal_fdir(self):
|
|
277
|
-
""" Signal the FDIR manager, if a fidr code was configured
|
|
278
|
-
"""
|
|
279
|
-
if self.configuration.fdir_code:
|
|
280
|
-
try:
|
|
281
|
-
logger.info(f"Signaled fdir: '{self.configuration.fdir_code}'")
|
|
282
|
-
with FdirManagerProxy() as fdir:
|
|
283
|
-
fdir.signal_fdir(self.configuration.fdir_code, self.configuration.metric)
|
|
284
|
-
except Exception as e:
|
|
285
|
-
logger.critical(f"Alert '{self.configuration.name}' : Could not signal FDIR manager. {e}")
|
|
286
|
-
else:
|
|
287
|
-
logger.info(f"Alert '{self.configuration.name}' Does not have a configured FDIR code.")
|
|
288
|
-
|
|
289
|
-
def get_housekeeping(self, metric, time_window=None):
|
|
290
|
-
""" Retrieves housekeeping from the HK files. Checks whether the value actually exists,
|
|
291
|
-
and if the age is still appropiate.
|
|
292
|
-
"""
|
|
293
|
-
try:
|
|
294
|
-
timestamps, values = get_housekeeping(hk_name=metric, time_window=time_window)
|
|
295
|
-
except HKError:
|
|
296
|
-
if self.daily_cycle_check():
|
|
297
|
-
timestamps, values = get_housekeeping(hk_name=metric, time_window=time_window)
|
|
298
|
-
else:
|
|
299
|
-
raise AlertException(f"{self.configuration.name} : Daily file cycle is being checked")
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
most_recent_timestamp = float(timestamps[-1]) if type(timestamps) == np.ndarray else timestamps
|
|
303
|
-
|
|
304
|
-
self.lastTimestamp = most_recent_timestamp
|
|
305
|
-
|
|
306
|
-
is_alive = time.time() - (most_recent_timestamp - EPOCH_1958_1970) > self.configuration.maximum_age
|
|
307
|
-
value_exists = values[-1] if type(values) == np.ndarray else values
|
|
308
|
-
|
|
309
|
-
if is_alive:
|
|
310
|
-
self.active = False
|
|
311
|
-
logger.critical(f"Alert '{self.configuration.name}' : Last housekeeping value was older than {self.configuration.maximum_age} seconds")
|
|
312
|
-
raise Exception(f"Alert '{self.configuration.name}' : Last housekeeping value was older than {self.configuration.maximum_age} seconds")
|
|
313
|
-
|
|
314
|
-
if not value_exists:
|
|
315
|
-
logger.critical(f"Alert '{self.configuration.name}' : Housekeeping returned an emptry string as latest value")
|
|
316
|
-
self.active = False
|
|
317
|
-
raise Exception(f"Alert '{self.configuration.name}' : Housekeeping returned an emptry string as latest value")
|
|
318
|
-
|
|
319
|
-
return timestamps, values
|
|
320
|
-
|
|
321
|
-
def limit_check(self, value):
|
|
322
|
-
""" Compares a given value to the configured upper and lower limits
|
|
323
|
-
"""
|
|
324
|
-
min_triggered = False
|
|
325
|
-
max_triggered = False
|
|
326
|
-
|
|
327
|
-
self.value = value
|
|
328
|
-
|
|
329
|
-
if self.configuration.limits['max']:
|
|
330
|
-
if float(value) > self.configuration.limits['max']:
|
|
331
|
-
self.timed_out = time.time() if self.timed_out == 0.0 else self.timed_out
|
|
332
|
-
if (time.time() - self.timed_out) > self.configuration.timeout:
|
|
333
|
-
max_triggered = True
|
|
334
|
-
|
|
335
|
-
if self.configuration.limits['min']:
|
|
336
|
-
if float(value) < self.configuration.limits['min']:
|
|
337
|
-
self.timed_out = time.time() if self.timed_out == 0.0 else self.timed_out
|
|
338
|
-
if (time.time() - self.timed_out) > self.configuration.timeout:
|
|
339
|
-
min_triggered = True
|
|
340
|
-
|
|
341
|
-
if min_triggered or max_triggered:
|
|
342
|
-
self.trigger_alert()
|
|
343
|
-
else:
|
|
344
|
-
self.clear_alert()
|
|
345
|
-
|
|
346
|
-
def setpoint_check(self, value):
|
|
347
|
-
""" Compares a given value to the configured setpoint (dynamic/static setpoint)
|
|
348
|
-
"""
|
|
349
|
-
self.value = value
|
|
350
|
-
|
|
351
|
-
if self.configuration.limits['metric']:
|
|
352
|
-
try:
|
|
353
|
-
_, sp_value = self.get_housekeeping(self.configuration.limits['metric'], None)
|
|
354
|
-
except Exception as ex:
|
|
355
|
-
logger.warning(ex, exc_info=True)
|
|
356
|
-
self.active = False
|
|
357
|
-
return
|
|
358
|
-
|
|
359
|
-
else:
|
|
360
|
-
sp_value = self.configuration.limits['value']
|
|
361
|
-
|
|
362
|
-
if not (float(sp_value) - self.configuration.limits['offset']) <= float(value) <= (float(sp_value) + self.configuration.limits['offset']):
|
|
363
|
-
self.timed_out = time.time() if self.timed_out == 0.0 else self.timed_out
|
|
364
|
-
if (time.time() - self.timed_out) > self.configuration.timeout:
|
|
365
|
-
self.trigger_alert()
|
|
366
|
-
else:
|
|
367
|
-
self.clear_alert()
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
def rate_of_change_check(self, timestamps, values):
|
|
371
|
-
""" Compares a given value to the configured rate-of-change setpoint
|
|
372
|
-
"""
|
|
373
|
-
deltaY = float(values[-1]) - float(values[0])
|
|
374
|
-
deltaX = float(timestamps[-1]) - float(timestamps[0])
|
|
375
|
-
|
|
376
|
-
self.value = deltaY / deltaX
|
|
377
|
-
|
|
378
|
-
if self.configuration.limits['limit'] < 0:
|
|
379
|
-
if self.value < self.configuration.limits['limit']:
|
|
380
|
-
self.timed_out = time.time() if self.timed_out == 0.0 else self.timed_out
|
|
381
|
-
if (time.time() - self.timed_out) > self.configuration.timeout:
|
|
382
|
-
self.trigger_alert()
|
|
383
|
-
else:
|
|
384
|
-
self.clear_alert()
|
|
385
|
-
else:
|
|
386
|
-
if self.value > self.configuration.limits['limit']:
|
|
387
|
-
self.timed_out = time.time() if self.timed_out == 0.0 else self.timed_out
|
|
388
|
-
if (time.time() - self.timed_out) > self.configuration.timeout:
|
|
389
|
-
self.trigger_alert()
|
|
390
|
-
else:
|
|
391
|
-
self.clear_alert()
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
def mask_check(self, value):
|
|
395
|
-
""" Masks a bitmap and check the state of a single bit
|
|
396
|
-
"""
|
|
397
|
-
if isinstance(value, str):
|
|
398
|
-
value = eval(value)
|
|
399
|
-
|
|
400
|
-
self.value = int(value)
|
|
401
|
-
|
|
402
|
-
if (self.value >> self.configuration.limits['mask']) & 0b1:
|
|
403
|
-
self.timed_out = time.time() if self.timed_out == 0.0 else self.timed_out
|
|
404
|
-
if (time.time() - self.timed_out) > self.configuration.maximum_age:
|
|
405
|
-
self.trigger_alert()
|
|
406
|
-
else:
|
|
407
|
-
self.clear_alert()
|
|
408
|
-
|
|
409
|
-
def update(self):
|
|
410
|
-
""" Update current alert status
|
|
411
|
-
"""
|
|
412
|
-
if self.active:
|
|
413
|
-
if (time.time() - self.last_update) > self.configuration.sample_rate:
|
|
414
|
-
try:
|
|
415
|
-
timestamps, values = self.get_housekeeping(self.configuration.metric,
|
|
416
|
-
self.configuration.limits.get('time_window', None))
|
|
417
|
-
except Exception as ex:
|
|
418
|
-
logger.warning(ex)
|
|
419
|
-
self.active = False
|
|
420
|
-
return
|
|
421
|
-
except AlertException as a_ex:
|
|
422
|
-
logger.warning(a_ex)
|
|
423
|
-
self.last_update = (time.time() + 10) # Increase delay before next check
|
|
424
|
-
return
|
|
425
|
-
else:
|
|
426
|
-
self.cycle_check = False
|
|
427
|
-
|
|
428
|
-
try:
|
|
429
|
-
if self.configuration.alert_type == LIMIT:
|
|
430
|
-
self.limit_check(values)
|
|
431
|
-
elif self.configuration.alert_type is SETPOINT:
|
|
432
|
-
self.setpoint_check(values)
|
|
433
|
-
elif self.configuration.alert_type is MASK:
|
|
434
|
-
self.mask_check(values)
|
|
435
|
-
elif self.configuration.alert_type is RATEOFCHANGE:
|
|
436
|
-
self.rate_of_change_check(timestamps, values)
|
|
437
|
-
except Exception as ex:
|
|
438
|
-
logger.warning(ex)
|
|
439
|
-
raise AlertException(ex)
|
|
440
|
-
|
|
441
|
-
self.last_update = time.time()
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
class ControlServerWorker:
|
|
446
|
-
""" Worker to monitor all running control servers
|
|
447
|
-
"""
|
|
448
|
-
def __init__(self, notifyer):
|
|
449
|
-
self.pm_proxy = ProcessManagerProxy()
|
|
450
|
-
self.devices = self.pm_proxy.get_devices()
|
|
451
|
-
self.notifyer = notifyer
|
|
452
|
-
self.thread = threading.Thread()
|
|
453
|
-
self.active = False
|
|
454
|
-
self.monitors = {}
|
|
455
|
-
|
|
456
|
-
for process_name, process_info in self.devices.items():
|
|
457
|
-
|
|
458
|
-
if ('alert' in process_name.lower()) or ('fdir' in process_name.lower()) or ('dpu' in process_name.lower()):
|
|
459
|
-
continue
|
|
460
|
-
|
|
461
|
-
self.monitors[process_name] = {'name' : process_name,
|
|
462
|
-
'type' : process_info[0],
|
|
463
|
-
'type_as_type' : find_class(process_info[0][7:]),
|
|
464
|
-
'device_args' : process_info[1],
|
|
465
|
-
'socket' : None,
|
|
466
|
-
'timeout' : 0.5,
|
|
467
|
-
'connected' : False,
|
|
468
|
-
'address' : None,
|
|
469
|
-
'prev_status' : False}
|
|
470
|
-
|
|
471
|
-
self.fdir_codes = {}
|
|
472
|
-
|
|
473
|
-
self.connect_to_sockets()
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
def load_fdir_codes(self, configuration):
|
|
477
|
-
if 'hardware' in configuration:
|
|
478
|
-
self.fdir_codes = configuration['hardware']
|
|
479
|
-
else:
|
|
480
|
-
self.fdir_codes = []
|
|
481
|
-
|
|
482
|
-
def connect_to_sockets(self):
|
|
483
|
-
""" Connect to commanding/monitoring socket of control servers
|
|
484
|
-
"""
|
|
485
|
-
for monitor in self.monitors.values():
|
|
486
|
-
try:
|
|
487
|
-
module_name = monitor['type'][7:].rsplit(".", 1)[0]
|
|
488
|
-
module = importlib.import_module(module_name)
|
|
489
|
-
|
|
490
|
-
ctrl_settings = module.CTRL_SETTINGS
|
|
491
|
-
transport = ctrl_settings.PROTOCOL
|
|
492
|
-
hostname = ctrl_settings.HOSTNAME
|
|
493
|
-
|
|
494
|
-
if module_name == "egse.aeu.aeu" or "egse.tempcontrol.agilent.agilent3497" in module_name:
|
|
495
|
-
name = monitor['name'].split(" ")[1].upper()
|
|
496
|
-
|
|
497
|
-
commanding_port = ctrl_settings[name]['COMMANDING_PORT']
|
|
498
|
-
monitoring_port = ctrl_settings[name]['MONITORING_PORT']
|
|
499
|
-
else:
|
|
500
|
-
commanding_port = ctrl_settings.COMMANDING_PORT
|
|
501
|
-
monitoring_port = ctrl_settings.MONITORING_PORT
|
|
502
|
-
|
|
503
|
-
monitoring_address = connect_address(transport, hostname, monitoring_port)
|
|
504
|
-
|
|
505
|
-
monitor['socket'] = zmq.Context().socket(zmq.SUB)
|
|
506
|
-
monitor['socket'].connect(monitoring_address)
|
|
507
|
-
monitor['socket'].setsockopt_string(zmq.SUBSCRIBE, "")
|
|
508
|
-
|
|
509
|
-
monitor['address'] = connect_address(transport, hostname, commanding_port)
|
|
510
|
-
monitor['connected'] = True
|
|
511
|
-
|
|
512
|
-
except AttributeError as attr_er:
|
|
513
|
-
logger.exception(attr_er)
|
|
514
|
-
monitor['connected'] = False
|
|
515
|
-
|
|
516
|
-
# break
|
|
517
|
-
|
|
518
|
-
def get_device_status(self, monitor):
|
|
519
|
-
""" Retreive the current status of a control server
|
|
520
|
-
"""
|
|
521
|
-
status = 0
|
|
522
|
-
|
|
523
|
-
try:
|
|
524
|
-
with find_class(monitor['type'])(*monitor['device_args']) as proxy:
|
|
525
|
-
if isinstance(proxy, DeviceInterface):
|
|
526
|
-
try:
|
|
527
|
-
if proxy.is_connected():
|
|
528
|
-
# CS connected to controller
|
|
529
|
-
status = 2
|
|
530
|
-
else:
|
|
531
|
-
# CS not connected to controller
|
|
532
|
-
status = 1
|
|
533
|
-
except AttributeError:
|
|
534
|
-
status = 1
|
|
535
|
-
|
|
536
|
-
else:
|
|
537
|
-
status = 2
|
|
538
|
-
|
|
539
|
-
except ConnectionError:
|
|
540
|
-
pass
|
|
541
|
-
|
|
542
|
-
return status
|
|
543
|
-
|
|
544
|
-
def start(self):
|
|
545
|
-
""" Start Control server monitor worker
|
|
546
|
-
"""
|
|
547
|
-
logger.info("Starting control server monitoring loop")
|
|
548
|
-
|
|
549
|
-
if self.thread != None:
|
|
550
|
-
if self.thread.is_alive():
|
|
551
|
-
return
|
|
552
|
-
|
|
553
|
-
self.thread = threading.Thread(target=self.run)
|
|
554
|
-
self.thread.start()
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
def stop(self):
|
|
558
|
-
""" Stop Control server monitor worker
|
|
559
|
-
"""
|
|
560
|
-
self.active = False
|
|
561
|
-
if self.thread != None:
|
|
562
|
-
if self.thread.is_alive():
|
|
563
|
-
self.thread.join()
|
|
564
|
-
return
|
|
565
|
-
logger.info("Control server monitor has been stopped")
|
|
566
|
-
|
|
567
|
-
def run(self):
|
|
568
|
-
""" Control server monitoring loop
|
|
569
|
-
"""
|
|
570
|
-
self.active = True
|
|
571
|
-
|
|
572
|
-
while self.active:
|
|
573
|
-
for monitor in self.monitors.values():
|
|
574
|
-
if monitor['connected']:
|
|
575
|
-
status = 0
|
|
576
|
-
try:
|
|
577
|
-
socket_list, _, _ = zmq.select([monitor['socket']], [], [], timeout=monitor['timeout'])
|
|
578
|
-
|
|
579
|
-
if monitor['socket'] in socket_list:
|
|
580
|
-
pickle_string = monitor['socket'].recv()
|
|
581
|
-
monitoring_info = pickle.loads(pickle_string)
|
|
582
|
-
|
|
583
|
-
monitor['timeout'] = monitoring_info['delay'] / 1000 + 0.5 # [s]
|
|
584
|
-
|
|
585
|
-
status = self.get_device_status(monitor)
|
|
586
|
-
|
|
587
|
-
except zmq.ZMQError:
|
|
588
|
-
pass
|
|
589
|
-
|
|
590
|
-
# Timeout occured
|
|
591
|
-
else:
|
|
592
|
-
if not is_control_server_active(endpoint=monitor['address'], timeout=30):
|
|
593
|
-
status = 0
|
|
594
|
-
else:
|
|
595
|
-
monitor['timeout'] += 0.5
|
|
596
|
-
|
|
597
|
-
if monitor['prev_status'] != status:
|
|
598
|
-
|
|
599
|
-
if monitor['prev_status'] >= 1 and status == 0:
|
|
600
|
-
|
|
601
|
-
msg = f"- {monitor['name']} Contol Server has gone offline."
|
|
602
|
-
|
|
603
|
-
self.notifyer.add_notification(-1, msg)
|
|
604
|
-
|
|
605
|
-
logger.warning(f"{monitor['name']} has stopped. Signalling FDIR to attempt a restart")
|
|
606
|
-
|
|
607
|
-
try:
|
|
608
|
-
with FdirManagerProxy() as fdir:
|
|
609
|
-
fdir.signal_fdir('FDIR_CS_STOPPED', [monitor['name']])
|
|
610
|
-
except Exception as ex:
|
|
611
|
-
logger.critical(f"Could not send FDIR signal: {ex}")
|
|
612
|
-
|
|
613
|
-
elif monitor['prev_status'] == 2 and status == 1:
|
|
614
|
-
|
|
615
|
-
msg = f"- {monitor['name']} Control Server has lost connection to the device controller"
|
|
616
|
-
|
|
617
|
-
self.notifyer.add_notification(-1, msg)
|
|
618
|
-
|
|
619
|
-
if self.fdir_codes != {}:
|
|
620
|
-
if monitor['name'] in self.fdir_codes:
|
|
621
|
-
fdir_code = self.fdir_codes[monitor['name']]
|
|
622
|
-
try:
|
|
623
|
-
with FdirManagerProxy() as fdir:
|
|
624
|
-
fdir.signal_fdir(f"FDIR_{fdir_code['code']}_HW", [f"{fdir_code['arg']}"])
|
|
625
|
-
except Exception as ex:
|
|
626
|
-
logger.critical(f"Could not send FDIR signal: {ex}")
|
|
627
|
-
|
|
628
|
-
logger.warning(f"{monitor['name']} has lost connection to the device. Signalling FDIR to recover")
|
|
629
|
-
|
|
630
|
-
monitor['prev_status'] = status
|
|
631
|
-
|
|
632
|
-
logger.debug(f"{monitor['name']} Control Server status: {CS_STATUS[monitor['prev_status']]}")
|
|
633
|
-
time.sleep(60)
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
class AlertNotificationWorker:
|
|
638
|
-
"""
|
|
639
|
-
Worker for handling the pushing of notification
|
|
640
|
-
"""
|
|
641
|
-
def __init__(self):
|
|
642
|
-
self.sender = replace_environment_variable(CTRL_SETTINGS.EMAIL_SENDER)
|
|
643
|
-
self.recipients = replace_environment_variable(CTRL_SETTINGS.EMAIL_CS_RECIPIENTS)
|
|
644
|
-
|
|
645
|
-
self._server = replace_environment_variable(CTRL_SETTINGS.EMAIL_SERVER)
|
|
646
|
-
self._port = 25
|
|
647
|
-
self._context = ssl.create_default_context()
|
|
648
|
-
|
|
649
|
-
self.gsm_module = BeagleboneProxy()
|
|
650
|
-
|
|
651
|
-
self.alert_messages = []
|
|
652
|
-
self.cs_messages = []
|
|
653
|
-
|
|
654
|
-
self.thread = threading.Thread()
|
|
655
|
-
|
|
656
|
-
def notify(self):
|
|
657
|
-
'''
|
|
658
|
-
Threaded method that waits 30 seconds for new messages before pushing the notification
|
|
659
|
-
'''
|
|
660
|
-
# Check if message buffer is empty
|
|
661
|
-
n = len(self.alert_messages) + len(self.cs_messages)
|
|
662
|
-
|
|
663
|
-
if n != 0:
|
|
664
|
-
start_time = time.time()
|
|
665
|
-
|
|
666
|
-
# Wait until no new messages are added to the buffer in the last 30 seconds
|
|
667
|
-
while time.time() - start_time < 60:
|
|
668
|
-
n2 = len(self.alert_messages) + len(self.cs_messages)
|
|
669
|
-
|
|
670
|
-
if n2 is not n:
|
|
671
|
-
start_time = time.time()
|
|
672
|
-
n = n2
|
|
673
|
-
time.sleep(1)
|
|
674
|
-
|
|
675
|
-
self.send_mail()
|
|
676
|
-
self.send_sms()
|
|
677
|
-
|
|
678
|
-
self.clear_notifications()
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
def add_notification(self, nType, msg: str):
|
|
682
|
-
"""
|
|
683
|
-
Adds a message to the notification queue and start the threaded method, if it is not running
|
|
684
|
-
"""
|
|
685
|
-
|
|
686
|
-
# Add notification to message buffer
|
|
687
|
-
if nType in [LIMIT, SETPOINT, RATEOFCHANGE, MASK]:
|
|
688
|
-
self.alert_messages.append(msg)
|
|
689
|
-
else:
|
|
690
|
-
self.cs_messages.append(msg)
|
|
691
|
-
# See if notification thread has already been started
|
|
692
|
-
if not self.thread.is_alive():
|
|
693
|
-
self.thread.__init__(target=self.notify, daemon=True)
|
|
694
|
-
self.thread.start()
|
|
695
|
-
|
|
696
|
-
def clear_notifications(self):
|
|
697
|
-
"""
|
|
698
|
-
Clears the notification queue's
|
|
699
|
-
"""
|
|
700
|
-
self.alert_messages = []
|
|
701
|
-
self.cs_messages = []
|
|
702
|
-
|
|
703
|
-
def send_mail(self):
|
|
704
|
-
"""
|
|
705
|
-
"""
|
|
706
|
-
message = """\
|
|
707
|
-
Subject: Plato Common-EGSE Notification
|
|
708
|
-
|
|
709
|
-
"""
|
|
710
|
-
# message = """Subject: Plato Common-EGSE Notification"""
|
|
711
|
-
if len(self.alert_messages) > 0:
|
|
712
|
-
message += ''.join(self.alert_messages)
|
|
713
|
-
|
|
714
|
-
if len(self.cs_messages) > 0:
|
|
715
|
-
message += ''.join(self.cs_messages)
|
|
716
|
-
|
|
717
|
-
message += "\n\nKind regards,\n" \
|
|
718
|
-
"Your friendly PLATO notification bot\n\n" \
|
|
719
|
-
"If I am not working as expected, please contact: s.n.gomashie@sron.nl"
|
|
720
|
-
try:
|
|
721
|
-
with SMTP(self._server, self._port) as server:
|
|
722
|
-
server.sendmail(self.sender, self.recipients.split(','), message)
|
|
723
|
-
except Exception as ex:
|
|
724
|
-
logger.critical(f"Notification worker could not send an email: {ex}")
|
|
725
|
-
|
|
726
|
-
logger.debug(f"Email send: {self.sender} {self.recipients}")
|
|
727
|
-
|
|
728
|
-
def send_sms(self):
|
|
729
|
-
self.gsm_module.set_alert(1)
|
|
730
|
-
|
|
731
|
-
class AlertManagerInterface:
|
|
732
|
-
"""
|
|
733
|
-
Interface for dynamic loading of the command for Alert Management.
|
|
734
|
-
"""
|
|
735
|
-
@dynamic_interface
|
|
736
|
-
def load_configuration(self, phase = 'none'):
|
|
737
|
-
""" Load a new phase into the alert manager. """
|
|
738
|
-
|
|
739
|
-
raise NotImplementedError
|
|
740
|
-
|
|
741
|
-
@dynamic_interface
|
|
742
|
-
def status(self, name = None) -> dict:
|
|
743
|
-
""" Returns the status of all configured alerts """
|
|
744
|
-
|
|
745
|
-
raise NotImplementedError
|
|
746
|
-
|
|
747
|
-
@dynamic_interface
|
|
748
|
-
def stop_cs_monitor(self):
|
|
749
|
-
""" Stops the control server monitor """
|
|
750
|
-
|
|
751
|
-
raise NotImplementedError
|
|
752
|
-
|
|
753
|
-
@dynamic_interface
|
|
754
|
-
def start_cs_monitor(self):
|
|
755
|
-
""" Starts the control server monitor """
|
|
756
|
-
|
|
757
|
-
raise NotImplementedError
|
|
758
|
-
|
|
759
|
-
@dynamic_interface
|
|
760
|
-
def start_alert(self, name = None):
|
|
761
|
-
""" Start the alert monitor """
|
|
762
|
-
|
|
763
|
-
raise NotImplementedError
|
|
764
|
-
|
|
765
|
-
@dynamic_interface
|
|
766
|
-
def stop_alert(self, name = None):
|
|
767
|
-
""" Stop the alert monitor """
|
|
768
|
-
|
|
769
|
-
raise NotImplementedError
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
class AlertManagerController(AlertManagerInterface):
|
|
774
|
-
|
|
775
|
-
"""
|
|
776
|
-
Controller for Alert Management
|
|
777
|
-
"""
|
|
778
|
-
|
|
779
|
-
def __init__(self, phase='none'):
|
|
780
|
-
""" Initialization for the Alert Manager Controller.
|
|
781
|
-
|
|
782
|
-
Args:
|
|
783
|
-
phase (str, optional): _description_. Defaults to 'none'.
|
|
784
|
-
"""
|
|
785
|
-
self.phase = phase
|
|
786
|
-
self.active = False
|
|
787
|
-
self.alerts = {}
|
|
788
|
-
self.notification_worker = AlertNotificationWorker()
|
|
789
|
-
self.control_server_worker = ControlServerWorker(self.notification_worker)
|
|
790
|
-
|
|
791
|
-
self.thread = threading.Thread()
|
|
792
|
-
|
|
793
|
-
self.load_configuration(phase)
|
|
794
|
-
self.control_server_worker.start()
|
|
795
|
-
|
|
796
|
-
def load_configuration(self, phase = 'none'):
|
|
797
|
-
""" Load a new phase into the alert manager. """
|
|
798
|
-
if phase != 'none':
|
|
799
|
-
self.stop_alert()
|
|
800
|
-
|
|
801
|
-
try:
|
|
802
|
-
self.configuration, alerts = load_alert_configuration(phase)
|
|
803
|
-
self.alerts = self.initialize_alerts(alerts)
|
|
804
|
-
|
|
805
|
-
self.control_server_worker.load_fdir_codes(self.configuration)
|
|
806
|
-
except Exception as ex:
|
|
807
|
-
logger.warning(f"Could not load {phase} phase: {ex}")
|
|
808
|
-
else:
|
|
809
|
-
self.phase = phase
|
|
810
|
-
|
|
811
|
-
else:
|
|
812
|
-
self.stop_alert()
|
|
813
|
-
self.alerts = {}
|
|
814
|
-
self.phase = phase
|
|
815
|
-
|
|
816
|
-
logger.info(f"Alert manager has been configured for the {self.phase} phase")
|
|
817
|
-
|
|
818
|
-
def initialize_alerts(self, alerts):
|
|
819
|
-
""" Initialize the alerts
|
|
820
|
-
"""
|
|
821
|
-
for key, configuration in alerts.items():
|
|
822
|
-
try:
|
|
823
|
-
alerts[key] = Alert(configuration, self.notification_worker)
|
|
824
|
-
except Exception as ex:
|
|
825
|
-
logger.warning(f"Could not create alert '{key}': {ex}")
|
|
826
|
-
return alerts
|
|
827
|
-
|
|
828
|
-
def status(self, name = None) -> dict:
|
|
829
|
-
""" Returns the status of all configured alerts """
|
|
830
|
-
status = {}
|
|
831
|
-
if self.alerts:
|
|
832
|
-
for key, alert in self.alerts.items():
|
|
833
|
-
status[key] = {
|
|
834
|
-
'active' : alert.active,
|
|
835
|
-
'triggered' : alert.triggered,
|
|
836
|
-
'value' : alert.value,
|
|
837
|
-
'lastTimestamp' : alert.lastTimestamp
|
|
838
|
-
}
|
|
839
|
-
if name and (name in status):
|
|
840
|
-
return status[name]
|
|
841
|
-
else:
|
|
842
|
-
return status
|
|
843
|
-
|
|
844
|
-
def stop_cs_monitor(self):
|
|
845
|
-
""" Stops the control server monitor
|
|
846
|
-
"""
|
|
847
|
-
self.control_server_worker.stop()
|
|
848
|
-
|
|
849
|
-
def start_cs_monitor(self):
|
|
850
|
-
""" Starts the control server monitor
|
|
851
|
-
"""
|
|
852
|
-
self.control_server_worker.start()
|
|
853
|
-
|
|
854
|
-
def start_alert(self, name = None):
|
|
855
|
-
""" Start one or all alert monitors
|
|
856
|
-
"""
|
|
857
|
-
if name != None:
|
|
858
|
-
self.alerts[name].active = True
|
|
859
|
-
else:
|
|
860
|
-
for alert in self.alerts.values():
|
|
861
|
-
alert.active = True
|
|
862
|
-
|
|
863
|
-
if self.thread.is_alive():
|
|
864
|
-
return
|
|
865
|
-
|
|
866
|
-
logger.info(f"Starting alert monitor in {self.phase} phase")
|
|
867
|
-
|
|
868
|
-
self.thread.__init__(target=self.run)
|
|
869
|
-
self.thread.setDaemon(True)
|
|
870
|
-
self.thread.start()
|
|
871
|
-
|
|
872
|
-
def stop_alert(self, name = None):
|
|
873
|
-
""" Stop one or all alert monitors
|
|
874
|
-
"""
|
|
875
|
-
if name != None:
|
|
876
|
-
self.alerts[name].active = False
|
|
877
|
-
else:
|
|
878
|
-
|
|
879
|
-
self.active = False
|
|
880
|
-
for alert in self.alerts.values():
|
|
881
|
-
alert.active = False
|
|
882
|
-
|
|
883
|
-
if self.thread.is_alive():
|
|
884
|
-
self.thread.join()
|
|
885
|
-
|
|
886
|
-
def run(self):
|
|
887
|
-
""" The alert monitoring loop
|
|
888
|
-
"""
|
|
889
|
-
self.active = True
|
|
890
|
-
|
|
891
|
-
while self.active:
|
|
892
|
-
for alert in self.alerts.values():
|
|
893
|
-
if alert.active:
|
|
894
|
-
try:
|
|
895
|
-
alert.update()
|
|
896
|
-
except Exception as ex:
|
|
897
|
-
logger.info(ex)
|
|
898
|
-
time.sleep(0.1)
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
class AlertManagerProxy(Proxy, AlertManagerInterface):
|
|
904
|
-
"""
|
|
905
|
-
Proxy for Alert Management, used to connect to the Alert Manager
|
|
906
|
-
Control Server and send commands remotely.
|
|
907
|
-
"""
|
|
908
|
-
|
|
909
|
-
def __init__(
|
|
910
|
-
self,
|
|
911
|
-
protocol=CTRL_SETTINGS.PROTOCOL,
|
|
912
|
-
hostname=CTRL_SETTINGS.HOSTNAME,
|
|
913
|
-
port=CTRL_SETTINGS.COMMANDING_PORT,
|
|
914
|
-
):
|
|
915
|
-
"""Initialisation of a new Proxy for Alert Management.
|
|
916
|
-
|
|
917
|
-
If no connection details (transport protocol, hostname, and port) are
|
|
918
|
-
not provided, these are taken from the settings file.
|
|
919
|
-
|
|
920
|
-
Args:
|
|
921
|
-
- protocol: Transport protocol [default is taken from settings
|
|
922
|
-
file].
|
|
923
|
-
- hostname: Location of the control server (IP address) [default
|
|
924
|
-
is taken from settings file].
|
|
925
|
-
- port: TCP port on which the Control Server is listening for
|
|
926
|
-
commands [default is taken from settings file].
|
|
927
|
-
"""
|
|
928
|
-
|
|
929
|
-
super().__init__(connect_address(protocol, hostname, port))
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
class AlertManagerProtocol(CommandProtocol):
|
|
934
|
-
"""
|
|
935
|
-
Command Protocol for Alert Management.
|
|
936
|
-
"""
|
|
937
|
-
def __init__(self, control_server: ControlServer, phase):
|
|
938
|
-
"""Initialisation of a new Protocol for Alert Management.
|
|
939
|
-
|
|
940
|
-
The initialisation of this Protocol consists of the following steps:
|
|
941
|
-
|
|
942
|
-
- create a Controller to which the given Control Server should send commands;
|
|
943
|
-
- load the commands;
|
|
944
|
-
- build a look-up table for the commands.
|
|
945
|
-
|
|
946
|
-
Args:
|
|
947
|
-
- control_server: Control Server via which commands should be sent
|
|
948
|
-
to the Controller.
|
|
949
|
-
"""
|
|
950
|
-
|
|
951
|
-
super().__init__()
|
|
952
|
-
|
|
953
|
-
# Control Server for Alert Management
|
|
954
|
-
|
|
955
|
-
self.control_server = control_server
|
|
956
|
-
|
|
957
|
-
# Create a new Controller for Alert Management
|
|
958
|
-
|
|
959
|
-
self.controller = AlertManagerController(phase)
|
|
960
|
-
|
|
961
|
-
# Load the commands (for commanding of the AM Controller) from the
|
|
962
|
-
# YAML file into a dictionary, stored in the AM Protocol
|
|
963
|
-
|
|
964
|
-
self.load_commands(
|
|
965
|
-
DEVICE_SETTINGS.Commands, AlertManagerCommand, AlertManagerController
|
|
966
|
-
)
|
|
967
|
-
|
|
968
|
-
# Build a look-up table for the methods
|
|
969
|
-
|
|
970
|
-
self.build_device_method_lookup_table(self.controller)
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
def get_bind_address(self):
|
|
974
|
-
"""Returns the address to bind a socket to.
|
|
975
|
-
|
|
976
|
-
This bind address is a properly formatted URL, based on the
|
|
977
|
-
communication protocol and the commanding port.
|
|
978
|
-
|
|
979
|
-
Returns:
|
|
980
|
-
- Properly formatted URL to bind a socket to.
|
|
981
|
-
"""
|
|
982
|
-
|
|
983
|
-
return bind_address(
|
|
984
|
-
self.control_server.get_communication_protocol(),
|
|
985
|
-
self.control_server.get_commanding_port(),
|
|
986
|
-
)
|
|
987
|
-
|
|
988
|
-
def get_status(self) -> dict:
|
|
989
|
-
"""Returns the status information for the Control Server.
|
|
990
|
-
|
|
991
|
-
This status information is returned in the form of a dictionary that
|
|
992
|
-
contains the following information about the Control Server for
|
|
993
|
-
Alert Management:
|
|
994
|
-
|
|
995
|
-
- timestamp (str): string representation of the current datetime;
|
|
996
|
-
- PID (int): process ID for the Control Server;
|
|
997
|
-
- Up (float): uptime of the Control Server [s];
|
|
998
|
-
- UUID (uuid1): Universally Unique Identifier for the Control
|
|
999
|
-
Server;
|
|
1000
|
-
- RSS (int): 'Resident Set Size', this is the non-swapped physical
|
|
1001
|
-
memory a process has used [byte];
|
|
1002
|
-
- USS (int): 'Unique Set Size', this is the memory which is unique
|
|
1003
|
-
to a process [byte];
|
|
1004
|
-
- CPU User (float): time spent in user mode [s];
|
|
1005
|
-
- CPU System (float): time spent in kernel mode [s];
|
|
1006
|
-
- CPU count: number of CPU cores in use by the process;
|
|
1007
|
-
- CPU% (float): process CPU utilization as a percentage [%].
|
|
1008
|
-
- Alert Status (dict): The status of all active alerts
|
|
1009
|
-
- Alert Phase (int): The enumerate value of the current phase
|
|
1010
|
-
|
|
1011
|
-
Returns:
|
|
1012
|
-
- Dictionary with status information for the Control Server for
|
|
1013
|
-
Alert Management.
|
|
1014
|
-
"""
|
|
1015
|
-
|
|
1016
|
-
status = super().get_status()
|
|
1017
|
-
|
|
1018
|
-
status['alert_status'] = self.controller.status()
|
|
1019
|
-
status['phase'] = self.controller.phase
|
|
1020
|
-
|
|
1021
|
-
return status
|
|
1022
|
-
|
|
1023
|
-
def get_housekeeping(self) -> dict:
|
|
1024
|
-
"""Returns the housekeeping data for the Control Server.
|
|
1025
|
-
|
|
1026
|
-
This housekeeping data is returns in the form of a dictionary that
|
|
1027
|
-
contains the following information about the Control Server for
|
|
1028
|
-
Alert Management:
|
|
1029
|
-
|
|
1030
|
-
- timestamp (str): string representation of the current datetime.
|
|
1031
|
-
|
|
1032
|
-
Returns:
|
|
1033
|
-
- Dictionary with housekeeping data for the Control Server for
|
|
1034
|
-
Alert Management.
|
|
1035
|
-
"""
|
|
1036
|
-
|
|
1037
|
-
return {"timestamp": format_datetime()}
|
|
1038
|
-
|
|
1039
|
-
def quit(self):
|
|
1040
|
-
self.controller.quit()
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
def main():
|
|
1045
|
-
|
|
1046
|
-
dev = AlertManagerController()
|
|
1047
|
-
|
|
1048
|
-
if __name__ == "__main__":
|
|
1049
|
-
main()
|