264 lines
9.9 KiB
Python
264 lines
9.9 KiB
Python
import sumolib
|
|
from Car import Car
|
|
from Flow import Flow
|
|
from math import ceil, dist
|
|
from globalImport import globalImport
|
|
|
|
def pysideImports():
|
|
globalImport(globals(), "carInfo", "Ui_carInfo")
|
|
globalImport(globals(), "varEdit", "Ui_varEdit")
|
|
globalImport(globals(), "PySide6.QtWidgets", ["QWidget", "QLabel", "QToolBox"])
|
|
globalImport(globals(), "PySide6.QtCore", ["Qt", "Slot", "Signal", "QThread"])
|
|
globalImport(globals(), "PySide6.QtCharts", ["QChart", "QSplineSeries", "QLineSeries"])
|
|
globalImport(globals(), "PySide6.QtGui", ["QColor"])
|
|
|
|
class carInfo(QWidget):
|
|
def __init__(self,parent):
|
|
super().__init__()
|
|
self.ui = Ui_carInfo()
|
|
self.ui.setupUi(self)
|
|
self.maxV = 0
|
|
self.parent = parent
|
|
self.pointsCount = [0] * 4
|
|
self.minX = 0
|
|
|
|
self.chart = self.ui.speedGraph.chart()
|
|
#self.chart.setAnimationOptions(QChart.AllAnimations)
|
|
|
|
speedsNames = ["vmax","vsec","Vitesse (m.s^-1)","inter vsec"]
|
|
self.speedSeries = []
|
|
for ind,s in enumerate(speedsNames):
|
|
self.speedSeries.append(QLineSeries())
|
|
self.speedSeries[ind].setName(s)
|
|
# self.speedSeries[ind].setPointsVisible()
|
|
self.chart.addSeries(self.speedSeries[ind])
|
|
self.chart.createDefaultAxes()
|
|
|
|
@Slot(tuple)
|
|
def addSpeedPoint(self,params):
|
|
ind=params[0]
|
|
if self.parent.currentWidget() != self:
|
|
return
|
|
t=params[1]
|
|
val=params[2]
|
|
if(self.pointsCount[ind] > 200):
|
|
self.speedSeries[ind].remove(0)
|
|
self.minX = max(self.minX,self.speedSeries[ind].at(0).x())
|
|
self.pointsCount[ind] -= 1
|
|
self.speedSeries[ind].append(t,val)
|
|
self.pointsCount[ind] += 1
|
|
|
|
if self.speedSeries[ind].count()>2:
|
|
i = self.speedSeries[ind].count()-1
|
|
pm2 = self.speedSeries[ind].at(i-2)
|
|
pm1 = self.speedSeries[ind].at(i-1)
|
|
pm0 = self.speedSeries[ind].at(i)
|
|
diffL = (pm1.y() - pm2.y())/(pm1.x() - pm2.x())
|
|
diffR = (pm0.y() - pm1.y())/(pm0.x() - pm1.x())
|
|
diff2 = diffR - diffL
|
|
if diff2 == 0:
|
|
self.speedSeries[ind].remove(i-1)
|
|
self.pointsCount[ind] -= 1
|
|
|
|
xAxis = self.chart.axes(Qt.Horizontal,self.speedSeries[ind])[0]
|
|
xAxis.setRange(self.minX,t)
|
|
|
|
if val>self.maxV:
|
|
self.maxV = val
|
|
yAxis = self.chart.axes(Qt.Vertical, self.speedSeries[ind])[0]
|
|
yAxis.setMax(ceil(val+1))
|
|
|
|
self.ui.speedGraph.update()
|
|
|
|
@Slot(tuple)
|
|
def setVal(self,params):
|
|
key = params[0]
|
|
val = params[1]
|
|
obj = self.findChild(QLabel,key)
|
|
if obj is None:
|
|
return
|
|
obj.setText(f"{key} : {val}")
|
|
obj.update()
|
|
|
|
global varEdit
|
|
class varEdit(QWidget):
|
|
def __init__(self, parent, carController, varName, value):
|
|
super().__init__()
|
|
self.ui = Ui_varEdit()
|
|
self.ui.setupUi(self)
|
|
|
|
self.hookName = varName
|
|
self.ui.name.setText(varName)
|
|
self.ui.value.setValue(value)
|
|
|
|
self.CC = carController
|
|
|
|
self.ui.value.valueChanged.connect(self.valueChanged)
|
|
|
|
def valueChanged(self):
|
|
value = self.ui.value.value()
|
|
self.CC.updateConstant(self.hookName, value)
|
|
|
|
class CarController:
|
|
def __init__(self, parentMap):
|
|
self.map=parentMap
|
|
self.cars=[]
|
|
self.flows=[]
|
|
self.t=0
|
|
self.dt=1/60
|
|
self.spawnFailed = 0
|
|
self.totalStopped = 0
|
|
self.carsDestroyed = 0
|
|
self.speedPercentageTotal = 0
|
|
|
|
self.maxCars = 100 # TODO: get it from net? demand?
|
|
self.dynSpeedRat = 1
|
|
|
|
self.infoWidget = None
|
|
self.selectedId = 0
|
|
|
|
self.vroomEnable=False
|
|
|
|
def addParent(self, mainWindow):
|
|
pysideImports()
|
|
|
|
self.infoWidget=mainWindow.findChild(QToolBox, "carInfos")
|
|
|
|
varWidget = mainWindow.ui.constEdit
|
|
varWidget.addWidget(varEdit(varWidget, self, "gamma", 5))
|
|
varWidget.addWidget(varEdit(varWidget, self, "delta", 0.5))
|
|
varWidget.addWidget(varEdit(varWidget, self, "T", 0.3))
|
|
varWidget.addWidget(varEdit(varWidget, self, "size", 3))
|
|
varWidget.addWidget(varEdit(varWidget, self, "dt", self.dt))
|
|
|
|
|
|
def fromPath(self,path):
|
|
self.cars=[]
|
|
self.flows=[]
|
|
self.t=0
|
|
|
|
while self.infoWidget is not None and self.infoWidget.count() != 0:
|
|
self.infoWidget.removeItem(0)
|
|
|
|
for vehicle in sumolib.xml.parse(path,["vehicle","flow"]):
|
|
route=vehicle.route[0].edges.split()
|
|
if vehicle.name == "vehicle":
|
|
dynSpeed = '0'
|
|
IA = '0'
|
|
if(vehicle.hasChild("param")):
|
|
for param in vehicle.getChild("param"):
|
|
key = param.getAttributeSecure("key")
|
|
if key == "dynamicSpeed":
|
|
dynSpeed = param.getAttributeSecure("value")
|
|
elif key == "IA":
|
|
IA = param.getAttributeSecure("value")
|
|
if(self.infoWidget is not None) :
|
|
wId=self.infoWidget.addItem(carInfo(self.infoWidget), vehicle.id)
|
|
self.cars.append(Car(vehicle.id,route,vehicle.depart,dynSpeed,IA,self.map,self,self.infoWidget.widget(wId)))
|
|
else:
|
|
self.cars.append(Car(vehicle.id,route,vehicle.depart,dynSpeed,IA,self.map,self,None))
|
|
elif vehicle.name == "flow":
|
|
randomFlow = 0
|
|
burstInterval = 1
|
|
burstTime = 1
|
|
dynSpeed = '0'
|
|
IA = '0'
|
|
if(vehicle.hasChild("param")):
|
|
for param in vehicle.getChild("param"):
|
|
key = param.getAttributeSecure("key")
|
|
if key == "random":
|
|
randomFlow = param.getAttributeSecure("value")
|
|
elif key == "burstInterval":
|
|
burstInterval = param.getAttributeSecure("value")
|
|
elif key == "burstTime":
|
|
burstTime = param.getAttributeSecure("value")
|
|
elif key == "dynamicSpeed":
|
|
dynSpeed = param.getAttributeSecure("value")
|
|
elif key == "IA":
|
|
IA = param.getAttributeSecure("value")
|
|
self.flows.append(Flow(vehicle.id, route, vehicle.begin, vehicle.vehsPerHour, randomFlow, burstInterval, burstTime, dynSpeed, IA, self.map, self))
|
|
|
|
def prepareRoute(self):
|
|
for car in self.cars:
|
|
car.prepareRoute()
|
|
|
|
for flow in self.flows:
|
|
flow.prepareRoute()
|
|
|
|
def getCarsOnEdge(self,edgeID):
|
|
return filter(lambda c: c.route[c.index].getID()==edgeID,self.cars)
|
|
|
|
def getCarsOnLane(self,edgeID,laneID):
|
|
cars = self.getCarsOnEdge(edgeID)
|
|
return filter(lambda c: c.laneId == laneID,cars)
|
|
|
|
def getCarsClose(self, car):
|
|
cars = self.getCarsOnEdge(car.getCurrentEdge().getID())
|
|
return filter(lambda c: dist(c.pos, car.pos) < car.minSpace, cars)
|
|
|
|
def update(self):
|
|
if self.map.net is None or self.dt == 0:
|
|
return
|
|
self.t+=self.dt
|
|
self.dynSpeedRat = 1 - len(self.cars) / self.maxCars
|
|
for car in self.cars:
|
|
car.update(self.dt)
|
|
|
|
for flow in sorted(self.flows, key=lambda f: f.priority, reverse = True):
|
|
if flow.shouldSpawn(self.t):
|
|
carsClose = self.getCarsClose(flow.carModel)
|
|
try:
|
|
next(carsClose)
|
|
except StopIteration:
|
|
self.cars.append(flow.spawnCar())
|
|
else:
|
|
self.spawnFailed += 1
|
|
#flow.addCar2Counter()
|
|
flow.priority += 1
|
|
#print(f"nope, y as déjà une voiture ici : n°{self.spawnFailed}")
|
|
|
|
def draw(self,painter):
|
|
selectedCar = [c for c in self.cars if c.id == self.selectedId]
|
|
for ind,car in enumerate(self.cars):
|
|
#selectedId = self.infoWidget.currentIndex()
|
|
colorOverride = False
|
|
if len(selectedCar) == 0:
|
|
painter.setPen(Qt.white)
|
|
pass
|
|
elif car.id == self.selectedId:
|
|
painter.setPen(Qt.green)
|
|
colorOverride = True
|
|
elif(selectedCar[0].leader is not None and selectedCar[0].leader.id == car.id):
|
|
painter.setPen(Qt.red)
|
|
colorOverride = True
|
|
elif(selectedCar[0].leaderAtInter is not None and selectedCar[0].leaderAtInter.id == car.id):
|
|
painter.setPen(QColor(100,0,255))
|
|
colorOverride = True
|
|
else:
|
|
painter.setPen(Qt.white)
|
|
colorOverride = False
|
|
car.draw(painter, colorOverride)
|
|
|
|
def destroyCar(self, car):
|
|
self.carsDestroyed += 1
|
|
self.totalStopped += car.timeStopped
|
|
self.speedPercentageTotal += car.speedPercentage / car.ticksLived
|
|
self.cars.remove(car)
|
|
|
|
def updateConstant(self, name, val):
|
|
if name == "dt":
|
|
self.dt = val
|
|
return
|
|
for car in self.cars:
|
|
car.__dict__[name] = val
|
|
|
|
def getFlowBacklog(self):
|
|
return sum([f.backlog(self.t) for f in self.flows])
|
|
|
|
def selectClosest(self, pos):
|
|
if len(self.cars) == 0:
|
|
return
|
|
closest = min(self.cars, key=lambda c: dist(c.pos, [pos.x(), pos.y()]))
|
|
self.selectedId = closest.id
|
|
self.infoWidget.setCurrentIndex(self.cars.index(closest))
|