1 #Copyright (C) 2014 Intel Corporation
3 #This library is free software; you can redistribute it and/or
4 #modify it under the terms of the GNU Lesser General Public
5 #License as published by the Free Software Foundation; either
6 #version 2.1 of the License, or (at your option) any later version.
8 #This library is distributed in the hope that it will be useful,
9 #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 #Lesser General Public License for more details.
13 #You should have received a copy of the GNU Lesser General Public
14 #License along with this library; if not, write to the Free Software
15 #Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 package Intel::IviPoc::AmbPluginGenerator;
18 our @ISA = qw(Exporter);
19 our @EXPORT = qw(processPlugin);
23 use warnings FATAL => 'all';
29 use Digest::SHA qw(sha1_hex);
30 use Intel::IviPoc::AmbCommon qw(readFileContent);
34 Intel::IviPoc::AmbPluginGenerator - The great new Intel::IviPoc::AmbPluginGenerator!
42 our $VERSION = '0.01';
47 Module provides subroutines for reading input JSON file and generating Automotive Message Broker
50 The following little code snippet shows the module usage.
52 use Intel::IviPoc::AmbPluginGenerator qw(processPlugin);
54 my $hashingAllowed = "E";
55 my $inputfile = "myfile.json";
56 my $targetDir = '/home/user/project/automotive-message-broker/plugins';
57 processPlugin ( $hashingAllowed, $inputfile, $targetDir );
62 The following convenience methods are provided by this module. They are
67 =item C<processPlugin $json_filename>
69 For given json file name generates the Automotive Message Broker plugin
73 =head1 SUBROUTINES/METHODS
77 Copies all template files into target folder while replacing the name
78 with newly generated plugin name. Then generates C++ and IDL definitions
79 based on information comming from input JSON file.
83 my $hashingAllowed = 'E'; # Enabled by default
86 $hashingAllowed = $_[0];
88 my $targetDir = $_[2];
90 if ($hashingAllowed eq 'E' ) {
91 &encryptAmbPropertyNames( $dbcjson );
94 my $pluginName = $dbcjson->{'pluginName'};
96 my $templatesDir = 'templates/';
97 my $pluginDir = File::Spec->catdir( ($targetDir, lc($pluginName). '_plugin/') );
100 &createDirectory( $pluginDir );
102 my @templatesFiles = ( "CMakeLists.txt"
103 , "ambtmpl_cansignal.h"
104 , "ambtmpl_cansignal.cpp"
106 , "ambtmpl_plugin.cpp"
107 , "ambtmpl_cansignals.h"
108 , "ambtmpl_plugin.idl"
112 my @pluginFiles = ( "CMakeLists.txt"
113 , lc ($pluginName) . "_cansignal.h"
114 , lc ($pluginName) . "_cansignal.cpp"
115 , lc ($pluginName) . "_plugin.h"
116 , lc ($pluginName) . "_plugin.cpp"
117 , lc ($pluginName) . "_cansignals.h"
118 , lc ($pluginName) . "_plugin.idl"
119 , lc ($pluginName) . ".in.json"
122 my @generationSubs = ( undef
126 , \&generateCppImplTypes
127 , \&generateSignalsTypes
132 my $templateFile = '';
134 my ($volume, $directory) = File::Spec->splitpath(__FILE__);
135 for my $i (0..scalar(@pluginFiles)-1) {
136 # First join templates folder and filename
137 $templateFile = File::Spec->catfile( ($templatesDir), $templatesFiles[$i] );
138 # Now prepend the module full path
139 $templateFile = File::Spec->catpath( $volume, $directory, $templateFile );
140 # Join target directory with target filename
141 $pluginFile = File::Spec->catfile( ($pluginDir), $pluginFiles[$i] );
142 # Generate each plugin files
143 &generatePluginFile( $templateFile, $pluginFile, $dbcjson, $generationSubs[$i]);
147 =head2 generatePluginFile
149 Copies one template file into target folder while replacing the name
150 with newly generated plugin name. Then generates definitions
151 based on information comming from input JSON file.
155 sub generatePluginFile {
156 my $srcFileName = $_[0];
157 my $dstFileName = $_[1];
159 my $generationSub = $_[3];
161 my $pluginName = $dbcjson->{'pluginName'};
164 my $content = &readFileContent( $srcFileName );
165 $content = &replaceTemplateStrings( $content, $pluginName );
167 if (defined $generationSub) {
168 my $generatedCode = $generationSub->( $dbcjson );
169 my $place = '\/\*GENERATED_CODE\*\/';
170 $content =~ s/$place/$generatedCode/g;
174 my $pluginFileHandle = &createFile( $dstFileName );
176 # Copy data from one file to another.
177 print $pluginFileHandle $content;
180 close ($pluginFileHandle);
183 =head2 createDirectory
185 Creates directory for plugin.
189 sub createDirectory {
192 unless(-e $dirName or mkdir $dirName) {
193 die "Unable to create directory '$dirName' $!";
199 Creates file and returns a file handle to it.
204 my $fileName = $_[0];
207 open(my $fileHandle, '>', $fileName)
208 or die "Could not open file '$fileName' $!";
213 =head2 replaceTemplateStrings
215 Replaces all occurencies of template specific symbols with plugin name.
219 sub replaceTemplateStrings {
221 my $pluginName = $_[1];
223 $text =~ s/AmbTmpl/$pluginName/g;
224 $pluginName = lc ($pluginName);
225 $text =~ s/ambtmpl/$pluginName/g;
226 $pluginName = uc ($pluginName);
227 $text =~ s/AMBTMPL/$pluginName/g;
233 For all signals generates C++ property instantiation.
234 Returns C++ code to be placed into the target plugin.
241 my $ug = new Data::UUID;
242 my $uuidText = $ug->create_str();
247 =head2 generateCppImplTypes
249 For all signals generates C++ property instantiation.
250 Returns C++ code to be placed into the target plugin.
254 sub generateCppImplTypes {
257 my $registerMessageText = '';
260 my @engineControlUnits = @{$dbcjson->{'electronicControlUnits'}};
261 for my $ecui (0..scalar(@engineControlUnits)-1) {
262 if ( exists( $engineControlUnits[$ecui]{'messages'} ) ) {
263 my @messages = @{$engineControlUnits[$ecui]{'messages'}};
264 for my $msgi (0..scalar(@messages)-1) {
265 $hexValue = '0x' . uc ( sprintf( "%x", $messages[$msgi]{'canId'} ) );
266 $registerMessageText .= " registerMessage($hexValue, $messages[$msgi]{'canDlc'}";
268 my @signals = @{$messages[$msgi]{'signals'}};
269 foreach my $signal ( @signals ) {
270 my $type = $signal->{'AMBPropertyType'};
271 $registerMessageText .= &generateCppProperty( $signal, $type);
274 $registerMessageText .= "\n );\n";
279 return $registerMessageText;
282 =head2 generateCppProperty
284 For given signal generates ADDPROPERTY calls and returns C++ property
289 sub generateCppProperty {
293 my $generatedText = '';
294 my $zonesInUse = 0; # TODO this needs to be in config file
296 if ( exists( $signal->{'AMBPropertyName'} ) ) {
297 my $ambPropertyName = $signal->{'AMBPropertyName'};
299 # TODO CANSignal needs to take zone as argument
300 #my $zone = 'Zone::None';
302 # $zone = &calculateZone( $ambPropertyName );
304 $generatedText .= "\n , new ${ambPropertyName}Type()";
306 return $generatedText;
309 =head2 generateSignalsTypes
311 For all signals generates C++ property definitions.
312 Returns C++ code to be placed into the target plugin.
316 sub generateSignalsTypes {
320 my $propertiesText = '';
322 # First generate the c++ enums from signals with values
323 my @engineControlUnits = @{$dbcjson->{'electronicControlUnits'}};
324 for my $ecui (0..scalar(@engineControlUnits)-1) {
325 if ( exists( $engineControlUnits[$ecui]{'messages'} ) ) {
326 my @messages = @{$engineControlUnits[$ecui]{'messages'}};
327 for my $msgi (0..scalar(@messages)-1) {
328 my @signals = @{$messages[$msgi]{'signals'}};
329 foreach my $signal ( @signals ) {
330 my $type = $signal->{'AMBPropertyType'};
331 $enumsText .= &generateEnumOrValues( $signal, $type);
332 $propertiesText .= &generatePropertyClasses( $signal, $type );
337 return $enumsText . $propertiesText;
340 =head2 generateEnumOrValues
342 For given signal generates enums or c++ constants and returns C++ types
347 sub generateEnumOrValues {
351 my $generatedText = "";
353 my $ambPropertyName = $signal->{'canId'};
354 if ( exists( $signal->{'AMBPropertyName'} ) ) {
355 $ambPropertyName = $signal->{'AMBPropertyName'};
358 if ( exists( $signal->{'values'} ) ) {
359 my @dupvalues = @{$signal->{'values'}};
360 my @values = sort { $$a{'value'} <=> $$b{'value'} } (&removeDuplicates (\@dupvalues));
363 if ( $type eq 'enum' or $type =~ m/int/) {
364 # Start with comments
365 $generatedText .= "/**< $ambPropertyName\n";
366 for my $vali (0..scalar(@values) -1 ) {
367 $hexValue = '0x' . uc ( sprintf( "%x", $values[$vali]->{'value'} ) );
368 $generatedText .= " * $hexValue = $values[$vali]->{'description'}\n";
370 $generatedText .= " */\n";
373 if ( $type eq 'enum' ) {
375 $generatedText .= "namespace ${ambPropertyName}s {\n";
376 $generatedText .= "enum ${ambPropertyName}Type {\n";
378 # Generate enum values
379 for my $vali (0..scalar(@values) -1 ) {
380 $hexValue = '0x' . uc ( sprintf( "%x", $values[$vali]->{'value'} ) );
381 $generatedText .= " $values[$vali]->{'name'} = $hexValue";
382 if ($vali != scalar(@values)-1 ) {
383 $generatedText .= ",";
385 $generatedText .= "\n";
387 $generatedText .= "};\n";
388 $generatedText .= "}\n\n";
389 } elsif ( $type =~ m/int/ ) {
390 $generatedText .= "namespace ${ambPropertyName}s {\n";
392 for my $vali (0..scalar(@values) -1 ) {
393 $hexValue = '0x' . uc ( sprintf( "%x", $values[$vali]->{'value'} ) );
394 $generatedText .= "static const $type $values[$vali]->{'name'} = $hexValue;";
395 $generatedText .= "\n";
397 $generatedText .= "}\n\n";
401 return $generatedText;
404 =head2 generatePropertyClasses
406 For given signal generates documentation, VehicleProperty::Property
407 properties with values of stringified properties names.
408 lastly using CANSIGNAL macro generates C++ classes
411 Returns C++ definitions of one signal property.
415 sub generatePropertyClasses {
419 my $generatedText = '';
421 my $ambPropertyName = $signal->{'canId'};
422 if ( exists( $signal->{'AMBPropertyName'} ) ) {
423 $ambPropertyName = $signal->{'AMBPropertyName'};
426 my $byteOrdering = "Endian::Intel"; # LittleEndian by default
427 if ( exists( $signal->{'byteOrdering'} ) and $signal->{'byteOrdering'} eq '0') {
428 $byteOrdering = "Endian::Motorola"; # BigEndian
432 if ($signal->{'signedness'} eq '+') {
433 $signedness = "Signedness::Unsigned"; # Unsigned
435 $signedness = "Signedness::Signed"; # Signed
438 my $convertFromFunction = "nullptr";
439 if ( exists( $signal->{'AMBConversionFrom'} ) ) {
440 $convertFromFunction = $signal->{'AMBConversionFrom'};
443 my $convertToFunction = "nullptr";
444 if ( exists( $signal->{'AMBConversionTo'} ) ) {
445 $convertToFunction = $signal->{'AMBConversionTo'};
448 $generatedText .= "\n";
449 $generatedText .= "/**< $ambPropertyName.\n";
451 my $typeBasedText = '';
454 if ( $type =~ m/enum/ ) {
455 if ( exists( $signal->{'values'} ) ) {
456 $generatedText .= " *\@see ${ambPropertyName}s::${ambPropertyName}Type\n";
457 $cppType = "${ambPropertyName}s::${ambPropertyName}Type";
459 } elsif ( $type =~ m/bool/ ) {
460 if ( exists( $signal->{'values'} ) ) {
461 my @dupvalues = @{$signal->{'values'}};
462 my @values = sort { $$a{'value'} <=> $$b{'value'} } (&removeDuplicates (\@dupvalues));
464 for my $vali (0..scalar(@values) -1 ) {
465 $hexValue = '0x' . uc ( sprintf( "%x", $values[$vali]->{'value'} ) );
466 $generatedText .= " * $hexValue = $values[$vali]->{'description'}\n";
470 } elsif ( $type =~ m/int8/ ) {
472 } elsif ( $type =~ m/int32/ ) {
473 if ( $type eq 'uint32_t' ) {
478 } else { # (u)int16, (u)int64
482 $typeBasedText .= "CANSIGNAL($ambPropertyName, $cppType, $signal->{'startBit'}, $signal->{'length'}, $byteOrdering, $signedness, $signal->{'factor'}, $signal->{'offset'}, static_cast<$cppType>($signal->{'minValue'}), static_cast<$cppType>($signal->{'maxValue'}), $convertFromFunction, $convertToFunction)\n";
484 $generatedText .= " */\n";
485 my $shownPropertyName = $ambPropertyName;
486 if ($hashingAllowed eq 'E' ) {
487 $shownPropertyName = $signal->{'AMBPropertyNameEnc'};
489 $generatedText .= "const VehicleProperty::Property $ambPropertyName = \"$shownPropertyName\";\n";
490 $generatedText .= $typeBasedText;
492 return $generatedText;
495 =head2 generateIdlTypes
497 For all signals generates WebIDL property documentation.
498 Returns IDL code to be placed into the target plugin.
502 sub generateIdlTypes {
505 my $generatedText = '';
507 my @engineControlUnits = @{$dbcjson->{'electronicControlUnits'}};
508 for my $ecui (0..scalar(@engineControlUnits)-1) {
509 if ( exists( $engineControlUnits[$ecui]{'messages'} ) ) {
510 my @messages = @{$engineControlUnits[$ecui]{'messages'}};
511 for my $msgi (0..scalar(@messages)-1) {
512 my @signals = @{$messages[$msgi]{'signals'}};
513 foreach my $signal ( @signals ) {
514 my $type = $signal->{'AMBPropertyType'};
515 $generatedText .= &generateIdlProperty( $signal, $type);
520 return $generatedText;
523 =head2 generateIdlProperty
525 For given signal generates WebIDL documentation and returns WebIDL
526 definitions of one signal property.
530 sub generateIdlProperty {
533 my $generatedText = '';
535 my $ambPropertyName = $signal->{'canId'};
536 if ( exists( $signal->{'AMBPropertyName'} ) ) {
537 $ambPropertyName = $signal->{'AMBPropertyName'};
540 $generatedText .= "[NoInterfaceObject]\n";
541 $generatedText .= "interface org.automotive.${ambPropertyName} : VehiclePropertyType {\n";
542 if ( $type eq 'enum' ) {
543 if ( exists( $signal->{'values'} ) ) {
544 my @dupvalues = @{$signal->{'values'}};
545 my @values = sort { $$a{'value'} <=> $$b{'value'} } (&removeDuplicates (\@dupvalues));
548 for my $vali (0..scalar(@values) -1 ) {
549 # TODO const unsigned short migth be not enough, guess type based on values
550 $hexValue = '0x' . uc (sprintf( "%x", $values[$vali]->{'value'} ) );
551 $generatedText .= " const unsigned short " . uc($values[$vali]->{'name'}) . " = $hexValue;\n";
556 $generatedText .= "\n";
557 $generatedText .= " /** ${ambPropertyName}\n";
558 $generatedText .= " * \\brief Returns ${ambPropertyName}\n";
559 $generatedText .= " **/\n";
562 if ( $type =~ m/uint/ ) {
563 $unsigned = 'unsigned ';
566 if ( $type =~ m/enum/ ) {
567 # TODO const unsigned short migth be not enough, guess type based on values
568 $generatedText .= " readonly attribute octet ${ambPropertyName};\n";
569 } elsif ( $type =~ m/bool/ ) {
570 $generatedText .= " readonly attribute boolean ${ambPropertyName};\n";
571 } elsif ( $type =~ m/int8/ ) {
572 $generatedText .= " readonly attribute ${unsigned}octet ${ambPropertyName};\n";
573 } elsif ( $type =~ m/int16/ ) {
574 $generatedText .= " readonly attribute ${unsigned}short ${ambPropertyName};\n";
575 } elsif ( $type =~ m/int32/ ) {
576 $generatedText .= " readonly attribute ${unsigned}long ${ambPropertyName};\n";
578 $generatedText .= " readonly attribute double ${ambPropertyName};\n";
580 $generatedText .= "};\n\n";
582 return $generatedText;
585 =head2 encryptAmbPropertyNames
587 Encrypt AmbPropertyNames.
591 sub encryptAmbPropertyNames {
594 my @engineControlUnits = @{$dbcjson->{'electronicControlUnits'}};
595 for my $ecui (0..scalar(@engineControlUnits)-1) {
596 if ( exists( $engineControlUnits[$ecui]{'messages'} ) ) {
597 my @messages = @{$engineControlUnits[$ecui]{'messages'}};
598 for my $msgi (0..scalar(@messages)-1) {
599 my @signals = @{$messages[$msgi]{'signals'}};
600 foreach my $signal ( @signals ) {
601 my $shownPropertyName = sha1_hex( $signal->{'AMBPropertyName'} );
602 $signal->{'AMBPropertyNameEnc'} = 'S' . sha1_hex( $shownPropertyName );
609 =head2 removeDuplicates
611 Returns array of values witout duplicates.
615 sub removeDuplicates {
616 my @arr = sort { $a->{'name'} cmp $b->{'name'} } @{$_[0]};
621 while (defined(my $x = pop @arr)) {
622 if ($prev->{'name'} eq $x->{'name'}) {
623 push @duplicates, $x;
624 while (defined(my $y = pop @arr)) {
625 if ($y->{'name'} ne $x->{'name'}) {
635 # Typically very small arrays
636 @arr = sort @{$_[0]};
637 if (scalar @duplicates > 0) {
638 foreach my $x (@arr) {
639 foreach my $y (@duplicates) {
640 if ($x->{'name'} eq $y->{'name'}) {
641 $x->{'name'} .= '_' . $x->{'value'};
652 Returns calculated Zone for given signal.
657 my $ambPropertyName = $_[0];
658 my $zone = 'Zone::None';
660 if ( $ambPropertyName =~ m/FrL/) {
661 $zone = 'Zone::FrontLeft';
662 } elsif ( $ambPropertyName =~ m/FrR/) {
663 $zone = 'Zone::FrontRight';
664 } elsif ( $ambPropertyName =~ m/ReL/) {
665 $zone = 'Zone::RearLeft';
666 } elsif ( $ambPropertyName =~ m/ReR/) {
667 $zone = 'Zone::RearRight';
672 ##############################################################################
676 IntelIVIPoc, C<< <ivipoc at intel.com> >>
680 You can find documentation for this module with the perldoc command.
682 perldoc Intel::IVIPoc::AMBPluginGenerator
685 =head1 ACKNOWLEDGEMENTS
687 =head1 LICENSE AND COPYRIGHT
689 Copyright (C) 2012 Intel Corporation
691 This library is free software; you can redistribute it and/or
692 modify it under the terms of the GNU Lesser General Public
693 License as published by the Free Software Foundation; either
694 version 2.1 of the License, or (at your option) any later version.
696 This library is distributed in the hope that it will be useful,
697 but WITHOUT ANY WARRANTY; without even the implied warranty of
698 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
699 Lesser General Public License for more details.
701 You should have received a copy of the GNU Lesser General Public
702 License along with this library; if not, write to the Free Software
703 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
708 1; # End of Intel::IviPoc::AmbPluginGenerator