public void AddComponent(PhysicalComponent[] x) { _components.AddRange(x); UpdateComponents(); }
// ================================================================================= // BuildMachineCoordinateData_m(): private bool BuildMachineCoordinateData_m() { double X_nom = 0; double Y_nom = 0; if (Properties.Settings.Default.Placement_SkipMeasurements) { foreach (DataGridViewRow Row in CadData_GridView.Rows) { // Cad data is validated. double.TryParse(Row.Cells["X_nominal"].Value.ToString(), out X_nom); double.TryParse(Row.Cells["Y_nominal"].Value.ToString(), out Y_nom); X_nom += Properties.Settings.Default.General_JigOffsetX; Y_nom += Properties.Settings.Default.General_JigOffsetY; Row.Cells["X_machine"].Value = X_nom.ToString("0.000", CultureInfo.InvariantCulture); Row.Cells["Y_machine"].Value = Y_nom.ToString("0.000", CultureInfo.InvariantCulture); Row.Cells["Rotation_machine"].Value = Row.Cells["Rotation"].Value; } // Refresh UI: Update_GridView(CadData_GridView); return true; }; if (ValidMeasurement_checkBox.Checked) { return true; } // Check, that our data is good: if (!ValidateCADdata_m()) return false; // Find the fiducials form CAD data: int FiducialsRow = 0; if (!FindFiducials_m(out FiducialsRow)) { return false; } // OriginalFiducials are at JobData_GridView.Rows[FiducialsRow] string[] FiducialDesignators = JobData_GridView.Rows[FiducialsRow].Cells["ComponentList"].Value.ToString().Split(','); // Are there at least two? if (FiducialDesignators.Length < 2) { ShowMessageBox( "Only one fiducial found.", "Too few fiducials", MessageBoxButtons.OK); return false; } // Get ready for position measurements DisplayText("SetFiducialsMeasurement"); SetFiducialsMeasurement(); // Move them to our array, checking the data: PhysicalComponent[] Fiducials = new PhysicalComponent[FiducialDesignators.Length]; // store the data here // double X_nom = 0; // double Y_nom = 0; for (int i = 0; i < FiducialDesignators.Length; i++) // for each fiducial in our OriginalFiducials array, { Fiducials[i] = new PhysicalComponent(); Fiducials[i].Designator = FiducialDesignators[i]; // find the fiducial in CAD data. foreach (DataGridViewRow Row in CadData_GridView.Rows) { if (Row.Cells["Component"].Value.ToString() == FiducialDesignators[i]) // If this is the fiducial we want, { // Get its nominal position (value already checked). double.TryParse(Row.Cells["X_nominal"].Value.ToString(), out X_nom); double.TryParse(Row.Cells["Y_nominal"].Value.ToString(), out Y_nom); break; } } Fiducials[i].X_nominal = X_nom; Fiducials[i].Y_nominal = Y_nom; // And measure it's true location: if (!MeasureFiducial_m(ref Fiducials[i])) { return false; } // We could put the machine data in place at this point. However, // we don't, as if the algorithms below are correct, the data will not change more than measurement error. // During development, that is a good checkpoint. } // Find the homographic tranformation from CAD data (fiducials.nominal) to measured machine coordinates // (fiducials.machine): Transform transform = new Transform(); HomographyEstimation.Point[] nominals = new HomographyEstimation.Point[Fiducials.Length]; HomographyEstimation.Point[] measured = new HomographyEstimation.Point[Fiducials.Length]; // build point data arrays: for (int i = 0; i < Fiducials.Length; i++) { nominals[i].X = Fiducials[i].X_nominal; nominals[i].Y = Fiducials[i].Y_nominal; nominals[i].W = 1.0; measured[i].X = Fiducials[i].X_machine; measured[i].Y = Fiducials[i].Y_machine; measured[i].W = 1.0; } // find the tranformation bool res = transform.Estimate(nominals, measured, ErrorMetric.Transfer, 450, 450); // the PCBs are smaller than 450mm if (!res) { ShowMessageBox( "Transform estimation failed.", "Data error", MessageBoxButtons.OK); return false; } // Analyze the transform: Displacement is for debug. We could also calculate X & Y stretch and shear, but why bother. // Find out the displacement in the transform (where nominal origin ends up): HomographyEstimation.Point Loc, Loc2; Loc.X = 0.0; Loc.Y = 0.0; Loc.W = 1.0; Loc = transform.TransformPoint(Loc); Loc = Loc.NormalizeHomogeneous(); DisplayText("Transform:"); DisplayText("dX= " + (Loc.X).ToString()); DisplayText("dY= " + Loc.Y.ToString()); // We do need rotation. Find out by rotatíng a unit vector: Loc2.X = 1.0; Loc2.Y = 0.0; Loc2.W = 1.0; Loc2 = transform.TransformPoint(Loc2); Loc2 = Loc2.NormalizeHomogeneous(); DisplayText("dX= " + Loc2.X.ToString()); DisplayText("dY= " + Loc2.Y.ToString()); double angle = Math.Asin(Loc2.Y - Loc.Y) * 180.0 / Math.PI; // in degrees DisplayText("angle= " + angle.ToString()); // Calculate machine coordinates of all components: foreach (DataGridViewRow Row in CadData_GridView.Rows) { // build a point from CAD data values double.TryParse(Row.Cells["X_nominal"].Value.ToString(), out Loc.X); double.TryParse(Row.Cells["Y_nominal"].Value.ToString(), out Loc.Y); Loc.W = 1; // transform it Loc = transform.TransformPoint(Loc); Loc = Loc.NormalizeHomogeneous(); // store calculated location values Row.Cells["X_machine"].Value = Loc.X.ToString("0.000", CultureInfo.InvariantCulture); Row.Cells["Y_machine"].Value = Loc.Y.ToString("0.000", CultureInfo.InvariantCulture); // handle rotation double rot; double.TryParse(Row.Cells["Rotation"].Value.ToString(), out rot); rot += angle; while (rot > 360.0) { rot -= 360.0; } while (rot < 0.0) { rot += 360.0; } Row.Cells["Rotation_machine"].Value = rot.ToString("0.0000", CultureInfo.InvariantCulture); } // Refresh UI: Update_GridView(CadData_GridView); // For debug, compare fiducials true measured locations and the locations. // Also, if a fiducials moves more than 0.5mm, something is off (maybe there // was a via too close to a fid, and we picked that for a measurement). Warn the user! bool DataOk = true; double dx, dy; for (int i = 0; i < Fiducials.Length; i++) { Loc.X = Fiducials[i].X_nominal; Loc.Y = Fiducials[i].Y_nominal; Loc.W = 1.0; Loc = transform.TransformPoint(Loc); Loc = Loc.NormalizeHomogeneous(); dx = Math.Abs(Loc.X - Fiducials[i].X_machine); dy = Math.Abs(Loc.Y - Fiducials[i].Y_machine); DisplayText(Fiducials[i].Designator + ": x_meas= " + Fiducials[i].X_machine.ToString("0.000", CultureInfo.InvariantCulture) + ", x_calc= " + Loc.X.ToString("0.000", CultureInfo.InvariantCulture) + ", dx= " + dx.ToString("0.000", CultureInfo.InvariantCulture) + ": y_meas= " + Fiducials[i].Y_machine.ToString("0.000", CultureInfo.InvariantCulture) + ", y_calc= " + Loc.Y.ToString("0.000", CultureInfo.InvariantCulture) + ", dy= " + dy.ToString("0.000", CultureInfo.InvariantCulture)); if ((Math.Abs(dx) > 0.4) || (Math.Abs(dy) > 0.4)) { DataOk = false; } }; if (!DataOk) { DisplayText(" ** A fiducial moved more than 0.4mm from its measured location"); DisplayText(" ** when applied the same calculations than regular componets."); DisplayText(" ** (Maybe the camera picked a via instead of a fiducial?)"); DisplayText(" ** Placement data is likely not good."); DialogResult dialogResult = ShowMessageBox( "Nominal to machine trasnformation seems to be off. (See log window)", "Cancel operation?", MessageBoxButtons.OKCancel ); if (dialogResult == DialogResult.Cancel) { return false; } } // Done! ValidMeasurement_checkBox.Checked = true; return true; }// end BuildMachineCoordinateData_m
public void AddComponent(PhysicalComponent x) { _components.Add(x); UpdateComponents(); }
// ================================================================================= // MeasureFiducial_m(): // Takes the parameter nominal location and measures its physical location. // Assumes measurement parameters already set. private bool MeasureFiducial_m(ref PhysicalComponent fid) { Cnc.CNC_XY(fid.nominal + JobOffset + PCBZeroLocation); var type = Properties.Settings.Default.use_template ? Shapes.ShapeTypes.Fiducial : Shapes.ShapeTypes.Circle; var loc = FindPositionAndMoveToClosest(type, 3, .1); if (loc == null) { ShowSimpleMessageBox("Can't Find Fiducial " + fid.Designator); return false; } fid.machine = loc; return true; }
// ================================================================================= // MeasureFiducial_m(): // Takes the parameter nominal location and measures its physical location. // Assumes measurement parameters already set. private bool MeasureFiducial_m(ref PhysicalComponent fid) { CNC_XY_m(fid.X_nominal + JobOffsetX + Properties.Settings.Default.General_JigOffsetX, fid.Y_nominal + JobOffsetY + Properties.Settings.Default.General_JigOffsetY); // If more than 3mm off here, not good. double X; double Y; if (!GoToCircleLocation_m(3, 0.1, out X, out Y)) { ShowMessageBox( "Finding fiducial: Can't regognize fiducial " + fid.Designator, "No Circle found", MessageBoxButtons.OK); return false; } fid.X_machine = Cnc.CurrentX + X; fid.Y_machine = Cnc.CurrentY + Y; // For user confidence, show it: for (int i = 0; i < 50; i++) { Application.DoEvents(); Thread.Sleep(10); } return true; }
private bool PickUpLoosePart_m(bool Probe, PhysicalComponent comp) { if (!Cnc.CNC_XY(Locations.GetLocation("Pickup"))) { return false; } // ask for it string ComponentType = comp.TypePlusFootprint; DialogResult dialogResult = ShowMessageBox( "Put one " + ComponentType + " to the pickup location.", "Placing " + comp.Designator, MessageBoxButtons.OKCancel); if (dialogResult == DialogResult.Cancel) { return false; } // Find component double X = 0; double Y = 0; double A = 0.0; cameraView.SetDownCameraFunctionSet("component"); // If we don't get a look from straight up (more than 2mm off) we need to re-measure for (int i = 0; i < 2; i++) { // measure 5 averages, component must be 8.0mm from its place int count = VideoDetection.MeasureClosestComponentInPx(out X, out Y, out A, cameraView.downVideoProcessing, (8.0 / Properties.Settings.Default.DownCam_XmmPerPixel), 5); if (count == 0) { ShowMessageBox( "Could not see component", "No component", MessageBoxButtons.OK); return false; } X = X * Properties.Settings.Default.DownCam_XmmPerPixel; Y = -Y * Properties.Settings.Default.DownCam_YmmPerPixel; DisplayText("PickUpLoosePart_m(): measurement " + i + ", X: " + X + ", Y: " + Y + ", A: " + A); if ((Math.Abs(X) < 2.0) && (Math.Abs(Y) < 2.0)) { break; } if (!Cnc.CNC_XY(Cnc.CurrentX + X, Cnc.CurrentY + Y)) { return false; } } Needle.Move_m(Cnc.CurrentX + X, Cnc.CurrentY + Y, A); // pick it up if (Probe) { DisplayText("PickUpLoosePart_m(): Probing pickup Z"); if (!Needle.ProbeDown()) { return false; } LoosePartPickupZ = Cnc.CurrentZ; DisplayText("PickUpLoosePart_m(): Probing Z= " + Cnc.CurrentZ); } else { DisplayText("PickUpLoosePart_m(): Part pickup, Z" + LoosePartPickupZ); if (!Cnc.CNC_Z(LoosePartPickupZ)) { return false; } } VacuumOn(); DisplayText("PickUpLoosePart_m(): needle up"); if (!Cnc.Zup()) { return false; } if (AbortPlacement) { return false; } return true; }
private bool PlacePart_m(bool LoosePart, bool resetTapeZ, PhysicalComponent comp) { if (AbortPlacement) return false; if (resetTapeZ && !LoosePart && !Tapes.ClearHeights_m(comp.MethodParameters)) return false; // Pickup: if (AbortPlacement) return false; if (LoosePart && !PickUpLoosePart_m(resetTapeZ, comp)) return false; bool win = false; var to = Tapes.GetTapeObjByID(comp.MethodParameters); if (to == null) return false; int startingPart = to.CurrentPart; for (int i = 0; i < NextPartRetryCount; i++) { for (int j = 0; j < SamePartRetryCount; j++) { if (AbortPlacement) return false; win = PickUpPart_m(comp.MethodParameters); if (win) break; } if (win) break; Tapes.GetTapeObjByID(comp.MethodParameters).IncrementPartNumber(); //try skipping a part } if (!win) { to.CurrentPart = startingPart; //didn't pick up anything so don't modify next part return false; } // Take the part to position: if (AbortPlacement) return false; DisplayText("PlacePart_m: " + comp.Designator + " At" + comp.machine, Color.Blue); if (!Needle.Move_m(comp.machine)) return false; // Place it: if (AbortPlacement) return false; if (LoosePart && !PutLoosePartDown_m(resetTapeZ)) return false; if (!PutPartDown_m(comp.MethodParameters)) return false; return true; }
// ================================================================================= // PlaceComponent_m() // This routine does the actual placement of a single component. // Component is the component name (Such as R15); based to this, we'll find the coordinates from CAD data // GroupRow is the row index to Job data grid view. // ================================================================================= private bool PlaceComponent(PhysicalComponent comp) { DisplayText("PlaceComponent_m: Component: " + comp.Designator); if (comp.IsFiducial) return true; //skip fiducials if ((comp.Method == "LoosePart") || (comp.Method == "Place") || (comp.Method == "Place Fast")) { PlacedComponent_label.Text = comp.Designator; PlacedComponent_label.Update(); PlacedValue_label.Text = comp.TypePlusFootprint; PlacedValue_label.Update(); var tapeObj = Tapes.GetTapeObjByID(comp.MethodParameters); if (tapeObj != null && !string.IsNullOrEmpty(tapeObj.TemplateFilename)) { var image = Image.FromFile(tapeObj.TemplateFilename); placement_Picturebox.Image = image; placement_Picturebox.Visible = true; } else { placement_Picturebox.Image = null; placement_Picturebox.Visible = false; } }; if (AbortPlacement) return false; // Component is at CadData_GridView.Rows[CADdataRow]. // What to do to it is at JobData_GridView.Rows[GroupRow]. switch (comp.Method) { case "?": ShowMessageBox( "Method is ? at run time", "Sloppy programmer error", MessageBoxButtons.OK); return false; case "Pause": ShowMessageBox( "Job pause, click OK to continue.", "Pause", MessageBoxButtons.OK); return true; case "LoosePart": if (comp.IsPlaced && skippedPlacedComponents_checkBox.Checked) return true; if (!PlacePart_m(true, comp.IsFirstInRow, comp)) { comp.IsError = true; return false; } comp.IsPlaced = true; break; case "Place Fast": case "Place": if (comp.IsPlaced && skippedPlacedComponents_checkBox.Checked) return true; if (!PlacePart_m(false, comp.IsFirstInRow, comp)) { comp.IsError = true; return false; } comp.IsPlaced = true; break; case "Change needle": if (!ChangeNeedle_m()) return false; break; case "Recalibrate": if (!PrepareToPlace_m()) return false; break; case "Ignore": return true; case "Fiducials": return true; default: ShowSimpleMessageBox("No way to handle method " + comp.Method); return false; } return true; }
// i think this is my favorite linq implementation especially how terse it is. so clean but very legible. public JobData FindJobDataThatContainsComponent(PhysicalComponent component) { return(JobData.FirstOrDefault(j => Enumerable.Contains(j.Components, component))); }
public bool ParseCadData_m(String[] AllLines, bool KiCad) { int ComponentIndex = -1; int ValueIndex = -1; int FootPrintIndex = -1; int X_Nominal_Index = -1; int Y_Nominal_Index = -1; int RotationIndex = -1; int LayerIndex = -1; bool LayerDataPresent = false; int i; int LineIndex = 0; // Parse header. Skip empty lines and comment lines (starting with # or "//") Regex skip_header = new Regex(@"^$|^#.*|//.*"); foreach (string s in AllLines) { if (skip_header.IsMatch(s)) { LineIndex++; continue; } break; } char delimiter; if (KiCad) { delimiter = ' '; } else { if (!FindDelimiter_m(AllLines[0], out delimiter)) { return(false); } ; } // Determien which column is what string[] h = SplitCSV(AllLines[LineIndex++], delimiter).ToArray(); Regex header_regexp = new Regex(@"^(designator|part|refdes|ref|component)$", RegexOptions.IgnoreCase); Regex value_regexp = new Regex(@"^(value|val|comment)$", RegexOptions.IgnoreCase); Regex footprint_regexp = new Regex(@"^(footprint|package|pattern)$", RegexOptions.IgnoreCase); Regex x_regexp = new Regex(@"^(x|x \(mm\)|posx|ref x)$", RegexOptions.IgnoreCase); Regex y_regexp = new Regex(@"^(y|y \(mm\)|posy|ref y)$", RegexOptions.IgnoreCase); Regex rotate_regexp = new Regex(@"^(rotation|rot|rotate)$", RegexOptions.IgnoreCase); Regex layer_regexp = new Regex(@"^(layer|side|tb)$", RegexOptions.IgnoreCase); for (int j = 0; j < h.Length; j++) { if (header_regexp.IsMatch(h[j])) { ComponentIndex = j; } if (value_regexp.IsMatch(h[j])) { ValueIndex = j; } if (footprint_regexp.IsMatch(h[j])) { FootPrintIndex = j; } if (x_regexp.IsMatch(h[j])) { X_Nominal_Index = j; } if (y_regexp.IsMatch(h[j])) { Y_Nominal_Index = j; } if (rotate_regexp.IsMatch(h[j])) { RotationIndex = j; } if (layer_regexp.IsMatch(h[j])) { LayerIndex = j; LayerDataPresent = true; } } if (ComponentIndex == -1 || ValueIndex == -1 || FootPrintIndex == -1 || X_Nominal_Index == -1 || Y_Nominal_Index == -1 || RotationIndex == -1) { Global.Instance.DisplayText("Headers not set correctly - unable to parse cad file"); return(false); } // Parse data string peek; Regex skip_lines_regex = new Regex("^$|^\"\"|^#.*|^//.*"); Regex top_regex = new Regex(@"top|f\.cu|t", RegexOptions.IgnoreCase); Regex bot_regex = new Regex(@"bottom|b\.cu|b|bot", RegexOptions.IgnoreCase); for (i = LineIndex; i < AllLines.Count(); i++) // for each component { peek = AllLines[i]; // Skip: empty lines and comment lines (starting with # or "//") if (skip_lines_regex.IsMatch(AllLines[i])) { continue; } List <String> Line = SplitCSV(AllLines[i], delimiter); // If layer is indicated and the component is not on this layer, skip it if (LayerDataPresent & MainForm.Bottom_checkBox.Checked & top_regex.IsMatch(Line[LayerIndex])) { continue; } if (LayerDataPresent & !MainForm.Bottom_checkBox.Checked & bot_regex.IsMatch(Line[LayerIndex])) { continue; } PhysicalComponent p = new PhysicalComponent(); p.Designator = Line[ComponentIndex]; p.Type = Line[ValueIndex]; p.Footprint = Line[FootPrintIndex]; p.nominal.X = double.Parse(Line[X_Nominal_Index].Replace("mm", "").Replace(",", ".")); if (LayerDataPresent && MainForm.Bottom_checkBox.Checked) { p.nominal.X *= -1; } p.nominal.Y = double.Parse(Line[Y_Nominal_Index].Replace("mm", "").Replace(",", ".")); p.Rotation = double.Parse(Line[RotationIndex]); p.PropertyChanged += (s, e) => { if (e.PropertyName.Equals("IsPlaced")) { MainForm.ReColorComponentsTable(); } }; ComponentData.Add(p); } MainForm.ReColorComponentsTable(); return(true); } // end ParseCadData
/* // If components are edited, update count automatically private void JobData_GridView_CellEndEdit(object sender, DataGridViewCellEventArgs e) { if (JobData_GridView.CurrentCell.ColumnIndex == 4) { // components List<String> Line = CAD.SplitCSV(JobData_GridView.CurrentCell.Value.ToString(), ','); int row = JobData_GridView.CurrentCell.RowIndex; JobData_GridView.Rows[row].Cells["ComponentCount"].Value = Line.Count.ToString(); Update_GridView(JobData_GridView); } } */ // ================================================================================= // Do someting to a group of components: // ================================================================================= // Several rows are selected at Job data: private void PlaceThese_button_Click(object sender, EventArgs e) { int selectedCount = JobData_GridView.SelectedRows.Count; if (selectedCount == 0) { CleanupPlacement(false); ShowSimpleMessageBox("Nothing selected"); return; } if (!PrepareToPlace_m()) { ShowSimpleMessageBox("Placement operation failed, nothing done."); return; } List<PhysicalComponent> toPlace = new List<PhysicalComponent>(); for (int i = 0; i < JobData_GridView.Rows.Count; i++) { for (int j = 0; j < selectedCount; j++) { if (JobData_GridView.SelectedRows[j].Index == i) { var job = (JobData)JobData_GridView.SelectedRows[j].DataBoundItem; //var job = (JobData) row.DataBoundItem; if (job.Method == "Change nozzle" || job.Method == "Recalibrate") { PhysicalComponent x = new PhysicalComponent(); x.Method = job.Method; x.MethodParameters = job.MethodParameters; x.JobData = job; toPlace.Add(x); } else { foreach (var component in job.GetComponents()) { component.JobData = job; if (!toPlace.Contains(component)) toPlace.Add(component); } } break; } } } if (!PlaceComponents(toPlace)) { ShowSimpleMessageBox("Placement operation failed. Review job status."); CleanupPlacement(false); return; } CleanupPlacement(false); }