transient srecord noeq G22NetworkInstance(G22Network network) { new Map valuesByIdentifier; new Map valuesByBlueprint; event dirtyStatesChanged; sclass Value { settable O object; // the actual value calculated by the node settable Timestamp calculatedWhen; // when the value was calculated settable volatile bool dirty; // does this node need recalculation? settable volatile bool calculating; // are we recalculating right now? settable volatile Throwable error; // error during calculation? O get() { ret object; } toString { ret classNameWithIdentity(this) + ": " + object; } bool hasError() { ret error != null; } } void calculateNode(G22Utils g22utils, G22NetworkElement e) { if (e == null) ret; Value value = valueForBlueprint(e); value.calculating(true); try { dirtyStatesChanged(); O instance = e.makeInstance(g22utils, this); print("Calculated: " + instance); value.error(null); setObject(e, value, instance); } catch print exception { value.error(exception); value.dirty(false); } finally { value.calculating(false); dirtyStatesChanged(); } } void setObject(G22NetworkElement e, Value value, O instance) { value.object(instance).calculatedWhen(tsNow()); if (value.dirty()) { value.dirty(false); dirtyStatesChanged(); } invalidateDependentNodes(e); } void init(G22Utils g22utils) { // make empty Value object for each node for (e : network.elements) { new Value value; value.dirty(true); dirtyStatesChanged(); valuesByBlueprint.put(e, value); //print("Put value in map: " + e + " => " + value); mapPut(valuesByIdentifier, e.identifier(), value); } } O getObjectForBlueprint(G22NetworkElement element) { var value = valueForBlueprint(element); O object = value!; printVars("getObjectForBlueprint", +element, +value, +object); ret object; } void invalidateDependentNodes(G22NetworkElement element) { if (element == null) ret; print("invalidateDependentNodes", element); for (e : element.downstreamNodes()) invalidateNode(e); } // a node is calculable if its upstream nodes (the inputs // it needs) are clean bool isCalculable(G22NetworkElement element) { ret all(element.upstreamNodes(), e -> !isDirty(e)); } Value valueForBlueprint(G22NetworkElement element) { ret syncGetOrCreate(valuesByBlueprint, element, -> new Value); } bool isDirty(G22NetworkElement element) { ret valueForBlueprint(element).dirty(); } void invalidateNode(G22NetworkElement element) { print("invalidateNode", element); var value = valueForBlueprint(element); if (value.dirty()) ret; // Node already dirty value.dirty(true); dirtyStatesChanged(); invalidateDependentNodes(element); } // select any calculable node and calculate it G22NetworkElement calculateOneNode(G22Utils g22utils) { var calculableElements = filter( e -> isDirty(e) && isCalculable(e), keys(valuesByBlueprint)); var e = random(calculableElements); calculateNode(g22utils, e); ret e; } }