@@ -4,7 +4,7 @@ package org.sireum.hamr.codegen.ros2
4
4
5
5
import org .sireum ._
6
6
import org .sireum .hamr .codegen .common .containers .Marker
7
- import org .sireum .hamr .codegen .common .symbols .{AadlDataPort , AadlEventDataPort , AadlPort , AadlSystem , AadlThread , Dispatch_Protocol }
7
+ import org .sireum .hamr .codegen .common .symbols .{AadlComponent , AadlDataPort , AadlEventDataPort , AadlPort , AadlProcess , AadlSystem , AadlThread , Dispatch_Protocol }
8
8
import org .sireum .hamr .codegen .common .types .{AadlType , EnumType }
9
9
import org .sireum .hamr .ir .Direction
10
10
import org .sireum .message .Reporter
@@ -505,24 +505,73 @@ object GeneratorPy {
505
505
// package="tc_cpp_pkg",
506
506
// executable="tc_exe"
507
507
// )
508
- def genPyFormatLaunchNodeDecl (launch_node_decl_nameT : String ,
509
- top_level_package_nameT : String ,
510
- component : AadlThread ) : ST = {
511
- val node_executable_file_nameT = genExecutableFileName (genNodeName(component ))
508
+ def genPyFormatLaunchNodeDecl (top_level_package_nameT : String ,
509
+ thread : AadlThread ) : ST = {
510
+ val node_executable_file_nameT = genExecutableFileName(genNodeName(thread))
511
+ val launch_node_decl_nameT = genPyFormatLaunchNodeDeclName (genNodeName(thread ))
512
512
val s =
513
513
st """ ${launch_node_decl_nameT} = Node(
514
514
| package = " ${top_level_package_nameT}",
515
515
| executable = " ${node_executable_file_nameT}"
516
- | )
516
+ |)
517
517
"""
518
518
return s
519
519
}
520
520
521
+ // Generate system launch code (including a system launch file)
522
+ // Example:
523
+ // <include file="$(find-pkg-share gazebo_ros)/launch/gazebo.launch.py"/>
524
+ def genPyFormatLaunchSystemDecl (top_level_package_nameT : String ,
525
+ system : AadlSystem ): ST = {
526
+ val launch_node_decl_nameT = genPyFormatLaunchNodeDeclName(system.identifier)
527
+ val launchFileName : String = genPyLaunchFileName(system.identifier)
528
+ val s =
529
+ st """ ${launch_node_decl_nameT} = IncludeLaunchDescription(
530
+ | PythonLaunchDescriptionSource(
531
+ | os.path.join(get_package_share_directory(' ${top_level_package_nameT}_bringup'),
532
+ | 'launch/ ${launchFileName}')
533
+ | )
534
+ |)
535
+ """
536
+ return s
537
+ }
538
+
539
+ def genPyFormatLaunchDecls (component : AadlComponent , packageName : String ): ISZ [ST ] = {
540
+ var launch_decls : ISZ [ST ] = IS ()
541
+
542
+ for (comp <- component.subComponents) {
543
+ comp match {
544
+ case thread : AadlThread =>
545
+ launch_decls = launch_decls :+ genPyFormatLaunchNodeDecl(packageName, thread)
546
+ case system : AadlSystem =>
547
+ launch_decls = launch_decls :+ genPyFormatLaunchSystemDecl(packageName, system)
548
+ case process : AadlProcess =>
549
+ launch_decls = launch_decls ++ genPyFormatLaunchDecls(process, packageName)
550
+ case _ =>
551
+ }
552
+ }
553
+
554
+ return launch_decls
555
+ }
556
+
521
557
// Example:
522
558
// ld.add_action(tc_node)
523
- def genPyFormatLaunchAddAction (launch_node_decl_nameT : String ): ST = {
524
- val s = st """ ld.add_action( ${launch_node_decl_nameT}) """
525
- return s
559
+ def genPyFormatLaunchAddAction (component : AadlComponent ): ISZ [ST ] = {
560
+ var ld_entries : ISZ [ST ] = IS ()
561
+
562
+ for (comp <- component.subComponents) {
563
+ comp match {
564
+ case thread : AadlThread =>
565
+ ld_entries = ld_entries :+ st """ ld.add_action( ${genPyFormatLaunchNodeDeclName(genNodeName(thread))}) """
566
+ case system : AadlSystem =>
567
+ ld_entries = ld_entries :+ st """ ld.add_action( ${genPyFormatLaunchNodeDeclName(system.identifier)}) """
568
+ case process : AadlProcess =>
569
+ ld_entries = ld_entries ++ genPyFormatLaunchAddAction(process)
570
+ case _ =>
571
+ }
572
+ }
573
+
574
+ return ld_entries
526
575
}
527
576
528
577
// For example, see https://github.com/santoslab/ros-examples/blob/main/tempControl_ws/src/tc_bringup/launch/tc.launch.py
@@ -534,15 +583,20 @@ object GeneratorPy {
534
583
var launchFiles : ISZ [(ISZ [String ], ST , B , ISZ [Marker ])] = IS ()
535
584
536
585
for (system <- systemComponents) {
537
- val fileName = genPyFormatLaunchNodeDeclName (system.identifier)
586
+ val fileName = genPyLaunchFileName (system.identifier)
538
587
539
- val node_decls : ISZ [ST ] = IS ( )
540
- val ld_entries : ISZ [ST ] = IS ( )
588
+ val node_decls : ISZ [ST ] = genPyFormatLaunchDecls(system, top_level_package_nameT )
589
+ val ld_entries : ISZ [ST ] = genPyFormatLaunchAddAction(system )
541
590
542
591
val launchFileBody =
543
592
st """ from launch import LaunchDescription
544
593
|from launch_ros.actions import Node
545
594
|
595
+ |import os
596
+ |from ament_index_python.packages import get_package_share_directory
597
+ |from launch.actions import IncludeLaunchDescription
598
+ |from launch.launch_description_sources import PythonLaunchDescriptionSource
599
+ |
546
600
|def generate_launch_description():
547
601
| ld = LaunchDescription()
548
602
|
@@ -772,6 +826,25 @@ object GeneratorPy {
772
826
return subscriptionMessageHeader
773
827
}
774
828
829
+ def genPySubscriptionHandlerVirtualStrict (inPort : AadlPort , portType : String ): ST = {
830
+ val handlerName = inPort.identifier
831
+
832
+ var handlerCode : ST = st " "
833
+ if (isEventPort(portType)) {
834
+ handlerCode =
835
+ st """ def handle_ ${handlerName}(self):
836
+ | raise NotImplementedError("Subclasses must implement this method")
837
+ | """
838
+ } else {
839
+ handlerCode =
840
+ st """ def handle_ ${handlerName}(self, msg):
841
+ | raise NotImplementedError("Subclasses must implement this method")
842
+ | """
843
+ }
844
+
845
+ return handlerCode
846
+ }
847
+
775
848
def genPySubscriptionHandlerBaseSporadic (inPort : AadlPort , portType : String ): ST = {
776
849
val handlerName = inPort.identifier
777
850
@@ -905,7 +978,7 @@ object GeneratorPy {
905
978
handler = st " self.event_handle_ ${handlerName}"
906
979
}
907
980
else {
908
- handler = st " handle_ ${handlerName}"
981
+ handler = st " self. handle_ ${handlerName}"
909
982
}
910
983
911
984
if (outPortNames.size == 1 ) {
@@ -986,6 +1059,25 @@ object GeneratorPy {
986
1059
return subscriptionMessageVar
987
1060
}
988
1061
1062
+ def genPySubscriptionHandlerVirtual (inPort : AadlPort , portType : String ): ST = {
1063
+ val handlerName = inPort.identifier
1064
+
1065
+ var handlerCode : ST = st " "
1066
+ if (isEventPort(portType)) {
1067
+ handlerCode =
1068
+ st """ def handle_ ${handlerName}(self):
1069
+ | raise NotImplementedError("Subclasses must implement this method")
1070
+ | """
1071
+ } else {
1072
+ handlerCode =
1073
+ st """ def handle_ ${handlerName}(self, msg):
1074
+ | raise NotImplementedError("Subclasses must implement this method")
1075
+ | """
1076
+ }
1077
+
1078
+ return handlerCode
1079
+ }
1080
+
989
1081
def genPyInfrastructureOutQueue (inPort : AadlPort ): ST = {
990
1082
val portName = inPort.identifier
991
1083
@@ -1217,7 +1309,7 @@ object GeneratorPy {
1217
1309
var inPortNames : ISZ [String ] = IS ()
1218
1310
var strictPutMsgMethods : ISZ [ST ] = IS ()
1219
1311
var strictSubscriptionMessageAcceptorMethods : ISZ [ST ] = IS ()
1220
- var strictSubscriptionHandlerBaseMethods : ISZ [ST ] = IS ()
1312
+ var subscriptionHandlerMethods : ISZ [ST ] = IS ()
1221
1313
var msgTypes : ISZ [String ] = IS ()
1222
1314
1223
1315
var inMsgVars : ISZ [ST ] = IS ()
@@ -1259,7 +1351,9 @@ object GeneratorPy {
1259
1351
subscriptionMessageGetters = subscriptionMessageGetters :+ genPyGetApplicationInValue(p, portDatatype)
1260
1352
}
1261
1353
else {
1262
- strictSubscriptionHandlerBaseMethods = strictSubscriptionHandlerBaseMethods :+
1354
+ subscriptionHandlerMethods = subscriptionHandlerMethods :+
1355
+ genPySubscriptionHandlerVirtualStrict(p, portDatatype)
1356
+ subscriptionHandlerMethods = subscriptionHandlerMethods :+
1263
1357
genPySubscriptionHandlerBaseSporadic(p, portDatatype)
1264
1358
}
1265
1359
hasInPorts = T
@@ -1317,6 +1411,9 @@ object GeneratorPy {
1317
1411
genPySubscriptionHandlerPeriodic(p, portDatatype)
1318
1412
subscriptionMessageGetters = subscriptionMessageGetters :+ genPyGetSubscriptionMessage(p, nodeName)
1319
1413
inMsgVars = inMsgVars :+ genPySubscriptionMessageVar(p)
1414
+ } else {
1415
+ subscriptionHandlerMethods = subscriptionHandlerMethods :+
1416
+ genPySubscriptionHandlerVirtual(p, portDatatype)
1320
1417
}
1321
1418
hasInPorts = T
1322
1419
}
@@ -1359,7 +1456,7 @@ object GeneratorPy {
1359
1456
if (! strictAADLMode && subscribers.size > 0 ) {
1360
1457
stdIncludes =
1361
1458
st """ ${stdIncludes}
1362
- |from ${packageName}.user_code.consumer_consumer_src import * """
1459
+ |from ${packageName}.user_code. ${genNodeName(component)} _src import *"""
1363
1460
}
1364
1461
1365
1462
var fileBody =
@@ -1451,9 +1548,9 @@ object GeneratorPy {
1451
1548
if (subscriberMethods.size > 0 || publisherMethods.size > 0 || (strictAADLMode && subscribers.size > 0 )) {
1452
1549
fileBody =
1453
1550
st """ ${fileBody}
1454
- |#=================================================
1455
- |# C o m m u n i c a t i o n
1456
- |#=================================================
1551
+ | #=================================================
1552
+ | # C o m m u n i c a t i o n
1553
+ | #=================================================
1457
1554
"""
1458
1555
1459
1556
if (strictSubscriptionMessageAcceptorMethods.size > 0 ) {
@@ -1474,12 +1571,6 @@ object GeneratorPy {
1474
1571
| ${(subscriptionMessageGetters, " \n " )}"""
1475
1572
}
1476
1573
1477
- if (strictSubscriptionHandlerBaseMethods.size > 0 ) {
1478
- fileBody =
1479
- st """ ${fileBody}
1480
- | ${(strictSubscriptionHandlerBaseMethods, " \n " )}"""
1481
- }
1482
-
1483
1574
if (publisherMethods.size > 0 ) {
1484
1575
fileBody =
1485
1576
st """ ${fileBody}
@@ -1488,6 +1579,16 @@ object GeneratorPy {
1488
1579
}
1489
1580
}
1490
1581
1582
+ if (subscriptionHandlerMethods.size > 0 ) {
1583
+ fileBody =
1584
+ st """ ${fileBody}
1585
+ | #=================================================
1586
+ | # C o m p u t e E n t r y P o i n t
1587
+ | #=================================================
1588
+ | ${(subscriptionHandlerMethods, " \n " )}
1589
+ | """
1590
+ }
1591
+
1491
1592
if (strictAADLMode) {
1492
1593
if (! isSporadic(component)) {
1493
1594
fileBody =
@@ -1510,6 +1611,12 @@ object GeneratorPy {
1510
1611
return (filePath, fileBody, T , IS ())
1511
1612
}
1512
1613
1614
+ def genPySubscriptionHandlerAdder (inPort : AadlPort ): ST = {
1615
+ val handlerName = inPort.identifier
1616
+ val subscriptionAdder : ST = st " node.handle_ ${handlerName} = handle_ ${handlerName}"
1617
+ return subscriptionAdder
1618
+ }
1619
+
1513
1620
def genPySubscriptionHandlerSporadicStrict (inPort : AadlPort , portType : String ): ST = {
1514
1621
val handlerName = inPort.identifier
1515
1622
@@ -1565,23 +1672,29 @@ object GeneratorPy {
1565
1672
val endMarker : String = " # Additions within these tags will be preserved when re-running Codegen"
1566
1673
1567
1674
var subscriptionHandlers : ISZ [ST ] = IS ()
1675
+ var subscriptionAdders : ISZ [ST ] = IS ()
1568
1676
if (isSporadic(component)) {
1569
1677
for (p <- component.getPorts()) {
1570
1678
val portDatatype : String = genPortDatatype(p, packageName, datatypeMap, reporter)
1571
1679
if (p.direction == Direction .In && ! p.isInstanceOf [AadlDataPort ]) {
1572
1680
if (strictAADLMode) {
1573
1681
subscriptionHandlers = subscriptionHandlers :+
1574
1682
genPySubscriptionHandlerSporadicStrict(p, portDatatype)
1683
+ subscriptionAdders = subscriptionAdders :+
1684
+ genPySubscriptionHandlerAdder(p)
1575
1685
}
1576
1686
else {
1577
1687
subscriptionHandlers = subscriptionHandlers :+
1578
1688
genPySubscriptionHandlerSporadic(p, portDatatype)
1689
+ subscriptionAdders = subscriptionAdders :+
1690
+ genPySubscriptionHandlerAdder(p)
1579
1691
}
1580
1692
}
1581
1693
}
1582
1694
}
1583
1695
else {
1584
1696
subscriptionHandlers = subscriptionHandlers :+ genPyTimeTriggeredMethod()
1697
+ subscriptionAdders = subscriptionAdders :+ st " node.timeTriggered = timeTriggered "
1585
1698
}
1586
1699
1587
1700
var includeFiles : ST =
@@ -1609,6 +1722,7 @@ object GeneratorPy {
1609
1722
| node.get_logger().info("Initialize Entry Point invoked")
1610
1723
|
1611
1724
| # Initialize the node
1725
+ | ${(subscriptionAdders, " \n " )}
1612
1726
|
1613
1727
|#=================================================
1614
1728
|# C o m p u t e E n t r y P o i n t
@@ -1735,9 +1849,16 @@ object GeneratorPy {
1735
1849
1736
1850
def genPyEnumConverterFile (packageName : String , enumTypes : ISZ [(String , AadlType )],
1737
1851
strictAADLMode : B ): (ISZ [String ], ST , B , ISZ [Marker ]) = {
1852
+ var includes : ISZ [ST ] = IS ()
1853
+
1854
+ for (enum <- enumTypes) {
1855
+ val enumName : String = ops.StringOps (enum ._2.classifier.apply(enum ._2.classifier.size - 1 )).replaceAllLiterally(" _" , " " )
1856
+ includes = includes :+ st " from ${packageName}_interfaces.msg import ${enumName}"
1857
+ }
1858
+
1738
1859
val fileBody =
1739
1860
st """ #!/usr/bin/env python3
1740
- |from datatypes_system_py_pkg_interfaces.msg import MyEnum
1861
+ | ${(includes, " \n " )}
1741
1862
|
1742
1863
|#========================================================
1743
1864
|# Re-running Codegen will overwrite changes to this file
@@ -1807,11 +1928,11 @@ object GeneratorPy {
1807
1928
return files
1808
1929
}
1809
1930
1810
- def genPyLaunchPkg (modelName : String , threadComponents : ISZ [AadlThread ]): ISZ [(ISZ [String ], ST , B , ISZ [Marker ])] = {
1931
+ def genPyLaunchPkg (modelName : String , threadComponents : ISZ [AadlThread ], systemComponents : ISZ [ AadlSystem ] ): ISZ [(ISZ [String ], ST , B , ISZ [Marker ])] = {
1811
1932
var files : ISZ [(ISZ [String ], ST , B , ISZ [Marker ])] = IS ()
1812
1933
files = files :+ genLaunchCMakeListsFile(modelName)
1813
1934
files = files :+ genLaunchPackageFile(modelName)
1814
- files = files :+ genPyFormatLaunchFile(modelName, threadComponents)
1935
+ files = files ++ genPyFormatLaunchFile(modelName, threadComponents, systemComponents )
1815
1936
1816
1937
return files
1817
1938
}
0 commit comments