package org.yamcs; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.yamcs.api.MediaType; import org.yamcs.api.YamcsConnectionProperties; import org.yamcs.api.rest.RestClient; import org.yamcs.api.ws.WebSocketClient; import org.yamcs.api.ws.WebSocketClientCallback; import org.yamcs.api.ws.WebSocketRequest; import org.yamcs.archive.PacketWithTime; import org.yamcs.management.ManagementService; import org.yamcs.parameter.ParameterValue; import org.yamcs.protobuf.Alarms.AlarmData; import org.yamcs.protobuf.Archive.StreamData; import org.yamcs.protobuf.Commanding.CommandHistoryEntry; import org.yamcs.protobuf.Commanding.CommandQueueInfo; import org.yamcs.protobuf.Pvalue.AcquisitionStatus; import org.yamcs.protobuf.Pvalue.ParameterData; import org.yamcs.protobuf.Rest.IssueCommandRequest; import org.yamcs.protobuf.Web.WebSocketServerMessage.WebSocketSubscriptionData; import org.yamcs.protobuf.Yamcs.Event; import org.yamcs.protobuf.Yamcs.NamedObjectId; import org.yamcs.protobuf.Yamcs.NamedObjectList; import org.yamcs.protobuf.Yamcs.TimeInfo; import org.yamcs.protobuf.YamcsManagement.ClientInfo; import org.yamcs.protobuf.YamcsManagement.LinkEvent; import org.yamcs.protobuf.YamcsManagement.ProcessorInfo; import org.yamcs.protobuf.YamcsManagement.Statistics; import org.yamcs.security.Privilege; import org.yamcs.security.UsernamePasswordToken; import org.yamcs.tctm.ParameterDataLink; import org.yamcs.tctm.ParameterSink; import org.yamcs.tctm.TmPacketDataLink; import org.yamcs.tctm.TmSink; import org.yamcs.utils.FileUtils; import org.yamcs.utils.TimeEncoding; import org.yamcs.utils.ValueUtility; import org.yamcs.web.HttpServer; import org.yamcs.web.websocket.ManagementResource; import org.yamcs.xtce.SequenceContainer; import org.yamcs.xtce.XtceDb; import org.yamcs.xtceproc.XtceDbFactory; import org.yamcs.yarch.management.JMXService; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.google.common.util.concurrent.AbstractService; import com.google.protobuf.MessageLite; import com.google.protobuf.MessageLite.Builder; import io.protostuff.JsonIOUtil; import io.protostuff.JsonInput; import io.protostuff.Schema; public abstract class AbstractIntegrationTest { final String yamcsInstance = "IntegrationTest"; PacketProvider packetProvider; ParameterProvider parameterProvider; YamcsConnectionProperties ycp = new YamcsConnectionProperties("localhost", 9190, "IntegrationTest"); MyWsListener wsListener; WebSocketClient wsClient; RestClient restClient; protected UsernamePasswordToken adminToken = new UsernamePasswordToken("admin", "rootpassword"); RefMdbPacketGenerator packetGenerator; static { // Logger.getLogger("org.yamcs").setLevel(Level.ALL); } @BeforeClass public static void beforeClass() throws Exception { setupYamcs(); } @Before public void before() throws InterruptedException { if(Privilege.getInstance().isEnabled()) { ycp.setAuthenticationToken(adminToken); } packetProvider = PacketProvider.instance; parameterProvider = ParameterProvider.instance; assertNotNull(packetProvider); assertNotNull(parameterProvider); wsListener = new MyWsListener(); wsClient = new WebSocketClient(ycp, wsListener); wsClient.setUserAgent("it-junit"); wsClient.connect(); assertTrue(wsListener.onConnect.tryAcquire(5, TimeUnit.SECONDS)); restClient = new RestClient(ycp); restClient.setAcceptMediaType(MediaType.JSON); restClient.setSendMediaType(MediaType.JSON); restClient.setAutoclose(false); packetGenerator = packetProvider.mdbPacketGenerator; packetGenerator.setGenerationTime(TimeEncoding.INVALID_INSTANT); // ClientInfo cinfo = wsListener.clientInfoList.poll(5, TimeUnit.SECONDS); // System.out.println("got cinfo:"+cinfo); // assertNotNull(cinfo); } protected ClientInfo getClientInfo() throws InterruptedException { WebSocketRequest wsr = new WebSocketRequest("management", ManagementResource.OP_getClientInfo); wsClient.sendRequest(wsr); ClientInfo cinfo = wsListener.clientInfoList.poll(5, TimeUnit.SECONDS); assertNotNull(cinfo); return cinfo; } private static void setupYamcs() throws Exception { File dataDir=new File("/tmp/yamcs-IntegrationTest-data"); FileUtils.deleteRecursively(dataDir.toPath()); YConfiguration.setup("IntegrationTest"); ManagementService.setup(false); JMXService.setup(false); new HttpServer().startServer(); // artemisServer = ArtemisServer.setupArtemis(); // ArtemisManagement.setupYamcsServerControl(); YamcsServer.setupYamcsServer(); } IssueCommandRequest getCommand(int seq, String... args) { IssueCommandRequest.Builder b = IssueCommandRequest.newBuilder(); b.setOrigin("IntegrationTest"); b.setSequenceNumber(seq); for(int i = 0; i<args.length; i+=2) { b.addAssignment(IssueCommandRequest.Assignment.newBuilder().setName(args[i]).setValue(args[i+1]).build()); } return b.build(); } protected NamedObjectList getSubscription(String... pfqname) { NamedObjectList.Builder b = NamedObjectList.newBuilder(); for(String p: pfqname) { b.addList(NamedObjectId.newBuilder().setName(p).build()); } return b.build(); } @After public void after() throws InterruptedException { wsClient.disconnect(); assertTrue(wsListener.onDisconnect.tryAcquire(5, TimeUnit.SECONDS)); } @AfterClass public static void shutDownYamcs() throws Exception { YamcsServer.shutDown(); } <T extends MessageLite> String toJson(T msg, Schema<T> schema) throws IOException { StringWriter writer = new StringWriter(); JsonIOUtil.writeTo(writer, msg, schema, false); return writer.toString(); } <T extends MessageLite.Builder> T fromJson(String jsonstr, Schema<T> schema) throws IOException { StringReader reader = new StringReader(jsonstr); T msg = schema.newMessage(); JsonIOUtil.mergeFrom(reader, msg, schema, false); return msg; } //parses a series of messages (not really a list because they are not separated by "," and do not have start and end of list ([ ]) <T extends MessageLite> List<T> allFromJson(String jsonstr, Schema schema) throws IOException { StringReader reader = new StringReader(jsonstr); JsonParser parser = JsonIOUtil.DEFAULT_JSON_FACTORY.createParser(reader); JsonInput input = new JsonInput(parser); List<T> r = new ArrayList<>(); while(true) { JsonToken t = parser.nextToken(); if(t==null) break; if (t != JsonToken.START_OBJECT) throw new RuntimeException("Expected: "+JsonToken.START_OBJECT+", got: "+t); MessageLite.Builder msgb = (Builder) schema.newMessage(); schema.mergeFrom(input, msgb); r.add((T)msgb.build()); } return r; } class MyWsListener implements WebSocketClientCallback { Semaphore onConnect = new Semaphore(0); Semaphore onDisconnect = new Semaphore(0); LinkedBlockingQueue<NamedObjectId> invalidIdentificationList = new LinkedBlockingQueue<>(); LinkedBlockingQueue<ParameterData> parameterDataList = new LinkedBlockingQueue<>(); LinkedBlockingQueue<CommandHistoryEntry> cmdHistoryDataList = new LinkedBlockingQueue<>(); LinkedBlockingQueue<ClientInfo> clientInfoList = new LinkedBlockingQueue<>(); LinkedBlockingQueue<ProcessorInfo> processorInfoList = new LinkedBlockingQueue<>(); LinkedBlockingQueue<Statistics> statisticsList = new LinkedBlockingQueue<>(); LinkedBlockingQueue<AlarmData> alarmDataList = new LinkedBlockingQueue<>(); LinkedBlockingQueue<Event> eventList = new LinkedBlockingQueue<>(); LinkedBlockingQueue<StreamData> streamDataList = new LinkedBlockingQueue<>(); LinkedBlockingQueue<TimeInfo> timeInfoList = new LinkedBlockingQueue<>(); LinkedBlockingQueue<LinkEvent> linkEventList = new LinkedBlockingQueue<>(); LinkedBlockingQueue<CommandQueueInfo> cmdQueueInfoList = new LinkedBlockingQueue<>(); int count =0; @Override public void connected() { onConnect.release(); } @Override public void disconnected() { onDisconnect.release(); } @Override public void onInvalidIdentification(NamedObjectId id) { invalidIdentificationList.add(id); } @Override public void onMessage(WebSocketSubscriptionData data) { switch (data.getType()) { case PARAMETER: count++; parameterDataList.add(data.getParameterData()); break; case CMD_HISTORY: cmdHistoryDataList.add(data.getCommand()); break; case CLIENT_INFO: clientInfoList.add(data.getClientInfo()); break; case PROCESSOR_INFO: processorInfoList.add(data.getProcessorInfo()); break; case PROCESSING_STATISTICS: statisticsList.add(data.getStatistics()); break; case ALARM_DATA: alarmDataList.add(data.getAlarmData()); break; case EVENT: eventList.add(data.getEvent()); break; case STREAM_DATA: streamDataList.add(data.getStreamData()); break; case TIME_INFO: timeInfoList.add(data.getTimeInfo()); break; case LINK_EVENT: linkEventList.add(data.getLinkEvent()); break; case COMMAND_QUEUE_INFO: cmdQueueInfoList.add(data.getCommandQueueInfo()); break; default: throw new IllegalArgumentException("Unexpected type " + data.getType()); } } } public static class PacketProvider extends AbstractService implements TmPacketDataLink, TmProcessor { static volatile PacketProvider instance; RefMdbPacketGenerator mdbPacketGenerator = new RefMdbPacketGenerator(); TmSink tmSink; public PacketProvider(String yinstance, String name, String spec) { instance = this; } @Override public String getLinkStatus() { return "OK"; } @Override public String getDetailedStatus() { return "OK"; } @Override public void enable() { } @Override public void disable() { } @Override public boolean isDisabled() { return false; } @Override public long getDataCount() { return 0; } @Override public void setTmSink(TmSink tmSink) { this.tmSink = tmSink; } @Override protected void doStart() { mdbPacketGenerator.init(null, this); notifyStarted(); } @Override protected void doStop() { notifyStopped(); } @Override public void processPacket(PacketWithTime pwrt) { tmSink.processPacket(pwrt); } @Override public void processPacket(PacketWithTime pwrt, SequenceContainer rootContainer) { tmSink.processPacket(pwrt); } @Override public void finished() { } } public static class ParameterProvider extends AbstractService implements ParameterDataLink { int seqNum = 0; ParameterSink ppListener; long generationTime; static volatile ParameterProvider instance; XtceDb xtcedb; public ParameterProvider(String yamcsInstance, String name) { instance = this; xtcedb = XtceDbFactory.getInstance(yamcsInstance); } @Override public String getLinkStatus() { return "OK"; } @Override public String getDetailedStatus() { return null; } @Override public void enable() { } @Override public void disable() { } @Override public boolean isDisabled() { return false; } @Override public long getDataCount() { return seqNum*3; } @Override public void setParameterSink(ParameterSink ppListener) { this.ppListener = ppListener; } @Override protected void doStart() { notifyStarted(); } @Override protected void doStop() { notifyStopped(); } public void setGenerationTime(long genTime) { this.generationTime = genTime; } void generateParameters(int x) { ParameterValue pv1 = new ParameterValue(xtcedb.getParameter("/REFMDB/SUBSYS1/processed_para_uint")); pv1.setUnsignedIntegerValue(x); pv1.setGenerationTime(generationTime); ParameterValue pv2 = new ParameterValue(xtcedb.getParameter("/REFMDB/SUBSYS1/processed_para_string")); pv2.setGenerationTime(generationTime); pv2.setStringValue("para" +x); ppListener.updateParameters(generationTime, "IntegrationTest", seqNum, Arrays.asList(pv1, pv2)); //this one should be combined with the two above in the archive as they have the same generation time, group and sequence org.yamcs.protobuf.Pvalue.ParameterValue pv3 = org.yamcs.protobuf.Pvalue.ParameterValue.newBuilder().setAcquisitionStatus(AcquisitionStatus.ACQUIRED) .setId(NamedObjectId.newBuilder().setName("/REFMDB/SUBSYS1/processed_para_double")) .setGenerationTime(generationTime) .setEngValue(ValueUtility.getDoubleGbpValue(x)) .build(); ppListener.updateParams(generationTime, "IntegrationTest", seqNum, Arrays.asList(pv3)); //mixup some ParameterValue with Protobuf ParameterValue to test compatibility with old yamcs org.yamcs.protobuf.Pvalue.ParameterValue pv4 = org.yamcs.protobuf.Pvalue.ParameterValue.newBuilder().setAcquisitionStatus(AcquisitionStatus.ACQUIRED) .setId(NamedObjectId.newBuilder().setName("/REFMDB/SUBSYS1/processed_para_uint")) .setGenerationTime(generationTime+20) .setEngValue(ValueUtility.getUint32GbpValue(x)) .build(); ppListener.updateParams(generationTime+20, "IntegrationTest", seqNum, Arrays.asList(pv4)); seqNum++; } } }