From 53e2db3b2421889c039a4c843c4b594e16eaeb41 Mon Sep 17 00:00:00 2001
From: "Richard Flay (Hyper)" <hyperreality.au+openpilot@gmail.com>
Date: Sat, 8 Sep 2012 14:41:55 +0930
Subject: [PATCH] Added a backwards-compatible way to specify UAVO field
 element names and options as lists of child elements of the field element.
 Added the ability to specify that a field is a clone of another field, just
 with a different name

---
 ground/uavobjgenerator/main.cpp              |  3 +
 ground/uavobjgenerator/uavobjectparser.cpp   | 96 +++++++++++++++++---
 shared/uavobjectdefinition/mixersettings.xml | 93 ++++++++++++++-----
 shared/uavobjectdefinition/taskinfo.xml      | 73 ++++++++++++++-
 4 files changed, 224 insertions(+), 41 deletions(-)

diff --git a/ground/uavobjgenerator/main.cpp b/ground/uavobjgenerator/main.cpp
index 725d65cd3..8bce4fd09 100644
--- a/ground/uavobjgenerator/main.cpp
+++ b/ground/uavobjgenerator/main.cpp
@@ -161,6 +161,9 @@ int main(int argc, char *argv[])
         QString res = parser->parseXML(xmlstr, filename);
 
         if (!res.isNull()) {
+	    if (!verbose) {
+               cout << "Error in XML file: " << fileinfo.fileName().toStdString() << endl;
+            }
             cout << "Error parsing " << res.toStdString() << endl;
             return RETURN_ERR_XML;
         }
diff --git a/ground/uavobjgenerator/uavobjectparser.cpp b/ground/uavobjgenerator/uavobjectparser.cpp
index 73c4833b0..2d1d28c66 100644
--- a/ground/uavobjgenerator/uavobjectparser.cpp
+++ b/ground/uavobjgenerator/uavobjectparser.cpp
@@ -213,6 +213,9 @@ QString UAVObjectParser::parseXML(QString& xml, QString& filename)
         qStableSort(info->fields.begin(), info->fields.end(), fieldTypeLessThan);
 
         // Make sure that required elements were found
+        if ( !fieldFound )
+            return QString("Object::field element is missing");
+
         if ( !accessFound )
             return QString("Object::access element is missing");
 
@@ -381,11 +384,38 @@ QString UAVObjectParser::processObjectFields(QDomNode& childNode, ObjectInfo* in
     // Get name attribute
     QDomNamedNodeMap elemAttributes = childNode.attributes();
     QDomNode elemAttr = elemAttributes.namedItem("name");
-    if ( elemAttr.isNull() )
+    if (elemAttr.isNull()) {
         return QString("Object:field:name attribute is missing");
+    }
+    QString name = elemAttr.nodeValue();
 
-    field->name = elemAttr.nodeValue();
-
+    // Check to see is this field is a clone of another
+    // field that has already been declared
+    elemAttr = elemAttributes.namedItem("cloneof");
+    if (!elemAttr.isNull()) {
+        QString parentName = elemAttr.nodeValue(); 
+        if (!parentName.isEmpty()) {
+	    foreach(FieldInfo * parent, info->fields) {
+                if (parent->name == parentName) {
+                    // clone from this parent
+                    *field = *parent;   // safe shallow copy, no ptrs in struct
+                    field->name = name; // set our name
+                    // Add field to object
+                    info->fields.append(field);
+                    // Done
+                    return QString();
+                }
+            }
+            return QString("Object:field::cloneof parent unknown");
+        }
+        else {
+            return QString("Object:field:cloneof attribute is empty");
+        }
+    }
+    else {
+        // this field is not a clone, so remember its name
+        field->name = name;
+    }
 
     // Get units attribute
     elemAttr = elemAttributes.namedItem("units");
@@ -410,6 +440,8 @@ QString UAVObjectParser::processObjectFields(QDomNode& childNode, ObjectInfo* in
     }
 
     // Get numelements or elementnames attribute
+    field->numElements = 0;
+    // Look for element names as an attribute first
     elemAttr = elemAttributes.namedItem("elementnames");
     if ( !elemAttr.isNull() ) {
         // Get element names
@@ -422,9 +454,26 @@ QString UAVObjectParser::processObjectFields(QDomNode& childNode, ObjectInfo* in
         field->defaultElementNames = false;
     }
     else {
+        // Look for a list of child elementname nodes
+        QDomNode listNode = childNode.firstChildElement("elementnames");
+        if (!listNode.isNull()) {
+            for (QDomElement node = listNode.firstChildElement("elementname");
+                 !node.isNull(); node = node.nextSiblingElement("elementname")) {
+		QDomNode name = node.firstChild();
+                if (!name.isNull() && name.isText() && !name.nodeValue().isEmpty()) {
+                    field->elementNames.append(name.nodeValue());
+                }
+            }
+	    field->numElements = field->elementNames.length();
+            field->defaultElementNames = false;
+        }
+    }
+    // If no element names were found, then fall back to looking
+    // for the number of elements in the 'elements' attribute
+    if (field->numElements == 0) {
         elemAttr = elemAttributes.namedItem("elements");
         if ( elemAttr.isNull() ) {
-            return QString("Object:field:elements and Object:field:elementnames attribute is missing");
+            return QString("Object:field:elements and Object:field:elementnames attribute/element is missing");
         }
         else {
             field->numElements = elemAttr.nodeValue().toInt();
@@ -434,19 +483,34 @@ QString UAVObjectParser::processObjectFields(QDomNode& childNode, ObjectInfo* in
             field->defaultElementNames = true;
         }
     }
-    // Get options attribute (only if an enum type)
+    // Get options attribute or child elements (only if an enum type)
     if (field->type == FIELDTYPE_ENUM) {
 
-        // Get options attribute
+        // Look for options attribute
         elemAttr = elemAttributes.namedItem("options");
-        if ( elemAttr.isNull() )
-            return QString("Object:field:options attribute is missing");
-
-        QStringList options = elemAttr.nodeValue().split(",", QString::SkipEmptyParts);
-        for (int n = 0; n < options.length(); ++n)
-            options[n] = options[n].trimmed();
-
-        field->options = options;
+        if (!elemAttr.isNull()) {
+            QStringList options = elemAttr.nodeValue().split(",", QString::SkipEmptyParts);
+            for (int n = 0; n < options.length(); ++n) {
+                options[n] = options[n].trimmed();
+            }
+            field->options = options;
+        }
+	else {
+	    // Look for a list of child 'option' nodes
+	    QDomNode listNode = childNode.firstChildElement("options");
+	    if (!listNode.isNull()) {
+	        for (QDomElement node = listNode.firstChildElement("option");
+	             !node.isNull(); node = node.nextSiblingElement("option")) {
+		    QDomNode name = node.firstChild();
+                    if (!name.isNull() && name.isText() && !name.nodeValue().isEmpty()) {
+	                field->options.append(name.nodeValue());
+	            }
+	        }
+	    }
+        }
+        if (field->options.length() == 0) {
+            return QString("Object:field:options attribute/element is missing");
+        }
     }
 
     // Get the default value attribute (required for settings objects, optional for the rest)
@@ -466,12 +530,14 @@ QString UAVObjectParser::processObjectFields(QDomNode& childNode, ObjectInfo* in
 				return QString("Object:field:incorrect number of default values");
 
 			/*support legacy single default for multiple elements
-			We sould really issue a warning*/
+			We should really issue a warning*/
 			for(int ct=1; ct< field->numElements; ct++)
 				defaults.append(defaults[0]);
 		}
 		field->defaultValues = defaults;
     }
+
+    // Limits attribute
     elemAttr = elemAttributes.namedItem("limits");
     if ( elemAttr.isNull() ) {
         field->limitValues=QString();
diff --git a/shared/uavobjectdefinition/mixersettings.xml b/shared/uavobjectdefinition/mixersettings.xml
index 6e0d519f1..ffbc067f9 100644
--- a/shared/uavobjectdefinition/mixersettings.xml
+++ b/shared/uavobjectdefinition/mixersettings.xml
@@ -5,29 +5,76 @@
         <field name="FeedForward" units="" type="float" elements="1" defaultvalue="0"/>
         <field name="AccelTime" units="ms" type="float" elements="1" defaultvalue="0"/>
         <field name="DecelTime" units="ms" type="float" elements="1" defaultvalue="0"/>
-	<field name="ThrottleCurve1" units="percent" type="float" elements="5" elementnames="0,25,50,75,100" defaultvalue="0,0,0,0,0"/>
-        <field name="Curve2Source" units="" type="enum" elements="1" options="Throttle,Roll,Pitch,Yaw,Collective,Accessory0,Accessory1,Accessory2,Accessory3,Accessory4,Accessory5" defaultvalue="Throttle"/>
-        <field name="ThrottleCurve2" units="percent" type="float" elements="5" elementnames="0,25,50,75,100" defaultvalue="0,0.25,0.5,0.75,1"/>
-        <field name="Mixer1Type" units="" type="enum" elements="1" options="Disabled,Motor,Servo,CameraRoll,CameraPitch,CameraYaw,Accessory0,Accessory1,Accessory2,Accessory3,Accessory4,Accessory5" defaultvalue="Disabled"/>
-        <field name="Mixer1Vector" units="" type="int8" elements="5" elementnames="ThrottleCurve1,ThrottleCurve2,Roll,Pitch,Yaw" defaultvalue="0"/>
-        <field name="Mixer2Type" units="" type="enum" elements="1" options="Disabled,Motor,Servo,CameraRoll,CameraPitch,CameraYaw,Accessory0,Accessory1,Accessory2,Accessory3,Accessory4,Accessory5" defaultvalue="Disabled"/>
-        <field name="Mixer2Vector" units="" type="int8" elements="5" elementnames="ThrottleCurve1,ThrottleCurve2,Roll,Pitch,Yaw" defaultvalue="0"/>
-        <field name="Mixer3Type" units="" type="enum" elements="1" options="Disabled,Motor,Servo,CameraRoll,CameraPitch,CameraYaw,Accessory0,Accessory1,Accessory2,Accessory3,Accessory4,Accessory5" defaultvalue="Disabled"/>
-        <field name="Mixer3Vector" units="" type="int8" elements="5" elementnames="ThrottleCurve1,ThrottleCurve2,Roll,Pitch,Yaw" defaultvalue="0"/>
-        <field name="Mixer4Type" units="" type="enum" elements="1" options="Disabled,Motor,Servo,CameraRoll,CameraPitch,CameraYaw,Accessory0,Accessory1,Accessory2,Accessory3,Accessory4,Accessory5" defaultvalue="Disabled"/>
-        <field name="Mixer4Vector" units="" type="int8" elements="5" elementnames="ThrottleCurve1,ThrottleCurve2,Roll,Pitch,Yaw" defaultvalue="0"/>
-        <field name="Mixer5Type" units="" type="enum" elements="1" options="Disabled,Motor,Servo,CameraRoll,CameraPitch,CameraYaw,Accessory0,Accessory1,Accessory2,Accessory3,Accessory4,Accessory5" defaultvalue="Disabled"/>
-        <field name="Mixer5Vector" units="" type="int8" elements="5" elementnames="ThrottleCurve1,ThrottleCurve2,Roll,Pitch,Yaw" defaultvalue="0"/>
-        <field name="Mixer6Type" units="" type="enum" elements="1" options="Disabled,Motor,Servo,CameraRoll,CameraPitch,CameraYaw,Accessory0,Accessory1,Accessory2,Accessory3,Accessory4,Accessory5" defaultvalue="Disabled"/>
-        <field name="Mixer6Vector" units="" type="int8" elements="5" elementnames="ThrottleCurve1,ThrottleCurve2,Roll,Pitch,Yaw" defaultvalue="0"/>
-        <field name="Mixer7Type" units="" type="enum" elements="1" options="Disabled,Motor,Servo,CameraRoll,CameraPitch,CameraYaw,Accessory0,Accessory1,Accessory2,Accessory3,Accessory4,Accessory5" defaultvalue="Disabled"/>
-        <field name="Mixer7Vector" units="" type="int8" elements="5" elementnames="ThrottleCurve1,ThrottleCurve2,Roll,Pitch,Yaw" defaultvalue="0"/>
-        <field name="Mixer8Type" units="" type="enum" elements="1" options="Disabled,Motor,Servo,CameraRoll,CameraPitch,CameraYaw,Accessory0,Accessory1,Accessory2,Accessory3,Accessory4,Accessory5" defaultvalue="Disabled"/>
-        <field name="Mixer8Vector" units="" type="int8" elements="5" elementnames="ThrottleCurve1,ThrottleCurve2,Roll,Pitch,Yaw" defaultvalue="0"/>
-        <field name="Mixer9Type" units="" type="enum" elements="1" options="Disabled,Motor,Servo,CameraRoll,CameraPitch,CameraYaw,Accessory0,Accessory1,Accessory2,Accessory3,Accessory4,Accessory5" defaultvalue="Disabled"/>
-        <field name="Mixer9Vector" units="" type="int8" elements="5" elementnames="ThrottleCurve1,ThrottleCurve2,Roll,Pitch,Yaw" defaultvalue="0"/>
-        <field name="Mixer10Type" units="" type="enum" elements="1" options="Disabled,Motor,Servo,CameraRoll,CameraPitch,CameraYaw,Accessory0,Accessory1,Accessory2,Accessory3,Accessory4,Accessory5" defaultvalue="Disabled"/>
-        <field name="Mixer10Vector" units="" type="int8" elements="5" elementnames="ThrottleCurve1,ThrottleCurve2,Roll,Pitch,Yaw" defaultvalue="0"/>
+	<field name="ThrottleCurve1" units="percent" type="float" elementnames="0,25,50,75,100" defaultvalue="0,0,0,0,0"/>
+        <field name="Curve2Source" units="" type="enum" elements="1" defaultvalue="Throttle">
+		<options>
+			<option>Throttle</option>
+		        <option>Roll</option>
+		        <option>Pitch</option>
+		        <option>Yaw</option>
+		        <option>Collective</option>
+		        <option>Accessory0</option>
+		        <option>Accessory1</option>
+		        <option>Accessory2</option>
+		        <option>Accessory3</option>
+		        <option>Accessory4</option>
+		        <option>Accessory5</option>
+		</options>
+	</field>
+        <field name="ThrottleCurve2" units="percent" type="float" elementnames="0,25,50,75,100" defaultvalue="0,0.25,0.5,0.75,1"/>
+	<field name="Mixer1Type" units="" type="enum" elements="1" defaultvalue="Disabled">
+		<options>
+			<option>Disabled</option>
+			<option>Motor</option>
+			<option>Servo</option>
+			<option>CameraRoll</option>
+			<option>CameraPitch</option>
+			<option>CameraYaw</option>
+			<option>Accessory0</option>
+			<option>Accessory1</option>
+			<option>Accessory2</option>
+			<option>Accessory3</option>
+			<option>Accessory4</option>
+			<option>Accessory5</option>
+		</options>
+	</field>
+	<field name="Mixer1Vector" units="" type="int8" defaultvalue="0">
+		<elementnames>
+			<elementname>ThrottleCurve1</elementname>
+			<elementname>ThrottleCurve2</elementname>
+			<elementname>Roll</elementname>
+			<elementname>Pitch</elementname>
+			<elementname>Yaw</elementname>
+		</elementnames>
+	</field>
+
+	<field name="Mixer2Type"   cloneof="Mixer1Type"/>
+	<field name="Mixer2Vector" cloneof="Mixer1Vector"/>
+
+	<field name="Mixer3Type"   cloneof="Mixer1Type"/>
+	<field name="Mixer3Vector" cloneof="Mixer1Vector"/>
+
+	<field name="Mixer4Type"   cloneof="Mixer1Type"/>
+	<field name="Mixer4Vector" cloneof="Mixer1Vector"/>
+
+	<field name="Mixer5Type"   cloneof="Mixer1Type"/>
+	<field name="Mixer5Vector" cloneof="Mixer1Vector"/>
+
+	<field name="Mixer6Type"   cloneof="Mixer1Type"/>
+	<field name="Mixer6Vector" cloneof="Mixer1Vector"/>
+
+	<field name="Mixer7Type"   cloneof="Mixer1Type"/>
+	<field name="Mixer7Vector" cloneof="Mixer1Vector"/>
+
+	<field name="Mixer8Type"   cloneof="Mixer1Type"/>
+	<field name="Mixer8Vector" cloneof="Mixer1Vector"/>
+
+	<field name="Mixer9Type"   cloneof="Mixer1Type"/>
+	<field name="Mixer9Vector" cloneof="Mixer1Vector"/>
+
+	<field name="Mixer10Type"   cloneof="Mixer1Type"/>
+	<field name="Mixer10Vector" cloneof="Mixer1Vector"/>
+
         <access gcs="readwrite" flight="readwrite"/>
         <telemetrygcs acked="true" updatemode="onchange" period="0"/>
         <telemetryflight acked="true" updatemode="onchange" period="0"/>
diff --git a/shared/uavobjectdefinition/taskinfo.xml b/shared/uavobjectdefinition/taskinfo.xml
index 551167501..e4f2ae652 100644
--- a/shared/uavobjectdefinition/taskinfo.xml
+++ b/shared/uavobjectdefinition/taskinfo.xml
@@ -1,9 +1,76 @@
 <xml>
     <object name="TaskInfo" singleinstance="true" settings="false">
         <description>Task information</description>
-        <field name="StackRemaining" units="bytes" type="uint16" elementnames="System,Actuator,Attitude,Sensors,TelemetryTx,TelemetryTxPri,TelemetryRx,GPS,ManualControl,Altitude,Stabilization,AltitudeHold,Guidance,FlightPlan,Com2UsbBridge,Usb2ComBridge,OveroSync,EventDispatcher"/>
-	<field name="Running" units="bool" type="enum" options="False,True" elementnames="System,Actuator,Attitude,Sensors,TelemetryTx,TelemetryTxPri,TelemetryRx,GPS,ManualControl,Altitude,Stabilization,AltitudeHold,Guidance,FlightPlan,Com2UsbBridge,Usb2ComBridge,OveroSync,EventDispatcher"/>
-	<field name="RunningTime" units="%" type="uint8" elementnames="System,Actuator,Attitude,Sensors,TelemetryTx,TelemetryTxPri,TelemetryRx,GPS,ManualControl,Altitude,Stabilization,AltitudeHold,Guidance,FlightPlan,Com2UsbBridge,Usb2ComBridge,OveroSync,EventDispatcher"/>
+        <field name="StackRemaining" units="bytes" type="uint16">
+		<elementnames>
+			<elementname>System</elementname>
+			<elementname>Actuator</elementname>
+			<elementname>Attitude</elementname>
+			<elementname>Sensors</elementname>
+			<elementname>TelemetryTx</elementname>
+			<elementname>TelemetryTxPri</elementname>
+			<elementname>TelemetryRx</elementname>
+			<elementname>GPS</elementname>
+			<elementname>ManualControl</elementname>
+			<elementname>Altitude</elementname>
+			<elementname>Stabilization</elementname>
+			<elementname>AltitudeHold</elementname>
+			<elementname>Guidance</elementname>
+			<elementname>FlightPlan</elementname>
+			<elementname>Com2UsbBridge</elementname>
+			<elementname>Usb2ComBridge</elementname>
+			<elementname>OveroSync</elementname>
+			<elementname>EventDispatcher</elementname>
+		</elementnames>
+	</field> 
+	<field name="Running" units="bool" type="enum">
+		<elementnames>
+			<elementname>System</elementname>
+			<elementname>Actuator</elementname>
+			<elementname>Attitude</elementname>
+			<elementname>Sensors</elementname>
+			<elementname>TelemetryTx</elementname>
+			<elementname>TelemetryTxPri</elementname>
+			<elementname>TelemetryRx</elementname>
+			<elementname>GPS</elementname>
+			<elementname>ManualControl</elementname>
+			<elementname>Altitude</elementname>
+			<elementname>Stabilization</elementname>
+			<elementname>AltitudeHold</elementname>
+			<elementname>Guidance</elementname>
+			<elementname>FlightPlan</elementname>
+			<elementname>Com2UsbBridge</elementname>
+			<elementname>Usb2ComBridge</elementname>
+			<elementname>OveroSync</elementname>
+			<elementname>EventDispatcher</elementname>
+		</elementnames>
+		<options>
+			<option>False</option>
+			<option>True</option>
+		</options>
+	</field>
+	<field name="RunningTime" units="%" type="uint8">
+		<elementnames>
+			<elementname>System</elementname>
+			<elementname>Actuator</elementname>
+			<elementname>Attitude</elementname>
+			<elementname>Sensors</elementname>
+			<elementname>TelemetryTx</elementname>
+			<elementname>TelemetryTxPri</elementname>
+			<elementname>TelemetryRx</elementname>
+			<elementname>GPS</elementname>
+			<elementname>ManualControl</elementname>
+			<elementname>Altitude</elementname>
+			<elementname>Stabilization</elementname>
+			<elementname>AltitudeHold</elementname>
+			<elementname>Guidance</elementname>
+			<elementname>FlightPlan</elementname>
+			<elementname>Com2UsbBridge</elementname>
+			<elementname>Usb2ComBridge</elementname>
+			<elementname>OveroSync</elementname>
+			<elementname>EventDispatcher</elementname>
+		</elementnames>
+	</field> 
         <access gcs="readwrite" flight="readwrite"/>
         <telemetrygcs acked="true" updatemode="onchange" period="0"/>
         <telemetryflight acked="true" updatemode="periodic" period="10000"/>