Load and store SIM_WARN flight events

This requires substantial changes to Warnings in .ork files. Instead of Warnings consisting of a text string with id and priority as attributes, it is now a mixed content element with id, priority, description, and possibly sources as subelements and the original text string still present to provide backward compatibility.

If a .ork file with a warning is saved with this PR and then loaded also with this PR, the warning is reconstructed from the subelements and is available if there is a SIM_WARN flight event. If at some point we want to highlight the components referenced in a Warning, we now have references to them.

If a .ork file with a warning is saved with 23.09 and then loaded with this PR, none of the subelements are present, so the Warning is reconstructed from the text.  It won't have actual references to the components mentioned in the Warning, and there won't be a SIM_WARN event needing reference the Warning.

If a .ork file with a warning is saved with this PR and then loaded in 23.09, there will be .ork loading warnings for all the elements defined in this PR, and then the Warning will be constructed from the text content just as for a .ork saved in 23.09.
This commit is contained in:
JoePfeiffer 2024-09-09 06:27:53 -06:00
parent fad20af879
commit 12f8802cd3
9 changed files with 158 additions and 36 deletions

View File

@ -403,7 +403,24 @@ public class OpenRocketSaver extends RocketSaver {
indent++;
for (Warning w : data.getWarningSet()) {
writeElementWithAttribute("warning", "priority", w.getPriority().getExportLabel(), TextUtil.escapeXML(w.toString()));
writeln("<warning>");
indent++;
writeElement("id", w.getID().toString());
writeElement("description", w.getMessageDescription());
writeElement("priority", w.getPriority());
if (null != w.getSources()) {
for (RocketComponent c : w.getSources()) {
writeElement("source", c.getID());
}
}
// We write the whole string content for backwards compatibility with old versions
writeln(TextUtil.escapeXML(w.toString()));
indent--;
writeln("</warning>");
}
// Check whether to store data
@ -579,16 +596,21 @@ public class OpenRocketSaver extends RocketSaver {
// Write events
for (FlightEvent event : branch.getEvents()) {
String eventStr = "<event time=\"" + TextUtil.doubleToString(event.getTime())
+ "\" type=\"" + enumToXMLName(event.getType());
+ "\" type=\"" + enumToXMLName(event.getType()) + "\"";
if (event.getSource() != null) {
eventStr += "\" source=\"" + TextUtil.escapeXML(event.getSource().getID());
eventStr += " source=\"" + TextUtil.escapeXML(event.getSource().getID()) + "\"";
}
if (event.getType() == FlightEvent.Type.SIM_WARN) {
eventStr += " id=\"" + TextUtil.escapeXML(((Warning) event.getData()).getID()) + "\"";
}
if (event.getType() == FlightEvent.Type.SIM_ABORT) {
eventStr += "\" cause=\"" + enumToXMLName(((SimulationAbort)(event.getData())).getCause());
eventStr += " cause=\"" + enumToXMLName(((SimulationAbort)(event.getData())).getCause()) + "\"";
}
eventStr += "\"/>";
eventStr += "/>";
writeln(eventStr);
}
@ -649,14 +671,6 @@ public class OpenRocketSaver extends RocketSaver {
content = "";
writeln("<" + element + ">" + TextUtil.escapeXML(content) + "</" + element + ">");
}
private void writeElementWithAttribute(String element, String attributeName, String attribute, Object content) throws IOException {
content = content == null ? "" : content;
writeln("<" + element + " " + attributeName + " = \"" + attribute + "\">" + TextUtil.escapeXML(content) + "</" + element + ">");
}
private void writeln(String str) throws IOException {
if (str.length() == 0) {

View File

@ -3,6 +3,7 @@ package info.openrocket.core.file.openrocket.importt;
import java.util.HashMap;
import java.util.UUID;
import info.openrocket.core.logging.Message;
import info.openrocket.core.logging.SimulationAbort;
import info.openrocket.core.logging.SimulationAbort.Cause;
import info.openrocket.core.logging.WarningSet;
@ -132,15 +133,14 @@ class FlightDataBranchHandler extends AbstractElementHandler {
if (element.equals("event")) {
double time;
FlightEvent.Type type;
SimulationAbort abort = null;
SimulationAbort.Cause cause = null;
Message data = null;
RocketComponent source = null;
String sourceID;
try {
time = DocumentConfig.stringToDouble(attributes.get("time"));
} catch (NumberFormatException e) {
warnings.add("Illegal event specification, ignoring.");
warnings.add("Illegal event time specification, ignoring: " + e.getMessage());
return;
}
@ -157,13 +157,23 @@ class FlightDataBranchHandler extends AbstractElementHandler {
source = rocket.findComponent(UUID.fromString(sourceID));
}
// For warning events, get the warning
if (type == FlightEvent.Type.SIM_WARN) {
data = simHandler.getWarningSet().findById(UUID.fromString(attributes.get("id")));
}
// For aborts, get the cause
cause = (Cause) DocumentConfig.findEnum(attributes.get("cause"), SimulationAbort.Cause.class);
Cause cause = (Cause) DocumentConfig.findEnum(attributes.get("cause"), SimulationAbort.Cause.class);
if (cause != null) {
abort = new SimulationAbort(cause);
data = new SimulationAbort(cause);
}
branch.addEvent(new FlightEvent(type, time, source, abort));
try {
branch.addEvent(new FlightEvent(type, time, source, data));
} catch (Exception e) {
warnings.add("Illegal parameters for FlightEvent: " + e.getMessage());
}
return;
}

View File

@ -39,7 +39,7 @@ class FlightDataHandler extends AbstractElementHandler {
WarningSet warnings) {
if (element.equals("warning")) {
return PlainTextHandler.INSTANCE;
return new WarningHandler(context.getOpenRocketDocument().getRocket(), warningSet);
}
if (element.equals("databranch")) {
if (attributes.get("name") == null || attributes.get("types") == null) {
@ -83,10 +83,10 @@ class FlightDataHandler extends AbstractElementHandler {
if (branch.getLength() > 0) {
branches.add(branch);
}
} else if (element.equals("warning")) {
String priorityStr = attributes.get("priority");
MessagePriority priority = MessagePriority.fromExportLabel(priorityStr);
warningSet.add(Warning.fromString(content, priority));
// } else if (element.equals("warning")) {
// String priorityStr = attributes.get("priority");
// MessagePriority priority = MessagePriority.fromExportLabel(priorityStr);
// warningSet.add(Warning.fromString(content, priority));
}
}
@ -157,6 +157,9 @@ class FlightDataHandler extends AbstractElementHandler {
data.getWarningSet().addAll(warningSet);
data.immute();
}
public WarningSet getWarningSet() {
return warningSet;
}
}

View File

@ -156,6 +156,13 @@ class SingleSimulationHandler extends AbstractElementHandler {
doc.addSimulation(simulation);
}
/**
* @return the warning set associated with this simulation
*/
public WarningSet getWarningSet() {
return dataHandler.getWarningSet();
}
private SimulationExtension compatibilityExtension(String className) {
JavaCode extension = Application.getInjector().getInstance(JavaCode.class);
extension.setClassName(className);

View File

@ -0,0 +1,73 @@
package info.openrocket.core.file.openrocket.importt;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.UUID;
import info.openrocket.core.file.simplesax.AbstractElementHandler;
import info.openrocket.core.file.simplesax.ElementHandler;
import info.openrocket.core.file.simplesax.PlainTextHandler;
import info.openrocket.core.logging.MessagePriority;
import info.openrocket.core.logging.Warning;
import info.openrocket.core.logging.WarningSet;
import info.openrocket.core.rocketcomponent.Rocket;
import info.openrocket.core.rocketcomponent.RocketComponent;
class WarningHandler extends AbstractElementHandler {
private Rocket rocket;
private WarningSet warningSet;
private Warning warning;
private UUID id = UUID.randomUUID();
private MessagePriority priority = MessagePriority.NORMAL;
private ArrayList<RocketComponent> sources = new ArrayList<>();
private String warningText;
public WarningHandler(Rocket rocket, WarningSet warningSet) {
this.rocket = rocket;
this.warningSet = warningSet;
}
@Override
public ElementHandler openElement(String element, HashMap<String, String> attributes,
WarningSet warnings) {
return PlainTextHandler.INSTANCE;
}
@Override
public void closeElement(String element, HashMap<String, String> attributes,
String content, WarningSet warnings) {
if (element.equals("id")) {
id = UUID.fromString(content);
} else if (element.equals("description")) {
warning = Warning.fromString(content);
} else if (element.equals("priority")) {
priority = MessagePriority.fromExportLabel(content);
} else if (element.equals("source")) {
RocketComponent component = rocket.findComponent(UUID.fromString(content));
sources.add(component);
} else {
warnings.add("Unknown element '" + element + "', ignoring.");
}
}
@Override
public void endHandler(String element, HashMap<String, String> attributes,
String content, WarningSet warnings) {
// If we didn't already create a warning, this came from an old version
if (null == warning) {
warning = Warning.fromString(content.trim());
}
if (null != id) {
warning.setID(id);
}
if (null != priority) {
warning.setPriority(priority);
}
if (null != sources) {
warning.setSources(sources.toArray(new RocketComponent[0]));
}
warningSet.add(warning);
}
}

View File

@ -80,4 +80,4 @@ public interface ElementHandler {
public abstract void endHandler(String element, HashMap<String, String> attributes,
String content, WarningSet warnings) throws SAXException;
}
}

View File

@ -73,6 +73,13 @@ public abstract class Message implements Cloneable {
public UUID getID() {
return id;
}
/**
* Set the ID
**/
public void setID(UUID id) {
this.id = id;
}
/**
* Return the rocket component(s) that are the source of this warning.

View File

@ -1,5 +1,10 @@
package info.openrocket.core.logging;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import info.openrocket.core.rocketcomponent.RocketComponent;
import info.openrocket.core.util.ArrayList;
import info.openrocket.core.util.BugException;
@ -7,10 +12,6 @@ import info.openrocket.core.util.ModID;
import info.openrocket.core.util.Monitorable;
import info.openrocket.core.util.Mutable;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.List;
/**
* A set that contains multiple <code>Message</code>s. When adding a
* {@link Message} to this set, the contents is checked for a message of the
@ -69,7 +70,7 @@ public abstract class MessageSet<E extends Message> extends AbstractSet<E> imple
* @param sources the sources of the message (rocket components that caused the message)
*
*/
public boolean add(E m, RocketComponent... sources) {
public boolean add(E m, RocketComponent... sources) {
mutable.check();
try {
m = (E) m.clone();
@ -78,7 +79,7 @@ public abstract class MessageSet<E extends Message> extends AbstractSet<E> imple
}
m.setSources(sources);
return add(m);
}
}
/**
* Add a <code>Message</code> of the specified type with the specified discriminator to the
@ -149,6 +150,14 @@ public abstract class MessageSet<E extends Message> extends AbstractSet<E> imple
return list;
}
public Message findById(UUID id) {
for (Message m : messages) {
if (m.id.equals(id))
return m;
}
throw new BugException("Message with id " + id + " not found");
}
public void immute() {
mutable.immute();
}

View File

@ -18,7 +18,8 @@ public abstract class Warning extends Message {
* @return a Message with the specific text and priority.
*/
public static Warning fromString(String text, MessagePriority priority) {
return new Warning.Other(text, priority);
Warning.Other warn = new Warning.Other(text, priority);
return warn;
}
/**
@ -27,8 +28,6 @@ public abstract class Warning extends Message {
public static Warning fromString(String text) {
return fromString(text, MessagePriority.NORMAL);
}
///////////// Specific warning classes /////////////