369 lines
12 KiB
ObjectPascal
369 lines
12 KiB
ObjectPascal
program GCodeGenerator;
|
|
|
|
uses
|
|
SysUtils, StrUtils, Math;
|
|
|
|
type
|
|
TPoint = record
|
|
X, Y: double;
|
|
end;
|
|
|
|
type
|
|
TStatorParams = record
|
|
BaseDiameter: Double;
|
|
NumberOfRays: Integer;
|
|
RayShape: string;
|
|
RayDiameter: Double;
|
|
RayWidth: Double;
|
|
RayHeight: Double;
|
|
RayLength: Double;
|
|
|
|
RayTopShape: string;
|
|
RayTopDiameter: Double;
|
|
RayTopWidth: Double;
|
|
RayTopHeight: Double;
|
|
|
|
RayCenterOffset: Double;
|
|
WireDiameter: Double;
|
|
NeedleDiameter: Double;
|
|
PathClearance: Double;
|
|
|
|
WorkSpeed :integer;
|
|
end;
|
|
|
|
var
|
|
InputFileName, OutputFileName: string;
|
|
StatorParams: TStatorParams;
|
|
InFile, OutFile: TextFile;
|
|
Line: string;
|
|
CoilRadius :double;
|
|
CurrentZ: double;
|
|
coords: TPoint;
|
|
normalizecoords: TPoint;
|
|
i, j, k: Integer;
|
|
CurrentCoilTurns :Integer;
|
|
AngleBetweenRays :double;
|
|
RequiredSpacing: Double;
|
|
LayerNumber :Integer;
|
|
angle : double;
|
|
|
|
function ParseLine(Line: string): Boolean;
|
|
var
|
|
Parts: array of string;
|
|
Value: string;
|
|
TrimmedLine: string;
|
|
begin
|
|
TrimmedLine := Trim(Line); // Óäàëÿåì ïðîáåëû â íà÷àëå è êîíöå ñòðîêè
|
|
if Length(TrimmedLine) = 0 then
|
|
begin
|
|
exit(true); // Ïóñòàÿ ñòðîêà - ïðîïóñêàåì
|
|
end
|
|
else if TrimmedLine[1] in [';','#'] then
|
|
begin
|
|
exit(true); // Ñòðîêà êîììåíòàðèÿ - ïðîïóñêàåì
|
|
end;
|
|
|
|
Parts := SplitString(TrimmedLine, '='); // Èñïîëüçóåì TrimmedLine
|
|
Result := Length(Parts) = 2;
|
|
if Result then
|
|
begin
|
|
Value := LowerCase(Trim(Parts[1])); // Ïðèâîäèì ê íèæíåìó ðåãèñòðó
|
|
case Trim(Parts[0]) of
|
|
'base_dia': begin
|
|
StatorParams.BaseDiameter := StrToFloat(Value);
|
|
writeln('Base Diameter: ', StatorParams.BaseDiameter:8:2);
|
|
writeln();
|
|
end;
|
|
'num_rays': begin
|
|
StatorParams.NumberOfRays := StrToInt(Value);
|
|
writeln('Number of Rays: ', StatorParams.NumberOfRays);
|
|
AngleBetweenRays := 360/StatorParams.NumberOfRays;
|
|
writeln('Angle Between Rays: ', AngleBetweenRays);
|
|
writeln();
|
|
end;
|
|
'ray_shape': begin
|
|
if Pos(Value, 'circle, rect, superellipse') = 0 then
|
|
raise Exception.Create('Invalid value for ray_shape: ' + Value);
|
|
StatorParams.RayShape := Value;
|
|
writeln('Ray Shape: ', StatorParams.RayShape);
|
|
writeln();
|
|
end;
|
|
'ray_dia': begin
|
|
StatorParams.RayDiameter := StrToFloat(Value);
|
|
writeln('Ray Diameter: ', StatorParams.RayDiameter:8:2);
|
|
writeln();
|
|
end;
|
|
'ray_w': begin
|
|
StatorParams.RayWidth := StrToFloat(Value);
|
|
writeln('Ray Width: ', StatorParams.RayWidth:8:2);
|
|
writeln();
|
|
end;
|
|
'ray_h': begin
|
|
StatorParams.RayHeight := StrToFloat(Value);
|
|
writeln('Ray Height: ', StatorParams.RayHeight:8:2);
|
|
writeln();
|
|
end;
|
|
'ray_len': begin
|
|
StatorParams.RayLength := StrToFloat(Value);
|
|
writeln('Ray Length: ', StatorParams.RayLength:8:2);
|
|
writeln();
|
|
end;
|
|
|
|
'raytop_shape': begin
|
|
if Pos(Value, 'circle, rect, superellipse') = 0 then
|
|
raise Exception.Create('Invalid value for ray_top: ' + Value);
|
|
StatorParams.RayTopShape := Value;
|
|
writeln('Ray Top Shape: ', StatorParams.RayTopShape);
|
|
end;
|
|
|
|
'raytop_dia': begin
|
|
StatorParams.RayTopDiameter := StrToFloat(Value);
|
|
writeln('Ray Top Diameter: ', StatorParams.RayTopDiameter:8:2);
|
|
writeln();
|
|
end;
|
|
'raytop_w': begin
|
|
StatorParams.RayTopWidth := StrToFloat(Value);
|
|
writeln('Ray Top Width: ', StatorParams.RayTopWidth:8:2);
|
|
writeln();
|
|
end;
|
|
'raytop_h': begin
|
|
StatorParams.RayTopHeight := StrToFloat(Value);
|
|
writeln('Ray Top Height: ', StatorParams.RayTopHeight:8:2);
|
|
writeln();
|
|
end;
|
|
|
|
'ray_offset': begin
|
|
StatorParams.RayCenterOffset := StrToFloat(Value);
|
|
writeln('Ray Center Offset: ', StatorParams.RayCenterOffset:8:2);
|
|
writeln();
|
|
end;
|
|
'wire_diameter': begin
|
|
StatorParams.WireDiameter := StrToFloat(Value);
|
|
writeln('Wire Diameter: ', StatorParams.WireDiameter:8:2);
|
|
writeln();
|
|
end;
|
|
'needle_diameter': begin
|
|
StatorParams.NeedleDiameter := StrToFloat(Value);
|
|
writeln('Needle Diameter: ', StatorParams.NeedleDiameter:8:2);
|
|
writeln();
|
|
end;
|
|
'path_clearance': begin
|
|
StatorParams.PathClearance := StrToFloat(Value);
|
|
writeln('Path Clearance: ', StatorParams.PathClearance:8:2);
|
|
writeln();
|
|
end;
|
|
'work_speed': begin
|
|
StatorParams.WorkSpeed := StrToInt(Value);
|
|
writeln('Work Speed: ', StatorParams.WorkSpeed);
|
|
writeln();
|
|
end;
|
|
|
|
|
|
else
|
|
Result := False;
|
|
end;
|
|
if not Result then
|
|
begin
|
|
writeln('Error: Unknown parameter: ', Parts[0]);
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function CircleCoordinates(diameter, angleDegrees: Double): TPoint;
|
|
var
|
|
radius: Double;
|
|
angleRadians: Double;
|
|
begin
|
|
radius := diameter / 2;
|
|
// angleRadians := -1*angleDegrees * PI / 180; // Ïåðåâîä ãðàäóñîâ â ðàäèàíû
|
|
angleRadians := -1 * DegToRad(angleDegrees);
|
|
|
|
Result.X := radius * Cos(angleRadians);
|
|
Result.Y := radius * Sin(angleRadians);
|
|
end;
|
|
|
|
function CalculateAngle(opposite, adjacent: Double): Double;
|
|
begin
|
|
if adjacent = 0 then
|
|
raise Exception.Create('Adjacent side cannot be zero');
|
|
// CalculateAngle := ArcTan(opposite / adjacent) * 180 / PI;
|
|
CalculateAngle := RadToDeg(ArcTan(opposite / adjacent));
|
|
end;
|
|
|
|
|
|
(* //ToDO Ïåðåïèñàòü ôóíêöèþ, ÷òîáû îíà âîçâðàùàëà òîëüêî îäíî çíà÷åíèå Z. Íàì æå íóæíî èñïðàâëÿòü òîëüêî îäíó êîîðäèíàòó.
|
|
function NormalizeZ(xOkr, yOkr, radius: Real; thetaDeg: Real): TPoint;
|
|
var
|
|
thetaRad, thetaCorrRad, xKas, yKas, alphaRad, mTang, bTang, mHor, xPrym, yPrym: Real;
|
|
begin
|
|
thetaRad := DegToRad(thetaDeg);
|
|
;thetaCorrRad := thetaRad + DegToRad(270);
|
|
|
|
// 1. Êîîðäèíàòû òî÷êè êàñàíèÿ
|
|
xKas := radius * Cos(thetaCorrRad);
|
|
yKas := radius * Sin(thetaCorrRad);
|
|
|
|
// 2. Óãîë íàêëîíà êàñàòåëüíîé
|
|
alphaRad := thetaCorrRad + PI / 2;
|
|
|
|
// 3. Óðàâíåíèå êàñàòåëüíîé (y = m*x + b)
|
|
mTang := Tan(alphaRad);
|
|
bTang := yKas - mTang * xKas;
|
|
|
|
// 4. Óðàâíåíèå ãîðèçîíòàëüíîé ïðÿìîé
|
|
mHor := Tan(alphaRad - PI/2);
|
|
|
|
// 5. Òî÷êà ïåðåñå÷åíèÿ
|
|
xPrym := (bTang + yOkr + mHor * xOkr)/(mHor - mTang);
|
|
yPrym := mTang * xPrym + bTang;
|
|
|
|
NormalizeZ.x := xPrym;
|
|
NormalizeZ.y := yPrym;
|
|
|
|
//Writeln('thetaDeg =',thetaDeg:0:2,' ,xKas = ',xKas:0:2,' ,yKas = ',yKas:0:2);
|
|
Writeln('thetaDeg =',thetaDeg:0:2,' ,xPrym = ',xPrym:0:2,' ,yPrym = ',yPrym:0:2);
|
|
end;
|
|
*)
|
|
|
|
function NormalizeZ(radius: Real; thetaDeg: Real): Real;
|
|
var
|
|
thetaRad, alphaRad, xKas, yKas, mTang, bTang: Real;
|
|
begin
|
|
// 1. Ïðåîáðàçîâàíèå óãëà èç ãðàäóñîâ â ðàäèàíû.
|
|
thetaRad := DegToRad(thetaDeg);
|
|
|
|
// 2. Âû÷èñëåíèå êîîðäèíàò òî÷êè êàñàíèÿ íà îêðóæíîñòè (îòíîñèòåëüíî öåíòðà 0,0).
|
|
xKas := radius * Cos(thetaRad);
|
|
yKas := radius * Sin(thetaRad);
|
|
|
|
// 3. Âû÷èñëåíèå óãëà, ïåðïåíäèêóëÿðíîãî ðàäèóñ-âåêòîðó (êàñàòåëüíîé).
|
|
alphaRad := thetaRad + PI / 2;
|
|
|
|
// 4. Âû÷èñëåíèå óãëîâîãî êîýôôèöèåíòà êàñàòåëüíîé.
|
|
mTang := Tan(alphaRad);
|
|
|
|
// 5. Âû÷èñëåíèå ñâîáîäíîãî ÷ëåíà óðàâíåíèÿ êàñàòåëüíîé (y = mTang * x + bTang).
|
|
bTang := yKas - mTang * xKas;
|
|
|
|
// 6. Âû÷èñëåíèå êîîðäèíàòû X òî÷êè ïåðåñå÷åíèÿ êàñàòåëüíîé ñ îñüþ X (y = 0).
|
|
// 0 = mTang * x + bTang
|
|
// x = -bTang / mTang
|
|
if mTang = 0 then
|
|
begin
|
|
// Êàñàòåëüíàÿ ïàðàëëåëüíà îñè X - íåò òî÷êè ïåðåñå÷åíèÿ. Âîçâðàùàåì NaN.
|
|
NormalizeZ := NaN;
|
|
end
|
|
else
|
|
begin
|
|
NormalizeZ := -bTang / mTang;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
// Command line argument handling
|
|
if ParamCount < 2 then
|
|
begin
|
|
Writeln('Usage: GCodeGenerator <input_file.txt> <output_file.gcode>');
|
|
Halt(1);
|
|
end;
|
|
InputFileName := ParamStr(1);
|
|
OutputFileName := ParamStr(2);
|
|
|
|
// **Opening the input file**
|
|
AssignFile(InFile, InputFileName);
|
|
try
|
|
Reset(InFile); // **This line opens the file for reading**
|
|
while not EOF(InFile) do
|
|
begin
|
|
ReadLn(InFile, Line);
|
|
if Length(Line) > 0 then
|
|
if not ParseLine(Line) then
|
|
writeln('Error: Invalid line: ', Line);
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
writeln('Error opening or reading input file: ', E.Message);
|
|
Halt(1);
|
|
end;
|
|
end;
|
|
CloseFile(InFile);
|
|
|
|
// G-code generation
|
|
AssignFile(OutFile, OutputFileName);
|
|
try
|
|
Rewrite(OutFile);
|
|
// *** Your G-code generation logic here ***
|
|
writeln(OutFile, '; G-code for stator winding');
|
|
writeln(OutFile, 'G90 ; Absolute coordinate system');
|
|
writeln(OutFile, 'G1 Y-10');
|
|
writeln(OutFile, 'G28 X Y Z');
|
|
writeln(OutFile, 'G1 X0 Y0');
|
|
//writeln(OutFile, 'G28 O'); //Home all "untrusted" axes
|
|
writeln(OutFile);
|
|
// Move to center of ray along Y axis and reset coordinate
|
|
writeln(OutFile, 'G1 X', StatorParams.RayCenterOffset:0:2);
|
|
writeln(OutFile, 'G92 X0');
|
|
|
|
//îòâîäèì èãëó ëåâåå âåðøèíû ëó÷à
|
|
CoilRadius:=(StatorParams.RayTopHeight/2+StatorParams.NeedleDiameter/2+StatorParams.PathClearance);
|
|
writeln('CoilRadius = ', CoilRadius:0:3);
|
|
writeln(OutFile, 'G1 X', -1*CoilRadius:0:3);
|
|
|
|
//ïðèáëèæàåìñÿ ê îñíîâàíèþ ñòàòîðà.
|
|
CurrentZ:=(StatorParams.BaseDiameter/2)+StatorParams.PathClearance;
|
|
writeln(OutFile, 'G1 Z', CurrentZ:0:2, ' F', StatorParams.WorkSpeed);
|
|
writeln(OutFile, 'M0');
|
|
|
|
writeln(OutFile, ';Start coil');
|
|
//Äåëàåì êðóã.
|
|
Inc(LayerNumber);
|
|
CurrentCoilTurns := round(StatorParams.RayLength/StatorParams.WireDiameter);
|
|
// CurrentCoilTurns := 1;
|
|
writeln(OutFile, ';We make ',CurrentCoilTurns, ' turns' );
|
|
for j := 0 to CurrentCoilTurns do
|
|
begin
|
|
for i := 0 to 360 do
|
|
begin
|
|
coords := CircleCoordinates(CoilRadius*2, i+180);
|
|
writeln(OutFile,'CoilRadius*2= ',CoilRadius*2:0:5,' X=', coords.X:0:3, ' Y=', coords.Y:0:5);
|
|
angle := CalculateAngle(coords.Y, CurrentZ);
|
|
|
|
// writeln(OutFile, ';G1 X', coords.X:0:3, ' Y', angle:0:5,' Z', CurrentZ:0:5);
|
|
writeln(OutFile, 'G1 X', coords.X:0:3, ' Y', angle:0:5,' Z', NormalizeZ(CurrentZ, angle):0:5);
|
|
// writeln(OutFile);
|
|
|
|
CurrentZ:=CurrentZ+StatorParams.WireDiameter/360;
|
|
end;
|
|
writeln(OutFile, ';Next coil.');
|
|
end;
|
|
writeln(OutFile, ';Second coil');
|
|
Inc(LayerNumber);
|
|
// RequiredSpacing:=StatorParams.RayWidth+StatorParams.NeedleDiameter+StatorParams.PathClearance*2+LayerNumber*StatorParams.WireDiameter*2;
|
|
// writeln(OutFile, ';RequiredSpacing = ',RequiredSpacing:0:5);
|
|
RequiredSpacing:=StatorParams.RayTopWidth;
|
|
writeln(OutFile, ';RequiredSpacing = ',RequiredSpacing:0:5);
|
|
RequiredSpacing:=RequiredSpacing+StatorParams.NeedleDiameter;
|
|
writeln(OutFile, ';RequiredSpacing = ',RequiredSpacing:0:5);
|
|
RequiredSpacing:=RequiredSpacing+StatorParams.PathClearance*2+LayerNumber*StatorParams.WireDiameter*2;
|
|
writeln(OutFile, ';RequiredSpacing = ',RequiredSpacing:0:5);
|
|
|
|
|
|
CurrentZ:=RequiredSpacing/(2 * tan(AngleBetweenRays*PI/180/2));
|
|
writeln(OutFile, ';CurrentZ = ',CurrentZ:0:3);
|
|
|
|
// *** Your G-code generation logic here ***
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
writeln('Error writing to output file: ', E.Message);
|
|
Halt(1);
|
|
end;
|
|
end;
|
|
CloseFile(OutFile);
|
|
|
|
writeln('G-code generated to: ', OutputFileName);
|
|
writeln('Press Enter... ');
|
|
Readln;
|
|
end.
|