diff --git a/bec_lib/bec_lib/configs/demo_config.yaml b/bec_lib/bec_lib/configs/demo_config.yaml index c1eaba96b..fb28039d4 100644 --- a/bec_lib/bec_lib/configs/demo_config.yaml +++ b/bec_lib/bec_lib/configs/demo_config.yaml @@ -54,7 +54,7 @@ eyefoc: - -50 - 50 tolerance: 0.01 - update_frequency: 400 + update_frequency: 50 deviceTags: - user motors enabled: true @@ -68,7 +68,7 @@ eyex: - -50 - 50 tolerance: 0.01 - update_frequency: 400 + update_frequency: 50 deviceTags: - user motors enabled: true @@ -82,7 +82,7 @@ eyey: - -50 - 50 tolerance: 0.01 - update_frequency: 400 + update_frequency: 50 deviceTags: - user motors enabled: true @@ -93,7 +93,7 @@ flyer_sim: deviceConfig: delay: 1 device_access: true - update_frequency: 400 + update_frequency: 50 deviceTags: - flyer enabled: true @@ -107,7 +107,7 @@ hrox: - -50 - 50 tolerance: 0.01 - update_frequency: 400 + update_frequency: 50 deviceTags: - user motors enabled: true @@ -121,7 +121,7 @@ hroy: - -50 - 50 tolerance: 0.01 - update_frequency: 400 + update_frequency: 50 deviceTags: - user motors enabled: true @@ -135,7 +135,7 @@ hroz: - -50 - 50 tolerance: 0.01 - update_frequency: 400 + update_frequency: 50 deviceTags: - user motors enabled: true @@ -149,7 +149,7 @@ hx: - -50 - 50 tolerance: 0.01 - update_frequency: 400 + update_frequency: 50 deviceTags: - user motors enabled: true @@ -163,7 +163,7 @@ hy: - -50 - 50 tolerance: 0.01 - update_frequency: 400 + update_frequency: 50 deviceTags: - user motors enabled: true @@ -177,7 +177,7 @@ hz: - -50 - 50 tolerance: 0.01 - update_frequency: 400 + update_frequency: 50 deviceTags: - user motors enabled: true @@ -191,7 +191,7 @@ mbsx: - -50 - 50 tolerance: 0.01 - update_frequency: 400 + update_frequency: 50 deviceTags: - user motors enabled: true @@ -205,7 +205,7 @@ mbsy: - -50 - 50 tolerance: 0.01 - update_frequency: 400 + update_frequency: 50 deviceTags: - user motors enabled: true @@ -219,7 +219,7 @@ pinx: - -50 - 50 tolerance: 0.01 - update_frequency: 400 + update_frequency: 50 deviceTags: - user motors enabled: true @@ -233,7 +233,7 @@ piny: - -50 - 50 tolerance: 0.01 - update_frequency: 400 + update_frequency: 50 deviceTags: - user motors enabled: true @@ -247,7 +247,7 @@ pinz: - -50 - 50 tolerance: 0.01 - update_frequency: 400 + update_frequency: 50 deviceTags: - user motors enabled: true @@ -261,7 +261,7 @@ samx: - -50 - 50 tolerance: 0.01 - update_frequency: 400 + update_frequency: 50 deviceTags: - user motors enabled: true @@ -275,7 +275,7 @@ samy: - -50 - 50 tolerance: 0.01 - update_frequency: 400 + update_frequency: 50 deviceTags: - user motors enabled: true @@ -289,7 +289,7 @@ samz: - -50 - 50 tolerance: 0.01 - update_frequency: 400 + update_frequency: 50 deviceTags: - user motors enabled: true @@ -681,7 +681,7 @@ aptrx: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -691,7 +691,7 @@ aptry: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -701,7 +701,7 @@ bim2x: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -711,7 +711,7 @@ bim2y: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -721,7 +721,7 @@ bm1trx: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -731,7 +731,7 @@ bm1try: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -741,7 +741,7 @@ bm2trx: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -751,7 +751,7 @@ bm2try: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -761,7 +761,7 @@ bm3trx: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -771,7 +771,7 @@ bm3try: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -781,7 +781,7 @@ bm4trx: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -791,7 +791,7 @@ bm4try: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -801,7 +801,7 @@ bm5trx: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -811,7 +811,7 @@ bm5try: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -821,7 +821,7 @@ bm6trx: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -831,7 +831,7 @@ bm6try: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -841,7 +841,7 @@ bpm4r: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -851,7 +851,7 @@ bpm5r: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -861,7 +861,7 @@ bs1x: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -871,7 +871,7 @@ bs1y: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -881,7 +881,7 @@ bs2x: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -891,7 +891,7 @@ bs2y: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -901,7 +901,7 @@ burstn: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -911,7 +911,7 @@ burstr: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -921,7 +921,7 @@ ddg1a: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -931,7 +931,7 @@ ddg1b: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -941,7 +941,7 @@ ddg1c: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -951,7 +951,7 @@ ddg1d: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -961,7 +961,7 @@ ddg1e: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -971,7 +971,7 @@ ddg1f: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -981,7 +981,7 @@ ddg1g: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -991,7 +991,7 @@ ddg1h: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1001,7 +1001,7 @@ dettrx: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1011,7 +1011,7 @@ di2trx: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1021,7 +1021,7 @@ di2try: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1031,7 +1031,7 @@ dtpush: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1041,7 +1041,7 @@ dtth: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1051,7 +1051,7 @@ dttrx: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1061,7 +1061,7 @@ dttry: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1071,7 +1071,7 @@ dttrz: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1081,7 +1081,7 @@ ebcsx: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1091,7 +1091,7 @@ ebcsy: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1101,7 +1101,7 @@ ebfi1: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1111,7 +1111,7 @@ ebfi2: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1121,7 +1121,7 @@ ebfi3: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1131,7 +1131,7 @@ ebfi4: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1141,7 +1141,7 @@ ebfzpx: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1151,7 +1151,7 @@ ebfzpy: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1161,7 +1161,7 @@ ebtrx: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1171,7 +1171,7 @@ ebtry: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1181,7 +1181,7 @@ ebtrz: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1191,7 +1191,7 @@ fi1try: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1201,7 +1201,7 @@ fi2try: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1211,7 +1211,7 @@ fi3try: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1221,7 +1221,7 @@ fsh1x: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1231,7 +1231,7 @@ fsh2x: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1241,7 +1241,7 @@ ftrans: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1251,7 +1251,7 @@ fttrx1: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1261,7 +1261,7 @@ fttrx2: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1271,7 +1271,7 @@ fttry1: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1281,7 +1281,7 @@ fttry2: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1291,7 +1291,7 @@ fttrz: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1301,7 +1301,7 @@ idgap: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1311,7 +1311,7 @@ mibd: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1321,7 +1321,7 @@ mibd1: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1331,7 +1331,7 @@ mibd2: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1341,7 +1341,7 @@ miroll: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1351,7 +1351,7 @@ mith: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1361,7 +1361,7 @@ mitrx: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1371,7 +1371,7 @@ mitry: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1381,7 +1381,7 @@ mitry1: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1391,7 +1391,7 @@ mitry2: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1401,7 +1401,7 @@ mitry3: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1411,7 +1411,7 @@ mobd: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1421,7 +1421,7 @@ mobdai: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1431,7 +1431,7 @@ mobdbo: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1441,7 +1441,7 @@ mobdco: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1451,7 +1451,7 @@ mobddi: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1461,7 +1461,7 @@ mokev: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1471,7 +1471,7 @@ mopush1: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1481,7 +1481,7 @@ mopush2: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1491,7 +1491,7 @@ moroll1: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1501,7 +1501,7 @@ moroll2: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1511,7 +1511,7 @@ moth1: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1521,7 +1521,7 @@ moth1e: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1531,7 +1531,7 @@ moth2: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1541,7 +1541,7 @@ moth2e: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1551,7 +1551,7 @@ motrx2: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1561,7 +1561,7 @@ motry: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1571,7 +1571,7 @@ motry2: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1581,7 +1581,7 @@ motrz1: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1591,7 +1591,7 @@ motrz1e: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1601,7 +1601,7 @@ moyaw2: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1611,7 +1611,7 @@ sl0ch: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1621,7 +1621,7 @@ sl0trxi: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1631,7 +1631,7 @@ sl0trxo: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1641,7 +1641,7 @@ sl0wh: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1651,7 +1651,7 @@ sl1ch: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1661,7 +1661,7 @@ sl1cv: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1671,7 +1671,7 @@ sl1trxi: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1681,7 +1681,7 @@ sl1trxo: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1691,7 +1691,7 @@ sl1tryb: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1701,7 +1701,7 @@ sl1tryt: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1711,7 +1711,7 @@ sl1wh: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1721,7 +1721,7 @@ sl1wv: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1731,7 +1731,7 @@ sl2ch: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1741,7 +1741,7 @@ sl2cv: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1751,7 +1751,7 @@ sl2trxi: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1761,7 +1761,7 @@ sl2trxo: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1771,7 +1771,7 @@ sl2tryb: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1781,7 +1781,7 @@ sl2tryt: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1791,7 +1791,7 @@ sl2wh: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1801,7 +1801,7 @@ sl2wv: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1811,7 +1811,7 @@ sl3ch: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1821,7 +1821,7 @@ sl3cv: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1831,7 +1831,7 @@ sl3trxi: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1841,7 +1841,7 @@ sl3trxo: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1851,7 +1851,7 @@ sl3tryb: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1861,7 +1861,7 @@ sl3tryt: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1871,7 +1871,7 @@ sl3wh: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1881,7 +1881,7 @@ sl3wv: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1891,7 +1891,7 @@ sl4ch: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1901,7 +1901,7 @@ sl4cv: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1911,7 +1911,7 @@ sl4trxi: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1921,7 +1921,7 @@ sl4trxo: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1931,7 +1931,7 @@ sl4tryb: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1941,7 +1941,7 @@ sl4tryt: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1951,7 +1951,7 @@ sl4wh: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1961,7 +1961,7 @@ sl4wv: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1971,7 +1971,7 @@ sl5ch: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1981,7 +1981,7 @@ sl5cv: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -1991,7 +1991,7 @@ sl5trxi: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -2001,7 +2001,7 @@ sl5trxo: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -2011,7 +2011,7 @@ sl5tryb: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -2021,7 +2021,7 @@ sl5tryt: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -2031,7 +2031,7 @@ sl5wh: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -2041,7 +2041,7 @@ sl5wv: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -2051,7 +2051,7 @@ strox: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -2061,7 +2061,7 @@ stroy: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -2071,7 +2071,7 @@ stroz: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -2081,7 +2081,7 @@ sttrx: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -2091,7 +2091,7 @@ sttry: deviceClass: ophyd_devices.SimPositioner deviceConfig: delay: 1 - update_frequency: 400 + update_frequency: 50 deviceTags: - beamline enabled: true @@ -2138,7 +2138,7 @@ rt_controller: - -50 - 50 tolerance: 0.01 - update_frequency: 400 + update_frequency: 50 deviceTags: - user motors - test device diff --git a/bec_lib/bec_lib/tests/test_config.yaml b/bec_lib/bec_lib/tests/test_config.yaml index 7b5e73337..7056b215e 100644 --- a/bec_lib/bec_lib/tests/test_config.yaml +++ b/bec_lib/bec_lib/tests/test_config.yaml @@ -3,209 +3,209 @@ ############################################################ eiger: - readoutPriority: monitored - deviceClass: ophyd_devices.SimCamera - deviceConfig: - device_access: true - deviceTags: - - detector - enabled: true - readOnly: false - softwareTrigger: true + readoutPriority: monitored + deviceClass: ophyd_devices.SimCamera + deviceConfig: + device_access: true + deviceTags: + - detector + enabled: true + readOnly: false + softwareTrigger: true ############## Cameras and detectors end here ############## dyn_signals: - readoutPriority: baseline - deviceClass: ophyd_devices.sim.sim.SynDynamicComponents - deviceConfig: - enabled: true - readOnly: false + readoutPriority: baseline + deviceClass: ophyd_devices.sim.sim.SynDynamicComponents + deviceConfig: + enabled: true + readOnly: false pseudo_signal1: - deviceClass: ophyd_devices.ComputedSignal - deviceConfig: - compute_method: "def compute_signals(signal1, signal2): + deviceClass: ophyd_devices.ComputedSignal + deviceConfig: + compute_method: "def compute_signals(signal1, signal2): - \ return signal1.get()*signal2.get()\n" - input_signals: - - "bpm4i" - - "bpm5i" - enabled: true - readOnly: false - readoutPriority: baseline + \ return signal1.get()*signal2.get()\n" + input_signals: + - "bpm4i" + - "bpm5i" + enabled: true + readOnly: false + readoutPriority: baseline failure_device: - deviceClass: ophyd_devices.sim.SimPositionerWithCommFailure - deviceConfig: - delay: 1 - update_frequency: 400 - enabled: true - readOnly: false - readoutPriority: baseline + deviceClass: ophyd_devices.sim.SimPositionerWithCommFailure + deviceConfig: + delay: 1 + update_frequency: 50 + enabled: true + readOnly: false + readoutPriority: baseline bec_signals_device: - deviceClass: ophyd_devices.sim.sim_test_devices.SimCameraWithPSIComponents - deviceConfig: - enabled: true - readOnly: false - readoutPriority: baseline + deviceClass: ophyd_devices.sim.sim_test_devices.SimCameraWithPSIComponents + deviceConfig: + enabled: true + readOnly: false + readoutPriority: baseline ############################################################ ####################### User motors ######################## ############################################################ eyefoc: - readoutPriority: baseline - deviceClass: ophyd_devices.SimPositioner - deviceConfig: - delay: 1 - limits: - - -50 - - 50 - tolerance: 0.01 - update_frequency: 400 - deviceTags: - - user motors - enabled: true - readOnly: false + readoutPriority: baseline + deviceClass: ophyd_devices.SimPositioner + deviceConfig: + delay: 1 + limits: + - -50 + - 50 + tolerance: 0.01 + update_frequency: 50 + deviceTags: + - user motors + enabled: true + readOnly: false eyex: - readoutPriority: baseline - deviceClass: ophyd_devices.SimPositioner - deviceConfig: - delay: 1 - limits: - - -50 - - 50 - tolerance: 0.01 - update_frequency: 400 - deviceTags: - - user motors - enabled: true - readOnly: false + readoutPriority: baseline + deviceClass: ophyd_devices.SimPositioner + deviceConfig: + delay: 1 + limits: + - -50 + - 50 + tolerance: 0.01 + update_frequency: 50 + deviceTags: + - user motors + enabled: true + readOnly: false eyey: - readoutPriority: baseline - deviceClass: ophyd_devices.SimPositioner - deviceConfig: - delay: 1 - limits: - - -50 - - 50 - tolerance: 0.01 - update_frequency: 400 - deviceTags: - - user motors - enabled: true - readOnly: false + readoutPriority: baseline + deviceClass: ophyd_devices.SimPositioner + deviceConfig: + delay: 1 + limits: + - -50 + - 50 + tolerance: 0.01 + update_frequency: 50 + deviceTags: + - user motors + enabled: true + readOnly: false flyer_sim: - readoutPriority: on_request - deviceClass: ophyd_devices.SynFlyer - deviceConfig: - delay: 1 - device_access: true - update_frequency: 400 - deviceTags: - - flyer - enabled: true - readOnly: false + readoutPriority: on_request + deviceClass: ophyd_devices.SynFlyer + deviceConfig: + delay: 1 + device_access: true + update_frequency: 50 + deviceTags: + - flyer + enabled: true + readOnly: false hrox: - readoutPriority: baseline - deviceClass: ophyd_devices.SimPositioner - deviceConfig: - delay: 1 - limits: - - -50 - - 50 - tolerance: 0.01 - update_frequency: 400 - deviceTags: - - user motors - enabled: true - readOnly: false + readoutPriority: baseline + deviceClass: ophyd_devices.SimPositioner + deviceConfig: + delay: 1 + limits: + - -50 + - 50 + tolerance: 0.01 + update_frequency: 50 + deviceTags: + - user motors + enabled: true + readOnly: false hroy: - readoutPriority: baseline - deviceClass: ophyd_devices.SimPositioner - deviceConfig: - delay: 1 - limits: - - -50 - - 50 - tolerance: 0.01 - update_frequency: 400 - deviceTags: - - user motors - enabled: true - readOnly: false + readoutPriority: baseline + deviceClass: ophyd_devices.SimPositioner + deviceConfig: + delay: 1 + limits: + - -50 + - 50 + tolerance: 0.01 + update_frequency: 50 + deviceTags: + - user motors + enabled: true + readOnly: false hroz: - readoutPriority: baseline - deviceClass: ophyd_devices.SimPositioner - deviceConfig: - delay: 1 - limits: - - -50 - - 50 - tolerance: 0.01 - update_frequency: 400 - deviceTags: - - user motors - enabled: true - readOnly: false + readoutPriority: baseline + deviceClass: ophyd_devices.SimPositioner + deviceConfig: + delay: 1 + limits: + - -50 + - 50 + tolerance: 0.01 + update_frequency: 50 + deviceTags: + - user motors + enabled: true + readOnly: false pinz: - readoutPriority: baseline - deviceClass: ophyd_devices.SimPositioner - deviceConfig: - delay: 1 - limits: - - -50 - - 50 - tolerance: 0.01 - update_frequency: 400 - deviceTags: - - user motors - enabled: true - readOnly: false + readoutPriority: baseline + deviceClass: ophyd_devices.SimPositioner + deviceConfig: + delay: 1 + limits: + - -50 + - 50 + tolerance: 0.01 + update_frequency: 50 + deviceTags: + - user motors + enabled: true + readOnly: false samx: - readoutPriority: baseline - deviceClass: ophyd_devices.SimPositioner - deviceConfig: - delay: 1 - limits: - - -50 - - 50 - tolerance: 0.01 - update_frequency: 400 - deviceTags: - - user motors - enabled: true - readOnly: false + readoutPriority: baseline + deviceClass: ophyd_devices.SimPositioner + deviceConfig: + delay: 1 + limits: + - -50 + - 50 + tolerance: 0.01 + update_frequency: 50 + deviceTags: + - user motors + enabled: true + readOnly: false samy: - readoutPriority: baseline - deviceClass: ophyd_devices.SimPositioner - deviceConfig: - delay: 1 - limits: - - -50 - - 50 - tolerance: 0.01 - update_frequency: 400 - deviceTags: - - user motors - enabled: true - readOnly: false + readoutPriority: baseline + deviceClass: ophyd_devices.SimPositioner + deviceConfig: + delay: 1 + limits: + - -50 + - 50 + tolerance: 0.01 + update_frequency: 50 + deviceTags: + - user motors + enabled: true + readOnly: false samz: - readoutPriority: baseline - deviceClass: ophyd_devices.SimPositioner - deviceConfig: - delay: 1 - limits: - - -50 - - 50 - tolerance: 0.01 - update_frequency: 400 - deviceTags: - - user motors - enabled: true - readOnly: false + readoutPriority: baseline + deviceClass: ophyd_devices.SimPositioner + deviceConfig: + delay: 1 + limits: + - -50 + - 50 + tolerance: 0.01 + update_frequency: 50 + deviceTags: + - user motors + enabled: true + readOnly: false ################### User motors end here ################### @@ -214,101 +214,101 @@ samz: ############################################################ bpm3a: - readoutPriority: monitored - deviceClass: ophyd_devices.SimMonitor - deviceConfig: - deviceTags: - - beamline - enabled: true - readOnly: false + readoutPriority: monitored + deviceClass: ophyd_devices.SimMonitor + deviceConfig: + deviceTags: + - beamline + enabled: true + readOnly: false bpm3b: - readoutPriority: monitored - deviceClass: ophyd_devices.SimMonitor - deviceConfig: - deviceTags: - - beamline - enabled: true - readOnly: false + readoutPriority: monitored + deviceClass: ophyd_devices.SimMonitor + deviceConfig: + deviceTags: + - beamline + enabled: true + readOnly: false bpm3c: - readoutPriority: monitored - deviceClass: ophyd_devices.SimMonitor - deviceConfig: - deviceTags: - - beamline - enabled: true - readOnly: false + readoutPriority: monitored + deviceClass: ophyd_devices.SimMonitor + deviceConfig: + deviceTags: + - beamline + enabled: true + readOnly: false bpm3d: - readoutPriority: monitored - deviceClass: ophyd_devices.SimMonitor - deviceConfig: - deviceTags: - - beamline - enabled: true - readOnly: false + readoutPriority: monitored + deviceClass: ophyd_devices.SimMonitor + deviceConfig: + deviceTags: + - beamline + enabled: true + readOnly: false bpm3i: - readoutPriority: monitored - deviceClass: ophyd_devices.SimMonitor - deviceConfig: - deviceTags: - - beamline - enabled: true - readOnly: false + readoutPriority: monitored + deviceClass: ophyd_devices.SimMonitor + deviceConfig: + deviceTags: + - beamline + enabled: true + readOnly: false bpm4i: - readoutPriority: monitored - deviceClass: ophyd_devices.SimMonitor - deviceConfig: - deviceTags: - - beamline - enabled: true - readOnly: false + readoutPriority: monitored + deviceClass: ophyd_devices.SimMonitor + deviceConfig: + deviceTags: + - beamline + enabled: true + readOnly: false bpm5i: - readoutPriority: monitored - deviceClass: ophyd_devices.SimMonitor - deviceConfig: - deviceTags: - - beamline - enabled: true - readOnly: false + readoutPriority: monitored + deviceClass: ophyd_devices.SimMonitor + deviceConfig: + deviceTags: + - beamline + enabled: true + readOnly: false bpm6i: - readoutPriority: monitored - deviceClass: ophyd_devices.SimMonitor - deviceConfig: - deviceTags: - - beamline - enabled: true - readOnly: false + readoutPriority: monitored + deviceClass: ophyd_devices.SimMonitor + deviceConfig: + deviceTags: + - beamline + enabled: true + readOnly: false temp: - readoutPriority: monitored - deviceClass: ophyd_devices.SimMonitor - deviceConfig: - deviceTags: - - beamline - enabled: true - readOnly: false + readoutPriority: monitored + deviceClass: ophyd_devices.SimMonitor + deviceConfig: + deviceTags: + - beamline + enabled: true + readOnly: false transd: - readoutPriority: monitored - deviceClass: ophyd_devices.SimMonitor - deviceConfig: - deviceTags: - - beamline - enabled: true - readOnly: false + readoutPriority: monitored + deviceClass: ophyd_devices.SimMonitor + deviceConfig: + deviceTags: + - beamline + enabled: true + readOnly: false monitor_async: - readoutPriority: async - deviceClass: ophyd_devices.sim.sim_monitor.SimMonitorAsync - deviceConfig: - sim_init: - model: GaussianModel - params: - amplitude: 500 - center: 0 - sigma: 1 - deviceTags: - - beamline - enabled: true - readOnly: false - softwareTrigger: true + readoutPriority: async + deviceClass: ophyd_devices.sim.sim_monitor.SimMonitorAsync + deviceConfig: + sim_init: + model: GaussianModel + params: + amplitude: 500 + center: 0 + sigma: 1 + deviceTags: + - beamline + enabled: true + readOnly: false + softwareTrigger: true ################ Beamline monitors end here ################ @@ -317,13 +317,13 @@ monitor_async: ############################################################ ring_current_sim: - readoutPriority: monitored - deviceClass: ophyd_devices.ReadOnlySignal - deviceConfig: - deviceTags: - - beamline - enabled: true - readOnly: false + readoutPriority: monitored + deviceClass: ophyd_devices.ReadOnlySignal + deviceConfig: + deviceTags: + - beamline + enabled: true + readOnly: false ################ Read-only signals end here ################ ############################################################ @@ -331,114 +331,114 @@ ring_current_sim: ############################################################ motor1_disabled: - readoutPriority: monitored - deviceClass: ophyd_devices.SimPositioner - deviceConfig: - delay: 1 - labels: motor1_disabled - limits: - - -50 - - 50 - name: motor1_disabled - tolerance: 0.01 - update_frequency: 400 - deviceTags: - - user motors - enabled: false - readOnly: false + readoutPriority: monitored + deviceClass: ophyd_devices.SimPositioner + deviceConfig: + delay: 1 + labels: motor1_disabled + limits: + - -50 + - 50 + name: motor1_disabled + tolerance: 0.01 + update_frequency: 50 + deviceTags: + - user motors + enabled: false + readOnly: false motor1_disabled_set: - readoutPriority: baseline - deviceClass: ophyd_devices.SimPositioner - deviceConfig: - delay: 1 - labels: motor1_disabled_set - limits: - - -50 - - 50 - name: motor1_disabled_set - tolerance: 0.01 - update_frequency: 400 - deviceTags: - - user motors - enabled: true - readOnly: true + readoutPriority: baseline + deviceClass: ophyd_devices.SimPositioner + deviceConfig: + delay: 1 + labels: motor1_disabled_set + limits: + - -50 + - 50 + name: motor1_disabled_set + tolerance: 0.01 + update_frequency: 50 + deviceTags: + - user motors + enabled: true + readOnly: true motor2_disabled: - readoutPriority: baseline - deviceClass: ophyd_devices.SimPositioner - deviceConfig: - delay: 1 - labels: motor2_disabled - limits: - - -50 - - 50 - name: motor2_disabled - tolerance: 0.01 - update_frequency: 400 - deviceTags: - - user motors - enabled: false - readOnly: false + readoutPriority: baseline + deviceClass: ophyd_devices.SimPositioner + deviceConfig: + delay: 1 + labels: motor2_disabled + limits: + - -50 + - 50 + name: motor2_disabled + tolerance: 0.01 + update_frequency: 50 + deviceTags: + - user motors + enabled: false + readOnly: false motor2_disabled_set: - readoutPriority: baseline - deviceClass: ophyd_devices.SimPositioner - deviceConfig: - delay: 1 - labels: motor2_disabled_set - limits: - - -50 - - 50 - name: motor2_disabled_set - tolerance: 0.01 - update_frequency: 400 - deviceTags: - - user motors - enabled: true - readOnly: true + readoutPriority: baseline + deviceClass: ophyd_devices.SimPositioner + deviceConfig: + delay: 1 + labels: motor2_disabled_set + limits: + - -50 + - 50 + name: motor2_disabled_set + tolerance: 0.01 + update_frequency: 50 + deviceTags: + - user motors + enabled: true + readOnly: true ######### DeviceProxy and SimCamera for delayed init tests ########## proxy_cam_test: - readoutPriority: monitored - deviceClass: ophyd_devices.SimCamera - deviceConfig: - device_access: true - deviceTags: - - detector - enabled: true - readOnly: false - softwareTrigger: false + readoutPriority: monitored + deviceClass: ophyd_devices.SimCamera + deviceConfig: + device_access: true + deviceTags: + - detector + enabled: true + readOnly: false + softwareTrigger: false sim_proxy_test: - readoutPriority: on_request - deviceClass: ophyd_devices.SlitProxy - deviceConfig: - proxy_cam_test: - signal_name: image - center_offset: [0, 0] # [x,y] - covariance: [[1000, 500], [200, 1000]] # [[x,x],[y,y]] - pixel_size: 0.01 - ref_motors: [samx, samy] - slit_width: [1, 1] - motor_dir: [0, 1] # x:0 , y:1, z:2 coordinates - enabled: true - readOnly: false + readoutPriority: on_request + deviceClass: ophyd_devices.SlitProxy + deviceConfig: + proxy_cam_test: + signal_name: image + center_offset: [0, 0] # [x,y] + covariance: [[1000, 500], [200, 1000]] # [[x,x],[y,y]] + pixel_size: 0.01 + ref_motors: [samx, samy] + slit_width: [1, 1] + motor_dir: [0, 1] # x:0 , y:1, z:2 coordinates + enabled: true + readOnly: false rt_controller: - readoutPriority: baseline - deviceClass: ophyd_devices.sim.SimPositionerWithController - deviceConfig: - delay: 1 - limits: - - -50 - - 50 - tolerance: 0.01 - update_frequency: 400 - deviceTags: - - user motors - - test device - enabled: true - readOnly: false + readoutPriority: baseline + deviceClass: ophyd_devices.sim.SimPositionerWithController + deviceConfig: + delay: 1 + limits: + - -50 + - 50 + tolerance: 0.01 + update_frequency: 50 + deviceTags: + - user motors + - test device + enabled: true + readOnly: false device_with_not_resolving_status: - readoutPriority: baseline - deviceClass: ophyd_devices.sim.sim_test_devices.SimDeviceWithStatusStageUnstage - deviceConfig: - deviceTags: - - test device - enabled: false - readOnly: false + readoutPriority: baseline + deviceClass: ophyd_devices.sim.sim_test_devices.SimDeviceWithStatusStageUnstage + deviceConfig: + deviceTags: + - test device + enabled: false + readOnly: false diff --git a/bec_server/bec_server/device_server/devices/devicemanager.py b/bec_server/bec_server/device_server/devices/devicemanager.py index 8857aaa7c..692cedf0d 100644 --- a/bec_server/bec_server/device_server/devices/devicemanager.py +++ b/bec_server/bec_server/device_server/devices/devicemanager.py @@ -36,6 +36,9 @@ disable_lazy_wait_for_connection, get_device_info, ) +from bec_server.device_server.devices.rate_limited_pipeline_publisher import ( + RateLimitedPipelinePublisher, +) if TYPE_CHECKING: # pragma: no cover from bec_lib.redis_connector import RedisConnector @@ -148,6 +151,10 @@ def __init__( self.failed_devices = {} self._bec_message_handler = BECMessageHandler(self) self._device_order_map = {} + self._device_event_rate_limit_s = 0.01 + self._rate_limited_publisher = RateLimitedPipelinePublisher( + connector_getter=lambda: self.connector, rate_limit_s=self._device_event_rate_limit_s + ) def initialize(self, bootstrap_server) -> None: self.config_update_handler = ( @@ -747,25 +754,26 @@ def _obj_callback_limit_change(self, *_args, obj: OphydObject, **kwargs): if not obj.connected: return name = obj.root.name - limits = { - "low": {"value": obj.root.low_limit_travel.get()}, - "high": {"value": obj.root.high_limit_travel.get()}, - } - dev_msg = messages.DeviceMessage(signals=limits) - pipe = self.connector.pipeline() - self.connector.set_and_publish(MessageEndpoints.device_limits(name), dev_msg, pipe=pipe) - pipe.execute() + self._rate_limited_publisher.publish_set_and_publish( + MessageEndpoints.device_limits(name), + lambda root=obj.root: messages.DeviceMessage( + signals={ + "low": {"value": root.low_limit_travel.get()}, + "high": {"value": root.high_limit_travel.get()}, + } + ), + ) def _obj_callback_readback(self, *_args, obj: OphydObject, **kwargs): if not obj.connected: return name = obj.root.name - signals = obj.root.read() - metadata = self.devices.get(obj.root.name).metadata - dev_msg = messages.DeviceMessage(signals=signals, metadata=metadata) - pipe = self.connector.pipeline() - self.connector.set_and_publish(MessageEndpoints.device_readback(name), dev_msg, pipe) - pipe.execute() + self._rate_limited_publisher.publish_set_and_publish( + MessageEndpoints.device_readback(name), + lambda root=obj.root, device_name=name: messages.DeviceMessage( + signals=root.read(), metadata=dict(self.devices.get(device_name).metadata) + ), + ) def _obj_callback_configuration(self, *_args, obj: OphydObject, **kwargs): if not obj.connected: @@ -774,14 +782,13 @@ def _obj_callback_configuration(self, *_args, obj: OphydObject, **kwargs): # we don't need to publish the configuration of a signal return name = obj.root.name - signals = obj.root.read_configuration() - metadata = self.devices.get(obj.root.name).metadata - dev_msg = messages.DeviceMessage(signals=signals, metadata=metadata) - pipe = self.connector.pipeline() - self.connector.set_and_publish( - MessageEndpoints.device_read_configuration(name), dev_msg, pipe + self._rate_limited_publisher.publish_set_and_publish( + MessageEndpoints.device_read_configuration(name), + lambda root=obj.root, device_name=name: messages.DeviceMessage( + signals=root.read_configuration(), + metadata=dict(self.devices.get(device_name).metadata), + ), ) - pipe.execute() @typechecked def _obj_callback_device_monitor_2d( @@ -864,11 +871,11 @@ def _obj_callback_device_monitor_1d( def _obj_callback_acq_done(self, *_args, **kwargs): device = kwargs["obj"].root.name - status = 0 - metadata = self.devices[device].metadata - self.connector.set( + self._rate_limited_publisher.publish_set( MessageEndpoints.device_status(device), - messages.DeviceStatusMessage(device=device, status=status, metadata=metadata), + lambda device_name=device: messages.DeviceStatusMessage( + device=device_name, status=0, metadata=dict(self.devices[device_name].metadata) + ), ) def _obj_callback_done_moving(self, *args, **kwargs): @@ -877,11 +884,13 @@ def _obj_callback_done_moving(self, *args, **kwargs): def _obj_callback_is_moving(self, *_args, **kwargs): device = kwargs["obj"].root.name - status = int(kwargs.get("value")) - metadata = self.devices[device].metadata - self.connector.set( + self._rate_limited_publisher.publish_set( MessageEndpoints.device_status(device), - messages.DeviceStatusMessage(device=device, status=status, metadata=metadata), + lambda device_name=device, status=int( + kwargs.get("value") + ): messages.DeviceStatusMessage( + device=device_name, status=status, metadata=dict(self.devices[device_name].metadata) + ), ) def _obj_flyer_callback(self, *_args, **kwargs): @@ -928,10 +937,15 @@ def _obj_callback_progress(self, *_args, obj, value, max_value, done, **kwargs): Callback for progress events. Sends the data to redis. """ metadata = self.devices[obj.root.name].metadata - msg = messages.ProgressMessage( - value=value, max_value=max_value, done=done, metadata=metadata + self._rate_limited_publisher.publish_set_and_publish( + MessageEndpoints.device_progress(obj.root.name), + lambda device_name=obj.root.name, value=value, max_value=max_value, done=done: messages.ProgressMessage( + value=value, + max_value=max_value, + done=done, + metadata=dict(self.devices[device_name].metadata), + ), ) - self.connector.set_and_publish(MessageEndpoints.device_progress(obj.root.name), msg) def _obj_callback_file_event( self, @@ -999,6 +1013,7 @@ def _obj_callback_bec_message_signal( def shutdown(self): """Shutdown the device manager and disconnect all devices""" + self._rate_limited_publisher.shutdown() for device in self.devices.values(): try: logger.info(f"Disconnecting device {device.name}") diff --git a/bec_server/bec_server/device_server/devices/rate_limited_pipeline_publisher.py b/bec_server/bec_server/device_server/devices/rate_limited_pipeline_publisher.py new file mode 100644 index 000000000..1d4c66fc6 --- /dev/null +++ b/bec_server/bec_server/device_server/devices/rate_limited_pipeline_publisher.py @@ -0,0 +1,323 @@ +from __future__ import annotations + +import enum +import threading +import time +from collections import deque +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, Any, Callable, TypeAlias + +from bec_lib import messages +from bec_lib.alarm_handler import Alarms +from bec_lib.logger import bec_logger + +if TYPE_CHECKING: # pragma: no cover + from redis.client import Pipeline + + from bec_lib.endpoints import EndpointInfo + from bec_lib.redis_connector import RedisConnector + +logger = bec_logger.logger + +RateLimitKey: TypeAlias = tuple[str, str] +MessageBuilder: TypeAlias = Callable[[], messages.BECMessage] + + +class _PublishMethod(str, enum.Enum): + SET = "set" + SET_AND_PUBLISH = "set_and_publish" + + +@dataclass +class _PendingPublish: + """Only the latest pending message per key is retained until the next shared flush.""" + + method: _PublishMethod + topic: EndpointInfo[Any] + builder: MessageBuilder + + +@dataclass +class _TopicState: + pending: _PendingPublish | None = None + inserted_in_cooldown: int = 0 + alarm_sent: float | None = None + recent_insert_counts: deque[int] = field(default_factory=deque) + + +class RateLimitedPipelinePublisher: + """Rate-limited Redis pipeline writes with immediate first publish per key.""" + + def __init__( + self, + connector_getter: Callable[[], RedisConnector], + rate_limit_s: float = 0.1, + high_update_rate_warning_hz: float = 500, + high_update_rate_warning_cycles: int = 3, + high_update_rate_warning_period_s: float = 30, + ) -> None: + """ + Create a shared-cadence coalescing publisher. + + Args: + connector_getter (Callable[[], RedisConnector]): Callback returning the + Redis connector used for publishing. We use a getter instead of directly + taking the connector to avoid issues with connector lifecycle. + rate_limit_s (float): Shared flush interval in seconds for pending updates. + high_update_rate_warning_hz (float): Threshold update rate in Hz for + triggering a warning alarm when updates are being rate-limited. Set to + 0 or below to disable the warning. + high_update_rate_warning_cycles (int): Number of cooldown windows to include + in the rolling average used for high-rate warnings. Values greater than 1 + suppress single-window spikes and only warn on sustained high rates. + high_update_rate_warning_period_s (float): Minimum time in seconds between + repeated high-rate alarms for the same rate-limited topic. + + """ + self._connector_getter = connector_getter + self.rate_limit_s = rate_limit_s + self._high_update_rate_warning_hz = high_update_rate_warning_hz + self._high_update_rate_warning_cycles = max(1, high_update_rate_warning_cycles) + self._high_update_rate_warning_period_s = high_update_rate_warning_period_s + self._lock = threading.Lock() + self._states: dict[RateLimitKey, _TopicState] = {} + self._ready: deque[_PendingPublish] = deque() + self._next_flush_at: float | None = None + self._evict_next_cycle: set[RateLimitKey] = set() + self._pending_event = threading.Event() + self._stop_event = threading.Event() + self._thread = threading.Thread( + target=self._dispatch_pending, name="device-event-rate-limiter", daemon=True + ) + self._thread.start() + + def publish_set_and_publish(self, topic: EndpointInfo[Any], builder: MessageBuilder) -> None: + """ + Queue a rate-limited `set_and_publish` operation. + + Args: + topic (EndpointInfo[Any]): Redis endpoint to update. + builder (MessageBuilder): Callback that builds the message at flush + time. + """ + self._publish_rate_limited( + (_PublishMethod.SET_AND_PUBLISH.value, topic.endpoint), + _PublishMethod.SET_AND_PUBLISH, + topic, + builder, + ) + + def publish_set(self, topic: EndpointInfo[Any], builder: MessageBuilder) -> None: + """ + Queue a rate-limited `set` operation. + + Args: + topic (EndpointInfo[Any]): Redis endpoint to update. + builder (MessageBuilder): Callback that builds the message at flush + time. + """ + self._publish_rate_limited( + (_PublishMethod.SET.value, topic.endpoint), _PublishMethod.SET, topic, builder + ) + + def shutdown(self) -> None: + """Stop the worker thread and flush any queued requests.""" + self._stop_event.set() + self._pending_event.set() + if self._thread.is_alive(): + self._thread.join(timeout=1) + + ################################################################################### + ################# Internal helper methods ######################################### + ################################################################################### + + def _dispatch_pending(self) -> None: + """ + Run the worker loop that flushes pending requests when they become due. + The loop is executed in a separate thread. + """ + while True: + pending_requests = self._collect_due_pending() + if pending_requests: + self._flush_requests(pending_requests) + continue + + if self._stop_event.is_set(): + return + + timeout = self._next_timeout() + self._pending_event.wait(timeout=timeout) + self._pending_event.clear() + + def _next_timeout(self) -> float | None: + """ + Return the wait time until the next shared flush. + + Returns: + float | None: Seconds until the next pending flush, or `None` if + nothing is pending. + """ + now = time.monotonic() + with self._lock: + if self._next_flush_at is None: + return None + return max(0.0, self._next_flush_at - now) + + def _collect_due_pending(self) -> list[_PendingPublish]: + """ + Collect requests that are ready to be flushed. + We also check if states did not receive updates during the cooldown + and evict them after the next flush to prevent unbounded memory growth. + + Returns: + list[_PendingPublish]: Ready-to-dispatch requests collected from the + immediate queue and the shared pending set. + """ + now = time.monotonic() + ready: list[_PendingPublish] = [] + with self._lock: + while self._ready: + ready.append(self._ready.popleft()) + if self._next_flush_at is None or self._next_flush_at > now: + return ready + + keys_to_evict = self._evict_next_cycle + self._evict_next_cycle = set() + flushed_keys: set[RateLimitKey] = set() + for key, state in self._states.items(): + if state.pending is None: + continue + ready.append(state.pending) + state.pending = None + self._start_cooldown_window_locked(key, state) + flushed_keys.add(key) + self._next_flush_at = None + for key in keys_to_evict: + if key in self._states and self._states[key].pending is None: + del self._states[key] + self._evict_next_cycle = flushed_keys + return ready + + def _dispatch_request( + self, connector: RedisConnector, pipe: Pipeline, request: _PendingPublish + ) -> None: + """Queue a single request onto the Redis pipeline. + + Args: + connector (RedisConnector): Redis connector used for publishing. + pipe (Pipeline): Redis pipeline that accumulates the write. + request (_PendingPublish): Pending request to enqueue. + + Raises: + ValueError: Raised if the publish method is unsupported. + """ + msg = request.builder() + if request.method is _PublishMethod.SET_AND_PUBLISH: + connector.set_and_publish(request.topic, msg, pipe=pipe) + return + if request.method is _PublishMethod.SET: + connector.set(request.topic, msg, pipe=pipe) + return + raise ValueError(f"Unsupported rate-limited publish method {request.method}") + + def _start_cooldown_window_locked(self, key: RateLimitKey, state: _TopicState) -> None: + """Reset per-key counters for a new cooldown window and warn on sustained high rates.""" + state.recent_insert_counts.append(state.inserted_in_cooldown) + state.inserted_in_cooldown = 0 + if self.rate_limit_s <= 0 or self._high_update_rate_warning_hz <= 0: + return + + rolling_counts = list(state.recent_insert_counts) + if len(rolling_counts) < self._high_update_rate_warning_cycles: + return + + inserted_rate_hz = sum(rolling_counts) / (len(rolling_counts) * self.rate_limit_s) + if inserted_rate_hz <= self._high_update_rate_warning_hz: + return + + now = time.monotonic() + if ( + state.alarm_sent is not None + and now - state.alarm_sent < self._high_update_rate_warning_period_s + ): + return + + state.alarm_sent = now + + self._connector_getter().raise_alarm( + severity=Alarms.WARNING, + info=messages.ErrorInfo( + error_message=( + f"Rate-limited topic {key[1]} averaged {sum(rolling_counts) / len(rolling_counts):.1f} " + f"updates per {self.rate_limit_s:.3f}s cooldown window across the last " + f"{len(rolling_counts)} windows (~{inserted_rate_hz:.1f} Hz)." + ), + compact_error_message=( + f"High update rate on {key[1]}: rolling average ~{inserted_rate_hz:.1f} Hz exceeds " + f"{self._high_update_rate_warning_hz:.1f} Hz." + ), + exception_type="Warning", + device=key[1], + ), + ) + + def _flush_requests(self, requests: list[_PendingPublish | None]) -> None: + """Flush a batch of queued requests through a single Redis pipeline. + + Args: + requests (list[_PendingPublish | None]): Requests to flush. `None` + entries are ignored. + """ + if not requests: + return + + connector = self._connector_getter() + pipe = connector.pipeline() + for request in requests: + if request is None: + continue + try: + self._dispatch_request(connector, pipe, request) + except Exception: + logger.exception("Failed to build or queue rate-limited device event callback") + + try: + pipe.execute() + except Exception: + logger.exception("Failed to flush rate-limited device event pipeline") + + def _publish_rate_limited( + self, + key: RateLimitKey, + method: _PublishMethod, + topic: EndpointInfo[Any], + builder: MessageBuilder, + ) -> None: + """Queue a request, collapsing updates received during the cooldown. + + Args: + key (RateLimitKey): Rate-limit bucket key derived from operation type and topic. + method (_PublishMethod): Redis operation to perform. + topic (EndpointInfo[Any]): Redis endpoint. + builder (MessageBuilder): Callback that builds the message at flush time. + """ + now = time.monotonic() + + with self._lock: + state = self._states.get(key) + if state is None: + state = _TopicState( + recent_insert_counts=deque(maxlen=self._high_update_rate_warning_cycles) + ) + self._states[key] = state + state.inserted_in_cooldown += 1 + state.pending = None + self._evict_next_cycle.discard(key) + self._ready.append(_PendingPublish(method=method, topic=topic, builder=builder)) + else: + state.inserted_in_cooldown += 1 + state.pending = _PendingPublish(method=method, topic=topic, builder=builder) + self._evict_next_cycle.discard(key) + if self._next_flush_at is None: + self._next_flush_at = now + self.rate_limit_s + self._pending_event.set() diff --git a/bec_server/tests/tests_device_server/test_device_manager_ds.py b/bec_server/tests/tests_device_server/test_device_manager_ds.py index 01ec78df0..8d1268271 100644 --- a/bec_server/tests/tests_device_server/test_device_manager_ds.py +++ b/bec_server/tests/tests_device_server/test_device_manager_ds.py @@ -168,14 +168,39 @@ def test_obj_callback_progress(dm_with_devices): samx = device_manager.devices.samx samx.metadata = {"scan_id": "12345"} - with mock.patch.object(device_manager, "connector") as mock_connector: + with mock.patch.object( + device_manager._rate_limited_publisher, "publish_set_and_publish" + ) as publish: device_manager._obj_callback_progress(obj=samx.obj, value=1, max_value=2, done=False) - mock_connector.set_and_publish.assert_called_once_with( - MessageEndpoints.device_progress("samx"), - messages.ProgressMessage( - value=1, max_value=2, done=False, metadata={"scan_id": "12345"} - ), - ) + assert publish.call_count == 1 + assert publish.call_args.args[0] == MessageEndpoints.device_progress("samx") + + +@pytest.mark.parametrize("device_manager_class", [DeviceManagerDS]) +def test_obj_callback_file_event(dm_with_devices, connected_connector): + device_manager = dm_with_devices + eiger = device_manager.devices.eiger + eiger.metadata = {"scan_id": "12345"} + # Use here fake redis connector, pipe is used and checks pydantic models + device_manager.connector = connected_connector + device_manager._obj_callback_file_event( + obj=eiger.obj, + file_path="test_file_path", + done=True, + successful=True, + hinted_h5_entries={"my_entry": "entry/data/data"}, + metadata={"user_info": "my_info"}, + ) + msg = connected_connector.get(MessageEndpoints.file_event(name="eiger")) + msg2 = connected_connector.get(MessageEndpoints.public_file(scan_id="12345", name="eiger")) + assert msg == msg2 + assert msg.content["file_path"] == "test_file_path" + assert msg.content["done"] is True + assert msg.content["successful"] is True + assert msg.content["hinted_h5_entries"] == {"my_entry": "entry/data/data"} + assert msg.content["file_type"] == "h5" + assert msg.metadata == {"scan_id": "12345", "user_info": "my_info"} + assert msg.content["is_master_file"] is False @pytest.mark.parametrize( @@ -223,33 +248,6 @@ def test_device_manager_ds_reset_config(dm_with_devices): ) -@pytest.mark.parametrize("device_manager_class", [DeviceManagerDS]) -def test_obj_callback_file_event(dm_with_devices, connected_connector): - device_manager = dm_with_devices - eiger = device_manager.devices.eiger - eiger.metadata = {"scan_id": "12345"} - # Use here fake redis connector, pipe is used and checks pydantic models - device_manager.connector = connected_connector - device_manager._obj_callback_file_event( - obj=eiger.obj, - file_path="test_file_path", - done=True, - successful=True, - hinted_h5_entries={"my_entry": "entry/data/data"}, - metadata={"user_info": "my_info"}, - ) - msg = connected_connector.get(MessageEndpoints.file_event(name="eiger")) - msg2 = connected_connector.get(MessageEndpoints.public_file(scan_id="12345", name="eiger")) - assert msg == msg2 - assert msg.content["file_path"] == "test_file_path" - assert msg.content["done"] is True - assert msg.content["successful"] is True - assert msg.content["hinted_h5_entries"] == {"my_entry": "entry/data/data"} - assert msg.content["file_type"] == "h5" - assert msg.metadata == {"scan_id": "12345", "user_info": "my_info"} - assert msg.content["is_master_file"] is False - - @pytest.mark.parametrize("device_manager_class", [DeviceManagerDS]) def test_subscribe_to_device_events(dm_with_devices): opaas_obj = mock.MagicMock() diff --git a/bec_server/tests/tests_device_server/test_rate_limited_pipeline_publisher.py b/bec_server/tests/tests_device_server/test_rate_limited_pipeline_publisher.py new file mode 100644 index 000000000..ab662edfe --- /dev/null +++ b/bec_server/tests/tests_device_server/test_rate_limited_pipeline_publisher.py @@ -0,0 +1,242 @@ +import time +from collections import deque +from unittest import mock + +import pytest + +from bec_lib import messages +from bec_lib.alarm_handler import Alarms +from bec_lib.endpoints import MessageEndpoints +from bec_lib.tests.utils import ConnectorMock +from bec_server.device_server.devices.rate_limited_pipeline_publisher import ( + RateLimitedPipelinePublisher, + _PublishMethod, + _TopicState, +) + + +@pytest.fixture +def publisher(): + connector = ConnectorMock("") + publisher = RateLimitedPipelinePublisher(connector_getter=lambda: connector, rate_limit_s=0.1) + try: + yield publisher, connector + finally: + publisher.shutdown() + + +def test_rate_limited_publish_flushes_latest_same_topic(publisher): + publisher, connector = publisher + publisher.rate_limit_s = 0.05 + connector.message_sent.clear() + + first = messages.ProgressMessage(value=1, max_value=10, done=False, metadata={"seq": 1}) + second = messages.ProgressMessage(value=2, max_value=10, done=False, metadata={"seq": 2}) + third = messages.ProgressMessage(value=2, max_value=10, done=False, metadata={"seq": 2}) + + publisher.publish_set_and_publish(MessageEndpoints.device_progress("samx"), lambda: first) + publisher.publish_set_and_publish(MessageEndpoints.device_progress("samx"), lambda: second) + publisher.publish_set_and_publish(MessageEndpoints.device_progress("samx"), lambda: third) + + deadline = time.time() + 1 + progress_msgs = [] + while time.time() < deadline: + progress_msgs = [ + msg + for msg in connector.message_sent + if msg["queue"] == MessageEndpoints.device_progress("samx").endpoint + ] + if len(progress_msgs) >= 2: + break + time.sleep(0.01) + + assert progress_msgs == [ + {"queue": MessageEndpoints.device_progress("samx").endpoint, "msg": first, "expire": None}, + {"queue": MessageEndpoints.device_progress("samx").endpoint, "msg": third, "expire": None}, + ] + + +def test_rate_limited_publish_is_per_topic(publisher): + publisher, connector = publisher + publisher.rate_limit_s = 0.5 + connector.message_sent.clear() + + publisher.publish_set_and_publish( + MessageEndpoints.device_progress("samx"), + lambda: messages.ProgressMessage(value=1, max_value=2, done=False, metadata={}), + ) + publisher.publish_set_and_publish( + MessageEndpoints.device_progress("samy"), + lambda: messages.ProgressMessage(value=2, max_value=2, done=False, metadata={}), + ) + + deadline = time.time() + 1 + progress_msgs = [] + while time.time() < deadline: + progress_msgs = [ + msg + for msg in connector.message_sent + if msg["queue"] + in ( + MessageEndpoints.device_progress("samx").endpoint, + MessageEndpoints.device_progress("samy").endpoint, + ) + ] + if len(progress_msgs) >= 2: + break + time.sleep(0.01) + + assert progress_msgs == [ + { + "queue": MessageEndpoints.device_progress("samx").endpoint, + "msg": messages.ProgressMessage(value=1, max_value=2, done=False, metadata={}), + "expire": None, + }, + { + "queue": MessageEndpoints.device_progress("samy").endpoint, + "msg": messages.ProgressMessage(value=2, max_value=2, done=False, metadata={}), + "expire": None, + }, + ] + + +def test_rate_limited_publish_warns_when_input_rate_exceeds_threshold(publisher): + publisher, connector = publisher + publisher.rate_limit_s = 10 + publisher._high_update_rate_warning_hz = 2 + publisher._high_update_rate_warning_cycles = 1 + + key = (_PublishMethod.SET_AND_PUBLISH.value, MessageEndpoints.device_progress("samx").endpoint) + state = _TopicState( + recent_insert_counts=deque(maxlen=publisher._high_update_rate_warning_cycles) + ) + + with mock.patch.object(connector, "raise_alarm") as raise_alarm: + for _ in range(21): + state.inserted_in_cooldown += 1 + publisher._start_cooldown_window_locked(key, state) + + raise_alarm.assert_called_once() + assert raise_alarm.call_args.kwargs["severity"] == Alarms.WARNING + info = raise_alarm.call_args.kwargs["info"] + assert info.device == MessageEndpoints.device_progress("samx").endpoint + assert ( + "averaged 21.0 updates per 10.000s cooldown window across the last 1 windows" + in info.error_message + ) + assert "rolling average ~2.1 Hz exceeds 2.0 Hz" in info.compact_error_message + assert info.exception_type == "Warning" + + +def test_rate_limited_publish_does_not_warn_on_single_cycle_spike_by_default(publisher): + publisher, connector = publisher + publisher.rate_limit_s = 10 + publisher._high_update_rate_warning_hz = 2 + publisher._high_update_rate_warning_cycles = 3 + + with mock.patch.object(connector, "raise_alarm") as raise_alarm: + for index in range(21): + publisher.publish_set_and_publish( + MessageEndpoints.device_progress("samx"), + lambda index=index: messages.ProgressMessage( + value=index, max_value=21, done=False, metadata={"seq": index} + ), + ) + + raise_alarm.assert_not_called() + + +def test_rate_limited_publish_warns_on_sustained_high_rate_across_multiple_cycles(publisher): + publisher, connector = publisher + publisher.rate_limit_s = 10 + publisher._high_update_rate_warning_hz = 2 + publisher._high_update_rate_warning_cycles = 3 + + key = (_PublishMethod.SET_AND_PUBLISH.value, MessageEndpoints.device_progress("samx").endpoint) + state = _TopicState() + + with mock.patch.object(connector, "raise_alarm") as raise_alarm: + for count in (21, 21): + for _ in range(count): + state.inserted_in_cooldown += 1 + publisher._start_cooldown_window_locked(key, state) + + for _ in range(21): + state.inserted_in_cooldown += 1 + publisher._start_cooldown_window_locked(key, state) + + raise_alarm.assert_called_once() + info = raise_alarm.call_args.kwargs["info"] + assert info.device == MessageEndpoints.device_progress("samx").endpoint + assert "across the last 3 windows" in info.error_message + assert "rolling average ~2.1 Hz exceeds 2.0 Hz" in info.compact_error_message + + +def test_rate_limited_publish_includes_idle_cycles_in_rolling_average(publisher): + publisher, connector = publisher + publisher.rate_limit_s = 10 + publisher._high_update_rate_warning_hz = 2 + publisher._high_update_rate_warning_cycles = 3 + + key = (_PublishMethod.SET_AND_PUBLISH.value, MessageEndpoints.device_progress("samx").endpoint) + state = _TopicState( + recent_insert_counts=deque(maxlen=publisher._high_update_rate_warning_cycles - 1) + ) + + with mock.patch.object(connector, "raise_alarm") as raise_alarm: + for _ in range(21): + state.inserted_in_cooldown += 1 + publisher._start_cooldown_window_locked(key, state) + + # Simulate one idle cooldown window between bursts. + publisher._start_cooldown_window_locked(key, state) + + for _ in range(21): + state.inserted_in_cooldown += 1 + publisher._start_cooldown_window_locked(key, state) + + raise_alarm.assert_not_called() + + +def test_rate_limited_publish_disables_high_rate_alarm_when_threshold_is_non_positive(publisher): + publisher, connector = publisher + publisher.rate_limit_s = 10 + publisher._high_update_rate_warning_hz = 0 + + key = (_PublishMethod.SET_AND_PUBLISH.value, MessageEndpoints.device_progress("samx").endpoint) + state = _TopicState( + recent_insert_counts=deque(maxlen=publisher._high_update_rate_warning_cycles) + ) + + with mock.patch.object(connector, "raise_alarm") as raise_alarm: + for _ in range(21): + state.inserted_in_cooldown += 1 + publisher._start_cooldown_window_locked(key, state) + + raise_alarm.assert_not_called() + + +def test_rate_limited_publish_repeats_alarm_only_after_period(publisher): + publisher, connector = publisher + publisher.rate_limit_s = 10 + publisher._high_update_rate_warning_hz = 2 + publisher._high_update_rate_warning_cycles = 1 + publisher._high_update_rate_warning_period_s = 30 + + key = (_PublishMethod.SET_AND_PUBLISH.value, MessageEndpoints.device_progress("samx").endpoint) + state = _TopicState( + recent_insert_counts=deque(maxlen=publisher._high_update_rate_warning_cycles) + ) + + with ( + mock.patch.object(connector, "raise_alarm") as raise_alarm, + mock.patch( + "bec_server.device_server.devices.rate_limited_pipeline_publisher.time.monotonic", + side_effect=[100.0, 120.0, 131.0], + ), + ): + for _ in range(3): + state.inserted_in_cooldown = 21 + publisher._start_cooldown_window_locked(key, state) + + assert raise_alarm.call_count == 2