#!/usr/bin/perl

#
# Read an Eagle .brd file, looking for parts.
# Output the suitable BOM and CPL spreadsheets
# needed to assemble the boards with pick and place
#
# Lines of interest in the .brd look a bit like this:
# <element name="E1" library="atmel-cpld" package="PQFP-100" value="ATF150X100PQFP" x="53.34" y="68.58" rot="R180"/>
#

#
# A known issue is that the origin appears to be 0.5mm
# down and left of where Eagle expects.
#
$jlcx = -0.5;
$jlcy = -0.5;
# BUGBUG: The above isn't actually working.
$jlcx = $jlcy = 0;

#
# Pick and place needs position and orientation information 
# relative to the center of mass.  For various reasons, the
# "center" of an Eagle part can have more to do with the
# visual center, and/or the alignments of pins to a grid.
#
# It is also possible for the Eagle part to be drawn with
# a different orientation (rotation) than JLC uses.
#
# To compensate, these tables note parts whose Eagle center
# is known to deviate from center-of-mass.  Each part may
# specify an angle and a distance from the Eagle center to
# center of mass.
%angle = (
  "C8512", 270,
);
%distance = (
);

#
# A key function of the BOM is to indicate which parts
# should be placed by the pick and place machine.  This
# means a sketchy mapping between the library;package;value
# tuple and a JLC part number.
#
%parts = (
  "atmel-cpld;TQFP-100;ATF1508-100", "C1521282",
  "atmel-cpld;TQFP-100;ATF150X100TQFP", "C1521282",
  "atmel-cpld;PQFP-100;ATF1508-100", "C184063",
  "atmel-cpld;PQFP-100;ATF150X100PQFP", "C184063",
  "con-shiua-chyuan;SCD-014-A;15V (CL)", " ", # omit
  "diode;SOD323-W;1N4148", "C2128",
  "diode;SOD323-W;1N4148WS", "C2128",
  "rcl;C0805;0.1uF", "C5378",
  "rcl;C0805;100nF", "C5378",
  "rcl;C0805;.22uF", "C5378",
  "rcl;C0805;10uF", "C15850",
  "rcl;R1206;100", "C17901", # Must be 1/4 watt at 5V
  "rcl;R0805;220", "C17557",
  "rcl;R0805;470", "C17710",
  "rcl;R0805;5.1K", "C27834",
  "transistor;SOT23-EBC;PN2222", "C8512",
  "USB4970-00-A;USB497000A;USB-C", " ", # omit
  "74xx-us;SO20W;74LS240DW", "C22449636",
  "rcl;C050-025X075;", " ", # omit
);

die "$ARGV[0]: Use a .brd file\n" unless $ARGV[0] =~ /[.]brd$/;
open(INPUT, $ARGV[0]) || die "$ARGV[0]: $!";
$comment = "$ARGV[0]"; $comment =~ s/[.]brd$//;
open(BOM, ">$comment.bom.csv") || die "$comment.bom.csv: $!";
print BOM "Comment,Designator,Footprint,PartNo\n";
open(CPL, ">$comment.cpl.csv") || die "$comment.cpl.csv: $!";
print CPL "Designator,Mid X,Mid Y,Layer,Rotation\n";

while (<INPUT>) {
  next unless /[<]element name="([^"]*)" library="([^"]*)" package="([^"]*)"/;
# <element name="E1" library="atmel-cpld" package="PQFP-100" value="ATF150X100PQFP" x="53.34" y="68.58" rot="R180"/>
  ($name, $library, $package) = ($1, $2, $3);
  $value = "";
  $value = $1 if /value="([^"]*)"/;
  die "$name: no position" unless /x="([^"]*)" y="([^"]*)"/;
  ($x, $y) = ($1, $2);
  $x += $jlcx;
  $y += $jlcy;
  $rot = "R0";
  $rot = $1 if /rot="([^"]*)"/;
  die "$name: confusing rotation: $rot" unless $rot =~ /^(M*)R(\d+)/;
  ($mirror, $angle) = ($1, $2);

  $jlc = undef;

  # Don't place through-hole connectors.
  $jlc = " " if $library eq "con-harting-ml";
  $jlc = " " if $library eq "con-lstb";
  $jlc = " " if $library eq "jumper";
  $jlc = " " if $package eq "DEC40PINH";

  # Don't place DIP packages (for now).
  $jlc = " " if $package =~ /^DIL.*/;

  # Don't place through-hole resistors.
  $jlc = " " if $package =~ /^0207\/10/;

  # Don't place components with value " ".
  $jlc = " " if $value eq " ";

  # Don't place board outlines or edge connectors.
  $jlc = " " if $package =~ /^EDGE-/;
  $jlc = " " if $value =~ /^OUTLINE-/;

  # Attempt to discern a JLC part number.
  $hash = "$library;$package;$value";
  $jlc = $parts{$hash} unless defined $jlc;

  # Don't place parts unless we know the JLC part number.
  die "$name: $hash: no match\n" unless defined $jlc;

  # Don't place parts we are meant to skip.
  $names{$hash} .= "$name " if $jlc eq " ";
  $count{$hash}++ if $jlc eq " ";
  next if $jlc eq " ";

  # Rotation of parts is CCW, per unit circle intuition.
  # Unit circle follows mirror; CCW from current side.
  # Is this the same at JLC?
  $mirror = "Bottom" if $mirror eq "M";
  $mirror = "Top" unless $mirror eq "Bottom";
  $angle += $angle{$jlc} if defined $angle{$jlc};
  $angle %= 360;
  # TODO: Adjust non-zero part-specific X and Y offsets!
  print BOM "$value,$name,$package,$jlc\n";
  print CPL "$name,$x,$y,$mirror,$angle\n";
}

foreach $part (sort keys %names) {
  chop $names{$part};
  print "$count{$part}\t$part\n";
}
