From 3d396536494680586b4952161b692823885351b8 Mon Sep 17 00:00:00 2001 From: xenovacivus Date: Sat, 18 Jan 2014 15:09:43 -0800 Subject: [PATCH] Added a settings object The settings object will interface to all the configurable parameters across the tool, allowing for easy saving & restoring of configurations. --- Commands/MoveTool.cs | 16 +++- GUI/GUI.csproj | 1 + GUI/PathCAM.cs | 23 +++--- GUI/RobotControl.cs | 11 ++- GUI/RouterGUI.cs | 51 ------------- GUI/Settings.cs | 86 ++++++++++++++++++++++ Robot/Robot.cs | 167 ++++++++++++++++++------------------------ Router/GCodeLoader.cs | 27 +++++-- Router/Router.cs | 28 ++----- 9 files changed, 219 insertions(+), 191 deletions(-) create mode 100644 GUI/Settings.cs diff --git a/Commands/MoveTool.cs b/Commands/MoveTool.cs index 98b2775..1072ad7 100644 --- a/Commands/MoveTool.cs +++ b/Commands/MoveTool.cs @@ -4,10 +4,16 @@ namespace Commands { public class MoveTool : ICommand { + public enum SpeedType + { + Cutting, + Rapid, + } + private Vector3 target; // Target location in inches - private float speed; // Moving speed in inches per minute + private SpeedType speed; - public MoveTool(Vector3 target, float speed) + public MoveTool(Vector3 target, SpeedType speed) { this.target = target; this.speed = speed; @@ -16,11 +22,13 @@ public MoveTool(Vector3 target, float speed) public Vector3 Target { get { return target; } + set { target = value; } } - public float Speed + public SpeedType Speed { - get { return speed; } + get { return speed; } + set { speed = value; } } } } diff --git a/GUI/GUI.csproj b/GUI/GUI.csproj index 1afef94..63834b7 100644 --- a/GUI/GUI.csproj +++ b/GUI/GUI.csproj @@ -100,6 +100,7 @@ Drawing3D.cs + diff --git a/GUI/PathCAM.cs b/GUI/PathCAM.cs index 3365b2e..455ac6b 100644 --- a/GUI/PathCAM.cs +++ b/GUI/PathCAM.cs @@ -26,21 +26,25 @@ public partial class PathCAM : Form, IOpenGLDrawable private RouterGUI router; private string dragDropFilename = null; private Regex acceptedFileRegex = new Regex(@".*(\.dae|\.obj|\.stl|\.nc|\.gcode)", RegexOptions.IgnoreCase); + private Settings settings; public PathCAM() { InitializeComponent(); router = new RouterGUI(); - propertyGrid.SelectedObject = router; drawing3D.AddObject(router); robotControl.AssignRouter(router); + drawing3D.AddObject(robotControl); drawing3D.AddObject(this); drawing3D.DragDrop += this.Drawing3D_DragDrop; drawing3D.DragOver += this.Drawing3D_DragOver; drawing3D.DragEnter += this.Drawing3D_DragEnter; drawing3D.DragLeave += this.Drawing3D_DragLeave; + + settings = new Settings(robotControl.GetRobot(), router); + propertyGrid.SelectedObject = settings; } void Drawing3D_DragLeave(object sender, EventArgs e) @@ -313,10 +317,10 @@ private void InitializeComponent() // propertyGrid // this.propertyGrid.ImeMode = System.Windows.Forms.ImeMode.NoControl; - this.propertyGrid.Location = new System.Drawing.Point(12, 128); + this.propertyGrid.Location = new System.Drawing.Point(12, 157); this.propertyGrid.Name = "propertyGrid"; this.propertyGrid.PropertySort = System.Windows.Forms.PropertySort.NoSort; - this.propertyGrid.Size = new System.Drawing.Size(183, 163); + this.propertyGrid.Size = new System.Drawing.Size(183, 205); this.propertyGrid.TabIndex = 5; this.propertyGrid.ToolbarVisible = false; // @@ -363,7 +367,7 @@ private void InitializeComponent() "25.4:1 (millimeters)", ".254:1 (meters)", "1:12 (feet)"}); - this.comboBox1.Location = new System.Drawing.Point(87, 299); + this.comboBox1.Location = new System.Drawing.Point(87, 129); this.comboBox1.Name = "comboBox1"; this.comboBox1.Size = new System.Drawing.Size(107, 21); this.comboBox1.TabIndex = 7; @@ -373,7 +377,7 @@ private void InitializeComponent() // openFileButton // this.openFileButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.openFileButton.Location = new System.Drawing.Point(12, 297); + this.openFileButton.Location = new System.Drawing.Point(12, 128); this.openFileButton.Name = "openFileButton"; this.openFileButton.Size = new System.Drawing.Size(70, 23); this.openFileButton.TabIndex = 6; @@ -394,8 +398,8 @@ private void InitializeComponent() // // pictureBox1 // - this.pictureBox1.BackColor = System.Drawing.SystemColors.ActiveBorder; - this.pictureBox1.Location = new System.Drawing.Point(86, 298); + this.pictureBox1.BackColor = System.Drawing.Color.Black; + this.pictureBox1.Location = new System.Drawing.Point(86, 128); this.pictureBox1.Name = "pictureBox1"; this.pictureBox1.Size = new System.Drawing.Size(109, 23); this.pictureBox1.TabIndex = 70; @@ -475,11 +479,6 @@ private void checkBox1_CheckedChanged(object sender, EventArgs e) robotControl.Visible = showRobotFormCheckbox.Checked; } - private void drawing3D_Load(object sender, EventArgs e) - { - - } - private void PathCAM_Load(object sender, EventArgs e) { // Programatically fill the entire client rectangle with the drawing area. diff --git a/GUI/RobotControl.cs b/GUI/RobotControl.cs index 8bba70b..270efc6 100644 --- a/GUI/RobotControl.cs +++ b/GUI/RobotControl.cs @@ -35,6 +35,11 @@ public void AssignRouter(Router.Router router) this.robot.onRobotStatusChange += new EventHandler(RobotStatusUpdate); } + public Robot.Robot GetRobot() + { + return robot; + } + void RobotStatusUpdate(object o, EventArgs e) { if (this.InvokeRequired && !this.Disposing) @@ -165,8 +170,8 @@ private void cancelButton_Click(object sender, EventArgs e) { robot.CancelPendingCommands(); Vector3 position = robot.GetPosition(); - robot.AddCommand(new MoveTool(new Vector3(position.X, position.Y, router.MoveHeight), router.MoveSpeed)); - robot.AddCommand(new MoveTool(new Vector3(0, 0, router.MoveHeight), router.MoveSpeed)); + robot.AddCommand(new MoveTool(new Vector3(position.X, position.Y, router.MoveHeight), MoveTool.SpeedType.Rapid)); + robot.AddCommand(new MoveTool(new Vector3(0, 0, router.MoveHeight), MoveTool.SpeedType.Rapid)); robot.SendResumeCommand(); this.pause_resume_button.Enabled = false; this.cancelButton.Enabled = false; @@ -195,7 +200,7 @@ private void runButton_Click(object sender, EventArgs e) private void zGo_Click(object sender, EventArgs e) { float f = float.Parse(zbox.Text); - MoveTool move = new MoveTool(new Vector3(0, 0, f), router.MoveSpeed); + MoveTool move = new MoveTool(new Vector3(0, 0, f), MoveTool.SpeedType.Rapid); robot.AddCommand(move); } diff --git a/GUI/RouterGUI.cs b/GUI/RouterGUI.cs index c96aaed..4fe042f 100644 --- a/GUI/RouterGUI.cs +++ b/GUI/RouterGUI.cs @@ -38,57 +38,6 @@ public RouterGUI() : base() { } - /// - /// Accessors for the property grid - /// - [DisplayName("Last Pass Height")] - [Description("Height of the last pass in inches")] - new public float LastPassHeight - { - get { return base.LastPassHeight; } - set { base.LastPassHeight = value; } - } - - [DisplayName("Tool Diameter")] - [Description("Tool Diameter in inches")] - new public float ToolDiameter - { - get { return base.ToolDiameter; } - set { base.ToolDiameter = value; } - } - - [DisplayName("Routing Speed")] - [Description("Rout Speed (inches per minute)")] - new public float RoutSpeed - { - get { return base.RoutSpeed; } - set { base.RoutSpeed = value; } - } - - [DisplayName("Moving Speed")] - [Description("Moving speed (inches per minute)")] - new public float MoveSpeed - { - get { return base.MoveSpeed; } - set { base.MoveSpeed = value; } - } - - [DisplayName("Move Height")] - [Description("Safe travel height")] - new public float MoveHeight - { - get { return base.MoveHeight; } - set { base.MoveHeight = value; } - } - - [DisplayName("Max Cut Depth")] - [Description("Maximum Cut Depth")] - new public float MaxCutDepth - { - get { return base.MaxCutDepth; } - set { base.MaxCutDepth = value; } - } - void IOpenGLDrawable.Draw() { try diff --git a/GUI/Settings.cs b/GUI/Settings.cs new file mode 100644 index 0000000..4fec3d4 --- /dev/null +++ b/GUI/Settings.cs @@ -0,0 +1,86 @@ +using OpenTK; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing.Design; +using System.Linq; +using System.Text; + +namespace GUI +{ + /// + /// Collects all settings and exposes them through one class. + /// + public class Settings + { + private Robot.Robot robot; + private Router.Router router; + public Settings(Robot.Robot robot, Router.Router router) + { + this.router = router; + this.robot = robot; + } + + + /// + /// Accessors for the property grid + /// + [DisplayName("Last Pass Height")] + [Description("Height of the last pass in inches")] + public float LastPassHeight + { + get { return router.LastPassHeight; } + set { router.LastPassHeight = value; } + } + + [DisplayName("Tool Diameter")] + [Description("Tool Diameter in inches")] + public float ToolDiameter + { + get { return router.ToolDiameter; } + set { router.ToolDiameter = value; } + } + + [DisplayName("Move Height")] + [Description("Safe travel height")] + public float MoveHeight + { + get { return router.MoveHeight; } + set { router.MoveHeight = value; } + } + + [DisplayName("Max Cut Depth")] + [Description("Maximum Cut Depth")] + public float MaxCutDepth + { + get { return router.MaxCutDepth; } + set { router.MaxCutDepth = value; } + } + + + + [DisplayName("Cutting Speed")] + [Description("Cutting Speed (inches per minute)")] + public float RoutSpeed + { + get { return robot.MaxCutSpeed; } + set { robot.MaxCutSpeed = value; } + } + + [DisplayName("Moving Speed")] + [Description("Rapid movement speed (inches per minute)")] + public float MoveSpeed + { + get { return robot.MaxRapidSpeed; } + set { robot.MaxRapidSpeed = value; } + } + + [DisplayName("Max Z Speed")] + [Description("Maximum possible Z axis speed (inches per minute)")] + public float MaxAxisSpeeds + { + get { return robot.MaxZSpeed; } + set { robot.MaxZSpeed = value; } + } + } +} diff --git a/Robot/Robot.cs b/Robot/Robot.cs index 0541959..87cd954 100644 --- a/Robot/Robot.cs +++ b/Robot/Robot.cs @@ -31,21 +31,38 @@ public class Robot { private Queue commands = new Queue(); public EventHandler onRobotStatusChange; + + private IRobotCommand currentCommand = null; + private SerialPortWrapper serial; + private Timer t; + private int elapsedCounter = 0; + private Vector3 currentPosition = new Vector3(0, 0, 0); + private Vector3 lastPosition = new Vector3(0, 0, 0); + private bool lastPositionKnown = false; + + private const float minSpeed = 0.01f; // All speeds are clamped to this. + private float maxZSpeed = 30; + private float maxCutSpeed = 60.0f; + private float maxRapidSpeed = 250.0f; + bool sendResumeCommand = false; bool sendPauseCommand = false; bool sendCancelCommand = false; bool sendEnableStepperCommand = false; bool sendDisableStepperCommand = false; + public void SendPauseCommand() { sendPauseCommand = true; sendResumeCommand = false; } + public void SendResumeCommand() { sendResumeCommand = true; sendPauseCommand = false; } + public void CancelPendingCommands() { sendCancelCommand = true; @@ -54,26 +71,49 @@ public void CancelPendingCommands() commands.Clear(); } } + public void EnableMotors() { sendDisableStepperCommand = false; sendEnableStepperCommand = true; } + public void DisableMotors() { sendEnableStepperCommand = false; sendDisableStepperCommand = true; } - IRobotCommand currentCommand = null; - SerialPortWrapper serial; - Timer t; - int elapsedCounter = 0; + public Vector3 GetPosition() + { + return currentPosition; + } + + public void AddCommand(ICommand command) + { + commands.Enqueue(command); + } + + + public float MaxZSpeed + { + get { return maxZSpeed; } + set { maxZSpeed = Math.Max(value, minSpeed); } + } + + public float MaxCutSpeed + { + get { return maxCutSpeed; } + set { maxCutSpeed = Math.Max(value, minSpeed); } + } + + public float MaxRapidSpeed + { + get { return maxRapidSpeed; } + set { maxRapidSpeed = Math.Max(value, minSpeed); } + } - Vector3 currentPosition = new Vector3(0, 0, 0); - Vector3 lastPosition = new Vector3(0, 0, 0); - bool lastPositionKnown = false; public Robot(SerialPortWrapper serial) { @@ -97,6 +137,9 @@ void t_Elapsed(object sender, ElapsedEventArgs e) if ((elapsedCounter * 50) > (1000)) // More than 1 second to reply { Console.WriteLine("Device Timeout!"); + + // Assume disconnected, won't give another move command until the position is known + lastPositionKnown = false; // Send a status command currentCommand = new StatusCommand(); @@ -109,8 +152,6 @@ void t_Elapsed(object sender, ElapsedEventArgs e) t.Start(); } - #region Serial Interface Callbacks - private void ReceiveDataError(byte err) { Console.WriteLine("Data Error: " + err); @@ -136,8 +177,6 @@ private void NewDataAvailable(SerialPortWrapper.SimpleSerialPacket packet) currentCommand.ProcessResponse(packet.Data); if (currentCommand.IsDataValid()) { - // See if there's any state information in the command used to - // update location or other fields... int locations = 0; if (currentCommand is StatusCommand) { @@ -151,30 +190,14 @@ private void NewDataAvailable(SerialPortWrapper.SimpleSerialPacket packet) } if (onRobotStatusChange != null) { - //onPositionUpdate(new object[] { currentPosition, c.time }, EventArgs.Empty); onRobotStatusChange(c, EventArgs.Empty); } } if (currentCommand is MoveCommand) { - MoveCommand m = currentCommand as MoveCommand; - locations = m.Locations; - currentPosition = m.CurrentPosition; - if (this.lastPositionKnown == false) - { - lastPosition = currentPosition; - lastPositionKnown = true; - } - if (onRobotStatusChange != null) - { - //onPositionUpdate(new object[] { currentPosition, m.time }, EventArgs.Empty); - onRobotStatusChange(m, EventArgs.Empty); - } } currentCommand = GetNextCommand(locations); - - serial.Transmit(currentCommand.GenerateCommand(), 0x21); } else @@ -185,8 +208,6 @@ private void NewDataAvailable(SerialPortWrapper.SimpleSerialPacket packet) } } - #endregion - private IRobotCommand GetNextCommand(int locations) { currentCommand = null; @@ -216,29 +237,17 @@ private IRobotCommand GetNextCommand(int locations) currentCommand = new StepperDisableCommand(); sendDisableStepperCommand = false; } - else if (locations > 0) + else if (locations > 0 && lastPositionKnown) { while (currentCommand == null && commands.Count > 0) { ICommand command = commands.Dequeue(); if (command is MoveTool) { - MoveTool m = command as MoveTool; - GoTo(m.Target, m.Speed); + currentCommand = CreateRobotCommand(command as MoveTool); } } } - - //if (currentCommand == null && locations > 0) - //{ - // // Ok to pass in another movement command - // // TODO: rework this to use a local buffer... - // if (onRobotReady != null) - // { - // onRobotReady(this, EventArgs.Empty); - // } - //} - if (currentCommand == null) { currentCommand = new StatusCommand(); @@ -246,69 +255,37 @@ private IRobotCommand GetNextCommand(int locations) return currentCommand; } - - public Vector3 GetPosition() - { - return currentPosition; - } - - public void AddCommand(ICommand command) - { - commands.Enqueue(command); - } /// - /// Run the router from the current position to the given position + /// Create a robot command from a MoveTool command. Adjusts for robot specific + /// max speeds. May return null if there is no effective move. /// - /// Destination location in inches - /// Tool speed in inches per second - private void GoTo(Vector3 p, float inches_per_minute) + /// + /// + private IRobotCommand CreateRobotCommand(MoveTool m) { - lock (thisLock) - { - if (this.lastPositionKnown == false) - { - inches_per_minute = Math.Min(MaxInchesPerMinute.X, Math.Min(MaxInchesPerMinute.Y, MaxInchesPerMinute.Z)); - } - Vector3 delta = lastPosition - p; - lastPosition = p; - lastPositionKnown = true; - - float inches = delta.Length; + var p = m.Target; - UInt16 time_milliseconds = (UInt16)(1000 * 60 * inches / inches_per_minute); + float inches_per_minute = m.Speed == MoveTool.SpeedType.Cutting ? MaxCutSpeed : MaxRapidSpeed; - if (delta.Length > 0) - { - if (Math.Abs(delta.X) > 0.0001f) - { - inches_per_minute = Math.Min(MaxInchesPerMinute.X, inches_per_minute); - } - if (Math.Abs(delta.Y) > 0.0001f) - { - inches_per_minute = Math.Min(MaxInchesPerMinute.Y, inches_per_minute); - } - if (Math.Abs(delta.Z) > 0.0001f) - { - inches_per_minute = Math.Min(MaxInchesPerMinute.Z, inches_per_minute); - } + Vector3 delta = lastPosition - p; + lastPosition = p; - currentCommand = new MoveCommand(p, inches_per_minute / 60.0f); - } - else + if (delta.Length > 0) + { + if (Math.Abs(delta.Z) > 0.0001f) { - Console.WriteLine("Ignoring command with time of 0"); + inches_per_minute = Math.Min(MaxZSpeed, inches_per_minute); } - } - } - - - Vector3 MaxInchesPerMinute - { - get { return new Vector3(400, 400, 40); } + return new MoveCommand(p, inches_per_minute / 60.0f); + } + else + { + Console.WriteLine("Ignoring command with length of 0"); + return null; + } } - } } diff --git a/Router/GCodeLoader.cs b/Router/GCodeLoader.cs index 421d394..13ec400 100644 --- a/Router/GCodeLoader.cs +++ b/Router/GCodeLoader.cs @@ -47,13 +47,20 @@ public static List Load(string filename) { // Rapid positioning or linear interpolation // Go to X, Y, Z at feedrate F. + Vector3 fromPoint = new Vector3(x, y, z); GetFloat(s, "F", ref speed); GetFloat(s, "X", ref x); GetFloat(s, "Y", ref y); GetFloat(s, "Z", ref z); Vector3 toPoint = new Vector3(x, y, z); - commands.Add(new MoveTool(toPoint * scale, speed * scale)); + // If the XYZ params weren't specified or didn't change, don't do a move. + // If only speed changed, that's ok; it has been updated for the next move. + if ((toPoint - fromPoint).Length > 0) + { + var speedType = g_value == 0 ? MoveTool.SpeedType.Rapid : MoveTool.SpeedType.Cutting; + commands.Add(new MoveTool(toPoint * scale, speedType)); + } } else if (g_value == 4) @@ -91,6 +98,12 @@ public static void ExportGCode(List commands, string filename) file.WriteLine("G20 (Units are Inches)"); file.WriteLine("G90 (Absolute Positioning)"); file.WriteLine("G94 (Units per Minute feed rate)"); + + // TODO: paramaterize these + float rapidSpeed = 50.0f; + float cuttingSpeed = 25.0f; + float plungeSpeed = 10.0f; + float lastSpeed = -1; float lastHeight = 0; float scale = 1.0f; @@ -99,20 +112,24 @@ public static void ExportGCode(List commands, string filename) if (command is MoveTool) { MoveTool m = command as MoveTool; - float speed = m.Speed * scale; + float speed = m.Speed == MoveTool.SpeedType.Cutting ? cuttingSpeed * scale : rapidSpeed * scale; + var gCommand = m.Speed == MoveTool.SpeedType.Cutting ? "G1" : "G0"; + Vector3 target = m.Target * scale; float height = target.Z; if ((height + 0.001f) < lastHeight) { - speed = Math.Min(10.0f, speed); // Maximum plunge speed is 10 inches per minute (make a parameter...) + speed = Math.Min(plungeSpeed, speed); } lastHeight = height; if (lastSpeed != speed) { lastSpeed = speed; - file.WriteLine("G1 F{0:F4}", speed); + file.WriteLine("{0} F{1:F4}", gCommand, speed); } - file.WriteLine("G1 X{0:F4} Y{1:F4} Z{2:F4}", target.X, target.Y, target.Z); + + file.WriteLine("{0} X{1:F4} Y{2:F4} Z{3:F4}", gCommand, target.X, target.Y, target.Z); + } } } diff --git a/Router/Router.cs b/Router/Router.cs index a9589c9..c7734c3 100644 --- a/Router/Router.cs +++ b/Router/Router.cs @@ -37,8 +37,6 @@ public class Router private float toolDiameter = 0.120f; private float move_height = 0.525f; // How high above the surface to move the router - private float move_speed = 250; // Moving speed (inches per minute) - private float rout_speed = 45; // Routing speed (inches per minute) private float max_cut_depth = 1.0f / 8.0f; // Maximum cut depth in inches private float lastPassHeight = -.020f; // Height of the last rout @@ -54,11 +52,11 @@ public List GetCommands() public void AddCommand(ICommand r) { - commands.Add(r); if (r is MoveTool) { finalPosition = (r as MoveTool).Target; } + commands.Add(r); } public void ClearCommands() @@ -77,18 +75,6 @@ public float ToolDiameter get { return toolDiameter; } set { if (value > 0.0f) { toolDiameter = value; } } } - - public float RoutSpeed - { - get { return rout_speed; } - set { if (value > 0) { rout_speed = value; } } - } - - public float MoveSpeed - { - get { return move_speed; } - set { if (value > 0) { move_speed = value; } } - } public float MoveHeight { @@ -111,7 +97,7 @@ public void RoutPath(LineStrip line, bool backwards, Vector3 offset) // TODO: Pick some unit and stick with it! Inches would be fine. Vector3 pointOffset = point + offset; - MoveTool m = new MoveTool(pointOffset, rout_speed); + MoveTool m = new MoveTool(pointOffset, MoveTool.SpeedType.Cutting); if (first) { first = false; @@ -119,8 +105,8 @@ public void RoutPath(LineStrip line, bool backwards, Vector3 offset) if ((finalPosition.Xy - pointOffset.Xy).Length > .0001) { // Need to move the router up, over to new position, then down again. - MoveTool m1 = new MoveTool(new Vector3(finalPosition.X, finalPosition.Y, move_height), move_speed); - MoveTool m2 = new MoveTool(new Vector3(m.Target.X, m.Target.Y, move_height), move_speed); + MoveTool m1 = new MoveTool(new Vector3(finalPosition.X, finalPosition.Y, move_height), MoveTool.SpeedType.Rapid); + MoveTool m2 = new MoveTool(new Vector3(m.Target.X, m.Target.Y, move_height), MoveTool.SpeedType.Rapid); AddCommand(m1); AddCommand(m2); } @@ -136,13 +122,13 @@ public void Complete() { if (finalPosition.Z < move_height) { - AddCommand(new MoveTool(new Vector3(finalPosition.X, finalPosition.Y, move_height), move_speed)); + AddCommand(new MoveTool(new Vector3(finalPosition.X, finalPosition.Y, move_height), MoveTool.SpeedType.Rapid)); } else { - AddCommand(new MoveTool(new Vector3(0, 0, finalPosition.Z), move_speed)); + AddCommand(new MoveTool(new Vector3(0, 0, finalPosition.Z), MoveTool.SpeedType.Rapid)); } - AddCommand(new MoveTool(new Vector3(0, 0, move_height), move_speed)); + AddCommand(new MoveTool(new Vector3(0, 0, move_height), MoveTool.SpeedType.Rapid)); } } }