diff --git a/lndyiswSup/lndyisw.db b/lndyiswSup/lndyisw.db index 1ce77c4..e8de014 100644 --- a/lndyiswSup/lndyisw.db +++ b/lndyiswSup/lndyisw.db @@ -135,107 +135,50 @@ record(ai, "$(P)OUTLET:TCURRENT") field(INP, "@$(HOST) public PDU-MIB::pdu01Value.0 INTEGER: 100 iR") } -record(waveform, "$(P)OUTLET:_STATUS") -{ - field(DESC, "PSU Outlet Status") - field(DTYP, "Snmp") - field(SCAN, "2 second") - field(INP, "@$(HOST) public PDU-MIB::pdu01OutletStatus.0 STRING: 100 s") - field(FTVL, "CHAR") - field(NELM, "200") - field(FLNK, "$(P)OUTLET:STATUS") -} - record(stringin, "$(P)OUTLET:SEP") { field(VAL, ",\"") } -record(aSub, "$(P)OUTLET:STATUS") -{ - field(DESC, "PSU Outlet Status") - field(SNAM, "splitCharWaveform") - field(INPA, "$(P)OUTLET:_STATUS") - field(INPB, "$(P)OUTLET:_STATUS.NORD") - field(FTB, "ULONG") - field(FTC, "ULONG") - field(FTD, "STRING") - field(FTE, "ULONG") - field(FTA, "CHAR") - field(FTVA, "STRING") - field(FTVB, "STRING") - field(FTVC, "STRING") - field(FTVD, "STRING") - field(FTVE, "STRING") - field(FTVF, "STRING") - field(FTVG, "STRING") - field(FTVH, "STRING") - field(FTVI, "STRING") - field(FTVJ, "STRING") - field(FTVU, "ULONG") - field(NOA, "200") - field(INPC, "1") - field(INPD, "$(P)OUTLET:SEP") -} - -record(waveform, "$(P)OUTLET:_CURRENT") -{ - field(DESC, "PSU Outlet Current value") - field(DTYP, "Snmp") - field(SCAN, "2 second") - field(INP, "@$(HOST) public PDU-MIB::pdu01SubValues.0 STRING: 100 s") - field(FTVL, "CHAR") - field(NELM, "200") - field(FLNK, "$(P)OUTLET:CURRENT") -} - -record(aSub, "$(P)OUTLET:CURRENT") -{ - field(DESC, "PSU Outlet CURRENT") - field(SNAM, "splitCharWaveform") - field(INPA, "$(P)OUTLET:_CURRENT") - field(INPB, "$(P)OUTLET:_CURRENT.NORD") - field(FTB, "ULONG") - field(FTC, "ULONG") - field(FTD, "STRING") - field(FTE, "ULONG") - field(FTA, "CHAR") - field(FTVA, "STRING") - field(FTVB, "STRING") - field(FTVC, "STRING") - field(FTVD, "STRING") - field(FTVE, "STRING") - field(FTVF, "STRING") - field(FTVG, "STRING") - field(FTVH, "STRING") - field(FTVI, "STRING") - field(FTVJ, "STRING") - field(FTVU, "ULONG") - field(NOA, "200") - field(INPC, "1") - field(INPD, "$(P)OUTLET:SEP") -} - record(stringout, "$(P)STATUS:ALLSET") { - field(DESC, "Sets all the outlet status") + field(DESC, "Read and Write the outlet status") + # EPICS SNMP allows passive polling on a record to update, using this fixes an issue with rapid sets being overwritten. field(DTYP, "Snmp") field(OUT, "@$(HOST) public PDU-MIB::pdu01OutletStatus.0 STRING: 100 s") + field(FLNK, "$(P)OUTLET:STATUS1") +} + +record(scalcout, "$(P)OUTLET:STATUS1") +{ + field(INJJ, "$(P)STATUS:ALLSET") + # Split the first 4 outlets out of the status string. + field(CALC, "B:=int(JJ[0,1]);C:=int(JJ[2,3]);D:=int(JJ[4,5]);E:=int(JJ[6,7]);0") + field(FLNK, "$(P)OUTLET:STATUS2") +} + + +record(scalcout, "$(P)OUTLET:STATUS2") +{ + field(INJJ, "$(P)STATUS:ALLSET") + # Split the remaining 4 outlets out of the status string. + field(CALC, "F:=int(JJ[8,9]);G:=int(JJ[10,11]);H:=int(JJ[12,13]);I:=int(JJ[14,15]);0") } record(scalcout, "$(P)STATUS:CURRVAL") { - field(INAA, "$(P)OUTLET:STATUS.VALB") - field(INBB, "$(P)OUTLET:STATUS.VALC") - field(INCC, "$(P)OUTLET:STATUS.VALD") - field(INDD, "$(P)OUTLET:STATUS.VALE") - field(INEE, "$(P)OUTLET:STATUS.VALF") - field(INFF, "$(P)OUTLET:STATUS.VALG") - field(INGG, "$(P)OUTLET:STATUS.VALH") - field(INHH, "$(P)OUTLET:STATUS.VALI") + field(INAA, "$(P)OUTLET:STATUS1.B") + field(INBB, "$(P)OUTLET:STATUS1.C") + field(INCC, "$(P)OUTLET:STATUS1.D") + field(INDD, "$(P)OUTLET:STATUS1.E") + field(INEE, "$(P)OUTLET:STATUS2.F") + field(INFF, "$(P)OUTLET:STATUS2.G") + field(INGG, "$(P)OUTLET:STATUS2.H") + field(INHH, "$(P)OUTLET:STATUS2.I") field(OCAL, "AA+','+BB+','+CC+','+DD+','+EE+','+FF+','+GG+','+HH") field(DOPT, "Use OCAL") - field(CALC, "A:=NINT(I/10-1);B:=I%10;@@A:=PRINTF('%d',B);0") + # Input I is a 2 digit number where the first is the index of input to alter, and the second is the value to set it to. + field(CALC, "A:=NINT(I/10-1);B:=I%10;@@A:=PRINTF('%d',B);0") field(OUT, "$(P)STATUS:ALLSET PP") - field(FLNK, "$(P)OUTLET:_STATUS") + field(FLNK, "$(P)OUTLET:_STATUS.PROC CA") } diff --git a/lndyiswSup/lndyisw_outlets.substitutions b/lndyiswSup/lndyisw_outlets.substitutions index 739a5bb..ff37d31 100644 --- a/lndyiswSup/lndyisw_outlets.substitutions +++ b/lndyiswSup/lndyisw_outlets.substitutions @@ -1,11 +1,11 @@ global{ HOST=\$(HOST), P=\$(P) } -pattern { OUTLET, WF } - { 1, B } - { 2, C } - { 3, D } - { 4, E } - { 5, F } - { 6, G } - { 7, H } - { 8, I } +pattern { OUTLET, WF, STATUS} + { 1, B, 1} + { 2, C, 1} + { 3, D, 1} + { 4, E, 1} + { 5, F, 2} + { 6, G, 2} + { 7, H, 2} + { 8, I, 2} diff --git a/lndyiswSup/lndyisw_outlets.template b/lndyiswSup/lndyisw_outlets.template index 1f9dfcf..68beca0 100644 --- a/lndyiswSup/lndyisw_outlets.template +++ b/lndyiswSup/lndyisw_outlets.template @@ -15,7 +15,7 @@ record(bi, "$(P)OUTLET$(OUTLET):STATUS") field(ZNAM, "OFF") field(ONAM, "ON") field(DESC, "PSU Outlet $(OUTLET) Status") - field(INP, "$(P)OUTLET:STATUS.VAL$(WF) CP") + field(INP, "$(P)OUTLET:STATUS$(STATUS).$(WF) CP") } record(longin, "$(P)OUTLET$(OUTLET):CURRENT") diff --git a/system_tests/tests/lndyisw.py b/system_tests/tests/lndyisw.py index 688a60b..e5d3282 100644 --- a/system_tests/tests/lndyisw.py +++ b/system_tests/tests/lndyisw.py @@ -1,10 +1,11 @@ import unittest -from utils.channel_access import ChannelAccess -from utils.emulator_launcher import CommandLineEmulatorLauncher -from utils.ioc_launcher import get_default_ioc_dir -from utils.test_modes import TestModes -from utils.testing import get_running_lewis_and_ioc +from parameterized import parameterized +from utils.channel_access import ChannelAccess # type: ignore +from utils.emulator_launcher import CommandLineEmulatorLauncher # type: ignore +from utils.ioc_launcher import get_default_ioc_dir # type: ignore +from utils.test_modes import TestModes # type: ignore +from utils.testing import get_running_lewis_and_ioc # type: ignore DEVICE_PREFIX = "LNDYISW_01" @@ -71,3 +72,81 @@ def test_LNDYISW_ioc_sets_new_location(self): self.ca.set_pv_value("LOCATION:SP", old_value) self.ca.assert_that_pv_is_not("LOCATION", new_value) self.ca.assert_that_pv_is("LOCATION", old_value) + + @parameterized.expand( + [ + ("_odds", "1,0,1,0,1,0,1,0"), + ("_even", "0,1,0,1,0,1,0,1"), + ("_all", "1,1,1,1,1,1,1,1"), + ("_first_half", "1,1,1,1,0,0,0,0"), + ("_second_half", "0,0,0,0,1,1,1,1"), + ] + ) + def test_status_split_properly(self, _, vals): + self.ca.set_pv_value("STATUS:ALLSET", "0,0,0,0,0,0,0,0") + self.ca.set_pv_value("STATUS:ALLSET", vals) + check_outlet_status_calc(vals.split(","), self.ca) + check_outlet_status_bi(vals.split(","), self.ca) + + @parameterized.expand( + [ + ("_index_1", 10, "AA"), + ("_index_2", 20, "BB"), + ("_index_3", 30, "CC"), + ("_index_4", 40, "DD"), + ("_index_5", 50, "EE"), + ("_index_6", 60, "FF"), + ("_index_7", 70, "GG"), + ("_index_8", 80, "HH"), + ] + ) + def test_WHEN_curr_val_i_updates_THEN_correct_allset_update(self, _, index, curr): + allset = ["0", "0", "0", "0", "0", "0", "0", "0"] + self.ca.set_pv_value("STATUS:ALLSET", ",".join(allset)) + self.ca.assert_that_pv_is(f"STATUS:CURRVAL.{curr}", "0") + self.ca.set_pv_value("STATUS:CURRVAL.I", index + 1) + self.ca.assert_that_pv_is(f"STATUS:CURRVAL.{curr}", "1") + allset[int(index / 10) - 1] = "1" + self.ca.assert_that_pv_is("STATUS:ALLSET", ",".join(allset)) + self.ca.set_pv_value("STATUS:CURRVAL.I", index) + self.ca.assert_that_pv_is(f"STATUS:CURRVAL.{curr}", "0") + + def test_LNDYISW_ioc_WHEN_two_sets_THEN_both_work(self): + self.ca.set_pv_value("STATUS:ALLSET", "0,0,0,0,0,0,0,0") + + self.ca.set_pv_value("OUTLET1:STATUS:SP", 1, sleep_after_set=0) + self.ca.set_pv_value("OUTLET2:STATUS:SP", 1) + + self.ca.assert_that_pv_is("OUTLET1:STATUS", "ON") + self.ca.assert_that_pv_is("OUTLET2:STATUS", "ON") + + self.ca.set_pv_value("OUTLET1:STATUS:SP", 0, sleep_after_set=0) + self.ca.set_pv_value("OUTLET2:STATUS:SP", 0) + + self.ca.assert_that_pv_is("OUTLET1:STATUS", "OFF") + self.ca.assert_that_pv_is("OUTLET2:STATUS", "OFF") + + +def check_outlet_status_calc(vals, ca): + ca.assert_that_pv_is("OUTLET:STATUS1.B", float(vals[0])) + ca.assert_that_pv_is("OUTLET:STATUS1.C", float(vals[1])) + ca.assert_that_pv_is("OUTLET:STATUS1.D", float(vals[2])) + ca.assert_that_pv_is("OUTLET:STATUS1.E", float(vals[3])) + + ca.assert_that_pv_is("OUTLET:STATUS2.F", float(vals[4])) + ca.assert_that_pv_is("OUTLET:STATUS2.G", float(vals[5])) + ca.assert_that_pv_is("OUTLET:STATUS2.H", float(vals[6])) + ca.assert_that_pv_is("OUTLET:STATUS2.I", float(vals[7])) + + +def check_outlet_status_bi(vals, ca): + status = ["OFF", "ON"] + ca.assert_that_pv_is("OUTLET1:STATUS", status[int(vals[0])]) + ca.assert_that_pv_is("OUTLET2:STATUS", status[int(vals[1])]) + ca.assert_that_pv_is("OUTLET3:STATUS", status[int(vals[2])]) + ca.assert_that_pv_is("OUTLET4:STATUS", status[int(vals[3])]) + + ca.assert_that_pv_is("OUTLET5:STATUS", status[int(vals[4])]) + ca.assert_that_pv_is("OUTLET6:STATUS", status[int(vals[5])]) + ca.assert_that_pv_is("OUTLET7:STATUS", status[int(vals[6])]) + ca.assert_that_pv_is("OUTLET8:STATUS", status[int(vals[7])]) diff --git a/system_tests/tests/public.snmprec b/system_tests/tests/public.snmprec index 8d5cc5e..9b0495b 100644 --- a/system_tests/tests/public.snmprec +++ b/system_tests/tests/public.snmprec @@ -24,17 +24,17 @@ 1.3.6.1.4.1.17420.1.2.6.0|4|0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 1.3.6.1.4.1.17420.1.2.7.0|4|0 1.3.6.1.4.1.17420.1.2.8.0|4|0 -1.3.6.1.4.1.17420.1.2.9.1.11.0|4|0 -1.3.6.1.4.1.17420.1.2.9.1.12.0|4:writecache|value=0,0,0,0,0,0,0,0 -1.3.6.1.4.1.17420.1.2.9.1.13.0|4:writecache|value=0,1,0,1,0,1,0,1 -1.3.6.1.4.1.17420.1.2.9.1.14.1.0|4:writecache|value=Eurotherm1,0,0,0,0 -1.3.6.1.4.1.17420.1.2.9.1.14.2.0|4:writecache|value=NDXSCIDEMO,0,0,0,0 -1.3.6.1.4.1.17420.1.2.9.1.14.3.0|4:writecache|value=NDXIMAT,0,0,0,0 -1.3.6.1.4.1.17420.1.2.9.1.14.4.0|4:writecache|value=NDXEMMA-2,0,0,0,0 -1.3.6.1.4.1.17420.1.2.9.1.14.5.0|4:writecache|value=MOXA_1,0,0,0,0 -1.3.6.1.4.1.17420.1.2.9.1.14.6.0|4:writecache|value=Eurotherm2,0,0,0,0 -1.3.6.1.4.1.17420.1.2.9.1.14.7.0|4:writecache|value=MOXA_2,0,0,0,0 -1.3.6.1.4.1.17420.1.2.9.1.14.8.0|4:writecache|value=MOXA_3,0,0,0,0 +1.3.6.1.4.1.17420.1.2.9.1.11.0|2|0 +1.3.6.1.4.1.17420.1.2.9.1.12.0|4:writecache|hexvalue=302c302c302c302c302c302c302c30 +1.3.6.1.4.1.17420.1.2.9.1.13.0|4:writecache|hexvalue=302c302c302c312c302c312c302c31 +1.3.6.1.4.1.17420.1.2.9.1.14.1.0|4:writecache|hexvalue=4575726f746865726d312c302c302c302c30 +1.3.6.1.4.1.17420.1.2.9.1.14.2.0|4:writecache|hexvalue=4e445853434944454d4f2c302c302c302c30 +1.3.6.1.4.1.17420.1.2.9.1.14.3.0|4:writecache|hexvalue=4e4458494d41542c302c302c302c30 +1.3.6.1.4.1.17420.1.2.9.1.14.4.0|4:writecache|hexvalue=4e4458454d4d412d322c302c302c302c30 +1.3.6.1.4.1.17420.1.2.9.1.14.5.0|4:writecache|hexvalue=4d4f58415f312c302c302c302c30 +1.3.6.1.4.1.17420.1.2.9.1.14.6.0|4:writecache|hexvalue=4575726f746865726d322c302c302c302c30 +1.3.6.1.4.1.17420.1.2.9.1.14.7.0|4:writecache|hexvalue=4d4f58415f322c302c302c302c30 +1.3.6.1.4.1.17420.1.2.9.1.14.8.0|4:writecache|hexvalue=4d4f58415f332c302c302c302c30 1.3.6.1.4.1.17420.1.2.9.1.15.0|4|45 1.3.6.1.4.1.17420.1.2.9.1.16.0|4|60 1.3.6.1.4.1.17420.1.2.9.1.17.0|4|230