Search code examples
nullpointerexceptionruntime-erroranylogic

SelectOutput routing causing NullPointerException - Anylogic


I am working on a university project with Anylogic version 8.1. We have to use this version because the university license covers Anylogic only up to this version.

In this project our task is to model a order picking system with 12 order picking stations. Four stations are connected with each other via a circuit so that three areas are formed. Those three areas are also connected via a circuit. A order gets injected and ejected at two different points in the main circle. It then can move into the smaller circles to visit stations.

Therefore each instance of (agent) type Order has the following properties:

Order

  • stations_to visit: ArrayList which holds instances of (agent) type Station & contains all order picking stations that a order should visit
  • visited_stations: ArrayList which holds instances of (agent) type Station & contains all order picking stations that a order already visited
  • next_station: instance of (agent) type Station which should be visited next

A Station can take up to 3 orders: one being processed on a delay block and two on the conveyor.

Station

A Station is entered from the circle, which connects four stations. This circle is named SubArea:

SubArea

This sub area has also a limit of orders it can take. Orders are guided by the selectOutput Blocks: the first four are calling the select_output_branch function and the last one is calling the select_output_exit function.

  • select_output_branch: checks if the orders next_station is the station below and if capacities in the station are left. If this is the case it routes the order to the station below.
if(!order.stations_to_visit.isEmpty() && order.next_station == station && station.number_of_orders < station.max_number_of_orders) {
    return false;
} else {
    return true;
}
  • select_output_exit: checks if the next_station in the order is in a different subArea or if the order has visited all stations.If that is the case it routes the order to the big circle, which connects all three subareas.
if(order.stations_to_visit.isEmpty() || order.next_station.subArea != sub_area) {
    return true;
} else {
    return false;
}

The big circle is called Area and looks like that:

Area

As in the SubArea, the order is routed by selectOutput blocks. The first three again use the select_output_branch function and the last uses the select_output_exit function.

  • select_output_branch: checks if the next_station of the orders is included in the subArea and if capacities in the subArea are left. If this is the case it routes the order to the subArea below.
if(!order.stations_to_visit.isEmpty() && order.next_station.subArea == sub_area && order.next_station.subArea.number_of_orders < order.next_station.subArea.max_number_of_orders) {
    return false;
} else {
    return true;
}
  • select_output_exit: checks if the order has visited all stations. If that is the case it routes the order to the exit.
if(order.stations_to_visit.isEmpty()) {
    return true;
} else {
    return false;
}

In every enter and delay block forced pushing is disabled.

The Main includes an instance of the Area agent and an instance of the Flow Controller:

Main

The FLowController is where the creation, injection and next_station selection happens.

FlowController

The creation process of the Orders is as follows: A source creates based on a schedule (order_load) at discrete time moments (e.g. 10:00) a specified number of orders. Those get added to the default population and onExit the generate_new_stations_to_visit function is called. This functions takes a random number of all stations and sets them as stations_to_visit by the order and sets the orders next station to the first element of stations_to_visit. After that the order exits on on that moment it moves to the population orders_to_inject.

The injection of orders in the area happens via the injection_event. This calls the inject_order function.

// Check if there is a order to inject available && if area has capacities left
if(!orders_to_inject.isEmpty() && injected_orders.size() < max_injected_orders) {
    
    // Select order to inject
    Order order_to_inject = orders_to_inject.random();
    
    // Move to injected orders
    order_to_inject.goToPopulation(injected_orders);
    
    // Select first station to visit
    select_next_station_to_visit(order_to_inject);
    
    // Inject to area entry
    main.area.enter_flow_controller.take(order_to_inject);
}

The function visited_station gets called by the delay block in the station:

// Check if the correct station was visited
if(order.next_station == station) {
    
    // Move station to visited stations
    order.visited_stations.add(station);
    order.stations_to_visit.remove(station);
    
    // Select next station
    select_next_station_to_visit(order);
    
} else {
    System.out.println("Visited wrong station");
}

And the selcet_next_station to visit does the following:

// If any stations to visit are left
if(!order.stations_to_visit.isEmpty()) {
    
    // Set next station 
    order.next_station = order.stations_to_visit.get(0);
    
}

After a order leaves the area it gets moved to the population finished orders.

Thats it for the model so far:

During runtime a NullPointerException occurs:

Exception during discrete event execution:
NullPointerException
    at com.anylogic.libraries.processmodeling.Conveyor.e(Unknown Source)
    at com.anylogic.libraries.processmodeling.Conveyor.b(Unknown Source)
    at com.anylogic.libraries.processmodeling.Conveyor$5.onExit(Unknown Source)
    at com.anylogic.libraries.processmodeling.Delay.d(Unknown Source)
    at com.anylogic.libraries.processmodeling.Delay.b(Unknown Source)
    at com.anylogic.libraries.processmodeling.Delay$6.onExit(Unknown Source)
    at com.anylogic.libraries.processmodeling.OutputBuffer.e(Unknown Source)
    at com.anylogic.libraries.processmodeling.OutputBuffer.c(Unknown Source)
    at com.anylogic.libraries.processmodeling.OutputBuffer$1.onExit(Unknown Source)
    at com.anylogic.libraries.processmodeling.OutputBlock.d(Unknown Source)
    at com.anylogic.libraries.processmodeling.OutputBlock.a(Unknown Source)
    at com.anylogic.libraries.processmodeling.OutputBlock$1.a(Unknown Source)
    at com.anylogic.libraries.processmodeling.OutPort.a(Unknown Source)
    at com.anylogic.libraries.processmodeling.InPort.receiveImmediately(Unknown Source)
    at com.anylogic.libraries.processmodeling.InputBlock$1.a(Unknown Source)
    at com.anylogic.libraries.processmodeling.OutPort.a(Unknown Source)
    at com.anylogic.libraries.processmodeling.OutPort.c(Unknown Source)
    at com.anylogic.libraries.processmodeling.OutPort.b(Unknown Source)
    at com.anylogic.libraries.processmodeling.OutputBlock.c(Unknown Source)
    at com.anylogic.libraries.processmodeling.OutputBlock.a(Unknown Source)
    at com.anylogic.libraries.processmodeling.OutputBlock$2.a(Unknown Source)
    at com.anylogic.libraries.processmodeling.OutputBlock$2.action(Unknown Source)
    at com.anylogic.libraries.processmodeling.AsynchronousExecutor_xjal.executeActionOf(Unknown Source)
    at com.anylogic.engine.EventTimeout.execute(Unknown Source)
    at com.anylogic.engine.Engine.d(Unknown Source)
    at com.anylogic.engine.Engine.nd(Unknown Source)
    at com.anylogic.engine.Engine.f(Unknown Source)
    at com.anylogic.engine.Engine$e.run(Unknown Source)

Here is what i have done so far to solve this:

  • displayed every exit of an order from a conveyor, delay, enter and exit block: No Order was null
  • investigated where the error occurs: i suspect at the enter blocks in the area agent instance. This is because the number_of_agents counter for area gets increased at the exit of each subarea and when the error occurs, the counter has a different value than the conveyors contain in sum
  • stripped back the original model which had prios for injection and station selection
  • rebuild the model

Anyways, all this narrowed the problem down to: When i remove the routing logic from the select Output Blocks the problem does not occur. When i put it back in the error occurs again.

EDIT: When i remove the routing logic (select_ouput_branch and select_output_exit) only from the subarea and change it to random(0.5) the error doesn't occur.

Thank you for your time and effort. I am glad if someone could help.

EDIT: I build a third model, with no logic at all, just anylogic blocks and default agents in the main:

Third try

The NullPointer still occurs. Might this be an Anylogic bug?


Solution

  • I finally found the solution.

    It seems like anylogic cant handle to many agents / orders on one conveyor. E.g. when a conveyor has a length of 1.5 meters and it can take up to two agents, which is kinda confusing and causes the NullPointer.

    So i wrapped each conveyor in a restricted area and it works :)