公司的切割机用的是梅塞尔的等离子切割机,技术部在下发套料图的时候,会将指令文件做简化处理,形式如下:
%
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 }
程序还有好多漏洞,待完善。