AmbSignalMapper: Improve handling of bitbake environment
[profile/ivi/automotive-message-broker.git] / tools / AmbSignalMapper / lib / Intel / IviPoc / AmbPluginGenerator.pm
1 #Copyright (C) 2014  Intel Corporation
2 #
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.
7 #
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.
12 #
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
16
17 package Intel::IviPoc::AmbPluginGenerator;
18 our @ISA = qw(Exporter);
19 our @EXPORT = qw(processPlugin);
20
21 use 5.006;
22 use strict;
23 use warnings FATAL => 'all';
24 use Exporter;
25
26 use JSON;
27 use Data::UUID;
28 use File::Spec;
29 use Digest::SHA qw(sha1_hex);
30 use Intel::IviPoc::AmbCommon  qw(readFileContent);
31
32 =head1 NAME
33
34 Intel::IviPoc::AmbPluginGenerator - The great new Intel::IviPoc::AmbPluginGenerator!
35
36 =head1 VERSION
37
38 Version 0.01
39
40 =cut
41
42 our $VERSION = '0.01';
43
44
45 =head1 SYNOPSIS
46
47 Module provides subroutines for reading input JSON file and generating Automotive Message Broker
48 plugin project.
49
50 The following little code snippet shows the module usage.
51
52     use Intel::IviPoc::AmbPluginGenerator qw(processPlugin);
53
54     my $hashingAllowed = "E";
55     my $inputfile = "myfile.json";
56     my $targetDir = '/home/user/project/automotive-message-broker/plugins';
57     processPlugin ( $hashingAllowed, $inputfile, $targetDir );
58     ...
59
60 =head1 EXPORT
61
62 The following convenience methods are provided by this module. They are
63 exported by default:
64
65 =over 4
66
67 =item C<processPlugin $json_filename>
68
69 For given json file name generates the Automotive Message Broker plugin
70
71 =back
72
73 =head1 SUBROUTINES/METHODS
74
75 =head2 processPlugin
76
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.
80
81 =cut
82
83 my $hashingAllowed = 'E';       # Enabled by default
84
85 sub processPlugin {
86     $hashingAllowed = $_[0];
87     my $dbcjson = $_[1];
88     my $targetDir = $_[2];
89     
90     if ($hashingAllowed eq 'E' ) {
91         &encryptAmbPropertyNames( $dbcjson );
92     }
93
94     my $pluginName = $dbcjson->{'pluginName'};
95
96     my $templatesDir = 'templates/';
97     my $pluginDir = File::Spec->catdir( ($targetDir, lc($pluginName). '_plugin/') );
98
99     # make new folder
100     &createDirectory( $pluginDir );
101
102     my @templatesFiles = ( "CMakeLists.txt"
103                          , "ambtmpl_cansignal.h"
104                          , "ambtmpl_cansignal.cpp"
105                          , "ambtmpl_plugin.h"
106                          , "ambtmpl_plugin.cpp"
107                          , "ambtmpl_cansignals.h"
108                          , "ambtmpl_plugin.idl"
109                          , "ambtmpl.in.json"
110                          );
111
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"
120                       );
121
122     my @generationSubs = ( undef
123                          , undef
124                          , undef
125                          , \&generateUuid
126                          , \&generateCppImplTypes
127                          , \&generateSignalsTypes
128                          , \&generateIdlTypes
129                          , undef
130                          );
131
132     my $templateFile = '';
133     my $pluginFile = '';
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]);
144     }
145 }
146
147 =head2 generatePluginFile
148
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.
152
153 =cut
154
155 sub generatePluginFile {
156     my $srcFileName = $_[0];
157     my $dstFileName = $_[1];
158     my $dbcjson = $_[2];
159     my $generationSub = $_[3];
160
161     my $pluginName = $dbcjson->{'pluginName'};
162
163     # Open template file
164     my $content = &readFileContent( $srcFileName );
165     $content = &replaceTemplateStrings( $content, $pluginName );
166
167     if (defined $generationSub) {
168         my $generatedCode = $generationSub->( $dbcjson );
169         my $place = '\/\*GENERATED_CODE\*\/';
170         $content =~ s/$place/$generatedCode/g;
171     }
172
173     # Create new file
174     my $pluginFileHandle = &createFile( $dstFileName );
175
176     # Copy data from one file to another.
177     print $pluginFileHandle $content;
178
179     # close the file
180     close ($pluginFileHandle);
181 }
182
183 =head2 createDirectory
184
185 Creates directory for plugin.
186
187 =cut
188
189 sub createDirectory {
190     my $dirName = $_[0];
191
192     unless(-e $dirName or mkdir $dirName) {
193         die "Unable to create directory '$dirName' $!";
194     }
195 }
196
197 =head2 createFile
198
199 Creates file and returns a file handle to it.
200
201 =cut
202
203 sub createFile {
204     my $fileName = $_[0];
205
206     # Open file or die
207     open(my $fileHandle, '>', $fileName)
208         or die "Could not open file '$fileName' $!";
209
210     return $fileHandle;
211 }
212
213 =head2 replaceTemplateStrings
214
215 Replaces all occurencies of template specific symbols with plugin name.
216
217 =cut
218
219 sub replaceTemplateStrings {
220     my $text = $_[0];
221     my $pluginName = $_[1];
222
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;
228     return $text;
229 }
230
231 =head2 generateUuid
232
233 For all signals generates C++ property instantiation.
234 Returns C++ code to be placed into the target plugin.
235
236 =cut
237
238 sub generateUuid {
239     my $dbcjson = $_[0];
240
241     my $ug = new Data::UUID;
242     my $uuidText = $ug->create_str();
243
244     return $uuidText;
245 }
246
247 =head2 generateCppImplTypes
248
249 For all signals generates C++ property instantiation.
250 Returns C++ code to be placed into the target plugin.
251
252 =cut
253
254 sub generateCppImplTypes {
255     my $dbcjson = $_[0];
256
257     my $registerMessageText = '';
258     my $hexValue = ();
259
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'}";
267
268                 my @signals = @{$messages[$msgi]{'signals'}};
269                 foreach my $signal ( @signals ) {
270                     my $type = $signal->{'AMBPropertyType'};
271                     $registerMessageText .= &generateCppProperty( $signal, $type);
272                 }
273
274                 $registerMessageText .= "\n                   );\n";
275             }
276         }
277     }
278
279     return $registerMessageText;
280 }
281
282 =head2 generateCppProperty
283
284 For given signal generates ADDPROPERTY calls and returns C++ property
285 definitions.
286
287 =cut
288
289 sub generateCppProperty {
290     my $signal = $_[0];
291     my $type = $_[1];
292
293     my $generatedText = '';
294     my $zonesInUse = 0; # TODO this needs to be in config file
295
296     if ( exists( $signal->{'AMBPropertyName'} ) ) {
297         my $ambPropertyName = $signal->{'AMBPropertyName'};
298
299         # TODO CANSignal needs to take zone as argument
300         #my $zone = 'Zone::None';
301         #if ($zonesInUse) {
302         #    $zone = &calculateZone( $ambPropertyName );
303         #}
304         $generatedText .= "\n                   , new ${ambPropertyName}Type()";
305     }
306     return $generatedText;
307 }
308
309 =head2 generateSignalsTypes
310
311 For all signals generates C++ property definitions.
312 Returns C++ code to be placed into the target plugin.
313
314 =cut
315
316 sub generateSignalsTypes {
317     my $dbcjson = $_[0];
318
319     my $enumsText = '';
320     my $propertiesText = '';
321
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 );
333                 }
334             }
335         }
336     }
337     return $enumsText . $propertiesText;
338 }
339
340 =head2 generateEnumOrValues
341
342 For given signal generates enums or c++ constants and returns C++ types
343 definitions.
344
345 =cut
346
347 sub generateEnumOrValues {
348     my $signal = $_[0];
349     my $type = $_[1];
350
351     my $generatedText = "";
352
353     my $ambPropertyName = $signal->{'canId'};
354     if ( exists( $signal->{'AMBPropertyName'} ) ) {
355         $ambPropertyName = $signal->{'AMBPropertyName'};
356     }
357
358     if ( exists( $signal->{'values'} ) ) {
359         my @dupvalues = @{$signal->{'values'}};
360         my @values = sort { $$a{'value'} <=> $$b{'value'} } (&removeDuplicates (\@dupvalues));
361         my $hexValue = ();
362
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";
369             }
370             $generatedText .= " */\n";
371         }
372
373         if ( $type eq 'enum' ) {
374             # Enum definition
375             $generatedText .= "namespace ${ambPropertyName}s {\n";
376             $generatedText .= "enum ${ambPropertyName}Type {\n";
377
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 .= ",";
384                 }
385                 $generatedText .= "\n";
386             }
387             $generatedText .= "};\n";
388             $generatedText .= "}\n\n";
389         } elsif ( $type =~ m/int/ ) {
390             $generatedText .= "namespace ${ambPropertyName}s {\n";
391             # Generate values
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";
396             }
397         $generatedText .= "}\n\n";
398         }
399     }
400
401     return $generatedText;
402 }
403
404 =head2 generatePropertyClasses
405
406 For given signal generates documentation, VehicleProperty::Property
407 properties with values of stringified properties names.
408 lastly using CANSIGNAL macro generates C++ classes
409 for given signal.
410
411 Returns C++ definitions of one signal property.
412
413 =cut
414
415 sub generatePropertyClasses {
416     my $signal = $_[0];
417     my $type = $_[1];
418
419     my $generatedText = '';
420
421     my $ambPropertyName = $signal->{'canId'};
422     if ( exists( $signal->{'AMBPropertyName'} ) ) {
423         $ambPropertyName = $signal->{'AMBPropertyName'};
424     }
425
426     my $byteOrdering = "Endian::Intel";                 # LittleEndian by default
427     if ( exists( $signal->{'byteOrdering'} ) and $signal->{'byteOrdering'} eq '0') {
428         $byteOrdering = "Endian::Motorola";                 # BigEndian
429     }
430
431     my $signedness;
432     if ($signal->{'signedness'} eq '+') {
433         $signedness = "Signedness::Unsigned";               # Unsigned
434     } else {
435         $signedness = "Signedness::Signed";                 # Signed
436     }
437
438     my $convertFromFunction = "nullptr";
439     if ( exists( $signal->{'AMBConversionFrom'} ) ) {
440         $convertFromFunction = $signal->{'AMBConversionFrom'};
441     }
442
443     my $convertToFunction = "nullptr";
444     if ( exists( $signal->{'AMBConversionTo'} ) ) {
445         $convertToFunction = $signal->{'AMBConversionTo'};
446     }
447
448     $generatedText .= "\n";
449     $generatedText .= "/**< $ambPropertyName.\n";
450
451     my $typeBasedText = '';
452     my $cppType;
453
454     if ( $type =~ m/enum/ ) {
455         if ( exists( $signal->{'values'} ) ) {
456             $generatedText .= " *\@see ${ambPropertyName}s::${ambPropertyName}Type\n";
457             $cppType = "${ambPropertyName}s::${ambPropertyName}Type";
458         }
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));
463             my $hexValue = ();
464             for my $vali (0..scalar(@values) -1 ) {
465                 $hexValue = '0x' . uc ( sprintf( "%x", $values[$vali]->{'value'} ) );
466                 $generatedText .= " * $hexValue = $values[$vali]->{'description'}\n";
467             }
468         }
469         $cppType = "$type";
470     } elsif ( $type =~ m/int8/ ) {
471         $cppType = "char";
472     } elsif ( $type =~ m/int32/ ) {
473         if ( $type eq 'uint32_t' ) {
474             $cppType = "$type";
475         } else {
476             $cppType = "int";
477         }
478     } else {    # (u)int16, (u)int64
479         $cppType = "$type";
480     }
481
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";
483
484     $generatedText .= " */\n";
485     my $shownPropertyName = $ambPropertyName;
486     if ($hashingAllowed eq 'E' ) {
487         $shownPropertyName = $signal->{'AMBPropertyNameEnc'};
488     }
489     $generatedText .= "const VehicleProperty::Property $ambPropertyName = \"$shownPropertyName\";\n";
490     $generatedText .= $typeBasedText;
491
492     return $generatedText;
493 }
494
495 =head2 generateIdlTypes
496
497 For all signals generates WebIDL property documentation.
498 Returns IDL code to be placed into the target plugin.
499
500 =cut
501
502 sub generateIdlTypes {
503     my $dbcjson = $_[0];
504
505     my $generatedText = '';
506
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);
516                 }
517             }
518         }
519     }
520     return $generatedText;
521 }
522
523 =head2 generateIdlProperty
524
525 For given signal generates WebIDL documentation and returns WebIDL
526 definitions of one signal property.
527
528 =cut
529
530 sub generateIdlProperty {
531     my $signal = $_[0];
532     my $type = $_[1];
533     my $generatedText = '';
534
535     my $ambPropertyName = $signal->{'canId'};
536     if ( exists( $signal->{'AMBPropertyName'} ) ) {
537         $ambPropertyName = $signal->{'AMBPropertyName'};
538     }
539
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));
546
547             my $hexValue = ();
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";
552             }
553         }
554     }
555
556     $generatedText .= "\n";
557     $generatedText .= "    /**  ${ambPropertyName}\n";
558     $generatedText .= "     *   \\brief  Returns ${ambPropertyName}\n";
559     $generatedText .= "     **/\n";
560
561     my $unsigned = '';
562     if ( $type =~ m/uint/ ) {
563         $unsigned = 'unsigned ';
564     }
565
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";
577     } else {
578         $generatedText .= "    readonly attribute double ${ambPropertyName};\n";
579     }
580     $generatedText .= "};\n\n";
581
582     return $generatedText;
583 }
584
585 =head2 encryptAmbPropertyNames
586
587 Encrypt AmbPropertyNames.
588
589 =cut
590
591 sub encryptAmbPropertyNames {
592     my $dbcjson = $_[0];
593
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 );
603                 }
604             }
605         }
606     }
607 }
608
609 =head2 removeDuplicates
610
611 Returns array of values witout duplicates.
612
613 =cut
614
615 sub removeDuplicates {
616     my @arr = sort { $a->{'name'} cmp $b->{'name'} }  @{$_[0]};
617
618     my @duplicates;
619     my $prev = pop @arr;
620
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'}) {
626                     $prev = $y;
627                     last;
628                 }
629             }
630         }
631         else {
632             $prev = $x;
633         }
634     }
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'};
642                 }
643             }
644         }
645     }
646
647     return @arr;
648 }
649
650 =head2 calculateZone
651
652 Returns calculated Zone for given signal.
653
654 =cut
655
656 sub calculateZone {
657     my $ambPropertyName = $_[0];
658     my $zone = 'Zone::None';
659
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';
668     }
669     return $zone;
670 }
671
672 ##############################################################################
673
674 =head1 AUTHOR
675
676 IntelIVIPoc, C<< <ivipoc at intel.com> >>
677
678 =head1 SUPPORT
679
680 You can find documentation for this module with the perldoc command.
681
682     perldoc Intel::IVIPoc::AMBPluginGenerator
683
684
685 =head1 ACKNOWLEDGEMENTS
686
687 =head1 LICENSE AND COPYRIGHT
688
689 Copyright (C) 2012 Intel Corporation
690
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.
695
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.
700
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
704
705
706 =cut
707
708 1; # End of Intel::IviPoc::AmbPluginGenerator