Improves FSM design and adds tools to diff design and implementation.

* Resolves conflicts between designs and implementation
* Adding fsm_diff to network_ui/tools
* Add extract.js for FSM toolchain to network_ui
This commit is contained in:
Ben Thomasson
2017-10-24 09:06:01 -04:00
parent 56991552d2
commit 09d461b1d0
19 changed files with 475 additions and 193 deletions

View File

@@ -1,7 +1,6 @@
/bundle.js
/node_modules
/style.css
/extract.js
/css
/js
/src-instrumented

View File

@@ -34,3 +34,40 @@ simple-server: lint main lessc
deploy: main
rsync -av src/ ../../../../awx/ui/client/src/network_ui/
extract:
mkdir -p extracted
./extract.js ./src/button.js > extracted/button.yml
./extract.js ./src/buttons.js > extracted/buttons.yml
./extract.js ./src/device.detail.fsm.js > extracted/device_detail.yml
./extract.js ./src/group.js > extracted/group.yml
./extract.js ./src/hotkeys.fsm.js > extracted/hotkeys.yml
./extract.js ./src/link.js > extracted/link.yml
./extract.js ./src/mode.fsm.js > extracted/mode.yml
./extract.js ./src/move.js > extracted/move.yml
./extract.js ./src/null.fsm.js > extracted/null.yml
./extract.js ./src/rack.fsm.js > extracted/rack.yml
./extract.js ./src/site.fsm.js > extracted/site.yml
./extract.js ./src/stream.fsm.js > extracted/stream.yml
./extract.js ./src/toolbox.fsm.js > extracted/toolbox.yml
./extract.js ./src/view.js > extracted/view.yml
./extract.js ./src/time.js > extracted/time.yml
diff:
fsm_diff designs/button.yml extracted/button.yml
fsm_diff designs/buttons.yml extracted/buttons.yml
fsm_diff designs/device_detail.yml extracted/device_detail.yml
fsm_diff designs/group.yml extracted/group.yml
fsm_diff designs/hotkeys.yml extracted/hotkeys.yml
fsm_diff designs/link.yml extracted/link.yml
fsm_diff designs/mode.yml extracted/mode.yml
fsm_diff designs/move.yml extracted/move.yml
fsm_diff designs/null.yml extracted/null.yml
fsm_diff designs/rack.yml extracted/rack.yml
fsm_diff designs/site.yml extracted/site.yml
fsm_diff designs/stream.yml extracted/stream.yml
fsm_diff designs/time.yml extracted/time.yml
fsm_diff designs/toolbox.yml extracted/toolbox.yml
fsm_diff designs/view.yml extracted/view.yml

View File

@@ -17,7 +17,7 @@ states:
y: 491
transitions:
- from_state: Start
label: ''
label: start
to_state: Ready
- from_state: Ready
label: onMouseDown

View File

@@ -1,6 +1,10 @@
finite_state_machine_id: 102
finite_state_machine_id: 6
name: fsm
states:
- id: 11
label: Disable
x: 497
y: 84
- id: 1
label: Resize
x: 571
@@ -25,14 +29,6 @@ states:
label: Move
x: 1297
y: 786
- id: 7
label: Ready
x: 740
y: 324
- id: 8
label: EditLabel
x: 1056
y: 148
- id: 9
label: Selected2
x: 1179
@@ -41,58 +37,75 @@ states:
label: Placing
x: 410
y: 295
- id: 7
label: Ready
x: 733
y: 304
- id: 8
label: EditLabel
x: 1130
y: 112
transitions:
- from_state: Ready
label: onMouseDown
to_state: Selected1
- from_state: EditLabel
label: onMouseDown
to_state: Ready
- from_state: Selected2
label: onMouseDown
to_state: Ready
- from_state: Selected2
label: onMouseDown
to_state: Selected3
- from_state: Selected1
label: onMouseUp
to_state: Selected2
- from_state: Move
label: onMouseUp
to_state: Selected2
- from_state: Selected1
label: onMouseMove
to_state: Move
- from_state: Start
label: start
to_state: Ready
- from_state: Ready
label: onMouseDown
to_state: CornerSelected
- from_state: Selected3
label: onMouseMove
to_state: Move
- from_state: Selected3
label: onMouseUp
to_state: EditLabel
- from_state: Ready
label: onNewGroup
to_state: Placing
- from_state: Placing
label: onMouseDown
to_state: CornerSelected
- from_state: CornerSelected
label: onMouseMove
to_state: Resize
- from_state: Resize
label: onMouseUp
to_state: Selected1
- from_state: Selected2
label: onNewGroup
to_state: Ready
- from_state: CornerSelected
label: onMouseUp
to_state: Selected1
- from_state: Placing
label: onMouseDown
to_state: CornerSelected
- from_state: Ready
label: onMouseDown
to_state: CornerSelected
- from_state: Move
label: onMouseDown
to_state: Selected1
- from_state: CornerSelected
label: onMouseUp
to_state: Selected1
- from_state: Resize
label: onMouseUp
to_state: Selected1
- from_state: Ready
label: onMouseDown
to_state: Selected1
- from_state: Selected2
label: onMouseDown
to_state: Selected3
- from_state: Selected3
label: onMouseMove
to_state: Move
- from_state: Selected1
label: onMouseMove
to_state: Move
- from_state: EditLabel
label: onKeyDown
to_state: Selected2
- from_state: Ready
label: onPasteGroup
to_state: Selected2
- from_state: Move
label: onMouseUp
to_state: Selected2
- from_state: Selected1
label: onMouseUp
to_state: Selected2
- from_state: Ready
label: onNewGroup
to_state: Placing
- from_state: Selected2
label: onKeyDown
to_state: Ready
- from_state: Selected2
label: onNewGroup
to_state: Ready
- from_state: Start
label: start
to_state: Ready
- from_state: Selected2
label: onMouseDown
to_state: Ready
- from_state: EditLabel
label: onMouseDown
to_state: Ready
- from_state: Selected3
label: onMouseUp
to_state: EditLabel

View File

@@ -1,32 +1,27 @@
app: link
panX: -15
panY: 0
scaleXY: 1
finite_state_machine_id: 10
name: src/link
states:
- label: Ready
size: 100
x: 540
y: 307
- label: Start
size: 100
x: 533
y: 96
- label: Selecting
size: 100
x: 780
y: 299
- label: Connecting
size: 100
x: 782
y: 541
- label: Connected
size: 100
x: 546
y: 543
- id: 5
label: Selecting
x: -429
y: 63
- id: 2
label: Start
x: 15
y: -221
- id: 4
label: Connecting
x: -429
y: 466
- id: 3
label: Connected
x: 47
y: 453
- id: 1
label: Ready
x: 26
y: 61
transitions:
- from_state: Start
label: start
to_state: Ready
- from_state: Ready
label: onNewLink
to_state: Selecting
@@ -36,6 +31,12 @@ transitions:
- from_state: Connecting
label: onMouseUp
to_state: Connected
- from_state: Connected
label: free
- from_state: Connecting
label: onMouseUp
to_state: Ready
- from_state: Connected
label: start
to_state: Ready
- from_state: Start
label: start
to_state: Ready

View File

@@ -1,6 +1,10 @@
finite_state_machine_id: 106
finite_state_machine_id: 13
name: move
states:
- id: 9
label: Disable
x: 743
y: 108
- id: 1
label: Start
x: 533
@@ -34,54 +38,57 @@ states:
x: 92
y: 82
transitions:
- from_state: Selected2
label: onKeyDown
to_state: Ready
- from_state: Selected2
label: onNewDevice
to_state: Ready
- from_state: EditLabel
label: onMouseDown
to_state: Ready
- from_state: Selected2
label: onMouseDown
to_state: Ready
- from_state: Start
label: start
to_state: Ready
- from_state: Ready
- from_state: Move
label: onMouseDown
to_state: Selected1
- from_state: Selected1
label: onMouseUp
to_state: Selected2
- from_state: Selected1
label: onMouseMove
to_state: Move
- from_state: Selected2
- from_state: Placing
label: onMouseDown
to_state: Ready
to_state: Selected1
- from_state: Move
label: onMouseUp
to_state: Selected1
- from_state: Ready
label: onMouseDown
to_state: Selected1
- from_state: Ready
label: onPasteDevice
to_state: Selected2
- from_state: EditLabel
label: onKeyDown
to_state: Selected2
- from_state: EditLabel
label: onMouseDown
to_state: Ready
- from_state: Selected2
label: onMouseDown
to_state: Selected3
- from_state: Selected1
label: onMouseUp
to_state: Selected2
- from_state: Placing
label: onMouseMove
to_state: Move
- from_state: Selected3
label: onMouseMove
to_state: Move
- from_state: Selected1
label: onMouseMove
to_state: Move
- from_state: Selected3
label: onMouseUp
to_state: EditLabel
- from_state: Placing
label: onMouseMove
to_state: Move
- from_state: Selected2
label: onMouseDown
to_state: Selected3
- from_state: Ready
label: onNewDevice
to_state: Placing
- from_state: Placing
label: onMouseDown
to_state: Selected1
- from_state: Move
label: onMouseDown
to_state: Selected1
- from_state: Selected2
label: onNewDevice
to_state: Ready
- from_state: Selected2
label: onKeyDown
to_state: Ready

View File

@@ -1,7 +1,7 @@
finite_state_machine_id: 89
finite_state_machine_id: 11
name: src/time
states:
- id: 3
- id: 1
label: Present
x: 256
y: 123
@@ -9,14 +9,11 @@ states:
label: Start
x: 245
y: -161
- id: 1
- id: 3
label: Past
x: -115
y: 129
transitions:
- from_state: Past
label: onMessage
to_state: Present
- from_state: Past
label: onRedo
to_state: Present
@@ -29,9 +26,6 @@ transitions:
- from_state: Start
label: start
to_state: Present
- from_state: Present
label: onMessage
to_state: Past
- from_state: Present
label: onUndo
to_state: Past

View File

@@ -1,49 +1,62 @@
finite_state_machine_id: 120
finite_state_machine_id: 14
name: toolbox
states:
- id: 6
label: Dropping
x: 1197
y: 427
- id: 4
label: Selected
x: 889
y: 713
- id: 2
label: Selected
x: 1180
y: 959
- id: 6
label: Move
x: 1409
y: 741
- id: 3
label: Ready
x: 892
y: 429
- id: 3
- id: 4
label: Scrolling
x: 567
y: 431
- id: 1
- id: 5
label: Start
x: 892
y: 216
- id: 5
label: Move
x: 1197
y: 708
- id: 7
label: Selecting
x: 888
y: 710
- id: 1
label: Dropping
x: 1358
y: 431
transitions:
- from_state: Start
label: start
to_state: Ready
- from_state: Move
label: onMouseUp
to_state: Dropping
- from_state: Dropping
label: start
to_state: Ready
- from_state: Ready
- from_state: Selecting
label: onMouseDown
to_state: Selected
- from_state: Ready
label: onMouseWheel
to_state: Scrolling
- from_state: Scrolling
label: start
to_state: Ready
- from_state: Selected
label: onMouseMove
to_state: Move
- from_state: Selecting
label: onMouseDown
to_state: Ready
- from_state: Selected
label: onMouseUp
to_state: Ready
- from_state: Dropping
label: start
to_state: Ready
- from_state: Start
label: start
to_state: Ready
- from_state: Scrolling
label: onMouseWheel
to_state: Ready
- from_state: Ready
label: onMouseWheel
to_state: Scrolling
- from_state: Ready
label: onMouseDown
to_state: Selecting
- from_state: Move
label: onMouseUp
to_state: Dropping

View File

@@ -1,38 +1,36 @@
app: view
panX: 1
panY: -67
scaleXY: 1
finite_state_machine_id: 15
name: view
states:
- label: Start
size: 100
- id: 1
label: Start
x: 498
y: 175
- label: Ready
size: 100
- id: 2
label: Ready
x: 506
y: 395
- label: Scale
size: 100
- id: 3
label: Scale
x: 310
y: 626
- label: Pan
size: 100
- id: 4
label: Pan
x: 741
y: 631
- label: Pressed
size: 100
- id: 5
label: Pressed
x: 739
y: 392
transitions:
- from_state: Scale
label: onMouseWheel
to_state: Ready
- from_state: Start
label: start
to_state: Ready
- from_state: Ready
label: onMouseWheel
to_state: Scale
- from_state: Scale
label: onTimeout
to_state: Ready
- from_state: Ready
label: onMouseDown
to_state: Pressed

View File

@@ -0,0 +1,47 @@
#!/usr/bin/env node
var YAML = require('yamljs');
function Iterator(o){
var k=Object.keys(o);
return {
next:function(){
return k.shift();
}
};
}
var myArgs = process.argv.slice(2);
var implementation = require(myArgs[0]);
var states = [];
var transitions = [];
var data = {states: states,
transitions: transitions};
var state_iter = Iterator(implementation);
var transition_iter = null;
var next_state = state_iter.next();
var next_transition = null;
var state = null;
var transition = null;
var i = 0;
while(next_state !== undefined) {
state = implementation[next_state];
transition_iter = Iterator(state.constructor.prototype)
next_transition = transition_iter.next();
while (next_transition !== undefined) {
transition = state.constructor.prototype[next_transition];
if (transition.transitions !== undefined) {
for (i = 0; i < transition.transitions.length; i++) {
transitions.push({from_state: next_state,
to_state:transition.transitions[i],
label:next_transition});
}
}
next_transition = transition_iter.next();
}
states.push({label: state.name});
next_state = state_iter.next();
}
console.log(YAML.stringify(data));

View File

@@ -2,8 +2,8 @@
<html ng-app="networkUI">
<head>
<link rel="stylesheet" href="css/style.css" />
<script src="js/bundle.js"></script>
<script src="js/vendor.bundle.js"></script>
<script src="js/bundle.js"></script>
</head>
<body style="margin: 0px">
<awx-network-ui></awx-network-ui>

View File

@@ -52,6 +52,7 @@ _Ready.prototype.onNewLink = function (controller, msg_type, message) {
controller.changeState(Selecting);
controller.next_controller.handle_message(msg_type, message);
};
_Ready.prototype.onNewLink.transitions = ['Selecting'];
@@ -60,6 +61,7 @@ _Start.prototype.start = function (controller) {
controller.changeState(Ready);
};
_Start.prototype.start.transitions = ['Ready'];
@@ -68,6 +70,7 @@ _Connected.prototype.start = function (controller) {
controller.scope.clear_selections();
controller.changeState(Ready);
};
_Connected.prototype.start.transitions = ['Ready'];
_Connecting.prototype.onMouseDown = function () {
@@ -121,6 +124,7 @@ _Connecting.prototype.onMouseUp = function (controller) {
controller.changeState(Ready);
}
};
_Connecting.prototype.onMouseUp.transitions = ['Ready', 'Connected'];
_Selecting.prototype.onMouseDown = function () {
@@ -135,4 +139,5 @@ _Selecting.prototype.onMouseUp = function (controller) {
controller.changeState(Connecting);
}
};
_Selecting.prototype.onMouseUp.transitions = ['Connecting'];

View File

@@ -101,7 +101,7 @@ _Selected.prototype.onMouseUp = function (controller) {
toolbox.selected_item = null;
controller.changeState(Ready);
};
_Selected.prototype.onMouseUp.transitions = ['Move'];
_Selected.prototype.onMouseUp.transitions = ['Ready'];
_Selecting.prototype.onMouseDown = function (controller) {
@@ -133,7 +133,7 @@ _Selecting.prototype.onMouseDown = function (controller) {
}
};
_Selecting.prototype.onMouseDown.transitions = ['Selected'];
_Selecting.prototype.onMouseDown.transitions = ['Selected', 'Ready'];
_Ready.prototype.onMouseDown = function (controller, msg_type, $event) {
@@ -182,7 +182,7 @@ _Scrolling.prototype.onMouseWheel = function (controller, msg_type, $event) {
controller.changeState(Ready);
};
_Scrolling.prototype.start.transitions = ['Ready'];
_Scrolling.prototype.onMouseWheel.transitions = ['Ready'];

View File

@@ -52,6 +52,7 @@ _Ready.prototype.onMouseDown = function (controller) {
controller.changeState(Pressed);
};
_Ready.prototype.onMouseDown.transitions = ['Pressed'];
_Ready.prototype.onTouchStart = function (controller, msg_type, event) {
@@ -79,12 +80,14 @@ _Ready.prototype.onTouchStart = function (controller, msg_type, event) {
controller.changeState(Pressed);
}
};
_Ready.prototype.onTouchStart.transitions = ['Pressed'];
_Ready.prototype.onMouseWheel = function (controller, msg_type, $event) {
controller.changeState(Scale);
controller.handle_message(msg_type, $event);
};
_Ready.prototype.onMouseWheel.transitions = ['Scale'];
_Start.prototype.start = function (controller) {
@@ -92,6 +95,7 @@ _Start.prototype.start = function (controller) {
controller.changeState(Ready);
};
_Start.prototype.start.transitions = ['Ready'];
_Scale.prototype.onMouseWheel = function (controller, msg_type, message) {
var delta = message[1];

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'Ben Thomasson'
__email__ = 'benthomasson@gmail.com'
__version__ = '0.1.0'

View File

@@ -0,0 +1,79 @@
"""
Usage:
fsm_diff [options] <a> <b> [<output>]
Options:
-h, --help Show this page
--debug Show debug logging
--verbose Show verbose logging
"""
from docopt import docopt
import logging
import sys
import yaml
logger = logging.getLogger('cli')
def fsm_diff(a, b, silent=True):
a_states = {x['label'] for x in a['states']}
b_states = {x['label'] for x in b['states']}
missing_in_a = b_states - a_states
missing_in_b = a_states - b_states
if (missing_in_b) and not silent:
print "Extra states in a:\n ", "\n ".join(list(missing_in_b))
if (missing_in_a) and not silent:
print "Extra states in b:\n ", "\n ".join(list(missing_in_a))
new_states = missing_in_b.union(missing_in_a)
a_transitions = {tuple(sorted(x.items())) for x in a['transitions']}
b_transitions = {tuple(sorted(x.items())) for x in b['transitions']}
missing_in_a = b_transitions - a_transitions
missing_in_b = a_transitions - b_transitions
if (missing_in_b) and not silent:
print "Extra transitions in a:\n ", "\n ".join(map(str, missing_in_b))
if (missing_in_a) and not silent:
print "Extra transitions in b:\n ", "\n ".join(map(str, missing_in_a))
new_transitions = missing_in_b.union(missing_in_a)
data = dict(states=[dict(label=x) for x in list(new_states)],
transitions=[dict(x) for x in list(new_transitions)])
return data
def main(args=None):
if args is None:
args = sys.argv[1:]
parsed_args = docopt(__doc__, args)
if parsed_args['--debug']:
logging.basicConfig(level=logging.DEBUG)
elif parsed_args['--verbose']:
logging.basicConfig(level=logging.INFO)
else:
logging.basicConfig(level=logging.WARNING)
with open(parsed_args['<a>']) as f:
a = yaml.load(f.read())
with open(parsed_args['<b>']) as f:
b = yaml.load(f.read())
data = fsm_diff(a, b, silent=False)
if parsed_args['<output>']:
with open(parsed_args['<output>'], 'w') as f:
f.write(yaml.dump(data, default_flow_style=False))
return 0

View File

@@ -0,0 +1,69 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Usage:
fsm_generate_diffs [options] <design> <implementation>
Options:
-h, --help Show this page
--debug Show debug logging
--verbose Show verbose logging
--append Append the newly generated code to the implementation.
"""
from docopt import docopt
import logging
import sys
import fsm_diff.cli
import transform_fsm
import yaml
from jinja2 import FileSystemLoader, Environment
from subprocess import Popen, PIPE
logger = logging.getLogger('fsm_generate_diffs')
def main(args=None):
if args is None:
args = sys.argv[1:]
parsed_args = docopt(__doc__, args)
if parsed_args['--debug']:
logging.basicConfig(level=logging.DEBUG)
elif parsed_args['--verbose']:
logging.basicConfig(level=logging.INFO)
else:
logging.basicConfig(level=logging.WARNING)
implementation = parsed_args['<implementation>']
p = Popen(['./extract.js', implementation], stdout=PIPE)
output = p.communicate()[0]
if p.returncode == 0:
b = yaml.load(output)
else:
return 1
with open(parsed_args['<design>']) as f:
a = yaml.load(f.read())
data = fsm_diff.cli.fsm_diff(a, b)
data = transform_fsm.transform_fsm(data)
env = Environment(loader=FileSystemLoader("templates"))
template = env.get_template('fsm.jst')
if parsed_args['--append']:
with open(implementation, "a") as f:
f.write(template.render(**data))
else:
print (template.render(**data))
return 0
if __name__ == '__main__':
import sys
sys.exit(main(sys.argv[1:]))

View File

@@ -0,0 +1,4 @@
jinja2
docopt
pyyaml

View File

@@ -18,19 +18,7 @@ import yaml
logger = logging.getLogger('transform_fsm')
def main(args=None):
if args is None:
args = sys.argv[1:]
parsed_args = docopt(__doc__, args)
if parsed_args['--debug']:
logging.basicConfig(level=logging.DEBUG)
elif parsed_args['--verbose']:
logging.basicConfig(level=logging.INFO)
else:
logging.basicConfig(level=logging.WARNING)
with open(parsed_args['<input>']) as f:
data = yaml.load(f.read())
def transform_fsm(data):
state_map = dict()
@@ -50,6 +38,24 @@ def main(args=None):
for state in data['states']:
state['functions'] = sorted(state['functions'].items())
return data
def main(args=None):
if args is None:
args = sys.argv[1:]
parsed_args = docopt(__doc__, args)
if parsed_args['--debug']:
logging.basicConfig(level=logging.DEBUG)
elif parsed_args['--verbose']:
logging.basicConfig(level=logging.INFO)
else:
logging.basicConfig(level=logging.WARNING)
with open(parsed_args['<input>']) as f:
data = yaml.load(f.read())
data = transform_fsm(data)
with open(parsed_args['<output>'], 'w') as f:
f.write(yaml.safe_dump(data, default_flow_style=False))