diff --git a/src/main/java/lu/jpt/csparqltest/Main.java b/src/main/java/lu/jpt/csparqltest/Main.java index 8e2eeff..ef8400f 100644 --- a/src/main/java/lu/jpt/csparqltest/Main.java +++ b/src/main/java/lu/jpt/csparqltest/Main.java @@ -11,6 +11,7 @@ import eu.larkc.csparql.core.engine.CsparqlEngine; import eu.larkc.csparql.core.engine.CsparqlEngineImpl; import eu.larkc.csparql.core.engine.CsparqlQueryResultProxy; import lu.jpt.csparqltest.rentacar.RentACarSimulation; +import lu.jpt.csparqltest.util.RandomHelper; public class Main { @@ -45,7 +46,7 @@ public class Main { simulationThread.start(); // Now build a query to run - interchangeable - String query = Main.getSPO(); + String query = Main.getAllCarAttributesQuery(); // Create a result proxy by registering the query at the engine CsparqlQueryResultProxy resultProxy = null; @@ -85,12 +86,31 @@ public class Main { + "PREFIX car: " + "SELECT ?s ?p ?o " + "FROM STREAM [RANGE 5s STEP 1s] " - + "FROM STREAM [RANGE 5s STEP 1s] " + + "FROM STREAM [RANGE 15s STEP 1s] " + "WHERE { " + " ?s ?p ?o . " + "}"; } + private static String getAllCarAttributesQuery() { + return "REGISTER QUERY BasicCarInfo AS " + + "PREFIX f: " + + "PREFIX xsd: " + + "PREFIX car: " + + "SELECT ?car ?locked ?on ?speed ?rpm ?handbrake ?tirePressure " + + "FROM STREAM [RANGE 5s STEP 1s] " + + "FROM STREAM [RANGE 15s STEP 1s] " + + "WHERE { " + + " ?e car:subject ?car . " + + " ?e car:locked ?locked . " + + " ?e car:motorOn ?on . " + + " ?e car:speed ?speed . " + + " ?e car:motorRPM ?rpm . " + + " ?e car:handbrakeEngaged ?handbrake . " + + " ?e car:tirePressure ?pressure . " + + "}"; + } + private static String getBasicCarInfoQuery() { return "REGISTER QUERY BasicCarInfo AS " + "PREFIX f: " diff --git a/src/main/java/lu/jpt/csparqltest/rentacar/Car.java b/src/main/java/lu/jpt/csparqltest/rentacar/Car.java index 682e576..adb74fa 100644 --- a/src/main/java/lu/jpt/csparqltest/rentacar/Car.java +++ b/src/main/java/lu/jpt/csparqltest/rentacar/Car.java @@ -8,30 +8,8 @@ import lu.jpt.csparqltest.util.RandomHelper; public class Car { // State-change related enums - private enum CarAction {NONE, UNLOCKING, STARTING, ACCELERATING, BRAKING, DRIFTING, CRASHING, STOPPING, LOCKING}; - private enum CarState {LOCKED, OFF, IDLE, DRIVE}; - - /* STATE-Übergänge - Überall: NONE - - LOCKED - * UNLOCKING - - STANDING - * LOCKING - * STARTING - - IDLING - * ACCELERATING - * STOPPING - - DRIVING - * ACCELERATING - * BRAKING - * DRIFTING - * CRASHING - * STOPPING - */ + private enum CarAction {NONE, UNLOCKING, STARTING, ACCELERATING, BRAKING, CRASHING, STOPPING, LOCKING, HALTING}; + public enum CarState {LOCKED, OFF, IDLE, DRIVE, WRECKED}; // Fix knowledge about the available car types: private static final String[] CAR_TYPENAME = {"A", "B", "C"}; @@ -51,19 +29,23 @@ public class Car { private final int CAR_TYPE; private CarState currentState; + private boolean isCurrentActionHard; private CarAction currentAction; private int currentActionTicksLeft; - + + private boolean isLocked; private boolean handbrakeEngaged; private boolean motorOn; private int motorRpm; private int speed; private double tirePressure; + + // Sitzplätze? Reservierungen für X Personen? - public Car(int id, String baseIri) { + public Car(int id) { this.id = id; - this.IRI = baseIri; + this.IRI = RentACarSimulation.CAR_IRI; this.quads = new ArrayList(); this.CAR_TYPE = RandomHelper.nextInt(2); this.initializeCarType(this.CAR_TYPE); @@ -79,15 +61,124 @@ public class Car { // State data this.currentState = CarState.LOCKED; this.currentAction = CarAction.NONE; + this.isCurrentActionHard = false; this.currentActionTicksLeft = 0; } + public void keyClose() { + this.currentAction = CarAction.LOCKING; + this.currentActionTicksLeft = 1; + } + + public void keyOpen() { + this.currentAction = CarAction.UNLOCKING; + this.currentActionTicksLeft = 1; + } + + public void start() { + this.currentAction = CarAction.STARTING; + this.currentActionTicksLeft = 1; + } + + public void accelerate(boolean isHard) { + this.currentAction = CarAction.ACCELERATING; + this.isCurrentActionHard = isHard; + this.currentActionTicksLeft = 3; + } + + public void brake(boolean isHard) { + this.currentAction = CarAction.BRAKING; + this.isCurrentActionHard = isHard; + this.currentActionTicksLeft = 3; + } + + public void engageHandbrake() { + this.handbrakeEngaged = true; + } + + public void releaseHandbrake() { + this.handbrakeEngaged = false; + } + + public boolean isHandbrakeEngaged() { + return this.handbrakeEngaged; + } + + public void crash() { + this.currentAction = CarAction.CRASHING; + this.currentActionTicksLeft = 1; + // Create airbag-event + this.fireAirbagEvent(); + } + + public void comeToHalt() { + this.currentAction = CarAction.HALTING; + this.currentActionTicksLeft = 3; + } + + public void stop() { + this.currentAction = CarAction.STOPPING; + this.currentActionTicksLeft = 1; + } + /** * Update internal stuff according to state */ public void tick() { + int rpmBonus = 0; + System.err.println("Old state: " + this.currentState + ", Action: " + this.currentAction); switch(this.currentAction) { case STARTING: + this.motorOn = true; + this.motorRpm = Car.MIN_RPM[this.CAR_TYPE]; + this.currentState = CarState.IDLE; + break; + case STOPPING: + this.motorOn = false; + this.motorRpm = 0; + this.currentState = CarState.OFF; + break; + case LOCKING: + this.isLocked = true; + this.currentState = CarState.LOCKED; + break; + case UNLOCKING: + this.isLocked = false; + this.currentState = CarState.OFF; + break; + case ACCELERATING: + if(this.currentState == CarState.IDLE) this.currentState = CarState.DRIVE; + if(this.isCurrentActionHard && this.currentActionTicksLeft == 3) rpmBonus = 200; + this.motorRpm += RandomHelper.getRandomNumberWithin(100, 350 + rpmBonus); + this.speed += RandomHelper.getRandomNumberWithin(5, 20); + if(this.isCurrentActionHard) this.speed += 30; + break; + case BRAKING: + if(this.isCurrentActionHard && this.currentActionTicksLeft == 3) rpmBonus = 200; + this.motorRpm -= RandomHelper.getRandomNumberWithin(100, 350 + rpmBonus); + this.speed -= RandomHelper.getRandomNumberWithin(5, 20); + if(this.isCurrentActionHard) this.speed -= 30; + if(this.speed < 0) this.speed = 0; + if(this.motorRpm < 0) this.motorRpm = Car.MIN_RPM[this.CAR_TYPE]; + break; + case HALTING: + this.motorRpm -= RandomHelper.getRandomNumberWithin(100, 350 + rpmBonus); + this.speed -= RandomHelper.getRandomNumberWithin(5, 20); + if(this.isCurrentActionHard) this.speed -= 30; + if(this.speed < 0) this.speed = 0; + if(this.motorRpm < 0) this.motorRpm = Car.MIN_RPM[this.CAR_TYPE]; + // Halt on last tick of halting action + if(this.currentActionTicksLeft == 1) { + this.currentState = CarState.IDLE; + this.speed = 0; + this.motorRpm = Car.MIN_RPM[this.CAR_TYPE]; + } + // Decrease RPM and stuff to idle niveau + break; + case CRASHING: + this.currentState = CarState.WRECKED; + this.motorOn = false; + this.motorRpm = 0; break; case NONE: break; @@ -99,8 +190,31 @@ public class Car { if(this.currentActionTicksLeft == 0) { this.currentAction = CarAction.NONE; } + this.generateContinousReportQuads(); + System.err.println("New state: " + this.currentState); } + private void fireAirbagEvent() { + long time = System.currentTimeMillis(); + String baseIri = RentACarSimulation.BASE_IRI; + String eventIri = baseIri + "/event#" + time; + this.quads.add(new RdfQuadruple(eventIri, "http://myexample.org/car#subject", this.getIri(), time)); + this.quads.add(new RdfQuadruple(eventIri, "http://myexample.org/car#airbagTriggered", ""+true+"^^http://www.w3.org/2001/XMLSchema#boolean", time)); + } + + private void generateContinousReportQuads() { + long time = System.currentTimeMillis(); + String baseIri = RentACarSimulation.BASE_IRI; + String eventIri = baseIri + "/event#" + time; + this.quads.add(new RdfQuadruple(eventIri, "http://myexample.org/car#subject", this.getIri(), time)); + this.quads.add(new RdfQuadruple(eventIri, "http://myexample.org/car#motorOn", ""+this.motorOn+"^^http://www.w3.org/2001/XMLSchema#boolean", time)); + this.quads.add(new RdfQuadruple(eventIri, "http://myexample.org/car#motorRPM", ""+this.motorRpm+"^^http://www.w3.org/2001/XMLSchema#integer", time)); + this.quads.add(new RdfQuadruple(eventIri, "http://myexample.org/car#speed", ""+this.speed+"^^http://www.w3.org/2001/XMLSchema#integer", time)); + this.quads.add(new RdfQuadruple(eventIri, "http://myexample.org/car#handbrakeEngaged", ""+this.handbrakeEngaged+"^^http://www.w3.org/2001/XMLSchema#boolean", time)); + this.quads.add(new RdfQuadruple(eventIri, "http://myexample.org/car#tirePressure", ""+this.tirePressure+"^^http://www.w3.org/2001/XMLSchema#double", time)); + this.quads.add(new RdfQuadruple(eventIri, "http://myexample.org/car#locked", ""+this.isLocked+"^^http://www.w3.org/2001/XMLSchema#boolean", time)); + } + public CarState getState() { return this.currentState; } @@ -122,7 +236,7 @@ public class Car { } public String getIri() { - return this.IRI + "#" + this.id; + return RentACarSimulation.CAR_IRI + "#" + this.id; } public List getQuadruples() { @@ -137,5 +251,6 @@ public class Car { sb.append("]"); return sb.toString(); } + } diff --git a/src/main/java/lu/jpt/csparqltest/rentacar/CarPool.java b/src/main/java/lu/jpt/csparqltest/rentacar/CarPool.java index e1d9ee1..6359352 100644 --- a/src/main/java/lu/jpt/csparqltest/rentacar/CarPool.java +++ b/src/main/java/lu/jpt/csparqltest/rentacar/CarPool.java @@ -11,12 +11,12 @@ public class CarPool { private Random rand; - public CarPool(int numberOfCars, String carIri) { + public CarPool(int numberOfCars) { this.rand = new Random(); this.cars = new ArrayList(); this.availableCars = new ArrayList(); for(int i = 0; i < numberOfCars; i++) { - Car newCar = new Car(i, carIri); + Car newCar = new Car(i); this.cars.add(newCar); this.availableCars.add(newCar); } @@ -27,8 +27,14 @@ public class CarPool { * @return Car the random car */ public Car takeRandomCar() { - Car randomCar = this.availableCars.get(this.rand.nextInt(this.availableCars.size())); - this.availableCars.remove(randomCar); + Car randomCar = null; + if(this.availableCars.isEmpty()) { + throw new RuntimeException("Tried to take a car, but no cars were there!"); + } else { + int randomIndex = this.rand.nextInt(this.availableCars.size()); + randomCar = this.availableCars.get(randomIndex); + this.availableCars.remove(randomCar); + } return randomCar; } diff --git a/src/main/java/lu/jpt/csparqltest/rentacar/Driver.java b/src/main/java/lu/jpt/csparqltest/rentacar/Driver.java index 14ebcf3..254b086 100644 --- a/src/main/java/lu/jpt/csparqltest/rentacar/Driver.java +++ b/src/main/java/lu/jpt/csparqltest/rentacar/Driver.java @@ -8,24 +8,33 @@ import lu.jpt.csparqltest.util.RandomHelper; public class Driver { + // Traits with corresponding behaviour for certain situations + public enum Trait {CAREFUL, REGULAR, FAST, INSANE}; + private static final double[] chanceToEngageHandbrakeOnLock = {1.00, 0.85, 0.60, 0.05}; + private static final double[] chanceToReleaseHandbrakeOnUnlock = {1.00, 0.95, 0.90, 0.84}; + private static final double[] chanceToCrashCar = {0.05, 0.10, 0.20, 0.40}; + private static final double[] chanceToDriftCar = {0.00, 0.05, 0.15, 0.70}; + private static final double[] chanceToChangeSpeedHard = {0.00, 0.10, 0.30, 0.80}; + private static final double[] chanceToStallEngine = {0.20, 0.01, 0.00, 0.00}; + private int id; - private final String IRI; private CarPool carPool; private int carUseCycles; private Car car; + private Trait trait; + private List quads; - private enum Trait {CAREFUL, REGULAR, FAST, INSANE}; - public Driver(int id, CarPool carPool, String baseIri) { + public Driver(int id, CarPool carPool) { this.id = id; this.carPool = carPool; - this.IRI = baseIri; this.car = null; this.quads = new ArrayList(); // Pick a random behaviour and stick to it. + this.trait = (Trait) RandomHelper.getRandomElementFromArray(Trait.values()); } public int getID() { @@ -33,7 +42,7 @@ public class Driver { } public String getIri() { - return this.IRI + "#" + this.id; + return RentACarSimulation.DRIVER_IRI + "#" + this.id; } /** @@ -43,35 +52,113 @@ public class Driver { public void tick() { if(car == null) { // Nothing to do, maybe randomly take a free car - if(RandomHelper.isLuckyByChance(0.4)) { - this.assignCar(this.carPool.takeRandomCar(), RandomHelper.getRandomNumberWithin(10,50)); + if(RandomHelper.isLuckyByChance(0.75)) { + this.takeRandomCarFor(RandomHelper.getRandomNumberWithin(20,50)); + } + } else { + // Do random things with the car + if(this.car.needsInput()) { + this.useCar(this.car); + } + // Decrement use cycles + this.carUseCycles--; + if(this.carUseCycles == 0) { + this.returnCar(); } } - // Do random things with the car - if(this.car.needsInput()) { - this.useCar(this.car); - } - // Decrement use cycles - this.carUseCycles--; - if(this.carUseCycles == 0) { - this.returnCar(); - } } + /** + * Interact with the car by starting a CarAction on the car. + * @param car The given car + */ private void useCar(Car car) { - // Whatever, hit the gas, slam the breaks, drift around, drive casually - + Car.CarState carState = car.getState(); + if(carState == Car.CarState.WRECKED) { + // Sad fade :-( + return; + } + switch(carState) { + case LOCKED: + if(this.carUseCycles > 5) { + car.keyOpen(); + } + break; + case OFF: + if(this.carUseCycles > 4) { + car.start(); + if(RandomHelper.isLuckyByChance(Driver.chanceToReleaseHandbrakeOnUnlock[this.trait.ordinal()])) { + car.releaseHandbrake(); + } + } else { + if(RandomHelper.isLuckyByChance(Driver.chanceToEngageHandbrakeOnLock[this.trait.ordinal()])) { + car.engageHandbrake(); + } + car.keyClose(); + } + break; + case IDLE: + if(this.carUseCycles > 3) { + car.accelerate(RandomHelper.isLuckyByChance(Driver.chanceToChangeSpeedHard[this.trait.ordinal()])); + } else { + car.stop(); + } + break; + case DRIVE: + // Always release handbrake after drifting or forgetting! + if(car.isHandbrakeEngaged()) { + car.releaseHandbrake(); + } + if(this.carUseCycles > 3) { + if(RandomHelper.isLuckyByChance(Driver.chanceToCrashCar[this.trait.ordinal()])) { + car.crash(); + } else if(RandomHelper.isLuckyByChance(Driver.chanceToStallEngine[this.trait.ordinal()])) { + car.stop(); + } else { + int nextAction = RandomHelper.getRandomNumberWithin(0, 1); + switch(nextAction) { + case 0: + boolean accHard = RandomHelper.isLuckyByChance(Driver.chanceToChangeSpeedHard[this.trait.ordinal()]); + car.accelerate(accHard); + if(accHard) { + if(RandomHelper.isLuckyByChance(Driver.chanceToDriftCar[this.trait.ordinal()])) { + car.engageHandbrake(); + } + } + break; + case 1: + car.brake(RandomHelper.isLuckyByChance(Driver.chanceToChangeSpeedHard[this.trait.ordinal()])); + break; + } + } + } else { + car.comeToHalt(); + } + break; + default: + System.err.println("Driver cannot handle car state: " + carState); + break; + } } - private void assignCar(Car car, int useCycles) { - this.car = car; + private void takeRandomCarFor(int useCycles) { + this.car = this.carPool.takeRandomCar(); this.carUseCycles = useCycles; - this.quads.add(new RdfQuadruple(this.getIri(), this.IRI+"#took", this.car.getIri(), System.currentTimeMillis())); - this.quads.add(new RdfQuadruple(this.getIri(), this.IRI+"#usagePeriod", useCycles+"^^http://www.w3.org/2001/XMLSchema#integer", System.currentTimeMillis())); + long time = System.currentTimeMillis(); + String baseIri = RentACarSimulation.BASE_IRI; + String eventIri = baseIri + "/event#" + time; + this.quads.add(new RdfQuadruple(eventIri, baseIri+"#user", this.getIri(), time)); + this.quads.add(new RdfQuadruple(eventIri, baseIri+"#took", this.car.getIri(), System.currentTimeMillis())); + this.quads.add(new RdfQuadruple(eventIri, baseIri+"#usagePeriod", ""+useCycles+"^^http://www.w3.org/2001/XMLSchema#integer", System.currentTimeMillis())); } private void returnCar() { - this.quads.add(new RdfQuadruple(this.getIri(), this.IRI+"#returned", this.car.getIri(), System.currentTimeMillis())); + long time = System.currentTimeMillis(); + String baseIri = RentACarSimulation.BASE_IRI; + String eventIri = baseIri + "/event#" + time; + this.quads.add(new RdfQuadruple(eventIri, baseIri+"#user", this.getIri(), time)); + this.quads.add(new RdfQuadruple(eventIri, baseIri+"#returned", this.car.getIri(), time)); + this.carPool.returnCar(this.car); this.car = null; } @@ -84,6 +171,7 @@ public class Driver { public String toString() { StringBuilder sb = new StringBuilder("[#"); sb.append(this.id); + sb.append(" trait="+this.trait); sb.append("]"); return sb.toString(); } diff --git a/src/main/java/lu/jpt/csparqltest/rentacar/RentACarSimulation.java b/src/main/java/lu/jpt/csparqltest/rentacar/RentACarSimulation.java index a827906..74f3477 100644 --- a/src/main/java/lu/jpt/csparqltest/rentacar/RentACarSimulation.java +++ b/src/main/java/lu/jpt/csparqltest/rentacar/RentACarSimulation.java @@ -22,13 +22,15 @@ public class RentACarSimulation implements Runnable { public RentACarSimulation() { - int numberOfCars = 6; + int numberOfCars = 1; int numberOfCustomers = 1; // Create a car pool and drivers - this.carPool = new CarPool(numberOfCars, CAR_IRI); + this.carPool = new CarPool(numberOfCars); this.drivers = new ArrayList(); for(int i = 0; i < numberOfCustomers; i++) { - this.drivers.add(new Driver(i, this.carPool, DRIVER_IRI)); + Driver driver = new Driver(i, this.carPool); + System.out.println(driver); + this.drivers.add(driver); } // Create streams this.carStream = new RdfStream(CAR_IRI); diff --git a/src/main/java/lu/jpt/csparqltest/util/RandomHelper.java b/src/main/java/lu/jpt/csparqltest/util/RandomHelper.java index ebc2b79..cb8a11f 100644 --- a/src/main/java/lu/jpt/csparqltest/util/RandomHelper.java +++ b/src/main/java/lu/jpt/csparqltest/util/RandomHelper.java @@ -21,4 +21,8 @@ public class RandomHelper { return RandomHelper.myRandom.nextDouble() < chance; } + public static Object getRandomElementFromArray(Object[] arr) { + return arr[RandomHelper.getRandomNumberWithin(0, (arr.length - 1))]; + } + }