公司的切割机用的是梅塞尔的等离子切割机,技术部在下发套料图的时候,会将指令文件做简化处理,形式如下:

%
N4G92X0Y0
N6F5000
N8G71
N10G91
N12G00X561.243Y1277.726
N14M11
N16M09
N18G01X75.071Y160.966
N20G01X-44.119Y75.746
N22G01X86.386Y14.882
N24G01X75.071Y160.966
N26M10
N28G00X53.516Y-0.015
N30M09
N32G01X-75.071Y-160.966
N34G01X44.119Y-75.746
N36G01X-86.386Y-14.882
N38G01X-75.071Y-160.966
N40M10
N42G00X15.005Y495.332
N44M09
N46G01X94.148Y237.483
N48G01X-48.505Y73.015
N50G01X85.359Y19.946

N168G03X2.562Y9.562I-3.5J6.062

 

 查了一下手册,%表示指令文件开始;N指令表示行号,但是我没弄明白为什么行号都是偶数;G命令表示切割与定位。XYIJ表示坐标命令,XY为相对相对坐标增量,IJ表示圆心(由于我公司加工的钢板零件都是直线和圆弧形式,且切割点都为相对增量模式,所以其他命令暂时没有研究)

  以N168G03X2.562Y9.562I-3.5J6.062 这句指令为例:N168,表示是第168个指令行,对切割机无实际作用,可忽略;G03,表示以顺时针方向切割圆弧,起始点为上一命令的切割点,终止点为此命令行的坐标点,即为X2.562Y9.562,圆心为I-3.5J6.062。注意:坐标值皆以上一命令点为基准作增量运算。

  命令代码绝大多数为 G00(走空直线,即切割嘴不点火定位)、G01(切割直线,即切割嘴点火切割定位)、G02/G03(切割圆弧,G02为顺时针切割,G03为逆时针切割)

  I和J为圆心坐标。

下面用C#解析G代码文件:

  1. 先把指令文件中与切割无关的命令过滤掉,生成一个临时的指令文件:



1 public static string GenFile(string filename)
 2         {
 3             StreamReader r_sReader = new StreamReader(filename);
 4             StreamWriter w_sWriter = new StreamWriter(filename + ".tmp");
 5 
 6             string r_Line = string.Empty;
 7             int g_index = -1;
 8             r_Line = r_sReader.ReadLine();
 9             if (r_Line != "%")
10             {
11                 r_sReader.Close();
12                 w_sWriter.Close();
13                 throw new Exception("命令: " + filename + " 的格式不在本程序处理范围内,请联系软件开发人员!");
14             }
15             for (; r_Line != null; r_Line = r_sReader.ReadLine())
16             {
17                 if (Lex.FindKey(r_Line, "G0") && Lex.FindKey(r_Line, 'X'))
18                 {
19                     g_index = r_Line.IndexOf("G0");
20                     r_Line = r_Line.Substring(g_index);
21                     w_sWriter.WriteLine(r_Line);
22                 }
23             }
24             r_sReader.Close();
25             w_sWriter.Close();
26             return filename + ".tmp";
27         }



生成的临时指令文件类似于:

G01X-733.139Y129.272
G01X-61.743Y-62.224
G01X-36.738Y79.589

G03X7.0Y7.0I0.0J7.0
G01X0.0Y534.0
G03X35.0Y35.0I0.0J35.0
G01X185.0Y0.0

这样看起来就清晰多了。只需要处理G命令和相应的坐标了:



1 public static void CalculateLength(string filename, out double CuttingLength, out Double WalkLength, out int FireTimes)
 2         {
 3             CuttingLength = 0.0;
 4             WalkLength = 0.0;
 5             FireTimes = 0;
 6 
 7             Point2D p_start = new Point2D(0.0, 0.0);
 8             Point2D p_end = new Point2D(0.0, 0.0);
 9             Point2D p_center = new Point2D(0.0, 0.0);
10 
11             Arc2D tmp_arc = new Arc2D(p_center, p_start, p_end);
12             LineSegment2D tmp_seg = new LineSegment2D(p_start, p_end);
13             StreamReader r_sReader = new StreamReader(filename);
14             // read line.
15             string r_Line = string.Empty;
16             // instruction appearing in current line.
17             string instruction = string.Empty;
18             #region Relative Coordinate
19             Point2D p_current = new Point2D();
20             for (int i=0; r_Line != null; r_Line = r_sReader.ReadLine(), i++)
21             {
22                 if (r_Line == string.Empty)
23                     continue;
24 
25                 instruction = GetAxisValue(r_Line, 'G');
26                 p_current.X = Convert.ToDouble(GetAxisValue(r_Line, 'X'));
27                 p_current.Y = Convert.ToDouble(GetAxisValue(r_Line, 'Y'));
28 
29                 switch (instruction)
30                 {
31                     case "00":
32                         tmp_seg.EndPt.X = p_current.X;
33                         tmp_seg.EndPt.Y = p_current.Y;
34                         WalkLength += tmp_seg.Length;
35                         FireTimes++;
36                         break;
37                     case "01":
38                         tmp_seg.EndPt.X = p_current.X;
39                         tmp_seg.EndPt.Y = p_current.Y;
40                         CuttingLength += tmp_seg.Length;
41                         break;
42                     case "02":
43                         tmp_arc.CenterPt.X = Convert.ToDouble(GetAxisValue(r_Line, 'I'));
44                         tmp_arc.CenterPt.Y = Convert.ToDouble(GetAxisValue(r_Line, 'J'));
45                         tmp_arc.EndPt.X = p_current.X;
46                         tmp_arc.EndPt.Y = p_current.Y;
47                         CuttingLength += tmp_arc.ClockWiseLength;
48                         break;
49                     case "03":
50                         tmp_arc.CenterPt.X = Convert.ToDouble(GetAxisValue(r_Line, 'I'));
51                         tmp_arc.CenterPt.Y = Convert.ToDouble(GetAxisValue(r_Line, 'J'));
52                         tmp_arc.EndPt.X = p_current.X;
53                         tmp_arc.EndPt.Y = p_current.Y;
54                         CuttingLength += tmp_arc.CounterClockWiseLength;
55                         break;
56                 }
57             }
58             #endregion
59             r_sReader.Close();
60         }



上面的代码有我自定义的几个类,用于计算线段长度和圆弧长度:



1 public static class m_Math
  2     {
  3         public const Double Pi = 3.1415926535;
  4         public const Double E = 2.7182818284;
  5         public const Double _precision = 0.00001;
  6 
  7         public static Double sqrt(Double n)
  8         {
  9             double result = 0.0, delta = 0.0;
 10             double x = 0.618 * n;
 11             do
 12             {
 13                 result = 0.5 * (x + (n / x));
 14                 delta = Math.Abs(x - result);
 15                 x = result;
 16             } while (delta > m_Math._precision);
 17             return result;
 18         }
 19 
 20         public static Double RadianToAngle(Double radian)
 21         {
 22             return (180.0 * radian) / m_Math.Pi;
 23         }
 24 
 25         public static Double AngleToRadian(Double angle)
 26         {
 27             return (m_Math.Pi * angle) / 180.0;
 28         }
 29 
 30         public static double[,] MatrixProduction(double[,] a, double[,] b)
 31         {
 32             int Length=a.Length;
 33             double[,] c=new double[Length,Length];
 34             return c;
 35         }
 36     }
 37 
 38     public class Point2D
 39     {
 40         public Point2D() { }
 41         public Point2D(Double x,Double y)
 42         {
 43             this.X = x;
 44             this.Y = y;
 45         }
 46 
 47         /// <summary>
 48         /// X coordinate figure
 49         /// </summary>
 50         private Double _X;
 51 
 52         /// <summary>
 53         /// Y coordinate figure
 54         /// </summary>
 55         private Double _Y;
 56 
 57         public Double X
 58         {
 59             get
 60             {
 61                 return this._X;
 62             }
 63 
 64             set
 65             {
 66                 this._X = value;
 67             }
 68         }
 69 
 70         public Double Y
 71         {
 72             get
 73             {
 74                 return this._Y;
 75             }
 76 
 77             set
 78             {
 79                 this._Y = value;
 80             }
 81         }
 82 
 83         public Double GetDistanceTo(Point2D p)
 84         {
 85             Double dx = this.X - p.X;
 86             Double dy = this.Y - p.Y;
 87             return Math.Sqrt(dx * dx + dy * dy);
 88         }
 89 
 90         public Vector2D GetVector2DTo(Point2D p)
 91         {
 92             return new Vector2D(p.X - this.X, p.Y - this.Y);
 93         }
 94 
 95         public void SetValueTo(Point2D p)
 96         {
 97             this.X = p.X;
 98             this.Y = p.Y;
 99         }
100 
101         public bool IsEqualTo(Point2D p)
102         {
103             return (this.X == p.X && this.Y == p.Y);
104         }
105 
106         public bool IsOnSeg(LineSegment2D seg)
107         {
108             if (this.IsOnSegLine(seg))
109             {
110                 return (this.X <= Math.Max(seg.StartPt.X, seg.EndPt.X)) && (this.X >= Math.Min(seg.StartPt.X, seg.EndPt.X));
111             }
112 
113             return false;
114         }
115 
116         public bool IsOnSegLine(LineSegment2D seg)
117         {
118             Vector2D vseg = seg.StartPt.GetVector2DTo(seg.EndPt);
119             Vector2D vthis = seg.StartPt.GetVector2DTo(this);
120             return Math.Abs(vseg.CrossProductTo(vthis)) < m_Math._precision;
121         }
122 
123         public static Point2D operator + (Point2D a, Point2D b)
124         {
125             return new Point2D((a.X + b.X), (a.Y + b.Y));
126         }
127 
128         public static Point2D operator - (Point2D a, Point2D b)
129         {
130             return new Point2D((a.X - b.X), (a.Y - b.Y));
131         }
132 
133         public override String ToString()
134         {
135             return "(" + this.X + "," + this.Y + ")";
136         }
137     }
138 
139     public class LineSegment2D
140     {
141         public LineSegment2D()
142         {
143             this.StartPt = new Point2D();
144             this.EndPt = new Point2D();
145 
146         }
147         public LineSegment2D(Point2D p1, Point2D p2)
148         {
149             this.StartPt = new Point2D();
150             this.EndPt = new Point2D();
151             this.StartPt.SetValueTo(p1);
152             this.EndPt.SetValueTo(p2);
153         }
154 
155         private Point2D _StartPt;
156         private Point2D _EndPt;
157 
158         public Point2D StartPt
159         {
160             get
161             {
162                 return this._StartPt;
163             }
164 
165             set
166             {
167                 this._StartPt = value;
168             }
169         }
170 
171         public Point2D EndPt
172         {
173             get
174             {
175                 return this._EndPt;
176             }
177 
178             set
179             {
180                 this._EndPt = value;
181             }
182         }
183 
184         public Double Length
185         {
186             get
187             {
188                 return StartPt.GetDistanceTo(EndPt);
189             }
190         }
191 
192         public void SetPt(Point2D p1, Point2D p2)
193         {
194             this.StartPt.X = p1.X;
195             this.StartPt.Y = p1.Y;
196             this.EndPt.X = p2.X;
197             this.EndPt.Y = p2.Y;
198         }
199 
200         public bool IsIntersectionWith(LineSegment2D seg)
201         {
202             Vector2D s1s2 = this.StartPt.GetVector2DTo(seg.StartPt);
203             Vector2D s1e2 = this.StartPt.GetVector2DTo(seg.EndPt);
204             Vector2D s1e1 = this.StartPt.GetVector2DTo(this.EndPt);
205             double straddle1 = (s1e1.CrossProductTo(s1s2)) * (s1e1.CrossProductTo(s1e2));
206             Vector2D s2s1 = seg.StartPt.GetVector2DTo(this.StartPt);
207             Vector2D s2e1 = seg.StartPt.GetVector2DTo(this.EndPt);
208             Vector2D s2e2 = seg.StartPt.GetVector2DTo(seg.EndPt);
209             double straddle2 = (s2e2.CrossProductTo(s2s1)) * (s2e2.CrossProductTo(s2e1));
210 
211             if (straddle1 < 0 && straddle2 < 0)
212             {
213                 return true;
214             }
215             else 
216             {
217                 return (this.StartPt.IsOnSeg(seg) || this.EndPt.IsOnSeg(seg) || seg.StartPt.IsOnSeg(this) || seg.EndPt.IsOnSeg(this));
218             }
219         }
220     }
221 
222     public class Vector2D
223     {
224         public Vector2D() { }
225         public Vector2D(Double x, Double y)
226         {
227             this.X = x;
228             this.Y = y;
229         }
230         /// <summary>
231         /// X coordinate figure
232         /// </summary>
233         private Double _X;
234         /// <summary>
235         /// Y coordinate figure
236         /// </summary>
237         private Double _Y;
238 
239         public Double X
240         {
241             get
242             {
243                 return this._X;
244             }
245 
246             set
247             {
248                 this._X = value;
249             }
250         }
251 
252         public Double Y
253         {
254             get
255             {
256                 return this._Y;
257             }
258 
259             set
260             {
261                 this._Y = value;
262             }
263         }
264 
265         public Double Length
266         {
267             get
268             {
269                 return Math.Sqrt(this.X * this.X + this.Y * this.Y);
270             }
271         }
272 
273         public Double StartAngle
274         {
275             get
276             {
277                 Double angle = Math.Acos(this.X / this.Length);
278                 if (this.Y<0)
279                 {
280                     return 2 * m_Math.Pi - angle;
281                 }
282                 return angle;
283             }
284         }
285 
286         /// <summary>
287         /// Counter clock wise Cross-Production to Vector2D(v)
288         /// as the two vectors are both on X-Y plane, their Cross-Production is on Z-Axis
289         /// </summary>
290         public Double CrossProductTo(Vector2D v)
291         {
292             return (this.X * v.Y - this.Y * v.X);
293         }
294 
295         public Double GetNormalAngleTo(Vector2D v)
296         {
297             Double DotProduction = this * v;
298             // may be "projection" is 1.00000000002 or some other, in this case, the Math.Acos() method will return NaN(Not a Number).
299             // so if |projection| > 1 and | |projection| - 1 |< _precision, must set it to 1.
300             Double projection = (DotProduction) / (this.Length * v.Length);
301             if (Math.Abs(projection) > 1)
302             {
303                 double delta = Math.Abs(Math.Abs(projection) - 1);
304                 if (delta < m_Math._precision)
305                 {
306                     if (projection > 1)
307                     {
308                         projection = 1;
309                     }
310                     else if (projection < -1)
311                     {
312                         projection = -1;
313                     }
314                 }
315             }
316             return Math.Acos(projection);
317         }
318 
319         public bool IsSameDirectionTo(Vector2D v)
320         {
321             Double DeltaAngle = Math.Abs(v.StartAngle-this.StartAngle) ;
322             return DeltaAngle > 0 && DeltaAngle < m_Math._precision;
323         }
324 
325         public bool IsOppositeDirectionTo(Vector2D v)
326         {
327             Double DeltaAngle = Math.Abs(Math.Abs(v.StartAngle - this.StartAngle)-m_Math.Pi);
328             return DeltaAngle > 0 && DeltaAngle < m_Math._precision;
329         }
330 
331         public bool IsClockWiseTo(Vector2D v)
332         {
333             return this.CrossProductTo(v) < 0;
334         }
335 
336         public bool IsCounterClockWiseTo(Vector2D v)
337         {
338             return this.CrossProductTo(v) > 0;
339         }
340 
341         public Double GetCounterClockWiseAngleTo(Vector2D v)
342         {
343             Double Angle = this.GetNormalAngleTo(v);
344             if (this.IsClockWiseTo(v))
345             {
346                 return 2 * m_Math.Pi - Angle;
347             }
348             return Angle;
349         }
350 
351         public override String ToString()
352         {
353             return "(" + this.X + "," + this.Y + ")";
354         }
355 
356         public static Vector2D operator + (Vector2D a, Vector2D b)
357         {
358             return new Vector2D((a.X + b.X), (a.Y + b.Y));
359         }
360 
361         public static Vector2D operator - (Vector2D a, Vector2D b)
362         {
363             return new Vector2D((a.X - b.X), (a.Y - b.Y));
364         }
365 
366         public static Double operator * (Vector2D a, Vector2D b)
367         {
368             return (a.X * b.X + a.Y * b.Y);
369         }
370     }
371 
372     public class Arc2D
373     {
374         public Arc2D()
375         {
376             this.CenterPt = new Point2D();
377             this.StartPt = new Point2D();
378             this.EndPt = new Point2D();
379         }
380 
381         public Arc2D(Point2D center, Point2D start, Point2D end)
382         {
383             if (Math.Abs(center.GetDistanceTo(start) - center.GetDistanceTo(end)) > m_Math._precision)
384             {
385                 //throw new Exception("Radius length is not equal!");
386                 throw new Exception("半径长度不相等, 不能构成圆!");
387             }
388 
389             this.CenterPt = new Point2D();
390             this.StartPt = new Point2D();
391             this.EndPt = new Point2D();
392 
393             this.CenterPt.SetValueTo(center);
394             this.StartPt.SetValueTo(start);
395             this.EndPt.SetValueTo(end);
396         }
397 
398         private Point2D _CenterPt;
399         private Point2D _StartPt;
400         private Point2D _EndPt;
401 
402         public Point2D CenterPt
403         {
404             get
405             {
406                 return this._CenterPt;
407             }
408 
409             set
410             {
411                 this._CenterPt = value;
412             }
413         }
414 
415         public Point2D StartPt
416         {
417             get
418             {
419                 return this._StartPt;
420             }
421 
422             set
423             {
424                 this._StartPt = value;
425             }
426         }
427 
428         public Point2D EndPt
429         {
430             get
431             {
432                 return this._EndPt;
433             }
434 
435             set
436             {
437                 this._EndPt = value;
438             }
439         }
440 
441         public Vector2D StartRadius
442         {
443             get
444             {
445                 return this.CenterPt.GetVector2DTo(this.StartPt);
446             }
447         }
448 
449         public Vector2D EndRadius
450         {
451             get
452             {
453                 return this.CenterPt.GetVector2DTo(this.EndPt);
454             }
455         }
456 
457         public Double RadiusLength
458         {
459             get
460             {
461                 return this.StartRadius.Length;
462             }
463         }
464 
465         public Double StartAngle
466         {
467             get
468             {
469                 return this.StartRadius.StartAngle;
470             }
471         }
472 
473         public Double EndAngle
474         {
475             get
476             {
477                 return this.EndRadius.StartAngle;
478             }
479         }
480 
481         /// <summary>
482         /// Angle is from StartAngle to EndAngle in Counter-Clock-Wise
483         /// </summary>
484         public Double Angle
485         {
486             get
487             {
488                 Double angle = this.StartRadius.GetCounterClockWiseAngleTo(this.EndRadius);
489                 return angle;
490             }
491         }
492 
493         public Double Bulge
494         {
495             get
496             {
497                 return Math.Tan((this.EndAngle - this.StartAngle) / 4.0);
498             }
499         }
500 
501         public Double CounterClockWiseLength
502         {
503             get
504             {
505                 return this.StartRadius.Length * this.Angle;
506             }
507         }
508 
509         public Double ClockWiseLength
510         {
511             get
512             {
513                 return 2 * m_Math.Pi * this.StartRadius.Length - this.CounterClockWiseLength;
514             }
515         }
516 
517         public Double CounterClockWiseArea
518         {
519             get
520             {
521                 return RadiusLength * RadiusLength * this.Angle;
522             }
523         }
524 
525         public Double ClockWisePatchArea
526         {
527             get
528             {
529                 return 2 * m_Math.Pi * this.RadiusLength * this.RadiusLength - this.CounterClockWiseArea;
530             }
531         }
532     }



Point2D 是二维点类,方便计算线段长度;LineSegment2D是线段类,没有方向,只用于计算长度;

Vector2D二维向量类,用于计算圆弧的圆心角;Arc2D圆弧类,用于计算顺时针与逆时针的圆弧长度。

还有两个方法:

用于检验是否是数字:



public static bool IsDigit(char ch)
        {
            return (ch >= '0' && ch <= '9');
        }



用于获取指令值与坐标值:



1 public static string GetAxisValue(string inLine, char AxisSymbol)
 2         {
 3             string Line = Lex.TrimAllSpace(inLine);
 4             int pos = Line.IndexOf(AxisSymbol);
 5             if (pos == -1)
 6             {
 7                 // if the current line has no AxisSymbol, then it contains no this AxisSymbol value at all.
 8                 return string.Empty;
 9             }
10 
11             //scan the current line until it meets a char that is not a digit.
12             bool DecimalPointFound = false;
13             string m_value = string.Empty;
14             for (int i = pos + 1; i < Line.Length; i++)
15             {
16                 if (Lex.IsDigit(Line[i]))
17                 {
18                     m_value += Line[i];
19                 }
20                 // check whether the '-' is a negative sign or not.
21                 else if (Line[i] == '-')
22                 {
23                     if (i < Line.Length - 1)
24                     {
25                         if ((Line[i - 1] == AxisSymbol) && Lex.IsDigit(Line[i + 1]))
26                         {
27                             m_value += Line[i];
28                         }
29                     }
30                 }
31                 // if the current char is a dot('.') , and both its previous and latter char are digit,
32                 // then it is a decimal point.
33                 else if (Line[i] == '.')
34                 {
35                     if (i < Line.Length - 1)
36                     {
37                         if (Lex.IsDigit(Line[i - 1]) && Lex.IsDigit(Line[i + 1]))
38                         {
39                             if (DecimalPointFound)
40                             {
41                                 // if Decimal Point is Found previously, 
42                                 // then the digits set found is not a digit string(e.g. : 2011.04.17).
43                                 // that is, decimal point only appear once.
44                                 return string.Empty;
45                             }
46                             DecimalPointFound = true;
47                             m_value += Line[i];
48                         }
49                         else if (Lex.IsAlpha(Line[i]))
50                         {
51                             return string.Empty;
52                         }
53                     }
54                 }
55                 else
56                 {
57                     // if the current char is not a digit, then the left ones are not necessary to be scanned.
58                     break;
59                 }
60             }
61             return m_value;
62         }



程序还有好多漏洞,待完善。