Skip to content

Commit

Permalink
Merge pull request FlexBE#13 from FlexBE/feature/state_update
Browse files Browse the repository at this point in the history
Update states when source code changes (see FlexBE#10)
  • Loading branch information
pschillinger authored Apr 3, 2018
2 parents 1116e4c + 7dea67d commit 1327ce3
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 26 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "FlexBE App",
"version": "2.0.6",
"version": "2.0.7",
"main": "src/main.js",
"window": {
"icon": "src/img/icon-128.png",
Expand Down
2 changes: 1 addition & 1 deletion package.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<package>
<name>flexbe_app</name>
<version>2.0.6</version>
<version>2.0.7</version>
<description>
flexbe_app provides a user interface (editor + runtime control) for the FlexBE behavior engine.
</description>
Expand Down
143 changes: 137 additions & 6 deletions src/_model/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ State = function(state_name, state_def) {
resolved_parameter_output_values_old = resolved_parameter_output_values;
}

var noMetaFilter = function(element) { return element[0] != '$'; };
var metaFilter = function(element) { return element[0] == '$'; };

var state_name = state_name;
var state_class = state_def.getStateClass();
var state_import = state_def.getStatePath();
Expand All @@ -139,22 +142,22 @@ State = function(state_name, state_def) {
var parameters = state_def.getParameters().clone();
var parameter_values = state_def.getDefaultParameterValues().clone();

var outcomes = state_def.getOutcomes().filter(function(element) { return element[0] != '$'; });
var outcomes = state_def.getOutcomes().filter(noMetaFilter);
var autonomy = state_def.getDefaultAutonomy().clone();

var meta_outcomes = state_def.getOutcomes().filter(function(element) { return element[0] == '$'; });
var meta_outcomes = state_def.getOutcomes().filter(metaFilter);
for (var i=0; i<meta_outcomes.length; ++i) meta_outcomes[i] = meta_outcomes[i].slice(1, meta_outcomes[i].length);

var outcomes_unc = outcomes.clone();
var outcomes_con = [];

var input_keys = state_def.getInputKeys().filter(function(element) { return element[0] != '$'; });
var output_keys = state_def.getOutputKeys().filter(function(element) { return element[0] != '$'; });
var input_keys = state_def.getInputKeys().filter(noMetaFilter);
var output_keys = state_def.getOutputKeys().filter(noMetaFilter);

var meta_input = state_def.getInputKeys().filter(function(element) { return element[0] == '$'; });
var meta_input = state_def.getInputKeys().filter(metaFilter);
for (var i=0; i<meta_input.length; ++i) meta_input[i] = meta_input[i].slice(1, meta_input[i].length);

var meta_output = state_def.getOutputKeys().filter(function(element) { return element[0] == '$'; });
var meta_output = state_def.getOutputKeys().filter(metaFilter);
for (var i=0; i<meta_output.length; ++i) meta_output[i] = meta_output[i].slice(1, meta_output[i].length);

var input_mapping = [];
Expand Down Expand Up @@ -357,4 +360,132 @@ State = function(state_name, state_def) {
return behavior;
}

this.updateStateDefinition = function(new_def) {
state_class = new_def.getStateClass();
state_import = new_def.getStatePath();
state_pkg = new_def.getStatePackage();

var updateKeys = function(old_keys, new_keys, old_values, new_values) {
var result = {keys: [], values: []};
for (var i=0; i<new_keys.length; i++) {
var j = old_keys.indexOf(new_keys[i]);
if (j != -1) {
result.keys.push(old_keys[j]);
result.values.push(old_values[j]);
} else {
result.keys.push(new_keys[i]);
result.values.push(new_values[i]);
}
}
return result;
}

// required for meta
var old_params = parameters;

if (!parameters.hasSameElements(new_def.getParameters())) {
var result = updateKeys(parameters, new_def.getParameters(), parameter_values, new_def.getDefaultParameterValues());
parameters = result.keys;
parameter_values = result.values;
}

if (!outcomes.hasSameElements(new_def.getOutcomes().filter(noMetaFilter))) {
var old_outcomes = outcomes;
var result = updateKeys(outcomes, new_def.getOutcomes().filter(noMetaFilter), autonomy, new_def.getDefaultAutonomy());
outcomes = result.keys;
autonomy = result.values;
for (var i=0; i<outcomes.length; i++) {
if (!old_outcomes.contains(outcomes[i])) {
outcomes_unc.push(outcomes[i]);
}
}
for (var i=0; i<old_outcomes.length; i++) {
if (!outcomes.contains(old_outcomes[i])) {
if (outcomes_con.contains(old_outcomes[i])) {
if (container != undefined) {
container.removeTransitionFrom(that, old_outcomes[i]);
}
outcomes_con.remove(old_outcomes[i]);
} else {
outcomes_unc.remove(old_outcomes[i]);
}
}
}
}

var new_meta_outcomes = new_def.getOutcomes().filter(metaFilter);
for (var i=0; i<new_meta_outcomes.length; ++i) new_meta_outcomes[i] = new_meta_outcomes[i].slice(1, new_meta_outcomes[i].length);
if (!meta_outcomes.hasSameElements(new_meta_outcomes)) {
for (var i=0; i<meta_outcomes.length; i++) {
if (!new_meta_outcomes.contains(meta_outcomes[i])) {
var pidx = old_params.indexOf(meta_outcomes[i]);
var remove_list = resolved_parameter_outcome_values_old[i];
remove_list.forEach(function(element) {
var idx = outcomes.indexOf(element);
outcomes.splice(idx, 1);
autonomy.splice(idx, 1);
if (outcomes_unc.contains(element)) {
outcomes_unc.remove(element);
} else if (outcomes_con.contains(element)) {
outcomes_con.remove(element);
if (container != undefined) {
container.removeTransitionFrom(that, element);
}
}
});
}
}
meta_outcomes = new_meta_outcomes;
updateGeneratedOutcomes(parameter_values);
}

if (!input_keys.hasSameElements(new_def.getInputKeys().filter(noMetaFilter))) {
var result = updateKeys(input_keys, new_def.getInputKeys().filter(noMetaFilter), input_mapping, new_def.getInputKeys().filter(noMetaFilter));
input_keys = result.keys;
input_mapping = result.values;
}

var new_meta_input = new_def.getInputKeys().filter(metaFilter);
for (var i=0; i<new_meta_input.length; ++i) new_meta_input[i] = new_meta_input[i].slice(1, new_meta_input[i].length);
if (!meta_input.hasSameElements(new_meta_input)) {
for (var i=0; i<meta_input.length; i++) {
if (!new_meta_input.contains(meta_input[i])) {
var pidx = old_params.indexOf(meta_input[i]);
var remove_list = resolved_parameter_input_values_old[i];
remove_list.forEach(function(element) {
var idx = input_keys.indexOf(element);
input_keys.splice(idx, 1);
input_mapping.splice(idx, 1);
});
}
}
meta_input = new_meta_input;
updateGeneratedInput(parameter_values);
}

if (!output_keys.hasSameElements(new_def.getOutputKeys().filter(noMetaFilter))) {
var result = updateKeys(output_keys, new_def.getOutputKeys().filter(noMetaFilter), output_mapping, new_def.getOutputKeys().filter(noMetaFilter));
output_keys = result.keys;
output_mapping = result.values;
}

var new_meta_output = new_def.getOutputKeys().filter(metaFilter);
for (var i=0; i<new_meta_output.length; ++i) new_meta_output[i] = new_meta_output[i].slice(1, new_meta_output[i].length);
if (!meta_output.hasSameElements(new_meta_output)) {
for (var i=0; i<meta_output.length; i++) {
if (!new_meta_output.contains(meta_output[i])) {
var pidx = old_params.indexOf(meta_output[i]);
var remove_list = resolved_parameter_output_values_old[i];
remove_list.forEach(function(element) {
var idx = output_keys.indexOf(element);
output_keys.splice(idx, 1);
output_mapping.splice(idx, 1);
});
}
}
meta_output = new_meta_output;
updateGeneratedOutput(parameter_values);
}
}

};
15 changes: 15 additions & 0 deletions src/_model/statemachine.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,21 @@ Statemachine = function(sm_name, sm_definition) {
}
return child.getStateByPath(path.slice(that.getStateName().length + 1));
}
this.traverseStates = function(_filter) {
var result = [];
for(var i=0; i<states.length; ++i) {
if (states[i] instanceof Statemachine) {
result = result.concat(states[i].traverseStates(_filter));
continue;
} else if (states[i] instanceof BehaviorState) {
continue;
}
if (_filter(states[i])) {
result.push(states[i]);
}
}
return result;
}

this.addState = function(state) {
states.push(state);
Expand Down
1 change: 1 addition & 0 deletions src/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ document.addEventListener('DOMContentLoaded', function() {
process.on('exit', (code) => {
RC.PubSub.shutdown();
ROS.shutdown();
IO.PackageParser.stopWatching();
});

nw.App.on('open', () => {
Expand Down
60 changes: 42 additions & 18 deletions src/io/io_packageparser.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ IO.PackageParser = new (function() {
});
}

this.stopWatching = function() {
for (var state in watched_states) {
watched_states[state].close();
}
}

var checkForRelevance = function(pkg_path, callback) {
var data = fs.readFileSync(path.join(pkg_path, 'package.xml'));
var xml = dom_parser.parseFromString(String(data), "text/xml");
Expand All @@ -51,24 +57,42 @@ IO.PackageParser = new (function() {
var watchStateFolder = function(folder_path, import_path) {
if (watched_states[folder_path] != undefined) return;

// watched_states[folder_path] = fs.watch(folder_path,
// {persistent: false},
// (eventType, filename) => {
// if (eventType == 'change') {
// var entry = path.join(folder_path, filename);
// IO.Filesystem.readFile(entry, (content) => {
// var imports = entry.replace(import_path+"/", "").replace(/.py$/i, "").replace(/[\/]/g, ".");
// var state_def = IO.StateParser.parseState(content, imports);
// if (state_def != undefined) {
// state_def.setFilePath(entry);
// WS.Statelib.updateDef(state_def);
// T.logInfo("Updating changed definition for state: " + state_def.getStateClass());
// // TODO update defs for existing states and re-draw
// }
// });
// }
// }
// );
watched_states[folder_path] = fs.watch(folder_path,
{persistent: false},
(eventType, filename) => {
if(RC.Controller.isReadonly()) {
T.logWarn("A state definition source file changed while in read-only mode, ignoring the change for now!");
return;
}
if (eventType == 'change') {
var entry = path.join(folder_path, filename);
IO.Filesystem.readFile(entry, (content) => {
var imports = entry.replace(import_path+"/", "").replace(/.py$/i, "").replace(/[\/]/g, ".");
var state_def = IO.StateParser.parseState(content, imports);
if (state_def != undefined) {
state_def.setFilePath(entry);
WS.Statelib.updateDef(state_def);
T.logInfo("Updating changed definition for state: " + state_def.getStateClass());
var update_states = Behavior.getStatemachine().traverseStates(function(state) {
return state.getStateClass() == state_def.getStateClass();
});
update_states.forEach(function (state) {
state.updateStateDefinition(state_def);
if (state.getContainer() == UI.Statemachine.getDisplayedSM()
&& UI.Panels.StateProperties.isCurrentState(state)) {
UI.Panels.StateProperties.hide();
UI.Panels.StateProperties.displayStateProperties(state);
}
});
if (UI.Menu.isPageStatemachine()) {
UI.Statemachine.refreshView();
}
RC.Controller.signalChanged();
}
});
}
}
);
}

this.parseStateFolder = function(folder, import_path, has_init) {
Expand Down
5 changes: 5 additions & 0 deletions src/prototype.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ Array.prototype.contains = function(element) {
return this.indexOf(element) != -1;
}

Array.prototype.hasSameElements = function(other) {
var diff = this.filter(el => !other.contains(el));
return 0 == diff.concat(other.filter(el => !this.contains(el))).length;
}


//=====================
// String
Expand Down

0 comments on commit 1327ce3

Please sign in to comment.