pythonrosgenerator.py
Go to the documentation of this file.
1 '''
2  Copyright (C) 1997-2017 JDERobot Developers Team
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU Library General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, see <http://www.gnu.org/licenses/>.
16 
17  Authors : Okan Asik (asik.okan@gmail.com)
18 
19  '''
20 import os
21 import shutil
22 from xml.dom import minidom
23 
24 from visualstates.gui.transition.transitiontype import TransitionType
25 from visualstates.configs.rospackage import getPackagePath
26 from visualstates.generators.basegenerator import BaseGenerator
27 
28 
29 class PythonRosGenerator(BaseGenerator):
30  def __init__(self, libraries, config, states, globalNamespace):
31  BaseGenerator.__init__(self, libraries, config, states, globalNamespace)
32 
33  def generate(self, projectPath, projectName):
34  stringList = []
35  self.generateImports(stringList)
36  self.generateSignalHandling(stringList)
37  self.generateGlobalNamespace(stringList, projectName)
38  self.generateStateClasses(stringList)
39  self.generateTransitionClasses(stringList)
40  self.generateMain(stringList)
41  sourceCode = ''.join(stringList)
42  fp = open(projectPath + os.sep + projectName + '.py', 'w')
43  fp.write(sourceCode)
44  fp.close()
45  os.system('chmod +x "' +projectPath+ '"' + os.sep + projectName + '.py')
46 
47  stringList = []
48  self.generateCmake(stringList, projectName)
49  cmakeString = ''.join(stringList)
50  fp = open(projectPath + os.sep + 'CMakeLists.txt', 'w')
51  fp.write(cmakeString)
52  fp.close()
53 
54  xmlDoc = self.generatePackageXml(self.config, projectName)
55  xmlStr = xmlDoc.toprettyxml(indent=' ')
56  with open(projectPath + os.sep + 'package.xml', 'w') as f:
57  f.write(xmlStr)
58 
59  # self.copyRuntime(projectPath)
60 
61  def generateImports(self, importStr):
62  mystr = '''#!/usr/bin/python
63 # -*- coding: utf-8 -*-
64 import sys, threading, time, rospy, signal
65 '''
66  importStr.append(mystr)
67 
68  typeSet = {''} # create set
69  for topic in self.config.getTopics():
70  typeStr = topic['type']
71  if typeStr not in typeSet:
72  if typeStr.find('/') >= 0:
73  types = typeStr.split('/')
74  importStr.append('from ' + types[0] + '.msg import ' + types[1] + '\n')
75  else:
76  importStr.append('import ' + typeStr + '\n')
77  typeSet.add(typeStr)
78 
79  mystr = '''from visualstates.codegen.python.state import State
80 from visualstates.codegen.python.temporaltransition import TemporalTransition
81 from visualstates.codegen.python.conditionaltransition import ConditionalTransition
82 from visualstates.codegen.python.runtimegui import RunTimeGui
83 from PyQt5.QtWidgets import QApplication
84 
85 '''
86  importStr.append(mystr)
87  for lib in self.libraries:
88  importStr.append('import ')
89  importStr.append(lib)
90  importStr.append('\n')
91  importStr.append('\n')
92 
93  return importStr
94 
95  def generateSignalHandling(self, signalStr):
96  rootState = self.getRootState()
97  signalStr.append('globalNamespace = None\n')
98  signalStr.append('state' + str(rootState.id) + ' = None\n')
99  signalStr.append('app = None\n\n')
100  signalStr.append('def stopRecursive(state):\n')
101  signalStr.append('\tstate.stop()\n')
102  signalStr.append('\tfor childState in state.states:\n')
103  signalStr.append('\t\tstopRecursive(childState)\n\n')
104  signalStr.append('def sigintHandler(signal, frame):\n')
105  signalStr.append('\tglobal globalNamespace\n')
106  signalStr.append('\tglobal state' + str(rootState.id) + '\n')
107  signalStr.append('\tglobal app\n')
108  signalStr.append('\tif app is not None:')
109  signalStr.append('\t\tapp.quit()\n')
110  signalStr.append('\tstopRecursive(state' + str(rootState.id) + ')\n')
111  signalStr.append('\tglobalNamespace.stop()\n\n')
112  signalStr.append('signal.signal(signal.SIGINT, sigintHandler)\n\n')
113 
114  def generateStateClasses(self, stateStr):
115  for state in self.getAllStates():
116  self.generateStateClass(state, stateStr)
117 
118  def generateStateClass(self, state, stateStr):
119  stateStr.append('class State')
120  stateStr.append(str(state.id))
121  stateStr.append('(State):\n')
122 
123  stateStr.append('\tdef __init__(self, id, initial, globalNamespace, namespace, cycleDuration, parent=None, gui=None):\n')
124  stateStr.append('\t\tState.__init__(self, id, initial, cycleDuration, parent, gui)\n')
125  stateStr.append('\t\tself.globalNamespace = globalNamespace\n')
126  stateStr.append('\t\tself.namespace = namespace\n\n')
127 
128  stateStr.append('\tdef runCode(self):\n')
129  if len(state.getCode()) > 0:
130  for codeLine in state.getCode().split('\n'):
131  stateStr.append('\t\t' + codeLine + '\n')
132  else:
133  stateStr.append('\t\tpass\n')
134  stateStr.append('\n\n')
135 
136  stateStr.append('class Namespace' + str(state.id) + '():\n')
137  stateStr.append('\tdef __init__(self, globalNamespace):\n')
138  stateStr.append('\t\tself.globalNamespace = globalNamespace\n')
139 
140  if(len(state.namespace.variables) > 0):
141  for varLine in state.namespace.variables.split('\n'):
142  stateStr.append('\t\t' + varLine + '\n')
143  stateStr.append('\n')
144 
145  if(len(state.namespace.functions) > 0):
146  for funcLine in state.namespace.functions.split('\n'):
147  stateStr.append('\t' + funcLine + '\n')
148  stateStr.append('\n')
149 
150  def generateGlobalNamespace(self, globalNamespaceStr, projectName):
151  globalNamespaceStr.append('class GlobalNamespace():\n')
152  globalNamespaceStr.append('\tdef __init__(self):\n')
153  globalNamespaceStr.append('\t\trospy.init_node("' + projectName + '", anonymous=True, disable_signals=True)\n\n')
154  for topic in self.config.getTopics():
155  if topic['opType'] == 'Publish':
156  typesStr = topic['type']
157  types = typesStr.split('/')
158  globalNamespaceStr.append('\t\tself.' + topic['methodname'] + 'Pub = rospy.Publisher("' +
159  topic['name'] + '", ' + types[1] + ', queue_size=10)\n')
160  elif topic['opType'] == 'Subscribe':
161  typesStr = topic['type']
162  types = typesStr.split('/')
163  globalNamespaceStr.append('\t\tself.' + topic['variablename'] + 'Sub = rospy.Subscriber("' +
164  topic['name'] + '", ' + types[1] + ', self.' + topic['variablename'] + 'Callback)\n')
165  globalNamespaceStr.append('\t\tself.' + topic['variablename'] + ' = ' + types[1] + '()\n')
166 
167  # add state variables as part of ros node
168  variables = self.globalNamespace.getVariables()
169  if len(variables) > 0:
170  for varLine in variables.split('\n'):
171  globalNamespaceStr.append('\t\t' + varLine + '\n')
172  globalNamespaceStr.append('\n')
173 
174  globalNamespaceStr.append('\t\ttime.sleep(1) # wait for initialization of the node, subscriber, and publisher\n\n')
175 
176  globalNamespaceStr.append('\tdef stop(self):\n')
177  globalNamespaceStr.append('\t\trospy.signal_shutdown("exit ROS node")\n\n')
178 
179  # define publisher methods and subscriber callbacks
180  for topic in self.config.getTopics():
181  if topic['opType'] == 'Publish':
182  globalNamespaceStr.append('\tdef ' + topic['methodname'] + '(self, _' + topic['methodname'] + '):\n')
183  globalNamespaceStr.append('\t\tself.' + topic['methodname'] + 'Pub.publish(_' + topic['methodname'] + ')\n\n')
184  elif topic['opType'] == 'Subscribe':
185  globalNamespaceStr.append('\tdef ' + topic['variablename'] + 'Callback(self, _' + topic['variablename'] + '):\n')
186  globalNamespaceStr.append('\t\tself.' + topic['variablename'] + ' = _' + topic['variablename'] + '\n')
187  globalNamespaceStr.append('\n\n')
188 
189  # define user functions as part of rosnode
190  functions = self.globalNamespace.getFunctions()
191  if len(functions) > 0:
192  for funcLine in functions.split('\n'):
193  globalNamespaceStr.append('\t' + funcLine + '\n')
194  globalNamespaceStr.append('\n\n')
195 
196 
197  def generateTransitionClasses(self, tranStr):
198  for tran in self.getAllTransitions():
199  if tran.getType() == TransitionType.CONDITIONAL:
200  tranStr.append('class Tran' + str(tran.id) + '(ConditionalTransition):\n')
201  tranStr.append('\tdef __init__(self, id, destinationId, globalNamespace, namespace):\n')
202  tranStr.append('\t\tConditionalTransition.__init__(self, id, destinationId, globalNamespace, namespace)\n')
203  tranStr.append('\tdef checkCondition(self):\n')
204  for checkLine in tran.getCondition().split('\n'):
205  tranStr.append('\t\t' + checkLine + '\n')
206  tranStr.append('\n')
207  elif tran.getType() == TransitionType.TEMPORAL:
208  tranStr.append('class Tran' + str(tran.id) + '(TemporalTransition):\n\n')
209  tranStr.append('\tdef __init__(self, id, destinationId, elapsedTime, globalNamespace, namespace):\n')
210  tranStr.append('\t\tTemporalTransition.__init__(self, id, destinationId, elapsedTime, globalNamespace, namespace)\n')
211  tranStr.append('\tdef runCode(self):\n')
212  if len(tran.getCode()) > 0:
213  for codeLine in tran.getCode().split('\n'):
214  tranStr.append('\t\t' + codeLine + '\n')
215  tranStr.append('\n')
216  else:
217  tranStr.append('\t\tpass\n\n')
218 
219  def generateMain(self, mainStr):
220  mystr = '''displayGui = False
221 guiThread = None
222 gui = None
223 
224 def readArgs():
225 \tglobal displayGui
226 \tfor arg in sys.argv:
227 \t\tsplitedArg = arg.split('=')
228 \t\tif splitedArg[0] == '--displaygui':
229 \t\t\tif splitedArg[1] == 'True' or splitedArg[1] == 'true':
230 \t\t\t\tdisplayGui = True
231 \t\t\t\tprint('runtime gui enabled')
232 \t\t\telse:
233 \t\t\t\tdisplayGui = False
234 \t\t\t\tprint('runtime gui disabled')
235 
236 def runGui():
237 \tglobal app
238 \tglobal gui
239 \tapp = QApplication(sys.argv)
240 \tgui = RunTimeGui()
241 \tgui.show()
242 \tapp.exec_()
243 
244 '''
245  mainStr.append(mystr)
246 
247  mainStr.append('if __name__ == "__main__":\n\n')
248  mainStr.append('\tglobalNamespace = GlobalNamespace()\n\n')
249  mainStr.append('\n')
250  mainStr.append('\treadArgs()\n')
251  mainStr.append('\tif displayGui:\n')
252  mainStr.append('\t\tguiThread = threading.Thread(target=runGui)\n')
253  mainStr.append('\t\tguiThread.start()\n\n')
254 
255  mainStr.append('\n\tif displayGui:\n')
256  mainStr.append('\t\twhile(gui is None):\n')
257  mainStr.append('\t\t\ttime.sleep(0.1)\n\n')
258  # create runtime gui code
259  for state in self.getAllStates():
260  mainStr.append('\t\tgui.addState(' + str(state.id) + ', "' + state.name +
261  '", ' + str(state.initial) + ', ' + str(state.x) + ', ' + str(state.y))
262  if state.parent == None:
263  mainStr.append(', None)\n')
264  else:
265  mainStr.append(', ' + str(state.parent.id) +')\n')
266 
267  mainStr.append('\n')
268 
269  for tran in self.getAllTransitions():
270  mainStr.append('\t\tgui.addTransition(' + str(tran.id) + ', "' + tran.name + '", ' +
271  str(tran.origin.id) + ', ' + str(tran.destination.id) +
272  ', ' + str(tran.x) + ', ' + str(tran.y) + ')\n')
273  mainStr.append('\n')
274 
275  mainStr.append('\tif displayGui:\n')
276  mainStr.append('\t\tgui.emitLoadFromRoot()\n')
277  mainStr.append('\t\tgui.emitActiveStateById(0)\n\n')
278 
279  for state in self.getAllStates():
280  mainStr.append('\tnamespace' + str(state.id) + ' = Namespace' + str(state.id) +'(globalNamespace)\n')
281  mainStr.append('\tstate' + str(state.id) + ' = State' + str(state.id) +
282  '(' + str(state.id) + ', ' + str(state.initial) + ', globalNamespace, ')
283  if state.parent == None:
284  mainStr.append('None, ' + str(state.getTimeStep()) + ', None, gui)\n')
285  else:
286  mainStr.append('namespace' + str(state.parent.id)+ ', ' + str(state.getTimeStep()) + ', state' + str(state.parent.id) + ', gui)\n')
287  mainStr.append('\n')
288 
289  # create and add transitions to their origins
290  for tran in self.getAllTransitions():
291  if tran.getType() == TransitionType.TEMPORAL:
292  mainStr.append('\ttran' + str(tran.id) + ' = Tran' + str(tran.id) +
293  '(' + str(tran.id) + ', ' + str(tran.destination.id) +
294  ', ' + str(tran.getTemporalTime()) + ', globalNamespace, namespace'
295  + str(tran.origin.parent.id) + ')\n')
296  elif tran.getType() == TransitionType.CONDITIONAL:
297  mainStr.append('\ttran' + str(tran.id) + ' = Tran' + str(tran.id) +
298  '(' + str(tran.id) + ', ' + str(tran.destination.id) + ', globalNamespace, namespace' + str(tran.origin.parent.id) + ')\n')
299 
300  mainStr.append('\tstate' + str(tran.origin.id) + '.addTransition(tran' + str(tran.id) + ')\n\n')
301 
302  # start threads
303  for state in self.states:
304  mainStr.append('\tstate' + str(state.id) + '.startThread()\n')
305 
306  mainStr.append('\n')
307  rootState = self.getRootState()
308  mainStr.append('\twhile state' + str(rootState.id) + '.running:\n')
309  mainStr.append('\t\ttime.sleep(0.01)\n\n')
310 
311  def generateCmake(self, cmakeStr, projectName):
312  cmakeStr.append('project(')
313  cmakeStr.append(projectName)
314  cmakeStr.append(')\n\n')
315 
316  cmakeStr.append('cmake_minimum_required(VERSION 2.8.3)\n\n')
317 
318  cmakeStr.append('find_package(catkin REQUIRED COMPONENTS visualstates\n')
319  for dep in self.config.getBuildDependencies():
320  cmakeStr.append(' ' + dep + '\n')
321  cmakeStr.append(')\n\n')
322  cmakeStr.append('catkin_package()\n')
323  cmakeStr.append('include_directories(${catkin_INCLUDE_DIRS})\n')
324  cmakeStr.append('install(PROGRAMS ' + projectName + '.py DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})\n')
325  return cmakeStr
326 
327  def generatePackageXml(self, config, projectName):
328  doc = minidom.Document()
329  root = doc.createElement('package')
330  nameElement = doc.createElement('name')
331  nameElement.appendChild(doc.createTextNode(projectName))
332  root.appendChild(nameElement)
333  versionElement = doc.createElement('version')
334  versionElement.appendChild(doc.createTextNode('0.0.0'))
335  root.appendChild(versionElement)
336  descElement = doc.createElement('description')
337  descElement.appendChild(doc.createTextNode('The ' + projectName + ' package'))
338  root.appendChild(descElement)
339  maintainerElement = doc.createElement('maintainer')
340  maintainerElement.setAttribute('email', 'todo@todo.todo')
341  maintainerElement.appendChild(doc.createTextNode('todo'))
342  root.appendChild(maintainerElement)
343  licenseElement = doc.createElement('license')
344  licenseElement.appendChild(doc.createTextNode('TODO (choose one: BSD, MIT, GPLv2, GPLv3 LGPLv3)'))
345  root.appendChild(licenseElement)
346  btoolDepElement = doc.createElement('buildtool_depend')
347  btoolDepElement.appendChild(doc.createTextNode('catkin'))
348  root.appendChild(btoolDepElement)
349  for bdep in ['visualstates']+config.getBuildDependencies():
350  bdepElement = doc.createElement('build_depend')
351  bdepElement.appendChild(doc.createTextNode(bdep))
352  root.appendChild(bdepElement)
353 
354  for rdep in ['visualstates']+config.getRunDependencies():
355  rdepElement = doc.createElement('run_depend')
356  rdepElement.appendChild(doc.createTextNode(rdep))
357  root.appendChild(rdepElement)
358 
359  # system dependencies
360  rdepElement = doc.createElement('run_depend')
361  rdepElement.appendChild(doc.createTextNode('python-qt5-bindings'))
362  root.appendChild(rdepElement)
363 
364  exportElement = doc.createElement('export')
365  root.appendChild(exportElement)
366  doc.appendChild(root)
367 
368  return doc
369 
370  def copyRuntime(self, projectPath):
371  if os.path.exists(projectPath + '/codegen'):
372  shutil.rmtree(projectPath + '/codegen')
373  if os.path.exists(projectPath + '/gui'):
374  shutil.rmtree(projectPath + '/gui')
375  if os.path.exists(projectPath + '/core'):
376  shutil.rmtree(projectPath + '/core')
377 
378  shutil.copytree(getPackagePath() + '/lib/python2.7/codegen', projectPath + '/codegen')
379  shutil.copytree(getPackagePath() + '/lib/python2.7/gui', projectPath + '/gui')
380  shutil.copytree(getPackagePath() + '/lib/python2.7/core', projectPath + '/core')
def __init__(self, libraries, config, states, globalNamespace)
def generateGlobalNamespace(self, globalNamespaceStr, projectName)


visualstates
Author(s):
autogenerated on Thu Apr 1 2021 02:42:20