1
0
mirror of git://projects.qi-hardware.com/eda-tools.git synced 2024-11-28 19:17:29 +02:00

rescue files from svn.openmoko.org

This commit is contained in:
Werner Almesberger 2012-05-01 23:09:24 -03:00
parent 702343cabc
commit 881bf33be0
15 changed files with 2210 additions and 0 deletions

140
old-boom/CHARACTERISTICS Normal file
View File

@ -0,0 +1,140 @@
*** This is an older draft of the concept - differs sometimes from the way
things are done in gta02-core. ***
BOM matching
============
BOMs are matched with inventories in the following way:
- a .lst file with the BOM is generated by KiCad
- using a ruleset, component characteristics are translated to a
canonical format and default values may be used for unspecified
characteristics
- part catalogs are searched for matches with the canonical component
descriptions. This yields a list of supplier-specific part numbers
for each component.
Parts can be characterized by either specifying their properties or
by equating them to another part. E.g., a Digi-Key part may be
defined as an NXP part which in turn is equivalent to a TI part.
- this list is then matched against inventories, using a suitable
optimization strategy (e.g., prioritize inventories and try to
pick as many suitable components as possible higher priority ones
before moving to lower priority ones)
E.g., local stock could be the first-level inventory, followed by
more distant warehouses, followed by distributors, followed by
manufacturers.
Inventories could also include pricing information.
- TBD: it would be good if parameters gathered in the matching process
could be fed back into KiCad (as some sort of annotations, similar
to the expanded view of schematic symbols), such that under-specifed
parts yielding mismatches can be spotted by manual review.
Catalog
=======
A catalog contains part characteristics and the reference number(s)
assigned to them.
Basic syntax
------------
Catalog entries consist of "words" in the sense that each word does
not contain any whitespace and words are separated from each other by
whitespace. Whitespace can be included in a word if it is enclosed in
double quotes.
Each entry begins in the first column of a line. If an entry needs
more than one line, the words on the continuation line(s) must be
indented by whitespace.
Trailing whitespace is ignored, and so are comments beginning with a
hash mark. Blank lines end any entry and are also ignored.
Each catalog entry begins with the part number followed by a part type
designator.
Characteristics have the form <field>=<value>, where
the fields follow the pattern outlined below. The value is some
description of the value of that characteristic, typically a number
and a unit (e.g., 4.7uF) or a name (e.g., X5R).
Numbers use a decimal point where necessary. Mantissas are normalized
such that they fall into the range 1 <= n < 1000. E.g., instead of
0.1uF, write 100nF. There is no space between number and unit. The
Omega of Ohm is written as "R".
Fields
------
Each
General fields
- - - - - - -
FP Footprint
H Height (overrides any height implied by footprint)
TOL Tolerance, with percent sign. Split tolerances are indicated as n/m%
DSC Free-format description
Resistors
- - - - -
RES Part type designator
R Resistance, with unit
P Maximum power dissipation
V Maximum volatage
Capacitors
- - - - -
CAP Part type designator
C Capacitance, with unit
M Material, e.g., TANT, NP0, X5R, etc.
V Maximum voltage
ESR ESR, with unit
Inductors
-- - - -
Diodes
- - -
DIODE Regular diode
STKY Schottky diode
Vf Maximum forward voltage
Vr Maximum reverse voltage
If Maximum forward current
Ir Maximum reverse current
C Capacitance
LED Ligh-emitting diode
COL Color, multiple colors are separated by /, e.g., blue/red
ARRAY If multiple diodes form an array, this parameter describes
its structure: CA = common anode, CC = common cathode,
SEQ = tap-A-C-tap-A-C-tap sequence
ZENER Zener diode
Vz Zener voltage
TVS Transient voltage suppressor
Vac Working voltage, AC
Vdc Working voltage, DC
E Energy

19
old-boom/Makefile Normal file
View File

@ -0,0 +1,19 @@
UPLOAD=werner@sita.openmoko.org:public_html/gta02-core/
COPY=rsync -e ssh --progress
.PHONY: all xpdf spotless upload
all:
@echo "make what ? xpdf, upload, or spotless ?" 1>&2
workflow.pdf: workflow.fig
fig2dev -L pdf $< >$@ || { rm -f $@; exit 1; }
xpdf: workflow.pdf
xpdf workflow.pdf
upload: workflow.pdf
$(COPY) workflow.pdf $(UPLOAD)/bom-workflow.pdf
spotless:
rm -f workflow.pdf

337
old-boom/README Normal file
View File

@ -0,0 +1,337 @@
The BOM processing system
=========================
The BOM processing system takes a bill of material generated by
KiCad and converts it in various steps into a "shopping list"
that can be used to order from various providers.
Introduction
============
The following sections describe how to use the basic elements of
the BOM processing system.
A simple BOM translation
------------------------
KiCad identifies components by a so-called component reference,
e.g., R1001, U5, etc. In addition to this, each component can have
various parameters, such as a "value", its footprint, and further
user-defined items. These parameters can be shown in the schematics
(e.g., the value usually is) or they can be hidden (e.g., the
footprint).
At the end of the process, we want a "shopping list" that can be
used to order items or to find them in an inventory or catalog.
Components in the shopping list are identified by a part number.
...
- BOM
- inventory
- ID matching
Equivalences
------------
A single component can be associated with multiple part numbers.
For example, a chip its manufacturer calls "XYZ-R1" may be listed in
a distributor's catalog with a completely different order number,
such as "20-1234-8". The BOM processing system therefore
distinguishes multiple so-called name spaces. A name space is
identified by a (unique) name and a part number is generally
qualified by the name of the name space.
E.g., if the manufacturer is called "ACME" and the distributor of
electronical components calls itself "DIST-EL", the part in our
example may have the equivalent names "ACME XYZ-R1" and "DIST-EL
20-1234-8".
...
- revise .inv
example.equ:
#INV
DIST-EL 20-1234-8
#EQU
ACME XYZ-R1 DIST-EL 20-1234-8
Adding stock and cost
---------------------
- .inv, more fields
- quanta
Substituting component names
----------------------------
- intro to .sub
- ad-hoc fixes
Selecting characteristics
-------------------------
- .sub
- .chr
- <rel><number><multiplier><unit> syntax
...
Generating characteristics
--------------------------
- .gen
Advanced topics
===============
- generating .inv files
- different presentations (e.g., CT, TR, ...)
- component substitution (one-way equivalence)
- problem reports
- hiding known problems (while sourcing)
File formats
============
The BOM processing system uses a large number of different files to
store information retrieved from the BOM, inventories, intermediate
results, etc. The following sections describe the various formats.
Part characteristics (.chr)
---------------------------
A part characteristics file lists the parameters of components.
This information is then matched with the parameters specified in
the schematics.
The part characteristics file begins with a line containing only
#CHR
After this, each line contains the manufacturer (namespace), the
part number, and a list of parameter=value entries. Fields are
separated by spaces.
Long lines can be wrapped by indenting the continuation lines.
Blank lines and comments (#) are ignored.
Substitutions (.sub)
--------------------
A substitutions file specifies rules for translating component
parameters in schematics to part characteristics.
A substitution rule consists of zero or more conditions and zero or
more assignments. The conditions are of the form field=pattern. The
field can be a per-component fields KiCad provides or any parameter
set by substitutions.
KiCad fields are named as follows:
KiCad field Field name
----------- ----------
Reference REF (*)
Value VAL
Footprint FP
Field1 F1
... ...
(*) As a shortcut, REF= can be omitted.
Note that fields with a user-defined name currently still only appear
as F1, F2, etc.
The special field name FN can be used to look for a match in all of
F1, F2, ... This way, it's sufficient to use a consistent syntax for
additional parameters, without having to assign also a fixed location
for them. If more than one field matches, the first match is taken.
Field names are case-insensitive.
The pattern is uses a notation similar to filename globbing. There
are the following special constructs:
- * matches a string of any length
- ? matches a single character
- (...) matches the pattern between the parentheses and records the
string matched
- $X marks a value in nXn notation, e.g., 4u7 or 100R. Such values
are converted to SI-like notation.
A rule is applied when all conditions are fulfilled. In this case,
assignments of the form field=value are executed. Strings obtained
in the match can be included in a value as follows:
- $field and ${field} are replaced by the respective field
- $field:n and ${field:n} are replaced by the n-th (...) pattern in
the match of the respective field
If a rule ends with an exclamation mark, the substitution process stops
after the rule is applied. Otherwise, further rules are processed.
Examples:
R* val=$R -> R=$val
This rule translates the values of all resistors to SI notation.
D* FN=(*)Vdc -> T=TSV Vdc=FN:1
This rule sets the parameters T and Vdc for Zeners acting as TSVs.
If a set of rules has a common set of conditions or assignments, the
more compact block notation can be used instead of repeating them for
each rule:
common-conditions -> common-assignments {
rule-specific-conditions -> rule-specific-assignments
...
}
Rules in a block only match if both the common and the rule-specific
conditions are met. Then the common and the rule-specific assignments
are performed. If a condition or an assignment appears both in the
common and the rule-specific part, only the latter is used.
Long lines can be wrapped by indenting the continuation lines. Note
that { and ! are also considered to be part of the same line as the
rest of the rule. In particular, the following construct wouldn't
work:
X=Y
{
...
}
With proper indentation, this would:
X=Y
{
...
}
Characteristics generation (.gen)
---------------------------------
The substitution mechanism can also be used to automatically generate
characteristics from part numbers, e.g., for resistors or capacitors.
.gen files are exactly .sub files, with the exception that the only
field used is the REF field and that it contains the part number.
Once the rule set has been processed, all fields (except REF) whose
name doesn't begin with an underscore are placed in the characteristics
entry as parameters.
An entry is only produced if the rule set is explicitly terminated.
Parts list (.par)
------------------
A parts file lists the parts that are suitable for a given BOM item.
The file begins with a line containing only
#PAR
After this, each line contains the component reference, a space, and
then one or more namespace part-number groups, separated by spaces as
well.
Blank lines and comments (#) are ignored.
Order list (.ord)
-----------------
An order file lists the quantities to order from inventories, along
with the cost and the component references the item is used for. The
file begins with a line containing only
#ORD
After this, each line contains the supplier (namespace), the part
number, the number of items to order, the currency code, the cost,
and one or more component references.
Blank lines and comments (#) are ignored.
Equivalence (.equ)
------------------
Equivalence files establish equivalences between parts numbers in the
same or in different name spaces. An equivalence file begins with a
line containing only
#EQU
After this, each line consists of the following four space-separated
fields:
namespace-1 part-number-1 namespace-2 part-number-2
Blank lines and comments (#) are ignored.
Inventory (.inv)
----------------
Inventory files list inventory and component cost. An inventory file
begins with a line containing only
#INV
After this, each line contains the namespace and the part number,
followed by the number of items in stock, the currency code, and one
or more pricing entries.
Each pricing entry consists of two fields: the number of items in an
order, and the per item price at that quantity. A sequence of
increasing order sizes indicates that they are quanta. A sequence of
decreasing order sizes indicates that smaller quanta are possible
after a previous larger threshold has been met.
Example:
... USD 1 0.5 10 0.4 100 0.2
Means that an order of at least 170 units would be made either as
2 * 100 units, costing USD 40, or as 1 * 100 + 7 * 10 units, costing
USD 20 + USD 28 = USD 48.
If the entry is
... USD 1 0.5 10 0.4 100 0.2 1 0.2
Then the USD 0.2 per unit cost would apply to any any quantity of at
least 100 units. So a 170 units order would cost USD 34.
Blank lines and comments (#) are ignored.
The number of items in stock and the pricing data can be omitted. We
call this "virtual inventory". In this case, the numer of items in
stock and the price default to large numbers (e.g., 999999). Virtual
inventory is used to suppress warnings for parts that have not been
sourced yet, but where sourcing is in progress.
Description (.dsc)
------------------
A description file contains plain text descriptions of parts. The file
begins with a like containing only
#DSC
Each line contains the name space, a space, the part number, another
space, and the description. The description can contain any printable
character and ends with a newline.
Blank lines and comments (#) are ignored.

222
old-boom/annotate Executable file
View File

@ -0,0 +1,222 @@
#!/usr/bin/perl
require "parser.pl";
require "misc.pl";
$H = 50; # character height
$W = $H*0.9; # character width
$L = $H+20; # line skip
sub normalize
{
my @t = @_;
# convert from (x0, y0, w, h) to (x0, y0, x1, y1)
$t[2] += $t[0];
$t[3] = $t[1]-$t[3];
return ($t[0], $t[3], $t[2], $t[1]);
}
#
# 2x2 matrix inversion
# http://en.wikipedia.org/wiki/Invertible_matrix#Inversion_of_2.C3.972_matrices
#
sub invert
{
my @m = @_;
my $f = 1/($m[0]*$m[3]-$m[1]*$m[2]);
return ($f*$m[3], -$f*$m[1], -$f*$m[2], $f*$m[0]);
}
sub block
{
my @t = &normalize(@_);
push(@block, [ @t ]);
$wnl .= "Wire Notes Line\n\t$t[0] $t[1] $t[2] $t[3]\n";
}
sub pass
{
my @t = &normalize(@_);
for (@block) {
my @b = @{ $_ };
next if $t[0] > $b[2];
next if $t[2] < $b[0];
next if $t[1] > $b[3];
next if $t[3] < $b[1];
return 0;
}
return 1;
}
sub put
{
local ($x0, $y0, $ref, @s) = @_;
my $h = @s*$L;
my $w = 0;
for (@s) {
my $t = $W*length $_;
$w = $t if $t > $w;
}
my $a = 270;
my $r = 100;
my $x, $y;
my $ym = $y0-$h+$H/2;
for ($i = 0; $i != 128; $i++) {
$x = int($x0+$r*cos($a/180*3.14159));
$y = int($ym+$r*sin($a/180*3.14159));
last if &pass($x, $y, $w, $h);
$a += 22.5;
$r += $L/8;
}
warn "no place found for \"$s[0]\"" if $i == 128;
my @m = &invert( @{ $m{$ref} });
&block($x, $y+$H/2, $w, $h);
my $n = 10;
for my $s (reverse @s) {
my $dx = $x-$x0;
my $dy = $y-$y0;
my $sx = $x0+$dx*$m[0]+$dy*$m[1];
my $sy = $y0+$dx*$m[2]+$dy*$m[3];
($hv, $hj, $vj) = ("H", "L", "C") if $m[0] == 1;
($hv, $hj, $vj) = ("H", "R", "C") if $m[0] == -1;
($hv, $hj, $vj) = ("V", "C", "B") if $m[1] == 1;
($hv, $hj, $vj) = ("V", "C", "T") if $m[1] == -1;
$s =~ s/~/-/g;
print "F $n \"$s\" $hv $sx $sy $H 0000 $hj ${vj}NN\n";
$y -= $L;
$n++;
}
}
sub dsc_parts
{
local ($ref) = @_;
my @p = @{ $parts{$ref} };
my @f = ();
while (@p) {
my @id = splice(@p, 0, 2);
my $id = "$id[0] $id[1]";
my $dsc = &dsc_find($id);
push(@f, &dsc) if defined $dsc;
}
return @f;
}
sub dsc_order
{
local ($ref) = @_;
my @f = ();
for my $id (keys %order) {
my @p = @{ $order{$id} };
for (splice(@p, 3)) {
push(@f, &dsc_find($id)) if $_ eq $ref;
}
}
return @f;
}
sub usage
{
print STDERR "usage: $0 [-s/from/to/ ...] ...\n";
exit(1);
}
while ($ARGV[0] =~ /^-s/) {
&usage unless &dsc_xlat_arg($');
shift @ARGV;
}
&usage if $ARGV[0] =~ /^-./;
&parse;
#
# pass 1: find the orientation of all parts
#
for (@eeschema) {
$ref = $1 if /^L \S+ (\S+)/;
undef $ref if /^\$EndComp/;
next unless /^\s+(-?[01])\s+(-?[01])\s+(-?[01])\s+(-?[01])\s*$/;
my @m = split(/\s+/);
shift @m;
$m{$ref} = [ @m ];
}
#
# pass 2: block the spaces occupied by fields
#
for (@eeschema) {
$ref = $1 if /^L \S+ (\S+)/;
if (/^P (\d+) (\d+)/) {
$x0 = $1;
$y0 = $2;
}
next unless /^F /;
die "$_" unless
/^F \d+ "([^"]*)" ([HV]) (\d+) (\d+) (\d+) +(\d+) ([LC]) (C)/;
($s, $hv, $x, $y, $size, $flag, $hj, $vj) =
($1, $2, $3, $4, $5, $6, $7, $8);
$dx = $x-$x0;
$dy = $y-$y0;
$x = $x0+$dx*$m{$ref}[0]+$dy*$m{$ref}[1];
$y = $y0+$dx*$m{$ref}[2]+$dy*$m{$ref}[3];
next if $flag != 0;
$w = $size*0.8*length $s;
# we don't need to consider H/V
&block($hj eq "L" ? $x : $x-$w/2, $y+$size/2, $w, $size);
}
#
# pass 3:
#
for (@eeschema) {
undef @f if /^\$Comp/;
if (/^L \S+ (\S+)/) {
$ref = $1;
push(@f, &dsc_order($ref)) if %order;
push(@f, &dsc_parts($ref)) if %parts;
}
if (/^P (\d+) (\d+)/) {
$x = $1;
$y = $2;
}
if (/^\s+/) {
my %seen;
my @u = ();
for (@f) {
next if $seen{$_};
push(@u, $_);
$seen{$_} = 1;
}
undef @f;
# $m{$ref}[0] == 1 OK
# $m{$ref}[0] == -1 OK
# $m{$ref}[1] == 1 OK
# $m{$ref}[1] == -1 OK (small deviations found)
&put($x, $y, $ref, @u) if 1 || $m{$ref}[1] == -1;
}
if (/\$EndSCHEMATC/) {
# uncomment for debugging
# print $wnl;
}
print "$_\n";
}

129
old-boom/bom2part Executable file
View File

@ -0,0 +1,129 @@
#!/usr/bin/perl
require "parser.pl";
require "match.pl";
require "misc.pl";
sub issue
{
print shift(@_), " ", join(" ", @_, &eq(@_)), "\n";
}
sub scale
{
local ($v, $m) = @_;
return $v*1e-12 if $m eq "p";
return $v*1e-9 if $m eq "n";
return $v*1e-6 if $m eq "u";
return $v*1e-3 if $m eq "m";
return $v*1e3 if $m eq "k";
return $v*1e6 if $m eq "M";
return $v*1e9 if $m eq "G";
return $v if $m eq "";
die "unknown multiplier \"$m\"";
}
sub compat
{
local ($a, $b) = @_; # $a = part char., $b = component spec.
return 1 if $a eq $b;
return 0 unless $a =~ /^([0-9.]+)([GMkmunp]?)/;
my ($av, $am, $au) = ($1, $2, $');
return 0 unless $b =~ /^(>|>=|<|<=)([0-9.]+)([GMkmunp]?)/;
my ($rel, $bv, $bm, $bu) = ($1, $2, $3, $');
return 0 if $au ne $bu;
$av = &scale($av, $am);
$bv = &scale($bv, $bm);
return $av > $bv if $rel eq ">";
return $av >= $bv if $rel eq ">=";
return $av < $bv if $rel eq "<";
return $av <= $bv if $rel eq "<=";
die;
}
if ($ARGV[0] eq "-d") {
$debug = 1;
shift @ARGV;
}
&parse;
$total = 0;
$bad = 0;
print "#PAR\n";
for $ref (keys %cmp) {
@f = @{ $cmp{$ref} };
$total++;
print STDERR "REF $ref\n" if $debug;
# if we're lucky, we get a direct ID match
if (defined $id{$f[0]}) {
print STDERR "FIRST ID\n" if $debug;
&issue($ref, $id{$f[0]});
next;
}
# no such luck. Let's roll up our sleeves and to the substitutions.
undef %field;
$field{"REF"} = $ref;
$field{"VAL"} = $f[0];
if ($f[1] eq "") {
print STDERR "warning: $ref ($f[0]) has no footprint\n";
} else {
$field{"FP"} = $f[1];
}
for (my $i = 1; $i != 10; $i++) {
$field{"F$i"} = $f[$i+1];
}
&apply_rules();
# try our luck again
if (defined $id{$field{"VAL"}}) {
print STDERR "SECOND ID\n" if $debug;
&issue($ref, $id{$field{"VAL"}});
next;
}
# still nothing. Let's match characteristics then.
my @p = ();
COMP: for my $c (keys %chr) {
print STDERR "PART $c\n" if $debug;
for (keys %field) {
next if $_ eq "REF" || $_ eq "VAL" || $_ =~ /^F\d$/;
next if $field{$_} eq "";
print STDERR " $_=",$field{$_}," " if $debug;
if (!defined $chr{$c}{$_}) {
print STDERR "NO FIELD\n" if $debug;
next COMP;
next;
}
if (&compat($chr{$c}{$_}, $field{$_})) {
print STDERR "== $chr{$c}{$_}\n" if $debug;
} else {
print STDERR "!= $chr{$c}{$_}\n" if $debug;
next COMP;
}
}
push(@p, $c);
}
if (@p) {
&issue($ref, @p);
next;
}
print STDERR "unmatched: $ref (", join(", ", @f), ")\n";
$bad++;
# print join("#", ($ref, @f)), " -> $id{$f[0]}\n";
}
print STDERR "$bad/$total unmatched\n" if $bad;

26
old-boom/boom Executable file
View File

@ -0,0 +1,26 @@
#!/usr/bin/perl
sub usage
{
print STDERR "usage: $0 command [arg ...]\n";
exit(1);
}
&usage unless @ARGV;
($d = $0) =~ s|/[^/]*$||;
if ($d eq "") {
$p = "/";
} elsif ($d =~ /^\//) {
$p = "$d";
} else {
chomp($cwd = `pwd`);
$p = "$cwd/$d";
}
$cmd = shift @ARGV;
$cmd = "$p/$cmd" unless $cmd =~ m|/|;
exec("perl", "-I", $p, $cmd, @ARGV);
die "exec perl: $!";

64
old-boom/gen2chr Executable file
View File

@ -0,0 +1,64 @@
#!/usr/bin/perl
require "parser.pl";
require "match.pl";
sub translate
{
local ($r) = @_;
undef %field;
$field{"REF"} = $r;
&match_set_error($r);
if (!&apply_rules()) {
print "$id{$r}\n" if $negate;
return;
}
return if $negate;
print $id{$r};
for (sort keys %field) {
next if $_ =~ /^_/;
next if $_ eq "REF";
print " $_=$field{$_}";
}
print "\n";
}
sub usage
{
print STDERR "usage: $0 [-d|-n] key file ...\n";
print STDERR " $0 -q query file ...\n";
exit(1);
}
while ($ARGV[0] =~ /^-/) {
if ($ARGV[0] eq "-d") {
$debug = 1;
} elsif ($ARGV[0] eq "-n") {
$negate = 1;
} elsif ($ARGV[0] eq "-q") {
$query = 1;
} else {
&usage;
}
shift @ARGV;
}
$key = shift @ARGV;
&usage unless defined $key;
&parse;
if ($query) {
$debug = 1;
&translate($key);
exit(0);
}
print "#CHR\n";
for (keys %id) {
next unless $id{$_} eq "$key $_";
&translate($_);
}

210
old-boom/match.pl Executable file
View File

@ -0,0 +1,210 @@
#!/usr/bin/perl
use re 'eval';
#
# "sub" populates the following global variables:
#
# $end[rule-number] = 0 / 1
# $match[rule-number]{field}[0] = original-pattern
# $match[rule-number]{field}[1] = RE1
# $match[rule-number]{field}[2] = RE2
# $action[rule-number]{field} = value
#
# $match_stack[depth]{field}[0] = original-pattern
# $match_stack[depth]{field}[1] = RE1
# $match_stack[depth]{field}[2] = RE2
# $action_stack[depth]{field} = value
# $may_cont = 0 / 1
# $last
# $last_action
#
#
# $cvn_from{internal-handle} = index
# $cvn_to{internal-handle} = index
# $cvn_unit{internal-handle} = unit-name
# $cvn_num = internal-handle
# $found{field-or-subfield} = string
#
# We convert each input pattern into two regular expressions: the first matches
# units in the nXn notation, e.g., 4u7 or 100R. The second matches them in SI
# notation (sans space).
#
# When matching (sub_match), we first apply the first expression. Each time we
# encounter a unit ($R, $F, etc.), __cvn is called. __cvn stores the index of
# the unit in %cvn_from and %cvn_to.
#
# We then pick these substrings from the input string and convert the units to
# SI notation. At the same time, we normalize the mantissa. Once done, we run
# the second expression. This one always matches (hopefully :-)
#
# All (...) ranges in the original pattern have been replaced with named
# capture buffers in the second expression, so all these subfields are now
# gathered in the $+ array. (The same also happened in the first pass, but we
# ignore it.)
#
# Finally, when expanding a value (sub_expand), we look for $field and
# $field:index, and expand accordingly.
#
sub __cvn
{
local ($num) = @_;
$cvn_from{$num} = $-[$#-];
$cvn_to{$num} = $+[$#+];
}
sub sub_match
{
local ($s, $field, $m1, $m2) = @_;
#
# Perform the first match and record where we saw $<unit> patterns.
#
undef %cvn_from;
undef %cvn_to;
return undef unless $s =~ $m1;
#
# Convert the unit patterns to almost-SI notation. (We don't put a space
# after the number, but the rest is SI-compliant.)
#
my $off = 0;
for (keys %cvn_from) {
my $unit = $cvn_unit{$_};
my $from = $cvn_from{$_}+$off;
my $len = $cvn_to{$_}-$cvn_from{$_};
die unless substr($s, $from, $len) =~
/(\d+)$unit(\d*)|(\d+)([GMkmunpf])(\d*)/;
#
# Normalize to \d+.\d*
#
my $v = "$1$3.$2$5";
my $exp = $4 eq "" ? " " : $4;
#
# Remove leading zeroes.
#
$v =~ s/^0*(\d+)/\1/;
#
# Mantissa must be < 1000.
# Do the math as string operation to avoid rounding errors.
#
while ($v =~ /(\d+)(\d{3})\./) {
$v = "$1.$2$'";
$exp =~ tr/GMk munpf/TGMk munp/;
}
#
# Mantissa must be >= 1.
#
while ($v =~ /\b0\.(\d+)/) {
if (length $1 < 3) {
$v = $1.("0" x (3-length $1)).".";
} else {
$v = substr($1, 0, 3).".".substr($1, 3);
}
$exp =~ tr/GMk munpf/Mk munpa/;
}
#
# Remove trailing zeroes
#
$v =~ s/(\.[1-9]*)0*/\1/;
$exp =~ s/ //;
$v =~ s/\.$//;
$v = $v.$exp.$unit;
$off += length($v)-$len;
substr($s, $from, $len, $v);
}
#
# Run the second match on the string to process any (...) patterns
#
$found{$field} = $s;
die $m2 unless $s =~ $m2;
for (keys %+) {
$found{$_} = $+{$_};
}
return $s;
}
sub sub_expand
{
local ($s) = @_;
while ($s =~ /^([^\$]*)\$([A-Za-z_]\w*)(:(\d+))?|^([^\$]*)\${([A-Za-z_]\w*)(:(\d+))?}/) {
my $name = "$2$6";
$name .= "__$4$8" if defined($4) || defined($8);
if (!defined $found{$name}) {
die "don't know \"$name\"".
(defined $__match_error ?
" (processing \"$__match_error\")" : "");
}
$s = $1.$5.$found{$name}.$';
}
return $s;
}
#
# return 0 if all rules have been exhausted, 1 if there was an explicit halt.
#
sub apply_rules
{
RULE: for (my $i = 0; $i <= $#match; $i++) {
print STDERR "RULE #$i\n" if $debug;
%found = %field;
FIELD: for my $f (keys %{ $match[$i] }) {
my @f = $f ne "FN" ? ($f) :
("F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9");
for (@f) {
print STDERR " MATCH $_=$match[$i]{$f}[0] " if $debug;
if (!defined $found{$_}) {
print STDERR "NO FIELD\n" if $debug;
next;
}
print STDERR "FIELD $found{$_} " if $debug;
if (!defined &sub_match($found{$_}, $f,
$match[$i]{$f}[1], $match[$i]{$f}[2])) {
print STDERR "MISS\n" if $debug;
next;
}
print STDERR "MATCH\n" if $debug;
next FIELD;
}
next RULE;
}
for (keys %{ $action[$i] }) {
my $s = &sub_expand($action[$i]{$_});
print STDERR " SET $_=$action[$i]{$_} => $s\n" if $debug;
$field{$_} = $s;
}
if ($end[$i]) {
print STDERR " END\n" if $debug;
return 1;
}
}
return 0;
}
sub match_set_error
{
$__match_error = $_[0];
}
return 1;

95
old-boom/misc.pl Executable file
View File

@ -0,0 +1,95 @@
#!/usr/bin/perl
#
# determine the equivalent parts, taking into account that %eq is transitive
#
sub eq
{
my %seen;
my @p = @_; # parts to consider
my @r = (); # new equivalences we've found
my $skip = @p;
while (@p) {
my $p = shift @p;
next if $seen{$p};
$seen{$p} = 1;
push(@r, $p) if $skip-- <= 0;
push(@p, @{ $eq{$p} });
}
return @r;
}
#
# When looking for a description, we also consider equivalent parts.
#
# Furthermore, some descriptions may just be pointers to other descriptions.
# Users can add regular expressions that are used to extract references from
# a description, which are then looked up as well.
#
sub __dsc_lookup
{
local ($id) = @_;
for ($id, &eq($id)) {
return $dsc{$_} if defined $dsc{$_};
}
return undef;
}
sub dsc_find
{
my $id = $_[0];
LOOKUP: while (1) {
my $dsc = &__dsc_lookup($id);
return undef unless defined $dsc;
for (my $i = 0; $i <= $#xlat_from; $i++) {
# @@@ this is UUUUHHHGLLEEEEE !!! Why can't I just expand $to[$i] ?
next
unless ($id = $dsc) =~ s/^.*$xlat_from[$i].*$/$xlat_to[$i] $1/;
next LOOKUP if defined &__dsc_lookup($id);
}
return $dsc;
}
return undef;
}
sub dsc_xlat
{
local ($from, $to) = @_;
push(@xlat_from, $from);
push(@xlat_to, $to);
}
sub dsc_xlat_arg
{
return undef unless $_[0] =~ /^(.)([^\1]*)\1([^\1]*)\1$/;
&dsc_xlat($2, $3);
return 1;
}
#
# Lexical ordering of component references
#
sub cmp_cref
{
local ($a, $b) = @_;
local ($as, $an, $bs, $bn);
return $a cmp $b unless ($as, $an) = $a =~ /^([[:alpha:]]+)(\d*)$/;
return $a cmp $b unless ($bs, $bn) = $b =~ /^([[:alpha:]]+)(\d*)$/;
return $as cmp $bs unless $as eq $bs;
return $an <=> $bn
}
return 1;

16
old-boom/pardup.pl Executable file
View File

@ -0,0 +1,16 @@
#!/usr/bin/perl
while (<>) {
@f = split(/\s+/);
$ref = shift @f;
for ($i = 0; $i != @f; $i++) {
next unless $f[$i] eq "FIC" || $f[$i] eq "MISSING" ||
$f[$i] eq "DIGI-KEY";
splice(@f, $i, 2);
$i--;
}
next if @f < 3;
push(@{ $multi{join(" ", @f)} }, $ref);
}
for (sort keys %multi) {
print "$_ -- ", join(" ", @{ $multi{$_} }), "\n";
}

496
old-boom/parser.pl Executable file
View File

@ -0,0 +1,496 @@
#!/usr/bin/perl
use re 'eval';
use IO::File;
#
# "sanitize" converts all "special" characters to underscores. This is used to
# avoid part names that could conflict with other uses of meta-characters, such
# as spaces or hash signs.
#
sub sanitize
{
local (*s) = @_;
my $ok = '[^-a-zA-Z0-9._%,:()=+\/]';
print STDERR "converting special character(s) in $s\n" if $s =~ /$ok/;
$s =~ s/$ok/_/g;
}
sub skip
{
# do nothing
}
#
# "bom" populates the following global variable:
#
# $cmp{component-reference}[0] = value
# $cmp{component-reference}[1] = footprint
# $cmp{component-reference}[2] = field1
# ...
#
sub bom
{
if (/^#End Cmp/) {
$mode = *skip;
return;
}
die unless /^\|\s+(\S+)\s+/;
my $ref = $1;
my @f = split(/\s*;\s*/, $');
next if $f[0] eq "NC";
for (@f) {
s/\s+$//;
&sanitize(\$_);
}
$cmp{$ref} = [ @f ];
}
#
# "equ" populates the following global variables:
#
# $id{item-number} = "namespace item-number"
# This is used for heuristics that look up parts commonly referred to by
# their part number.
#
# $eq{"namespace0 item-number0"}[] = ("namespace1 item-number1", ...)
# List of all parts a given part is equivalent to.
#
sub equ
{
my @f = split(/\s+/);
&sanitize(\$f[1]);
&sanitize(\$f[3]);
my $a = "$f[0] $f[1]";
my $b = "$f[2] $f[3]";
$id{$f[1]} = $a;
$id{$f[3]} = $b;
push @{ $eq{$a} }, $b;
push @{ $eq{$b} }, $a;
}
#
# "inv" populates the following global variables:
#
# $id{item-number} = "namespace item-number"
# This is used for heuristics that look up parts commonly referred to by
# their part number.
#
# $inv{"namespace item-number"}[0] = items-in-stock
# $inv{"namespace item-number"}[1] = currency
# $inv{"namespace item-number"}[2] = order-quantity
# $inv{"namespace item-number"}[3] = unit-price
# [2] and [3] may repeat.
#
sub inv
{
my @f = split(/\s+/);
&sanitize(\$f[1]);
my $id = "$f[0] $f[1]";
shift @f;
my $ref = shift @f;
die "duplicate inventory entry for \"$id\"" if defined $inv{$id};
$id{$ref} = $id;
$inv{$id} = [ @f ];
$inv{$id}[0] = 999999 unless defined $inv{$id}[0];
$inv{$id}[1] = "N/A" unless defined $inv{$id}[1];
$inv{$id}[2] = 1 unless defined $inv{$id}[2];
$inv{$id}[3] = 999999 unless defined $inv{$id}[3];
}
#
# "par" populates the following global variables:
#
# $parts{component-ref}[0] = namespace
# $parts{component-ref}[1] = item-number
# [0] and [1] may repeat
#
# $want{"namespace item"} = number of times we may use the part. If multiple
# parts are eligible for a component, each of them is counted as desirable
# for each component.
#
# $comps{"namespace item"}{component-ref} = 1
# Set of components a part may be used for.
#
sub par
{
my @f = split(/\s+/);
my $ref = shift @f;
$parts{$ref} = [ @f ];
while (@f) {
my @id = splice(@f, 0, 2);
my $id = "$id[0] $id[1]";
$want{$id}++;
$comps{$id}{$ref} = 1;
}
}
#
# "chr" populates the following global variable:
#
# $chr{"namespace item-number"}{parameter} = value
#
# $last is used internally for continuation lines.
#
sub chr
{
my @f;
if (/^\s+/) {
@f = split(/\s+/, $');
} else {
@f = split(/\s+/);
my $ref = shift @f;
my $num = shift @f;
$last = "$ref $num";
}
for (@f) {
die "\"=\" missing in $_" unless /=/;
$chr{$last}{uc($`)} = $';
}
}
#
# "sub" populates the following global variables:
#
# $end[rule-number] = 0 / 1
# $match[rule-number]{field}[0] = original-pattern
# $match[rule-number]{field}[1] = RE1
# $match[rule-number]{field}[2] = RE2
# $action[rule-number]{field} = value
#
# $match_stack[depth]{field}[0] = original-pattern
# $match_stack[depth]{field}[1] = RE1
# $match_stack[depth]{field}[2] = RE2
# $action_stack[depth]{field} = value
# $may_cont = 0 / 1
# $last
# $last_action
#
#
# $cvn_from{internal-handle} = index
# $cvn_to{internal-handle} = index
# $cvn_unit{internal-handle} = unit-name
# $cvn_num = internal-handle
# $found{field-or-subfield} = string
sub sub_pattern
{
local ($field, $p) = @_;
my $n = 0;
$p =~ s/\./\\./g;
$p =~ s/\+/\\+/g;
$p =~ s/\?/./g;
$p =~ s/\*/.*/g;
my $tmp = "";
while ($p =~ /^([^\(]*)\(/) {
$n++;
$tmp .= "$1(?'${field}__$n'";
$p = $';
}
$p = "^".$tmp.$p."\$";
my $q = $p;
while ($p =~ /^([^\$]*)\$(.)/) {
$p = "$1(\\d+$2\\d*|\\d+[GMkmunpf$2]\\d*)(?{ &__cvn($cvn_num); })$'";
$cvn_unit{$cvn_num} = $2;
die unless $q =~ /^([^\$]*)\$(.)/;
$q = "$1(\\d+(\.\\d+)?[GMkmunpf]?$2)$'";
$cvn_num++;
}
return ($p, $q);
}
sub sub_value
{
return $_[0];
}
sub sub
{
/^(\s*)/;
my $indent = $1;
my @f = split(/\s+/, $');
my $f;
my $in = 0; # indentation level
while (length $indent) {
my $c = substr($indent, 0, 1, "");
if ($c eq " ") {
$in++;
} elsif ($c eq "\t") {
$in = ($in+8) & ~7;
} else {
die;
}
}
if ($may_cont && $in > $last) {
pop(@match);
pop(@action);
pop(@end);
} else {
$match_stack[0] = undef;
$action_stack[0] = undef;
$last_action = 0;
$last = $in;
}
if (!$last_action) {
while (@f) {
$f = shift @f;
last if $f eq "->" || $f eq "{" || $f eq "}" || $f eq "!";
if ($f =~ /=/) {
$match_stack[0]{uc($`)} = [ $', &sub_pattern(uc($`), $') ];
} else {
$match_stack[0]{"REF"} = [ &sub_pattern("REF", $f) ];
}
}
$last_action = 1 if $f eq "->";
}
if ($last_action) {
while (@f) {
$f = shift @f;
last if $f eq "{" || $f eq "!";
die unless $f =~ /=/;
$action_stack[0]{uc($`)} = &sub_value($');
}
}
$may_cont = 0;
if ($f eq "{") {
unshift(@match_stack, undef);
unshift(@action_stack, undef);
die "items following {" if @f;
} elsif ($f eq "}") {
shift @match_stack;
shift @action_stack;
die "items following }" if @f;
} else {
die "items following !" if @f && $f eq "!";
push(@end, $f eq "!");
$may_cont = $f ne "!";
my $n = $#end;
push(@match, undef);
push(@action, undef);
for my $m (reverse @match_stack) {
for (keys %{ $m }) {
$match[$n]{$_} = $m->{$_};
}
}
for my $a (reverse @action_stack) {
for (keys %{ $a }) {
$action[$n]{$_} = $a->{$_};
}
}
}
}
#
# "ord" populates the following global variables:
#
# $order{"namespace item-number"}[0] = quantity to order
# $order{"namespace item-number"}[1] = currency
# $order{"namespace item-number"}[2] = total cost in above currency
# $order{"namespace item-number"}[3] = component reference
# ...
#
sub ord
{
my @f = split(/\s+/);
my @id = splice(@f, 0, 2);
@{ $order{"$id[0] $id[1]"} } = @f;
}
#
# "dsc" populates the following global variable:
#
# $dsc{"namespace item-number"} = description
#
sub dsc
{
my @f = split(/\s+/);
my @id = splice(@f, 0, 2);
$dsc{"$id[0] $id[1]"} = join(" ", @f);
}
#
# "eeschema" populates the following global variable:
#
# $eeschema[] = line
#
sub eeschema
{
push(@eeschema, $_[0]);
if ($_[0] =~ /^\$EndSCHEMATC/) {
$mode = *skip;
undef $raw;
}
}
sub babylonic
{
if ($_[0] =~ /^#/) {
$hash++;
if ($hash == 2) {
$mode = *skip;
undef $raw;
}
return;
}
&bom($_[0]) if $hash == 1;
}
sub dirname
{
local ($name) = @_;
return $name =~ m|/[^/]*$| ? $` : ".";
}
sub rel_path
{
local ($cwd, $path) = @_;
return $path =~ m|^/| ? $path : "$cwd/$path";
}
sub parse_one
{
local ($name) = @_;
my $file = new IO::File->new($name) || die "$name: $!";
my $dir = &dirname($name);
while (1) {
$_ = <$file>;
if (!defined $_) {
$file->close();
return unless @inc;
$file = pop @inc;
$dir = pop @dir;
next;
}
if (/^\s*include\s+(.*?)\s*$/) {
push(@inc, $file);
push(@dir, $dir);
$name = &rel_path($dir, $1);
$dir = &dirname($name);
$file = new IO::File->new($name) || die "$name: $!";
next;
}
chop;
# ----- KiCad BOM parsing. Alas, the BOM is localized, so there are almost no
# reliable clues for the parser. Below would be good clues for the English
# version:
if (0 && /^#Cmp.*order = Reference/) {
$mode = *bom;
next;
}
if (0 && /^#Cmp.*order = Value/) {
$mode = *skip;
next;
}
if (0 && /^eeschema \(/) { # hack to allow loading in any order
$mode = *skip;
next;
}
# ----- now an attempt at a "generic" version:
if (/^eeschema \(/) {
$mode = *babylonic;
$hash = 0;
$raw = 1;
next;
}
# -----
if (/^EESchema Schematic/) {
$mode = *eeschema;
$raw = 1;
die "only one schematic allowed" if defined @eeschema;
&eeschema($_);
next;
}
if (/^#EQU\b/) {
$mode = *equ;
next;
}
if (/^#INV\b/) {
$mode = *inv;
next;
}
if (/^#PAR\b/) {
$mode = *par;
next;
}
if (/^#CHR\b/) {
$mode = *chr;
undef $last;
next;
}
if (/^#(SUB|GEN)\b/) {
$mode = *sub;
undef $last;
undef $last_action;
undef $may_cont;
next;
}
if (/^#ORD\b/) {
$mode = *ord;
next;
}
if (/^#DSC\b/) {
$mode = *dsc;
next;
}
if (/^#END\b\(/) { # for commenting things out
$mode = *skip;
next;
}
if (!$raw) {
s/#.*//;
next if /^\s*$/;
}
&$mode($_);
}
}
sub parse
{
$mode = *skip;
for (@ARGV) {
&parse_one($_);
}
}
#
# in case user calls directly &parse_one and not &parse
#
$mode = *skip;
return 1;

133
old-boom/part2order Executable file
View File

@ -0,0 +1,133 @@
#!/usr/bin/perl
require "parser.pl";
require "misc.pl";
$mult = shift(@ARGV);
&parse;
sub number
{
local ($id) = @_;
my $s = $inv{$id}[0];
my $n = $want{$id}*$mult;
return $n < $s ? $n : $s;
}
#
# The heuristics here aren't very nice. We give zero-cost stock priority over
# any other stock, when we go by stock size up to the quantity we need. The
# idea is to exhause local stock (zero-cost) first, then try to obtain the
# parts with as few orders as possible.
#
# It would be better to have some sort of priority, so that we can express a
# preference among stock we already own. Also, if non-zero-cost stock has widly
# different prices, the smallest order cost may not be a good indicator of
# which source we prefer.
#
# Furthermore, the algorithm doesn't consider the number of sources we use in
# total or things like lead time, shipping cost, customs, etc.
#
sub rank
{
local ($a, $b) = @_;
my $na = &number($a); # min(number wanted, available)
my $nb = &number($b);
my $pa = $inv{$a}[3]; # per unit price for smallest quantum
my $pb = $inv{$b}[3];
#print STDERR "a=$a b=$b na=$na nb=$nb pa=$pa pb=$pb\n";
return 1 if $na && !$pa && $pb;
return -1 if $nb && $pa && !$pb;
return $na <=> $nb if $na != $nb;
return $pb <=> $pa;
}
for (keys %parts) {
$parts++;
}
print "#ORD\n";
for (sort { &rank($b, $a) } keys %want) {
my $n = &number($_);
$n -= $n % $mult;
next unless $n;
my @f = @{ $inv{$_} };
my $max = shift @f;
my $currency = shift @f;
my @qty;
my @price;
my %index;
my $best_qty;
my $best_price = undef;
while (@f) {
my $q = shift @f;
my $p = shift @f;
if (defined $index{$q}) {
$price[$index{$q}] = $p;
} else {
push(@qty, $q);
push(@price, $p);
$index{$q} = $#qty;
# @@@ this fails if smaller quantities following a large quantity
# differ from the quantities preceding them. E.g., 1 10 100 25
# wouldn't yield correct results.
}
for (my $i = $#qty; $i >= 0; $i--) {
my $order = 0;
my $price = 0;
my $left = $n;
for (my $j = $#qty; $j >= $i; $j--) {
while ($left >= ($j == $i ? 1 : $qty[$j])) {
$left -= $qty[$j];
$order += $qty[$j];
$price += $price[$j]*$qty[$j];
}
}
next if $order > $max;
if (!defined $best_price || $price < $best_price) {
$best_price = $price;
$best_qty = $order;
}
}
}
next if !defined $best_price;
print "$_ $best_qty $currency $best_price";
my $id = $_;
while (keys %{ $comps{$id} }) {
last if $best_qty < $mult;
$best_qty -= $mult;
my $ref = (sort { &cmp_cref($a, $b); } keys %{ $comps{$id} })[0];
#print STDERR "$id: $ref + ", join("|", keys %{ $comps{$id} }), "\n";
my @f = @{ $parts{$ref} };
while (@f) {
my @id2 = splice(@f, 0, 2);
my $id2 = "$id2[0] $id2[1]";
$want{$id2}--;
delete $comps{$id2}{$ref};
}
print " $ref";
}
print "\n";
}
for my $id (sort { $want{$b} <=> $want{$a} } keys %want) {
next unless $want{$id};
print STDERR "$id";
for (&eq($id)) {
# next unless $want{$_};
die "\n$_ ($want{$_}) vs. $id want ($want{$id})"
unless $want{$_} == $want{$id};
print STDERR " $_";
$want{$_} = 0;
}
print STDERR ": want $want{$id}\n";
$want{$id} = 0;
}

131
old-boom/prettyord Executable file
View File

@ -0,0 +1,131 @@
#!/usr/bin/perl
require "parser.pl";
require "misc.pl";
sub usage
{
print STDERR <<"END";
usage: $0 [-c] [-f|-t] [-r] [-s/from/to/ ...] ...
-c generate CSV output (default: generate formatted text)
-f generate SMT fab output (default: generate shopping list)
-r sort by component reference (default: sort by part number)
-s/from/to/ substitute description and treat result as reference
-t print the total number of items and the total cost.
-t cannot be combined with -c or -f.
END
exit(1);
}
$shop = 1;
$by_pn = 1;
while ($ARGV[0] =~ /^-./) {
if ($ARGV[0] =~ /^-s/) {
&usage unless &dsc_xlat_arg($');
} elsif ($ARGV[0] eq "-c") {
$csv = 1;
} elsif ($ARGV[0] eq "-f") {
$shop = 0;
} elsif ($ARGV[0] eq "-r") {
$by_pn = 0;
} elsif ($ARGV[0] eq "-t") {
$total = 1;
} else {
&usage;
}
shift @ARGV;
}
&usage if $total && !$shop;
&usage if $total && $csv;
&parse;
$out[0][0] = "Pos";
$out[1][0] = "Qty";
$out[2][0] = "P/N";
$out[3][0] = "Description";
if ($shop) {
$out[4][0] = "Value";
$out[5][0] = "";
} else {
$out[4][0] = "Ref";
}
for (sort { $by_pn ? $a cmp $b : &cmp_cref($order{$a}[3], $order{$b}[3]) }
keys %order) {
push(@{ $out[0] }, ++$n);
push(@{ $out[1] }, $shop ? $order{$_}[0] : @{ $order{$_} }-3);
@f = split(/\s+/, $_);
push(@{ $out[2] }, $shop ? $f[1] : "$f[0] $f[1]");
my $dsc = &dsc_find($_);
print STDERR "$_: no description\n" unless defined $dsc;
push(@{ $out[3] }, defined $dsc ? $dsc : "???");
if ($shop) {
push(@{ $out[4] }, $order{$_}[1]);
push(@{ $out[5] }, sprintf("%.2f", $order{$_}[2]));
$price{$order{$_}[1]} += $order{$_}[2];
} else {
my @r = @{ $order{$_} };
push(@{ $out[4] }, join(", ", @r[3..$#r]));
}
}
if ($csv) {
for ($i = 0; $i <= $#{ $out[0] }; $i++) {
for ($j = 0; $j <= $#out; $j++) {
print "," if $j;
if ($i && $j < 2) {
print $out[$j][$i];
} else {
my $s = $out[$j][$i];
$s =~ s/"/''/g;
print "\"$s\"";
}
}
print "\n";
}
exit(0);
}
for (@out) {
push(@max, 0);
if (length $_->[0]) {
$max[$last_pos] = $last_len if defined $last_pos;
$last_pos = $#max;
$last_len = length $_->[0];
}
}
$i = 0;
for (@out) {
$first = 1;
for (@{ $_ }) {
next if $first-- > 0;
$max[$i] = length $_ if length $_ > $max[$i];
}
$i++;
}
for ($i = 0; $i <= $#{ $out[0] }; $i++) {
$l = "";
for ($j = 0; $j != 6; $j++) {
my $s = $out[$j][$i];;
$l .= $s if $j == 2 || $j == 3 || $j == 4;
$l .= " " x ($max[$j]-length $s);
$l .= $s if $j == 0 || $j == 1 || $j == 5;
$l .= " " unless $j == 5;
}
$l =~ s/\s*$//;
print "$l\n";
}
if ($total) {
print "$n item".($n == 1 ? "" : "s");
for (sort keys %price) {
print ", $_ $price{$_}";
}
print "\n";
}

45
old-boom/testsub Executable file
View File

@ -0,0 +1,45 @@
#!/usr/bin/perl
require "parser.pl";
require "match.pl";
sub usage
{
print STDERR "usage: $0 [-d] file.sub|field ...\n\n";
print STDERR " fields: ref value [footprint user-field ...]\n";
}
for (@ARGV) {
if ($_ eq "-d") {
$debug = 1;
next;
}
&usage if /^-/;
if (/\.sub$/) {
&parse_one($_);
} else {
push(@f, $_);
}
}
&usage unless @f >= 2;
$field{"REF"} = shift @f;
$field{"VAL"} = shift @f;
$field{"FP"} = shift @f;
for (my $i = 1; $i != 10; $i++) {
$field{"F$i"} = $f[$i-1];
}
&apply_rules;
for (sort keys %field) {
if ($field{$_} =~ / /) {
print "$_ = \"$field{$_}\"\n";
} else {
print "$_ = $field{$_}\n";
}
}

147
old-boom/workflow.fig Normal file
View File

@ -0,0 +1,147 @@
#FIG 3.2 Produced by xfig version 3.2.5a
Landscape
Center
Metric
A4
100.00
Single
-2
1200 2
6 450 5850 3150 6525
4 0 0 50 -1 14 12 0.0000 4 180 2700 450 6030 Source file (in SVN)\001
4 0 0 50 -1 12 12 0.0000 4 150 1890 450 6255 Generated file\001
4 0 0 50 -1 18 12 0.0000 4 210 1830 450 6480 Program (in SVN)\001
-6
6 450 7875 6480 9000
4 0 0 50 -1 12 12 0.0000 4 105 540 450 8055 .csv\001
4 0 0 50 -1 12 12 0.0000 4 150 540 450 8280 .inv\001
4 0 0 50 -1 12 12 0.0000 4 150 540 450 8505 .equ\001
4 0 0 50 -1 12 12 0.0000 4 150 540 450 8730 .par\001
4 0 0 50 -1 12 12 0.0000 4 150 540 450 8955 .ord\001
4 0 0 50 -1 1 12 0.0000 4 195 2520 1350 8280 inventory with stock and cost\001
4 0 0 50 -1 1 12 0.0000 4 195 3045 1350 8055 GTA02 EE component stock at FIC\001
4 0 0 50 -1 1 12 0.0000 4 195 5130 1350 8505 part number equivalences, e.g., manufacturer vs. distributor\001
4 0 0 50 -1 1 12 0.0000 4 195 2910 1350 8730 component to part number(s) map\001
4 0 0 50 -1 1 12 0.0000 4 195 5025 1350 8955 list of parts to order, with price and component references\001
-6
6 450 7425 3465 7875
4 0 0 50 -1 12 12 0.0000 4 150 540 450 7605 .chr\001
4 0 0 50 -1 12 12 0.0000 4 150 540 450 7830 .sub\001
4 0 0 50 -1 1 12 0.0000 4 195 1695 1350 7605 part characteristics\001
4 0 0 50 -1 1 12 0.0000 4 195 2085 1350 7830 parameter substitutions\001
-6
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
1 1 2.00 60.00 60.00
7875 2700 7875 2925 6525 2925 6525 4725
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
1 1 2.00 60.00 60.00
9000 2700 9000 5850 6525 5850 6525 6075
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
1 1 2.00 60.00 60.00
3150 1350 3150 5850 6075 5850 6075 6075
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 2.00 60.00 60.00
6300 2700 6300 4725
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 2.00 60.00 60.00
6300 2025 6300 2475
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 2.00 60.00 60.00
6300 1350 6300 1800
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
1 1 2.00 60.00 60.00
4950 1350 4950 4275 6075 4275 6075 4725
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
1 1 2.00 60.00 60.00
3150 4500 5850 4500 5850 4725
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
1 1 2.00 60.00 60.00
8100 4275 8100 4500 6750 4500 6750 4725
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
11700 1575 11700 3150 10170 3150
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 2.00 60.00 60.00
6300 4950 6300 5400
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 2.00 60.00 60.00
6300 5625 6300 6075
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 2.00 60.00 60.00
6300 6300 6300 6750
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 2.00 60.00 60.00
6300 6975 6300 7425
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 2.00 60.00 60.00
6525 7650 6525 8100
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 2.00 60.00 60.00
9000 1350 9000 1800
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
1 1 2.00 60.00 60.00
9000 1575 7875 1575 7875 1800
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
1 1 2.00 60.00 60.00
9000 1575 10125 1575 10125 1800
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 2.00 60.00 60.00
7875 2025 7875 2475
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 2.00 60.00 60.00
9000 2025 9000 2475
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 2.00 60.00 60.00
7875 2925 7875 3375
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
1 1 2.00 60.00 60.00
8955 3150 8325 3150 8325 3375
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 2.00 60.00 60.00
8100 3600 8100 4050
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 2.00 60.00 60.00
10125 2025 10125 2475
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
1 1 2.00 60.00 60.00
10125 2700 10125 7200 6750 7200 6750 7425
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 2.00 60.00 60.00
8955 4500 8100 4500
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
9045 3150 10080 3150
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
11700 4050 11700 4500 10170 4500
2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
10080 4500 9045 4500
4 0 0 50 -1 2 16 0.0000 4 255 3345 450 675 BOM Processing - Workflow\001
4 1 0 50 -1 12 12 0.0000 4 195 1890 6300 2655 gta02-core.lst\001
4 1 0 50 -1 18 12 0.0000 4 165 1035 6300 1980 eeschema\001
4 1 0 50 -1 14 12 0.0000 4 150 675 6300 1305 *.sch\001
4 1 0 50 -1 18 12 0.0000 4 210 990 6300 4905 bom2part\001
4 1 0 50 -1 18 12 0.0000 4 210 1110 6300 6255 part2order\001
4 1 0 50 -1 12 12 0.0000 4 195 1890 6300 6930 gta02-core.ord\001
4 1 0 50 -1 12 12 0.0000 4 195 1890 6300 5580 gta02-core.par\001
4 1 0 50 -1 14 12 0.0000 4 195 1890 4950 1305 gta02-core.sub\001
4 1 0 50 -1 14 12 0.0000 4 195 1890 3105 1305 gta02-core.inv\001
4 1 0 50 -1 18 12 0.0000 4 210 975 6525 7605 prettyord\001
4 1 0 50 -1 18 12 0.0000 4 165 1095 7875 1980 fic2vendor\001
4 1 0 50 -1 18 12 0.0000 4 165 675 9000 1980 fic2inv\001
4 1 0 50 -1 18 12 0.0000 4 210 855 8100 3555 gen2chr\001
4 1 0 50 -1 12 12 0.0000 4 150 675 8100 4230 *.chr\001
4 1 0 50 -1 12 12 0.0000 4 195 945 7875 2655 fic.equ\001
4 1 0 50 -1 12 12 0.0000 4 150 945 9000 2655 fic.inv\001
4 1 0 50 -1 18 12 0.0000 4 165 735 10125 1980 fic2dsc\001
4 1 0 50 -1 14 12 0.0000 4 195 2700 9000 1305 inventory-fic-ee.csv\001
4 0 0 50 -1 12 12 0.0000 4 150 540 450 7155 .lst\001
4 0 0 50 -1 1 12 0.0000 4 195 2220 1350 7155 BOM generated by KiCad\001
4 0 0 50 -1 12 12 0.0000 4 150 540 450 6930 .sch\001
4 0 0 50 -1 1 12 0.0000 4 195 2010 1350 6930 schematics (for KiCad)\001
4 0 0 50 -1 12 12 0.0000 4 150 540 450 7380 .gen\001
4 0 0 50 -1 1 12 0.0000 4 195 2745 1350 7380 characteristics generation rules\001
4 0 0 50 -1 12 12 0.0000 4 150 540 450 9180 .dsc\001
4 0 0 50 -1 1 12 0.0000 4 195 2685 1350 9180 Textual component description\001
4 1 0 50 -1 14 12 0.0000 4 180 675 11700 1305 *.gen\001
4 1 0 50 -1 14 12 0.0000 4 150 675 11700 3780 *.chr\001
4 1 0 50 -1 14 9 0.0000 4 135 1440 11700 4005 (acx, misc, ...)\001
4 1 0 50 -1 14 9 0.0000 4 135 1800 11700 1530 (darfon, ralec, ...)\001
4 1 0 50 -1 12 12 0.0000 4 150 945 10125 2655 fic.dsc\001