AnyLogic: how to programmatically populate the Experiment page with information from Database? - anylogic

I have a Supply Chain simulation model where the names and locations of the warehouses are fed from the database that I have in the model. What I want to achieve is the following:
When simulation starts, it copies the warehouse names from the DB and links them to the respective agents on the Experiment page. That is to say, let's say I have two warehouses: WH1, WH2. It should display theses as text on the Experiment page and when I click on WH1, it should take me to the respective agent. I have various plots inside the agents. The idea is to give the user the flexibility to be able to interact the model and take screenshots of the plots of whatever warehouses they wish.
Is this achievable in AnyLogic? If yes, how?
I tried to follow the recommendation of AnyLogic of creating interim variables, but can't really scale this up when I have hundreds of nodes.

Write into the "on click" field of the replicated Text object:

I resolved this issue in the following manner. This creates the list of my agents, sorts them alphabetically by agentName in the Main agent.
Initializing the variables. Here quotient and reminder helps me to keep track of the columns; which of each should contain 60 agent names numberPerColumn.
int row=0;
int numberPerColumn=60;
int quotient=0;
int remainder=0;
int i1=0;
int newIndex=0;
Looping through the agents, creating ShapeText object to display their names.
while (i1<myAgents.size())
{
int ii=i1;
String myAgentName = myAgent.name;
ShapeText text = new ShapeText(
SHAPE_DRAW_2D, true, 300, 300.0, 0.0, 0.0,
black, myAgentName,
font, ALIGNMENT_LEFT )
{
//3. Overriding the onClick() method with the action that I want to happen upon click. In this case, it will take me to the `viewArea` inside `myAgent`.
#Override
public boolean onClick(double x, double y)
{
myAgent.viewArea.navigateTo();
return false;
}};
//4. Setting the objects' position programmatically.
quotient=i1/numberPerColumn;
remainder=i1%numberPerColumn;
text.setPos(quotient*250+50, -4550+remainder*20);
presentation.add(text);
i1+=1;
}

Related

Change State on model runtime

P.S. This Question has been edited to answer questions made by #Felipe
I have an Agent-Based model simulation for churn behavior modeling. On each iteration(based on time--month) each user reconsiders her choice of operator(our or other) based on model metrics (Cost/SocialNetwork/...). In runtime even when I change parameters to affect Agents' decision, no one changes his/her operator. here is my state chart image on the below:
I should note that internal transition of (our user) has below details:
the first two lines are something for display. Advocate() refers to the action of sending messages which affects social influence.
But Switch() is where decision happens based on new parameters' value. In short, d defines a normalized range between -1 and 1 : signum(d) predicts which provider is the preferred one and abs(d) shows how preferred the selected provider will be.
//Definition for Switch()
double d = (this.Social_impact()/20)+this.Monthly_Charge_Impact();
if (d>0)
SwitchToUs();
else
SwitchToOther();
the two SwitchToUs and SwitchToOther functions simply change the operator (as if creating arrows between OUR_USER and OTHER_USER states)

Track collection in Anylogic

I'm building a pedestrian model using Anylogic. I have set my agents move in several groups between S2/S3/S4 and S1 (the movement direction is indicated by the blue two-way arrow in the figure). Background picture for problem statement
I have build a cyclic event and type these code in "action" to collect their track data:
t = time(); //get time
for(Agent p: level.getPeds()){
x = p.getX(); //position x
y = p.getY(); //Position y
id = p.getId(); //get pedestrian id
collectionTime.add(t); //add data
collectionID.add(id);
collectionX.add(x);
collectionY.add(y);
Timeid.add(t,id);
XY.add(x,y);
}
I also add these code in "Main-Agent type_Agent actions_On destroy" to write data into excel file:
TrackCollection.writeDataSet(Timeid,1,1,1); //TrackCollection is the name of excel file
TrackCollection.writeDataSet(XY,1,1,3);
But in this way I can only collect all the trajectories together in the model. How can I collect the tracks of these different groups separately? Or how to collect tracks from different PedSources?
Your question is a bit unclear, nevertheless, if you are creating groups, you can do this after creating a groupId variable in your pedestrian agent.
for(Pedestrian p : group.getPeds()){
p.groupId=group.getId();
}
you can use this groupId and export it to your excel too so you can keep track of the groups

AnyLogic - how to avoid fillSchedule error when the schedule is empty?

I have a simulation model that contains several schedules for moving the trucks between origin and destination stations. The inputs are created automatically by a Python script and in some cases, one or two of the schedules can be empty - meaning that I don't want the trucks move in those directions. An example is given below:
In such cases, the model throws the following error:
Is there a more elegant way of suppressing this error? (i.e. except giving a dummy row as input with all zeros)
The problem here is that since the scehdules are dragged onto the canvas they get created automatically when the Main agent gets created and you have no control over them... you can't do try() catch() and you also can't prevent them from being created if their tables are empty... or just let them be created with a blank value...
You have two options:
Option 1:
So you need to create the schedules programmatically - using the trick described in this article - you can get to the code used to create the schedules.
public Schedule<Integer> schedule = new Schedule<Integer>(
this, true, SUNDAY, 7L * TIME_UNIT_DAY, TIME_UNIT_DAY, null, 0, _schedule_Starts_xjal(), null, _schedule_Values_xjal(), false, null, true, true );
and then the schedule gets populated using
if (schedule.isInitialized()) {
new TableElementDatabaseBuilder(this).setSqlQuery("SELECT time, unit FROM truck_sc_ip").fillSchedule(
schedule,
Integer.class,
true,
true,
604800000L,
false,
false,
3600000L
);
}
You also need to create two variables for each schedule...
Object[] _schedule_Values_xjal() and Object[]_schedule_Starts_xjal()
There might be some other parts of the logic that gets created that I missed here but this should be sufficient.
Option 2:
The alternative is that you simply read in the entries into the DB and create your own Java class that you use as a schedule (my personal preference)

How can I inject agents in a source block from an uploaded database table?

I am trying to inject agents from a database into a specific source block. The database consists of 2 columns of "OrderType" & "OrderAmount". I wish to inject "OrderAmount" of agents of their corresponding "OrderType" into this source, while retaining the differentiation (I.E. by storing a parameter attribute ID of each entry/agent in the corresponding agenttype of the source block).
I have saved the entries from the database in collections and constructed a table as such (For both arrays; type = double):
double [][] ArrayCustomerOrders = new double [coll_CustomerOrderType.size()][2];
for (int i = 0; i < coll_CustomerOrderType.size(); i++) {
ArrayCustomerOrders[i][0] = coll_CustomerOrderType.get(i);
ArrayCustomerOrders[i][1] = coll_CustomerOrderAmount.get(i);
}
I tried playing around with the source block calls of inject() function in the same event as I constructed the order table in, but was unable to inject eligible arguments.
Does anyone have a suggestion on how to go about this?
In your case, you can simply replace the Source block with an Enter block (named "myEnterBlock"):
Create an empty agent population pop_Orders with an agent type that has 2 parameters p_Type and p_Amount.
In your for-loop code, when you have the current order type and amount, create such an agent and push it into the Enter block directly:
myEnterBlock.take(add_pop_Orders(currentType, currentAmount))

Does model.getProperty() return a live object for objects that are members of an array?

I get an object from within an array in my model (a JSONmodel type) which is
{
"task": [
{
"dbid": 465,
"bk_cnt": 11,
}, {
"dbid": 472,
"bk_cnt": 16,
}
]
}
I bind this model to a table and connect the bk_cnt up to an objectNumber in a cell. No problem so far.
In code I want to change the value of the first bk_cnt value from 11 to 20 on press of a button. Inside the event I have:
var model = this.getView().getModel() // get the model
var tasks = model.getProperty("/task"); // get as a JS object
tasks[0].bk_cnt = 20 // update the model...will it update the view?
// model.refresh() // it will if this is uncommented.
Problem: Though it is bound to the view, the displayed value of bk_cnt does not change. if I add model.refresh() it does. This code is extracted from a larger section and one of the larger features is sorting by column click. When I click a column to re-sort (no change to the model), the value 20 appears.
What gives?
Musings: I have read that the model.getProprty() function returns a javascript object with a live reference back to the model, and that a change to the value of the object will automatically be reflected in the view for any bound controls. Does this statement fall down on array attributes ?
EDIT: Still feeling around the issue I find that
model.setProperty("/task/0/bk_cnt", 20)
Does not require a model.refresh() to update the view. Not a total surprise as this command is directly acting through the model. This leaves me thinking that the 'live' object returned by getProperty() is only live when it is a primitive datatype like a string or integer, but not for a JS object. Or am I missing something ?
EDIT 2: #Ash points out in his answer that there is a further approach which is to access the JS object from the model property, set whatever attributes need to be updated in the JS object, then replace that into the model, e.g.
var tasks = model.getProperty("/task");
tasks[0].bk_cnt = 20
model.setProperty('/task', tasks)
Second edit done to complete the trio of approaches for future readers.
The Model object is an abstraction layer ON TOP of a javascript object. There is no way that a change within an object is notified anywhere. You need to explicitly trigger the notifications through model.refresh() or model.setProperty().
So both of your solutions are valid, another one (which I favor) would be
var tasks = model.getProperty("/task");
tasks[0].bk_cnt = 20
model.setProperty('/task', tasks)
But this actually depends on how you bind your model to your UI objects :)