blob: 0ea238e0eb70fcdd78277d8d93571c394d2f9cbf [file] [log] [blame]
Andrew Geissler65ca8e12022-07-16 11:36:14 -04001#!/usr/bin/perl
Matt Spinler1cb9ecc2020-06-02 10:48:31 -05002
3# This script is used for generating callout lists from the MRW for devices
4# that can be accessed from the BMC. The callouts have a location code, the
5# target name (for debug), a priority, and in some cases a MRU. The script
6# supports I2C, FSI, SPI, FSI-I2C, and FSI-SPI devices. The output is a JSON
7# file organized into sections for each callout type, with keys based on the
8# type. I2c uses a bus and address, FSI uses a link, and SPI uses a bus
9# number. If FSI is combined with I2C or SPI, then the link plus the I2C/SPI
10# keys is used. Multi-hop FSI links are indicated by a dash in between the
11# links, eg "0-1".
12#
13# An example section is:
14# "FSI":
15# {
16# "5":
17# {
18# "Callouts":[
19# {
20# "Priority":"H"
21# "LocationCode": "P1-C50",
22# "MRU":"/sys-0/node-0/motherboard/proc_socket-0/module-0/power9-0",
23# "Name":"/sys-0/node-0/motherboard/cpu0"
24# },
25# {
26# "Priority":"H",
27# "LocationCode": "P1-C42",
28# "MRU":"/sys-0/node-0/motherboard/ebmc-card/BMC-0",
29# "Name":"/sys-0/node-0/motherboard/ebmc-card"
30# },
31# {
32# "Priority":"L",
33# "LocationCode": "P1",
34# "Name":"/sys-0/node-0/motherboard"
35# }
36# ],
37# "Dest":"/sys-0/node-0/motherboard-0/proc_socket-0/module-0/power9-0",
38# "Source":"/sys-0/node-0/motherboard-0/ebmc-card-connector-0/card-0/bmc-0"
39# }
40# }
41# The Name, Dest and Source entries are MRW targets and are just for debug.
42#
43# The optional --segments argument will output a JSON file of all the bus
44# segments in the system, which is what the callouts are made from.
45
46use strict;
47use warnings;
48
Matt Spinler00ef7662020-06-02 10:58:24 -050049# Callout object
50# Base class for other callouts.
51# There is an object per device, so it can contain multiple
52# FRU callouts in the calloutList attribute.
53package Callout;
54sub new
55{
56 my $class = shift;
57 my $self = {
58 type => shift,
59 sourceChip => shift,
60 destChip => shift,
61 calloutList => shift,
62 };
63
64 return bless $self, $class;
65}
66
67sub sourceChip
68{
69 my $self = shift;
70 return $self->{sourceChip};
71}
72
73sub destChip
74{
75 my $self = shift;
76 return $self->{destChip};
77}
78
79sub type
80{
81 my $self = shift;
82 return $self->{type};
83}
84
85sub calloutList
86{
87 my $self = shift;
88 return $self->{calloutList};
89}
90
91# I2CCallout object for I2C callouts
92package I2CCallout;
93our @ISA = qw(Callout);
94sub new
95{
96 my ($class) = @_;
97 # source, dest, calloutList
98 my $self = $class->SUPER::new("I2C", $_[1], $_[2], $_[3]);
99 $self->{i2cBus} = $_[4];
100 $self->{i2cAddr} = $_[5];
101 return bless $self, $class;
102}
103
104sub i2cBus
105{
106 my $self = shift;
107 return $self->{i2cBus};
108}
109
110sub i2cAddress
111{
112 my $self = shift;
113 return $self->{i2cAddr};
114}
115
116# FSICallout object for FSI callouts
117package FSICallout;
118our @ISA = qw(Callout);
119sub new
120{
121 my ($class) = @_;
122 my $self = $class->SUPER::new("FSI", $_[1], $_[2], $_[3]);
123 $self->{FSILink} = $_[4];
124 bless $self, $class;
125 return $self;
126}
127
128sub fsiLink
129{
130 my $self = shift;
131 return $self->{FSILink};
132}
133
134# SPICallout object for SPI callouts
135package SPICallout;
136our @ISA = qw(Callout);
137sub new
138{
139 my ($class) = @_;
140 my $self = $class->SUPER::new("SPI", $_[1], $_[2], $_[3]);
141 $self->{SPIBus} = $_[4];
142 bless $self, $class;
143 return $self;
144}
145
146sub spiBus
147{
148 my $self = shift;
149 return $self->{SPIBus};
150}
151
Matt Spinlerbbaf7382020-06-02 11:16:28 -0500152# FSII2CCallout object for FSI-I2C callouts
153# Ideally this would be derived from FSICallout, but I can't
154# get it to work.
155package FSII2CCallout;
156
157our @ISA = qw(Callout);
158sub new
159{
160 my ($class) = @_;
161 # source, dest, calloutList
162 my $self = $class->SUPER::new("FSI-I2C", $_[1], $_[2], $_[3]);
163 $self->{FSILink} = $_[4];
164 $self->{i2cBus} = $_[5];
165 $self->{i2cAddr} = $_[6];
166 return bless $self, $class;
167}
168
169sub fsiLink
170{
171 my $self = shift;
172 return $self->{FSILink};
173}
174
175sub i2cBus
176{
177 my $self = shift;
178 return $self->{i2cBus};
179}
180
181sub i2cAddress
182{
183 my $self = shift;
184 return $self->{i2cAddr};
185}
186
187#FSISPICallout object for FSI-SPI callouts
188package FSISPICallout;
189
190our @ISA = qw(Callout);
191sub new
192{
193 my ($class) = @_;
194 # source, dest, calloutList
195 my $self = $class->SUPER::new("FSI-SPI", $_[1], $_[2], $_[3]);
196 $self->{FSILink} = $_[4];
197 $self->{SPIBus} = $_[5];
198 return bless $self, $class;
199}
200
201sub fsiLink
202{
203 my $self = shift;
204 return $self->{FSILink};
205}
206
207sub spiBus
208{
209 my $self = shift;
210 return $self->{SPIBus};
211}
212
Matt Spinler00ef7662020-06-02 10:58:24 -0500213package main;
214
Matt Spinler1cb9ecc2020-06-02 10:48:31 -0500215use mrw::Targets;
216use mrw::Util;
217use Getopt::Long;
218use File::Basename;
219use JSON;
220
221my $mrwFile = "";
222my $outFile = "";
223my $printSegments = 0;
224
225# Not supporting priorites A, B, or C until necessary
226my %priorities = (H => 3, M => 2, L => 1);
227
228# Segment bus types
229my %busTypes = ( I2C => 1, FSIM => 1, FSICM => 1, SPI => 1 );
230
231GetOptions(
232 "m=s" => \$mrwFile,
233 "o=s" => \$outFile,
234 "segments" => \$printSegments
235)
236 or printUsage();
237
238if (($mrwFile eq "") or ($outFile eq ""))
239{
240 printUsage();
241}
242
243# Load system MRW
244my $targets = Targets->new;
245$targets->loadXML($mrwFile);
246
247# Find all single segment buses that we care about
248my %allSegments = getPathSegments();
249
Matt Spinler00ef7662020-06-02 10:58:24 -0500250my @callouts;
251
252# Build the single and multi segment callouts
253buildCallouts(\%allSegments, \@callouts);
254
Matt Spinler44aecce2020-06-02 12:40:49 -0500255printJSON(\@callouts);
Matt Spinler00ef7662020-06-02 10:58:24 -0500256
Matt Spinler1cb9ecc2020-06-02 10:48:31 -0500257# Write the segments to a JSON file
258if ($printSegments)
259{
260 my $outDir = dirname($outFile);
261 my $segmentsFile = "$outDir/segments.json";
262
263 open(my $fh, '>', $segmentsFile) or
264 die "Could not open file '$segmentsFile' $!";
265
266 my $json = JSON->new;
267 $json->indent(1);
268 $json->canonical(1);
269 my $text = $json->encode(\%allSegments);
270 print $fh $text;
271 close $fh;
272}
273
274# Returns a hash of all the FSI, I2C, and SPI segments in the MRW
275sub getPathSegments
276{
277 my %segments;
278 foreach my $target (sort keys %{$targets->getAllTargets()})
279 {
280 my $numConnections = $targets->getNumConnections($target);
281
282 if ($numConnections == 0)
283 {
284 next;
285 }
286
287 for (my $connIndex=0;$connIndex<$numConnections;$connIndex++)
288 {
289 my $connBusObj = $targets->getConnectionBus($target, $connIndex);
290 my $busType = $connBusObj->{bus_type};
291
292 # We only care about certain bus types
293 if (not exists $busTypes{$busType})
294 {
295 next;
296 }
297
298 my $dest = $targets->getConnectionDestination($target, $connIndex);
299
300 my %segment;
301 $segment{BusType} = $busType;
302 $segment{SourceUnit} = $target;
303 $segment{SourceChip} = getParentByClass($target, "CHIP");
304 if ($segment{SourceChip} eq "")
305 {
306 die "Warning: Could not get parent chip for source $target\n";
307 }
308
309 $segment{DestUnit} = $dest;
310 $segment{DestChip} = getParentByClass($dest, "CHIP");
311
312 # If the unit's direct parent is a connector that's OK too.
313 if ($segment{DestChip} eq "")
314 {
315 my $parent = $targets->getTargetParent($dest);
316 if ($targets->getAttribute($parent, "CLASS") eq "CONNECTOR")
317 {
318 $segment{DestChip} = $parent;
319 }
320 }
321
322 if ($segment{DestChip} eq "")
323 {
324 die "Warning: Could not get parent chip for dest $dest\n";
325 }
326
327 my $fruPath = $targets->getBusAttribute(
328 $target, $connIndex, "FRU_PATH");
329
330 if (defined $fruPath)
331 {
332 $segment{FRUPath} = $fruPath;
333 my @callouts = getFRUPathCallouts($fruPath);
334 $segment{Callouts} = \@callouts;
335 }
336 else
337 {
338 $segment{FRUPath} = "";
339 my @empty;
340 $segment{Callouts} = \@empty;
341 }
342
343 if ($busType eq "I2C")
344 {
Matt Spinler7bedb122022-05-27 14:15:35 -0500345 # If the bus goes through a mux, then the I2C_BUS_ALIAS field
346 # will be filled in with the bus alias number. For example
347 # 28 could be an alias for bus 5 mux channel 2. Old parts may
348 # not have this attribute. Note this is the actual alias value
349 # so doesn't need a 1 subtracted later, so account for that now.
350 #
351 if (isValidBusAttribute($target, $connIndex, "I2C_BUS_ALIAS"))
352 {
353 $segment{I2CBus} = $targets->getBusAttribute(
354 $target, $connIndex, "I2C_BUS_ALIAS");
355
356 if ($segment{I2CBus} ne "")
357 {
358 $segment{I2CBus} = $segment{I2CBus} + 1;
359 }
360 }
361
362 if ($segment{I2CBus} eq "")
363 {
364 $segment{I2CBus} = $targets->getAttribute($target, "I2C_PORT");
365 }
366
Matt Spinler1cb9ecc2020-06-02 10:48:31 -0500367 $segment{I2CAddress} =
368 hex($targets->getAttribute($dest, "I2C_ADDRESS"));
369
Matt Spinler1cb9ecc2020-06-02 10:48:31 -0500370 # Convert to the 7 bit address that linux uses
371 $segment{I2CAddress} =
372 Util::adjustI2CAddress($segment{I2CAddress});
373 }
374 elsif ($busType eq "FSIM")
375 {
376 $segment{FSILink} =
377 hex($targets->getAttribute($target, "FSI_LINK"));
378 }
379 elsif ($busType eq "SPI")
380 {
381 $segment{SPIBus} = $targets->getAttribute($target, "SPI_PORT");
382
383 # Seems to be in HEX sometimes
384 if ($segment{SPIBus} =~ /^0x/i)
385 {
386 $segment{SPIBus} = hex($segment{SPIBus});
387 }
388 }
389
390 push @{$segments{$busType}}, { %segment };
391 }
392 }
393
394 return %segments;
395}
396
397#Breaks the FRU_PATH atttribute up into its component callouts.
398#It looks like: "H:<some target>,L:<some other target>(<MRU>)"
399#Where H/L are the priorities and can be H/M/L.
400#The MRU that is in parentheses is optional and is a chip name on that
401#FRU target.
402sub getFRUPathCallouts
403{
404 my @callouts;
405 my $fruPath = shift;
406
407 my @entries = split(',', $fruPath);
408
409 for my $entry (@entries)
410 {
411 my %callout;
412 my ($priority, $path) = split(':', $entry);
413
414 # pull the MRU out of the parentheses at the end and then
415 # remove the parentheses.
416 if ($path =~ /\(.+\)$/)
417 {
418 ($callout{MRU}) = $path =~ /\((.+)\)/;
419
420 $path =~ s/\(.+\)$//;
421 }
422
423 # check if the target we read out is valid by
424 # checking for a required attribute
425 if ($targets->isBadAttribute($path, "CLASS"))
426 {
427 die "FRU Path $path not a valid target\n";
428 }
429
430 $callout{Priority} = $priority;
431 if (not exists $priorities{$priority})
432 {
433 die "Invalid priority: '$priority' on callout $path\n";
434 }
435
436 $callout{Name} = $path;
437
438 push @callouts, \%callout;
439 }
440
441 return @callouts;
442}
443
444# Returns an ancestor target based on its class
445sub getParentByClass
446{
447 my ($target, $class) = @_;
448 my $parent = $targets->getTargetParent($target);
449
450 while (defined $parent)
451 {
452 if (!$targets->isBadAttribute($parent, "CLASS"))
453 {
454 if ($class eq $targets->getAttribute($parent, "CLASS"))
455 {
456 return $parent;
457 }
458 }
459 $parent = $targets->getTargetParent($parent);
460 }
461
462 return "";
463}
464
Matt Spinler00ef7662020-06-02 10:58:24 -0500465# Build the callout objects
466sub buildCallouts
467{
468 my ($segments, $callouts) = @_;
469
470 # Callouts for 1 segment connections directly off of the BMC.
471 buildBMCSingleSegmentCallouts($segments, $callouts);
Matt Spinlerbbaf7382020-06-02 11:16:28 -0500472
473 # Callouts more than 1 segment away
474 buildMultiSegmentCallouts($segments, $callouts);
Matt Spinler00ef7662020-06-02 10:58:24 -0500475}
476
477# Build the callout objects for devices 1 segment away.
478sub buildBMCSingleSegmentCallouts
479{
480 my ($segments, $callouts) = @_;
481
482 for my $busType (keys %$segments)
483 {
484 for my $segment (@{$$segments{$busType}})
485 {
486 my $chipType = $targets->getType($segment->{SourceChip});
487 if ($chipType eq "BMC")
488 {
489 my $callout = buildSingleSegmentCallout($segment);
490
491 if (defined $callout)
492 {
493 push @{$callouts}, $callout;
494 }
495 }
496 }
497 }
498}
499
500# Build the callout object based on the callout type using the
501# callout list from the single segment.
502sub buildSingleSegmentCallout
503{
504 my ($segment, $callouts) = @_;
505
506 if ($segment->{BusType} eq "I2C")
507 {
508 return createI2CCallout($segment, $callouts);
509 }
510 elsif ($segment->{BusType} eq "FSIM")
511 {
512 return createFSICallout($segment, $callouts);
513 }
514 elsif ($segment->{BusType} eq "SPI")
515 {
516 return createSPICallout($segment, $callouts);
517 }
518
519 return undef;
520}
521
Matt Spinlerbbaf7382020-06-02 11:16:28 -0500522# Build the callout objects for devices more than 1 segment away from
523# the BMC. All but the last segment will always be FSI. The FRU
524# callouts accumulate as segments are added.
525sub buildMultiSegmentCallouts
526{
527 my ($segments, $callouts) = @_;
528 my $hops = 0;
529 my $found = 1;
530
531 # Connect FSI link callouts to other FSI segments to make new callouts, and
532 # connect all FSI link callouts up with the I2C/SPI segments to make even
533 # more new callouts. Note: Deal with I2C muxes, if they are ever modeled,
534 # when there are some.
535
536 # Each time through the loop, go out another FSI hop.
537 # Stop when no more new hops are found.
538 while ($found)
539 {
540 my @newCallouts;
541 $found = 0;
542
543 for my $callout (@$callouts)
544 {
545 if ($callout->type() ne "FSI")
546 {
547 next;
548 }
549
550 # link numbers are separated by '-'s in the link field,
551 # so 0-5 = 1 hop
552 my @numHops = $callout->fsiLink() =~ /(-)/g;
553
554 # only deal with callout objects that contain $hops hops.
555 if ($hops != scalar @numHops)
556 {
557 next;
558 }
559
560 for my $segmentType (keys %$segments)
561 {
562 for my $segment (@{$$segments{$segmentType}})
563 {
564 # If the destination on this callout is the same
565 # as the source of the segment, then make a new
566 # callout that spans both.
567 if ($callout->destChip() eq $segment->{SourceChip})
568 {
569 # First build the new single segment callout
570 my $segmentCallout =
571 buildSingleSegmentCallout($segment);
572
573 # Now merge both callouts into one.
574 if (defined $segmentCallout)
575 {
576 my $newCallout =
577 mergeCallouts($callout, $segmentCallout);
578
579 push @newCallouts, $newCallout;
580 $found = 1;
581 }
582 }
583 }
584 }
585 }
586
587 if ($found)
588 {
589 push @{$callouts}, @newCallouts;
590 }
591
592 $hops = $hops + 1;
593 }
594}
595
596# Merge 2 callout objects into 1 that contains all of their FRU callouts.
597sub mergeCallouts
598{
599 my ($firstCallout, $secondCallout) = @_;
600
601 # This callout list will be merged/sorted later.
602 # Endpoint callouts are added first, so they will be higher
603 # in the callout list (priority permitting).
604 my @calloutList;
605 push @calloutList, @{$secondCallout->calloutList()};
606 push @calloutList, @{$firstCallout->calloutList()};
607
608 # FSI
609 if (($firstCallout->type() eq "FSI") && ($secondCallout->type() eq "FSI"))
610 {
611 # combine the FSI links with a -
612 my $FSILink = $firstCallout->fsiLink() . "-" .
613 $secondCallout->fsiLink();
614
615 my $fsiCallout = new FSICallout($firstCallout->sourceChip(),
616 $secondCallout->destChip(), \@calloutList, $FSILink);
617
618 return $fsiCallout;
619 }
620 # FSI-I2C
621 elsif (($firstCallout->type() eq "FSI") &&
622 ($secondCallout->type() eq "I2C"))
623 {
624 my $i2cCallout = new FSII2CCallout($firstCallout->sourceChip(),
625 $secondCallout->destChip(), \@calloutList,
626 $firstCallout->fsiLink(), $secondCallout->i2cBus(),
627 $secondCallout->i2cAddress());
628
629 return $i2cCallout;
630 }
631 # FSI-SPI
632 elsif (($firstCallout->type() eq "FSI") &&
633 ($secondCallout->type() eq "SPI"))
634 {
635 my $spiCallout = new FSISPICallout($firstCallout->sourceChip(),
636 $secondCallout->destChip(), \@calloutList,
637 $firstCallout->fsiLink(), $secondCallout->spiBus());
638
639 return $spiCallout;
640 }
641
642 die "Unrecognized callouts to merge: " . $firstCallout->type() .
643 " " . $secondCallout->type() . "\n";
644}
645
Matt Spinler00ef7662020-06-02 10:58:24 -0500646# Create an I2CCallout object
647sub createI2CCallout
648{
649 my $segment = shift;
650 my $bus = $segment->{I2CBus};
651
652 # Convert MRW BMC I2C numbering to the linux one for the BMC
653 if ($targets->getAttribute($segment->{SourceChip}, "TYPE") eq "BMC")
654 {
655 $bus = Util::adjustI2CPort($segment->{I2CBus});
656
657 if ($bus < 0)
658 {
659 die "After adjusting BMC I2C bus $segment->{I2CBus}, " .
660 "got a negative number\n";
661 }
662 }
663
664 my $i2cCallout = new I2CCallout($segment->{SourceChip},
665 $segment->{DestChip}, $segment->{Callouts}, $bus,
666 $segment->{I2CAddress});
667
668 return $i2cCallout;
669}
670
671# Create an FSICallout object
672sub createFSICallout
673{
674 my $segment = shift;
675
676 my $fsiCallout = new FSICallout($segment->{SourceChip},
677 $segment->{DestChip}, $segment->{Callouts},
678 $segment->{FSILink}, $segment);
679
680 return $fsiCallout;
681}
682
683# Create a SPICallout object
684sub createSPICallout
685{
686 my $segment = shift;
687
688 my $spiCallout = new SPICallout($segment->{SourceChip},
689 $segment->{DestChip}, $segment->{Callouts},
690 $segment->{SPIBus});
691
692 return $spiCallout;
693}
694
Matt Spinler44aecce2020-06-02 12:40:49 -0500695# Convert all of the *Callout objects to JSON and write them to a file.
696# It will convert the callout target names to location codes and also sort
697# the callout list before doing so.
698sub printJSON
699{
700 my $callouts = shift;
701 my %output;
702
703 for my $callout (@$callouts)
704 {
705 my %c;
706 $c{Source} = $callout->sourceChip();
707 $c{Dest} = $callout->destChip();
708
709 for my $fruCallout (@{$callout->calloutList()})
710 {
711 my %entry;
712
713 if (length($fruCallout->{Name}) == 0)
714 {
715 die "Could not get target name for a callout on path:\n" .
716 " (" . $callout->sourceChip() . ") ->\n" .
717 " (" . $callout->destChip() . ")\n";
718 }
719
720 $entry{Name} = $fruCallout->{Name};
721
722 $entry{LocationCode} =
723 Util::getLocationCode($targets, $fruCallout->{Name});
724
725 # MRUs - for now just use the path + MRU name
726 if (exists $fruCallout->{MRU})
727 {
728 $entry{MRU} = $entry{Name} . "/" . $fruCallout->{MRU};
729 }
730
731 $entry{Priority} = validatePriority($fruCallout->{Priority});
732
733 push @{$c{Callouts}}, \%entry;
734 }
735
736
737 # Remove duplicate callouts and sort
738 @{$c{Callouts}} = sortCallouts(@{$c{Callouts}});
739
740 # Setup the entry based on the callout type
741 if ($callout->type() eq "I2C")
742 {
743 # The address key is in decimal, but save the hex value
744 # for easier debug.
745 $c{HexAddress} = $callout->i2cAddress();
746 my $decimal = hex($callout->i2cAddress());
747
748 $output{"I2C"}{$callout->i2cBus()}{$decimal} = \%c;
749 }
750 elsif ($callout->type() eq "SPI")
751 {
752 $output{"SPI"}{$callout->spiBus()} = \%c;
753 }
754 elsif ($callout->type() eq "FSI")
755 {
756 $output{"FSI"}{$callout->fsiLink()} = \%c;
757 }
758 elsif ($callout->type() eq "FSI-I2C")
759 {
760 $c{HexAddress} = $callout->i2cAddress();
761 my $decimal = hex($callout->i2cAddress());
762
763 $output{"FSI-I2C"}{$callout->fsiLink()}
764 {$callout->i2cBus()}{$decimal} = \%c;
765 }
766 elsif ($callout->type() eq "FSI-SPI")
767 {
768 $output{"FSI-SPI"}{$callout->fsiLink()}{$callout->spiBus()} = \%c;
769 }
770 }
771
772 open(my $fh, '>', $outFile) or die "Could not open file '$outFile' $!";
773 my $json = JSON->new->utf8;
774 $json->indent(1);
775 $json->canonical(1);
776 my $text = $json->encode(\%output);
777 print $fh $text;
778 close $fh;
779}
780
781# This will remove duplicate callouts from the input callout list, keeping
782# the highest priority value and MRU, and then also sort by priority.
783#
784# There could be duplicates when multiple single segment callouts are
785# combined into 1.
786sub sortCallouts
787{
788 my @callouts = @_;
789
790 # This will undef the duplicates, and then remove them at the end,
791 for (my $i = 0; $i < (scalar @callouts) - 1; $i++)
792 {
793 next if not defined $callouts[$i];
794
795 for (my $j = $i + 1; $j < (scalar @callouts); $j++)
796 {
797 next if not defined $callouts[$j];
798
799 if ($callouts[$i]->{LocationCode} eq $callouts[$j]->{LocationCode})
800 {
801 # Keep the highest priority value
802 $callouts[$i]->{Priority} = getHighestPriority(
803 $callouts[$i]->{Priority}, $callouts[$j]->{Priority});
804
805 # Keep the MRU if present
806 if (defined $callouts[$j]->{MRU})
807 {
808 $callouts[$i]->{MRU} = $callouts[$j]->{MRU};
809 }
810
811 $callouts[$j] = undef;
812 }
813 }
814 }
815
816 # removed the undefined ones
817 @callouts = grep {defined ($_)} @callouts;
818
819 # sort from highest to lowest priorities
820 @callouts = sort {
821 $priorities{$b->{Priority}} <=> $priorities{$a->{Priority}}
822 } @callouts;
823
824 return @callouts;
825}
826
827# Returns the highest priority value of the two passed in
828sub getHighestPriority
829{
830 my ($p1, $p2) = @_;
831
832 if ($priorities{$p1} > $priorities{$p2})
833 {
834 return $p1;
835 }
836 return $p2;
837}
838
839# Dies if the input priority isn't valid
840sub validatePriority
841{
842 my $priority = shift;
843
844 if (not exists $priorities{$priority})
845 {
846 die "Invalid callout priority found: $priority\n";
847 }
848
849 return $priority;
850}
Matt Spinler00ef7662020-06-02 10:58:24 -0500851
Matt Spinler7bedb122022-05-27 14:15:35 -0500852# Check if the attribute is present on the bus
853sub isValidBusAttribute
854{
855 my $target = shift;
856 my $connIndex = shift;
857 my $attr = shift;
858
859 if (defined($targets->getTarget($target)->
860 {CONNECTION}->{BUS}->[$connIndex]->{bus_attribute}->
861 {$attr}->{default}))
862 {
863 return 1;
864 }
865 return 0;
866}
867
Matt Spinler1cb9ecc2020-06-02 10:48:31 -0500868sub printUsage
869{
870 print "$0 -m <MRW file> -o <Output filename> [--segments] [-n]\n" .
871 " -m <MRW file> = The MRW XML\n" .
872 " -o <Output filename> = The output JSON\n" .
Matt Spinlerf481f2a2021-05-26 15:06:43 -0600873 " [--segments] = Optionally create a segments.json file\n";
Matt Spinler1cb9ecc2020-06-02 10:48:31 -0500874 exit(1);
875}