Skip to content

Commit

Permalink
1 version of yasmin
Browse files Browse the repository at this point in the history
Former-commit-id: a17c6a3
  • Loading branch information
mgonzs13 committed Aug 25, 2021
1 parent 2c680b4 commit 161b433
Show file tree
Hide file tree
Showing 81 changed files with 18,848 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.vscode
log
__pycache__
72 changes: 71 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,72 @@
# YASMIN
# YASMIN (Yet Another State Machine)

## Simple Demo
```python
from yasmin import State
from yasmin import StateMachine


# define state Foo
class Foo(State):
def __init__(self):
super().__init__(["outcome1", "outcome2"])
self.counter = 0

def execute(self, blackboard):
print("Executing state FOO")
if self.counter < 3:
self.counter += 1
blackboard.foo_str = "Counter: " + str(self.counter)
return "outcome1"
else:
return "outcome2"


# define state Bar
class Bar(State):
def __init__(self):
super().__init__(outcomes=["outcome2"])

def execute(self, blackboard):
print("Executing state BAR")
print(blackboard.foo_str)
return "outcome2"


# main
def main():
print("demo_state_machine")

# Create a state machine
sm = StateMachine(outcomes=["outcome4", "outcome5"])

# Add states
sm.add_state("FOO", Foo(),
transitions={"outcome1": "BAR",
"outcome2": "outcome4"})
sm.add_state("BAR", Bar(),
transitions={"outcome2": "FOO"})

# Execute
outcome = sm()
print(outcome)


if __name__ == "__main__":
main()
```

## yasmin_viewer

### Installation
```shell
./install_viewer.sh
```

### Run
```shell
ros2 run yasmin_viewer yasmin_viewer_node
```

### Viewer
http://localhost:5000/
3 changes: 3 additions & 0 deletions install_viewer.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#sudo apt install npm
sudo pip3 install Flask waitress expiringdict
#cd yasmin_fsm_viewer/yasmin_viewer_web_client && npm install && npm run build
18 changes: 18 additions & 0 deletions yasmin/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>yasmin</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="[email protected]">miguel</maintainer>
<license>TODO: License declaration</license>

<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>

<export>
<build_type>ament_python</build_type>
</export>
</package>
Empty file added yasmin/resource/yasmin
Empty file.
4 changes: 4 additions & 0 deletions yasmin/setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[develop]
script-dir=$base/lib/yasmin
[install]
install-scripts=$base/lib/yasmin
25 changes: 25 additions & 0 deletions yasmin/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from setuptools import setup, find_packages

package_name = 'yasmin'

setup(
name=package_name,
version='0.0.0',
packages=find_packages(),
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='miguel',
maintainer_email='[email protected]',
description='TODO: Package description',
license='TODO: License declaration',
tests_require=['pytest'],
entry_points={
'console_scripts': [
],
},
)
23 changes: 23 additions & 0 deletions yasmin/test/test_copyright.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2015 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from ament_copyright.main import main
import pytest


@pytest.mark.copyright
@pytest.mark.linter
def test_copyright():
rc = main(argv=['.', 'test'])
assert rc == 0, 'Found errors'
25 changes: 25 additions & 0 deletions yasmin/test/test_flake8.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright 2017 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from ament_flake8.main import main_with_errors
import pytest


@pytest.mark.flake8
@pytest.mark.linter
def test_flake8():
rc, errors = main_with_errors(argv=[])
assert rc == 0, \
'Found %d code style errors / warnings:\n' % len(errors) + \
'\n'.join(errors)
23 changes: 23 additions & 0 deletions yasmin/test/test_pep257.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2015 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from ament_pep257.main import main
import pytest


@pytest.mark.linter
@pytest.mark.pep257
def test_pep257():
rc = main(argv=['.', 'test'])
assert rc == 0, 'Found code style errors / warnings'
Empty file added yasmin/yasmin/__init__.py
Empty file.
24 changes: 24 additions & 0 deletions yasmin/yasmin/blackboard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@


class Blackboard(object):
def __init__(self, init=None):
if init is not None:
self.__dict__.update(init)

def __getitem__(self, key):
return self.__dict__[key]

def __setitem__(self, key, value):
self.__dict__[key] = value

def __delitem__(self, key):
del self.__dict__[key]

def __contains__(self, key):
return key in self.__dict__

def __len__(self):
return len(self.__dict__)

def __repr__(self):
return repr(self.__dict__)
15 changes: 15 additions & 0 deletions yasmin/yasmin/cb_state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@


from typing import List
from .state import State


class CbState(State):

def __init__(self, outcomes: List[str], cb):

super().__init__(outcomes)
self._cb = cb

def execute(self, blackboard):
return self._cb(blackboard)
46 changes: 46 additions & 0 deletions yasmin/yasmin/state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@


from typing import List
from abc import ABC, abstractmethod
from .blackboard import Blackboard


class State(ABC):

def __init__(self, outcomes: List[str]):
self._outcomes = []
self._canceled = False

if outcomes:
self._outcomes = outcomes
else:
raise Exception("There must be at least one outcome")

def __call__(self, blackboard: Blackboard = None):
self._canceled = False

if blackboard is None:
blackboard = Blackboard()

outcome = self.execute(blackboard)

if not outcome in self._outcomes:
raise Exception("Outcome " + outcome + " does not belong")

return outcome

@abstractmethod
def execute(self, blackboard: Blackboard) -> str:
""" state execution """

def __str__(self):
return self.__class__.__name__

def cancel_state(self):
self._canceled = True

def is_canceled(self):
return self._canceled

def get_outcomes(self) -> List[str]:
return self._outcomes
80 changes: 80 additions & 0 deletions yasmin/yasmin/state_machine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@


from typing import Dict, List
from .state import State
from .blackboard import Blackboard


class StateMachine(State):
def __init__(self, outcomes: List[str]):

super().__init__(outcomes)

self._states = {}
self._start_state = None
self._current_state = None

def add_state(self,
name: str,
state: State,
transitions: Dict[str, str] = None):

if not transitions:
transitions = {}

name = name.upper()

self._states[name] = {
"state": state,
"transitions": transitions
}

if not self._start_state:
self._start_state = name

def set_start(self, name: str):
self._start_state = name.upper()

def cancel_state(self):
super().cancel_state()
if self._current_state:
self._states[self._current_state]["state"].cancel_state()

def execute(self, blackboard: Blackboard):

self._current_state = self._start_state

state = self._states[self._start_state]

while True:
outcome = state["state"](blackboard)

# tranlate outcome using transitions
if outcome in state["transitions"]:
outcome = state["transitions"][outcome]

# outcome is an outcome of the sm
if outcome in self._outcomes:
self._current_state = None
return outcome

# outcome is a state
elif outcome in self._states:
self._current_state = outcome
state = self._states[self._current_state]

# outcome is not in the sm
else:
raise Exception("Outcome (" + outcome + ") without transition")

def get_states(self) -> Dict[str, str]:
return self._states

def get_current_state(self) -> str:
if self._current_state:
return self._current_state

return ""

def __str__(self):
return str(self._states)
Loading

0 comments on commit 161b433

Please sign in to comment.