Browse Source

Added config file

rparedis 2 years ago
parent
commit
2ac04f22db

BIN
__pycache__/mapper.cpython-38.pyc


+ 34 - 0
config.ini

@@ -0,0 +1,34 @@
+[simulation]
+; Whether to visualize the movement of vessels on the map
+visualization = true
+
+; Whether to print internal simulation trace to the terminal
+verbose = false
+
+; Whether to print the simulation statistics at the end
+statistics = true
+
+; When positive, the time scale to run the simulator at
+;    1 means wall-clock time (1:1 second scale)
+;    2 means 1 simulated second takes 2 seconds to run
+; When zero or negative, runs the simulator as fast as possible
+; Note: timescale must be strictly positive when using the
+;    visualization!
+timescale = 0.002
+
+; The time to end the simulation. When zero, no end time
+;    is considered.
+end_time = 0
+
+; Schedule file for the simulation. The simulation also
+;    ends when there is nothing scheduled anymore
+schedule_file = results-de2/plan.csv
+
+[movement]
+; Both tugging and sailing are sampled from a normal distribution
+;    with the parameters defined below.
+; Everything is using SI units, so m/s instead of knots!
+velocity_mean_tugging = 3.343889
+velocity_std_tugging = 0.2572222
+velocity_mean_sailing = 3.343889
+velocity_std_sailing = 0.2572222

BIN
de2/__pycache__/elements.cpython-38.pyc


BIN
de2/__pycache__/tracer.cpython-38.pyc


+ 11 - 43
de2/elements.py

@@ -1,3 +1,4 @@
+import numpy as np
 from pypdevs.DEVS import AtomicDEVS, CoupledDEVS
 from pypdevs.infinity import INFINITY
 
@@ -7,6 +8,9 @@ import pandas as pd
 from de2.routing import get_graph, get_closest_vertex
 
 
+TUGS = pd.read_excel("20230405_Tugs.xlsx", dtype={"MMSI": str, "NAME": str, "category": str})
+
+
 @dataclass
 class Footprint:
 	usage_time: float = 0.0
@@ -73,6 +77,11 @@ class Pool(AtomicDEVS):
 		self.vessel_in = self.addInPort("vessel_in")
 		self.vessel_out = self.addOutPort("vessel_out")
 
+
+		for mmsi in TUGS["MMSI"]:
+			if isinstance(mmsi, str):
+				self.state["waiting"][mmsi] = Vessel(mmsi)
+
 	def extTransition(self, inputs):
 		self.state["time"] += self.elapsed
 		if self.req_in in inputs:
@@ -204,52 +213,12 @@ class Sailer(AtomicDEVS):
 		return self.state
 
 
-class Tracer(AtomicDEVS):
-	def __init__(self, name, ivef):
-		super(Tracer, self).__init__(name)
-
-		self.ivef = pd.read_csv(ivef, usecols=["mmsi", "name", "start",
-		                                       "source_lon", "source_lat", "target_lon", "target_lat",
-		                                       "distance", "task", "velocity"],
-		                        dtype={"start": float, "distance": float, "velocity": float,
-		                               "source_lon": float, "source_lat": float,
-		                               "target_lon": float, "target_lat": float})
-		self.ivef.sort_values(by=["start"])
-		self.starting_time = self.ivef.iloc[0]["start"]
-		# self.ivef["start"] -= self.starting_time
-		# self.ivef["end"] -= self.starting_time
-
-		self.state = {
-			"index": 0,
-			"time": 0.0
-		}
-
-		self.reqs = self.addOutPort("reqs")
-
-	def timeAdvance(self):
-		if self.state["index"] < len(self.ivef):
-			start = self.ivef.iloc[self.state["index"]]["start"] - self.starting_time
-			return round(start / 1000 - self.state["time"], 6)
-		return INFINITY
-
-	def intTransition(self):
-		self.state["time"] += self.timeAdvance()
-		self.state["index"] += 1
-		return self.state
-
-	def outputFnc(self):
-		if self.state["index"] < len(self.ivef):
-			return {
-				self.reqs: [self.ivef.iloc[self.state["index"]]]
-			}
-		return {}
-
-
 class Scheduler(AtomicDEVS):
 	def __init__(self, name, ivef):
 		super(Scheduler, self).__init__(name)
 
 		self.ivef = pd.read_csv(ivef, usecols=["mmsi", "start", "ETA", "source", "target", "task"], dtype={"mmsi": str})
+		# self.ivef = self.ivef[self.ivef["mmsi"] == "205501790"]
 		self.ivef.sort_values(by=["start"])
 		self.starting_time = self.ivef.iloc[0]["start"]
 		# self.ivef["start"] -= self.starting_time
@@ -286,7 +255,6 @@ class RoutePlanner(AtomicDEVS):
 		super(RoutePlanner, self).__init__(name)
 
 		self.graph = get_graph()
-		self.tugs = pd.read_excel("20230405_Tugs.xlsx", dtype={"MMSI": str, "NAME": str, "category": str})
 
 		self.state = {
 			"request": []
@@ -303,7 +271,7 @@ class RoutePlanner(AtomicDEVS):
 	def extTransition(self, inputs):
 		if self.req_in in inputs:
 			for request in inputs[self.req_in]:
-				tug = self.tugs[self.tugs["MMSI"] == str(request["mmsi"])].iloc[0]
+				tug = TUGS[TUGS["MMSI"] == str(request["mmsi"])].iloc[0]
 				request["name"] = tug["NAME"]
 				request["category"] = tug["category"]
 

+ 46 - 31
de2/tracer.py

@@ -1,12 +1,11 @@
 from pypdevs.tracers.tracerBase import BaseTracer
 from pypdevs.util import runTraceAtController
-import sys
 from dataclasses import dataclass
+import sys
 
-from de2.elements import Pool, Sailer
+from de2.elements import Pool, Sailer, Clock, Scheduler
 
 
-# TODO: Update always!
 class TracerPort(BaseTracer):
     """
     A tracer for port simulation output
@@ -25,6 +24,7 @@ class TracerPort(BaseTracer):
         else:
             self.filename = None
         self.prevtime = (-1, -1)
+        self.starting_time = 0
 
     def startTracer(self, recover):
         """
@@ -64,7 +64,7 @@ class TracerPort(BaseTracer):
 
     def _vesselStr(self, vessel):
         # VESSEL: mmsi, name, source, target, task, total distance, distance left, velocity
-        return "'%s', '%s', %s, %s, '%s', %.6f, %.6f, %.6f" % vessel.tuple()
+        return "%s, %s, %s, %s, %s, %.6f, %.6f, %.6f" % vessel.tuple()
 
     def traceInternal(self, aDEVS):
         """
@@ -72,29 +72,23 @@ class TracerPort(BaseTracer):
 
         :param aDEVS: the model that transitioned
         """
-        if isinstance(aDEVS, Pool):
-            for vessel in aDEVS.state["waiting"].values():
-                runTraceAtController(self.server, self.uid, aDEVS,
-                                     [aDEVS.time_last, '"' + self._vesselStr(vessel) + '"'])
-        elif isinstance(aDEVS, Sailer):
-            for vessel in aDEVS.state["vessels"]:
-                runTraceAtController(self.server, self.uid, aDEVS,
-                                     [aDEVS.time_last, '"' + self._vesselStr(vessel) + '"'])
-
-    def traceExternal(self, aDEVS):
-        """
-        Tracing done for the external transition function
-
-        :param aDEVS: the model that transitioned
-        """
-        if isinstance(aDEVS, Pool):
-            for vessel in aDEVS.state["waiting"].values():
-                runTraceAtController(self.server, self.uid, aDEVS,
-                                     [aDEVS.time_last, '"' + self._vesselStr(vessel) + '"'])
-        elif isinstance(aDEVS, Sailer):
-            for vessel in aDEVS.state["vessels"]:
-                runTraceAtController(self.server, self.uid, aDEVS,
-                                     [aDEVS.time_last, '"' + self._vesselStr(vessel) + '"'])
+        if isinstance(aDEVS, Clock):
+            par = aDEVS.parent
+            for model in par.component_set:
+                if isinstance(model, Pool):
+                    for vessel in model.state["waiting"].values():
+                        runTraceAtController(self.server, self.uid, aDEVS,
+                                             [aDEVS.time_last, '"' + self._vesselStr(vessel) + '"'])
+                elif isinstance(model, Sailer):
+                    for vessel in model.state["vessels"]:
+                        runTraceAtController(self.server, self.uid, aDEVS,
+                                             [aDEVS.time_last, '"' + self._vesselStr(vessel) + '"'])
+
+    def traceInit(self, aDEVS, t):
+        if isinstance(aDEVS, Scheduler):
+            self.starting_time = aDEVS.starting_time / 1000
+            runTraceAtController(self.server, self.uid, aDEVS,
+                                 [aDEVS.time_last, '"START: %10.6f"' % self.starting_time])
 
 
 @dataclass
@@ -108,22 +102,43 @@ class StreamedVessel:
     distance_left: float
     velocity: float
 
+    @staticmethod
+    def from_str(args):
+        vec = args.split(", ")
+
+        if vec[2] == "None":
+            st = None
+            if vec[3] == "None":
+                tt = None
+            else:
+                tt = float(vec[3][1:]), float(vec[4][:-1])
+        else:
+            st = float(vec[2][1:]), float(vec[3][:-1])
+            if vec[4] == "None":
+                tt = None
+            else:
+                tt = float(vec[4][1:]), float(vec[5][:-1])
+
+        alist = vec[:2] + [st, tt, vec[-4]] + [float(x) for x in vec[-3:]]
+        return StreamedVessel(*alist)
+
 
 class Streamer:
     def __init__(self):
         self.time = 0
-        self.starting_time = None
+        self.starting_time = 0
         self.vessels = []
 
     def write(self, text: str):
+        # print(text, end='')
         texts = text.split("\n")
         for t in texts:
             if t.startswith("TIME: "):
                 self.time = float(t[6:])
-                if self.starting_time is None:
-                    self.starting_time = self.time
                 self.vessels.clear()
+            elif t.startswith("START: "):
+                self.starting_time = float(t[7:])
             elif len(t) > 0:
-                self.vessels.append(StreamedVessel(*eval(t)))
+                self.vessels.append(StreamedVessel.from_str(text))
 
     def flush(self): pass

+ 45 - 0
main.py

@@ -0,0 +1,45 @@
+import configparser
+from de2.elements import Port
+from pypdevs.simulator import Simulator
+
+config = configparser.ConfigParser()
+config.read('config.ini')
+
+port = Port("PoAB", config.get('simulation', 'schedule_file'))
+
+sim = Simulator(port)
+if config.getboolean('simulation', 'verbose'):
+	sim.setVerbose(None)
+end_time = config.getfloat('simulation', 'end_time')
+if end_time > 0:
+	sim.setTerminationCondition(lambda t, m: t[0] > end_time or m.scheduler.state["index"] >= len(m.scheduler.ivef))
+else:
+	sim.setTerminationCondition(lambda _, m: m.scheduler.state["index"] >= len(m.scheduler.ivef))
+
+timescale = config.getfloat('simulation', 'timescale')
+if timescale > 0:
+	sim.setRealTime(scale=timescale)
+
+if config.getboolean('simulation', 'visualization') and timescale > 0:
+	from de2.tracer import Streamer
+	import geoplotlib
+	from mapper import PoABLayer
+
+	streamer = Streamer()
+	sim.setCustomTracer("de2.tracer", "TracerPort", [streamer])
+	layer = PoABLayer(sim, streamer)
+
+	geoplotlib.set_window_size(640, 760)
+	geoplotlib.tiles_provider({
+	    'url': lambda zoom, xtile, ytile: 'https://tile.openstreetmap.org/%d/%d/%d.png' % (zoom, xtile, ytile),
+	    'tiles_dir': 'mytiles',
+	    'attribution': 'Map tiles by OpenStreetMap Carto, under CC BY 3.0. Data @ OpenStreetMap contributors.'
+	})
+
+	geoplotlib.add_layer(layer)
+	geoplotlib.show()
+else:
+	sim.simulate()
+
+if config.getboolean('simulation', 'statistics'):
+	sim.model.print_statistics()

+ 17 - 6
mapper.py

@@ -10,7 +10,6 @@ import numpy as np
 from de2.routing import get_graph, euler_distance, pathfinder, find_percentage_point_in_path as fpp
 
 
-# TODO: decouple this from simulator/model knowledge
 class PoABLayer(BaseLayer):
     """
     Renders the map.
@@ -44,6 +43,7 @@ class PoABLayer(BaseLayer):
 
         self.vcache = {}  # vessel -> source
         self.pcache = {}  # vessel -> path, dists
+        self.tcache = {}  # mmsi -> start time
 
         self.graph = get_graph()
 
@@ -65,12 +65,21 @@ class PoABLayer(BaseLayer):
             path:   The path to draw as (x, y) tuples.
             proj:   The projector.
         """
+        x = []
+        y = []
         for ix in range(len(path) - 1):
             point1 = path[ix]
             point2 = path[ix + 1]
-            vx1, vy1 = proj.lonlat_to_screen(np.asarray([point1[0]]), np.asarray([point1[1]]))
-            vx2, vy2 = proj.lonlat_to_screen(np.asarray([point2[0]]), np.asarray([point2[1]]))
-            self.painter.lines(vx1, vy1, vx2, vy2, width=1.5)
+            x.append(point1[0])
+            x.append(point2[0])
+            y.append(point1[1])
+            y.append(point2[1])
+        px, py = proj.lonlat_to_screen(np.asarray(x), np.asarray(y))
+        vx1 = [e for ei, e in enumerate(px) if ei % 2 == 0]
+        vx2 = [e for ei, e in enumerate(px) if ei % 2 == 1]
+        vy1 = [e for ei, e in enumerate(py) if ei % 2 == 0]
+        vy2 = [e for ei, e in enumerate(py) if ei % 2 == 1]
+        self.painter.lines(vx1, vy1, vx2, vy2, width=1.5)
 
     def draw(self, proj, mouse_x, mouse_y, ui_manager):
         self.painter = BatchPainter()
@@ -101,7 +110,7 @@ class PoABLayer(BaseLayer):
             # Show the simulation time in top right
             # model = self.sim.model
             time = self.streamer.time
-            ts = int(time + self.streamer.starting_time / 1000)
+            ts = int(time + self.streamer.starting_time)
             # time = model.clock.state["time"]
             # ts = int(time + model.scheduler.starting_time / 1000)
             ui_manager.info("TIME: %s" % epoch_to_str(ts))
@@ -118,6 +127,7 @@ class PoABLayer(BaseLayer):
                     if vessel.mmsi not in self.vcache or self.vcache[vessel.mmsi] != vessel.source:
                         self.vcache[vessel.mmsi] = vessel.source
                         self.pcache[vessel.mmsi] = pathfinder(self.graph, vessel.source, vessel.target)
+                        self.tcache[vessel.mmsi] = time
 
                     path, dists = self.pcache[vessel.mmsi]
 
@@ -126,7 +136,7 @@ class PoABLayer(BaseLayer):
                     # Compute distance traveled and draw a vessel there
                     tot_distance = vessel.total_distance
                     traveled_approx = tot_distance - vessel.distance_left
-                    delta = time - self.streamer.time
+                    delta = time - self.tcache[vessel.mmsi]
                     traveled_approx += delta * vessel.velocity
                     percentage = traveled_approx / tot_distance
 
@@ -171,6 +181,7 @@ if __name__ == '__main__':
 
     sim = Simulator(port)
     # sim.setClassicDEVS()
+    # sim.setVerbose(None)
     sim.setRealTime(scale=0.0002)
     sim.setCustomTracer("de2.tracer", "TracerPort", [streamer])
     # sim.setTerminationTime(31 * 24 * 60 * 60)  # 14 days

2023.09.07-meeting-Havenhuis.txt → notes/2023.09.07-meeting-Havenhuis.txt