package com.virtualpairprogrammers.simulator.journey; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Scanner; import java.util.concurrent.LinkedBlockingQueue; import javax.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import com.virtualpairprogrammers.simulator.PositionsimulatorApplication; import com.virtualpairprogrammers.simulator.services.PositionTrackerExternalService; import com.virtualpairprogrammers.simulator.services.VehicleBuilder; import com.virtualpairprogrammers.simulator.services.VehiclePosition; import com.virtualpairprogrammers.simulator.utils.VehicleNameUtils; /** * Invoked periodically by Quartz, a random position update is selected and transmitted. */ @Component public class JourneySimulator { @Autowired private PositionTrackerExternalService positionTracker; private Map<String, Queue<String>> reports = new HashMap<>(); private List<String> vehicleNames = new ArrayList<>(); /** * Read the data from the resources directory - should work for an executable Jar as * well as through direct execution */ @PostConstruct private void setUpData() { PathMatchingResourcePatternResolver path = new PathMatchingResourcePatternResolver(); try { for (Resource nextFile : path.getResources("tracks/*")) { URL resource = nextFile.getURL(); File f = new File(resource.getFile()); String vehicleName = VehicleNameUtils.prettifyName(f.getName()); vehicleNames.add(vehicleName); populateReportQueueForVehicle(vehicleName); } } catch (IOException e) { throw new RuntimeException(e); } positionTracker.clearHistories(); } private void populateReportQueueForVehicle(String vehicleName) { InputStream is = PositionsimulatorApplication.class.getResourceAsStream("/tracks/" + VehicleNameUtils.uglifyName(vehicleName)); try (Scanner sc = new Scanner(is)) { Queue<String> thisVehicleReports = new LinkedBlockingQueue<>(); while (sc.hasNextLine()) { String nextReport = sc.nextLine(); thisVehicleReports.add(nextReport); } reports.put(vehicleName,thisVehicleReports); } } @Scheduled(fixedDelay=100) public void randomPositionUpdate() { // Random jitter. Sometimes we'll do nothing if (Math.random() < 0.9) return; // Choose random vehicle int position = (int)(Math.random() * vehicleNames.size()); String chosenVehicleName = vehicleNames.get(position); // Grab next report for this vehicle // To smooth out fluctuations, we'll miss out lots of reports int randomReportDrop = (int)(Math.random() * 10); String nextReport = null; for (int i=0; i<=randomReportDrop; i++) { nextReport = reports.get(chosenVehicleName).poll(); if (nextReport == null) { System.out.println("Journey over for " + chosenVehicleName + ". Restarting route"); populateReportQueueForVehicle(chosenVehicleName); nextReport = reports.get(chosenVehicleName).poll(); } } VehiclePosition report = getVehicleDataFromRawString(chosenVehicleName, nextReport); positionTracker.sendReportToPositionTracker(report); } private VehiclePosition getVehicleDataFromRawString(String chosenVehicleName, String nextReport) { String[] data = nextReport.split("\""); String lat = data[1]; String lng = data[3]; VehiclePosition report = new VehicleBuilder() .withName(chosenVehicleName) .withLat(lat) .withLng(lng) .withTimestamp(new java.util.Date()) .build(); return report; } }