package org.yamcs.algorithms; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Queue; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.yamcs.ConfigurationException; import org.yamcs.InvalidIdentification; import org.yamcs.parameter.ParameterValue; import org.yamcs.ProcessorFactory; import org.yamcs.RefMdbPacketGenerator; import org.yamcs.YConfiguration; import org.yamcs.Processor; import org.yamcs.ProcessorException; import org.yamcs.api.EventProducerFactory; import org.yamcs.management.ManagementService; import org.yamcs.parameter.ParameterConsumer; import org.yamcs.parameter.ParameterProvider; import org.yamcs.parameter.ParameterRequestManagerImpl; import org.yamcs.protobuf.Yamcs.Event; import org.yamcs.protobuf.Yamcs.Event.EventSeverity; import org.yamcs.protobuf.Yamcs.NamedObjectId; import org.yamcs.tctm.SimpleTcTmService; import org.yamcs.utils.TimeEncoding; import org.yamcs.xtce.Parameter; import org.yamcs.xtce.XtceDb; import org.yamcs.xtceproc.XtceDbFactory; public class AlgorithmManagerTest { @BeforeClass public static void setUpBeforeClass() throws Exception { YConfiguration.setup("refmdb"); ManagementService.setup(false); XtceDbFactory.reset(); } private XtceDb db; private Processor c; private RefMdbPacketGenerator tmGenerator; private ParameterRequestManagerImpl prm; private Queue<Event> q; @Before public void beforeEachTest() throws ConfigurationException, ProcessorException { EventProducerFactory.setMockup(true); q=EventProducerFactory.getMockupQueue(); db=XtceDbFactory.getInstance("refmdb"); assertNotNull(db.getParameter("/REFMDB/SUBSYS1/FloatPara1_1_2")); tmGenerator=new RefMdbPacketGenerator(); tmGenerator=new RefMdbPacketGenerator(); List<ParameterProvider> paramProviderList = new ArrayList<ParameterProvider>(); Map<String, Object> jslib = new HashMap<String, Object>(); Map<String, Object> config = new HashMap<String, Object>(); jslib.put("JavaScript", Arrays.asList("mdb/algolib.js")); jslib.put("python", Arrays.asList("mdb/algolib.py")); config.put("libraries", jslib); paramProviderList.add(new AlgorithmManager("refmdb", config)); SimpleTcTmService tmtcs = new SimpleTcTmService(tmGenerator, paramProviderList, null); c=ProcessorFactory.create("refmdb", "AlgorithmManagerTest", "refmdb", tmtcs, "junit"); prm=c.getParameterRequestManager(); } @After public void afterEachTest() { // Prevents us from wrapping our code in try-finally c.quit(); } @Test public void testFloatAdd() throws InvalidIdentification { Parameter floatPara = prm.getParameter(NamedObjectId.newBuilder().setName("/REFMDB/SUBSYS1/FloatPara1_1_2").build()); Parameter floatAddition = prm.getParameter(NamedObjectId.newBuilder().setName("/REFMDB/SUBSYS1/AlgoFloatAddition").build()); final ArrayList<ParameterValue> params=new ArrayList<ParameterValue>(); prm.addRequest(Arrays.asList(floatPara, floatAddition), new ParameterConsumer() { @Override public void updateItems(int subscriptionId, List<ParameterValue> items) { params.addAll(items); } }); c.start(); tmGenerator.generate_PKT1_1(); assertEquals(2, params.size()); for(ParameterValue pvwi:params) { if(pvwi.getParameter().equals(floatPara)) { assertEquals(0.1672918, pvwi.getEngValue().getFloatValue(), 0.001); } else if(pvwi.getParameter().equals(floatAddition)) { assertEquals(2.1672918, pvwi.getRawValue().getFloatValue(), 0.001); } else { fail("Unexpected parameter "+pvwi.getParameter()); } } } @Ignore @Test //this can be used to see that the performance of javascript is much worse in some later versions of Java 6 //OpenJDK 7 is very fast. public void testJavascriptPerformanceFloatAdd() throws InvalidIdentification { List<Parameter> paraList=new ArrayList<Parameter>(); paraList.add(prm.getParameter(NamedObjectId.newBuilder().setName("/REFMDB/SUBSYS1/AlgoYprFloat").build())); paraList.add(prm.getParameter(NamedObjectId.newBuilder().setName("/REFMDB/SUBSYS1/FloatPara1_1_2").build())); final ArrayList<ParameterValue> params=new ArrayList<ParameterValue>(); prm.addRequest(paraList, new ParameterConsumer() { @Override public void updateItems(int subscriptionId, List<ParameterValue> items) { params.addAll(items); } }); c.start(); long t0 = System.currentTimeMillis(); int n = 100000; for(int i = 0 ; i<n; i++) { tmGenerator.generate_PKT1_1(); } long t1 = System.currentTimeMillis(); assertEquals(2*n, params.size()); } @Test public void testSlidingWindow() throws InvalidIdentification, InterruptedException { Parameter p = prm.getParameter(NamedObjectId.newBuilder().setName("/REFMDB/SUBSYS1/AlgoWindowResult").build()); final List<ParameterValue> params = new ArrayList<ParameterValue>(); prm.addRequest(p, new ParameterConsumer() { @Override public void updateItems(int subscriptionId, List<ParameterValue> items) { params.addAll(items); } }); c.start(); long startTime=TimeEncoding.getWallclockTime(); tmGenerator.generate_PKT1_6(1, 2, startTime, startTime); assertEquals(0, params.size()); // Windows: [* * * 1] && [* 2] tmGenerator.generate_PKT1_6(2, 4, startTime+1, startTime+1); assertEquals(0, params.size()); // Windows: [* * 1 2] && [2 4] tmGenerator.generate_PKT1_6(3, 6, startTime+2, startTime+2); assertEquals(0, params.size()); // Windows: [* 1 2 3] && [4 6] // Production starts only when all relevant values for the expression are present tmGenerator.generate_PKT1_6(5, 8, startTime+3, startTime+3); assertEquals(1, params.size()); // Windows: [1 2 3 5] && [6 8] => produce (1 + 5) * 6 assertEquals(36, params.get(0).getEngValue().getUint32Value()); params.clear(); tmGenerator.generate_PKT1_6(8, 10, startTime+4, startTime+4); assertEquals(1, params.size()); // Windows: [2 3 5 8] && [8 10] => produce (2 + 8) * 8 assertEquals(80, params.get(0).getEngValue().getUint32Value()); } @Test public void testEvents() throws Exception { // No need to subscribe. This algorithm doesn't have any outputs // and is therefore auto-activated (will only trigger if an input changes) c.start(); tmGenerator.generate_PKT1_6(1, 0); assertEquals(1, q.size()); Event evt = q.poll(); assertEquals("CustomAlgorithm", evt.getSource()); assertEquals("script_events", evt.getType()); assertEquals("low", evt.getMessage()); assertEquals(EventSeverity.INFO, evt.getSeverity()); tmGenerator.generate_PKT1_6(7, 0); assertEquals(1, q.size()); evt = q.poll(); assertEquals("CustomAlgorithm", evt.getSource()); assertEquals("script_events", evt.getType()); assertEquals("med", evt.getMessage()); assertEquals(EventSeverity.WARNING, evt.getSeverity()); tmGenerator.generate_PKT1_6(10, 0); assertEquals(1, q.size()); evt = q.poll(); assertEquals("CustomAlgorithm", evt.getSource()); assertEquals("script_events", evt.getType()); assertEquals("high", evt.getMessage()); assertEquals(EventSeverity.ERROR, evt.getSeverity()); } @Test public void testExternalLibrary() throws InvalidIdentification { final ArrayList<ParameterValue> params=new ArrayList<ParameterValue>(); Parameter p = prm.getParameter(NamedObjectId.newBuilder().setName("/REFMDB/SUBSYS1/AlgoFloatDivision").build()); prm.addRequest(p, new ParameterConsumer() { @Override public void updateItems(int subscriptionId, List<ParameterValue> items) { params.addAll(items); } }); c.start(); tmGenerator.generate_PKT1_1(); assertEquals(1, params.size()); assertEquals(tmGenerator.pIntegerPara1_1_1, params.get(0).getEngValue().getFloatValue()*3, 0.001); } @Test public void testAlgorithmChaining() throws InvalidIdentification { final ArrayList<ParameterValue> params=new ArrayList<ParameterValue>(); Parameter p = prm.getParameter(NamedObjectId.newBuilder().setName("/REFMDB/SUBSYS1/AlgoFloatMultiplication").build()); int subscriptionId=prm.addRequest(p, new ParameterConsumer() { @Override public void updateItems(int subscriptionId, List<ParameterValue> items) { params.addAll(items); } }); c.start(); tmGenerator.generate_PKT1_1(); assertEquals(1, params.size()); assertEquals(tmGenerator.pIntegerPara1_1_1, params.get(0).getEngValue().getFloatValue(), 0.001); // Test unsubscribe params.clear(); prm.removeItemsFromRequest(subscriptionId, p); tmGenerator.generate_PKT1_1(); assertTrue(params.isEmpty()); // Subscribe again params.clear(); prm.addItemsToRequest(subscriptionId, p); tmGenerator.generate_PKT1_1(); assertEquals(1, params.size()); assertEquals(tmGenerator.pIntegerPara1_1_1, params.get(0).getEngValue().getFloatValue(), 0.001); } @Test public void testAlgorithmChainingWithWindowing() throws InvalidIdentification { final ArrayList<ParameterValue> params=new ArrayList<ParameterValue>(); int subscriptionId=prm.addRequest(Arrays.asList( prm.getParameter("/REFMDB/SUBSYS1/AlgoFloatAverage"), prm.getParameter("/REFMDB/SUBSYS1/IntegerPara1_1_1") ), new ParameterConsumer() { @Override public void updateItems(int subscriptionId, List<ParameterValue> items) { params.addAll(items); } }); c.start(); tmGenerator.generate_PKT1_1(); assertEquals(1, params.size()); assertEquals(tmGenerator.pIntegerPara1_1_1, params.get(0).getEngValue().getUint32Value()); params.clear(); tmGenerator.generate_PKT1_1(); assertEquals(2, params.size()); assertEquals(tmGenerator.pIntegerPara1_1_1, params.get(0).getEngValue().getUint32Value()); assertEquals((20 + 20 + 20 + (20 / 3.0)) / 4.0, params.get(1).getEngValue().getFloatValue(), 0.001); // Unsubscribe params.clear(); prm.removeItemsFromRequest(subscriptionId, prm.getParameter("/REFMDB/SUBSYS1/AlgoFloatAverage")); tmGenerator.generate_PKT1_1(); tmGenerator.generate_PKT1_1(); assertEquals(2, params.size()); assertEquals(tmGenerator.pIntegerPara1_1_1, params.get(0).getEngValue().getUint32Value()); assertEquals(tmGenerator.pIntegerPara1_1_1, params.get(1).getEngValue().getUint32Value()); // Unsubscribe after subscribing to dependent algorithm's output as well params.clear(); prm.addItemsToRequest(subscriptionId, Arrays.asList( prm.getParameter("/REFMDB/SUBSYS1/AlgoFloatAverage"), prm.getParameter("/REFMDB/SUBSYS1/AlgoFloatMultiplication"))); prm.removeItemsFromRequest(subscriptionId, prm.getParameter("/REFMDB/SUBSYS1/AlgoFloatAverage")); tmGenerator.generate_PKT1_1(); // We should still get AlgoFloatMultiplication assertEquals(2, params.size()); assertEquals("/REFMDB/SUBSYS1/IntegerPara1_1_1", params.get(0).getParameter().getQualifiedName()); assertEquals("/REFMDB/SUBSYS1/AlgoFloatMultiplication", params.get(1).getParameter().getQualifiedName()); } @Test public void testEnumCalibration() throws InvalidIdentification { final ArrayList<ParameterValue> params=new ArrayList<ParameterValue>(); prm.addRequest(prm.getParameter("/REFMDB/SUBSYS1/AlgoCalibrationEnum"), new ParameterConsumer() { @Override public void updateItems(int subscriptionId, List<ParameterValue> items) { params.addAll(items); } }); c.start(); tmGenerator.generate_PKT1_6(1, 1); assertEquals(1, params.size()); assertEquals(1, params.get(0).getRawValue().getUint32Value()); assertEquals("one_why not", params.get(0).getEngValue().getStringValue()); } @Test public void testBooleanAlgorithms() throws InvalidIdentification { final ArrayList<ParameterValue> params=new ArrayList<ParameterValue>(); prm.addRequest(Arrays.asList( prm.getParameter("/REFMDB/SUBSYS1/AlgoBooleanTrueOutcome"), prm.getParameter("/REFMDB/SUBSYS1/AlgoBooleanFalseOutcome") ), new ParameterConsumer() { @Override public void updateItems(int subscriptionId, List<ParameterValue> items) { params.addAll(items); } }); c.start(); tmGenerator.generate_PKT1_9(); assertEquals(2, params.size()); assertEquals(true, params.get(0).getRawValue().getBooleanValue()); assertEquals(true, params.get(0).getEngValue().getBooleanValue()); assertEquals(false, params.get(1).getRawValue().getBooleanValue()); assertEquals(false, params.get(1).getEngValue().getBooleanValue()); } @Test public void testFloatCalibration() throws InvalidIdentification { final ArrayList<ParameterValue> params=new ArrayList<ParameterValue>(); prm.addRequest(prm.getParameter("/REFMDB/SUBSYS1/AlgoCalibrationFloat"), new ParameterConsumer() { @Override public void updateItems(int subscriptionId, List<ParameterValue> items) { params.addAll(items); } }); c.start(); tmGenerator.generate_PKT1_6(1, 1); assertEquals(1, params.size()); assertEquals(1, params.get(0).getRawValue().getUint32Value()); assertEquals(0.0001672918, params.get(0).getEngValue().getFloatValue(), 1e-8); } @Test public void testSeparateUpdate() throws InvalidIdentification { final ArrayList<ParameterValue> params=new ArrayList<ParameterValue>(); prm.addRequest(prm.getParameter("/REFMDB/SUBSYS1/AlgoSeparateUpdateOutcome"), new ParameterConsumer() { @Override public void updateItems(int subscriptionId, List<ParameterValue> items) { params.addAll(items); } }); c.start(); tmGenerator.generate_PKT1_1(); assertEquals(1, params.size()); assertEquals(0.1672918, params.get(0).getEngValue().getFloatValue(), 1e-8); params.clear(); tmGenerator.generate_PKT1_6(5, 6); assertEquals(1, params.size()); assertEquals(5.167291, params.get(0).getEngValue().getFloatValue(), 1e-6); params.clear(); tmGenerator.generate_PKT1_6(4, 6); assertEquals(1, params.size()); assertEquals(4.167291, params.get(0).getEngValue().getFloatValue(), 1e-6); } @Test public void testMarkedNotUpdated() throws InvalidIdentification { final ArrayList<ParameterValue> params=new ArrayList<ParameterValue>(); prm.addRequest(Arrays.asList( prm.getParameter("/REFMDB/SUBSYS1/AlgoUpdatedOut"), prm.getParameter("/REFMDB/SUBSYS1/AlgoUnupdatedOut")), new ParameterConsumer() { @Override public void updateItems(int subscriptionId, List<ParameterValue> items) { params.addAll(items); } }); c.start(); int pIntegerPara16_1 = 5; tmGenerator.generate_PKT1_6(pIntegerPara16_1, 0); assertEquals(1, params.size()); assertEquals("/REFMDB/SUBSYS1/AlgoUpdatedOut", params.get(0).getParameter().getQualifiedName()); assertEquals(pIntegerPara16_1, params.get(0).getEngValue().getUint32Value()); } @Test public void testSelectiveRun() throws InvalidIdentification { final ArrayList<ParameterValue> params=new ArrayList<ParameterValue>(); prm.addRequest(prm.getParameter("/REFMDB/SUBSYS1/AlgoSelectiveOut"), new ParameterConsumer() { @Override public void updateItems(int subscriptionId, List<ParameterValue> items) { params.addAll(items); } }); c.start(); int pIntegerPara16_1 = 5; tmGenerator.generate_PKT1_6(pIntegerPara16_1, 0); assertEquals(1, params.size()); assertEquals("/REFMDB/SUBSYS1/AlgoSelectiveOut", params.get(0).getParameter().getQualifiedName()); assertEquals(pIntegerPara16_1, params.get(0).getEngValue().getFloatValue(), 1e-6); tmGenerator.generate_PKT1_1(); assertEquals(1, params.size()); // No change, not in OnParameterUpdate list pIntegerPara16_1 = 7; tmGenerator.generate_PKT1_6(pIntegerPara16_1, 0); assertEquals(2, params.size()); // Now change, also with updated float from PKT11 assertEquals(pIntegerPara16_1+tmGenerator.pFloatPara1_1_3, params.get(1).getEngValue().getFloatValue(), 1e-6); } @Test public void testOnPeriodicRate() throws InvalidIdentification, InterruptedException { final ArrayList<ParameterValue> params=new ArrayList<ParameterValue>(); prm.addRequest(prm.getParameter("/REFMDB/SUBSYS1/OnPeriodicRateOut"), new ParameterConsumer() { @Override public void updateItems(int subscriptionId, List<ParameterValue> items) { params.addAll(items); } }); c.start(); Thread.sleep(10000); } }