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))