public void WrapActionSections(string type) { string wrapperName = inFileName + "Action" + type + MsgAutoGenUtilities.ActionClassSuffix; string msgName = inFileName + type + MsgAutoGenUtilities.ActionClassSuffix; string outPath = Path.Combine(this.outPath, wrapperName + ".cs"); string imports = "using System.Collections.Generic;\n" + "using Unity.Robotics.ROSTCPConnector.MessageGeneration;\n" + "using RosMessageTypes.Std;\n" + "using RosMessageTypes.Actionlib;\n\n"; symbolTable = new Dictionary <string, string>(); using (StreamWriter writer = new StreamWriter(outPath, false)) { // Write imports writer.Write(imports); // Write namespace writer.Write( "namespace RosMessageTypes." + MsgAutoGenUtilities.ResolvePackageName(rosPackageName) + "\n" + "{\n" ); // Write class declaration writer.Write( ONE_TAB + "public class " + wrapperName + " : Action" + type + "<" + msgName + ">\n" + ONE_TAB + "{\n" ); // Write ROS package name writer.Write( TWO_TABS + "public const string k_RosMessageName = \"" + rosPackageName + "/" + inFileName + "Action" + type + "\";\n" + TWO_TABS + "public override string RosMessageName => k_RosMessageName;\n\n" ); // Record goal/result/feedback declaration symbolTable.Add(MsgAutoGenUtilities.LowerFirstLetter(type), msgName); writer.Write("\n"); // Write default value constructor writer.Write(GenerateDefaultValueConstructor(wrapperName) + "\n"); // Write parameterized constructor writer.Write(GenerateParameterizedConstructor(wrapperName, type)); writer.Write(GenerateDeserializerConstructor(wrapperName)); writer.Write(GenerateSerializationStatements(type)); // Close class writer.Write(ONE_TAB + "}\n"); // Close namespace writer.Write("}\n"); } }
public static List <string> GenerateSingleAction(string inPath, string outPath, string rosPackageName = "", bool verbose = false) { // If no ROS package name is provided, extract from path if (rosPackageName.Equals("")) { string[] hierarchy = inPath.Split(new char[] { '/', '\\' }); rosPackageName = hierarchy[hierarchy.Length - 3]; } outPath = Path.Combine(outPath, MsgAutoGenUtilities.ResolvePackageName(rosPackageName)); string inFileName = Path.GetFileNameWithoutExtension(inPath); if (verbose) { Console.WriteLine("Parsing: " + inPath); Console.WriteLine("Output Location: " + outPath); } MessageTokenizer tokenizer = new MessageTokenizer(inPath, new HashSet <string>(MsgAutoGenUtilities.builtInTypesMapping.Keys)); List <List <MessageToken> > listsOfTokens = tokenizer.Tokenize(); if (listsOfTokens.Count != 3) { throw new MessageParserException("Unexpected number of sections. Action should have 3 sections."); } List <string> warnings = new List <string>(); ActionWrapper actionWrapper = new ActionWrapper(inPath, rosPackageName, outPath); for (int i = 0; i < listsOfTokens.Count; i++) { List <MessageToken> tokens = listsOfTokens[i]; // Action is made up of goal, result, feedback string className = inFileName + types[i]; // Parse and generate goal, result, feedback messages MessageParser parser = new MessageParser(tokens, outPath, rosPackageName, "action", MsgAutoGenUtilities.builtInTypesMapping, MsgAutoGenUtilities.builtInTypesDefaultInitialValues, MsgAutoGenUtilities.numericTypeDeserializationFunctions, MsgAutoGenUtilities.numericTypeByteSize, className); parser.Parse(); warnings.AddRange(parser.GetWarnings()); // Generate action section wrapper messages actionWrapper.WrapActionSections(types[i]); } // Generate action wrapper actionWrapper.WrapAction(); return(warnings); }
public static List <string> GenerateSingleService(string inPath, string outPath, string rosPackageName = "", bool verbose = false) { // If no ROS package name is provided, extract from path if (rosPackageName.Equals("")) { string[] hierarchy = inPath.Split(new char[] { '/', '\\' }); rosPackageName = hierarchy[hierarchy.Length - 3]; } outPath = Path.Combine(outPath, MsgAutoGenUtilities.ResolvePackageName(rosPackageName)); string inFileName = Path.GetFileNameWithoutExtension(inPath); if (verbose) { Console.WriteLine("Parsing: " + inPath); Console.WriteLine("Output Location: " + outPath); } MessageTokenizer tokenizer = new MessageTokenizer(inPath, new HashSet <string>(MsgAutoGenUtilities.builtInTypesMapping.Keys)); List <List <MessageToken> > listsOfTokens = tokenizer.Tokenize(); if (listsOfTokens.Count != 2) { throw new MessageParserException("Unexpected number of sections. Service should have 2 sections."); } List <string> warnings = new List <string>(); for (int i = 0; i < listsOfTokens.Count; i++) { List <MessageToken> tokens = listsOfTokens[i]; // Service is made up of request and response string className = inFileName + MsgAutoGenUtilities.ServiceClassSuffix + types[i]; MessageParser parser = new MessageParser( tokens, outPath, rosPackageName, "srv", MsgAutoGenUtilities.builtInTypesMapping, MsgAutoGenUtilities.builtInTypesDefaultInitialValues, className, subtopic: i == 0 ? MessageSubtopic.Default : MessageSubtopic.Response); parser.Parse(); warnings.AddRange(parser.GetWarnings()); } return(warnings); }
public MessageParser(List <MessageToken> tokens, string outPath, string rosPackageName, string type, Dictionary <string, string> builtInTypeMapping, Dictionary <string, string> builtInTypesDefaultInitialValues, string className = "", string rosMsgName = "", MessageSubtopic subtopic = MessageSubtopic.Default) { this.tokens = tokens; this.inFilePath = tokens[0].content; this.inFileName = Path.GetFileNameWithoutExtension(inFilePath); this.rosPackageName = rosPackageName; this.rosPackageNamespace = MsgAutoGenUtilities.ResolvePackageName(rosPackageName); this.subtopic = subtopic; if (className.Equals("")) { this.className = MsgAutoGenUtilities.CapitalizeFirstLetter(inFileName) + MsgAutoGenUtilities.MessageClassSuffix; } else { this.className = className; } if (rosMsgName.Equals("")) { if (Char.IsLower(inFileName[0])) { Debug.LogWarningFormat("File {0} starts with a lowercase character. Message file names should be PascalCased (see : http://wiki.ros.org/ROS/Patterns/Conventions#line-38)", inFileName); } this.rosMsgName = inFileName; } else { this.rosMsgName = rosMsgName; } this.outPath = outPath; this.outFilePath = Path.Combine(outPath, type); this.tokens.RemoveAt(0); this.builtInTypeMapping = builtInTypeMapping; this.builtInTypesDefaultInitialValues = builtInTypesDefaultInitialValues; }
// Declaration -> BuiltInType Identifier | BuiltInType Identifier ConstantDeclaration | BuiltInType ArrayDeclaration Identifier // Declaration -> DefinedType Identifier | DefinedType ArrayDeclaration Identifier // Declaration -> Header Identifier private void Declaration() { string declaration = ""; // Type MessageToken peeked = Peek(); string type = ""; bool canHaveConstDecl = false; declaration += MsgAutoGenUtilities.TWO_TABS + "public "; if (PeekType(MessageTokenType.BuiltInType)) { type = builtInTypeMapping[MatchByType(MessageTokenType.BuiltInType)]; if (type == "TimeMsg" || type == "DurationMsg") { // Need to import BuiltinInterfaces imports.Add("BuiltinInterfaces"); } else { // Time and Duration can't have constant declaration // See <wiki.ros.org/msg> canHaveConstDecl = true; } } else if (PeekType(MessageTokenType.DefinedType)) { type = MatchByType(MessageTokenType.DefinedType); string[] hierarchy = type.Split(new char[] { '/', '\\' }); // Assume type can only be either: // Type // package/Type switch (hierarchy.Length) { case 1: type = type + MsgAutoGenUtilities.MessageClassSuffix; break; case 2: if (hierarchy[0].Equals("") || hierarchy[1].Equals("")) { throw new MessageParserException( "Invalid field type '" + type + "'. + " + "(" + inFilePath + ":" + lineNum + ")"); } string package = MsgAutoGenUtilities.ResolvePackageName(hierarchy[0]); // Do not add package name if exists in current namespace type = package.Equals(rosPackageNamespace) ? hierarchy[1] + MsgAutoGenUtilities.MessageClassSuffix : package + "." + hierarchy[1] + MsgAutoGenUtilities.MessageClassSuffix; break; default: throw new MessageParserException( "Invalid field type '" + type + "'. + " + "(" + inFilePath + ":" + lineNum + ")"); } } else { type = MatchByType(MessageTokenType.Header) + MsgAutoGenUtilities.MessageClassSuffix; if (PeekType(MessageTokenType.FixedSizeArray) || PeekType(MessageTokenType.VariableSizeArray)) { Warn( "By convention, there is only one header for each message." + "(" + inFilePath + ":" + lineNum + ")"); } if (PeekType(MessageTokenType.Identifier) && !Peek().content.Equals("header")) { Warn( "By convention, a ros message Header will be named 'header'. '" + Peek().content + "'. (" + inFilePath + ":" + lineNum + ")"); } imports.Add("Std"); } // Array Declaration int arraySize = -1; if (PeekType(MessageTokenType.FixedSizeArray)) { type += "[]"; canHaveConstDecl = false; arraySize = int.Parse(MatchByType(MessageTokenType.FixedSizeArray)); } if (PeekType(MessageTokenType.VariableSizeArray)) { type += "[]"; canHaveConstDecl = false; MatchByType(MessageTokenType.VariableSizeArray); arraySize = 0; } // Identifier string identifier = MatchByType(MessageTokenType.Identifier); // Check for duplicate declaration if (symbolTable.ContainsKey(identifier)) { throw new MessageParserException( "Field '" + identifier + "' at " + inFilePath + ":" + lineNum + " already declared!"); } // Check if identifier is a ROS message built-in type if (builtInTypeMapping.ContainsKey(identifier)) { throw new MessageParserException( "Invalid field identifier '" + identifier + "' at " + inFilePath + ":" + lineNum + ". '" + identifier + "' is a ROS message built-in type."); } // Check if identifier is a C# keyword if (MsgAutoGenUtilities.k_CSharpKeywords.Contains(identifier)) { Warn( "'" + identifier + "' is a C# keyword. It can be accessed under the name @" + identifier + "." + "(" + inFilePath + ":" + lineNum + ")"); identifier = "@" + identifier; } symbolTable.Add(identifier, type); // Array declaration table if (arraySize > -1) { arraySizes.Add(identifier, arraySize); } // Constant Declaration if (PeekType(MessageTokenType.ConstantDeclaration)) { if (canHaveConstDecl) { declaration += "const " + type + " " + identifier + " = "; declaration += ConstantDeclaration(type); constants.Add(identifier); } else { throw new MessageParserException( "Type " + type + "' at " + inFilePath + ":" + lineNum + " cannot have constant declaration"); } } else if (PeekType(MessageTokenType.DefaultValueDeclaration)) { declaration += $"{type} {identifier} = {DefaultValueDeclaration(type)}"; defaultValues.Add(identifier); } else { declaration += type + " " + identifier + ";\n"; } body += declaration; }
public static string GetMessageOutFolder(string outPath, string rosPackageName) { return(Path.Combine(outPath, MsgAutoGenUtilities.ResolvePackageName(rosPackageName))); }
public void WrapAction() { string msgNamePrefix = inFileName + MsgAutoGenUtilities.ActionClassSuffix; string className = msgNamePrefix + "Action"; string type = "wrapper"; string outPath = Path.Combine(this.outPath, className + ".cs"); string imports = "using System.Collections.Generic;\n" + "using Unity.Robotics.ROSTCPConnector.MessageGeneration;\n" + "\n\n"; symbolTable = new Dictionary <string, string>(); using (StreamWriter writer = new StreamWriter(outPath, false)) { // Write imports writer.Write(imports); // Write namespace writer.Write( "namespace RosMessageTypes." + MsgAutoGenUtilities.ResolvePackageName(rosPackageName) + "\n" + "{\n" ); // Write class declaration string[] genericParams = new string[] { msgNamePrefix + "ActionGoal", msgNamePrefix + "ActionResult", msgNamePrefix + "ActionFeedback", msgNamePrefix + "Goal", msgNamePrefix + "Result", msgNamePrefix + "Feedback" }; writer.Write( ONE_TAB + "public class " + className + " : Action<" + string.Join(", ", genericParams) + ">\n" + ONE_TAB + "{\n" ); // Write ROS package name writer.Write( TWO_TABS + "public const string k_RosMessageName = \"" + rosPackageName + "/" + inFileName + "Action" + "\";\n" + TWO_TABS + "public override string RosMessageName => k_RosMessageName;\n\n" ); // Record variables // Action Goal symbolTable.Add("action_goal", className + "Goal"); // Action Result symbolTable.Add("action_result", className + "Result"); //Action Feedback symbolTable.Add("action_feedback", className + "Feedback"); // Write default value constructor writer.Write("\n"); writer.Write(GenerateDefaultValueConstructor(className) + "\n"); writer.Write(GenerateDeserializerConstructor(className, false) + "\n"); writer.Write(GenerateSerializationStatements(type)); // Close class writer.Write(ONE_TAB + "}\n"); // Close namespace writer.Write("}\n"); } }