/************************************************************************************ * Copyright (C) 2008 by Politehnica University of Bucharest and Rutgers University * All rights reserved. * Refer to LICENSE for terms and conditions of use. ***********************************************************************************/ package vnsim.applications.trafficview; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.Serializable; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.LinkedList; import java.util.ListIterator; import java.util.Random; import java.util.TreeMap; import vnsim.core.CarInfo; import vnsim.core.Communicator; import vnsim.core.Engine; import vnsim.core.events.ReceiveEvent; import vnsim.core.events.SendEvent; import vnsim.core.events.UnicastSendEvent; import vnsim.map.object.Globals; import vnsim.map.object.PeanoKey; import vnsim.map.object.Point; import vnsim.map.object.Road; import vnsim.network.RadioDev; import vnsim.vehicular.simulator.CarInstance; import vnsim.vehicular.simulator.intersections.Intersection; /** * Class that implements the TrafficView applications. Vehicle datasets are * broadcasted on a regular basis. The vehicle dataset is maintained based on * incoming messages from neighbor vehicles. */ public class SimulatedCarInfo extends CarInfo implements Communicator { // protected int vehicleId; private CarInstance realPos; // public ArrayList neighbors = new ArrayList(); public ArrayList trafficDB = new ArrayList(); private int lastMediumTransmission = 0; //the following moments of time when a packet should be received completely (ReceiveEvents) public LinkedList receiveEvents = new LinkedList(); //Petroaca - the emergency channel receive events public LinkedList receiveEventsEmergency = new LinkedList(); public TreeMap messagePeriods = new TreeMap(); public int messageNo = 1; public int singleMessageNo = -2; public boolean isEquipped; public boolean promiscuousMode = false; public boolean pulseMessage = true; int reportedNeighbors = 0; double neighPlusDistance = 0; double neighMinusDistance = 0; // public SimulationRecord record; public static final int STANDARD_MESSAGE_PERIOD = 500; // milis public static int STANDARD_MESSAGE_ID = 1; public int infoTime = -1; public int currentPhase = -1; public int phaseRemainingTime = -1; public double queueSize = -1; public int interGreenPhaseTime = -1; public int cycleLength = -1; public Intersection intersection; //the following intersection private static Random random = new Random(System.currentTimeMillis()); private RadioDev radio; public SimulatedCarInfo(){ } public SimulatedCarInfo(int vehicleId){ this.vehicleId = vehicleId; // radio = new RadioDev(this); } public SimulatedCarInfo(int vehicleId, byte lane, double speed, short roadIdx, short pointIdx, byte direction, double offset) { super(speed, roadIdx, pointIdx, direction, offset, lane); this.vehicleId = vehicleId; int chance = (int) (100.0 * random.nextDouble()); if (chance < Globals.PERCENT_EQUIPPED_VEHICLES) { isEquipped = true; } else { isEquipped = false; } radio = new RadioDev(this); } public SimulatedCarInfo(SimulatedCarInfo sci) { super(sci.speed, sci.roadIdx, sci.pointIdx, sci.direction, sci.offset, sci.lane); this.vehicleId = sci.vehicleId; this.timestamp = sci.timestamp; radio = new RadioDev(this); } public void init() { int standardFPSPeriod = (int)((double)(STANDARD_MESSAGE_PERIOD * Globals.engine.fps) / 1000); messagePeriods.put(STANDARD_MESSAGE_ID, new Integer(standardFPSPeriod)); } public void update() { this.direction = realPos.getDirection(); this.roadIdx = realPos.getRoadIdx(); this.pointIdx = realPos.getPointIdx(); this.speed = realPos.getSpeed(); this.lane = realPos.getLane(); this.offset = realPos.getOffset(); this.timestamp = realPos.getTimestamp(); this.intersection = realPos.intersection; } public boolean isPromiscuousMode() { return promiscuousMode; } public void setPromiscuousMode(boolean promiscuousMode) { this.promiscuousMode = promiscuousMode; } public boolean equals(Object arg0) { if (!(arg0 instanceof CarInfo)) { // System.out.println(arg0.getClass().getCanonicalName()); return false; } CarInfo sci = (CarInfo)arg0; return (vehicleId == sci.getVehicleId()); } /** * NeighborDiscovery mode * @param bytesReceived */ public void onReceive(byte[] bytesReceived, Serializable message, Communicator sender){ if (!isEquipped || bytesReceived == null) return; if (bytesReceived[0] == Globals.PROT_SETOFCARS){ parsePacket(bytesReceived); } if (bytesReceived[0] == Globals.PROT_NEIGHBOR_DISCOVERY){ // first record in message - the sender CarInfo sc = new CarInfo(); sc.wrap(bytesReceived, 2); boolean added = false; for (int i=0;i pidx && this.direction == 0 && dir == 1) || (this.getPointIdx() < pidx && this.direction == 1 && dir == 0)) ){ phaseRemainingTime = remaining; currentPhase = color; queueSize = qsize; interGreenPhaseTime = interPhase; if (Globals.engine.startedWithGUI && Globals.demo.mv.currentCar != null && this.equals(Globals.demo.mv.currentCar)){ Globals.demo.st.setSemTime(color, remaining); Globals.demo.st.setDemand("sw", (int)(queueSize*1000), 0, false); } } if (i trafficDBcopy = null; static vnsim.map.object.Map map = Globals.map; /** * Create the message to be broadcasted to neighbors. This contains either * the vehicles going in the same direction, oposite direction or all the * other cars * */ public byte[] prepareMessage(int messageType){ byte[] bytesToSend = null; if (realPos.finished) return null; if (!isEquipped) return null; //for messageType = 0, standard neighborDiscovery message if (messageType == STANDARD_MESSAGE_ID){ //neighbor discovery if (messageType == Globals.PROT_NEIGHBOR_DISCOVERY){ byte[] localinfo = toBytes(); ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); outBytes.write(Globals.PROT_NEIGHBOR_DISCOVERY); outBytes.write(CRTDIR); try{ outBytes.write(localinfo); }catch(IOException e){ } bytesToSend = outBytes.toByteArray(); } if (messageType == Globals.PROT_SETOFCARS){ if (Globals.probabilisticModel){ decideTransmissionMode(); if (pulseMessage){ if (isPromiscuousMode() && speed > 1){ super.state = 0; setPromiscuousMode(false); } if (super.state == 3){ setPromiscuousMode(true); return null; } } else { setPromiscuousMode(false); super.state = 0; } } // if (li.getToSend().getRoadIdx() != li.getPrevSent().getRoadIdx()){ // state = CRTDIR; // } int carsno; ByteArrayOutputStream outBytes = null; while(true){ int roadIndex = -1, roadIndex1 = -1; int dir = -1, direction1 = -1; carsno = 1; int off = 0; outBytes = new ByteArrayOutputStream(); if (comState == CRTDIR){ roadIndex = getRoadIdx(); dir = getDirection(); }else if (comState == OPPDIR){ roadIndex = getRoadIdx(); dir = 1 - getDirection(); } outBytes.write(Globals.PROT_SETOFCARS); // leave room for number of records outBytes.write(0); // leave room for number of records outBytes.write(comState); off++; try{ //local information if (this.speed < 1 ){ super.state = 3; //promiscuous mode }else{ super.state = 0; } if (realPos.turningLeft) setSignal((byte)1); else setSignal((byte)0); byte[] car = toBytes(); outBytes.write(car); off += car.length; if (!pulseMessage){ ListIterator it = trafficDB.listIterator(); for (; it.hasNext(); ){ CarInfo sc = it.next(); if (sc.getRoadIdx() == roadIndex && sc.getDirection() == dir){ car = sc.toBytes(); if (off + car.length > Globals.MTU_SIZE - 100){ // no more room in the packet for this car //TODO break; } outBytes.write(car); off += car.length; carsno ++; } } } }catch(IOException e){ System.out.println("Error sending cars"); e.printStackTrace(); } if (comState == OPPDIR && carsno == 1){ // no cars on the opposed movement // comState = REST; comState = CRTDIR; continue; } // if (comState == REST && carsno == 1){ // // no more cars // comState = CRTDIR; // } break; } bytesToSend = outBytes.toByteArray(); bytesToSend[1] = (byte)carsno; //set the first byte to be the number of records if (comState == CRTDIR){ comState = OPPDIR; reportedNeighbors = 0; }else if (comState == OPPDIR) comState = CRTDIR; // comState = REST; } } return bytesToSend; } static Random randgen = new Random(); public static int count = 0; public void decideTransmissionMode(){ pulseMessage = false; double liderProbability; if (reportedNeighbors == 0){ return; } if (neighMinusDistance + neighPlusDistance < 0.010){ liderProbability = (double)1 / (reportedNeighbors); }else if (neighMinusDistance + neighPlusDistance < Engine.WIRELESS_RANGE){ liderProbability = (double)3 / (reportedNeighbors); }else{ liderProbability = (double)5 / (reportedNeighbors); } double rand = randgen.nextDouble(); // System.out.println(liderProbability + " " +reportedNeighbors+ " "+(neighMinusDistance + neighPlusDistance)); if (rand > liderProbability) pulseMessage = true; } /** * Parses a TrafficView packet, and updates the local database accordingly. * * @param buffer */ public void parsePacket(byte buffer[]){ ByteArrayInputStream byteStream = new ByteArrayInputStream(buffer); byte numberOfRecords, recordsRead = 0; int offset; byte[] b = new byte[Globals.NONAGG_RECORD_SIZE]; // read protocol byte byteStream.read(); numberOfRecords = (byte)byteStream.read(); if (numberOfRecords == -1) return; offset = 2; int messageRoadDirection = byteStream.read(); //The records in the packet represent cars that run on a street in a //certain direction. //The first record is always the car that sent the packet even if it is on //a different street or runs in the opposite direction while (recordsRead < numberOfRecords){ int n = byteStream.read(b, 0, Globals.NONAGG_RECORD_SIZE); if (n == -1 || n < Globals.NONAGG_RECORD_SIZE){ break; } offset += Globals.NONAGG_RECORD_SIZE; recordsRead ++; CarInfo sc = new CarInfo(); sc.wrap(b); if (recordsRead == 1 && sc.getRoadIdx() != getRoadIdx()){ return; } if (sc.getVehicleId() == getVehicleId() || Globals.engine.crtTime - sc.getTimestamp() > Globals.NEIGHBOR_EXPIRES) continue; CarInfo storedCar = null; //no validation, other than timestamp check for each incoming car record int i = trafficDB.indexOf(sc); if (i!=-1){ storedCar = trafficDB.get(i); if (storedCar.getVehicleId() == sc.getVehicleId()){ if (storedCar.getTimestamp() <= sc.getTimestamp()) { synchronized(trafficDB){ trafficDB.set(i,sc); } } } }else{ synchronized(trafficDB){ trafficDB.add(sc); // System.out.println("Add "+dc +" at "+(Globals.trafficDB.size()-1)); } } if (recordsRead == 1){ //first record is the car that sent the packet reportedNeighbors ++; Road r = Globals.map.roads.get(roadIdx); Point p1 = r.points.get(pointIdx); Point p2 = r.points.get(sc.getPointIdx()); double distance = Math.abs(p1.getDistance() - p2.getDistance()); if (sc.getPointIdx() > this.getPointIdx() && distance > neighPlusDistance) neighPlusDistance = distance; if (sc.getPointIdx() < this.getPointIdx() && distance > neighMinusDistance) neighMinusDistance = distance; } // For the moment, I'm interested only in road I'm driving on, or in roads // connected to it. This is the relevance area. // If I receive a packet for cars that are not in this area, I drop the // packet, after I store the direct neighbor. if (recordsRead == 1 && sc.getRoadIdx() != getRoadIdx()){ // && map.connectionPoint(sc.getRoadIdx(), getRoadIdx()) ==-1 ){ return; } } } /** * delay and period of the new event (in frames) * @return the identifier of the new message type */ public int scheduleNewSendEvent(int delay, int period){ Engine engine = Globals.engine; int newType = messageNo; messageNo ++; messagePeriods.put(newType, new Integer(period)); engine.schedEvent(new SendEvent(engine.crtTime + delay, this, newType)); return newType; } /** * delay and period of the new event (in frames) * if id is not null then, the transmission will be a unicast to the car with the specified id * @return the identifier of the new message type */ public int scheduleSingleSendEvent(int delay, Integer id){ Engine engine = Globals.engine; int newType = singleMessageNo; singleMessageNo --; // if (singleMessageNo < MIN_NEG_INT) singleMessageNo = -1; if (id == null){ engine.schedEvent(new SendEvent(engine.crtTime + delay, this, newType)); }else{ engine.schedEvent(new UnicastSendEvent(engine.crtTime + delay, this, newType, id.intValue())); } return newType; } //Petroaca - emergency type message scheduler public void scheduleEmergencySendEvent(int delay) { Engine engine=Globals.engine; if(Globals.MULTI_CHANNEL) engine.schedEventEmergency(new SendEvent(engine.crtTime+delay,this,-1)); else engine.schedEvent(new SendEvent(engine.crtTime+delay,this,-1)); } public boolean isPeriodicalMessage(int messageType){ return (messageType >= 0); } public int getPeriod(int messageType){ try{ return messagePeriods.get(new Integer(messageType)).intValue(); }catch(Exception e){ return -1; } } public String toString(){ return "("+super.toString()+")"; } // public String getRealPos(){ // return "("+getVehicleId()+" - "+realPos.toString()+")"; // } public void setRealPos(CarInstance realPos) { if (this.realPos != null){ realPos.deleteCarToPointMapping(); } this.realPos = realPos; realPos.setSimulatedCarInfo(this); realPos.updateCarToPointMapping(); } public CarInstance getRealPos() { return realPos; } public int getLastMediumTransmition() { return lastMediumTransmission; } public void setLastMediumTransmition(int lastMediumTransmission) { this.lastMediumTransmission = lastMediumTransmission; } public boolean mediumAvailable(int crtTime){ int fps = Globals.engine.fps; if (crtTime - lastMediumTransmission >= Globals.WIRELESS_TRANSMISSION_FRAMES) return true; return false; } public RadioDev getRadio(){ return radio; } public ReceiveEvent getReceiveEventForTime(int t){ int i = receiveEvents.indexOf(new ReceiveEvent(t, null, null, null, 0)); if (i == -1) return null; return receiveEvents.get(i); } public void addReceiveEventForTime(ReceiveEvent re){ receiveEvents.add(re); } public void removeReceiveEventForTime(int t){ int i = receiveEvents.indexOf(new ReceiveEvent(t, null, null, null, 0)); if (i == -1) return; receiveEvents.remove(i); } //Petroaca - for the emergency receive events list public ReceiveEvent getReceiveEventEmergencyForTime(int t){ int i = receiveEventsEmergency.indexOf(new ReceiveEvent(t, null, null, null, 0)); if (i == -1) return null; return receiveEventsEmergency.get(i); } public void addReceiveEventEmergencyForTime(ReceiveEvent re){ receiveEventsEmergency.add(re); } public void removeReceiveEventEmergencyForTime(int t){ int i = receiveEventsEmergency.indexOf(new ReceiveEvent(t, null, null, null, 0)); if (i == -1) return; receiveEventsEmergency.remove(i); } }