/// <summary> /// Saves a UscSettings object to a textfile. /// </summary> /// <param name="settings">The settings to read from.</param> /// <param name="sw">The file to write to.</param> public static void save(UscSettings settings, StreamWriter sw) { XmlWriter writer = XmlWriter.Create(sw); writer.Settings.Indent = true; writer.WriteComment("Pololu Maestro servo controller settings file, http://www.pololu.com/catalog/product/1350"); writer.WriteStartElement("UscSettings"); writer.WriteAttributeString("version", "1"); // XML file version, so that we can parse old XML types in the future writer.WriteElementString("NeverSuspend", settings.neverSuspend ? "true" : "false"); writer.WriteElementString("SerialMode", settings.serialMode.ToString().Replace("SERIAL_MODE_", "")); writer.WriteElementString("FixedBaudRate", settings.fixedBaudRate.ToString()); writer.WriteElementString("SerialTimeout", settings.serialTimeout.ToString()); writer.WriteElementString("EnableCrc", settings.enableCrc ? "true" : "false"); writer.WriteElementString("SerialDeviceNumber", settings.serialDeviceNumber.ToString()); writer.WriteElementString("SerialMiniSscOffset", settings.miniSscOffset.ToString()); if (settings.servoCount > 18) { writer.WriteElementString("EnablePullups", settings.enablePullups ? "true" : "false"); } writer.WriteStartElement("Channels"); // Attributes of the Channels tag if (settings.servoCount == 6) { writer.WriteAttributeString("ServosAvailable", settings.servosAvailable.ToString()); writer.WriteAttributeString("ServoPeriod", settings.servoPeriod.ToString()); } else { writer.WriteAttributeString("MiniMaestroServoPeriod", settings.miniMaestroServoPeriod.ToString()); writer.WriteAttributeString("ServoMultiplier", settings.servoMultiplier.ToString()); } writer.WriteComment("Period = " + (settings.periodInMicroseconds / 1000M).ToString() + " ms"); for (byte i = 0; i < settings.servoCount; i++) { ChannelSetting setting = settings.channelSettings[i]; writer.WriteComment("Channel " + i.ToString()); writer.WriteStartElement("Channel"); writer.WriteAttributeString("name", setting.name); writer.WriteAttributeString("mode", setting.mode.ToString()); writer.WriteAttributeString("min", setting.minimum.ToString()); writer.WriteAttributeString("max", setting.maximum.ToString()); writer.WriteAttributeString("homemode", setting.homeMode.ToString()); writer.WriteAttributeString("home", setting.home.ToString()); writer.WriteAttributeString("speed", setting.speed.ToString()); writer.WriteAttributeString("acceleration", setting.acceleration.ToString()); writer.WriteAttributeString("neutral", setting.neutral.ToString()); writer.WriteAttributeString("range", setting.range.ToString()); writer.WriteEndElement(); } writer.WriteEndElement(); writer.WriteStartElement("Sequences"); foreach (Sequence sequence in settings.sequences) { writer.WriteStartElement("Sequence"); writer.WriteAttributeString("name", sequence.name); foreach (Frame frame in sequence.frames) { frame.writeXml(writer); } writer.WriteEndElement(); } writer.WriteEndElement(); // end sequences writer.WriteStartElement("Script"); writer.WriteAttributeString("ScriptDone", settings.scriptDone ? "true" : "false"); writer.WriteString(settings.script); writer.WriteEndElement(); // end script writer.WriteEndElement(); // End UscSettings tag. }
/// <summary> /// Parses a saved configuration file and returns a UscSettings object. /// </summary> /// <param name="warnings">A list of warnings. Whenever something goes /// wrong with the file loading, a warning will be added to this list. /// The warnings are not fatal; if the function returns it will return /// a valid UscSettings object. /// </param> /// <param name="sr">The file to read from.</param> /// <remarks>This function is messy. Maybe I should have tried the XPath /// library.</remarks> public static UscSettings load(StreamReader sr, List <String> warnings) { XmlReader reader = XmlReader.Create(sr); UscSettings settings = new UscSettings(); string script = ""; // The x prefix means "came directly from XML" Dictionary <String, String> xParams = new Dictionary <string, string>(); // Only read the data inside the UscSettings element. reader.ReadToFollowing("UscSettings"); readAttributes(reader, xParams); reader = reader.ReadSubtree(); // Check the version number if (!xParams.ContainsKey("version")) { warnings.Add("This file has no version number, so it might have been read incorrectly."); } else if (xParams["version"] != "1") { warnings.Add("Unrecognized settings file version \"" + xParams["version"] + "\"."); } reader.Read(); // this is needed, otherwise the first tag inside uscSettings doesn't work work (not sure why) while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element && reader.Name == "Channels") { // We found the Channels tag. // Read the ServosAvailable and ServoPeriod attributes from it in to our collection. readAttributes(reader, xParams); // Make a reader that can only read the stuff inside the Channels tag. var channelsReader = reader.ReadSubtree(); // For each Channel tag... while (channelsReader.ReadToFollowing("Channel")) { // Read all the attributes. Dictionary <String, String> xChannel = new Dictionary <string, string>(); readAttributes(channelsReader, xChannel); // Transform the attributes in to a ChannelSetting object. ChannelSetting cs = new ChannelSetting(); if (assertKey("name", xChannel, warnings)) { cs.name = xChannel["name"]; } if (assertKey("mode", xChannel, warnings)) { switch (xChannel["mode"].ToLowerInvariant()) { case "servomultiplied": cs.mode = ChannelMode.ServoMultiplied; break; case "servo": cs.mode = ChannelMode.Servo; break; case "input": cs.mode = ChannelMode.Input; break; case "output": cs.mode = ChannelMode.Output; break; default: warnings.Add("Invalid mode \"" + xChannel["mode"] + "\"."); break; } } if (assertKey("homemode", xChannel, warnings)) { switch (xChannel["homemode"].ToLowerInvariant()) { case "goto": cs.homeMode = HomeMode.Goto; break; case "off": cs.homeMode = HomeMode.Off; break; case "ignore": cs.homeMode = HomeMode.Ignore; break; default: warnings.Add("Invalid homemode \"" + xChannel["homemode"] + "\"."); break; } } if (assertKey("min", xChannel, warnings)) { parseU16(xChannel["min"], ref cs.minimum, "min", warnings); } if (assertKey("max", xChannel, warnings)) { parseU16(xChannel["max"], ref cs.maximum, "max", warnings); } if (assertKey("home", xChannel, warnings)) { parseU16(xChannel["home"], ref cs.home, "home", warnings); } if (assertKey("speed", xChannel, warnings)) { parseU16(xChannel["speed"], ref cs.speed, "speed", warnings); } if (assertKey("acceleration", xChannel, warnings)) { parseU8(xChannel["acceleration"], ref cs.acceleration, "acceleration", warnings); } if (assertKey("neutral", xChannel, warnings)) { parseU16(xChannel["neutral"], ref cs.neutral, "neutral", warnings); } if (assertKey("range", xChannel, warnings)) { parseU16(xChannel["range"], ref cs.range, "range", warnings); } settings.channelSettings.Add(cs); } if (channelsReader.ReadToFollowing("Channel")) { warnings.Add("More than " + settings.servoCount + " channel elements were found. The extra elements have been discarded."); } } else if (reader.NodeType == XmlNodeType.Element && reader.Name == "Sequences") { // We found the Sequences tag. // For each Sequence tag in this sequence... var sequencesReader = reader.ReadSubtree(); while (sequencesReader.ReadToFollowing("Sequence")) { // Create a new sequence. Sequence sequence = new Sequence(); settings.sequences.Add(sequence); // Read the sequence tag attributes (should just be "name"). Dictionary <String, String> sequenceAttributes = new Dictionary <string, string>(); readAttributes(sequencesReader, sequenceAttributes); if (sequenceAttributes.ContainsKey("name")) { sequence.name = sequenceAttributes["name"]; } else { sequence.name = "Sequence " + settings.sequences.Count; warnings.Add("No name found for sequence " + sequence.name + "."); } // For each frame tag in this sequence... var framesReader = reader.ReadSubtree(); while (framesReader.ReadToFollowing("Frame")) { // Create a new frame. Frame frame = new Frame(); sequence.frames.Add(frame); // Read the frame attributes from XML (name, duration) Dictionary <String, String> frameAttributes = new Dictionary <string, string>(); readAttributes(framesReader, frameAttributes); if (frameAttributes.ContainsKey("name")) { frame.name = frameAttributes["name"]; } else { frame.name = "Frame " + sequence.frames.Count; warnings.Add("No name found for " + frame.name + " in sequence \"" + sequence.name + "\"."); } if (frameAttributes.ContainsKey("duration")) { parseU16(frameAttributes["duration"], ref frame.length_ms, "Duration for frame \"" + frame.name + "\" in sequence \"" + sequence.name + "\".", warnings); } else { frame.name = "Frame " + sequence.frames.Count; warnings.Add("No duration found for frame \"" + frame.name + "\" in sequence \"" + sequence.name + "\"."); } frame.setTargetsFromString(reader.ReadElementContentAsString(), settings.servoCount); } } } else if (reader.NodeType == XmlNodeType.Element && reader.Name == "Script") { // We found the <Script> tag. // Get the ScriptDone attribute in to our dictionary. readAttributes(reader, xParams); // Read the script. script = reader.ReadElementContentAsString(); } else if (reader.NodeType == XmlNodeType.Element) { // Read the miscellaneous parameters that come in element tags, like <NeverSuspend>false</NeverSuspend>. try { xParams[reader.Name] = reader.ReadElementContentAsString(); } catch (XmlException e) { warnings.Add("Unable to parse element \"" + reader.Name + "\": " + e.Message); } } } reader.Dispose(); //// Step 2: Put the data in to the settings object. try { settings.setAndCompileScript(script); } catch (Exception e) { warnings.Add("Error compiling script from XML file: " + e.Message); settings.scriptInconsistent = true; } if (assertKey("NeverSuspend", xParams, warnings)) { parseBool(xParams["NeverSuspend"], ref settings.neverSuspend, "NeverSuspend", warnings); } if (assertKey("SerialMode", xParams, warnings)) { switch (xParams["SerialMode"]) { default: settings.serialMode = uscSerialMode.SERIAL_MODE_UART_DETECT_BAUD_RATE; break; case "UART_FIXED_BAUD_RATE": settings.serialMode = uscSerialMode.SERIAL_MODE_UART_FIXED_BAUD_RATE; break; case "USB_DUAL_PORT": settings.serialMode = uscSerialMode.SERIAL_MODE_USB_DUAL_PORT; break; case "USB_CHAINED": settings.serialMode = uscSerialMode.SERIAL_MODE_USB_CHAINED; break; } } if (assertKey("FixedBaudRate", xParams, warnings)) { parseU32(xParams["FixedBaudRate"], ref settings.fixedBaudRate, "FixedBaudRate", warnings); } if (assertKey("SerialTimeout", xParams, warnings)) { parseU16(xParams["SerialTimeout"], ref settings.serialTimeout, "SerialTimeout", warnings); } if (assertKey("EnableCrc", xParams, warnings)) { parseBool(xParams["EnableCrc"], ref settings.enableCrc, "EnableCrc", warnings); } if (assertKey("SerialDeviceNumber", xParams, warnings)) { parseU8(xParams["SerialDeviceNumber"], ref settings.serialDeviceNumber, "SerialDeviceNumber", warnings); } if (assertKey("SerialMiniSscOffset", xParams, warnings)) { parseU8(xParams["SerialMiniSscOffset"], ref settings.miniSscOffset, "SerialMiniSscOffset", warnings); } if (assertKey("ScriptDone", xParams, warnings)) { parseBool(xParams["ScriptDone"], ref settings.scriptDone, "ScriptDone", warnings); } // These parameters are optional because they don't apply to all Maestros. if (xParams.ContainsKey("ServosAvailable")) { parseU8(xParams["ServosAvailable"], ref settings.servosAvailable, "ServosAvailable", warnings); } if (xParams.ContainsKey("ServoPeriod")) { parseU8(xParams["ServoPeriod"], ref settings.servoPeriod, "ServoPeriod", warnings); } if (xParams.ContainsKey("EnablePullups")) { parseBool(xParams["EnablePullups"], ref settings.enablePullups, "EnablePullups", warnings); } if (xParams.ContainsKey("MiniMaestroServoPeriod")) { parseU32(xParams["MiniMaestroServoPeriod"], ref settings.miniMaestroServoPeriod, "MiniMaestroServoPeriod", warnings); } if (xParams.ContainsKey("ServoMultiplier")) { parseU16(xParams["ServoMultiplier"], ref settings.servoMultiplier, "ServoMultiplier", warnings); } return(settings); }
public void fixSettings(UscSettings settings, List<string> warnings) { // Discard extra channels if needed. if (settings.servoCount > this.servoCount) { warnings.Add("The settings loaded include settings for " + settings.servoCount + " channels, but this device has only " + this.servoCount + " channels. The extra channel settings will be ignored."); settings.channelSettings.RemoveRange(this.servoCount, settings.servoCount - this.servoCount); } // Add channels if needed. if (settings.servoCount < this.servoCount) { warnings.Add("The settings loaded include settings for only " + settings.servoCount + " channels, but this device has " + this.servoCount + " channels. The other channels will be initialized with default settings."); while(settings.servoCount < this.servoCount) { ChannelSetting cs = new ChannelSetting(); if (this.microMaestro && settings.servosAvailable <= settings.servoCount) { cs.mode = ChannelMode.Input; } settings.channelSettings.Add(cs); } } // Prevent users from experiencing the bug with Ignore mode in Micro Maestro v1.00. if (settings.servoCount == 6 && firmwareVersionMajor <= 1 && firmwareVersionMinor == 0) { bool servoIgnoreWarningShown = false; foreach (ChannelSetting setting in settings.channelSettings) { if ((setting.mode == ChannelMode.Servo) && setting.homeMode == HomeMode.Ignore) { setting.homeMode = HomeMode.Off; if (!servoIgnoreWarningShown) { warnings.Add("Ignore mode does not work for servo channels on the Micro Maestro 6-Channel Servo Controller firmware versions prior to 1.01.\nYour channels will be changed to Off mode.\nVisit Pololu.com for a firmware upgrade."); servoIgnoreWarningShown = true; } } } } // Set homeMode to ignore for inputs (silently, because it's not the user's fault). foreach (ChannelSetting cs in settings.channelSettings) { switch (cs.mode) { case ChannelMode.Input: { cs.homeMode = HomeMode.Ignore; cs.minimum = 0; cs.maximum = 1024; // Should probably be 1023, but this is the tradition from the Micro Maestros. cs.speed = 0; cs.acceleration = 0; cs.neutral = 1024; cs.range = 1905; break; } case ChannelMode.Output: { cs.minimum = 3986; cs.maximum = 8000; cs.speed = 0; cs.acceleration = 0; cs.neutral = 6000; cs.range = 1905; break; } } } if (settings.serialDeviceNumber >= 128) { settings.serialDeviceNumber = 12; warnings.Add("The serial device number must be less than 128. It will be changed to 12."); } }
/// <summary> /// Gets a settings object, pulling some info from the registry and some from the device. /// If there is an inconsistency, a special flag is set. /// </summary> /// <returns></returns> public UscSettings getUscSettings() { var settings = new UscSettings(); settings.serialMode = (uscSerialMode)getRawParameter(uscParameter.PARAMETER_SERIAL_MODE); settings.fixedBaudRate = convertSpbrgToBps(getRawParameter(uscParameter.PARAMETER_SERIAL_FIXED_BAUD_RATE)); settings.enableCrc = getRawParameter(uscParameter.PARAMETER_SERIAL_ENABLE_CRC) != 0; settings.neverSuspend = getRawParameter(uscParameter.PARAMETER_SERIAL_NEVER_SUSPEND) != 0; settings.serialDeviceNumber = (byte)getRawParameter(uscParameter.PARAMETER_SERIAL_DEVICE_NUMBER); settings.miniSscOffset = (byte)getRawParameter(uscParameter.PARAMETER_SERIAL_MINI_SSC_OFFSET); settings.serialTimeout = getRawParameter(uscParameter.PARAMETER_SERIAL_TIMEOUT); settings.scriptDone = getRawParameter(uscParameter.PARAMETER_SCRIPT_DONE) != 0; if (servoCount == 6) { settings.servosAvailable = (byte)getRawParameter(uscParameter.PARAMETER_SERVOS_AVAILABLE); settings.servoPeriod = (byte)getRawParameter(uscParameter.PARAMETER_SERVO_PERIOD); } else { UInt32 tmp = (UInt32)(getRawParameter(uscParameter.PARAMETER_MINI_MAESTRO_SERVO_PERIOD_HU) << 8); tmp |= (byte)getRawParameter(uscParameter.PARAMETER_MINI_MAESTRO_SERVO_PERIOD_L); settings.miniMaestroServoPeriod = tmp; settings.servoMultiplier = (ushort)(getRawParameter(uscParameter.PARAMETER_SERVO_MULTIPLIER) + 1); } if (servoCount > 18) { settings.enablePullups = getRawParameter(uscParameter.PARAMETER_ENABLE_PULLUPS) != 0; } byte ioMask = 0; byte outputMask = 0; byte[] channelModeBytes = new Byte[6]; if (microMaestro) { ioMask = (byte)getRawParameter(uscParameter.PARAMETER_IO_MASK_C); outputMask = (byte)getRawParameter(uscParameter.PARAMETER_OUTPUT_MASK_C); } else { for (byte i = 0; i < 6; i++) { channelModeBytes[i] = (byte)getRawParameter(uscParameter.PARAMETER_CHANNEL_MODES_0_3 + i); } } for (byte i = 0; i < servoCount; i++) { // Initialize the ChannelSettings objects and // set all parameters except name and mode. ChannelSetting setting = new ChannelSetting(); if (microMaestro) { byte bitmask = (byte)(1 << channelToPort(i)); if ((ioMask & bitmask) == 0) { setting.mode = ChannelMode.Servo; } else if ((outputMask & bitmask) == 0) { setting.mode = ChannelMode.Input; } else { setting.mode = ChannelMode.Output; } } else { setting.mode = (ChannelMode)((channelModeBytes[i >> 2] >> ((i & 3)<<1)) & 3); } ushort home = getRawParameter(specifyServo(uscParameter.PARAMETER_SERVO0_HOME, i)); if (home == 0) { setting.homeMode = HomeMode.Off; setting.home = 0; } else if (home == 1) { setting.homeMode = HomeMode.Ignore; setting.home = 0; } else { setting.homeMode = HomeMode.Goto; setting.home = home; } setting.minimum = (ushort)(64 * getRawParameter(specifyServo(uscParameter.PARAMETER_SERVO0_MIN, i))); setting.maximum = (ushort)(64 * getRawParameter(specifyServo(uscParameter.PARAMETER_SERVO0_MAX, i))); setting.neutral = getRawParameter(specifyServo(uscParameter.PARAMETER_SERVO0_NEUTRAL, i)); setting.range = (ushort)(127 * getRawParameter(specifyServo(uscParameter.PARAMETER_SERVO0_RANGE, i))); setting.speed = exponentialSpeedToNormalSpeed((byte)getRawParameter(specifyServo(uscParameter.PARAMETER_SERVO0_SPEED, i))); setting.acceleration = (byte)getRawParameter(specifyServo(uscParameter.PARAMETER_SERVO0_ACCELERATION, i)); settings.channelSettings.Add(setting); } RegistryKey key = openRegistryKey(); if (key != null) { // Get names for servos from the registry. for (byte i = 0; i < servoCount; i++) { settings.channelSettings[i].name = (string)key.GetValue("servoName" + i.ToString("d2"), ""); } // Get the script from the registry string script = (string)key.GetValue("script"); if (script == null) script = ""; try { // compile it to get the checksum settings.setAndCompileScript(script); BytecodeProgram program = settings.bytecodeProgram; if (program.getByteList().Count > this.maxScriptLength) { throw new Exception(); } if (program.getCRC() != (ushort)getRawParameter(uscParameter.PARAMETER_SCRIPT_CRC)) { throw new Exception(); } } catch (Exception) { // no script found or error compiling - leave script at "" settings.scriptInconsistent = true; } // Get the sequences from the registry. settings.sequences = Sequencer.Sequence.readSequencesFromRegistry(key, servoCount); } return settings; }
/// <summary> /// Parses a saved configuration file and returns a UscSettings object. /// </summary> /// <param name="warnings">A list of warnings. Whenever something goes /// wrong with the file loading, a warning will be added to this list. /// The warnings are not fatal; if the function returns it will return /// a valid UscSettings object. /// </param> /// <param name="sr">The file to read from.</param> /// <remarks>This function is messy. Maybe I should have tried the XPath /// library.</remarks> public static UscSettings load(StreamReader sr, List<String> warnings) { XmlReader reader = XmlReader.Create(sr); UscSettings settings = new UscSettings(); string script = ""; // The x prefix means "came directly from XML" Dictionary<String, String> xParams = new Dictionary<string, string>(); // Only read the data inside the UscSettings element. reader.ReadToFollowing("UscSettings"); readAttributes(reader, xParams); reader = reader.ReadSubtree(); // Check the version number if (!xParams.ContainsKey("version")) { warnings.Add("This file has no version number, so it might have been read incorrectly."); } else if (xParams["version"] != "1") { warnings.Add("Unrecognized settings file version \"" + xParams["version"] + "\"."); } reader.Read(); // this is needed, otherwise the first tag inside uscSettings doesn't work work (not sure why) while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element && reader.Name == "Channels") { // We found the Channels tag. // Read the ServosAvailable and ServoPeriod attributes from it in to our collection. readAttributes(reader, xParams); // Make a reader that can only read the stuff inside the Channels tag. var channelsReader = reader.ReadSubtree(); // For each Channel tag... while(channelsReader.ReadToFollowing("Channel")) { // Read all the attributes. Dictionary<String, String> xChannel = new Dictionary<string, string>(); readAttributes(channelsReader, xChannel); // Transform the attributes in to a ChannelSetting object. ChannelSetting cs = new ChannelSetting(); if (assertKey("name", xChannel, warnings)) { cs.name = xChannel["name"]; } if (assertKey("mode", xChannel, warnings)) { switch (xChannel["mode"].ToLower()) { case "servomultiplied": cs.mode = ChannelMode.ServoMultiplied; break; case "servo": cs.mode = ChannelMode.Servo; break; case "input": cs.mode = ChannelMode.Input; break; case "output": cs.mode = ChannelMode.Output; break; default: warnings.Add("Invalid mode \"" + xChannel["mode"] + "\"."); break; } } if (assertKey("homemode", xChannel, warnings)) { switch (xChannel["homemode"].ToLower()) { case "goto": cs.homeMode = HomeMode.Goto; break; case "off": cs.homeMode = HomeMode.Off; break; case "ignore": cs.homeMode = HomeMode.Ignore; break; default: warnings.Add("Invalid homemode \"" + xChannel["homemode"] + "\"."); break; } } if (assertKey("min", xChannel, warnings)) { parseU16(xChannel["min"], ref cs.minimum, "min", warnings); } if (assertKey("max", xChannel, warnings)) { parseU16(xChannel["max"], ref cs.maximum, "max", warnings); } if (assertKey("home", xChannel, warnings)) { parseU16(xChannel["home"], ref cs.home, "home", warnings); } if (assertKey("speed", xChannel, warnings)) { parseU16(xChannel["speed"], ref cs.speed, "speed", warnings); } if (assertKey("acceleration", xChannel, warnings)) { parseU8(xChannel["acceleration"], ref cs.acceleration, "acceleration", warnings); } if (assertKey("neutral", xChannel, warnings)) { parseU16(xChannel["neutral"], ref cs.neutral, "neutral", warnings); } if (assertKey("range", xChannel, warnings)) { parseU16(xChannel["range"], ref cs.range, "range", warnings); } settings.channelSettings.Add(cs); } if (channelsReader.ReadToFollowing("Channel")) { warnings.Add("More than " + settings.servoCount + " channel elements were found. The extra elements have been discarded."); } } else if (reader.NodeType == XmlNodeType.Element && reader.Name == "Sequences") { // We found the Sequences tag. // For each Sequence tag in this sequence... var sequencesReader = reader.ReadSubtree(); while (sequencesReader.ReadToFollowing("Sequence")) { // Create a new sequence. Sequence sequence = new Sequence(); settings.sequences.Add(sequence); // Read the sequence tag attributes (should just be "name"). Dictionary<String, String> sequenceAttributes = new Dictionary<string, string>(); readAttributes(sequencesReader, sequenceAttributes); if (sequenceAttributes.ContainsKey("name")) { sequence.name = sequenceAttributes["name"]; } else { sequence.name = "Sequence " + settings.sequences.Count; warnings.Add("No name found for sequence " + sequence.name + "."); } // For each frame tag in this sequence... var framesReader = reader.ReadSubtree(); while (framesReader.ReadToFollowing("Frame")) { // Create a new frame. Frame frame = new Frame(); sequence.frames.Add(frame); // Read the frame attributes from XML (name, duration) Dictionary<String, String> frameAttributes = new Dictionary<string, string>(); readAttributes(framesReader, frameAttributes); if (frameAttributes.ContainsKey("name")) { frame.name = frameAttributes["name"]; } else { frame.name = "Frame " + sequence.frames.Count; warnings.Add("No name found for " + frame.name + " in sequence \"" + sequence.name + "\"."); } if (frameAttributes.ContainsKey("duration")) { parseU16(frameAttributes["duration"], ref frame.length_ms, "Duration for frame \"" + frame.name + "\" in sequence \"" + sequence.name + "\".", warnings); } else { frame.name = "Frame " + sequence.frames.Count; warnings.Add("No duration found for frame \"" + frame.name + "\" in sequence \"" + sequence.name + "\"."); } frame.setTargetsFromString(reader.ReadElementContentAsString(), settings.servoCount); } } } else if (reader.NodeType == XmlNodeType.Element && reader.Name == "Script") { // We found the <Script> tag. // Get the ScriptDone attribute in to our dictionary. readAttributes(reader, xParams); // Read the script. script = reader.ReadElementContentAsString(); } else if (reader.NodeType == XmlNodeType.Element) { // Read the miscellaneous parameters that come in element tags, like <NeverSuspend>false</NeverSuspend>. try { xParams[reader.Name] = reader.ReadElementContentAsString(); } catch (XmlException e) { warnings.Add("Unable to parse element \"" + reader.Name + "\": " + e.Message); } } } reader.Close(); //// Step 2: Put the data in to the settings object. try { settings.setAndCompileScript(script); } catch (Exception e) { warnings.Add("Error compiling script from XML file: " + e.Message); settings.scriptInconsistent = true; } if (assertKey("NeverSuspend", xParams, warnings)) { parseBool(xParams["NeverSuspend"], ref settings.neverSuspend, "NeverSuspend", warnings); } if (assertKey("SerialMode", xParams, warnings)) { switch (xParams["SerialMode"]) { default: settings.serialMode = uscSerialMode.SERIAL_MODE_UART_DETECT_BAUD_RATE; break; case "UART_FIXED_BAUD_RATE": settings.serialMode = uscSerialMode.SERIAL_MODE_UART_FIXED_BAUD_RATE; break; case "USB_DUAL_PORT": settings.serialMode = uscSerialMode.SERIAL_MODE_USB_DUAL_PORT; break; case "USB_CHAINED": settings.serialMode = uscSerialMode.SERIAL_MODE_USB_CHAINED; break; } } if (assertKey("FixedBaudRate", xParams, warnings)) { parseU32(xParams["FixedBaudRate"], ref settings.fixedBaudRate, "FixedBaudRate", warnings); } if (assertKey("SerialTimeout", xParams, warnings)) { parseU16(xParams["SerialTimeout"], ref settings.serialTimeout, "SerialTimeout", warnings); } if (assertKey("EnableCrc", xParams, warnings)) { parseBool(xParams["EnableCrc"], ref settings.enableCrc, "EnableCrc", warnings); } if (assertKey("SerialDeviceNumber", xParams, warnings)) { parseU8(xParams["SerialDeviceNumber"], ref settings.serialDeviceNumber, "SerialDeviceNumber", warnings); } if (assertKey("SerialMiniSscOffset", xParams, warnings)) { parseU8(xParams["SerialMiniSscOffset"], ref settings.miniSscOffset, "SerialMiniSscOffset", warnings); } if (assertKey("ScriptDone", xParams, warnings)) { parseBool(xParams["ScriptDone"], ref settings.scriptDone, "ScriptDone", warnings); } // These parameters are optional because they don't apply to all Maestros. if (xParams.ContainsKey("ServosAvailable")) { parseU8(xParams["ServosAvailable"], ref settings.servosAvailable, "ServosAvailable", warnings); } if (xParams.ContainsKey("ServoPeriod")) { parseU8(xParams["ServoPeriod"], ref settings.servoPeriod, "ServoPeriod", warnings); } if (xParams.ContainsKey("EnablePullups")) { parseBool(xParams["EnablePullups"], ref settings.enablePullups, "EnablePullups", warnings); } if (xParams.ContainsKey("MiniMaestroServoPeriod")) { parseU32(xParams["MiniMaestroServoPeriod"], ref settings.miniMaestroServoPeriod, "MiniMaestroServoPeriod", warnings); } if (xParams.ContainsKey("ServoMultiplier")) { parseU16(xParams["ServoMultiplier"], ref settings.servoMultiplier, "ServoMultiplier", warnings); } return settings; }