Subversion Repositories OpenARM Single-board Computer

Rev

Rev 313 | Rev 319 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
280 agaran 1
#!/usr/bin/perl -w
2
use strict;
293 agaran 3
# $Id: inventory.pl 318 2008-12-29 10:58:58Z agaran $
280 agaran 4
# Thu, 13 Nov 2008 21:06:23 +0100
5
# Maciej 'agaran' Pijanka <agaran@pld-linux.org>
6
# for OpenARM SBC Project
7
# license: gpl v3
8
 
293 agaran 9
 
280 agaran 10
use Getopt::Long qw//;
293 agaran 11
use File::Basename qw/basename/;
280 agaran 12
 
313 agaran 13
my %Config;
280 agaran 14
 
313 agaran 15
# ==================================================
16
$Config{docdir} = '.';
17
$Config{bomdir} = '.';
18
$Config{verbose} = 1;
280 agaran 19
 
313 agaran 20
# 0 mean not show, -1 show all, positive value limits depth of shown
21
$Config{dbg_showdirs} = 0;
280 agaran 22
 
313 agaran 23
 
24
# modes
25
my $build_inventory = 0;
26
my $build_bom = 0;
27
my $show_conf = 0;
28
my $show_help = 0;
29
 
30
# ==================================================
31
 
32
my %Inv_By_PartNo;
33
my @Inv;
34
 
293 agaran 35
sub err_printf($@) {
36
        my ($format, @args) = @_;
280 agaran 37
 
313 agaran 38
        printf STDERR "-E- ".$format."\n", @args;
39
        # exit? or fail-exit here
280 agaran 40
}
41
 
293 agaran 42
sub wrn_printf($@) {
43
        my ($format, @args) = @_;
44
 
45
        printf STDERR "-W- ".$format."\n", @args;
46
}
47
 
48
sub inf_printf($@) {
49
        my ($format, @args) = @_;
50
 
51
        printf STDERR "-I- ".$format."\n", @args;
52
}
53
 
313 agaran 54
sub Config_Show {
55
        printf "Config for %s\n----------------------------------------\n", basename($0);
56
        foreach my $name (sort keys %Config) {
57
                printf "%-20s %s\n", $name, $Config{$name};
58
        }
59
}
60
 
61
sub Help_Show {
62
        printf "Help for %s\n----------------------------------------\n", basename($0);
63
        unless (defined($_[1]) && length($_[1]) != 0) {
64
                print "Basic help\n\t--showrc|showconf shows current configuration\n".
65
                        "\t--docdir|-d <dir> tells script where information.txt files should be searched\n".
66
                        "\t--define <something>=<somethingelseornot> defines some configration value\n".
67
                        "\t--verbose|-v [level] sets verbosity level\nOrder of options DOES matter\n";
68
                return;
69
        }
70
        if ($_[1] =~ /^foo$/) {
71
                print "Noo, there is no foo's here\n";
72
        } else {
73
                printf "Sorry, help for `%s' don't exist (eventually) yet\n", $_[1];
74
        }
75
        exit;
76
}
77
 
78
sub fix_dir ($) {
79
        my $dir = shift @_;
80
 
81
        $dir =~ s/\/$//;
82
 
83
        if (! -d $dir) {
84
                err_printf("Sorry `%s' is not valid directory, exiting", $dir);
85
        }
86
 
87
        return $dir;
88
}
89
 
90
sub shortdir ($) {
91
        my $path = shift @_;
92
 
93
        return substr($path, 2) if ($path =~ /^\.\//) ;
94
        return $path;
95
}
96
 
97
 
98
sub trim($) {
99
        my ($value) = @_;
100
 
101
        $value =~ s/^ +//;
102
        $value =~ s/ +$//;
103
        return $value;
104
}
105
 
106
sub etrim($) {
107
        my ($value) = @_;
108
 
109
        $value =~ s/^[  ]+//;
110
        $value =~ s/[   ]+$//;
111
        return $value;
112
}
113
 
114
# this subroutine is used as callback function
115
# executed by file_lookup
280 agaran 116
sub parse_ifile($) {
293 agaran 117
        my ($filepath) = @_;
280 agaran 118
 
318 agaran 119
 
293 agaran 120
        open(IN, $filepath) or return 1;
280 agaran 121
 
293 agaran 122
        my %data;
280 agaran 123
 
293 agaran 124
        while (not eof IN) {
125
                my $line = <IN>;
280 agaran 126
 
293 agaran 127
                chomp $line;
280 agaran 128
 
293 agaran 129
                next if ($line =~ /^[   ]*$/);
130
                next if ($line =~ /^;/);
131
 
132
                last if ($line =~ /^--/);
133
 
134
                if ($line =~ /^([A-Za-z ]+):(.*)$/) {
313 agaran 135
                        my ($name,$value) = (lc etrim($1),etrim($2));
293 agaran 136
 
137
                        if ($name =~ /^price$/) {
138
                                $value =~ s/[^0-9\.\,]//g;
139
                                if ($value =~ s/^([0-9]+)[,.]([0-9]+)$/$1.$2/) {
140
                                        # printf STDERR "Price %.3f\n", $value;
141
                                        if (!defined($data{price})) {
142
                                                $data{price} = $value;
143
                                        } else {
313 agaran 144
                                                wrn_printf("Duplicated price field in file %s", shortdir($filepath));
293 agaran 145
                                        }
146
                                } else {
313 agaran 147
                                        err_printf("Bad price field in file %s", shortdir($filepath));
293 agaran 148
                                }
149
                        } elsif ($name =~ /^manufacturer$/i) {
150
                                # printf STDERR "Manufacturer %s\n", $value;
151
                                if (!defined($data{manufacturer})) {
152
                                        $data{manufacturer} = $value;
153
                                } else {
313 agaran 154
                                        wrn_printf("Duplicated manufacturer field in file %s", shortdir($filepath));
293 agaran 155
                                }
156
                        } elsif ($name =~ /^manufacturer part no$/ ) {
157
                                # printf STDERR "ManPartNo %s\n", $value;
158
                                if (!defined($data{manufact_partno})) {
313 agaran 159
                                        $data{manufact_partno} = trim($value);
293 agaran 160
                                } else {
161
                                        wrn_printf("Duplicated manufacturer part no field in file %s",
313 agaran 162
                                                shortdir($filepath));
293 agaran 163
                                }
164
                        } elsif ($name =~ /^description$/i) {
313 agaran 165
                                if (!defined($data{desc})) {
166
                                        $data{desc} = $value;
167
                                } else {
168
                                        wrn_printf("Duplicated description no field in file %s",
169
                                                shortdir($filepath));
170
                                }
293 agaran 171
                        } elsif ($name =~ /^datasheet$/i) {
172
                                $data{datasheet} = [] unless defined $data{datasheet};
173
                                push @{$data{datasheet}}, $value;
174
                                # printf STDERR "Datasheet %s\n", $value;
175
                        } elsif ($name =~ /^supplier$/i) {
176
                                # printf STDERR "Supplier %s\n", $value;
177
                                if (!defined($data{supplier})) {
178
                                        $data{supplier} = $value;
179
                                } else {
180
                                        wrn_printf("Duplicated supplier field in file %s",
313 agaran 181
                                                shortdir($filepath));
293 agaran 182
                                }
183
                        } elsif ($name =~ /^order code$/) {
184
                                # printf STDERR "Order Code %s\n", $value;
185
                                if (!defined($data{ordercode})) {
186
                                        $data{ordercode} = $value;
187
                                } else {
188
                                        wrn_printf("Duplicated order code field in file %s",
313 agaran 189
                                                shortdir($filepath));
293 agaran 190
                                }
191
                                #push @DATA, { $name => $value };
192
                        } elsif ($name =~ /^url .*$/) {
193
                                # printf STDERR "URL %s\n", $value;
194
                        } elsif ($name =~ /^catalog(ue|) page$/) {
195
                                # printf STDERR "Catalogue Page %s\n", $value;
196
                        } else {
197
                                err_printf("Unhandled field %s in file %s", $name,
313 agaran 198
                                        shortdir($filepath));
293 agaran 199
                        }
200
 
201
                } else {
202
                        wrn_printf("Unparseable line `%s', forgot ; to set it as comment in file %s\n", $line,
313 agaran 203
                                shortdir($filepath));
293 agaran 204
                }
205
        }
206
        close(IN);
207
 
208
        if (scalar keys %data == 0) {
209
                inf_printf("Skipping file %s because contain no data for me",
313 agaran 210
                        shortdir($filepath));
293 agaran 211
                return;
212
        }
213
 
214
        unless (defined ($data{price}) && $data{price} != 0) {
215
                wrn_printf("Missing Price in file %s",
313 agaran 216
                        shortdir($filepath));
293 agaran 217
        }
218
 
219
        unless (defined($data{manufact_partno})) {
220
                inf_printf("Missing Manufacturer Part No in file %s",
313 agaran 221
                        shortdir($filepath));
293 agaran 222
        }
223
 
224
        unless (defined($data{ordercode})) {
225
                wrn_printf("Missing Order Code in file %s",
313 agaran 226
                        shortdir($filepath));
293 agaran 227
        }
228
 
313 agaran 229
 
293 agaran 230
        use Data::Dumper qw/Dumper/;
313 agaran 231
 
232
        my $id = scalar @Inv;
233
 
234
        $Inv[$id] = {};
235
 
236
        $Inv[$id]{Datasheet} = delete $data{'datasheet'} if defined $data{'datasheet'};
237
        $Inv[$id]{Manufacturer} = delete $data{'manufacturer'} if defined $data{'manufacturer'};
238
        $Inv[$id]{Description} = delete $data{'desc'} if defined $data{'desc'};
239
        $Inv[$id]{Price} = delete $data{'price'} if defined $data{'price'};
240
        $Inv[$id]{Ordercode} = delete $data{'ordercode'} if defined $data{'ordercode'};
241
        $Inv[$id]{Manufacturer_Partno} = delete $data{'manufact_partno'} if defined $data{'manufact_partno'};
242
        $Inv[$id]{Supplier} = delete $data{'supplier'} if defined $data{'supplier'};
243
 
244
        unless (defined ($Inv_By_PartNo{$Inv[$id]{Manufacturer_Partno}})) {
245
                $Inv_By_PartNo{$Inv[$id]{Manufacturer_Partno}} = $id;
246
        } else {
247
                wrn_printf("PartNumber %s happened more than once, using first occurence (id:%d)",
248
                        $Inv[$id]{Manufacturer_Partno}, $id);
249
        }
250
        #inf_printf("Part %s defined in file %s", $Inv[$id]{Manufacturer_Partno}, shortdir($filepath));
293 agaran 251
 
313 agaran 252
        wrn_printf("Unhandled data from parsing: %s", Dumper(\%data)) if (scalar keys %data > 0);
280 agaran 253
}
254
 
255
 
313 agaran 256
my %BomData;
257
sub parse_bom ($) {
258
        my ($filepath) = @_;
280 agaran 259
 
313 agaran 260
        open(IN, $filepath) or return 1;
261
 
262
        #wrn_printf("GotARg: %s", shortdir($filepath));
263
 
264
        my @Fields;
265
 
266
        my %data;
267
        my $v = '';
268
        while (not eof IN) {
269
                my $line = <IN>;
270
 
271
                chomp $line;
272
 
273
                if ($line =~ /^\.START$/) {
274
                        $v = 'boms';
275
                        next;
276
                }
277
 
278
                if ($line =~ /^\.END$/) {
279
                        $v = '';
280
                        next;
281
                }
282
 
283
                if ($v eq 'boms') {
284
                        @Fields = split(/\t/, substr($line,2));
285
                        $v = 'bom';
286
                        # some funny way to generate field-map
287
                        # that if someone reorder bom file columns we are still on place
288
                        next;
289
                }
290
 
291
                if ($v eq 'bom') {
292
                        my ($refdes, $device, $value, $footprint, $quantity) = split (/\t/, $line);
293
 
294
                        $device = trim($device);
295
 
296
#                       wrn_printf("Device: %s (value: %s) at refdes %s, %d pcs", $device, $value, $refdes, $quantity);
297
 
298
                        if (!defined $Inv_By_PartNo{$device}) {
299
                                wrn_printf("Device %s not found in inventory in file %s", $device, shortdir($filepath));
300
                                next;
301
                        }
302
                        my $id = $Inv_By_PartNo{$device};
303
                        next if ($Inv[$id]{Manufacturer} =~ /none/i); # skip parts whose manufacturer is none
304
                        #inf_printf("Found in Inventory at %d %s", $id, $Inv[$id]{Manufacturer_Partno});
305
                        push @{$data{$id}{RefDes}}, $refdes;
306
                }
307
        }
308
        close(IN);
309
 
310
        if (scalar keys %data == 0) {
311
                inf_printf("Skipping file %s because contain no data for me",
312
                        shortdir($filepath));
313
                return;
314
        }
315
 
316
        foreach my $id (keys %data) {
317
                my %tmp;
318
                map { $tmp{$_} = 1 } @{$data{$id}{RefDes}};
319
                @{$data{$id}{RefDes}} = keys %tmp;
320
                my $cnt = scalar @{$data{$id}{RefDes}};
321
                if (!defined $Inv[$id]{Price}) {
322
                        wrn_printf("%s has no price, setting to 0.0", $Inv[$id]{Manufacturer_Partno});
323
                        $Inv[$id]{Price} = 0;
324
                }
318 agaran 325
                push @{$BomData{$id}{RefDes}}, @{$data{$id}{RefDes}};
326
 
313 agaran 327
#               printf "%-20s %.4f %s\n", $Inv[$id]{Manufacturer_Partno}, $cnt, $icost, join (', ', @{$data{$id}{RefDes}});
328
#               %BomData{ById}{$id}{RefDes}
329
        }
330
}
331
 
332
sub file_lookup ($$$$) ;
333
sub file_lookup ($$$$) {
334
        my ($dir, $depth, $regexp, $callback) = @_;
335
 
336
        err_printf("Sorry, callback must be CODE ref") unless (ref $callback eq 'CODE');
337
 
293 agaran 338
        if ( -d $dir) {
339
                opendir(DIR, $dir) or return 1;
340
                foreach my $e (readdir(DIR)) {
341
                        my $fe = $dir .'/'. $e;
342
                        if ( -f $fe) {
313 agaran 343
                                if ($fe =~ $regexp) {
344
                                        &$callback($fe);
293 agaran 345
                                }
346
                        } elsif (-d $fe) { # now its dir...
347
                                if ($e eq '.svn') { # if entry name is equal to svn
348
                                        next; # go to next entry in foreach loop
349
                                }
350
                                next if ($e eq '.' or $e eq '..'); # skip to next if dir entry is . or ..
281 jelle 351
 
313 agaran 352
                                if ($Config{dbg_showdirs} == -1 or $Config{dbg_showdirs} > $depth) {
353
                                        printf STDERR "Entering directory %s\n", shortdir($fe);
293 agaran 354
                                }
313 agaran 355
                                return 1 if (file_lookup($fe, $depth+1, $regexp, $callback) == 1);
293 agaran 356
                        } else {
313 agaran 357
                                # symlink or other mysterius beast
293 agaran 358
                        }
359
                }
360
                closedir(DIR);
361
        }
313 agaran 362
        return 0;
280 agaran 363
}
364
 
293 agaran 365
Getopt::Long::Configure("bundling");
280 agaran 366
 
313 agaran 367
my $result = Getopt::Long::GetOptions (
368
        "showrc|showconf" => sub { $show_conf = 1 },
369
        "docdir|d=s" => sub { $Config{docdir} = $_[1]; },
370
        # not sure if bomdir or SCH dir
371
        "bomdir|b=s" => sub { $Config{$_[0]} = $_[1]; },  # $_[0] contain basename of option, so in few cases could be (ab)used
372
        "define|D=s" => sub { my ($p,$q) = split(/=/,$_[1],2); $Config{$p} = $q; },
373
        "verbose|v:+" => sub { $Config{$_[0]} = ($_[1]>1?0:$Config{$_[0]}) + $_[1]; },
374
        "help|h|?:s" => sub { $show_help = 1 },
375
 
376
        # options 
293 agaran 377
);
378
if (!$result) {
379
        printf "Usage: %s [-d directory] [-v]\n",basename($0);
380
        exit;
381
}
313 agaran 382
 
383
# ==================================================
384
# processing of options/config values, checking ranges etc
385
# 
386
 
387
$Config{docdir} = fix_dir ($Config{docdir});
388
$Config{bomdir} = fix_dir ($Config{bomdir});
389
 
390
if ( $show_help == 1) {
391
        Help_Show();
392
        exit;
293 agaran 393
}
394
 
313 agaran 395
if ( $show_conf == 1) {
396
        Config_Show();
397
        exit;
398
}
293 agaran 399
 
313 agaran 400
# make Inventory
401
file_lookup($Config{docdir}, 0, qr/\/information.txt$/, \&parse_ifile);
402
 
403
# process BOM files
404
file_lookup($Config{bomdir}, 0, qr/\.bom$/, \&parse_bom);
405
 
318 agaran 406
my $n = 0;
407
my $cost = 0.0;
408
 
409
sub shortstring($$) {
410
        my ($str,$lim) = @_;
411
        return substr($str,0,$lim-4).'(..)' if (length ($str) > $lim);
412
        return $str;
413
}
414
 
415
 
416
 
417
open (BOMOUT, '>output.bom') || die 'cant open output.bom: $!';
418
open (PARTMAP, '>output.map') || die 'cant open output.map: $!';
419
 
420
printf BOMOUT "|    |  %-33s|  %-20s|  %-18s|  %-11s|%-5s|%-6s|%-6s|\n|----+-----------------------------------+".
421
        "----------------------+--------------------+-------------+------+------+------|\n",
422
        'description','manufact. partno','manufacturer','order code','quant.','price','cost';
423
printf PARTMAP "%-35s| refdes\n\n", 'part';
424
 
425
 
426
foreach my $id (keys %BomData) {
427
        my %tmp;
428
        map { $tmp{$_} = 1 } @{$BomData{$id}{RefDes}};
429
        @{$BomData{$id}{RefDes}} = keys %tmp;
430
        my $cnt = scalar @{$BomData{$id}{RefDes}};
431
        if (!defined $Inv[$id]{Price}) {
432
                wrn_printf("%s has no price, setting to 0.0", $Inv[$id]{Manufacturer_Partno});
433
                $Inv[$id]{Price} = 0;
434
        }
435
        my $icost = $cnt * $Inv[$id]{Price};
436
 
437
        printf BOMOUT "|%4d|%-35s|%-22s|%-20s|%-13s|%6d|%6.3f|%6.3f|\n", $n, shortstring($Inv[$id]{Description},35),
438
                shortstring($Inv[$id]{Manufacturer_Partno},22), shortstring($Inv[$id]{Manufacturer},20),
439
                shortstring($Inv[$id]{Ordercode}, 13), $cnt, $Inv[$id]{Price}, $icost;
440
 
441
        printf PARTMAP "%-35s|%s\n", shortstring($Inv[$id]{Description},35), join (', ', @{$BomData{$id}{RefDes}});
442
        $cost += $icost;
443
#       %BomData{ById}{$id}{RefDes}
444
        $n ++;
445
}
446
printf BOMOUT "|%s|\n| %-118s|\n|%s|\n", ("-" x 119), sprintf ("Total cost: %.3f", $cost), ("-" x 119);
447
 
448
close BOMOUT;
449
close PARTMAP;