├── .gitattributes ├── .gitignore ├── .travis.yml ├── Examples ├── Examples.csproj └── Program.cs ├── GCodeNet.sln ├── GCodeNet ├── Base │ ├── CRC.cs │ ├── Command.cs │ ├── CommandAttribute.cs │ ├── CommandBase.cs │ ├── CommandMapping.cs │ ├── CommandType.cs │ ├── GCodeTokenizer.cs │ ├── ParameterType.cs │ └── ParameterTypeAttribute.cs ├── CommandCollection.cs ├── Commands │ ├── G │ │ ├── ControlledArcMoveClockwise.cs │ │ ├── ControlledArcMoveCounterClockwise.cs │ │ ├── Dwell.cs │ │ ├── LinearMove.cs │ │ ├── MoveToOrigin.cs │ │ ├── RapidLinearMove.cs │ │ ├── SetAbsolutePositioning.cs │ │ ├── SetPosition.cs │ │ ├── SetRelativePositioning.cs │ │ ├── SetUnitsToInches.cs │ │ └── SetUnitsToMillimeters.cs │ └── M │ │ ├── DisplayMessage.cs │ │ ├── FanOff.cs │ │ ├── FanOn.cs │ │ ├── SetExtruderTemperature.cs │ │ ├── SetExtruderTemperatureAndWait.cs │ │ ├── SetExtruderToAbsolute.cs │ │ └── StopIdleHold.cs ├── ExportFileOptions.cs ├── GCodeFile.cs ├── GCodeFileLine.cs ├── GCodeFileOptions.cs ├── GCodeNet.csproj └── Reflection │ ├── CommandReflection.cs │ └── CommandReflectionData.cs ├── README.md └── UnitTests ├── BoolType.cs ├── ByteType.cs ├── CommandTest.cs ├── DecimalType.cs ├── DoubleType.cs ├── EnumType.cs ├── GCodeFileTest.cs ├── IntType.cs ├── M117.cs ├── MappedCommand.cs └── UnitTests.csproj /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | *.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.pfx 193 | *.publishsettings 194 | node_modules/ 195 | orleans.codegen.cs 196 | 197 | # Since there are multiple workflows, uncomment next line to ignore bower_components 198 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 199 | #bower_components/ 200 | 201 | # RIA/Silverlight projects 202 | Generated_Code/ 203 | 204 | # Backup & report files from converting an old project file 205 | # to a newer Visual Studio version. Backup files are not needed, 206 | # because we have git ;-) 207 | _UpgradeReport_Files/ 208 | Backup*/ 209 | UpgradeLog*.XML 210 | UpgradeLog*.htm 211 | 212 | # SQL Server files 213 | *.mdf 214 | *.ldf 215 | 216 | # Business Intelligence projects 217 | *.rdl.data 218 | *.bim.layout 219 | *.bim_*.settings 220 | 221 | # Microsoft Fakes 222 | FakesAssemblies/ 223 | 224 | # GhostDoc plugin setting file 225 | *.GhostDoc.xml 226 | 227 | # Node.js Tools for Visual Studio 228 | .ntvs_analysis.dat 229 | 230 | # Visual Studio 6 build log 231 | *.plg 232 | 233 | # Visual Studio 6 workspace options file 234 | *.opt 235 | 236 | # Visual Studio LightSwitch build output 237 | **/*.HTMLClient/GeneratedArtifacts 238 | **/*.DesktopClient/GeneratedArtifacts 239 | **/*.DesktopClient/ModelManifest.xml 240 | **/*.Server/GeneratedArtifacts 241 | **/*.Server/ModelManifest.xml 242 | _Pvt_Extensions 243 | 244 | # Paket dependency manager 245 | .paket/paket.exe 246 | paket-files/ 247 | 248 | # FAKE - F# Make 249 | .fake/ 250 | 251 | # JetBrains Rider 252 | .idea/ 253 | *.sln.iml 254 | 255 | # CodeRush 256 | .cr/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | solution: GCodeNet.sln 3 | install: 4 | - nuget restore GCodeNet.sln 5 | - nuget install NUnit.Console -Version 3.5.0 -OutputDirectory testrunner 6 | script: 7 | - xbuild /p:Configuration=Release GCodeNet.sln 8 | - mono ./testrunner/NUnit.ConsoleRunner.3.5.0/tools/nunit3-console.exe ./UnitTests/bin/Release/TestProject.dll -------------------------------------------------------------------------------- /Examples/Examples.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netcoreapp2.1 4 | Exe 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using GCodeNet; 3 | using GCodeNet.Commands; 4 | 5 | namespace Examples 6 | { 7 | class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | CreateGCodeCommand(); 12 | CreateCommandFromGCode(); 13 | GCodeFromMappedCommand(); 14 | MappedCommandFromGCode(); 15 | 16 | Console.ReadLine(); 17 | } 18 | 19 | static void CreateGCodeCommand() 20 | { 21 | //Create a G1 command (Rapid Linear Movement) 22 | var cmd = new Command(CommandType.G, 1); 23 | cmd.SetParameterValue(ParameterType.X, 10); 24 | cmd.SetParameterValue(ParameterType.Y, 20); 25 | 26 | //Convert to GCode 27 | Console.WriteLine(cmd.ToGCode()); //Output: "G1 X10 Y20" 28 | 29 | //Convert to GCode with the CRC 30 | Console.WriteLine(cmd.ToGCode(addCrc: true)); //Output: "G1 X10 Y20*116" 31 | 32 | //Convert to GCode with the CRC and a line number 33 | Console.WriteLine(cmd.ToGCode(addCrc: true, lineNumber: 4)); //Output: "N4 G1 X10 Y20*46" 34 | } 35 | 36 | static void CreateCommandFromGCode() 37 | { 38 | var cmd = Command.Parse("G1 X10 Y20"); 39 | 40 | Console.WriteLine(cmd.CommandType); //Output: "G" 41 | Console.WriteLine(cmd.CommandSubType); //Output: "1" 42 | Console.WriteLine(cmd.GetParameterValue(ParameterType.X)); //Output: "10" 43 | Console.WriteLine(cmd.GetParameterValue(ParameterType.Y)); //Output: "20" 44 | } 45 | 46 | static void GCodeFromMappedCommand() 47 | { 48 | var cmd = new RapidLinearMove(); 49 | cmd.MoveX = 10; 50 | cmd.MoveY = 20; 51 | 52 | Console.WriteLine(cmd.CommandType); //Output: "G" 53 | Console.WriteLine(cmd.CommandSubType); //Output: "1" 54 | Console.WriteLine(cmd.GetParameterValue(ParameterType.X)); //Output: "10" 55 | Console.WriteLine(cmd.GetParameterValue(ParameterType.Y)); //Output: "20" 56 | 57 | Console.WriteLine(cmd.ToGCode()); //Output: "G1 X10 Y20 S0" 58 | } 59 | 60 | static void MappedCommandFromGCode() 61 | { 62 | var cmd = CommandMapping.Parse("G1 X10 Y20") as RapidLinearMove; 63 | 64 | Console.WriteLine(cmd.CommandType); //Output: "G" 65 | Console.WriteLine(cmd.CommandSubType); //Output: "1" 66 | Console.WriteLine(cmd.MoveX); //Output: "10" 67 | Console.WriteLine(cmd.MoveY); //Output: "20" 68 | Console.WriteLine(cmd.ToGCode()); //Output: "G1 X10 Y20 S0" 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /GCodeNet.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GCodeNet", "GCodeNet\GCodeNet.csproj", "{12E6988E-D61B-4D15-AE53-6478EA04939F}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{50B99882-A4CE-48B7-BC65-F730824CF5F8}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Examples", "Examples\Examples.csproj", "{BE7EB1B0-A70B-43A9-AE03-CC84643C8D15}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {12E6988E-D61B-4D15-AE53-6478EA04939F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {12E6988E-D61B-4D15-AE53-6478EA04939F}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {12E6988E-D61B-4D15-AE53-6478EA04939F}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {12E6988E-D61B-4D15-AE53-6478EA04939F}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {50B99882-A4CE-48B7-BC65-F730824CF5F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {50B99882-A4CE-48B7-BC65-F730824CF5F8}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {50B99882-A4CE-48B7-BC65-F730824CF5F8}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {50B99882-A4CE-48B7-BC65-F730824CF5F8}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {BE7EB1B0-A70B-43A9-AE03-CC84643C8D15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {BE7EB1B0-A70B-43A9-AE03-CC84643C8D15}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {BE7EB1B0-A70B-43A9-AE03-CC84643C8D15}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {BE7EB1B0-A70B-43A9-AE03-CC84643C8D15}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /GCodeNet/Base/CRC.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace GCodeNet 7 | { 8 | public static class CRC 9 | { 10 | public static byte Calculate(string line) 11 | { 12 | var bytes = Encoding.UTF8.GetBytes(line); 13 | int cs = 0; 14 | for (int i = 0; i < bytes.Length; i++) 15 | { 16 | cs = cs ^ bytes[i]; 17 | } 18 | cs = cs & 0xff; 19 | return (byte)cs; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /GCodeNet/Base/Command.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace GCodeNet 7 | { 8 | public class Command : CommandBase 9 | { 10 | Dictionary parameters = new Dictionary(); 11 | 12 | private Command() { } 13 | 14 | public Command(CommandType type, int subType) 15 | { 16 | this.CommandType = type; 17 | this.CommandSubType = subType; 18 | } 19 | 20 | public override IEnumerable GetParameters() 21 | { 22 | return parameters.Keys.ToArray(); 23 | } 24 | 25 | public override object GetParameterValue(ParameterType parameter) 26 | { 27 | if (parameters.ContainsKey(parameter)) 28 | { 29 | return parameters[parameter]; 30 | } 31 | return null; 32 | } 33 | 34 | public override bool HasParameter(ParameterType parameter) 35 | { 36 | return parameters.ContainsKey(parameter); 37 | } 38 | 39 | public override void RemoveParameter(ParameterType parameter) 40 | { 41 | parameters.Remove(parameter); 42 | } 43 | 44 | public override void SetParameterValue(ParameterType parameter, object value) 45 | { 46 | parameters[parameter] = value; 47 | } 48 | 49 | public static Command Parse(string gcode) 50 | { 51 | var tokenizer = new GCodeTokenizer(gcode); 52 | var commands = tokenizer.GetCommandTokens().ToArray(); 53 | if (commands.Length != 1) 54 | { 55 | throw new Exception("gcode may only contain a single command"); 56 | } 57 | return FromTokens(commands[0]); 58 | } 59 | 60 | public static Command FromTokens(params string[] tokens) 61 | { 62 | var obj = new Command(); 63 | obj.SetTokens(tokens); 64 | return obj; 65 | } 66 | 67 | public override string ToString() 68 | { 69 | return this.ToGCode(); 70 | } 71 | 72 | public override void ClearAllParameters() 73 | { 74 | parameters.Clear(); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /GCodeNet/Base/CommandAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace GCodeNet 4 | { 5 | [AttributeUsage(AttributeTargets.Class)] 6 | public class CommandAttribute : Attribute 7 | { 8 | public CommandType CommandType { get; private set; } 9 | public int CommandSubType { get; private set; } 10 | 11 | public CommandAttribute(CommandType commandType, int commandSubType) 12 | { 13 | this.CommandType = commandType; 14 | this.CommandSubType = commandSubType; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /GCodeNet/Base/CommandBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Collections; 6 | 7 | namespace GCodeNet 8 | { 9 | public abstract class CommandBase : IDictionary 10 | { 11 | public CommandType CommandType { get; set; } 12 | public int CommandSubType { get; set; } 13 | 14 | public abstract IEnumerable GetParameters(); 15 | public abstract bool HasParameter(ParameterType parameter); 16 | public abstract object GetParameterValue(ParameterType parameter); 17 | public abstract void SetParameterValue(ParameterType parameter, object value); 18 | public abstract void RemoveParameter(ParameterType parameter); 19 | public abstract void ClearAllParameters(); 20 | 21 | internal void SetTokens(string[] tokens) 22 | { 23 | this.CommandType = (CommandType)Enum.Parse(typeof(CommandType), tokens[0]); 24 | this.CommandSubType = int.Parse(tokens[1]); 25 | 26 | int i = 2; 27 | while (i < tokens.Length) 28 | { 29 | var paramType = (ParameterType)Enum.Parse(typeof(ParameterType), tokens[i++]); 30 | object value = null; 31 | if (tokens.Length > i && !char.IsLetter(tokens[i][0])) 32 | { 33 | if (tokens[i][0] == '"') 34 | { 35 | value = tokens[i++].Trim('"'); 36 | } 37 | else 38 | { 39 | value = decimal.Parse(tokens[i++]); 40 | } 41 | } 42 | this.SetParameterValue(paramType, value); 43 | } 44 | } 45 | 46 | public static CommandBase Parse(string gcode, bool useMappedCommands = true) 47 | { 48 | var tokenizer = new GCodeTokenizer(gcode); 49 | var commands = tokenizer.GetCommandTokens().ToArray(); 50 | if (commands.Length != 1) 51 | { 52 | throw new Exception("gcode may only contain a single command"); 53 | } 54 | return FromTokens(commands[0], useMappedCommands); 55 | } 56 | 57 | public static CommandBase FromTokens(string[] tokens, bool useMappedCommands = true) 58 | { 59 | if (useMappedCommands) 60 | { 61 | var commandLetter = (CommandType)Enum.Parse(typeof(CommandType), tokens[0]); 62 | int commandNumber = int.Parse(tokens[1]); 63 | var type = CommandReflection.GetCommandObjectType(commandLetter, commandNumber); 64 | if (type != null) 65 | { 66 | return CommandMapping.FromTokens(tokens); 67 | } 68 | } 69 | return Command.FromTokens(tokens); 70 | } 71 | 72 | public virtual string ToGCode(bool addCrc = false, int lineNumber = -1) 73 | { 74 | StringBuilder sb = new StringBuilder(); 75 | 76 | if (lineNumber > -1) 77 | { 78 | if (this.CommandType == CommandType.N) 79 | { 80 | throw new Exception("Can't add a line number on a line number command type"); 81 | } 82 | sb.Append("N" + lineNumber + " "); 83 | } 84 | sb.Append(this.CommandType); 85 | sb.Append(this.CommandSubType); 86 | foreach (var param in this.GetParameters()) 87 | { 88 | sb.Append(" " + param); 89 | var val = this.GetParameterValue(param); 90 | if (val != null) 91 | { 92 | sb.Append(val); 93 | } 94 | } 95 | if (addCrc) 96 | { 97 | sb.Append("*" + CRC.Calculate(sb.ToString())); 98 | } 99 | return sb.ToString(); 100 | } 101 | 102 | public ICollection Keys 103 | { 104 | get 105 | { 106 | return (ICollection)GetParameters(); 107 | } 108 | } 109 | 110 | public ICollection Values 111 | { 112 | get 113 | { 114 | return (ICollection)GetParameters().Select(k => GetParameterValue(k)); 115 | } 116 | } 117 | 118 | public int Count 119 | { 120 | get 121 | { 122 | return GetParameters().Count(); 123 | } 124 | } 125 | 126 | public bool IsReadOnly 127 | { 128 | get 129 | { 130 | return false; 131 | } 132 | } 133 | 134 | public object this[ParameterType key] 135 | { 136 | get 137 | { 138 | return GetParameterValue(key); 139 | } 140 | 141 | set 142 | { 143 | SetParameterValue(key, value); 144 | } 145 | } 146 | public void Add(ParameterType key, object value) 147 | { 148 | if (HasParameter(key)) 149 | { 150 | throw new Exception("Command already this ParameterType"); 151 | } 152 | SetParameterValue(key, value); 153 | } 154 | 155 | public bool ContainsKey(ParameterType key) 156 | { 157 | return HasParameter(key); 158 | } 159 | 160 | public bool Remove(ParameterType key) 161 | { 162 | if (HasParameter(key)) 163 | { 164 | RemoveParameter(key); 165 | return true; 166 | } 167 | return false; 168 | } 169 | 170 | public bool TryGetValue(ParameterType key, out object value) 171 | { 172 | if (HasParameter(key)) 173 | { 174 | value = GetParameterValue(key); 175 | return true; 176 | } 177 | value = null; 178 | return false; 179 | } 180 | 181 | public void Add(KeyValuePair item) 182 | { 183 | SetParameterValue(item.Key, item.Value); 184 | } 185 | 186 | public void Clear() 187 | { 188 | ClearAllParameters(); 189 | } 190 | 191 | public bool Contains(KeyValuePair item) 192 | { 193 | foreach (var key in GetParameters()) 194 | { 195 | var val = GetParameterValue(key); 196 | if (key == item.Key && val.Equals(item.Value)) 197 | { 198 | return true; 199 | } 200 | } 201 | return false; 202 | } 203 | 204 | public void CopyTo(KeyValuePair[] array, int arrayIndex) 205 | { 206 | throw new NotImplementedException(); 207 | } 208 | 209 | public bool Remove(KeyValuePair item) 210 | { 211 | throw new NotImplementedException(); 212 | } 213 | 214 | public IEnumerator> GetEnumerator() 215 | { 216 | foreach (var key in GetParameters()) 217 | { 218 | var val = GetParameterValue(key); 219 | yield return new KeyValuePair(key, val); 220 | } 221 | } 222 | 223 | IEnumerator IEnumerable.GetEnumerator() 224 | { 225 | throw new NotImplementedException(); 226 | } 227 | } 228 | } -------------------------------------------------------------------------------- /GCodeNet/Base/CommandMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | 5 | namespace GCodeNet 6 | { 7 | public abstract class CommandMapping : CommandBase 8 | { 9 | protected CommandMapping() 10 | { 11 | var attrib = this.GetType().GetCustomAttributes(typeof(CommandAttribute), true).FirstOrDefault() as CommandAttribute; 12 | if (attrib == null) 13 | { 14 | throw new Exception("A mapped command must have the CommandAttribute attribute"); 15 | } 16 | this.CommandType = attrib.CommandType; 17 | this.CommandSubType = attrib.CommandSubType; 18 | } 19 | 20 | public override IEnumerable GetParameters() 21 | { 22 | var reflectionData = CommandReflection.GetReflectionData(this.GetType()); 23 | foreach (var kv in reflectionData.MappedProperties) 24 | { 25 | var paramType = kv.Key; 26 | var propInfo = kv.Value; 27 | 28 | if (propInfo.PropertyType.Equals(typeof(bool)) && (bool)propInfo.GetValue(this, null) == true) 29 | { 30 | yield return paramType; 31 | } 32 | else if (GetParameterValue(paramType) != null) 33 | { 34 | yield return paramType; 35 | } 36 | } 37 | } 38 | 39 | public override bool HasParameter(ParameterType parameter) 40 | { 41 | var reflectionData = CommandReflection.GetReflectionData(this.GetType()); 42 | return reflectionData.MappedProperties.ContainsKey(parameter); 43 | } 44 | 45 | public override object GetParameterValue(ParameterType parameter) 46 | { 47 | var reflectionData = CommandReflection.GetReflectionData(this.GetType()); 48 | var propInfo = reflectionData.MappedProperties[parameter]; 49 | var type = propInfo.PropertyType; 50 | 51 | if (type.Equals(typeof(bool))) 52 | { 53 | return null; 54 | } 55 | else if (type.IsEnum) 56 | { 57 | return (decimal)(int)propInfo.GetValue(this, null); 58 | } 59 | else if (type.IsGenericType && type.GetGenericArguments()[0].IsEnum) 60 | { 61 | var val = propInfo.GetValue(this, null); 62 | if (val == null) return null; 63 | return (decimal)(int)val; 64 | } 65 | else 66 | { 67 | var val = propInfo.GetValue(this, null); 68 | if (val == null) return null; 69 | var dec = (decimal)Convert.ChangeType(val, typeof(decimal)); 70 | return dec; 71 | } 72 | } 73 | 74 | public override void SetParameterValue(ParameterType parameter, object value) 75 | { 76 | var reflectionData = CommandReflection.GetReflectionData(this.GetType()); 77 | var propInfo = reflectionData.MappedProperties[parameter]; 78 | 79 | var type = propInfo.PropertyType; 80 | bool isNullableType = type.Equals(typeof(string)) || (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)); 81 | 82 | if (type.Equals(typeof(bool)) || type.Equals(typeof(bool?))) 83 | { 84 | propInfo.SetValue(this, true, null); 85 | } 86 | else if (value == null) 87 | { 88 | if (isNullableType) 89 | { 90 | propInfo.SetValue(this, null, null); 91 | } 92 | else if (type.IsEnum) 93 | { 94 | propInfo.SetValue(this,0, null); 95 | } 96 | else 97 | { 98 | propInfo.SetValue(this, Convert.ChangeType(0, type), null); 99 | } 100 | } 101 | else if (value != null) 102 | { 103 | if (type.Equals(typeof(string))) 104 | { 105 | propInfo.SetValue(this, value, null); 106 | } 107 | else if (type.Equals(typeof(byte)) || type.Equals(typeof(byte?))) 108 | { 109 | propInfo.SetValue(this, Convert.ChangeType(value, typeof(byte)), null); 110 | } 111 | else if (type.Equals(typeof(int)) || type.Equals(typeof(int?))) 112 | { 113 | propInfo.SetValue(this, Convert.ChangeType(value, typeof(int)), null); 114 | } 115 | else if (propInfo.PropertyType.Equals(typeof(double)) || propInfo.PropertyType.Equals(typeof(double?))) 116 | { 117 | propInfo.SetValue(this, Convert.ChangeType(value, typeof(double)), null); 118 | } 119 | else if (propInfo.PropertyType.Equals(typeof(decimal)) || propInfo.PropertyType.Equals(typeof(decimal?))) 120 | { 121 | propInfo.SetValue(this, Convert.ChangeType(value, typeof(decimal)), null); 122 | } 123 | else if (type.IsEnum) 124 | { 125 | var enumVal = Enum.ToObject(type, Convert.ChangeType(value, typeof(int))); 126 | propInfo.SetValue(this, enumVal, null); 127 | } 128 | else if (type.IsGenericType && type.GetGenericArguments()[0].IsEnum) 129 | { 130 | var enumVal = Enum.ToObject(type.GetGenericArguments()[0], Convert.ChangeType(value, typeof(int))); 131 | propInfo.SetValue(this, enumVal, null); 132 | } 133 | else 134 | { 135 | throw new Exception("Command property type not supported: " + type); 136 | } 137 | } 138 | } 139 | 140 | public override void RemoveParameter(ParameterType parameter) 141 | { 142 | SetParameterValue(parameter, null); 143 | } 144 | 145 | public override void ClearAllParameters() 146 | { 147 | foreach (var key in GetParameters()) 148 | { 149 | SetParameterValue(key, null); 150 | } 151 | } 152 | 153 | public static CommandMapping Parse(string gcode) 154 | { 155 | var tokenizer = new GCodeTokenizer(gcode); 156 | var commands = tokenizer.GetCommandTokens().ToArray(); 157 | if (commands.Length != 1) 158 | { 159 | throw new Exception("gcode may only contain a single command"); 160 | } 161 | return FromTokens(commands[0]); 162 | } 163 | 164 | public static CommandMapping Parse(Type mappedType, string gcode) 165 | { 166 | var tokenizer = new GCodeTokenizer(gcode); 167 | var commands = tokenizer.GetCommandTokens().ToArray(); 168 | if (commands.Length != 1) 169 | { 170 | throw new Exception("gcode may only contain a single command"); 171 | } 172 | return FromTokens(mappedType, commands[0]); 173 | } 174 | 175 | public static CommandMapping FromTokens(string[] tokens) 176 | { 177 | var commandLetter = (CommandType)Enum.Parse(typeof(CommandType), tokens[0]); 178 | int commandNumber = int.Parse(tokens[1]); 179 | var type = CommandReflection.GetCommandObjectType(commandLetter, commandNumber); 180 | if (type == null) 181 | { 182 | throw new Exception("No mapping defined for these tokens"); 183 | } 184 | return FromTokens(type, tokens); 185 | } 186 | 187 | public static CommandMapping FromTokens(Type mappedType, string[] tokens) 188 | { 189 | var obj = (CommandMapping)Activator.CreateInstance(mappedType); 190 | obj.SetTokens(tokens); 191 | return obj; 192 | } 193 | } 194 | } -------------------------------------------------------------------------------- /GCodeNet/Base/CommandType.cs: -------------------------------------------------------------------------------- 1 | namespace GCodeNet 2 | { 3 | public enum CommandType 4 | { 5 | G,M,N 6 | } 7 | } -------------------------------------------------------------------------------- /GCodeNet/Base/GCodeTokenizer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace GCodeNet 6 | { 7 | class GCodeTokenizer 8 | { 9 | string gcode; 10 | 11 | public GCodeTokenizer(string gcode) 12 | { 13 | //gcode = RemoveWhitespace(gcode); 14 | //CheckForIllegalChars(gcode); 15 | //gcode = gcode.ToUpper(); 16 | 17 | this.gcode = gcode; 18 | } 19 | 20 | void CheckForIllegalChars(string gcode) 21 | { 22 | HashSet list = new HashSet(); 23 | for (char c='a'; c<='z';c++) 24 | { 25 | list.Add(c); 26 | } 27 | for (char c = 'A'; c <= 'Z'; c++) 28 | { 29 | list.Add(c); 30 | } 31 | for (char c = '0'; c <= '9'; c++) 32 | { 33 | list.Add(c); 34 | } 35 | list.Add('.'); 36 | list.Add('+'); 37 | list.Add('-'); 38 | list.Add(':'); 39 | 40 | foreach (var c in gcode) 41 | { 42 | if (!list.Contains(c)) 43 | { 44 | throw new System.Exception($"Illegal character in gcode: '{c}'"); 45 | } 46 | } 47 | } 48 | 49 | string RemoveWhitespace(string str) 50 | { 51 | //this.gcode = Regex.Replace(gcode, @"\s+", ""); This is a bit slower 52 | StringBuilder sb = new StringBuilder(); 53 | foreach (var c in str) 54 | { 55 | if (!char.IsWhiteSpace(c)) 56 | { 57 | sb.Append(c); 58 | } 59 | } 60 | return sb.ToString(); 61 | } 62 | 63 | public IEnumerable GetCommandTokens() 64 | { 65 | bool isFirstToken = true; 66 | 67 | List tokens = new List(); 68 | foreach (var token in GetTokens()) 69 | { 70 | if (isFirstToken) 71 | { 72 | if (!IsValidCommandType(token)) 73 | { 74 | throw new Exception("The first token must be a valid CommandType: G,M,N, etc"); 75 | } 76 | isFirstToken = false; 77 | } 78 | 79 | if (tokens.Count == 2 && tokens[0] == "M" && tokens[1] == "117") 80 | { 81 | } 82 | else if (!IsValidParameter(token) && !IsValidCommandType(token) && !IsParameterValue(token)) 83 | { 84 | throw new Exception("Invalid token: " + token); 85 | } 86 | 87 | if (IsValidCommandType(token)) 88 | { 89 | if (tokens.Count == 1) 90 | { 91 | throw new Exception("A command subtype must always follow a command type."); 92 | } 93 | else if (tokens.Count > 1) 94 | { 95 | yield return tokens.ToArray(); 96 | } 97 | tokens.Clear(); 98 | } 99 | tokens.Add(token); 100 | } 101 | 102 | if (tokens.Count == 1) 103 | { 104 | throw new Exception("A command subtype must always follow a command type."); 105 | } 106 | else if (tokens.Count > 1) 107 | { 108 | yield return tokens.ToArray(); 109 | } 110 | } 111 | 112 | bool IsValidCommandType(string token) 113 | { 114 | return Enum.IsDefined(typeof(CommandType), token); 115 | } 116 | 117 | bool IsValidParameter(string token) 118 | { 119 | if (token[0] == '"' && token[token.Length - 1] == '"') 120 | return true; 121 | return Enum.IsDefined(typeof(ParameterType), token); 122 | } 123 | 124 | bool IsParameterValue(string token) 125 | { 126 | decimal tmp; 127 | return decimal.TryParse(token, out tmp); 128 | } 129 | 130 | public IEnumerable GetTokens() 131 | { 132 | StringBuilder buff = new StringBuilder(); 133 | 134 | string lastToken = ""; 135 | 136 | int i = 0; 137 | while(i < gcode.Length) 138 | { 139 | var token = GetNextToken(ref i); 140 | if (!string.IsNullOrEmpty(token)) 141 | { 142 | yield return token; 143 | } 144 | 145 | //This is a special formatting case for M117 146 | if (lastToken == "M" && token == "117") 147 | { 148 | var displayStr = ReadUntilEndOfLine(ref i); 149 | yield return ParameterType.D.ToString(); //Assign any param to this string because one is not provided. 150 | yield return '"' + displayStr + '"'; 151 | } 152 | lastToken = token; 153 | } 154 | } 155 | 156 | string ReadUntilEndOfLine(ref int i) 157 | { 158 | ConsumeWhiteSpace(ref i); 159 | 160 | StringBuilder token = new StringBuilder(); 161 | while (i < gcode.Length) 162 | { 163 | char c = gcode[i]; 164 | if (c == '\n') 165 | { 166 | return token.ToString().TrimEnd('\n', '\r'); 167 | } 168 | else 169 | { 170 | token.Append(c); 171 | i++; 172 | } 173 | } 174 | return token.ToString().TrimEnd('\n','\r'); 175 | } 176 | 177 | void ConsumeWhiteSpace(ref int i) 178 | { 179 | while (i < gcode.Length) 180 | { 181 | char c = gcode[i]; 182 | if (!char.IsWhiteSpace(c)) 183 | { 184 | break; 185 | } 186 | i++; 187 | } 188 | } 189 | 190 | string GetNextToken(ref int i) 191 | { 192 | ConsumeWhiteSpace(ref i); 193 | if (i >= gcode.Length) return null; 194 | 195 | if (char.IsLetter(gcode[i])) 196 | { 197 | return gcode[i++].ToString().ToUpper(); 198 | } 199 | 200 | StringBuilder token = new StringBuilder(); 201 | while (i < gcode.Length) 202 | { 203 | char c = gcode[i]; 204 | if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.' || c == ':') 205 | { 206 | token.Append(c); 207 | i++; 208 | } 209 | else 210 | { 211 | if (token.Length == 0) throw new Exception("invalid token character"); 212 | return token.ToString().ToUpper(); 213 | } 214 | } 215 | 216 | if (token.Length > 0) 217 | { 218 | return token.ToString(); 219 | } 220 | return null; 221 | } 222 | } 223 | 224 | } -------------------------------------------------------------------------------- /GCodeNet/Base/ParameterType.cs: -------------------------------------------------------------------------------- 1 | namespace GCodeNet 2 | { 3 | public enum ParameterType 4 | { 5 | T, S, P, X, Y, Z, I, J, D, H, F, R, Q, E 6 | } 7 | } -------------------------------------------------------------------------------- /GCodeNet/Base/ParameterTypeAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace GCodeNet 4 | { 5 | [AttributeUsage(AttributeTargets.Property)] 6 | public class ParameterTypeAttribute : Attribute 7 | { 8 | public ParameterType Param { get; private set; } 9 | public ParameterTypeAttribute(ParameterType param) 10 | { 11 | this.Param = param; 12 | } 13 | 14 | public ParameterTypeAttribute(string param) 15 | { 16 | this.Param = (ParameterType)Enum.Parse(typeof(ParameterType), param); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /GCodeNet/CommandCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | 7 | namespace GCodeNet 8 | { 9 | 10 | public class CommandCollection : List 11 | { } 12 | 13 | } -------------------------------------------------------------------------------- /GCodeNet/Commands/G/ControlledArcMoveClockwise.cs: -------------------------------------------------------------------------------- 1 | namespace GCodeNet.Commands 2 | { 3 | [Command(CommandType.G, 2)] 4 | public class ControlledArcMoveClockwise : CommandMapping 5 | { 6 | [ParameterType("X")] 7 | public decimal? MoveX { get; set; } 8 | [ParameterType("Y")] 9 | public decimal? MoveY { get; set; } 10 | [ParameterType("I")] 11 | public decimal? CenterX { get; set; } 12 | [ParameterType("J")] 13 | public decimal? CenterY { get; set; } 14 | [ParameterType("E")] 15 | public decimal? Extrude { get; set; } 16 | [ParameterType("F")] 17 | public decimal? Feedrate { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /GCodeNet/Commands/G/ControlledArcMoveCounterClockwise.cs: -------------------------------------------------------------------------------- 1 | namespace GCodeNet.Commands 2 | { 3 | [Command(CommandType.G, 3)] 4 | public class ControlledArcMoveCounterClockwise : CommandMapping 5 | { 6 | [ParameterType("X")] 7 | public decimal? MoveX { get; set; } 8 | [ParameterType("Y")] 9 | public decimal? MoveY { get; set; } 10 | [ParameterType("I")] 11 | public decimal? CenterX { get; set; } 12 | [ParameterType("J")] 13 | public decimal? CenterY { get; set; } 14 | [ParameterType("E")] 15 | public decimal? Extrude { get; set; } 16 | [ParameterType("F")] 17 | public decimal? Feedrate { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /GCodeNet/Commands/G/Dwell.cs: -------------------------------------------------------------------------------- 1 | namespace GCodeNet.Commands 2 | { 3 | [Command(CommandType.G, 4)] 4 | public class Dwell : CommandMapping 5 | { 6 | [ParameterType("P")] 7 | public decimal? WaitInMSecs { get; set; } 8 | [ParameterType("S")] 9 | public decimal? WaitInSecs { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /GCodeNet/Commands/G/LinearMove.cs: -------------------------------------------------------------------------------- 1 | namespace GCodeNet.Commands 2 | { 3 | [Command(CommandType.G, 0)] 4 | public class LinearMove: CommandMapping 5 | { 6 | [ParameterType("X")] 7 | public decimal? MoveX { get; set; } 8 | [ParameterType("Y")] 9 | public decimal? MoveY { get; set; } 10 | [ParameterType("Z")] 11 | public decimal? MoveZ { get; set; } 12 | [ParameterType("E")] 13 | public decimal? Extrude { get; set; } 14 | [ParameterType("F")] 15 | public decimal? Feedrate { get; set; } 16 | [ParameterType("S")] 17 | public CheckEndstop CheckEndstop { get; set; } 18 | } 19 | 20 | public enum CheckEndstop 21 | { 22 | Ignore = 0, 23 | Check = 1, 24 | } 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /GCodeNet/Commands/G/MoveToOrigin.cs: -------------------------------------------------------------------------------- 1 | namespace GCodeNet.Commands 2 | { 3 | [Command(CommandType.G, 28)] 4 | public class MoveToOrigin : CommandMapping 5 | { 6 | [ParameterType("X")] 7 | public bool GotoX { get; set; } 8 | [ParameterType("Y")] 9 | public bool GotoY { get; set; } 10 | [ParameterType("Z")] 11 | public bool GotoZ { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /GCodeNet/Commands/G/RapidLinearMove.cs: -------------------------------------------------------------------------------- 1 | namespace GCodeNet.Commands 2 | { 3 | [Command(CommandType.G, 1)] 4 | public class RapidLinearMove : CommandMapping 5 | { 6 | [ParameterType("X")] 7 | public decimal? MoveX { get; set; } 8 | [ParameterType("Y")] 9 | public decimal? MoveY { get; set; } 10 | [ParameterType("Z")] 11 | public decimal? MoveZ { get; set; } 12 | [ParameterType("E")] 13 | public decimal? Extrude { get; set; } 14 | [ParameterType("F")] 15 | public decimal? Feedrate { get; set; } 16 | [ParameterType("S")] 17 | public CheckEndstop CheckEndstop { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /GCodeNet/Commands/G/SetAbsolutePositioning.cs: -------------------------------------------------------------------------------- 1 | namespace GCodeNet.Commands 2 | { 3 | [Command(CommandType.G, 90)] 4 | public class SetAbsolutePositioning : CommandMapping 5 | { 6 | } 7 | } -------------------------------------------------------------------------------- /GCodeNet/Commands/G/SetPosition.cs: -------------------------------------------------------------------------------- 1 | namespace GCodeNet.Commands 2 | { 3 | [Command(CommandType.G, 92)] 4 | public class SetPosition : CommandMapping 5 | { 6 | [ParameterType("X")] 7 | public decimal? MoveX { get; set; } 8 | [ParameterType("Y")] 9 | public decimal? MoveY { get; set; } 10 | [ParameterType("Z")] 11 | public decimal? MoveZ { get; set; } 12 | [ParameterType("E")] 13 | public decimal? Extrude { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /GCodeNet/Commands/G/SetRelativePositioning.cs: -------------------------------------------------------------------------------- 1 | namespace GCodeNet.Commands 2 | { 3 | [Command(CommandType.G, 91)] 4 | public class SetRelativePositioning : CommandMapping 5 | { 6 | } 7 | } -------------------------------------------------------------------------------- /GCodeNet/Commands/G/SetUnitsToInches.cs: -------------------------------------------------------------------------------- 1 | namespace GCodeNet.Commands 2 | { 3 | [Command(CommandType.G, 20)] 4 | public class SetUnitsToInches : CommandMapping 5 | { 6 | } 7 | } -------------------------------------------------------------------------------- /GCodeNet/Commands/G/SetUnitsToMillimeters.cs: -------------------------------------------------------------------------------- 1 | namespace GCodeNet.Commands 2 | { 3 | [Command(CommandType.G, 21)] 4 | public class SetUnitsToMillimeters : CommandMapping 5 | { 6 | } 7 | } -------------------------------------------------------------------------------- /GCodeNet/Commands/M/DisplayMessage.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace GCodeNet.Commands.M 4 | { 5 | [Command(CommandType.M, 117)] 6 | public class DisplayMessage : CommandMapping 7 | { 8 | [ParameterType("D")] 9 | public string Message { get; set; } 10 | 11 | public override string ToGCode(bool addCrc = false, int lineNumber = -1) 12 | { 13 | StringBuilder sb = new StringBuilder(); 14 | 15 | if (lineNumber > -1) 16 | { 17 | sb.Append("N" + lineNumber + " "); 18 | } 19 | sb.Append(this.CommandType); 20 | sb.Append(this.CommandSubType); 21 | if (!string.IsNullOrEmpty(Message)) 22 | { 23 | sb.Append(" " + Message); 24 | } 25 | if (addCrc) 26 | { 27 | sb.Append("*" + CRC.Calculate(sb.ToString())); 28 | } 29 | return sb.ToString(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /GCodeNet/Commands/M/FanOff.cs: -------------------------------------------------------------------------------- 1 | namespace GCodeNet.Commands 2 | { 3 | [Command(CommandType.M, 107)] 4 | public class FanOff : CommandMapping 5 | { 6 | } 7 | } -------------------------------------------------------------------------------- /GCodeNet/Commands/M/FanOn.cs: -------------------------------------------------------------------------------- 1 | namespace GCodeNet.Commands 2 | { 3 | [Command(CommandType.M, 106)] 4 | public class FanOn : CommandMapping 5 | { 6 | [ParameterType("P")] 7 | public int? FanNumber { get; set; } 8 | [ParameterType("S")] 9 | public double FanSpeed { get; set; } 10 | [ParameterType("I")] 11 | public int? Invert { get; set; } 12 | [ParameterType("F")] 13 | public int? FanPwmFreq { get; set; } 14 | [ParameterType("H")] 15 | public int? MonitorHeaters { get; set; } 16 | [ParameterType("R")] 17 | public int? RestoreFanSpeed { get; set; } 18 | [ParameterType("T")] 19 | public int? Temperature { get; set; } 20 | } 21 | } -------------------------------------------------------------------------------- /GCodeNet/Commands/M/SetExtruderTemperature.cs: -------------------------------------------------------------------------------- 1 | namespace GCodeNet.Commands 2 | { 3 | [Command(CommandType.M, 104)] 4 | public class SetExtruderTemperature : CommandMapping 5 | { 6 | [ParameterType("S")] 7 | public int? Temperature { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /GCodeNet/Commands/M/SetExtruderTemperatureAndWait.cs: -------------------------------------------------------------------------------- 1 | namespace GCodeNet.Commands 2 | { 3 | [Command(CommandType.M, 109)] 4 | public class SetExtruderTemperatureAndWait : CommandMapping 5 | { 6 | [ParameterType("S")] 7 | public int? MinTemperature { get; set; } 8 | [ParameterType("R")] 9 | public int? AccurateTargetTemperature { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /GCodeNet/Commands/M/SetExtruderToAbsolute.cs: -------------------------------------------------------------------------------- 1 | namespace GCodeNet.Commands 2 | { 3 | [Command(CommandType.M, 82)] 4 | public class SetExtruderToAbsolute : CommandMapping 5 | { 6 | } 7 | } -------------------------------------------------------------------------------- /GCodeNet/Commands/M/StopIdleHold.cs: -------------------------------------------------------------------------------- 1 | namespace GCodeNet.Commands 2 | { 3 | [Command(CommandType.M, 84)] 4 | public class StopIdleHold : CommandMapping 5 | { 6 | [ParameterType("I")] 7 | public int? ResetFlags { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /GCodeNet/ExportFileOptions.cs: -------------------------------------------------------------------------------- 1 | namespace GCodeNet 2 | { 3 | public class ExportFileOptions 4 | { 5 | public bool WriteLineNumbers { get; set; } = true; 6 | public bool WriteCRC { get; set; } = true; 7 | } 8 | } -------------------------------------------------------------------------------- /GCodeNet/GCodeFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.IO; 5 | using System.Text; 6 | 7 | namespace GCodeNet 8 | { 9 | public class GCodeFile 10 | { 11 | public CommandCollection Commands { get; private set; } = new CommandCollection(); 12 | 13 | public GCodeFile(string gcode) : this(gcode, new GCodeFileOptions()) 14 | { 15 | } 16 | 17 | public GCodeFile(string gcode, GCodeFileOptions options) 18 | { 19 | MemoryStream stream = new MemoryStream(); 20 | StreamWriter writer = new StreamWriter(stream); 21 | writer.Write(gcode); 22 | writer.Flush(); 23 | stream.Position = 0; 24 | Init(stream, options); 25 | } 26 | 27 | public GCodeFile(Stream stream) 28 | { 29 | Init(stream, new GCodeFileOptions()); 30 | } 31 | 32 | public GCodeFile(Stream stream, GCodeFileOptions options) 33 | { 34 | Init(stream, options); 35 | } 36 | 37 | void Init(Stream stream, GCodeFileOptions options) 38 | { 39 | var gcodeLines = GetAllGCodeLines(stream).ToArray(); 40 | 41 | if (options.CheckCRC) 42 | { 43 | CheckCRC(gcodeLines); 44 | } 45 | 46 | var gcodeString = string.Join(Environment.NewLine, gcodeLines.Select(l => l.GCode)); 47 | var tokenizer = new GCodeTokenizer(gcodeString); 48 | var commandTokens = tokenizer.GetCommandTokens().ToArray(); 49 | 50 | this.Commands.AddRange(commandTokens.Select(c => CreateCommandFromTokens(c, options.UseMappedObjects))); 51 | 52 | if (options.CheckLineNumers) 53 | { 54 | CheckLineNumbers(this.Commands); 55 | } 56 | } 57 | 58 | IEnumerable GetAllGCodeLines(Stream stream) 59 | { 60 | StreamReader reader = new StreamReader(stream); 61 | while (!reader.EndOfStream) 62 | { 63 | var lineStr = reader.ReadLine(); 64 | var line = new GCodeFileLine(lineStr); 65 | yield return line; 66 | } 67 | } 68 | 69 | void CheckCRC(GCodeFileLine[] gcodeLines) 70 | { 71 | for (int i = 0; i < gcodeLines.Length; i++) 72 | { 73 | if (!gcodeLines[i].IsChecksumValid) 74 | { 75 | var expectedCrc = CRC.Calculate(gcodeLines[i].GCode); 76 | throw new Exception($"Checksum is invalid on line {i+1}: {gcodeLines[i].OriginalString}, Expected CRC: {expectedCrc}"); 77 | } 78 | } 79 | } 80 | 81 | void CheckLineNumbers(IEnumerable commands) 82 | { 83 | var lineNumCommands = commands.Where(c => c.CommandType == CommandType.N).ToArray(); 84 | for (int i=0; i c.CommandType != CommandType.N).ToArray(); 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /GCodeNet/GCodeFileLine.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace GCodeNet 4 | { 5 | internal class GCodeFileLine 6 | { 7 | public string OriginalString { get; private set; } 8 | public string Comment { get; private set; } 9 | public int? Checksum { get; private set; } 10 | public string GCode { get; private set; } 11 | 12 | public bool IsChecksumValid { get; private set; } 13 | 14 | public GCodeFileLine(string line) 15 | { 16 | OriginalString = line; 17 | line = GetComment(line); 18 | line = GetChecksum(line); 19 | ValidateChecksum(line); 20 | GCode = line; 21 | } 22 | 23 | string GetComment(string str) 24 | { 25 | var idx = str.IndexOf(';'); 26 | if (idx >= 0) 27 | { 28 | Comment = str.Substring(idx + 1); 29 | return str.Substring(0, idx); 30 | } 31 | Comment = ""; 32 | return str; 33 | } 34 | 35 | void ValidateChecksum(string line) 36 | { 37 | if (Checksum == null) 38 | { 39 | IsChecksumValid = true; 40 | return; 41 | } 42 | 43 | IsChecksumValid = Checksum == CRC.Calculate(line); 44 | } 45 | 46 | string GetChecksum(string str) 47 | { 48 | var idx = str.LastIndexOf('*'); 49 | if (idx >= 0) 50 | { 51 | Checksum = int.Parse(str.Substring(idx+1)); 52 | return str.Substring(0, idx); 53 | } 54 | Checksum = null; 55 | return str; 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /GCodeNet/GCodeFileOptions.cs: -------------------------------------------------------------------------------- 1 | namespace GCodeNet 2 | { 3 | public class GCodeFileOptions 4 | { 5 | public bool CheckCRC { get; set; } = true; 6 | public bool CheckLineNumers { get; set; } = true; 7 | public bool UseMappedObjects { get; set; } = true; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /GCodeNet/GCodeNet.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard2.0 4 | en 5 | 6 | -------------------------------------------------------------------------------- /GCodeNet/Reflection/CommandReflection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | 6 | namespace GCodeNet 7 | { 8 | public static class CommandReflection 9 | { 10 | static Dictionary, Type> typesLookup = new Dictionary, Type>(); 11 | 12 | static Dictionary propsLookup = new Dictionary(); 13 | 14 | static CommandReflection() 15 | { 16 | var assembly = Assembly.GetExecutingAssembly(); 17 | AddMappedTypesFromAssembly(assembly); 18 | } 19 | 20 | public static void ClearMappings() 21 | { 22 | typesLookup.Clear(); 23 | propsLookup.Clear(); 24 | } 25 | public static void AddMappedTypesFromAssembly(Assembly assembly) 26 | { 27 | foreach (Type type in assembly.GetTypes()) 28 | { 29 | if (type.IsSubclassOf(typeof(CommandMapping))) 30 | { 31 | AddMappedType(type); 32 | } 33 | } 34 | } 35 | 36 | public static void AddMappedType(Type type) 37 | { 38 | if (!type.IsSubclassOf(typeof(CommandMapping))) 39 | { 40 | throw new Exception("Can only map a type derived from CommandMapping"); 41 | } 42 | 43 | var gcommandAttrib = (CommandAttribute)type.GetCustomAttributes(typeof(CommandAttribute), true).SingleOrDefault(); 44 | if (gcommandAttrib != null) 45 | { 46 | var key = new Tuple(gcommandAttrib.CommandType, gcommandAttrib.CommandSubType); 47 | typesLookup[key] = type; 48 | propsLookup[type] = new CommandReflectionData(type); 49 | } 50 | } 51 | 52 | public static Type GetCommandObjectType(CommandType cmdType, int cmdNum) 53 | { 54 | var key = new Tuple(cmdType, cmdNum); 55 | if (typesLookup.ContainsKey(key)) 56 | { 57 | return typesLookup[key]; 58 | } 59 | return null; 60 | } 61 | 62 | /*public static CommandBase CreateCommandObject(CommandType cmdType, int cmdNum) 63 | { 64 | var type = GetCommandObjectType(cmdType, cmdNum); 65 | if (type != null) 66 | { 67 | return (CommandBase)Activator.CreateInstance(type); 68 | } 69 | return new Command(cmdType, cmdNum); 70 | }*/ 71 | 72 | public static CommandReflectionData GetMappedProperties(CommandType cmdType, int cmdNum) 73 | { 74 | var type = GetCommandObjectType(cmdType, cmdNum); 75 | return GetReflectionData(type); 76 | } 77 | 78 | public static CommandReflectionData GetReflectionData(Type type) 79 | { 80 | if (!propsLookup.ContainsKey(type)) 81 | { 82 | AddMappedType(type); 83 | } 84 | 85 | if (propsLookup.ContainsKey(type)) 86 | { 87 | return propsLookup[type]; 88 | } 89 | throw new Exception("There is no mapped command for this type"); 90 | } 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /GCodeNet/Reflection/CommandReflectionData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | 6 | namespace GCodeNet 7 | { 8 | public class CommandReflectionData 9 | { 10 | public Type Type { get; private set; } 11 | 12 | public Dictionary MappedProperties { get; private set; } 13 | 14 | public CommandReflectionData(Type type) 15 | { 16 | this.Type = type; 17 | 18 | MappedProperties = new Dictionary(); 19 | 20 | foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)) 21 | { 22 | var attrib = (ParameterTypeAttribute)prop.GetCustomAttributes(typeof(ParameterTypeAttribute), true).SingleOrDefault(); 23 | if (attrib != null) 24 | { 25 | MappedProperties[attrib.Param] = prop; 26 | } 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GCodeNet 2 | Import, export, and manipulate GCode for 3d printing. 3 | 4 | [GCode format](http://reprap.org/wiki/G-code) 5 | 6 | ## Create a GCode command 7 | ``` 8 | //Create a G1 command (Rapid Linear Movement) 9 | var cmd = new Command(CommandType.G, 1); 10 | cmd.SetParameterValue(ParameterType.X, 10); 11 | cmd.SetParameterValue(ParameterType.Y, 20); 12 | 13 | //Convert to GCode 14 | Console.WriteLine(cmd.ToGCode()); //Output: "G1 X10 Y20" 15 | 16 | //Convert to GCode with the CRC 17 | Console.WriteLine(cmd.ToGCode(addCrc: true)); //Output: "G1 X10 Y20*116" 18 | 19 | //Convert to GCode with the CRC and a line number 20 | Console.WriteLine(cmd.ToGCode(addCrc: true, lineNumber: 4)); //Output: "N4 G1 X10 Y20*46" 21 | ``` 22 | 23 | ## Create a command from GCode 24 | ``` 25 | var cmd = Command.Parse("G1 X10 Y20"); 26 | 27 | Console.WriteLine(cmd.CommandType); //Output: "G" 28 | Console.WriteLine(cmd.CommandSubType); //Output: "1" 29 | Console.WriteLine(cmd.GetParameterValue(ParameterType.X)); //Output: "10" 30 | Console.WriteLine(cmd.GetParameterValue(ParameterType.Y)); //Output: "20" 31 | ``` 32 | 33 | ## Create GCode using a mapped command object 34 | ``` 35 | var cmd = new RapidLinearMove(); 36 | cmd.MoveX = 10; 37 | cmd.MoveY = 20; 38 | 39 | Console.WriteLine(cmd.CommandType); //Output: "G" 40 | Console.WriteLine(cmd.CommandSubType); //Output: "1" 41 | Console.WriteLine(cmd.GetParameterValue(ParameterType.X)); //Output: "10" 42 | Console.WriteLine(cmd.GetParameterValue(ParameterType.Y)); //Output: "20" 43 | 44 | Console.WriteLine(cmd.ToGCode()); //Output: "G1 X10 Y20 S0" 45 | ``` 46 | 47 | ## Create a mapped command object from GCode 48 | ``` 49 | var cmd = CommandMapping.Parse("G1 X10 Y20") as RapidLinearMove; 50 | 51 | Console.WriteLine(cmd.CommandType); //Output: "G" 52 | Console.WriteLine(cmd.CommandSubType); //Output: "1" 53 | Console.WriteLine(cmd.MoveX); //Output: "10" 54 | Console.WriteLine(cmd.MoveY); //Output: "20" 55 | Console.WriteLine(cmd.ToGCode()); //Output: "G1 X10 Y20 S0" 56 | ``` 57 | -------------------------------------------------------------------------------- /UnitTests/BoolType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using GCodeNet; 4 | 5 | namespace TestProject 6 | { 7 | [Command(CommandType.M,999)] 8 | class BoolClass : CommandMapping 9 | { 10 | [ParameterType(ParameterType.X)] 11 | public bool X { get; set; } 12 | 13 | } 14 | 15 | [TestFixture] 16 | public class BoolType 17 | { 18 | [Test] 19 | public void TestBool() 20 | { 21 | CommandReflection.AddMappedType(typeof(BoolClass)); 22 | var c1 = (BoolClass)CommandMapping.Parse("M999 X-1.1"); 23 | Assert.IsTrue(c1.X == true); 24 | Assert.IsTrue(c1.ToGCode() == "M999 X"); 25 | var c2 = (BoolClass)CommandMapping.Parse("M999 X1.1"); 26 | Assert.IsTrue(c2.X == true); 27 | Assert.IsTrue(c2.ToGCode() == "M999 X"); 28 | var c3 = (BoolClass)CommandMapping.Parse("M999 X1"); 29 | Assert.IsTrue(c3.X == true); 30 | Assert.IsTrue(c3.ToGCode() == "M999 X"); 31 | var c4 = (BoolClass)CommandMapping.Parse("M999 X"); 32 | Assert.IsTrue(c4.X == true); 33 | Assert.IsTrue(c4.ToGCode() == "M999 X"); 34 | var c5 = (BoolClass)CommandMapping.Parse("M999"); 35 | Assert.IsTrue(c5.X == false); 36 | Assert.IsTrue(c5.ToGCode() == "M999"); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /UnitTests/ByteType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using GCodeNet; 4 | 5 | namespace TestProject 6 | { 7 | [Command(CommandType.M,999)] 8 | class ByteClass : CommandMapping 9 | { 10 | [ParameterType(ParameterType.X)] 11 | public byte X { get; set; } 12 | [ParameterType(ParameterType.Y)] 13 | public byte? Y { get; set; } 14 | } 15 | 16 | [TestFixture] 17 | public class ByteType 18 | { 19 | [Test] 20 | public void TestByte() 21 | { 22 | CommandReflection.AddMappedType(typeof(ByteClass)); 23 | var c2 = (ByteClass)CommandMapping.Parse("M999 X1.1"); 24 | Assert.IsTrue(c2.X == 1); 25 | Assert.IsTrue(c2.ToGCode() == "M999 X1"); 26 | var c3 = (ByteClass)CommandMapping.Parse("M999 X1"); 27 | Assert.IsTrue(c3.X == 1); 28 | Assert.IsTrue(c3.ToGCode() == "M999 X1"); 29 | var c4 = (ByteClass)CommandMapping.Parse("M999 X"); 30 | Assert.IsTrue(c4.X == 0); 31 | Assert.IsTrue(c4.ToGCode() == "M999 X0"); 32 | var c5 = (ByteClass)CommandMapping.Parse("M999"); 33 | Assert.IsTrue(c5.X == 0); 34 | Assert.IsTrue(c5.ToGCode() == "M999 X0"); 35 | } 36 | 37 | [Test] 38 | public void TestNullableByte() 39 | { 40 | CommandReflection.AddMappedType(typeof(ByteClass)); 41 | var c2 = (ByteClass)CommandMapping.Parse("M999 Y1.1"); 42 | Assert.IsTrue(c2.Y == 1); 43 | Assert.IsTrue(c2.ToGCode() == "M999 X0 Y1"); 44 | var c3 = (ByteClass)CommandMapping.Parse("M999 Y1"); 45 | Assert.IsTrue(c3.Y == 1); 46 | Assert.IsTrue(c3.ToGCode() == "M999 X0 Y1"); 47 | var c4 = (ByteClass)CommandMapping.Parse("M999 Y"); 48 | Assert.IsTrue(c4.Y == null); 49 | Assert.IsTrue(c4.ToGCode() == "M999 X0"); 50 | var c5 = (ByteClass)CommandMapping.Parse("M999"); 51 | Assert.IsTrue(c5.Y == null); 52 | Assert.IsTrue(c5.ToGCode() == "M999 X0"); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /UnitTests/CommandTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using GCodeNet; 4 | using System.Linq; 5 | 6 | namespace TestProject 7 | { 8 | [TestFixture] 9 | public class CommandTest 10 | { 11 | [Test] 12 | public void CommandCtor() 13 | { 14 | Command cmd = new Command(CommandType.G, 3); 15 | Assert.IsTrue(cmd.ToGCode() == "G3"); 16 | } 17 | 18 | [Test] 19 | public void SetParameterValue() 20 | { 21 | Command cmd = new Command(CommandType.G, 3); 22 | cmd.SetParameterValue(ParameterType.X, -23); 23 | Assert.IsTrue(cmd.ToGCode() == "G3 X-23"); 24 | } 25 | 26 | [Test] 27 | public void SetParameterEmptyValue() 28 | { 29 | Command cmd = new Command(CommandType.G, 3); 30 | cmd.SetParameterValue(ParameterType.X, null); 31 | Assert.IsTrue(cmd.ToGCode() == "G3 X"); 32 | } 33 | 34 | [Test] 35 | public void ToGCodeWithLineNumber() 36 | { 37 | Command cmd = new Command(CommandType.G, 3); 38 | cmd.SetParameterValue(ParameterType.X, -23.1234m); 39 | Assert.IsTrue(cmd.ToGCode(false, 5) == "N5 G3 X-23.1234"); 40 | } 41 | 42 | [Test] 43 | public void ToGCodeWithCRC() 44 | { 45 | Command cmd = new Command(CommandType.G, 3); 46 | cmd.SetParameterValue(ParameterType.X, -23); 47 | Assert.IsTrue(cmd.ToGCode(true) == "G3 X-23*32"); 48 | } 49 | 50 | [Test] 51 | public void ToGCodeWithLineNumberAndCRC() 52 | { 53 | Command cmd = new Command(CommandType.G, 3); 54 | cmd.SetParameterValue(ParameterType.X, -23); 55 | Assert.IsTrue(cmd.ToGCode(true, 5) == "N5 G3 X-23*123"); 56 | } 57 | 58 | [Test] 59 | public void CommandParse() 60 | { 61 | var cmd = Command.Parse("G1 X Y-3 Z1.4"); 62 | Assert.IsTrue(cmd.CommandType == CommandType.G); 63 | Assert.IsTrue(cmd.CommandSubType == 1); 64 | var parameters = cmd.GetParameters().ToArray(); 65 | Assert.IsTrue(parameters[0] == ParameterType.X); 66 | Assert.IsTrue(parameters[1] == ParameterType.Y); 67 | Assert.IsTrue(parameters[2] == ParameterType.Z); 68 | 69 | Assert.IsTrue(cmd.GetParameterValue(ParameterType.X) == null); 70 | Assert.IsTrue((decimal)cmd.GetParameterValue(ParameterType.Y) == -3); 71 | Assert.IsTrue((decimal)cmd.GetParameterValue(ParameterType.Z) == 1.4m); 72 | } 73 | 74 | [Test] 75 | public void CommandParseLowercase() 76 | { 77 | var cmd = Command.Parse("g1 x"); 78 | Assert.IsTrue(cmd.CommandType == CommandType.G); 79 | Assert.IsTrue(cmd.CommandSubType == 1); 80 | var parameters = cmd.GetParameters().ToArray(); 81 | Assert.IsTrue(parameters[0] == ParameterType.X); 82 | 83 | Assert.IsTrue(cmd.GetParameterValue(ParameterType.X) == null); 84 | } 85 | 86 | [Test] 87 | public void CommandParseMultipleCommands() 88 | { 89 | Assert.Catch(typeof(Exception), () => { var cmd = Command.Parse("N2 G1"); }); 90 | } 91 | 92 | [Test] 93 | public void CommandParseCrcException() 94 | { 95 | Assert.Catch(typeof(Exception), () => { var cmd = Command.Parse("G1*34"); }); 96 | } 97 | 98 | [Test] 99 | public void CommandParseCommentException() 100 | { 101 | Assert.Catch(typeof(Exception), () => { var cmd = Command.Parse("G1;comment"); }); 102 | 103 | } 104 | 105 | [Test] 106 | public void CommandParseEmptyStringException() 107 | { 108 | Assert.Catch(typeof(Exception), () => { var cmd = Command.Parse(""); }); 109 | } 110 | 111 | [Test] 112 | public void CommandParseNoSubtypeException() 113 | { 114 | Assert.Catch(typeof(Exception), () => { var cmd = Command.Parse("G"); }); 115 | } 116 | 117 | [Test] 118 | public void CommandParseWhitespace() 119 | { 120 | var cmd = Command.Parse("M \n 1 \t\t\t X \t \n\n 23"); 121 | Assert.IsTrue(cmd.CommandType == CommandType.M); 122 | Assert.IsTrue(cmd.CommandSubType == 1); 123 | Assert.IsTrue((decimal)cmd.GetParameterValue( ParameterType.X) == 23m); 124 | } 125 | 126 | [Test] 127 | public void CommandParseNoWhitespace() 128 | { 129 | var cmd = Command.Parse("M1X23YZ"); 130 | Assert.IsTrue(cmd.CommandType == CommandType.M); 131 | Assert.IsTrue(cmd.CommandSubType == 1); 132 | Assert.IsTrue((decimal)cmd.GetParameterValue(ParameterType.X) == 23m); 133 | Assert.IsTrue(cmd.GetParameterValue(ParameterType.Y) == null); 134 | Assert.IsTrue(cmd.GetParameterValue(ParameterType.Z) == null); 135 | } 136 | 137 | [Test] 138 | public void CommandParseInvalidCommandTypeException() 139 | { 140 | Assert.Catch(typeof(Exception), () => { var cmd = Command.Parse("Z3"); }); 141 | } 142 | 143 | [Test] 144 | public void CommandParseInvalidParameterException() 145 | { 146 | Assert.Catch(typeof(Exception), () => { var cmd = Command.Parse("M3 A"); }); 147 | } 148 | 149 | [Test] 150 | public void CommandParseNoCommandTypeException() 151 | { 152 | Assert.Catch(typeof(Exception), () => { var cmd = Command.Parse("34"); }); 153 | } 154 | 155 | [Test] 156 | public void CommandParseLeadingZeros() 157 | { 158 | var cmd = Command.Parse("G0003 X00020"); 159 | Assert.IsTrue(cmd.CommandSubType == 3); 160 | Assert.IsTrue((decimal)cmd.GetParameterValue(ParameterType.X) == 20); 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /UnitTests/DecimalType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using GCodeNet; 4 | 5 | namespace TestProject 6 | { 7 | [Command(CommandType.M,999)] 8 | class DecimalClass : CommandMapping 9 | { 10 | [ParameterType(ParameterType.X)] 11 | public decimal X { get; set; } 12 | [ParameterType(ParameterType.Y)] 13 | public decimal? Y { get; set; } 14 | } 15 | 16 | [TestFixture] 17 | public class DecimalType 18 | { 19 | [Test] 20 | public void TestDecimal() 21 | { 22 | CommandReflection.AddMappedType(typeof(DecimalClass)); 23 | var c1 = (DecimalClass)CommandMapping.Parse("M999 X-1.1"); 24 | Assert.IsTrue(c1.X == -1.1m); 25 | Assert.IsTrue(c1.ToGCode() == "M999 X-1.1"); 26 | var c2 = (DecimalClass)CommandMapping.Parse("M999 X1.1"); 27 | Assert.IsTrue(c2.X == 1.1m); 28 | Assert.IsTrue(c2.ToGCode() == "M999 X1.1"); 29 | var c3 = (DecimalClass)CommandMapping.Parse("M999 X1"); 30 | Assert.IsTrue(c3.X == 1); 31 | Assert.IsTrue(c3.ToGCode() == "M999 X1"); 32 | var c4 = (DecimalClass)CommandMapping.Parse("M999 X"); 33 | Assert.IsTrue(c4.X == 0); 34 | Assert.IsTrue(c4.ToGCode() == "M999 X0"); 35 | var c5 = (DecimalClass)CommandMapping.Parse("M999"); 36 | Assert.IsTrue(c5.X == 0); 37 | Assert.IsTrue(c5.ToGCode() == "M999 X0"); 38 | } 39 | 40 | [Test] 41 | public void TestNullableDecimal() 42 | { 43 | CommandReflection.AddMappedType(typeof(DecimalClass)); 44 | var c1 = (DecimalClass)CommandMapping.Parse("M999 Y-1.1"); 45 | Assert.IsTrue(c1.Y == -1.1m); 46 | Assert.IsTrue(c1.ToGCode() == "M999 X0 Y-1.1"); 47 | var c2 = (DecimalClass)CommandMapping.Parse("M999 Y1.1"); 48 | Assert.IsTrue(c2.Y == 1.1m); 49 | Assert.IsTrue(c2.ToGCode() == "M999 X0 Y1.1"); 50 | var c3 = (DecimalClass)CommandMapping.Parse("M999 Y1"); 51 | Assert.IsTrue(c3.Y == 1); 52 | Assert.IsTrue(c3.ToGCode() == "M999 X0 Y1"); 53 | var c4 = (DecimalClass)CommandMapping.Parse("M999 Y"); 54 | Assert.IsTrue(c4.Y == null); 55 | Assert.IsTrue(c4.ToGCode() == "M999 X0"); 56 | var c5 = (DecimalClass)CommandMapping.Parse("M999"); 57 | Assert.IsTrue(c5.Y == null); 58 | Assert.IsTrue(c4.ToGCode() == "M999 X0"); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /UnitTests/DoubleType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using GCodeNet; 4 | 5 | namespace TestProject 6 | { 7 | [Command(CommandType.M,999)] 8 | class DoubleClass : CommandMapping 9 | { 10 | [ParameterType(ParameterType.X)] 11 | public double X { get; set; } 12 | [ParameterType(ParameterType.Y)] 13 | public double? Y { get; set; } 14 | } 15 | 16 | [TestFixture] 17 | public class DoubleType 18 | { 19 | [Test] 20 | public void TestDouble() 21 | { 22 | CommandReflection.AddMappedType(typeof(DoubleClass)); 23 | var c1 = (DoubleClass)CommandMapping.Parse("M999 X-1.1"); 24 | Assert.IsTrue(c1.X == -1.1); 25 | Assert.IsTrue(c1.ToGCode() == "M999 X-1.1"); 26 | var c2 = (DoubleClass)CommandMapping.Parse("M999 X1.1"); 27 | Assert.IsTrue(c2.X == 1.1); 28 | Assert.IsTrue(c2.ToGCode() == "M999 X1.1"); 29 | var c3 = (DoubleClass)CommandMapping.Parse("M999 X1"); 30 | Assert.IsTrue(c3.X == 1); 31 | Assert.IsTrue(c3.ToGCode() == "M999 X1"); 32 | var c4 = (DoubleClass)CommandMapping.Parse("M999 X"); 33 | Assert.IsTrue(c4.X == 0); 34 | Assert.IsTrue(c4.ToGCode() == "M999 X0"); 35 | var c5 = (DoubleClass)CommandMapping.Parse("M999"); 36 | Assert.IsTrue(c5.X == 0); 37 | Assert.IsTrue(c5.ToGCode() == "M999 X0"); 38 | } 39 | 40 | [Test] 41 | public void TestNullableDouble() 42 | { 43 | CommandReflection.AddMappedType(typeof(DoubleClass)); 44 | var c1 = (DoubleClass)CommandMapping.Parse("M999 Y-1.1"); 45 | Assert.IsTrue(c1.Y == -1.1); 46 | Assert.IsTrue(c1.ToGCode() == "M999 X0 Y-1.1"); 47 | var c2 = (DoubleClass)CommandMapping.Parse("M999 Y1.1"); 48 | Assert.IsTrue(c2.Y == 1.1); 49 | Assert.IsTrue(c2.ToGCode() == "M999 X0 Y1.1"); 50 | var c3 = (DoubleClass)CommandMapping.Parse("M999 Y1"); 51 | Assert.IsTrue(c3.Y == 1); 52 | Assert.IsTrue(c3.ToGCode() == "M999 X0 Y1"); 53 | var c4 = (DoubleClass)CommandMapping.Parse("M999 Y"); 54 | Assert.IsTrue(c4.Y == null); 55 | Assert.IsTrue(c4.ToGCode() == "M999 X0"); 56 | var c5 = (DoubleClass)CommandMapping.Parse("M999"); 57 | Assert.IsTrue(c5.Y == null); 58 | Assert.IsTrue(c5.ToGCode() == "M999 X0"); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /UnitTests/EnumType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using GCodeNet; 4 | 5 | namespace TestProject 6 | { 7 | enum EnumObject 8 | { 9 | A = 1, 10 | B = 2, 11 | } 12 | 13 | [Command(CommandType.M,999)] 14 | class EnumClass : CommandMapping 15 | { 16 | [ParameterType(ParameterType.X)] 17 | public EnumObject X { get; set; } 18 | [ParameterType(ParameterType.Y)] 19 | public EnumObject? Y { get; set; } 20 | } 21 | 22 | [TestFixture] 23 | public class EnumType 24 | { 25 | [Test] 26 | public void TestEnum() 27 | { 28 | CommandReflection.AddMappedType(typeof(EnumClass)); 29 | var c1 = (EnumClass)CommandMapping.Parse("M999 X1"); 30 | Assert.IsTrue(c1.X == EnumObject.A); 31 | Assert.IsTrue(c1.ToGCode() == "M999 X1"); 32 | var c2 = (EnumClass)CommandMapping.Parse("M999 X2"); 33 | Assert.IsTrue(c2.X == EnumObject.B); 34 | Assert.IsTrue(c2.ToGCode() == "M999 X2"); 35 | var c3 = (EnumClass)CommandMapping.Parse("M999 X5"); 36 | Assert.IsTrue(c3.X == (EnumObject)5); 37 | Assert.IsTrue(c3.ToGCode() == "M999 X5"); 38 | var c4 = (EnumClass)CommandMapping.Parse("M999 X"); 39 | Assert.IsTrue(c4.X == (EnumObject)0); 40 | Assert.IsTrue(c4.ToGCode() == "M999 X0"); 41 | var c5 = (EnumClass)CommandMapping.Parse("M999"); 42 | Assert.IsTrue(c5.X == (EnumObject)0); 43 | Assert.IsTrue(c5.ToGCode() == "M999 X0"); 44 | var c6 = (EnumClass)CommandMapping.Parse("M999 X-1.1"); 45 | Assert.IsTrue(c6.X == (EnumObject)(-1)); 46 | Assert.IsTrue(c6.ToGCode() == "M999 X-1"); 47 | } 48 | 49 | [Test] 50 | public void TestNullableEnum() 51 | { 52 | CommandReflection.AddMappedType(typeof(EnumClass)); 53 | var c1 = (EnumClass)CommandMapping.Parse("M999 Y1"); 54 | Assert.IsTrue(c1.Y == EnumObject.A); 55 | Assert.IsTrue(c1.ToGCode() == "M999 X0 Y1"); 56 | var c2 = (EnumClass)CommandMapping.Parse("M999 Y2"); 57 | Assert.IsTrue(c2.Y == EnumObject.B); 58 | Assert.IsTrue(c2.ToGCode() == "M999 X0 Y2"); 59 | var c3 = (EnumClass)CommandMapping.Parse("M999 Y5"); 60 | Assert.IsTrue(c3.Y == (EnumObject)5); 61 | Assert.IsTrue(c3.ToGCode() == "M999 X0 Y5"); 62 | var c4 = (EnumClass)CommandMapping.Parse("M999 Y"); 63 | Assert.IsTrue(c4.Y == null); 64 | Assert.IsTrue(c4.ToGCode() == "M999 X0"); 65 | var c5 = (EnumClass)CommandMapping.Parse("M999"); 66 | Assert.IsTrue(c5.Y == null); 67 | Assert.IsTrue(c5.ToGCode() == "M999 X0"); 68 | var c6 = (EnumClass)CommandMapping.Parse("M999 Y-1.1"); 69 | Assert.IsTrue(c6.Y == (EnumObject)(-1)); 70 | Assert.IsTrue(c6.ToGCode() == "M999 X0 Y-1"); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /UnitTests/GCodeFileTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using GCodeNet; 4 | using GCodeNet.Commands; 5 | 6 | namespace TestProject 7 | { 8 | [TestFixture] 9 | public class GCodeFileTest 10 | { 11 | [Test] 12 | public void MultipleCommands() 13 | { 14 | string gcode = "G0X1\nG0X2\nG0X3"; 15 | GCodeFile file = new GCodeFile(gcode); 16 | Assert.IsTrue((decimal)file.Commands[0].GetParameterValue(ParameterType.X) == 1); 17 | Assert.IsTrue((decimal)file.Commands[1].GetParameterValue(ParameterType.X) == 2); 18 | Assert.IsTrue((decimal)file.Commands[2].GetParameterValue(ParameterType.X) == 3); 19 | } 20 | 21 | [Test] 22 | public void SingleCommand() 23 | { 24 | string gcode = "G0"; 25 | GCodeFile file = new GCodeFile(gcode); 26 | Assert.IsTrue(((CommandBase)file.Commands[0]).CommandType == CommandType.G); 27 | } 28 | 29 | [Test] 30 | public void SingleCommandWithNewLine() 31 | { 32 | string gcode = "G0" + Environment.NewLine; 33 | GCodeFile file = new GCodeFile(gcode); 34 | Assert.IsTrue(((CommandBase)file.Commands[0]).CommandType == CommandType.G); 35 | } 36 | 37 | [Test] 38 | public void MultipleCommandsOnSameLine() 39 | { 40 | string gcode = "G0X1G0X2G0X3"; 41 | GCodeFile file = new GCodeFile(gcode); 42 | Assert.IsTrue((decimal)file.Commands[0].GetParameterValue(ParameterType.X) == 1); 43 | Assert.IsTrue((decimal)file.Commands[1].GetParameterValue(ParameterType.X) == 2); 44 | Assert.IsTrue((decimal)file.Commands[2].GetParameterValue(ParameterType.X) == 3); 45 | } 46 | 47 | [Test] 48 | public void CommandsSplitUpOnDifferentLines() 49 | { 50 | string gcode = "G0\nX1G0\nX2\nG0\nX\n3"; 51 | GCodeFile file = new GCodeFile(gcode); 52 | Assert.IsTrue((decimal)file.Commands[0].GetParameterValue(ParameterType.X) == 1); 53 | Assert.IsTrue((decimal)file.Commands[1].GetParameterValue(ParameterType.X) == 2); 54 | Assert.IsTrue((decimal)file.Commands[2].GetParameterValue(ParameterType.X) == 3); 55 | } 56 | 57 | [Test] 58 | public void CommandsWithComments() 59 | { 60 | string gcode = "G0X1;G1X2\nG1X3"; 61 | GCodeFile file = new GCodeFile(gcode); 62 | Assert.IsTrue((decimal)file.Commands[0].GetParameterValue(ParameterType.X) == 1); 63 | Assert.IsTrue((decimal)file.Commands[1].GetParameterValue(ParameterType.X) == 3); 64 | } 65 | 66 | [Test] 67 | public void CommentsAtStartOfLine() 68 | { 69 | string gcode = ";comment\nG1X1\n;comment"; 70 | GCodeFile file = new GCodeFile(gcode); 71 | Assert.IsTrue((decimal)file.Commands[0].GetParameterValue(ParameterType.X) == 1); 72 | } 73 | 74 | [Test] 75 | public void CrcOnly() 76 | { 77 | GCodeFile file = new GCodeFile("*0"); 78 | Assert.IsTrue(file.Commands.Count == 0); 79 | } 80 | 81 | [Test] 82 | public void InvalidCrcOnly() 83 | { 84 | Assert.Catch(typeof(Exception), () => { var file = new GCodeFile("*2"); }); 85 | } 86 | 87 | [Test] 88 | public void CrcMultipleLines() 89 | { 90 | GCodeFile file = new GCodeFile("G1*118\nG2*117"); 91 | Assert.IsTrue(file.Commands[0].CommandSubType == 1); 92 | Assert.IsTrue(file.Commands[1].CommandSubType == 2); 93 | } 94 | 95 | [Test] 96 | public void CrcAfterComment() 97 | { 98 | GCodeFile file = new GCodeFile("G1;*118"); 99 | Assert.IsTrue(file.Commands[0].CommandSubType == 1); 100 | } 101 | 102 | [Test] 103 | public void CommentAfterCrc() 104 | { 105 | GCodeFile file = new GCodeFile("G1*118;comment"); 106 | Assert.IsTrue(file.Commands[0].CommandSubType == 1); 107 | } 108 | 109 | [Test] 110 | public void EmptyFile() 111 | { 112 | GCodeFile file = new GCodeFile(""); 113 | Assert.IsTrue(file.Commands.Count == 0); 114 | } 115 | 116 | [Test] 117 | public void TwoCrcException() 118 | { 119 | Assert.Catch(typeof(Exception), () => { var file = new GCodeFile("G1*118*118"); }); 120 | } 121 | 122 | [Test] 123 | public void TwoCommentsSameLine() 124 | { 125 | GCodeFile file = new GCodeFile("G1;comment;comment"); 126 | Assert.IsTrue(file.Commands[0].CommandSubType == 1); 127 | } 128 | 129 | [Test] 130 | public void IgnoreCrcCheck() 131 | { 132 | GCodeFileOptions options = new GCodeFileOptions(); 133 | options.CheckCRC = false; 134 | GCodeFile file = new GCodeFile("G1*0", options); 135 | Assert.IsTrue(file.Commands[0].CommandSubType == 1); 136 | } 137 | 138 | [Test] 139 | public void LineNumbersInOrder() 140 | { 141 | GCodeFile file = new GCodeFile("N1N2N3"); 142 | Assert.IsTrue(file.Commands.Count == 3); 143 | } 144 | 145 | [Test] 146 | public void LineNumbersInOrder2() 147 | { 148 | GCodeFile file = new GCodeFile("N78N79N80"); 149 | Assert.IsTrue(file.Commands.Count == 3); 150 | } 151 | 152 | [Test] 153 | public void InvalidOrder() 154 | { 155 | Assert.Catch(typeof(Exception), () => { var file = new GCodeFile("N2N1N3"); }); 156 | } 157 | 158 | [Test] 159 | public void IgnoreInvalidOrder() 160 | { 161 | GCodeFileOptions options = new GCodeFileOptions(); 162 | options.CheckLineNumers = false; 163 | GCodeFile file = new GCodeFile("N2N1N3", options); 164 | Assert.IsTrue(file.Commands.Count == 3); 165 | } 166 | 167 | [Test] 168 | public void UseMappedObjects() 169 | { 170 | GCodeFileOptions options = new GCodeFileOptions(); 171 | options.UseMappedObjects = true; 172 | GCodeFile file = new GCodeFile("G1X1", options); 173 | Assert.IsTrue(file.Commands[0] is RapidLinearMove); 174 | } 175 | 176 | [Test] 177 | public void NotUseMappedObjects() 178 | { 179 | GCodeFileOptions options = new GCodeFileOptions(); 180 | options.UseMappedObjects = false; 181 | GCodeFile file = new GCodeFile("G1X1", options); 182 | Assert.IsTrue(!(file.Commands[0] is RapidLinearMove)); 183 | } 184 | 185 | [Test] 186 | public void ExportWithNoLineNumbersOrCrc() 187 | { 188 | ExportFileOptions options = new ExportFileOptions(); 189 | options.WriteCRC = false; 190 | options.WriteLineNumbers = false; 191 | GCodeFile file = new GCodeFile("G1X1G1X2"); 192 | Assert.IsTrue(file.ToGCode(options) == "G1 X1 S0" + Environment.NewLine + "G1 X2 S0" + Environment.NewLine); 193 | } 194 | 195 | [Test] 196 | public void ExportWithLineNumbersOnly() 197 | { 198 | ExportFileOptions options = new ExportFileOptions(); 199 | options.WriteCRC = false; 200 | options.WriteLineNumbers = true; 201 | GCodeFile file = new GCodeFile("G1X1G1X2"); 202 | Assert.IsTrue(file.ToGCode(options) == "N1 G1 X1 S0" + Environment.NewLine + "N2 G1 X2 S0" + Environment.NewLine); 203 | } 204 | 205 | [Test] 206 | public void ExportWithCrcOnly() 207 | { 208 | ExportFileOptions options = new ExportFileOptions(); 209 | options.WriteCRC = true; 210 | options.WriteLineNumbers = false; 211 | GCodeFile file = new GCodeFile("G1X1G1X2"); 212 | Assert.IsTrue(file.ToGCode(options) == "G1 X1 S0*124" + Environment.NewLine + "G1 X2 S0*127" + Environment.NewLine); 213 | } 214 | 215 | [Test] 216 | public void ExportWithLineNumbersAndCrc() 217 | { 218 | ExportFileOptions options = new ExportFileOptions(); 219 | options.WriteCRC = true; 220 | options.WriteLineNumbers = true; 221 | GCodeFile file = new GCodeFile("G1X1G1X2"); 222 | Assert.IsTrue(file.ToGCode(options) == "N1 G1 X1 S0*35" + Environment.NewLine + "N2 G1 X2 S0*35" + Environment.NewLine); 223 | } 224 | 225 | [Test] 226 | public void RemoveOldLineNumbers() 227 | { 228 | ExportFileOptions options = new ExportFileOptions(); 229 | options.WriteCRC = false; 230 | options.WriteLineNumbers = true; 231 | GCodeFile file = new GCodeFile("N5 G1X1 N6 G1X2"); 232 | Assert.IsTrue(file.ToGCode(options) == "N1 G1 X1 S0" + Environment.NewLine + "N2 G1 X2 S0" + Environment.NewLine); 233 | } 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /UnitTests/IntType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using GCodeNet; 4 | 5 | namespace TestProject 6 | { 7 | [Command(CommandType.M,999)] 8 | class IntClass : CommandMapping 9 | { 10 | [ParameterType(ParameterType.X)] 11 | public int X { get; set; } 12 | [ParameterType(ParameterType.Y)] 13 | public int? Y { get; set; } 14 | } 15 | 16 | [TestFixture] 17 | public class IntType 18 | { 19 | [Test] 20 | public void TestInt() 21 | { 22 | CommandReflection.AddMappedType(typeof(IntClass)); 23 | var c1 = (IntClass)CommandMapping.Parse("M999 X-1.1"); 24 | Assert.IsTrue(c1.X == -1); 25 | Assert.IsTrue(c1.ToGCode() == "M999 X-1"); 26 | var c2 = (IntClass)CommandMapping.Parse("M999 X1.1"); 27 | Assert.IsTrue(c2.X == 1); 28 | Assert.IsTrue(c2.ToGCode() == "M999 X1"); 29 | var c3 = (IntClass)CommandMapping.Parse("M999 X1"); 30 | Assert.IsTrue(c3.X == 1); 31 | Assert.IsTrue(c3.ToGCode() == "M999 X1"); 32 | var c4 = (IntClass)CommandMapping.Parse("M999 X"); 33 | Assert.IsTrue(c4.X == 0); 34 | Assert.IsTrue(c4.ToGCode() == "M999 X0"); 35 | var c5 = (IntClass)CommandMapping.Parse("M999"); 36 | Assert.IsTrue(c5.X == 0); 37 | Assert.IsTrue(c5.ToGCode() == "M999 X0"); 38 | } 39 | 40 | [Test] 41 | public void TestNullableInt() 42 | { 43 | CommandReflection.AddMappedType(typeof(IntClass)); 44 | var c1 = (IntClass)CommandMapping.Parse("M999 Y-1.1"); 45 | Assert.IsTrue(c1.Y == -1); 46 | Assert.IsTrue(c1.ToGCode() == "M999 X0 Y-1"); 47 | var c2 = (IntClass)CommandMapping.Parse("M999 Y1.1"); 48 | Assert.IsTrue(c2.Y == 1); 49 | Assert.IsTrue(c2.ToGCode() == "M999 X0 Y1"); 50 | var c3 = (IntClass)CommandMapping.Parse("M999 Y1"); 51 | Assert.IsTrue(c3.Y == 1); 52 | Assert.IsTrue(c3.ToGCode() == "M999 X0 Y1"); 53 | var c4 = (IntClass)CommandMapping.Parse("M999 Y"); 54 | Assert.IsTrue(c4.Y == null); 55 | Assert.IsTrue(c4.ToGCode() == "M999 X0"); 56 | var c5 = (IntClass)CommandMapping.Parse("M999"); 57 | Assert.IsTrue(c5.Y == null); 58 | Assert.IsTrue(c5.ToGCode() == "M999 X0"); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /UnitTests/M117.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using GCodeNet; 4 | using GCodeNet.Commands; 5 | using System.Linq; 6 | 7 | namespace TestProject 8 | { 9 | [TestFixture] 10 | public class M117Test 11 | { 12 | [Test] 13 | public void M117CommandParse() 14 | { 15 | var cmd = CommandBase.Parse("M117 Hello World"); 16 | Assert.IsTrue(cmd.CommandType == CommandType.M); 17 | Assert.IsTrue(cmd.CommandSubType == 117); 18 | Assert.IsTrue(cmd.ToGCode() == "M117 Hello World"); 19 | } 20 | 21 | [Test] 22 | public void M117CommandParseEmpty() 23 | { 24 | var cmd = CommandBase.Parse("M117"); 25 | Assert.IsTrue(cmd.CommandType == CommandType.M); 26 | Assert.IsTrue(cmd.CommandSubType == 117); 27 | Assert.IsTrue(cmd.ToGCode() == "M117"); 28 | } 29 | 30 | [Test] 31 | public void M117CommandNoWhiteSpace() 32 | { 33 | var cmd = CommandBase.Parse("M117Hello"); 34 | Assert.IsTrue(cmd.CommandType == CommandType.M); 35 | Assert.IsTrue(cmd.CommandSubType == 117); 36 | Assert.IsTrue(cmd.ToGCode() == "M117 Hello"); 37 | } 38 | 39 | [Test] 40 | public void M117CommandExtraWhiteSpace() 41 | { 42 | var cmd = CommandBase.Parse(" M117 Hello"); 43 | Assert.IsTrue(cmd.CommandType == CommandType.M); 44 | Assert.IsTrue(cmd.CommandSubType == 117); 45 | Assert.IsTrue(cmd.ToGCode() == "M117 Hello"); 46 | } 47 | 48 | [Test] 49 | public void M117CommandParseMultiple() 50 | { 51 | var cmd = CommandBase.Parse("M117 Hello M117 Hello"); 52 | Assert.IsTrue(cmd.CommandType == CommandType.M); 53 | Assert.IsTrue(cmd.CommandSubType == 117); 54 | Assert.IsTrue(cmd.ToGCode() == "M117 Hello M117 Hello"); 55 | } 56 | 57 | [Test] 58 | public void M117CommandParseInvalidChars() 59 | { 60 | var cmd = CommandBase.Parse("M117 Hello:?.+-';*"); 61 | Assert.IsTrue(cmd.CommandType == CommandType.M); 62 | Assert.IsTrue(cmd.CommandSubType == 117); 63 | Assert.IsTrue(cmd.ToGCode() == "M117 Hello:?.+-';*"); 64 | } 65 | 66 | [Test] 67 | public void M117FileParserGBeforeM() 68 | { 69 | string gcode = "G1M117 Hello"; 70 | GCodeFile file = new GCodeFile(gcode); 71 | Assert.IsTrue(file.Commands.Count ==2); 72 | Assert.IsTrue(file.Commands[0].CommandType == CommandType.G); 73 | Assert.IsTrue(file.Commands[1].CommandType == CommandType.M); 74 | Assert.IsTrue(file.Commands[1].ToGCode() == "M117 Hello"); 75 | } 76 | 77 | [Test] 78 | public void M117FileParserGAfterM() 79 | { 80 | string gcode = "M117G1 Hello"; 81 | GCodeFile file = new GCodeFile(gcode); 82 | Assert.IsTrue(file.Commands.Count == 1); 83 | Assert.IsTrue(file.Commands[0].CommandType == CommandType.M); 84 | Assert.IsTrue(file.Commands[0].ToGCode() == "M117 G1 Hello"); 85 | } 86 | 87 | [Test] 88 | public void M117FileParserMultipleLines() 89 | { 90 | string gcode = "M117 Hello\r\nM117 World"; 91 | GCodeFile file = new GCodeFile(gcode); 92 | Assert.IsTrue(file.Commands.Count == 2); 93 | Assert.IsTrue(file.Commands[0].CommandType == CommandType.M); 94 | Assert.IsTrue(file.Commands[0].ToGCode() == "M117 Hello"); 95 | Assert.IsTrue(file.Commands[1].CommandType == CommandType.M); 96 | Assert.IsTrue(file.Commands[1].ToGCode() == "M117 World"); 97 | } 98 | 99 | [Test] 100 | public void M117FileParserWithComment() 101 | { 102 | string gcode = "M117 Hello;World"; 103 | GCodeFile file = new GCodeFile(gcode); 104 | Assert.IsTrue(file.Commands.Count == 1); 105 | Assert.IsTrue(file.Commands[0].CommandType == CommandType.M); 106 | Assert.IsTrue(file.Commands[0].ToGCode() == "M117 Hello"); 107 | } 108 | 109 | [Test] 110 | public void M117FileParserWithCRC() 111 | { 112 | string gcode = "M117 Hello*24"; 113 | GCodeFile file = new GCodeFile(gcode); 114 | Assert.IsTrue(file.Commands.Count == 1); 115 | Assert.IsTrue(file.Commands[0].CommandType == CommandType.M); 116 | Assert.IsTrue(file.Commands[0].ToGCode() == "M117 Hello"); 117 | } 118 | } 119 | } -------------------------------------------------------------------------------- /UnitTests/MappedCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using GCodeNet; 4 | using GCodeNet.Commands; 5 | using System.Linq; 6 | 7 | namespace TestProject 8 | { 9 | class MissingAttributeClass : CommandMapping 10 | { 11 | } 12 | 13 | [Command(CommandType.M, 999)] 14 | class CustomCommand : CommandMapping 15 | { 16 | [ParameterType(ParameterType.X)] 17 | public int X { get; set; } 18 | } 19 | 20 | [TestFixture] 21 | public class MappedCommand 22 | { 23 | [Test] 24 | public void RapidLinearMoveTest() 25 | { 26 | var cmd = new RapidLinearMove(); 27 | cmd.MoveX = 1; 28 | cmd.MoveY = 1.2m; 29 | 30 | Assert.IsTrue(cmd.CommandType == CommandType.G); 31 | Assert.IsTrue(cmd.CommandSubType == 1); 32 | 33 | var parameters = cmd.GetParameters().ToArray(); 34 | Assert.IsTrue(parameters[0] == ParameterType.X); 35 | Assert.IsTrue(parameters[1] == ParameterType.Y); 36 | Assert.IsTrue(parameters[2] == ParameterType.S); 37 | 38 | Assert.IsTrue((decimal)cmd.GetParameterValue(ParameterType.X) == 1); 39 | Assert.IsTrue((decimal)cmd.GetParameterValue(ParameterType.Y) == 1.2m); 40 | Assert.IsTrue((decimal)cmd.GetParameterValue(ParameterType.S) == 0); 41 | 42 | Assert.IsTrue(cmd.ToGCode() == "G1 X1 Y1.2 S0"); 43 | } 44 | 45 | [Test] 46 | public void SetExtruderTemperatureTest() 47 | { 48 | var cmd = new SetExtruderTemperature(); 49 | cmd.Temperature = 98; 50 | 51 | Assert.IsTrue(cmd.CommandType == CommandType.M); 52 | Assert.IsTrue(cmd.CommandSubType == 104); 53 | 54 | var parameters = cmd.GetParameters().ToArray(); 55 | Assert.IsTrue(parameters[0] == ParameterType.S); 56 | Assert.IsTrue((decimal)cmd.GetParameterValue(ParameterType.S) == 98); 57 | 58 | Assert.IsTrue(cmd.ToGCode() == "M104 S98"); 59 | } 60 | 61 | [Test] 62 | public void MissingCommandAttribute() 63 | { 64 | Assert.Catch(typeof(Exception), () => { var cmd = new MissingAttributeClass(); }); 65 | } 66 | 67 | [Test] 68 | public void MappingPropertyTest() 69 | { 70 | var cmd = new SetExtruderTemperature(); 71 | cmd.Temperature = 98; 72 | Assert.IsTrue((decimal)cmd.GetParameterValue(ParameterType.S) == 98); 73 | 74 | cmd.SetParameterValue(ParameterType.S, 33); 75 | Assert.IsTrue(cmd.Temperature == 33); 76 | 77 | cmd.SetParameterValue(ParameterType.S, null); 78 | Assert.IsTrue(cmd.Temperature == null); 79 | 80 | cmd.SetParameterValue(ParameterType.S, 1); 81 | cmd.Temperature = null; 82 | Assert.IsTrue(cmd.GetParameterValue(ParameterType.S) == null); 83 | 84 | } 85 | 86 | 87 | [Test] 88 | public void AutoAddCustomCommand() 89 | { 90 | CommandReflection.ClearMappings(); 91 | var cmd = new CustomCommand(); 92 | var g = cmd.ToGCode(); 93 | } 94 | 95 | [Test] 96 | public void CustomCommandMappingException() 97 | { 98 | CommandReflection.ClearMappings(); 99 | Assert.Catch(typeof(Exception), () => { var cmd = CommandMapping.Parse("M999 X"); }); 100 | } 101 | 102 | [Test] 103 | public void CustomCommandAddType() 104 | { 105 | CommandReflection.ClearMappings(); 106 | CommandReflection.AddMappedType(typeof(CustomCommand)); 107 | var cmd = CommandMapping.Parse("M999 X"); 108 | } 109 | 110 | [Test] 111 | public void CustomCommandAddAssembly() 112 | { 113 | CommandReflection.ClearMappings(); 114 | CommandReflection.AddMappedTypesFromAssembly(System.Reflection.Assembly.GetExecutingAssembly()); 115 | var cmd = CommandMapping.Parse("M999 X"); 116 | } 117 | 118 | [Test] 119 | public void AutoAddCustomCommand2() 120 | { 121 | CommandReflection.ClearMappings(); 122 | var cmd = CommandMapping.Parse(typeof(CustomCommand), "M999 X"); 123 | var g = cmd.ToGCode(); 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /UnitTests/UnitTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netcoreapp2.1 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | --------------------------------------------------------------------------------