From 47fc4bff949b87058b9e044314ac510e116f9d53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Okan=20A=C5=9F=C4=B1k?= Date: Sun, 26 Nov 2017 23:59:03 +0300 Subject: [PATCH] functions and variables are added as part of interfaces and ros nodes --- .../inspectionProfiles/profiles_settings.xml | 7 + src/tools/visualStates_py/.idea/misc.xml | 4 + src/tools/visualStates_py/.idea/modules.xml | 8 + .../visualStates_py/.idea/visualStates_py.iml | 12 + src/tools/visualStates_py/.idea/workspace.xml | 1124 +++++++++++++++++ .../codegen/cpp/runtimegui.cpp | 52 +- .../visualStates_py/codegen/cpp/runtimegui.h | 3 +- .../codegen/cpp/temporaltransition.cpp | 3 +- .../visualStates_py/codegen/cpp/transition.h | 2 + .../codegen/python/runtimegui.py | 45 +- .../codegen/python/temporaltransition.py | 4 +- src/tools/visualStates_py/cpptest | Bin 0 -> 188000 bytes src/tools/visualStates_py/gui/cppgenerator.py | 62 +- src/tools/visualStates_py/gui/cppparser.py | 64 +- .../visualStates_py/gui/cpprosgenerator.py | 58 +- .../visualStates_py/gui/pythongenerator.py | 37 +- .../visualStates_py/gui/pythonrosgenerator.py | 60 +- src/tools/visualStates_py/gui/state.py | 26 +- .../visualStates_py/install_manifest.txt | 30 + .../samples/sample1/CMakeLists.txt | 46 + .../visualStates_py/samples/sample1/sample1 | Bin 0 -> 43384 bytes .../samples/sample1/sample1.cpp | 106 ++ .../visualStates_py/samples/sample1/sample1.h | 58 + .../samples/sample1/sample1.py | 153 +++ .../samples/sample1/sample1.xml | 61 + .../samples/sample1/sample1.yml | 7 + .../samples/sample1/sample1_runtime.py | 31 + .../samples/sample2/CMakeLists.txt | 44 + .../samples/sample2/sample2.cpp | 106 ++ .../visualStates_py/samples/sample2/sample2.h | 58 + .../samples/sample2/sample2.xml | 61 + .../samples/sample2/sample2.yml | 7 + .../samples/sample2/sample2_runtime.py | 31 + src/tools/visualStates_py/visualStates_py | 3 + 34 files changed, 2237 insertions(+), 136 deletions(-) create mode 100644 src/tools/visualStates_py/.idea/inspectionProfiles/profiles_settings.xml create mode 100644 src/tools/visualStates_py/.idea/misc.xml create mode 100644 src/tools/visualStates_py/.idea/modules.xml create mode 100644 src/tools/visualStates_py/.idea/visualStates_py.iml create mode 100644 src/tools/visualStates_py/.idea/workspace.xml create mode 100755 src/tools/visualStates_py/cpptest create mode 100644 src/tools/visualStates_py/install_manifest.txt create mode 100644 src/tools/visualStates_py/samples/sample1/CMakeLists.txt create mode 100755 src/tools/visualStates_py/samples/sample1/sample1 create mode 100644 src/tools/visualStates_py/samples/sample1/sample1.cpp create mode 100644 src/tools/visualStates_py/samples/sample1/sample1.h create mode 100755 src/tools/visualStates_py/samples/sample1/sample1.py create mode 100644 src/tools/visualStates_py/samples/sample1/sample1.xml create mode 100644 src/tools/visualStates_py/samples/sample1/sample1.yml create mode 100755 src/tools/visualStates_py/samples/sample1/sample1_runtime.py create mode 100644 src/tools/visualStates_py/samples/sample2/CMakeLists.txt create mode 100644 src/tools/visualStates_py/samples/sample2/sample2.cpp create mode 100644 src/tools/visualStates_py/samples/sample2/sample2.h create mode 100644 src/tools/visualStates_py/samples/sample2/sample2.xml create mode 100644 src/tools/visualStates_py/samples/sample2/sample2.yml create mode 100755 src/tools/visualStates_py/samples/sample2/sample2_runtime.py create mode 100644 src/tools/visualStates_py/visualStates_py diff --git a/src/tools/visualStates_py/.idea/inspectionProfiles/profiles_settings.xml b/src/tools/visualStates_py/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 000000000..c23ecacb3 --- /dev/null +++ b/src/tools/visualStates_py/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/src/tools/visualStates_py/.idea/misc.xml b/src/tools/visualStates_py/.idea/misc.xml new file mode 100644 index 000000000..5bbe586f1 --- /dev/null +++ b/src/tools/visualStates_py/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/tools/visualStates_py/.idea/modules.xml b/src/tools/visualStates_py/.idea/modules.xml new file mode 100644 index 000000000..f34e1f1dc --- /dev/null +++ b/src/tools/visualStates_py/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/tools/visualStates_py/.idea/visualStates_py.iml b/src/tools/visualStates_py/.idea/visualStates_py.iml new file mode 100644 index 000000000..3f8acbf35 --- /dev/null +++ b/src/tools/visualStates_py/.idea/visualStates_py.iml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/tools/visualStates_py/.idea/workspace.xml b/src/tools/visualStates_py/.idea/workspace.xml new file mode 100644 index 000000000..578bbc8d4 --- /dev/null +++ b/src/tools/visualStates_py/.idea/workspace.xml @@ -0,0 +1,1124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + copyRuntime + self.configs + State + Transition + State.__i + generateInterfaces + comm.ini + #include + destroy + cmake + std::cout + cout + print + print( + print(' + print('t + print('type + interface + interfaceHeaders + package + getFunction + functions + signal + varName[0] + parseFunctions + getVar + runCode + doc.create + getVariables + replace + + + + Okan Asik + + + $PROJECT_DIR$ + + + + + + + + + true + DEFINITION_ORDER + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1499771568074 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/tools/visualStates_py/codegen/cpp/runtimegui.cpp b/src/tools/visualStates_py/codegen/cpp/runtimegui.cpp index 6b3375de0..e6942d4fa 100644 --- a/src/tools/visualStates_py/codegen/cpp/runtimegui.cpp +++ b/src/tools/visualStates_py/codegen/cpp/runtimegui.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -28,14 +29,14 @@ RunTimeGui::RunTimeGui() { // create shared memory - createSharedMem(); + createSharedMemAndSemaphore(); pthread_create(&threadIPC, NULL, &RunTimeGui::loopStaticIPC, this); - ipcData[0] == '0'; + } void RunTimeGui::emitRunningStateById(int id) { std::stringstream strstream; -// strstream << "emitRunningStateById " << id; + strstream << "emitRunningStateById " << id << " "; msgQueue.push(strstream.str()); // std::cout << "running state:" << id << std::endl; } @@ -43,13 +44,13 @@ void RunTimeGui::emitRunningStateById(int id) { void RunTimeGui::emitLoadFromRoot() { std::stringstream strstream; // strstream << "emitLoadFromRoot"; - msgQueue.push(strstream.str()); +// msgQueue.push(strstream.str()); } void RunTimeGui::emitActiveStateById(int id) { std::stringstream strstream; // strstream << "emitActiveStateById " << id; - msgQueue.push(strstream.str()); +// msgQueue.push(strstream.str()); } void* RunTimeGui::loopStaticIPC(void* owner) { @@ -57,20 +58,30 @@ void* RunTimeGui::loopStaticIPC(void* owner) { } void RunTimeGui::loopIPC() { + struct sembuf sb = {0, 0, 0}; /* set to allocate resource */ + int semop_result; while(true) { +// std::cout <<"data:"<< std::endl; +// for (int i = 0; i < 1024; i++) { +// std::cout << ipcData[i]; +// } +// std::cout << ":data" << std::endl; +// std::cout << "msg.size=" << msgQueue.size() << std::endl; if (msgQueue.size() > 0) { - if (ipcData[0] == '0') { - std::string msg = msgQueue.front(); - msgQueue.pop(); - strncpy(ipcData, msg.c_str(), 1024); - } + sb.sem_op = 0; + semop_result = semop(sem_id, &sb, 1); + std::string msg = msgQueue.front(); + std::cout << "sending msg:" << msg << std::endl; + msgQueue.pop(); + strncpy(ipcData, msg.c_str(), 1024); + sb.sem_op = 1; + semop_result = semop(sem_id, &sb, 1); } - - usleep(1000); +// usleep(1000); } } -void RunTimeGui::createSharedMem() { +void RunTimeGui::createSharedMemAndSemaphore() { key_t fkey; int shmid; int mode; @@ -83,6 +94,7 @@ void RunTimeGui::createSharedMem() { } else { break; } + trial++; usleep(100000); // sleep for 100 milliseconds } @@ -94,9 +106,23 @@ void RunTimeGui::createSharedMem() { return; } + trial = 0; + while (trial < numTrial) { + if ((sem_id = semget(9, 1, 0)) == -1) { + std::cerr << "semget error with id 9 try for " << trial << std::endl; + } else { + break; + } + + trial++; + usleep(100000); // sleep for 100 milliseconds + } + ipcData = (char*) shmat(shmid, (void *)0, 0); if (ipcData == (char *)(-1)) { std::cerr << "error shmat" << std::endl; return; } + + } diff --git a/src/tools/visualStates_py/codegen/cpp/runtimegui.h b/src/tools/visualStates_py/codegen/cpp/runtimegui.h index c17fe9519..752cdb9de 100644 --- a/src/tools/visualStates_py/codegen/cpp/runtimegui.h +++ b/src/tools/visualStates_py/codegen/cpp/runtimegui.h @@ -27,10 +27,11 @@ class RunTimeGui { protected: pthread_t threadIPC; + int sem_id; std::queue msgQueue; char* ipcData; - void createSharedMem(); + void createSharedMemAndSemaphore(); public: RunTimeGui(); diff --git a/src/tools/visualStates_py/codegen/cpp/temporaltransition.cpp b/src/tools/visualStates_py/codegen/cpp/temporaltransition.cpp index 29963a58e..3adad6f3a 100644 --- a/src/tools/visualStates_py/codegen/cpp/temporaltransition.cpp +++ b/src/tools/visualStates_py/codegen/cpp/temporaltransition.cpp @@ -20,7 +20,8 @@ #include "temporaltransition.h" #include -TemporalTransition::TemporalTransition(int id, int destinationId, int elapsedTime):Transition(id, destinationId) { +TemporalTransition::TemporalTransition(int id, int destinationId, int elapsedTime): + Transition(id, destinationId) { this->elapsedTime = elapsedTime; } diff --git a/src/tools/visualStates_py/codegen/cpp/transition.h b/src/tools/visualStates_py/codegen/cpp/transition.h index 94263b92a..6dac678ce 100644 --- a/src/tools/visualStates_py/codegen/cpp/transition.h +++ b/src/tools/visualStates_py/codegen/cpp/transition.h @@ -20,6 +20,8 @@ #ifndef TRANSITION_H #define TRANSITION_H +class State; + class Transition { protected: int id; diff --git a/src/tools/visualStates_py/codegen/python/runtimegui.py b/src/tools/visualStates_py/codegen/python/runtimegui.py index d726680a8..481a33719 100644 --- a/src/tools/visualStates_py/codegen/python/runtimegui.py +++ b/src/tools/visualStates_py/codegen/python/runtimegui.py @@ -64,6 +64,7 @@ def __init__(self, parent=None): self.memory = None self.ipcThread = None + self.semaphore = None def createTreeView(self): dockWidget = QDockWidget() @@ -210,37 +211,39 @@ def loopIPC(self): while True: msg = self.getIPCMessage() if msg is not None: - # print('msg received:' + msg) - methodName = msg.split(' ')[0] - id = int(msg.split(' ')[1]) - if methodName == 'emitRunningStateById': - self.emitRunningStateById(id) - else: - print('unknown method name') + if len(msg) > 0: + msg = msg.strip() + methodName = msg.split(' ')[0] + id = int(msg.split(' ')[1]) + if methodName == 'emitRunningStateById': + self.emitRunningStateById(id) + else: + print('unknown method name') - time.sleep(1.0/1000) + # time.sleep(1.0/1000) def activateIPC(self): try: - self.memory = sysv_ipc.SharedMemory(123456, sysv_ipc.IPC_CREX) + self.memory = sysv_ipc.SharedMemory(123456, flags=sysv_ipc.IPC_CREAT, size = 1024) except: - # if memory exists just open shared memory - self.memory = sysv_ipc.SharedMemory(123456) + print('cannot create or open share mem with id 123456') + return + + # create sempahore + try: + self.semaphore = sysv_ipc.Semaphore(9, flags=sysv_ipc.IPC_CREAT, initial_value=0) + except: + print('cannot create or open semaphore with id 9') + self.memory.remove() + return self.ipcThread = Thread(target=self.loopIPC) self.ipcThread.start() def getIPCMessage(self): - if self.memory is not None: - data = self.memory.read().decode() - if data[0] != '0': - self.memory.write('0'.encode()) - i = data.find('\0') - if i != -1: - data = data[:i] - return data - - return None + self.semaphore.acquire() + data = self.memory.read().decode() + return data diff --git a/src/tools/visualStates_py/codegen/python/temporaltransition.py b/src/tools/visualStates_py/codegen/python/temporaltransition.py index 0f3ad0b93..6aed45360 100644 --- a/src/tools/visualStates_py/codegen/python/temporaltransition.py +++ b/src/tools/visualStates_py/codegen/python/temporaltransition.py @@ -20,8 +20,8 @@ from codegen.python.transition import Transition from time import time -class TemporalTransition(Transition, object): - def __init__(self, id, destinationId, elapsedTime): +class TemporalTransition(Transition): + def __init__(self, id, destinationId, elapsedTime): super(TemporalTransition, self).__init__(id, destinationId) # elapsed time in milliseconds self.elapsedTime = elapsedTime diff --git a/src/tools/visualStates_py/cpptest b/src/tools/visualStates_py/cpptest new file mode 100755 index 0000000000000000000000000000000000000000..f8412c220fecc43cdd04c3e3785e89764d30615b GIT binary patch literal 188000 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 z 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 literal 0 HcmV?d00001 diff --git a/src/tools/visualStates_py/gui/cppgenerator.py b/src/tools/visualStates_py/gui/cppgenerator.py index 87adaf7cd..5e46655ff 100644 --- a/src/tools/visualStates_py/gui/cppgenerator.py +++ b/src/tools/visualStates_py/gui/cppgenerator.py @@ -66,7 +66,7 @@ def getAllTransitions(self): def generate(self, projectPath, projectName): stringList = [] self.generateHeaders(stringList, projectName) - self.generateInterfaceClasses(stringList) + self.generateInterfaceClass(stringList) self.generateStateClasses(stringList) self.generateTransitionClasses(stringList) stringList.append('#endif') @@ -79,7 +79,7 @@ def generate(self, projectPath, projectName): self.generateHeadersForCpp(stringList, projectName) self.generateStateMethods(stringList) self.generateTranMethods(stringList) - self.generateProxies(stringList, projectName) + self.generateInterfaceMethods(stringList, projectName) self.generateReadArgs(stringList, projectName) self.generateMain(stringList, projectName) sourceCode = ''.join(stringList) @@ -143,17 +143,12 @@ def generateStateClasses(self, classStr): classStr.append('class State' + str(state.id) + ' : public State {\n') classStr.append('public:\n') classStr.append('\tInterfaces* interfaces;\n') - classStr.append(state.getVariables()) classStr.append('\tState' + str(state.id) + '(int id, bool initial, Interfaces* interfaces, int cycleDuration, State* parent, RunTimeGui* gui):\n') classStr.append('\t\tState(id, initial, cycleDuration, parent, gui) {this->interfaces = interfaces;}\n') classStr.append('\tvirtual void runCode();\n') - - returnTypes, funcNames, codes = CPPParser.parseFunctions(state.getFunctions()) - for i in range(len(returnTypes)): - classStr.append('\t' + returnTypes[i] + ' ' + funcNames[i] + ';\n') - classStr.append('};\n\n') + def generateTransitionClasses(self, classStr): for tran in self.getAllTransitions(): if tran.getType() == TransitionType.CONDITIONAL: @@ -169,11 +164,13 @@ def generateTransitionClasses(self, classStr): elif tran.getType() == TransitionType.TEMPORAL: classStr.append('class Tran' + str(tran.id) + ' : public TemporalTransition {\n') classStr.append('\tpublic:\n') - classStr.append('\tTran' + str(tran.id) + '(int id, int destId, int elapsedTime):TemporalTransition(id, destId, elapsedTime) {}\n') + classStr.append('\tTran' + str(tran.id) + '(int id, int destId, int elapsedTime):\n') + classStr.append('TemporalTransition(id, destId, elapsedTime) {}\n') classStr.append('\tvirtual void runCode();\n') classStr.append('};\n\n') - def generateInterfaceClasses(self, classStr): + + def generateInterfaceClass(self, classStr): classStr.append('class Interfaces {\n') classStr.append('public:\n') classStr.append('\tComm::Communicator* jdrc;\n') @@ -182,8 +179,18 @@ def generateInterfaceClasses(self, classStr): classStr.append('\n') classStr.append('\tvirtual void connectProxies(int argc, char* argv[]);\n') classStr.append('\tvirtual void destroyProxies();\n') + for state in self.getAllStates(): + # define variables + types, varNames, initialValues = CPPParser.parseVariables(state.getVariables()) + for i in range(len(types)): + classStr.append('\t' + types[i] + ' ' + varNames[i] + ';\n') + classStr.append('\n') + returnTypes, funcNames, codes = CPPParser.parseFunctions(state.getFunctions()) + for i in range(len(returnTypes)): + classStr.append('\t' + returnTypes[i] + ' ' + funcNames[i] + ';\n') classStr.append('};\n\n') + def generateHeadersForCpp(self, headerStr, projectName): headerStr.append('#include "' + projectName + '.h"\n') headerStr.append('#include \n') @@ -197,11 +204,6 @@ def generateStateMethods(self, stateStr): stateStr.append('\t' + codeLine + '\n') stateStr.append('}\n\n') - returnTypes, funcNames, codes = CPPParser.parseFunctions(state.getFunctions()) - for i in range(len(returnTypes)): - stateStr.append(returnTypes[i] + ' State' + str(state.id) + '::' + funcNames[i] + '\n') - stateStr.append(codes[i]) - stateStr.append('\n\n') def generateTranMethods(self, tranStr): for tran in self.getAllTransitions(): @@ -209,7 +211,7 @@ def generateTranMethods(self, tranStr): #todo: currently user does not provide init method tranStr.append('void Tran' + str(tran.id) + '::init() {\n') tranStr.append('}\n\n') - tranStr.append('void Tran' + str(tran.id) + '::checkCondition() {\n') + tranStr.append('bool Tran' + str(tran.id) + '::checkCondition() {\n') for codeLine in tran.getCondition().split('\n'): tranStr.append('\t' + codeLine + '\n') tranStr.append('}\n') @@ -220,7 +222,7 @@ def generateTranMethods(self, tranStr): tranStr.append('}\n\n') - def generateProxies(self, proxyStr, projectName): + def generateInterfaceMethods(self, proxyStr, projectName): proxyStr.append('void Interfaces::connectProxies(int argc, char* argv[]) {\n') proxyStr.append('\tConfig::Properties props = Config::load(argc, argv);\n') proxyStr.append('\tjdrc = new Comm::Communicator(props);\n\n') @@ -229,13 +231,29 @@ def generateProxies(self, proxyStr, projectName): proxyStr.append('\tif (' + cfg['name'] + ' == NULL) {\n') proxyStr.append('\t\tthrow "invalid proxy ' + cfg['name'] + '";\n') proxyStr.append('\t}\n') - proxyStr.append('\tstd::cout << "' + cfg['name'] + ' is connected" << std::endl;\n') + proxyStr.append('\tstd::cout << "' + cfg['name'] + ' is connected" << std::endl;\n\n') + + # set inital values of variables + for state in self.getAllStates(): + types, varNames, initialValues = CPPParser.parseVariables(state.getVariables()) + for i in range(len(types)): + if initialValues[i] is not None: + proxyStr.append('\t' + varNames[i] + ' = ' + initialValues[i] + ';\n') + proxyStr.append('}\n\n') proxyStr.append('void Interfaces::destroyProxies() {\n') proxyStr.append('\tif (jdrc != 0) {\n') proxyStr.append('\t}\n}\n\n') + for state in self.getAllStates(): + returnTypes, funcNames, codes = CPPParser.parseFunctions(state.getFunctions()) + for i in range(len(returnTypes)): + proxyStr.append(returnTypes[i] + ' Interfaces::' + funcNames[i] + '\n') + proxyStr.append(codes[i]) + proxyStr.append('\n\n') + + def generateReadArgs(self, argStr, projectName): mystr = ''' pthread_t guiThread; @@ -308,11 +326,11 @@ def generateMain(self, mainStr, projectName): # create transition instances for tran in self.getAllTransitions(): if tran.getType() == TransitionType.CONDITIONAL: - mainStr.append('\tTransition* tran' + str(tran.id) + ' = new Tran' + str(tran.id) + '(' + str(tran.id) + - ', ' + str(tran.destination.id) + ', &interfaces);\n') + mainStr.append('\tTransition* tran' + str(tran.id) + ' = new Tran' + str(tran.id) + + '(' + str(tran.id) + ', ' + str(tran.destination.id) + ', &interfaces);\n') elif tran.getType() == TransitionType.TEMPORAL: - mainStr.append('\tTransition* tran' + str(tran.id) + ' = new Tran' + str(tran.id) + '(' + str(tran.id) + - ', ' + str(tran.destination.id) + ', ' + str(tran.getTemporalTime()) + ');\n') + mainStr.append('\tTransition* tran' + str(tran.id) + ' = new Tran' + str(tran.id) + + '(' + str(tran.id) + ', ' + str(tran.destination.id) + ', ' + str(tran.getTemporalTime()) + ');\n') mainStr.append('\tstate' + str(tran.origin.id) + '->addTransition(tran' + str(tran.id) + ');\n') mainStr.append('\n') diff --git a/src/tools/visualStates_py/gui/cppparser.py b/src/tools/visualStates_py/gui/cppparser.py index 90fd395f4..f3c20f302 100644 --- a/src/tools/visualStates_py/gui/cppparser.py +++ b/src/tools/visualStates_py/gui/cppparser.py @@ -42,11 +42,11 @@ def parseFunctions(funcStr): firstCurlyIndex = None lastCurlyIndex = None for i, ch in enumerate(funcStr): - if ch == '{': curlyCounter += 1 - firstCurlyFound = True - firstCurlyIndex = i + if not firstCurlyFound: + firstCurlyFound = True + firstCurlyIndex = i elif ch == '}': curlyCounter -= 1 @@ -70,6 +70,34 @@ def parseFunctions(funcStr): return returnTypes, funcNames, codes + @staticmethod + def parseVariables(variableStr): + types = [] + varNames = [] + initialValues = [] + variableStr = variableStr.strip() + variableLines = variableStr.split(';') + for varLine in variableLines: + varLine = varLine.strip() + if len(varLine) == 0: + continue + + varType = varLine[0:varLine.find(' ')] + varName = None + initalValue = None + if varLine.find('=') >= 0: + # if there is initial value + varName = varLine[varLine.find(' ')+1:varLine.find('=')].strip() + initialValue = varLine[varLine.find('=')+1:].strip() + else: + varName = varLine[varLine.find(' ')+1:].strip() + + types.append(varType) + varNames.append(varName) + initialValues.append(initialValue) + + return types, varNames, initialValues + if __name__ == '__main__': sampleCode = ''' @@ -83,6 +111,9 @@ def parseFunctions(funcStr): c = 10; d = 12; a = c*d*b; + if ( a == 2) { + b = 3; + } return a; } @@ -93,12 +124,27 @@ def parseFunctions(funcStr): int b = 324; int c = 0; c = a + b; + for (int i = 0; i < 10; i++) { + c = a + b; + } } ''' - returnTypes, funcNames, codes = CPPParser.parseFunctions(sampleCode) - for i in range(len(returnTypes)): - print(returnTypes[i]) - print(funcNames[i]) - print(codes[i]) - # print(returnType, funcName) \ No newline at end of file + # returnTypes, funcNames, codes = CPPParser.parseFunctions(sampleCode) + # for i in range(len(returnTypes)): + # print(returnTypes[i]) + # print(funcNames[i]) + # print(codes[i]) + # print(returnType, funcName) + + sampleVariables = ''' + int a = 12; int b = 23; + float myVar; float myVar2 = 12.2; + ''' + + types, varNames, initialValues = CPPParser.parseVariables(sampleVariables) + for i in range(len(types)): + print(types[i]) + print(varNames[i]) + print(initialValues[i]) + diff --git a/src/tools/visualStates_py/gui/cpprosgenerator.py b/src/tools/visualStates_py/gui/cpprosgenerator.py index 7aa01f99b..7ae7f3b3c 100644 --- a/src/tools/visualStates_py/gui/cpprosgenerator.py +++ b/src/tools/visualStates_py/gui/cpprosgenerator.py @@ -29,6 +29,10 @@ def __init__(self, libraries, config, interfaceHeaders, states): CppGenerator.__init__(self, libraries, config, interfaceHeaders, states) def generate(self, projectPath, projectName): + # create source dir if not exists + if not os.path.exists(projectPath + os.sep + 'src'): + os.makedirs(projectPath + os.sep + 'src') + stringList = [] self.generateHeaders(stringList, projectName) self.generateRosNodeClass(stringList, self.config) @@ -49,10 +53,6 @@ def generate(self, projectPath, projectName): self.generateMain(stringList, projectName) sourceCode = ''.join(stringList) - # create source dir if not exists - if not os.path.exists(projectPath + os.sep + 'src'): - os.makedirs(projectPath + os.sep + 'src') - fp = open(projectPath + os.sep + 'src' + os.sep + projectName + '.cpp', 'w') fp.write(sourceCode) fp.close() @@ -112,7 +112,9 @@ def generateRosNodeClass(self, classStr, config): classStr.append('\tpthread_t thread;\n\n') for topic in config.getTopics(): - varName = topic['name'].replace('/', '-') + varName = topic['name'].replace('/', '_') + if varName[0] == '_': + varName = varName[1:] if topic['opType'] == 'Publish': classStr.append('\tros::Publisher ' + varName + 'Pub;\n') @@ -136,7 +138,9 @@ def generateRosNodeClass(self, classStr, config): classStr.append('\tvoid join();\n\n') for topic in config.getTopics(): - varName = topic['name'].replace('/', '-') + varName = topic['name'].replace('/', '_') + if varName[0] == '_': + varName = varName[1:] if topic['opType'] == 'Subscribe': type = topic['type'] @@ -153,6 +157,17 @@ def generateRosNodeClass(self, classStr, config): else: classStr.append('\tvoid publish' + varName + '(' + type + '& ' + varName + ');\n') classStr.append('\n') + + for state in self.getAllStates(): + # define variables + types, varNames, initialValues = CPPParser.parseVariables(state.getVariables()) + for i in range(len(types)): + classStr.append('\t' + types[i] + ' ' + varNames[i] + ';\n') + classStr.append('\n') + returnTypes, funcNames, codes = CPPParser.parseFunctions(state.getFunctions()) + for i in range(len(returnTypes)): + classStr.append('\t' + returnTypes[i] + ' ' + funcNames[i] + ';\n') + classStr.append('};\n\n') def generateStateClasses(self, classStr): @@ -160,15 +175,10 @@ def generateStateClasses(self, classStr): classStr.append('class State' + str(state.id) + ' : public State {\n') classStr.append('public:\n') classStr.append('\tRosNode* node;\n') - classStr.append(state.getVariables()) classStr.append('\tState' + str(state.id) + '(int id, bool initial, RosNode* node, int cycleDuration, State* parent, RunTimeGui* gui):\n') classStr.append('\t\tState(id, initial, cycleDuration, parent, gui) {this->node = node;}\n') classStr.append('\tvirtual void runCode();\n') - returnTypes, funcNames, codes = CPPParser.parseFunctions(state.getFunctions()) - for i in range(len(returnTypes)): - classStr.append('\t' + returnTypes[i] + ' ' + funcNames[i] + ';\n') - classStr.append('};\n\n') def generateTransitionClasses(self, classStr): @@ -201,7 +211,10 @@ def generateHeadersForCpp(self, headerStr, projectName): def generateRosMethods(self, rosStr, config): rosStr.append('RosNode::RosNode(int nodeRate):rate(nodeRate) {\n') for topic in config.getTopics(): - varName = topic['name'].replace('/', '-') + varName = topic['name'].replace('/', '_') + if varName[0] == '_': + varName = varName[1:] + type = topic['type'] types = type.split('/') if topic['opType'] == 'Publish': @@ -212,6 +225,14 @@ def generateRosMethods(self, rosStr, config): 'name'] + '", 10);\n') elif topic['opType'] == 'Subscribe': rosStr.append('\t' + varName + 'Sub = nh.subscribe("' + topic['name'] + '", 10, &RosNode::'+varName+'Callback, this);\n') + + # set inital values of variables + for state in self.getAllStates(): + types, varNames, initialValues = CPPParser.parseVariables(state.getVariables()) + for i in range(len(types)): + if initialValues[i] is not None: + rosStr.append('\t' + varNames[i] + ' = ' + initialValues[i] + ';\n') + rosStr.append('}\n\n') rosStr.append('void* RosNode::threadRunner(void* owner) {\n') @@ -234,7 +255,10 @@ def generateRosMethods(self, rosStr, config): rosStr.append('}\n\n') for topic in config.getTopics(): - varName = topic['name'].replace('/', '-') + varName = topic['name'].replace('/', '_') + if varName[0] == '_': + varName = varName[1:] + if topic['opType'] == 'Subscribe': type = topic['type'] types = type.split('/') @@ -263,6 +287,12 @@ def generateRosMethods(self, rosStr, config): rosStr.append('\t' + varName + 'Pub.publish(' + varName + ');\n') rosStr.append('}\n\n') + for state in self.getAllStates(): + returnTypes, funcNames, codes = CPPParser.parseFunctions(state.getFunctions()) + for i in range(len(returnTypes)): + rosStr.append(returnTypes[i] + ' RosNode::' + funcNames[i] + '\n') + rosStr.append(codes[i]) + rosStr.append('\n\n') def generateReadArgs(self, argStr, projectName): @@ -350,7 +380,7 @@ def generateMain(self, mainStr, projectName): mainStr.append('\tstate' + str(state.id) + '->startThread();\n') mainStr.append('\n') - mainStr.append('signal(SIGINT, signalCallback);\n') + mainStr.append('\tsignal(SIGINT, signalCallback);\n') for state in self.states: mainStr.append('\tstate' + str(state.id) + '->join();\n') diff --git a/src/tools/visualStates_py/gui/pythongenerator.py b/src/tools/visualStates_py/gui/pythongenerator.py index 763a10b14..f0025de4c 100644 --- a/src/tools/visualStates_py/gui/pythongenerator.py +++ b/src/tools/visualStates_py/gui/pythongenerator.py @@ -74,9 +74,6 @@ def generate(self, projectPath, projectName): fp.close() self.generateAndSaveCfgYaml(projectPath, projectName) - #fp = open(projectPath + os.sep + projectName + '.cfg', 'w') - #fp.write(''.join(stringList)) - #fp.close() os.system('chmod +x ' + projectPath + os.sep + projectName + '.py') @@ -108,8 +105,6 @@ def generateImports(self, headerStr): # headerStr.append(cfg['interface']) # headerStr.append('Prx\n') - headerStr.append('import comm\n') - headerStr.append('\n') return headerStr @@ -125,13 +120,7 @@ def generateStateClass(self, state, stateStr): stateStr.append('\tdef __init__(self, id, initial, interfaces, cycleDuration, parent=None, gui=None):\n') stateStr.append('\t\tState.__init__(self, id, initial, cycleDuration, parent, gui)\n') - stateStr.append('\t\tself.interfaces = interfaces\n') - - if len(state.getVariables()) > 0: - for varLine in state.getVariables().split('\n'): - stateStr.append('\t\t' + varLine + '\n') - stateStr.append('\n') - stateStr.append('\n') + stateStr.append('\t\tself.interfaces = interfaces\n\n') stateStr.append('\tdef runCode(self):\n') if len(state.getCode()) > 0: @@ -141,10 +130,6 @@ def generateStateClass(self, state, stateStr): stateStr.append('\t\tpass\n') stateStr.append('\n') - if len(state.getFunctions()) > 0: - for funcLine in state.getFunctions().split('\n'): - stateStr.append('\t' + funcLine + '\n') - stateStr.append('\n') def generateInterfaces(self, interfaceStr, projectName): mystr = '''class Interfaces(): @@ -155,6 +140,15 @@ def generateInterfaces(self, interfaceStr, projectName): for cfg in self.config.getInterfaces(): interfaceStr.append('\t\tself.' + cfg['name'] + ' = None\n') + for state in self.getAllStates(): + if len(state.getVariables()) > 0: + for varLine in state.getVariables().split('\n'): + varLine = varLine.strip() + if len(varLine) > 0: + interfaceStr.append('\t\t' + varLine + '\n') + + interfaceStr.append('\n') + interfaceStr.append('\t\tself.connectProxies()\n\n') interfaceStr.append('\tdef connectProxies(self):\n') @@ -173,20 +167,25 @@ def generateInterfaces(self, interfaceStr, projectName): interfaceStr.append('\t\tif self.jdrc is not None:\n') interfaceStr.append('\t\t\tself.jdrc.destroy()\n\n') + for state in self.getAllStates(): + if len(state.getFunctions()) > 0: + for funcLine in state.getFunctions().split('\n'): + interfaceStr.append('\t' + funcLine + '\n') + interfaceStr.append('\n') + def generateTransitionClasses(self, tranStr): for tran in self.getAllTransitions(): if tran.getType() == TransitionType.CONDITIONAL: tranStr.append('class Tran' + str(tran.id) + '(ConditionalTransition):\n') - tranStr.append('\tdef __init__(self, id, destinationId, interfaces)\n') + tranStr.append('\tdef __init__(self, id, destinationId, interfaces):\n') tranStr.append('\t\tConditionalTransition.__init__(self, id, destinationId)\n') tranStr.append('\t\tself.interfaces = interfaces\n\n') tranStr.append('\tdef checkCondition(self):\n') for checkLine in tran.getCondition().split('\n'): - tranStr.append('\t\treturn ' + checkLine + '\n') + tranStr.append('\t\t' + checkLine + '\n') tranStr.append('\n') elif tran.getType() == TransitionType.TEMPORAL: tranStr.append('class Tran' + str(tran.id) + '(TemporalTransition):\n\n') - tranStr.append('\tdef runCode(self):\n') if len(tran.getCode()) > 0: for codeLine in tran.getCode().split('\n'): diff --git a/src/tools/visualStates_py/gui/pythonrosgenerator.py b/src/tools/visualStates_py/gui/pythonrosgenerator.py index 84d88ce1d..d417a124d 100644 --- a/src/tools/visualStates_py/gui/pythonrosgenerator.py +++ b/src/tools/visualStates_py/gui/pythonrosgenerator.py @@ -134,13 +134,7 @@ def generateStateClass(self, state, stateStr): stateStr.append('\tdef __init__(self, id, initial, rosNode, cycleDuration, parent=None, gui=None):\n') stateStr.append('\t\tState.__init__(self, id, initial, cycleDuration, parent, gui)\n') - stateStr.append('\t\tself.rosNode = rosNode\n') - - if len(state.getVariables()) > 0: - for varLine in state.getVariables().split('\n'): - stateStr.append('\t\t' + varLine + '\n') - stateStr.append('\n') - stateStr.append('\n') + stateStr.append('\t\tself.rosNode = rosNode\n\n') stateStr.append('\tdef runCode(self):\n') if len(state.getCode()) > 0: @@ -148,12 +142,7 @@ def generateStateClass(self, state, stateStr): stateStr.append('\t\t' + codeLine + '\n') else: stateStr.append('\t\tpass\n') - stateStr.append('\n') - - if len(state.getFunctions()) > 0: - for funcLine in state.getFunctions().split('\n'): - stateStr.append('\t' + funcLine + '\n') - stateStr.append('\n') + stateStr.append('\n\n') def generateRosInterface(self, rosNodeStr, projectName): rosNodeStr.append('class RosNode():\n') @@ -163,14 +152,22 @@ def generateRosInterface(self, rosNodeStr, projectName): if topic['opType'] == 'Publish': typesStr = topic['type'] types = typesStr.split('/') - rosNodeStr.append('\t\tself.' + topic['name'] + 'Pub = rospy.Publisher("' + + rosNodeStr.append('\t\tself.' + self.getVarName(topic['name']) + 'Pub = rospy.Publisher("' + topic['name'] + '", ' + types[1] + ', queue_size=10)\n') elif topic['opType'] == 'Subscribe': typesStr = topic['type'] types = typesStr.split('/') - rosNodeStr.append('\t\tself.' + topic['name'] + 'Sub = rospy.Subscriber("' + - topic['name'] + '", ' + types[1] + ', self.'+topic['name']+'Callback)\n') - rosNodeStr.append('\t\tself.' + topic['name'] + ' = ' + types[1] + '()\n') + rosNodeStr.append('\t\tself.' + self.getVarName(topic['name']) + 'Sub = rospy.Subscriber("' + + topic['name'] + '", ' + types[1] + ', self.'+self.getVarName(topic['name'])+'Callback)\n') + rosNodeStr.append('\t\tself.' + self.getVarName(topic['name']) + ' = ' + types[1] + '()\n') + + # add state variables as part of ros node + for state in self.getAllStates(): + if len(state.getVariables()) > 0: + for varLine in state.getVariables().split('\n'): + rosNodeStr.append('\t\t' + varLine + '\n') + rosNodeStr.append('\n') + rosNodeStr.append('\t\ttime.sleep(1) # wait for initialization of the node, subscriber, and publisher\n\n') rosNodeStr.append('\tdef stop(self):\n') @@ -179,13 +176,21 @@ def generateRosInterface(self, rosNodeStr, projectName): # define publisher methods and subscriber callbacks for topic in self.config.getTopics(): if topic['opType'] == 'Publish': - rosNodeStr.append('\tdef publish' + topic['name'] + '(self, ' + topic['name'] + '):\n') - rosNodeStr.append('\t\tself.' + topic['name'] + 'Pub.publish(' + topic['name'] + ')\n\n') + rosNodeStr.append('\tdef publish' + self.getVarName(topic['name']) + '(self, ' + self.getVarName(topic['name']) + '):\n') + rosNodeStr.append('\t\tself.' + self.getVarName(topic['name']) + 'Pub.publish(' + self.getVarName(topic['name']) + ')\n\n') elif topic['opType'] == 'Subscribe': - rosNodeStr.append('\tdef ' + topic['name'] + 'Callback(self, ' + topic['name'] + '):\n') - rosNodeStr.append('\t\tself.' + topic['name'] + ' = ' + topic['name'] + '\n') + rosNodeStr.append('\tdef ' + self.getVarName(topic['name']) + 'Callback(self, ' + self.getVarName(topic['name']) + '):\n') + rosNodeStr.append('\t\tself.' + self.getVarName(topic['name']) + ' = ' + self.getVarName(topic['name']) + '\n') rosNodeStr.append('\n\n') + # define user functions as part of rosnode + for state in self.getAllStates(): + if len(state.getFunctions()) > 0: + for funcLine in state.getFunctions().split('\n'): + rosNodeStr.append('\t' + funcLine + '\n') + rosNodeStr.append('\n\n') + + def generateTransitionClasses(self, tranStr): for tran in self.getAllTransitions(): if tran.getType() == TransitionType.CONDITIONAL: @@ -195,7 +200,7 @@ def generateTransitionClasses(self, tranStr): tranStr.append('\t\tself.rosNode = rosNode\n\n') tranStr.append('\tdef checkCondition(self):\n') for checkLine in tran.getCondition().split('\n'): - tranStr.append('\t\treturn ' + checkLine + '\n') + tranStr.append('\t\t' + checkLine + '\n') tranStr.append('\n') elif tran.getType() == TransitionType.TEMPORAL: tranStr.append('class Tran' + str(tran.id) + '(TemporalTransition):\n\n') @@ -366,8 +371,15 @@ def generatePackageXml(self, config, projectName): def copyRuntime(self, projectPath): if os.path.exists(projectPath + '/codegen'): shutil.rmtree(projectPath + '/codegen') - if os.path.exists(projectPath + 'gui'): + if os.path.exists(projectPath + '/gui'): shutil.rmtree(projectPath + '/gui') shutil.copytree(CMAKE_INSTALL_PREFIX + '/lib/python2.7/visualStates_py/codegen', projectPath + '/codegen') - shutil.copytree(CMAKE_INSTALL_PREFIX + '/lib/python2.7/visualStates_py/gui', projectPath + '/gui') \ No newline at end of file + shutil.copytree(CMAKE_INSTALL_PREFIX + '/lib/python2.7/visualStates_py/gui', projectPath + '/gui') + + + def getVarName(self, varName): + varName = varName.replace('/', '_') + if varName[0] == '_': + varName = varName[1:] + return varName \ No newline at end of file diff --git a/src/tools/visualStates_py/gui/state.py b/src/tools/visualStates_py/gui/state.py index 1045b3eee..54382b1f4 100644 --- a/src/tools/visualStates_py/gui/state.py +++ b/src/tools/visualStates_py/gui/state.py @@ -100,8 +100,15 @@ def getNewCopy(self): copy.y = self.y return copy - def parse(self, stateElement): + def parseElement(self, elementName, parentElement): + elements = parentElement.getElementsByTagName(elementName) + if len(elements) > 0: + if len(elements[0].childNodes) > 0: + return elements[0].childNodes[0].nodeValue + return '' + + def parse(self, stateElement): # parse attributes of the state for (name, value) in stateElement.attributes.items(): if name == 'id': @@ -114,15 +121,10 @@ def parse(self, stateElement): self.x = float(stateElement.getElementsByTagName('posx')[0].childNodes[0].nodeValue) self.y = float(stateElement.getElementsByTagName('posy')[0].childNodes[0].nodeValue) - # optinal state tags - if len(stateElement.getElementsByTagName('code')[0].childNodes) > 0: - self.code = stateElement.getElementsByTagName('code')[0].childNodes[0].nodeValue - - if len(stateElement.getElementsByTagName('functions')[0].childNodes) > 0: - self.functions = stateElement.getElementsByTagName('functions')[0].childNodes[0].nodeValue - - if len(stateElement.getElementsByTagName('timestep')[0].childNodes) > 0: - self.timeStepDuration = int(stateElement.getElementsByTagName('timestep')[0].childNodes[0].nodeValue) + self.code = self.parseElement('code', stateElement) + self.functions = self.parseElement('functions', stateElement) + self.variables = self.parseElement('variables', stateElement) + self.timeStepDuration = int((self.parseElement('timestep', stateElement))) # recursive child state parsing allChildTransitions = [] @@ -148,6 +150,7 @@ def parse(self, stateElement): # return transitions of the state to be able to wire after all states are created return stateTransitions + def createElement(self, doc, parentElement=None): stateElement = doc.createElement('state') stateElement.setAttribute('initial', str(self.initial)) @@ -167,6 +170,9 @@ def createElement(self, doc, parentElement=None): functionsElement = doc.createElement('functions') functionsElement.appendChild(doc.createTextNode(self.functions)) stateElement.appendChild(functionsElement) + varElement = doc.createElement('variables') + varElement.appendChild(doc.createTextNode(self.variables)) + stateElement.appendChild(varElement) timeElement = doc.createElement('timestep') timeElement.appendChild(doc.createTextNode(str(self.timeStepDuration))) stateElement.appendChild(timeElement) diff --git a/src/tools/visualStates_py/install_manifest.txt b/src/tools/visualStates_py/install_manifest.txt new file mode 100644 index 000000000..344a62917 --- /dev/null +++ b/src/tools/visualStates_py/install_manifest.txt @@ -0,0 +1,30 @@ +/opt/jderobot/lib/visualstates_py/libvisualStatesRunTime.so +/opt/jderobot/include/visualstates_py//runtimegui.h +/opt/jderobot/include/visualstates_py//test.h +/opt/jderobot/include/visualstates_py//transition.h +/opt/jderobot/include/visualstates_py//conditionaltransition.h +/opt/jderobot/include/visualstates_py//temporaltransition.h +/opt/jderobot/include/visualstates_py//state.h +/opt/jderobot/lib/python2.7/visualStates_py/codegen/python//conditionaltransition.py +/opt/jderobot/lib/python2.7/visualStates_py/codegen/python//transition.py +/opt/jderobot/lib/python2.7/visualStates_py/codegen/python//state.py +/opt/jderobot/lib/python2.7/visualStates_py/codegen/python//test.py +/opt/jderobot/lib/python2.7/visualStates_py/codegen/python//temporaltransition.py +/opt/jderobot/lib/python2.7/visualStates_py/codegen/python//__init__.py +/opt/jderobot/lib/python2.7/visualStates_py/codegen/python//testipc.py +/opt/jderobot/lib/python2.7/visualStates_py/codegen/python//runtimegui.py +/opt/jderobot/lib/python2.7/visualStates_py/gui/state.py +/opt/jderobot/lib/python2.7/visualStates_py/gui/transition.py +/opt/jderobot/lib/python2.7/visualStates_py/gui/guitransition.py +/opt/jderobot/lib/python2.7/visualStates_py/gui/guistate.py +/opt/jderobot/lib/python2.7/visualStates_py/gui/idtextboxgraphicsitem.py +/opt/jderobot/lib/python2.7/visualStates_py/gui/recthandlegraphicsitem.py +/opt/jderobot/lib/python2.7/visualStates_py/gui/transitiontype.py +/opt/jderobot/lib/python2.7/visualStates_py/gui/treemodel.py +/opt/jderobot/lib/python2.7/visualStates_py/gui/treenode.py +/opt/jderobot/lib/python2.7/visualStates_py/gui/cmakevars.py +/opt/jderobot/lib/python2.7/visualStates_py/__init__.py +/opt/jderobot/lib/python2.7/visualStates_py/codegen/__init__.py +/opt/jderobot/lib/python2.7/visualStates_py/codegen/python/__init__.py +/opt/jderobot/lib/python2.7/visualStates_py/gui/__init__.py +/opt/jderobot/bin/getinterfaces.sh \ No newline at end of file diff --git a/src/tools/visualStates_py/samples/sample1/CMakeLists.txt b/src/tools/visualStates_py/samples/sample1/CMakeLists.txt new file mode 100644 index 000000000..6a36833d7 --- /dev/null +++ b/src/tools/visualStates_py/samples/sample1/CMakeLists.txt @@ -0,0 +1,46 @@ +project(sample1) + +cmake_minimum_required(VERSION 2.8) + +SET(SOURCE_FILES + sample1.cpp) + +SET(JDEROBOT_INSTALL_PATH /opt/jderobot) + + +SET(JDEROBOT_INCLUDE_DIR ${JDEROBOT_INSTALL_PATH}/include) +SET(VISUALSTATE_RUNTIME_INCLUDE_DIR ${JDEROBOT_INSTALL_PATH}/include/visualstates_py) + +SET(JDEROBOT_LIBS_DIR ${JDEROBOT_INSTALL_PATH}/lib) +SET(VISUALSTATE_RUNTIME_LIBS_DIR ${JDEROBOT_INSTALL_PATH}/lib/visualstates_py) + +SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=gnu++0x") + +include_directories( + ${JDEROBOT_INCLUDE_DIR} + ${VISUALSTATE_RUNTIME_INCLUDE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} +) + +link_directories( + ${JDEROBOT_LIBS_DIR} + ${VISUALSTATE_RUNTIME_LIBS_DIR} +) + +add_executable(sample1 ${SOURCE_FILES}) + +target_link_libraries( sample1 + visualStatesRunTime + comm + config + JderobotInterfaces + jderobotutil + colorspacesmm + pthread + Ice + IceUtil + IceStorm + glog +) diff --git a/src/tools/visualStates_py/samples/sample1/sample1 b/src/tools/visualStates_py/samples/sample1/sample1 new file mode 100755 index 0000000000000000000000000000000000000000..d7f71683553c25b87c861056b3c94352c0b54149 GIT binary patch literal 43384 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

BRSL$U}UgBT`}f!%r9FRC`Os!1So&G%ka#Uc*&= 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%0D!*%ap`@Y5W7dd51JQ!CT;L=h-R{g>K|kKU zPXTa}&%Q#*A8H`n1Qf=~zTr)RKi-42)C)SB_>2A~_+y`;*6+45&SWuf6ceSD{lPzh zUh#|(Rd$(FAGW`MIR382LW!i-j*Y)iR;8y&LlHfH-@L&?$ zXlRdgMDPV#7S2TWE-b3@mX%i5z)TT++#rnayI zAd*0;w&*v6!#MM{I#TAiG92(X_*Vrw{ID7j#1{sg4Rq8nMdk3R>7jP;XqI3eK0*t% z`(+^C>kYsxk~o`Km=Gw-o?k5vOLzNQ@%cs(zAJ2oSrs{fKe0er@Z6Y$%+ zKRt(}40Vsq8MYcona0T&U9zJAA#>;{-2~S53K7u+bFB3&F9`<%>%Z^BS zN3%t-9rHN4kiP!0roj=|;nr^JxNL{`j0c)uBo7RSHds?S@-Y^49Fr_N2gWHdLd30X z1@O%nK2;Ni7u_j0#|vu_$F_+j#)Eh=$Gn;%k1N<1MjwvUzyO3fs=B%?SyXLtvQ)X( zS0_t!eRYzk9EaDN%q~{Ez|!_hS@t|H%wy=ryvC6^a=gk~6Ae$IURT0GMX-}h9+hPy zOME%!!v}F*`U=t;4z&gvWM_l|`8vAs!5e+j6li8!Bp3``6-&f7MBa*ma+lyJjLqb` z+S^0zXkiSc^vSiixU|YuR8zXdn*Umq^_O6~$%NvdO3T>q~3g z-ik_Zb!|yWX`Lm%D_s@E-pUfHbRmqjloMY|LOTs z@rnuJlQYRFuPuXRjWQRN)w;p+Vk89>MefR~V^Wos7F4^esd5S^lD=xIapAv}emP1j zmVm0*>vma7d7dJuttc(3EVgd4Tt(!nE`pFPbIPf$SX5DYJ(|CAk;__@bBZgANI}4) ztf0!83%->;21%~cv!k$qjujG{(u!h8Mwt4s$}XSO!3*`UigY*%VJ2u1qHC%?Z$&U4 z3g=zqvW&Zu;vo52YhlfkEmu)l<1MMIO_G0})DU!&qfzB3aR`=H<#fa(CK+1MWZQ3W zRaLUQNOGd?R+NrdhRI3t3d##h7t~hPR(q3Y>BvrRU0$@IyBvdY4OG9>9- z<}LM--5V_N$U3F9jWMscqzX#~(E?;MiXI86HR!&kyeM{`@Q?;mB<80!l&&3*W1Mqk zNlCQ}W~k_cXK*S0JeDG*MM$xrqSotjbAImPDnN}LAupesqE;ZnET*#Rbu{C#()^L7 z$)e-60IRi%W+ODwwxJTE$;c3y`9yxD(nfW{$*wGUM$$HR$Bj)=EIdOKC!Y&ocR+5 zamWkHzqo1QxxJb@e$Sbjkx`l2>wr+1ZzT{HBik$dV zNHt1mWjPflvHe)#_&;wvInlbU)&bSxSejE5BgpZs1Em5A>!rdN>2Vr`JaGYH?p9`j zE#$~{GsQAA(HL?P+r)bPm*|TUR7@@uggseZ(Rf{@+tifnPOAY>4#4W+E4EZUn%T{Y z|EB$5D>20_dDhfeOxtSZY*_<2^K;El!~!S{%xuD5*@cf=beDsI9S^>0v1+Gr^*Hs& z#F{01R9ox1HQ;g0DyOjd{4;}ks*YJ` z>ZfE)EvheGSo0kAs+f6Sxs=Cx#Q}O@E*bX89eE|%k7|l6v0Y=R7`KGF1w7SW-NtfF zX-o)Lx|D@+G2l@lk8gZb)k>Um5~f9^3v3!%E~U)N5k~?!3eU5sZtPY)y5(R3BZIvi zc=;6yx=^inh?F+bCN7%#_^ZR6%u=OkfhEmubDPJeWiD9ma{MGIo~c@AHNEO1qRMW_4~NKa>isIWTcz|I@~2PhLbDnE%rm7g$jt8o#o(yQ|j z_H%l54vETNrC0Ue!08L&lT+uKxaY{QItPW)V{2|Kf0h0&U{pmGx1r8~*=z?0H$!@{ zzf@MU>p{SrUY)}*#OdigN3&D=&6IzimR_CLFpwitsro7&dYJZCCgh*v6r@9({(!bg zP35cjeN#*SScy#kSc#-KSrM|Y*Z(`fDSuVX0Zu=_>Aj@fCa0JGNyM4ctMeo>t7Ibj zF5m3*{09+fPOr|L7^s%1UR4dtO!WMp)6%Q+Dek<1({noI>G}T=5tDIIA0(%~!<)HO zhIM)C`Tq@M)WubL^?hGHr`PHA@cT$h^;hZDxfsslOn(GvNkl5WIzPkB=?A!Bl^9k2 zs$QG5{M9)dN4$uDQ}i+v4=X$cg*TT!b{JG*{PKcsa4n@Y(3nMjZShF!x|`dkesT5`&tihGr}h~PhU{qp8~ wMaZ}o6ns?QV-n%*^U2T=o +#include +#include + +void State0::runCode() { + +} + +void State1::runCode() { + interfaces->myMotors->sendV(0.1); +} + +void State2::runCode() { + interfaces->myMotors->sendV(0); +} + +void Tran1::runCode() { + +} + +void Tran2::runCode() { + +} + +void Interfaces::connectProxies(int argc, char* argv[]) { + Config::Properties props = Config::load(argc, argv); + jdrc = new Comm::Communicator(props); + + myMotors = Comm::getMotorsClient(jdrc, "sample1.myMotors"); + if (myMotors == NULL) { + throw "invalid proxy myMotors"; + } + std::cout << "myMotors is connected" << std::endl; +} + +void Interfaces::destroyProxies() { + if (jdrc != 0) { + } +} + + +pthread_t guiThread; +RunTimeGui* runTimeGui = NULL; +bool displayGui = false; + +void readArgs(int *argc, char* argv[]) { + int i; + std::string splitedArg; + + for(i = 0; i < *argc; i++) { + splitedArg = strtok(argv[i], "="); + if (splitedArg.compare("--displaygui") == 0){ + splitedArg = strtok(NULL, "="); + if (splitedArg.compare("true") == 0 || splitedArg.compare("True") == 0){ + displayGui = true; + std::cout << "displayGui ENABLED" << std::endl; + }else{ + displayGui = false; + std::cout << "displayGui DISABLED" << std::endl; + } + } + if(i == *argc -1){ + (*argc)--; + } + } +} + +void* runGui(void*) { + system("./sample1_runtime.py"); +} + +int main(int argc, char* argv[]) { + Interfaces interfaces; + try { + interfaces.connectProxies(argc, argv); + } catch (const Ice::Exception& ex) { + std::cerr << ex << std::endl; + interfaces.destroyProxies(); + return 1; + } catch (const char* msg) { + std::cerr << msg << std::endl; + interfaces.destroyProxies(); + return 1; + } + + readArgs(&argc, argv); + + if (displayGui) { + pthread_create(&guiThread, NULL, &runGui, NULL); + runTimeGui = new RunTimeGui(); + + } + State* state0 = new State0(0, true, &interfaces, 100, NULL, runTimeGui); + State* state1 = new State1(1, true, &interfaces, 100, state0, runTimeGui); + State* state2 = new State2(2, false, &interfaces, 100, state0, runTimeGui); + + Transition* tran1 = new Tran1(1, 2, 1000); + state1->addTransition(tran1); + Transition* tran2 = new Tran2(2, 1, 1000); + state2->addTransition(tran2); + + state0->startThread(); + + state0->join(); +} diff --git a/src/tools/visualStates_py/samples/sample1/sample1.h b/src/tools/visualStates_py/samples/sample1/sample1.h new file mode 100644 index 000000000..ea0ebebce --- /dev/null +++ b/src/tools/visualStates_py/samples/sample1/sample1.h @@ -0,0 +1,58 @@ +#ifndef sample1_H +#define sample1_H + +#include +#include +#include +#include + +#include +#include +#include + +class Interfaces { +public: + Comm::Communicator* jdrc; + Comm::MotorsClient* myMotors; + + virtual void connectProxies(int argc, char* argv[]); + virtual void destroyProxies(); +}; + +class State0 : public State { +public: + Interfaces* interfaces; + State0(int id, bool initial, Interfaces* interfaces, int cycleDuration, State* parent, RunTimeGui* gui): + State(id, initial, cycleDuration, parent, gui) {this->interfaces = interfaces;} + virtual void runCode(); +}; + +class State1 : public State { +public: + Interfaces* interfaces; + State1(int id, bool initial, Interfaces* interfaces, int cycleDuration, State* parent, RunTimeGui* gui): + State(id, initial, cycleDuration, parent, gui) {this->interfaces = interfaces;} + virtual void runCode(); +}; + +class State2 : public State { +public: + Interfaces* interfaces; + State2(int id, bool initial, Interfaces* interfaces, int cycleDuration, State* parent, RunTimeGui* gui): + State(id, initial, cycleDuration, parent, gui) {this->interfaces = interfaces;} + virtual void runCode(); +}; + +class Tran1 : public TemporalTransition { + public: + Tran1(int id, int destId, int elapsedTime):TemporalTransition(id, destId, elapsedTime) {} + virtual void runCode(); +}; + +class Tran2 : public TemporalTransition { + public: + Tran2(int id, int destId, int elapsedTime):TemporalTransition(id, destId, elapsedTime) {} + virtual void runCode(); +}; + +#endif \ No newline at end of file diff --git a/src/tools/visualStates_py/samples/sample1/sample1.py b/src/tools/visualStates_py/samples/sample1/sample1.py new file mode 100755 index 000000000..02fd6b4d1 --- /dev/null +++ b/src/tools/visualStates_py/samples/sample1/sample1.py @@ -0,0 +1,153 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +import sys, threading, time, signal +sys.path.append("/opt/jderobot/lib/python2.7") +sys.path.append("/opt/jderobot/lib/python2.7/visualStates_py") +from codegen.python.state import State +from codegen.python.temporaltransition import TemporalTransition +from codegen.python.conditionaltransition import ConditionalTransition +from codegen.python.runtimegui import RunTimeGui +from PyQt5.QtWidgets import QApplication +import config, comm + +import comm + +class State0(State): + def __init__(self, id, initial, interfaces, cycleDuration, parent=None, gui=None): + State.__init__(self, id, initial, cycleDuration, parent, gui) + self.interfaces = interfaces + + def runCode(self): + pass + + +class State1(State): + def __init__(self, id, initial, interfaces, cycleDuration, parent=None, gui=None): + State.__init__(self, id, initial, cycleDuration, parent, gui) + self.interfaces = interfaces + + def runCode(self): + self.interfaces.myMotors.sendV(0.1) + + +class State2(State): + def __init__(self, id, initial, interfaces, cycleDuration, parent=None, gui=None): + State.__init__(self, id, initial, cycleDuration, parent, gui) + self.interfaces = interfaces + + def runCode(self): + pass + + +class Tran1(TemporalTransition): + + def runCode(self): + pass + +class Tran2(TemporalTransition): + + def runCode(self): + pass + +class Interfaces(): + def __init__(self): + self.jdrc = None + self.myMotors = None + self.connectProxies() + + def connectProxies(self): + cfg = config.load(sys.argv[1]) + self.jdrc = comm.init(cfg, "sample1") + self.myMotors = self.jdrc.getMotorsClient("sample1.myMotors") + if not self.myMotors: + raise Exception("could not create client with name:myMotors") + print("myMotors is connected") + + def destroyProxies(self): + if self.jdrc is not None: + self.jdrc.destroy() + +displayGui = False +guiThread = None +gui = None +state0 = None + +def signal_handler(signal, frame): + global gui + print("SIGINT is captured. The program exits") + if gui is not None: + gui.close() + global state0 + state0.stop() + +def readArgs(): + global displayGui + for arg in sys.argv: + splitedArg = arg.split('=') + if splitedArg[0] == '--displaygui': + if splitedArg[1] == 'True' or splitedArg[1] == 'true': + displayGui = True + print('runtime gui enabled') + else: + displayGui = False + print('runtime gui disabled') + +def runGui(): + global gui + app = QApplication(sys.argv) + gui = RunTimeGui() + gui.show() + app.exec_() + +if __name__ == "__main__": + interfaces = Interfaces() + + readArgs() + if displayGui: + guiThread = threading.Thread(target=runGui) + guiThread.start() + + + if displayGui: + while(gui is None): + time.sleep(0.1) + + gui.addState(0, "root", True, 0.0, 0.0, None) + gui.addState(1, "state 1", True, 848.0, 820.0, 0) + gui.addState(2, "state 2", False, 1050.0, 1061.0, 0) + + gui.addTransition(1, "transition 1", 1, 2, 1029.0, 867.5) + gui.addTransition(2, "transition 2", 2, 1, 853.0, 968.5) + + if displayGui: + gui.emitLoadFromRoot() + gui.emitActiveStateById(0) + + state0 = State0(0, True, interfaces, 100, None, gui) + state1 = State1(1, True, interfaces, 100, state0, gui) + state2 = State2(2, False, interfaces, 100, state0, gui) + + tran1 = Tran1(1, 2, 1000) + state1.addTransition(tran1) + + tran2 = Tran2(2, 1, 1000) + state2.addTransition(tran2) + + try: + state0.startThread() + signal.signal(signal.SIGINT, signal_handler) + signal.pause() + state0.join() + if displayGui: + guiThread.join() + + interfaces.destroyProxies() + except: + state0.stop() + if displayGui: + gui.close() + guiThread.join() + + state0.join() + interfaces.destroyProxies() + sys.exit(1) diff --git a/src/tools/visualStates_py/samples/sample1/sample1.xml b/src/tools/visualStates_py/samples/sample1/sample1.xml new file mode 100644 index 000000000..5c7d64072 --- /dev/null +++ b/src/tools/visualStates_py/samples/sample1/sample1.xml @@ -0,0 +1,61 @@ + + + + + + ice + myMotors + + Motors + localhost + 9001 + Motors + + + + + + 0.0 + 0.0 + root + + + 100 + + 848.0 + 820.0 + state 1 + self.interfaces.myMotors.sendV(0.1) + + 100 + + 0 + + 1029.0 + 867.5 + transition 1 + 1 + 2 + + + + + 1050.0 + 1061.0 + state 2 + + + 100 + + 0 + + 853.0 + 968.5 + transition 2 + 2 + 1 + + + + + diff --git a/src/tools/visualStates_py/samples/sample1/sample1.yml b/src/tools/visualStates_py/samples/sample1/sample1.yml new file mode 100644 index 000000000..4d9f36071 --- /dev/null +++ b/src/tools/visualStates_py/samples/sample1/sample1.yml @@ -0,0 +1,7 @@ +sample1: + NodeName: sample1 + myMotors: + Name: myMotors + Proxy: Motors:default -h localhost -p 9001 + Server: 1 + Topic: '' diff --git a/src/tools/visualStates_py/samples/sample1/sample1_runtime.py b/src/tools/visualStates_py/samples/sample1/sample1_runtime.py new file mode 100755 index 000000000..3f207a9da --- /dev/null +++ b/src/tools/visualStates_py/samples/sample1/sample1_runtime.py @@ -0,0 +1,31 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +import sys +sys.path.append("/opt/jderobot/lib/python2.7/visualStates_py") + +from PyQt5.QtWidgets import QApplication +from codegen.python.runtimegui import RunTimeGui + +gui = None + +def runGui(): + global gui + app = QApplication(sys.argv) + gui = RunTimeGui() + gui.activateIPC() + + gui.addState(0, "root", True, 0.0, 0.0, None) + gui.addState(1, "state 1", True, 848.0, 820.0, 0) + gui.addState(2, "state 2", False, 1050.0, 1061.0, 0) + + gui.addTransition(1, "transition 1", 1, 2, 1029.0, 867.5) + gui.addTransition(2, "transition 2", 2, 1, 853.0, 968.5) + + gui.emitLoadFromRoot() + gui.emitActiveStateById(0) + gui.show() + app.exec_() + +if __name__ == "__main__": + runGui() + diff --git a/src/tools/visualStates_py/samples/sample2/CMakeLists.txt b/src/tools/visualStates_py/samples/sample2/CMakeLists.txt new file mode 100644 index 000000000..96faeefe5 --- /dev/null +++ b/src/tools/visualStates_py/samples/sample2/CMakeLists.txt @@ -0,0 +1,44 @@ +project(sample2) + +cmake_minimum_required(VERSION 2.8) + +SET(SOURCE_FILES + sample2.cpp) + +SET(JDEROBOT_INSTALL_PATH /opt/jderobot) + + +SET(JDEROBOT_INCLUDE_DIR ${JDEROBOT_INSTALL_PATH}/include) +SET(VISUALSTATE_RUNTIME_INCLUDE_DIR ${JDEROBOT_INSTALL_PATH}/include/visualstates_py) + +SET(JDEROBOT_LIBS_DIR ${JDEROBOT_INSTALL_PATH}/lib) +SET(VISUALSTATE_RUNTIME_LIBS_DIR ${JDEROBOT_INSTALL_PATH}/lib/visualstates_py) + +SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) + +include_directories( + ${JDEROBOT_INCLUDE_DIR} + ${VISUALSTATE_RUNTIME_INCLUDE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} +) + +link_directories( + ${JDEROBOT_LIBS_DIR} + ${VISUALSTATE_RUNTIME_LIBS_DIR} +) + +add_executable(sample2 ${SOURCE_FILES}) + +target_link_libraries( sample2 +visualStatesRunTime + config + comm + JderobotInterfaces + jderobotutil + colorspacesmm + pthread + Ice + IceUtil + IceStorm + glog +) diff --git a/src/tools/visualStates_py/samples/sample2/sample2.cpp b/src/tools/visualStates_py/samples/sample2/sample2.cpp new file mode 100644 index 000000000..bac72f1f8 --- /dev/null +++ b/src/tools/visualStates_py/samples/sample2/sample2.cpp @@ -0,0 +1,106 @@ +#include "sample2.h" +#include +#include +#include + +void State0::runCode() { + +} + +void State1::runCode() { + +} + +void State2::runCode() { + +} + +void Tran1::runCode() { + +} + +void Tran2::runCode() { + +} + +void Interfaces::connectProxies(int argc, char* argv[]) { + Config::Properties props = Config::load(argc, argv); + jdrc = new Comm::Communicator(props); + + pose3d = Comm::getPose3dClient(jdrc, "sample2.pose3d"); + if (pose3d == NULL) { + throw "invalid proxy pose3d"; + } + std::cout << "pose3d is connected" << std::endl; +} + +void Interfaces::destroyProxies() { + if (jdrc != 0) { + } +} + + +pthread_t guiThread; +RunTimeGui* runTimeGui = NULL; +bool displayGui = false; + +void readArgs(int *argc, char* argv[]) { + int i; + std::string splitedArg; + + for(i = 0; i < *argc; i++) { + splitedArg = strtok(argv[i], "="); + if (splitedArg.compare("--displaygui") == 0){ + splitedArg = strtok(NULL, "="); + if (splitedArg.compare("true") == 0 || splitedArg.compare("True") == 0){ + displayGui = true; + std::cout << "displayGui ENABLED" << std::endl; + }else{ + displayGui = false; + std::cout << "displayGui DISABLED" << std::endl; + } + } + if(i == *argc -1){ + (*argc)--; + } + } +} + +void* runGui(void*) { + system("./sample2_runtime.py"); +} + +int main(int argc, char* argv[]) { + Interfaces interfaces; + try { + interfaces.connectProxies(argc, argv); + } catch (const Ice::Exception& ex) { + std::cerr << ex << std::endl; + interfaces.destroyProxies(); + return 1; + } catch (const char* msg) { + std::cerr << msg << std::endl; + interfaces.destroyProxies(); + return 1; + } + + readArgs(&argc, argv); + + if (displayGui) { + pthread_create(&guiThread, NULL, &runGui, NULL); + runTimeGui = new RunTimeGui(); + + } + State* state0 = new State0(0, true, &interfaces, 100, NULL, runTimeGui); + State* state1 = new State1(1, true, &interfaces, 100, state0, runTimeGui); + State* state2 = new State2(2, false, &interfaces, 100, state0, runTimeGui); + + Transition* tran1 = new Tran1(1, 2, 1000); + state1->addTransition(tran1); + Transition* tran2 = new Tran2(2, 1, 1000); + state2->addTransition(tran2); + + state0->startThread(); + + state0->join(); +} diff --git a/src/tools/visualStates_py/samples/sample2/sample2.h b/src/tools/visualStates_py/samples/sample2/sample2.h new file mode 100644 index 000000000..78b2d9135 --- /dev/null +++ b/src/tools/visualStates_py/samples/sample2/sample2.h @@ -0,0 +1,58 @@ +#ifndef sample2_H +#define sample2_H + +#include +#include +#include +#include + +#include +#include +#include + +class Interfaces { +public: + Comm::Communicator* jdrc; + Comm::Pose3dClient* pose3d; + + virtual void connectProxies(int argc, char* argv[]); + virtual void destroyProxies(); +}; + +class State0 : public State { +public: + Interfaces* interfaces; + State0(int id, bool initial, Interfaces* interfaces, int cycleDuration, State* parent, RunTimeGui* gui): + State(id, initial, cycleDuration, parent, gui) {this->interfaces = interfaces;} + virtual void runCode(); +}; + +class State1 : public State { +public: + Interfaces* interfaces; + State1(int id, bool initial, Interfaces* interfaces, int cycleDuration, State* parent, RunTimeGui* gui): + State(id, initial, cycleDuration, parent, gui) {this->interfaces = interfaces;} + virtual void runCode(); +}; + +class State2 : public State { +public: + Interfaces* interfaces; + State2(int id, bool initial, Interfaces* interfaces, int cycleDuration, State* parent, RunTimeGui* gui): + State(id, initial, cycleDuration, parent, gui) {this->interfaces = interfaces;} + virtual void runCode(); +}; + +class Tran1 : public TemporalTransition { + public: + Tran1(int id, int destId, int elapsedTime):TemporalTransition(id, destId, elapsedTime) {} + virtual void runCode(); +}; + +class Tran2 : public TemporalTransition { + public: + Tran2(int id, int destId, int elapsedTime):TemporalTransition(id, destId, elapsedTime) {} + virtual void runCode(); +}; + +#endif \ No newline at end of file diff --git a/src/tools/visualStates_py/samples/sample2/sample2.xml b/src/tools/visualStates_py/samples/sample2/sample2.xml new file mode 100644 index 000000000..9fe1678e3 --- /dev/null +++ b/src/tools/visualStates_py/samples/sample2/sample2.xml @@ -0,0 +1,61 @@ + + + + + + ice + pose3d + + pose3d + localhost + 9001 + Pose3d + + + + + + 0.0 + 0.0 + root + + + 100 + + 903.0 + 922.0 + state 1 + + + 100 + + 0 + + 1025.0 + 954.0 + transition 1 + 1 + 2 + + + + + 1031.0 + 1096.0 + state 2 + + + 100 + + 0 + + 860.0 + 1054.0 + transition 2 + 2 + 1 + + + + + diff --git a/src/tools/visualStates_py/samples/sample2/sample2.yml b/src/tools/visualStates_py/samples/sample2/sample2.yml new file mode 100644 index 000000000..f608faf10 --- /dev/null +++ b/src/tools/visualStates_py/samples/sample2/sample2.yml @@ -0,0 +1,7 @@ +sample2: + NodeName: sample2 + pose3d: + Name: pose3d + Proxy: pose3d:default -h localhost -p 9001 + Server: 1 + Topic: '' diff --git a/src/tools/visualStates_py/samples/sample2/sample2_runtime.py b/src/tools/visualStates_py/samples/sample2/sample2_runtime.py new file mode 100755 index 000000000..7d0b34241 --- /dev/null +++ b/src/tools/visualStates_py/samples/sample2/sample2_runtime.py @@ -0,0 +1,31 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +import sys +sys.path.append("/opt/jderobot/lib/python2.7/visualStates_py") + +from PyQt5.QtWidgets import QApplication +from codegen.python.runtimegui import RunTimeGui + +gui = None + +def runGui(): + global gui + app = QApplication(sys.argv) + gui = RunTimeGui() + gui.activateIPC() + + gui.addState(0, "root", True, 0.0, 0.0, None) + gui.addState(1, "state 1", True, 903.0, 922.0, 0) + gui.addState(2, "state 2", False, 1031.0, 1096.0, 0) + + gui.addTransition(1, "transition 1", 1, 2, 1025.0, 954.0) + gui.addTransition(2, "transition 2", 2, 1, 860.0, 1054.0) + + gui.emitLoadFromRoot() + gui.emitActiveStateById(0) + gui.show() + app.exec_() + +if __name__ == "__main__": + runGui() + diff --git a/src/tools/visualStates_py/visualStates_py b/src/tools/visualStates_py/visualStates_py new file mode 100644 index 000000000..1b0b65a8e --- /dev/null +++ b/src/tools/visualStates_py/visualStates_py @@ -0,0 +1,3 @@ +#!/bin/bash + +python /opt/jderobot/lib/python2.7/visualStates_py/visualStates.py $*