-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 80a5774
Showing
12 changed files
with
225 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# Auto detect text files and perform LF normalization | ||
* text=auto |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from main import FSMconstructor |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
class NoStatesProvided(Exception): | ||
def __init__(self, message:str = "No states was passed as \"define_states\" argument") -> None: | ||
super().__init__(message) | ||
pass | ||
|
||
class NoEntryPoint(Exception): | ||
def __init__(self, message:str = "No first state was set") -> None: | ||
super().__init__(message) | ||
pass | ||
|
||
class MachineAlrStarted(Exception): | ||
def __init__(self, message:str = "Machine is already in execution") -> None: | ||
super().__init__(message) | ||
pass | ||
|
||
class VariableNotDefined(Exception): | ||
def __init__(self, variable) -> None: | ||
super().__init__(f"Variable \"{variable}\" was not defined!") | ||
pass | ||
|
||
class StateNotExists(Exception): | ||
def __init__(self, state) -> None: | ||
super().__init__(f"State \"{state}\" was not defined!") | ||
pass | ||
|
||
class FewArguments(Exception): | ||
def __init__(self, var) -> None: | ||
super().__init__(f"Argument \"{var}\" was not provided!") | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
############################# | ||
## ## | ||
## BasicFSM by Levorin ## | ||
## ## | ||
## ver. 0.1 ## | ||
## ## | ||
############################# | ||
|
||
from fsm_errors import * | ||
from threading import Thread | ||
|
||
class FSMconstructor: | ||
def __init__(self): | ||
self.states: dict = {} | ||
self.states_names: list | ||
self.check_function: function = None | ||
self.prev_variables: dict = {} | ||
self.variables: dict = {} | ||
self.change_checklist: dict = {} | ||
self.conditions: dict = {} | ||
|
||
self.loop_thread = None | ||
self.check_thread = None | ||
|
||
# private | ||
self._current_state: str | ||
|
||
def define_states(self, *args: list) -> list: | ||
''' | ||
Used to declare all machine states, pass as argument all the associated function | ||
''' | ||
# error check | ||
if not args: raise NoStatesProvided() | ||
|
||
for func in args: | ||
self.states[func.__name__] = func | ||
|
||
self.states_names = list(self.states.keys()) | ||
return self.states_names | ||
|
||
def change_state(self, state: str): | ||
''' | ||
safe method to switch between states | ||
''' | ||
if not state in self.states_names: raise StateNotExists(state) | ||
self._current_state = state | ||
|
||
def thread_check(self): | ||
while True: | ||
# user defined | ||
if self.check_function: | ||
self.check_function() | ||
|
||
# if defined check condition | ||
if self._current_state in self.conditions.keys(): | ||
self.conditions[self._current_state]() | ||
|
||
# check vars changes | ||
for var in self.change_checklist: | ||
if self.variables[var] != self.prev_variables[var]: | ||
self.change_state(self.change_checklist[var]) | ||
self.prev_variables[var] = self.variables[var] | ||
|
||
def thread_loop(self): | ||
while True: self.states[self._current_state]() | ||
|
||
def start(self, entry_point: str) -> None: | ||
''' | ||
Start the Finite State Machine. The machine will be executed as a separate process | ||
''' | ||
# error check | ||
if not self.states: raise NoStatesProvided() | ||
if not entry_point: raise NoEntryPoint() | ||
if not entry_point in self.states_names: raise StateNotExists(entry_point) | ||
if self.loop_thread or self.check_thread: raise MachineAlrStarted() | ||
|
||
self._current_state = entry_point | ||
self.loop_thread = Thread(target = self.thread_check, daemon = True) | ||
self.check_thread = Thread(target = self.thread_loop, daemon = True) | ||
self.loop_thread.start() | ||
self.check_thread.start() | ||
|
||
def on_change(self, var_name: str, change_to: str) -> None: | ||
''' | ||
Change to the specified state when the variable passed as argument is changed | ||
''' | ||
# error check | ||
if var_name not in self.variables.keys(): raise VariableNotDefined(variable = var_name) | ||
if change_to not in self.states_names: raise StateNotExists(change_to) | ||
|
||
# change_to, block | ||
self.prev_variables[var_name] = self.variables[var_name] | ||
self.change_checklist[var_name] = change_to | ||
|
||
def addCondition(self, attach_to: str, condition): | ||
''' | ||
Attach switch state condition to the specified state | ||
''' | ||
if attach_to not in self.states_names: raise StateNotExists(attach_to) | ||
|
||
self.conditions[attach_to] = condition |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2023 Francesco Levorin | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# BasicFSM | ||
|
||
BasicFSM is a Python library that aims to simplify Finite State Machine creation in Python. | ||
### Features | ||
|
||
- The machine runs indipendently from the rest of the code | ||
- It supports multiple machines per code | ||
- Doesn't need third part libraries | ||
|
||
### Installation | ||
|
||
```sh | ||
pip install basicfsm | ||
``` | ||
|
||
|
||
### Example | ||
|
||
Basic FSM structure | ||
```python | ||
from basicfsm import FSMConstructor # import the library | ||
from time import sleep | ||
|
||
fsm = FSMConstructor() | ||
# declaring internal fsm variables | ||
fsm.variables["count1"] = 0 | ||
fsm.variables["count2"] = 0 | ||
|
||
def state1(): | ||
fsm.variables["count1"] += 1 | ||
print(fsm.variables["count1"]) | ||
sleep(1) | ||
|
||
def state2(): | ||
fsm.variables["count2"] += 1 | ||
print(fsm.variables["count2"]) | ||
sleep(1) | ||
|
||
# the function that will control the state switch | ||
def state1_condition(): | ||
if fsm.variables["count1"] >= 10: | ||
fsm.change_state("state2") | ||
|
||
# declare all the states | ||
fsm.define_states(state1, state2) | ||
# start the fsm at the defined state | ||
fsm.start("state1") | ||
``` |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[metadata] | ||
description-file = README.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from distutils.core import setup | ||
setup( | ||
name = 'BasicFSM', # How you named your package folder (MyLib) | ||
packages = ['BasicFSM'], # Chose the same as "name" | ||
version = '0.1', # Start with a small number and increase it with every change you make | ||
license='MIT', # Chose a license from here: https://help.github.com/articles/licensing-a-repository | ||
description = 'A lightweight library to manage a Finite State Machine', # Give a short description about your library | ||
author = 'Levorin', # Type in your name | ||
author_email = '[email protected]', # Type in your E-Mail | ||
url = 'https://github.com/user/reponame', # Provide either the link to your github or to your website | ||
download_url = 'https://github.com/user/reponame/archive/v_01.tar.gz', # I explain this later on | ||
keywords = ['FSM', 'Async'], # Keywords that define your package best | ||
install_requires=[], | ||
classifiers=[ | ||
'Development Status :: 3 - Alpha', # Chose either "3 - Alpha", "4 - Beta" or "5 - Production/Stable" as the current state of your package | ||
'Intended Audience :: Developers', # Define that your audience are developers | ||
'Topic :: Software Development :: Build Tools', | ||
'License :: OSI Approved :: MIT License', # Again, pick a license | ||
'Programming Language :: Python :: 3.10' | ||
], | ||
) |