From 2d2c9d5b57737215dc894d39eeba76b741b55001 Mon Sep 17 00:00:00 2001 From: matsfunk Date: Sun, 10 Apr 2016 16:17:21 +0200 Subject: [PATCH] added state machine implementation to OOCSI --- src/nl/tue/id/oocsi/OOCSI.java | 9 ++ src/nl/tue/id/oocsi/StateMachine.java | 103 +++++++++++++++++++++++ test/state/TestState.java | 113 ++++++++++++++++++++++++++ 3 files changed, 225 insertions(+) create mode 100644 src/nl/tue/id/oocsi/StateMachine.java create mode 100644 test/state/TestState.java diff --git a/src/nl/tue/id/oocsi/OOCSI.java b/src/nl/tue/id/oocsi/OOCSI.java index afc5dfa..2a77869 100644 --- a/src/nl/tue/id/oocsi/OOCSI.java +++ b/src/nl/tue/id/oocsi/OOCSI.java @@ -230,6 +230,15 @@ public String getChannels(String channelName) { return oocsi.isConnected() ? oocsi.channels(channelName) : ""; } + /** + * retrieve the internal OOCSICommunicator + * + * @return + */ + OOCSICommunicator getCommunicator() { + return oocsi; + } + /** * start OOCSI with a connection and handlers * diff --git a/src/nl/tue/id/oocsi/StateMachine.java b/src/nl/tue/id/oocsi/StateMachine.java new file mode 100644 index 0000000..3626b30 --- /dev/null +++ b/src/nl/tue/id/oocsi/StateMachine.java @@ -0,0 +1,103 @@ +package nl.tue.id.oocsi; + +import nl.tue.id.oocsi.client.protocol.EventHandler; +import nl.tue.id.oocsi.client.protocol.Handler; + +public class StateMachine { + + private nl.tue.id.oocsi.client.behavior.state.OOCSIStateMachine sm; + + private OOCSICommunicator oocsi; + + public StateMachine(OOCSI oocsi) { + this.oocsi = oocsi.getCommunicator(); + sm = new nl.tue.id.oocsi.client.behavior.state.OOCSIStateMachine(); + } + + public State addState(String name, String enterAction, String executeAction, String exitAction) { + Handler enter = oocsi.createSimpleCallerHandler(enterAction); + Handler execute = oocsi.createSimpleCallerHandler(executeAction); + Handler exit = oocsi.createSimpleCallerHandler(exitAction); + + sm.addState(name, enter, execute, exit); + return new State(name); + } + + public State addState(String name) { + sm.addState(name, null, null, null); + return new State(name); + } + + public void execute() { + sm.execute(); + } + + public boolean isInState(String state) { + return sm.isInState(state); + } + + public String get() { + return sm.get(); + } + + public void set(String newState) { + sm.set(newState); + } + + public class State { + + private String name; + + public State(String name) { + this.name = name; + } + + public State enter(String enterAction) { + sm.get(name).setEnter(oocsi.createSimpleCallerHandler(enterAction)); + return this; + } + + public State execute(String executeAction) { + sm.get(name).setExecute(oocsi.createSimpleCallerHandler(executeAction)); + return this; + } + + public State exit(String exitAction) { + sm.get(name).setExit(oocsi.createSimpleCallerHandler(exitAction)); + return this; + } + + public void connect(String channelName, final String key) { + oocsi.subscribe(channelName, new EventHandler() { + @Override + public void receive(OOCSIEvent event) { + if (event.has(key)) { + sm.set(name); + } + } + }); + } + + public void connect(String channelName, final String key, final Number value) { + oocsi.subscribe(channelName, new EventHandler() { + @Override + public void receive(OOCSIEvent event) { + if (event.has(key) && event.getDouble(key, Double.MAX_VALUE) == value.doubleValue()) { + sm.set(name); + } + } + }); + } + + public void connect(String channelName, final String key, final String value) { + oocsi.subscribe(channelName, new EventHandler() { + @Override + public void receive(OOCSIEvent event) { + if (event.has(key) && event.getString(key, "").equals(value)) { + sm.set(name); + } + } + }); + } + } +} diff --git a/test/state/TestState.java b/test/state/TestState.java new file mode 100644 index 0000000..32d574a --- /dev/null +++ b/test/state/TestState.java @@ -0,0 +1,113 @@ +package state; + +import nl.tue.id.oocsi.OOCSI; +import nl.tue.id.oocsi.StateMachine; +import processing.core.PApplet; + +/** + * example application - proposes a leader to be elected and waits for votes from other clients in the same channel + * "electionChannel" via OOCSI + * + * @author matsfunk + */ +@SuppressWarnings("serial") +public class TestState extends PApplet { + + OOCSI oocsi; + StateMachine sm; + + int brightness = 255; + + public void setup() { + size(200, 200); + noStroke(); + background(0); + frameRate(30); + + // open connection to local OOCSI + // (for more information how to run an OOCSI server refer to: https://iddi.github.io/oocsi/) + oocsi = new OOCSI(this, ("state_" + Math.random()).toString().substring(0, 20), "localhost"); + + // create state machine + sm = new StateMachine(oocsi); + + // add different states + + // add state normal constructor version + sm.addState("rect", "blueBackground", "drawRect", "lightFill").connect("state_channel", "cornered"); + sm.addState("triangle", "redBackground", "drawTriangle", "darkFill").connect("state_channel", "pointy"); + + // add state, telescope version + sm.addState("ellipse").enter("redBackground").execute("drawEllipse").connect("state_channel", "round"); + + // set starting state to "ellipse" state + sm.set("ellipse"); + } + + public void draw() { + // fade + fill(0, 10); + rect(0, 0, width, height); + + // move to center and rotate + translate(width / 2f, height / 2f); + rotate(radians(frameCount)); + fill(brightness, 70); + + // call update of state machine + sm.execute(); + } + + public void redBackground() { + background(255, 10, 10); + } + + public void blueBackground() { + background(10, 10, 250); + } + + public void lightFill() { + brightness = 255; + } + + public void darkFill() { + brightness = 110; + } + + public void drawEllipse() { + ellipse(15, 0, 30, 30); + } + + public void drawRect() { + rect(0, 0, 30, 30); + } + + public void drawTriangle() { + triangle(0, 0, 30, 30, 0, 30); + } + + // allow for mouse presses to forget all roles for this client + public void mousePressed() { + if (frameCount % 3 == 0) { + oocsi.channel("state_channel").data("cornered", "").send(); + sm.set("rect"); + } else if (frameCount % 3 == 1) { + oocsi.channel("state_channel").data("round", "").send(); + sm.set("ellipse"); + } else { + oocsi.channel("state_channel").data("pointy", "").send(); + sm.set("triangle"); + } + } + + public static void main(String[] args) { + PApplet.main(new String[] { "--location=200,100", "state.TestState" }); + PApplet.main(new String[] { "--location=200,320", "state.TestState" }); + PApplet.main(new String[] { "--location=400,100", "state.TestState" }); + PApplet.main(new String[] { "--location=400,320", "state.TestState" }); + PApplet.main(new String[] { "--location=600,100", "state.TestState" }); + PApplet.main(new String[] { "--location=600,320", "state.TestState" }); + PApplet.main(new String[] { "--location=800,100", "state.TestState" }); + PApplet.main(new String[] { "--location=800,320", "state.TestState" }); + } +}