package org.opentrafficsim.remotecontrol; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.FlowLayout; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.IOException; import java.io.PrintStream; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Scanner; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.WindowConstants; import javax.swing.border.EmptyBorder; import org.djunits.unit.DurationUnit; import org.djunits.unit.TimeUnit; import org.djunits.value.vdouble.scalar.Duration; import org.djunits.value.vdouble.scalar.Time; import org.djutils.cli.Checkable; import org.djutils.cli.CliUtil; import org.djutils.decoderdumper.HexDumper; import org.djutils.io.URLResource; import org.djutils.logger.CategoryLogger; import org.djutils.logger.LogCategory; import org.djutils.serialization.SerializationException; import org.pmw.tinylog.Level; import org.sim0mq.Sim0MQException; import org.sim0mq.message.Sim0MQMessage; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ; import org.zeromq.ZMQException; import picocli.CommandLine.Command; import picocli.CommandLine.Option; /** * Remotely control OTS using Sim0MQ messages. *
* Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
* BSD-style license. See OpenTrafficSim License.
*
o
*/
private String ackNack(final Object o)
{
if (null == o)
{
return "null";
}
if (o instanceof Boolean)
{
return ((Boolean) o) ? "ACK" : "NACK";
}
return "????";
}
/** {@inheritDoc} */
@Override
public void run()
{
do
{
try
{
// Read from remotely controlled OTS
byte[] bytes = this.fromOTS.recv(0); /// XXX: this one is okay to block
// System.out.println("remote controller has received a message on the fromOTS PULL socket");
Object[] message = Sim0MQMessage.decode(bytes).createObjectArray();
if (message.length > 8 && message[5] instanceof String)
{
// System.out.println(Sim0MQMessage.print(message));
String command = (String) message[5];
switch (command)
{
case "GTU move":
Sim0MQRemoteControllerNew.this.output
.println(String.format("%10.10s (%s): location=%s heading=%s, v=%s, a=%s", message[8],
message[9], message[10], message[11], message[12], message[13]));
break;
case "NEWSIMULATION":
Sim0MQRemoteControllerNew.this.output
.println(String.format("NEWSIMULATION %s: %s", ackNack(message[8]), message[9]));
break;
case "SIMULATEUNTIL":
Sim0MQRemoteControllerNew.this.output
.println(String.format("SIMULATEUNTIL %s: %s", ackNack(message[8]), message[9]));
break;
case "GTUs in network":
StringBuilder listOfGTUIds = new StringBuilder();
listOfGTUIds.append(message[5] + ":");
for (int index = 8; index < message.length; index++)
{
listOfGTUIds.append(" " + message[index]);
}
Sim0MQRemoteControllerNew.this.output.println(listOfGTUIds.toString());
for (int index = 8; index < message.length; index++)
{
// Request detailed data
try
{
write(Sim0MQMessage.encodeUTF8(true, 0, "RemoteControl", "OTS", "GTU move|GET_CURRENT",
0, message[index]));
}
catch (IOException e)
{
e.printStackTrace();
}
}
break;
case "TIME_CHANGED_EVENT":
Sim0MQRemoteControllerNew.this.output.println(message[8]);
break;
case "TRAFFICCONTROL.CONTROLLER_WARNING":
Sim0MQRemoteControllerNew.this.output
.println(String.format("%s: warning %s", message[8], message[9]));
break;
case "TRAFFICCONTROL.CONTROLLER_EVALUATING":
Sim0MQRemoteControllerNew.this.output
.println(String.format("%s: evaluating %s", message[8], message[9]));
break;
case "TRAFFICCONTROL.CONFLICT_GROUP_CHANGED":
Sim0MQRemoteControllerNew.this.output.println(String.format(
"%s: conflict group changed from %s to %s", message[8], message[9], message[10]));
break;
case "NETWORK.GTU.ADD":
Sim0MQRemoteControllerNew.this.output.println(String.format("GTU added %s", message[8]));
break;
case "NETWORK.GTU.REMOVE":
Sim0MQRemoteControllerNew.this.output.println(String.format("GTU removed %s", message[8]));
break;
case "READY":
Sim0MQRemoteControllerNew.this.output.println("Slave is ready for the next command");
break;
default:
Sim0MQRemoteControllerNew.this.output.println("Unhandled reply: " + command);
Sim0MQRemoteControllerNew.this.output.println(HexDumper.hexDumper(bytes));
Sim0MQRemoteControllerNew.this.output.println("Received:");
Sim0MQRemoteControllerNew.this.output.println(Sim0MQMessage.print(message));
break;
}
}
else
{
Sim0MQRemoteControllerNew.this.output.println(HexDumper.hexDumper(bytes));
}
}
catch (ZMQException | Sim0MQException | SerializationException e)
{
e.printStackTrace();
return;
}
}
while (true);
}
}
/**
* Open an URL, read it and store the contents in a string. Adapted from
* https://stackoverflow.com/questions/4328711/read-url-to-string-in-few-lines-of-java-code
* @param url URL; the URL
* @return String
* @throws IOException when reading the file fails
*/
public static String readStringFromURL(final URL url) throws IOException
{
try (Scanner scanner = new Scanner(url.openStream(), StandardCharsets.UTF_8.toString()))
{
scanner.useDelimiter("\\A");
return scanner.hasNext() ? scanner.next() : "";
}
}
/** {@inheritDoc} */
@Override
public void actionPerformed(final ActionEvent e)
{
switch (e.getActionCommand())
{
case "SendNetwork":
{
String networkFile = "/TrafCODDemo2/TrafCODDemo2.xml";
Duration warmupDuration = Duration.ZERO;
Duration runDuration = new Duration(3600, DurationUnit.SECOND);
Long seed = 123456L;
URL url = URLResource.getResource(networkFile);
// System.out.println("url is " + url);
try
{
String xml = readStringFromURL(url);
// System.out.println("xml length = " + xml.length());
try
{
write(Sim0MQMessage.encodeUTF8(true, 0, "RemoteControl", "OTS", "NEWSIMULATION", 0, xml, runDuration,
warmupDuration, seed));
String caption = this.stepTo.getText();
int position;
for (position = 0; position < caption.length(); position++)
{
if (Character.isDigit(caption.charAt(position)))
{
break;
}
}
Time toTime = new Time(10.0, TimeUnit.BASE_SECOND);
this.stepTo.setText(caption.substring(0, position)
+ String.format("%.0f %s", toTime.getInUnit(), toTime.getDisplayUnit()));
}
catch (IOException e1)
{
this.output.println("Write failed; Caught IOException");
((JComponent) e.getSource()).setEnabled(false);
}
catch (Sim0MQException e1)
{
e1.printStackTrace();
}
catch (SerializationException e1)
{
e1.printStackTrace();
}
}
catch (IOException e2)
{
System.err.println("Could not load file " + networkFile);
e2.printStackTrace();
}
break;
}
case "StepTo":
{
String caption = this.stepTo.getText();
int position;
for (position = 0; position < caption.length(); position++)
{
if (Character.isDigit(caption.charAt(position)))
{
break;
}
}
Time toTime = Time.valueOf(caption.substring(position));
try
{
write(Sim0MQMessage.encodeUTF8(true, 0, "RemoteControl", "OTS", "SIMULATEUNTIL", 0, toTime));
toTime = toTime.plus(new Duration(10, DurationUnit.SECOND));
this.stepTo.setText(caption.substring(0, position)
+ String.format("%.0f %s", toTime.getInUnit(), toTime.getDisplayUnit()));
}
catch (IOException | Sim0MQException | SerializationException e1)
{
e1.printStackTrace();
}
break;
}
case "Step100To":
{
for (int i = 0; i < 100; i++)
{
actionPerformed(new ActionEvent(this.stepTo, 0, "StepTo"));
}
break;
}
case "GetAllGTUPositions":
{
try
{
write(Sim0MQMessage.encodeUTF8(true, 0, "RemoteControl", "OTS", "GTUs in network|GET_CURRENT", 0));
}
catch (IOException | Sim0MQException | SerializationException e1)
{
e1.printStackTrace();
}
break;
}
case "Send DIE command":
{
try
{
write(Sim0MQMessage.encodeUTF8(true, 0, "RemoteControl", "OTS", "DIE", 0));
}
catch (IOException | Sim0MQException | SerializationException e1)
{
e1.printStackTrace();
}
break;
}
default:
this.output.println("Oops: unhandled action command");
}
}
}