InfSR
InfSR

Reputation: 13

Create agents with a parameter and a sourceblock

I created my own Flowchart block with a few parameters. Two of them are of the type 'Agent' and in Main I selected the corresponding agents. What my block does is, it creates new agents depending on the agents, that enter the block (kinda like the batch-block).

So far, I was able to verify the incoming agent to make sure, that the right agent-type was selected in Main. Now I want to create the other agent with a source block and the inject function. But here comes my problem. I want to create agents dynamically depending on the selected parameter (type Agent). Obviously, putting just the name of the parameter in the new agent field didn't work (it does work, but only for the first agent - after that I get an error, since the same agent gets created). I know that normally I have to use something like 'new Agent()' to create new agents, but I can't find a link between the parameter-value and the agent-type.

My Issue here is, that I try to make my block as customizable as possible, meaning that I want to use this block in future projects again without changing the code at all (or at least too much). Every project will have different agents, variables, names, parameters, etc.

Edit: Added Screenshot

Simplified version of my block

Upvotes: 1

Views: 961

Answers (3)

Artem P.
Artem P.

Reputation: 816

There are two ways to achieve this: via Java Reflection and using java.util.function.Supplier. Please note that the drawback of both methods is that since the type of the resulting agent is not known in advance the code refers to the new entity as just Agent however it actually creates the agent of correct type, which can be checked using 'instanceof' or cast.

Both versions are provided below. Example .alp can be found here for the next 2 hrs. In this example there are 2 agents Main and ProgrammaticAgent.

View of Main agent

View of ProgrammaticAgent

Now on to the methods of creation.

  1. Via Java Reflection

This is the least preferable method of the two. In the example Main agent, the code is in the action property of the 'create directly' button. The code is as follows, please read the comments for more detailed description.

// This is the name of the class that needs to be created
//  as a string. It can be passed as a parameter. 
// NOTE: you need the whole name including package name 
//  (in this case 'testprogcreation.' but you'll have your own) 
String classNameValue = "testprogcreation.ProgrammaticAgent";

try {
    // get a handle on the Class of that agent
    Class agentClass = Class.forName(classNameValue);
    
    // find the Constructor
    Constructor c = agentClass.getDeclaredConstructor(
        com.anylogic.engine.Engine.class,
        com.anylogic.engine.Agent.class,
        com.anylogic.engine.AgentList.class);
        
    // Create the agent. Now, because we don't know the type
    // at compile time it has to be declared as 'Agent' here
    // but in actuality it is of type specified in 'classNameValue' above
    Agent agent = 
        (Agent)c.newInstance(getEngine(), this, getDefaultPopulation());
    
    // add to default population
    ((AgentLinkedHashSet<Agent>)getDefaultPopulation())._add(agent);
        
    // set up parameters by name
    agent.setParameter("parameterOne", 5.0, false);
    agent.setParameter("parameterTwo", "some value", false);
    agent.markParametersAreSet(); // <- mark that parameters are set

    // tell AnyLogic that agent is created and started  
    agent.create();
    agent.start();
    
    // now you can do whatever with the agent, in this case send it
    // via 'enter' into some process
    enter.take(agent);
} catch (Exception e) {
    e.printStackTrace();
    error("Could not instantiate %s, see Console for full error", classNameValue);
}
  1. Via function Supplier

This is a much neater method. Here the block that will be creating new agents has a dynamic parameter called agentSupplier (see image) of type java.util.function.Supplier and calls agentSupplier.get() when new instance of an agent is needed.

This Supplier is then provided with a reference to 'add_' method which is automatically generate by AnyLogic when an agent population object is created. In this case the population is called 'programmaticAgents' and therefore the method is called 'add_programmaticAgents(...'.

agentCreator parameter

Note that in the above image the code in the Default Value field would actually be changed in the enclosing agent to reflect the correct automatically create method.

Upvotes: 1

InfSR
InfSR

Reputation: 13

First of all thank you for all the tips.

I added a small screenshot to my original post. As mentioned before, the idea behind my block is that I want to generate new elements depending on the incoming agent and the parameter. For instance, I want to transform agent "A" into agent "B" if I have set the associated parameter to agent "B".

My issue here is not the implementation. I can hard-code everything which works just as fine. My thought was to create my own custom library that I can reuse or share with other colleagues. So every custom block should be as customizable and foolproof as possible.

In the end I did something similar that Felipe suggested - writing a function that uses instanceof instead.

Agent a = null;

if(pToAgent instanceof Lot){
    a = new Lot();
}else if(pToAgent instanceof Magazine){
    a = new Magazine();
}else if(pToAgent instanceof Module){
    a = new Module();
}else{
    traceln("CustomUnbatch: Error - Undefined Agent-type");
}

return a;

The only downside to this solution is that the function has to be changed again and again when new agents are added or when a new project is started. Maybe I'll include the idea of a mapping table in the future, so that I don't have to add more lines to the function.

Upvotes: 0

Felipe
Felipe

Reputation: 9421

I'm not sure if this helps, but it's what I think can lead you to the right answer... also im not sure i actually understand what you want exactly

First, the source can be created as a population, with each member of the population creating a different agent type... Like this:

population of sources

On this example i have an array of 2 sources... Now, you want to generate a different agent type from each of the sources in the array... for this you need to make a function on the new agent parameter that returns an Agent

new agent

this function takes as an argument the index of the source of that population of sources... this is a local variable that you can freely use when you create population of objects

now the function can look something like this:

Agent x=null;
if(index==0)
    x=new MyAgent1();
else if(index==1)
    x=new MyAgent2();
    
return x;

Now the only problem is that you have to rewrite this function every time you have new conditions on a new model, but depending how many agent types you have, it might not be so much work....

The the selection can be done through the index... so you could make it flexible enough if you have something like that... if this is the right direction, maybe we can refine when you give more info

you can of course inject agents by using source.get(index).inject();

and the parameter you use is the index, and not the agent type.. you can have a map with

index -> agent type 

somewhere else in your model

Upvotes: 0

Related Questions