Skip to content

Commit

Permalink
Added support for KiCAD
Browse files Browse the repository at this point in the history
  • Loading branch information
nathanpc committed Sep 17, 2013
1 parent 74efd43 commit 1f728f2
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 49 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Some screenshots of the program running:
build-bom supports the following packages:

- [EAGLE](http://www.cadsoftusa.com/)
- [KiCAD](http://www.kicad-pcb.org/)

If you want your package to be added, please [open a Issue](https://github.com/nathanpc/build-bom/issues/new) providing the package name, a schematic file, and a PDF (or image) of the schematic. I'll implement support for your CAD package as soon as possible.

Expand All @@ -36,3 +37,4 @@ This script requires the following libraries to be installed in your Perl system

- [XML::LibXML](http://search.cpan.org/dist/XML-LibXML/LibXML.pod)
- [JSON](http://search.cpan.org/~makamaka/JSON-2.59/lib/JSON.pm)
- [File::Slurp](http://search.cpan.org/~uri/File-Slurp-9999.19/lib/File/Slurp.pm)
188 changes: 139 additions & 49 deletions build-bom.pl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use XML::LibXML;
use Term::ANSIColor;
use JSON;
use File::Slurp;


# Usage message.
Expand All @@ -27,60 +28,147 @@ sub usage {
print "\nExport Formats: json, csv, html\n";
}

sub check_program {
my ($filename) = @_;
my $header = "";

# Read the file header.
open(SCHFILE, '<', $filename);
for (my $line = 0; $line < 3; $line++) {
$header .= <SCHFILE>;
}
close(SCHFILE);

if ($header =~ /\<\!DOCTYPE eagle SYSTEM "eagle.dtd"\>/) {
return "eagle";
} elsif ($header =~ /Cmp-Mod/) {
return "kicad";
} else {
print colored("Error: ", "red") . "Couldn't identify the file format.\n\n";
print "Supported formats are:\n";
print " - *.sch EAGLE schematics\n";
print " - *.cmp KiCAD file\n";

die;
#return "unknown";
}
}

# Gets the list of parts in a hash.
sub get_parts_list {
my ($schematic) = @_;
my ($program, $schematic) = @_;
my $items = {};

# Setup the XML parser and stuff.
my $parser = XML::LibXML->new();
my $xml = $parser->parse_file($schematic);
if ($program eq "eagle") {
# EAGLE.

# Find the nodes.
my $parts = $xml->findnodes("/eagle/drawing/schematic/parts/part");
my $items = {};
# Setup the XML parser and stuff.
my $parser = XML::LibXML->new();
my $xml = $parser->parse_file($schematic);

# Parse each element.
foreach my $part ($parts->get_nodelist()) {
# Should this part be ignored?
if ($part->getAttribute("library") =~ /(supply[0-9]*)/) {
next;
}
# Find the nodes.
my $parts = $xml->findnodes("/eagle/drawing/schematic/parts/part");

# Parse each element.
foreach my $part ($parts->get_nodelist()) {
# Should this part be ignored?
if ($part->getAttribute("library") =~ /(supply[0-9]*)/) {
next;
}

# Get a value (if defined).
my $value = $part->getAttribute("value");
if (!defined $value) {
$value = "";
} else {
# Encode $value so it doesn't generate a warning when you try to print the ohms symbol.
if (utf8::is_utf8($value)) {
utf8::encode($value);
# Get a value (if defined).
my $value = $part->getAttribute("value");
if (!defined $value) {
$value = "";
} else {
# Encode $value so it doesn't generate a warning when you try to print the ohms symbol.
if (utf8::is_utf8($value)) {
utf8::encode($value);
}
}

# Create a key name and remove any special characters from it.
my $key_name = $part->getAttribute("deviceset") . $value;
$key_name =~ s/[^[:print:]]/_/g;

# Create the item hash.
my $item = {
"quantity" => 1,
"names" => [ $part->getAttribute("name") ],
"device" => $part->getAttribute("deviceset"),
"package" => $part->getAttribute("device"),
"value" => $value
};

# Check if the item already exists
if (defined $items->{$key_name}) {
# Just add to the quantity.
$items->{$key_name}->{"quantity"}++;

my @names = @{ $items->{$key_name}->{"names"} };
push(@names, $part->getAttribute("name"));
$items->{$key_name}->{"names"} = \@names;
} else {
# New item.
$items->{$key_name} = $item;
}
}
} elsif ($program eq "kicad") {
# KiCAD.
my $file = read_file($schematic);

# Iterate through the component block.
while ($file =~ /BeginCmp(.+?)EndCmp/sg) {
my $cmpblock = $1;
chomp $cmpblock;

my ($key_name, $name, $device, $pkg, $value);

# Iterate through the block lines.
while ($cmpblock =~ /(.+)[^;\n]/g) {
my @pair = split(/\s+=\s+/, $1);
$pair[1] =~ s/;//;

if ($pair[0] eq "Reference") {
# Part ID.
$name = $pair[1];
} elsif ($pair[0] eq "ValeurCmp") {
# Check if it's a value or a device name.
if ($pair[1] =~ /^[0-9]+[a-zA-Z]+$/) {
# Value.
$value = $pair[1];
$device = "";
} else {
# Device.
$device = $pair[1];
$value = "";
}
} elsif ($pair[0] eq "IdModule") {
$pkg = $pair[1];
}
}

# Create a key name and remove any special characters from it.
my $key_name = $part->getAttribute("deviceset") . $value;
$key_name =~ s/[^[:print:]]/_/g;

# Create the item hash.
my $item = {
"quantity" => 1,
"names" => [ $part->getAttribute("name") ],
"device" => $part->getAttribute("deviceset"),
"package" => $part->getAttribute("device"),
"value" => $value
};

# Check if the item already exists
if (defined $items->{$key_name}) {
# Just add to the quantity.
$items->{$key_name}->{"quantity"}++;

my @names = @{ $items->{$key_name}->{"names"} };
push(@names, $part->getAttribute("name"));
$items->{$key_name}->{"names"} = \@names;
} else {
# New item.
$items->{$key_name} = $item;
$key_name = "$pkg-$value$device";
my $item = {
"quantity" => 1,
"names" => [ $name ],
"device" => $device,
"package" => $pkg,
"value" => $value
};

# Check if the item already exists
if (defined $items->{$key_name}) {
# Just add to the quantity.
$items->{$key_name}->{"quantity"}++;

my @names = @{ $items->{$key_name}->{"names"} };
push(@names, $name);
$items->{$key_name}->{"names"} = \@names;
} else {
# New item.
$items->{$key_name} = $item;
}
}
}

Expand Down Expand Up @@ -248,25 +336,27 @@ sub main {
if (-f $schematic) {
# File.
my @spath = split(/[\/\\]/, $schematic);
my $program = check_program($schematic);

# Append the schematic.
push(@items, {
"name" => $spath[-1],
"parts" => get_parts_list($schematic)
"parts" => get_parts_list($program, $schematic)
});
} elsif (-d $schematic) {
# Directory.
opendir(DIR, $schematic) or die $!;

while (my $file = readdir(DIR)) {
# Only files ending in .sch, please...
# Only files with supported extensions, please...
next unless (-f "$schematic/$file");
next unless ($file =~ /\.sch$/);
next unless ($file =~ /\.(sch)|(cmp)$/);
my $program = check_program("$schematic/$file");

# Append the schematic.
push(@items, {
"name" => $file,
"parts" => get_parts_list("$schematic/$file")
"parts" => get_parts_list($program, "$schematic/$file")
});
}

Expand Down

0 comments on commit 1f728f2

Please sign in to comment.