Step 4: Take initial model data from a file
Rather than hard-code the initial model state, we will read scenario data from a JSON file.
Reading a Scenario from an external JSON file
First, add a new dependency to the pom.xml
file:
<!-- JSON -->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20230227</version>
</dependency>
Create a ScenarioParser
class with the following code:
package tutorial.scenario;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.json.JSONObject;
public class ScenarioParser {
public static Scenario parseScenario(JSONObject jsonScenario) {
LocalDateTime beginDate = parseLocalDateTime(jsonScenario, "beginDate");
LocalDateTime endDate = parseLocalDateTime(jsonScenario, "endDate");
double intervalBetweenRequestHrs = parseDouble(jsonScenario, "intervalBetweenRequestHrs");
double maxDeliveryTimeHrs = parseDouble(jsonScenario, "maxDeliveryTimeHrs");
Map<String, Node> nodes = new HashMap<>();
List<Truck> trucks = new ArrayList<>();
List<Arc> arcs = new ArrayList<>();
List<Warehouse> warehouses = new ArrayList<>();
List<Store> stores = new ArrayList<>();
jsonScenario.getJSONArray("nodes").forEach(elem -> {
JSONObject json = (JSONObject) elem;
String id = parseString(json, "id");
nodes.put(id, new Node(id, parseDouble(json, "x"), parseDouble(json, "y")));
});
jsonScenario.getJSONArray("arcs").forEach(elem -> {
JSONObject json = (JSONObject) elem;
Node source = parseNode(nodes, json, "source");
Node dest = parseNode(nodes, json, "dest");
if (source.equals(dest)) {
String message = "Arc source and destination should be different Nodes.\n";
System.err.println(message);
throw new RuntimeException(message);
}
List<Point> points = new ArrayList<>();
json.getJSONArray("points").forEach(pointObj -> {
JSONObject pointJson = (JSONObject) pointObj;
points.add(new Point(pointJson.getDouble("x"), pointJson.getDouble("y")));
});
arcs.add(new Arc(source, dest, points));
});
jsonScenario.getJSONArray("trucks").forEach(elem -> {
JSONObject json = (JSONObject) elem;
trucks.add(new Truck(parseString(json, "id"), parseString(json, "name"), parseDouble(json, "speed"), parseNode(nodes, json, "initialNode")));
});
jsonScenario.getJSONArray("warehouses").forEach(elem -> {
JSONObject json = (JSONObject) elem;
warehouses.add(new Warehouse(parseString(json, "id"), parseString(json, "name"), parseNode(nodes, json, "node")));
});
jsonScenario.getJSONArray("stores").forEach(elem -> {
JSONObject json = (JSONObject) elem;
stores.add(new Store(parseString(json, "id"), parseString(json, "name"), parseNode(nodes, json, "node")));
});
return new Scenario(trucks, intervalBetweenRequestHrs, maxDeliveryTimeHrs,
nodes.values().stream().toList(), arcs, warehouses, stores, beginDate, endDate);
}
private static LocalDateTime parseLocalDateTime(JSONObject jsonScenario, String str) {
return LocalDateTime.parse(jsonScenario.getString(str));
}
private static Node parseNode(Map<String, Node> nodes, JSONObject object, String nodeFieldName) {
String nodeFieldValue = object.getString(nodeFieldName);
Node node = nodes.get(nodeFieldValue);
if (Objects.nonNull(node)) {
return node;
} else {
String message = "Node with id '" + nodeFieldValue + "' does not exist.\n";
System.err.println(message);
throw new RuntimeException(message);
}
}
private static String parseString(JSONObject object, String str) {
return object.getString(str);
}
private static double parseDouble(JSONObject object, String str) {
return object.getDouble(str);
}
}
Prepare one example scenario
Create a new scenarios
folder in the project folder.
Place the following 'scenario.json' file into the scenarios
folder:
{
"name": "scenario",
"beginDate": "2023-01-01T00:00:00",
"endDate": "2023-01-01T12:00:00",
"intervalBetweenRequestHrs": 0.5,
"maxDeliveryTimeHrs": 6,
"trucks": [
{
"id": "truck-1",
"name": "Truck 1",
"speed": 40.0,
"initialNode": "node3"
}
],
"nodes": [
{
"id": "node1",
"x": 0,
"y": 0
},
{
"id": "node2",
"x": 0,
"y": 30
},
{
"id": "node3",
"x": 40,
"y": 30
},
{
"id": "node4",
"x": 40,
"y": 0
}
],
"arcs": [
{
"source": "node1",
"dest": "node3",
"points": []
},
{
"source": "node1",
"dest": "node4",
"points": []
},
{
"source": "node2",
"dest": "node3",
"points": []
},
{
"source": "node2",
"dest": "node4",
"points": []
}
],
"warehouses": [
{
"id": "wh1",
"name": "Warehouse 1",
"node": "node1"
},
{
"id": "wh2",
"name": "Warehouse 2",
"node": "node1"
},
{
"id": "wh3",
"name": "Warehouse 3",
"node": "node2"
}
],
"stores": [
{
"id": "st1",
"name": "Store 1",
"node": "node3"
},
{
"id": "st2",
"name": "Store 2",
"node": "node3"
},
{
"id": "st1",
"name": "Store 3",
"node": "node4"
}
]
}
Note that this scenario file contains exactly the same scenario data as in the static{}
section of the Main
class.
Simulated period is taken from the createAndRunOneExperimentWithStats()
method (12 hours).
Changes to the Main class
Get to the Main
class.
Add new 'import' statements:
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Objects;
import org.json.JSONObject;
import tutorial.scenario.ScenarioParser;
Add a new field:
private static final String SCENARIOS_PATH = "scenarios/";
Add a new method:
runScenariosFromFiles()
methodprivate static void runScenariosFromFiles() {
File folder = new File(SCENARIOS_PATH);
for (File file : folder.listFiles()) {
JSONObject jsonScenario = null;
try {
jsonScenario = new JSONObject(Files.readString(file.toPath()));
} catch (IOException e) {
e.printStackTrace();
}
if (Objects.nonNull(jsonScenario)) {
try {
Scenario scenario = ScenarioParser.parseScenario(jsonScenario);
runExperimentWithStats(scenario, file.getName());
} catch (Exception e) {
System.err.printf("File '%s' contains error or has incorrect format\n", file.getName());
e.printStackTrace();
}
}
}
}
Replace the main()
method to use the runScenariosFromFiles()
method:
main()
methodpublic static void main(String[] args) {
runScenariosFromFiles();
}
Check the result
Run the program and see if you get the following result in the console output:
Scenario Trucks count SL Expenses Expenses/SL scenario.json 1 57,14% $ 420,00 $ 7,35
This is exactly the same result for "12 hours" simulation as we had in the previous step.