Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple State Machine Support #15

Closed
danieljlevine opened this issue Dec 28, 2020 · 29 comments
Closed

Multiple State Machine Support #15

danieljlevine opened this issue Dec 28, 2020 · 29 comments

Comments

@danieljlevine
Copy link

I'm interested in displaying the state of multiple state machines simultaneously on the same web page. It would seem that each of the transitions-gui instances starts it's own web server. I suppose, I could use different ports for this. Any chance there's already a way to do this using a single server? I'm headed towards a hundred independent state machines (for character states, as an example). The other method that occurs to me is to perhaps use the parallel state machine.

Also is there a way to color the state machine by the state it is presently in?

@aleneum
Copy link
Member

aleneum commented Dec 29, 2020

Hello @danieljlevine,

are you talking about different state machines (with individual transitions and states) or models (stateful instances using the same states/transitions)?

Any chance there's already a way to do this using a single server?

You can pass websocket_handler to the WebMachine constructor. A websocket_handler just needs to implement a send_message(<json>) method. WebMachine will not create a server if this argument is present.

The frontend code is pretty naive. It will connect to a websocket (at <url>:<port>/ws) and expect valid state machine configurations/state changes as JSON. One machine can manage multiple models. The relevant information to manage multiple models are already passed to the frontent. AFAICT it is not considered though.

Also is there a way to color the state machine by the state it is presently in?

Right now, the CSS class .current is set on an active state/node. This could be extended to set model-specific classes as well. Nodes probably have IDs and classes which can be used to customize the appearance further.

@aleneum
Copy link
Member

aleneum commented Dec 29, 2020

Additionally,

WebMachine will reuse tornado's event queue when it has been initialized already. So when you initialize multiple instances of WebMachine with different ports, each WebMachine will run its own server thread but will be processed in the same event loop. This might need to be verified though.

@danieljlevine
Copy link
Author

I suppose what I'm requesting is the display of several instances of the state machine (perhaps this is the state of independent models governed by the state machine I define) on the WebPage. It looks like pytransitions supports the multiple models. If I passed multiple models in to the constructor as in the pytransitions example, would transitions-gui display both models? (I suppose I could just try it.)

@aleneum
Copy link
Member

aleneum commented Dec 29, 2020

In 27ea90c, I implemented handling of mutliple models. You can pass custome css-like style selectors based on model classes and model names. See examples/multiple.py. It's not polished though. If two models are in the same state, only one selector in multiple.py will be visible. Edge classes might be removed prematurely. However, there is room for customization by tinkering around with the selectors. For me, multiple.py looks like this on localhost:8080 and localhost:8081:

legend_splitscreen

@aleneum
Copy link
Member

aleneum commented Dec 29, 2020

if running multiple instances of tornado in threads becomes an issue, I will have a look into whether threading can be limited to one server thread which can be reused by multiple WebMachine instances.

@danieljlevine
Copy link
Author

I see, so with different models they are shown on the same state machine. Originally, I was thinking I'd have numerous replicated models being shown and identified, as opposed to having them all collapsed on to a single visualized machine. Maybe another way to do this is to be able to show a list of model IDs adorning the states they are presently in in their respective models? You are doing that nicely with colors here, but I suppose with a large number of models, this won't work so well (perhaps my idea won't either with even larger numbers of models, but it's a start). Is there a way to name my models with a string or number and have that appear within the state the model presently has. Perhaps I'd use the state entering and leaving callbacks to remove/add the ids from/to the state? I suppose while I'm at it perhaps I'd do the same for the transition to show which model id is using the transition at that moment.

Thoughts?

@danieljlevine
Copy link
Author

What if I used parallel states instead to represent the different models in parallel? I could label each parallel machine with the model id (where the example uses "numbers" and "greek") and move along independently. My state machines are HSMs, so hopefully I can still do that. Maybe that's what I want.

@aleneum
Copy link
Member

aleneum commented Dec 29, 2020

I think both approaches (state node extended with model names or legend entries extended with model states) are feasible. See https://js.cytoscape.org/ for a better idea about how to edit graphs. Nodes are edited in WebMachine.selectState. Currently, only classes are set and removed. If you have an idea how to edit labels instead (or in addition to) altering classes, let me know.

@aleneum
Copy link
Member

aleneum commented Dec 29, 2020

What if I used parallel states instead to represent the different models in parallel?

I usually try to find the simplest FSM architecture that gets the job done. Parallel states can be a solution but can also result in quite complex configurations. If you have instances that share states and transitions (e.g. agents, NPCs), I'd go for multiple models and simple machine. If instances rather act as components (e.g. robot arm, leg, head), I'd choose parallel states.

@danieljlevine
Copy link
Author

Thanks for the advice. I suppose I should consider the state annotation with the ids.

@danieljlevine
Copy link
Author

Trying out the graph_css parameter. I would have thought that I could pass graph_css=[] to the NestedWebMachine constructor and have it not doing anything different. Instead it halts with Exception <class 'ValueError'>. Is graph_css supported in NestedWebMachine? I assumed, yes. It barfed on my agent_css list, so I'm just simplifying. Removing the graph_css makes it not throw the exception.

@danieljlevine
Copy link
Author

Changed it to just a WebMachine and it still didn't work. Must be missing something subtle here. Also tried setting graph_css=None with same issue.

@danieljlevine
Copy link
Author

Running your multiple.py example produces the same issue, but more useful output (to you perhaps):

ValueError: Passing arguments dict_keys[['graph_css']] caused an inheritance error: object init() takes exactly one argument (the instance to initialize).

I wonder if I'm not using your latest code somehow...

@aleneum
Copy link
Member

aleneum commented Dec 30, 2020

Did you install transitions-gui maybe? I experience this when I accidentally use classes from site-packages instead of local versions. Try pip uninstall transitions-gui in your environment.

@aleneum
Copy link
Member

aleneum commented Dec 30, 2020

I would have thought that I could pass graph_css=[] to the NestedWebMachine

I'd expect the same.

@danieljlevine
Copy link
Author

So, I did install transitions and transitions-gui initially and it worked until using this graph_css feature. So I did the uninstall of transitions-gui. It doesn't seem to have helped it to start working. Here's the message I get from your multiple.py pasted into my jupyter notebook:

TypeError Traceback (most recent call last)
~/miniconda3/lib/python3.8/site-packages/transitions/core.py in init(self, model, states, initial, transitions, send_event, auto_transitions, ordered_transitions, ignore_invalid_triggers, before_state_change, after_state_change, name, queued, prepare_event, finalize_event, model_attribute, **kwargs)
536 try:
--> 537 super(Machine, self).init(**kwargs)
538 except TypeError as err:

TypeError: object.init() takes exactly one argument (the instance to initialize)

During handling of the above exception, another exception occurred:

ValueError Traceback (most recent call last)
in
46 agents = [Agent(), Soldier(), Soldier("SgtBloom")]
47
---> 48 agent_machine = WebMachine(
49 model=agents,
50 states=agent_states,

~/miniconda3/lib/python3.8/site-packages/transitions_gui/web.py in init(self, *args, **kwargs)
31 _init_default_handler(self, kwargs.pop('port', 8080),
32 kwargs.pop('daemon', False)))
---> 33 super(WebMachine, self).init(*args, **kwargs)
34
35 def process_message(self, message):

~/miniconda3/lib/python3.8/site-packages/transitions/extensions/markup.py in init(self, *args, **kwargs)
26 self._add_markup_model(m)
27 else:
---> 28 super(MarkupMachine, self).init(*args, **kwargs)
29 self._markup['before_state_change'] = [x for x in (rep(f) for f in self.before_state_change) if x]
30 self._markup['after_state_change'] = [x for x in (rep(f) for f in self.before_state_change) if x]

~/miniconda3/lib/python3.8/site-packages/transitions/core.py in init(self, model, states, initial, transitions, send_event, auto_transitions, ordered_transitions, ignore_invalid_triggers, before_state_change, after_state_change, name, queued, prepare_event, finalize_event, model_attribute, **kwargs)
537 super(Machine, self).init(**kwargs)
538 except TypeError as err:
--> 539 raise ValueError('Passing arguments {0} caused an inheritance error: {1}'.format(kwargs.keys(), err))
540
541 # initialize protected attributes first

ValueError: Passing arguments dict_keys(['graph_css']) caused an inheritance error: object.init() takes exactly one argument (the instance to initialize)

I see in web.py you pop the web arguments and send things along to the super class. Which looks like it finds its way to markup.py, but it doesn't seem to pop the graph_css arguments before passing things along to the super class and that's where I believe graph_css is unepexpected and things go wrong in core.py.

@danieljlevine
Copy link
Author

Oh, is the problem that I have done a pip install transitions in order to put pytransitions into ~/miniconda3? I think for my particular environment, I need the correct pytransitions to be found under ~/miniconda3. So perhaps I need to pip uninstall transitions, then git clone your transitions repo then somehow install that latest source into ~/miniconda3. Does that makes sense for this to behave differently (correctly)?

If so, I see I can do something like changing into the pytransition directory with setup.py and do:

python3 setup.py install

Would that install it into ~/miniconda3?

Guess I'll give it a tray and see what happens.

@danieljlevine
Copy link
Author

The above did what I expected and installed transitions into ~/miniconda3. But no difference in bahavior. So I looked at the MarkupMachine code. It seems to be reference a "markup" parameter as opposed to a "graph_css" parameter you are using in the multiple.py example. Perhaps your latest code isn't pushed?

I changed "graph_css" to "markup" and got further along in the process. However, then it complained with:

~/miniconda3/lib/python3.8/site-packages/transitions-0.8.6-py3.8.egg/transitions/extensions/markup.py in init(self, *args, **kwargs)
21
22 if self._markup:
---> 23 models_markup = self._markup.pop('models', [])
24 super(MarkupMachine, self).init(None, **self._markup)
25 for m in models_markup:

TypeError: pop expected at most 1 argument, got 2

aleneum added a commit that referenced this issue Dec 30, 2020
@aleneum
Copy link
Member

aleneum commented Dec 30, 2020

I added the current state to the legend entry. wont suffice for hundreds of states but maybe makes working with multiple states less cluttered:

legend_states

@aleneum
Copy link
Member

aleneum commented Dec 30, 2020

~/miniconda3/lib/python3.8/site-packages/transitions_gui/web.py

you still use an installed version of transitions_gui. seems like you may have multiple versions installed.

@aleneum
Copy link
Member

aleneum commented Dec 30, 2020

git clone https://github.com/pytransitions/transitions-gui.git 
cd transitions-gui
pip install -r requirements.txt
# [1]
python examples/multiple.py

and make sure that you dont have an older version of transitions-gui installed. You could install the package in editable mode (put pip install -e . at [1]) if you want to.

@aleneum
Copy link
Member

aleneum commented Dec 30, 2020

ValueError: Passing arguments dict_keys(['graph_css']) caused an inheritance error: object.init() takes exactly one argument (the instance to initialize)

this basically means that graph_css hasn't been processed which still implies that you are dealing with an old version of WebMachine. You could import transitions_gui from an interactive shell and check its origin with __file__

@danieljlevine
Copy link
Author

Ok, about to follow your git clone to install transitions-gui from source. I was able to pip uninstall a few transitions-gui modules. I also pip uninstalled transitions. So, now do I clone the source for transitions and install that from source and then do the same as you describe for transitions-gui?

@aleneum
Copy link
Member

aleneum commented Dec 30, 2020

pip install -r requirements.txt in the transitions-gui code folder should install the most recent version of transitions which is sufficient.

@danieljlevine
Copy link
Author

Ok, I believe I have everything working as intended. Am I using the source because what we're working with hasn't been released? Just want to make sure I'm following why I'm doing it this way as opposed to using pip install.

@aleneum
Copy link
Member

aleneum commented Dec 30, 2020

I created a new dev branch dev-multimodels since all these updates to main.js might bloat git's history. Futhermore, I am not sure which changes will be kept. I added model names to nodes:

legend_node_label

@danieljlevine
Copy link
Author

Ok, still working with what I've got her now on my models. I see the legends for my models. Background color doesn't seem to be getting applied for some reason. I'll keep looking at it. Thanks for getting me straightened out!

@danieljlevine
Copy link
Author

I'm really liking the model names in the states they occupy. I gave a demo of it and people were pretty excited about what they saw. How can I turn off the legend? It doesn't seem to be necessary for my needs, since color doesn't mean much other than recent change.

Also, it would seem that my nested FSM doesn't color the activated transition when the transition is between sub-states of a state. Other transitions get colored fine as far as I can recall.

@aleneum
Copy link
Member

aleneum commented May 24, 2024

This has been merged with #40

@aleneum aleneum closed this as completed May 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants