I have a file browser type application consisting of a CellTree displaying the hierarchy and a browser pane showing the contents of the current node.
The application has to handle hierarchies which may be too large for the CellTree to handle effectively, so the location shown in the browser pane is not strongly coupled to the open nodes in the tree.
I wish to implement a button which will open the current browse location in the CellTree. My server side data model has parent/child references on all objects so obtaining the hierarchy is a simple matter, but I am struggling with how to apply this to the tree.
This is my current attempt, it is a method on the widget which contains my tree, and is called from the reciever. The RecordTreeLevel objects each represent a level in the tree.
public void navigateTo(List<RecordTreeLevelProxy> hierarchy) {
TreeNode currentNode = tree.getRootTreeNode();
ListIterator iter = hierarchy.listIterator(hierarchy.size());
while (iter.hasPrevious()){
RecordTreeLevelProxy level = (RecordTreeLevelProxy) iter.previous();
Integer nodeIndex = locateNode(level.getUniqueRef(), currentNode);
if (nodeIndex != null){
currentNode = currentNode.setChildOpen(nodeIndex, true, true);
}
}
}
This doesn't work, because the loop completes in its entirity before the children are added to any of the nodes. Therefore the first level node opens as it should, but doesn't have any children when the loop comes around the second time.
I can't help but feel that I'm missing something obvious, or doing this in the wrong way, but I can't see it. Any help much apprieciated (even if it's to say "don't be silly, you can't do it that way)
Your code should work if you populate the data synchronously from your TreeViewModel (provided locateNode does what I think it does, i.e. iterate over currentNode.getChildValue(i) with i between 0 and currentNode.getChildCount()).
What I'm doing in a similar scenario (I'm actually doing it with a CellBrowser) is that I preload the cache (I'm caching the response for the various branches of the tree) using the hierarchy to the selected node, and then run code very similar to yours. The TreeViewModel runs synchronously if the value is in the cache, asynchronously otherwise (asking the server for the children of the opened node).
For example, for the following tree:
- A
+- A1
+- A1a
+- A2
+- A2a
+- A2b
- B
+- B1
+- B2
If the A2a node should be pre-selected, I start by load the root branch, the A branch, then the A2 branch:
<root> -> [ A, B ]
A -> [ A1, A2 ]
A2 -> [ A2a, A2b ]
That way, I'm sure my TreeViewModel will run synchronously for the nodes <root>, A and A2.
And then only I run the setChildOpen code. Because the TreeViewModel is synchronous for these nodes, setChildOpen returns a non-null value.
Related
Here is the raw code but I will explain it roughly:
func processTile(_ tile: Tile) {
// Add tile to pos list if valid, otherwise prune the
branch
if isBarrier(tile) || alreadyChecked(tile) {
return
} else {
posList.append(tile.pos)
}
depth++ ; if depth > maxDepth { return }
for neighborTile in neighbors(of: tilePos) {
processTile(neighborTile)
}
}
This is the main recursive function. Basically it checks if a tile is part of a room by being neither a barrier tile or already checked. If either one of those cases is true then the branch of the flood tree is pruned and ended. If the tile was valid and was added to the list then the depth is incremented with a check on it. Then the tile's neighbors are all checked with the same process.
For some reason the flood algorithm just stops randomly. I changed the code adding a print() in front of the returns to see why a branch just stops. This worked for all the correct pruning cases where it hits a barrier or already checked tile, but when it would just randomly stop and not check a tile it didn't even log anything.
The only thing I can think of is that Swift stops executing the functions after a certain level of recursion. If this is the case does anyone know what the maximum number of recursions is? And what a possible workaround would be. I'm thinking you would have to make a list of all the "edge" tiles of the flood, and a list of all the "interior" tiles of the flood. Then have a while loop running until there are no edge tiles which just expands the flood.
I am using a GraphMachine to model a workflow of a MongoDB record.
I am only storing the state in MongoDB and when I am reloading at a later time, I use the set_state() option on the machine to force it back to where it was left off.
This all works correctly except when I try to show the state machine graph.
After loading it always shows itself in the initial state even though it seems it did accept the set_state because transitions are accepted as if it was in the restored state.
Lets say I have a simple linear FSM like: S0 -> S1 -> S2 -> S3 -> S3 -> S0.
S0 is the initial state, and S2 is where it was saved.
When I restore, it always graphs itself in S0, but if I try to make the S2->S3 transition, it accepts it. When I make the graph afterwards, it is in the correct S3 state.
Is there a way I can make the GraphMachine 'initialize' to the correct state?
Thanks
Machine.set_state will hard set the model state but won't call necessary callbacks to regenerate the graph. You can either pass the initial state to the constructor or force a recreation of the graph after set_state:
from transitions.extensions import GraphMachine
states = ["A", "B", "C"]
m1 = GraphMachine(states=states, initial="A", ordered_transitions=True, show_state_attributes=True)
m1.next_state()
m2 = GraphMachine(states=states, initial=m1.state, ordered_transitions=True)
m2.get_graph().draw("machine2.png")
m1.set_state("C")
m1.get_graph(force_new=True).draw("machine1.png")
I'm working on an epidemic model. In this model, there are 2 types of susceptible states, S1 and S2. At the start of the model, I want there to be agents in S1 state as well as S2 state.
For example, I have total 1000 agents at first, and I want there to be 200 in S1 and 800 in S2.
I tried to set S1 as the start state and create a message transition from S1 to S2, then coded in main as:
for(int i = 0; i < 1000*0.8; i++)
send("s1 to s2", people.random());
But it will cause repeat message to the same agent, thus there won't be 800 in S2 state. Also I think it's not a good design.
You need some code to do selection-without-replacement so you don't choose the same agent multiple times. There's nothing built-in to AnyLogic to do this for you, so you'd want code something like the below (assuming your agents are of type Person).
// Create selection list with all Person agents in
List<Person> selectionList = new ArrayList<Person>();
for (Person p : people) {
selectionList.add(p);
}
// Select one 800 times (removing them from our selection list as we go)
for (int i = 0; i < 800; i++) {
int randomIndex = uniform_discr(0, selectionList.size() - 1);
send ("s1 to s2", selectionList.get(randomIndex));
selectionList.remove(randomIndex);
}
The crucial thing to remember (and something that many AnyLogic modellers don't understand) is that messages to statecharts are processed asynchronously (in hidden timeout 0 events which you can see on the Events View at runtime) however you send them (e.g., whether you use send, receive or the statechart's fireEvent function --- these just affect the number of such intermediate events and whether the "On message received" action of the Connections element is triggered or not).
Thus an agent will never have changed state in a block of code which sends messages to them. (It will only do so after this block of code --- and the current event it is triggered from --- completes.)
P.S. As per your comment to Emile's (incorrect) answer, the other way to do this is via dynamic events so that each message is sent after previous ones have been processed. But then you have to be very careful that you get the at-the-same-sim-time event ordering correct (loads of subtle detail here) and you'd still have to do the filtering in Emile's answer which is very wasteful; much easier to do it the more conceptually correct way where you whittle down the set of agents you're sampling from as you go.
Why is the conversation going back to [root] node ?
Thank you.
The very first node (to the immediate right of "Conversation starts") is being triggered for your first input (with intent #DevoxxUS and #location:US).
The next input has an intent of #whereUS, however, there is only one child of the previous node, and that child will only trigger on #whenUS.
So, the runtime checks the child, which doesn't trigger, and since there are no more children (note it doesn't go down the tree to grandchildren, etc.) it returns back to the root to find an appropriate node.
To prevent Conversation from dropping back to the root node, have the last node in your tree with a condition true. This will always be hit, and move you onto the next part of your tree.
For example:
Typing Hot and Hot again will return "One" and "Five", and your next node will be the branch from "Five".
I spoke with Sandhya and it looks like her app isn't passing in the context to the next request so it falls back to the default response and doesn't continue the conversation. When testing through a REST client, it works fine.
I have an e4 application that has two perspective:
Operations
Configuration -> Contains (among other things) a part stack where the configurations are open. Each configuration in a part.
When a new model is loaded all configuration parts are to be closed. This works fine if a load the new model when the configuration perspective is active.
However, if I open some configurations in the Configuration perspective. Switch to the Operations perspective and load a new model.
I can see in the logs that the code to close the parts is called and everything seems to be alright. However, when i switch back to the configuration perspective the parts are still visible an open.
Could somebody tell me how to make sure that the parts are close, regardless of the which is the active perspective?
I found a "workaround" to solve my issue.
I had an event thrown to detect the model load as follows and use it to "close"/hide the parts:
#Inject #Optional
void modelLoadedHandler(#UIEventTopic(STUConstants.UI_TOPIC_CONFIG_LOADED) Object nothing) {
viewer.setInput(sleConfigService);
//Close open config parts
MPartStack stack = (MPartStack) modelService
.find(STUConstants.PART_STACK_ID_CONFIG_VIEW,
application);
List<MStackElement> parts = new ArrayList<>(stack.getChildren());
MPart mpart;
for (MStackElement element : parts) {
mpart = (MPart) element;
log.error("Removing part {} visible {}", mpart.getElementId(), mpart.isVisible());
partService.hidePart(mpart, true);
}
// Adding this make it work regardless of which perspective is
// active.
stack.getChildren().clear();
}
Adding the stack.getChildren().clear(); did the trick. I am not hundred percent whether that would be the right way to deal with this, as i would have though that the PartStack should be emptied automatically when i remove a part.