import sumolib from Car import Car from Flow import Flow from carInfo import Ui_carInfo from varEdit import Ui_varEdit from PySide6.QtWidgets import QWidget, QLabel, QToolBox from PySide6.QtCore import Qt, Slot, Signal, QThread from PySide6.QtCharts import QChart, QSplineSeries, QLineSeries from PySide6.QtGui import QColor from math import ceil, dist 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() 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=True def addParent(self, mainWindow): 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))