org.onebusaway.nyc.vehicle_tracking.impl.simulator.SimulatorTask.java Source code

Java tutorial

Introduction

Here is the source code for org.onebusaway.nyc.vehicle_tracking.impl.simulator.SimulatorTask.java

Source

/**
 * Copyright (c) 2011 Metropolitan Transportation Authority
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package org.onebusaway.nyc.vehicle_tracking.impl.simulator;

import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;

import org.onebusaway.csv_entities.EntityHandler;
import org.onebusaway.gtfs.model.AgencyAndId;
import org.onebusaway.nyc.vehicle_tracking.impl.particlefilter.Particle;
import org.onebusaway.nyc.vehicle_tracking.model.NycRawLocationRecord;
import org.onebusaway.nyc.vehicle_tracking.model.NycTestInferredLocationRecord;
import org.onebusaway.nyc.vehicle_tracking.model.library.RecordLibrary;
import org.onebusaway.nyc.vehicle_tracking.model.simulator.VehicleLocationDetails;
import org.onebusaway.nyc.vehicle_tracking.model.simulator.VehicleLocationSimulationSummary;
import org.onebusaway.nyc.vehicle_tracking.services.inference.VehicleLocationInferenceService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.HashMultiset;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multiset;
import com.google.common.collect.Ordering;
import com.google.common.collect.TreeMultiset;

public class SimulatorTask implements Runnable, EntityHandler {

    private int _maxParticleHistorySize = Integer.MAX_VALUE;

    private static Logger _log = LoggerFactory.getLogger(SimulatorTask.class);

    private static DateFormat _format = DateFormat.getTimeInstance(DateFormat.SHORT);

    private final List<NycTestInferredLocationRecord> _records = new ArrayList<NycTestInferredLocationRecord>();

    private final List<NycTestInferredLocationRecord> _results = new ArrayList<NycTestInferredLocationRecord>();

    private final List<VehicleLocationDetails> _details = new ArrayList<VehicleLocationDetails>();

    private final AtomicInteger _recordsProcessed = new AtomicInteger();

    private NycTestInferredLocationRecord _mostRecentRecord = null;

    private VehicleLocationInferenceService _vehicleLocationInferenceService;

    private int _id;

    private Future<?> _future;

    private volatile int _recordIndex = 0;

    private volatile boolean _paused;

    private volatile boolean _stepForOne = false;

    private volatile boolean _resetRecordIndex = true;

    private int _stepRecordIndex = -1;

    private boolean _runInRealtime = false;

    private boolean _shiftStartTime = false;

    private boolean _bypassInference = false;

    private boolean _fillActualProperties = false;

    private int _minimumRecordInterval = 0;

    private long _startTimeOffset = 0;

    private final Set<Object> _tags = new HashSet<Object>();

    private AgencyAndId _vehicleId = null;

    private boolean _complete = false;

    private boolean _loop;

    private String _filename = null;

    private int _particleParentSize = 2;

    private final long creationTime = System.currentTimeMillis();

    public SimulatorTask() {
    }

    public void setVehicleLocationService(VehicleLocationInferenceService vehicleLocationService) {
        _vehicleLocationInferenceService = vehicleLocationService;
    }

    public void setId(int id) {
        _id = id;
    }

    public int getId() {
        return _id;
    }

    public void setFuture(Future<?> future) {
        _future = future;
    }

    public Future<?> getFuture() {
        return _future;
    }

    public void setPauseOnStart(boolean pauseOnStart) {
        _paused = pauseOnStart;
    }

    public void setRunInRealtime(boolean runInRealtime) {
        _runInRealtime = runInRealtime;
    }

    public void setShiftStartTime(boolean shiftStartTime) {
        _shiftStartTime = shiftStartTime;
    }

    public void setMinimumRecordInterval(int minimumRecordInterval) {
        _minimumRecordInterval = minimumRecordInterval;
    }

    public void setBypassInference(boolean bypassInference) {
        _bypassInference = bypassInference;
    }

    public void setFillActualProperties(boolean fillActualProperties) {
        _fillActualProperties = fillActualProperties;
    }

    public void setLoop(boolean loop) {
        _loop = loop;
    }

    public void addTag(Object tag) {
        _tags.add(tag);
    }

    public Set<Object> getTags() {
        return _tags;
    }

    public void addRecord(NycTestInferredLocationRecord record) {
        if (_shiftStartTime) {
            if (_records.isEmpty())
                _startTimeOffset = System.currentTimeMillis() - record.getTimestamp();
            record.setTimestamp(record.getTimestamp() + _startTimeOffset);
        }

        // Should we prune a record that comes too close behind a previous record?
        if (!_records.isEmpty()) {
            final NycTestInferredLocationRecord previous = _records.get(_records.size() - 1);
            if ((record.getTimestamp() - previous.getTimestamp()) / 1000 < _minimumRecordInterval)
                return;
        }

        if (_records.isEmpty() && record.locationDataIsMissing()) {
            _log.info("pruning initial record with no gps data");
            return;
        }

        _records.add(record);
        record.setRecordNumber(_records.size() - 1);

        /*
         * We add this time so that multiple traces for the 
         * same vehicle id can be run without conflict.
         */
        final String adjId = record.getVehicleId().getId() + "_" + this.creationTime;
        record.getVehicleId().setId(adjId);

        final AgencyAndId vid = record.getVehicleId();
        if (_vehicleId != null) {
            if (!_vehicleId.equals(vid))
                throw new IllegalArgumentException(
                        "simulation should only include records for same vehicleId: expected=" + _vehicleId
                                + " actual=" + vid);
        } else {
            _vehicleId = vid;
        }
    }

    public List<NycTestInferredLocationRecord> getRecords() {
        return _records;
    }

    public synchronized boolean isComplete() {
        return _complete;
    }

    public synchronized void toggle() {
        _paused = !_paused;
        notify();
    }

    public synchronized void step() {
        _paused = true;
        _stepForOne = true;
        notify();
    }

    public synchronized void step(int recordIndex) {
        _paused = true;
        _stepForOne = true;
        _stepRecordIndex = recordIndex;
        notify();
    }

    public synchronized void restart() {
        _paused = true;
        _stepForOne = false;
        _stepRecordIndex = -1;
        _resetRecordIndex = true;
        _details.clear();
        notify();
    }

    public VehicleLocationSimulationSummary getSummary() {
        final VehicleLocationSimulationSummary summary = new VehicleLocationSimulationSummary();
        summary.setFilename(_filename);
        summary.setId(_id);
        summary.setVehicleId(_vehicleId);
        summary.setNumberOfRecordsProcessed(_recordsProcessed.get());
        summary.setNumberOfRecordsTotal(_records.size());
        summary.setMostRecentRecord(_mostRecentRecord);
        summary.setPaused(_paused);
        return summary;
    }

    public VehicleLocationDetails getDetails(int recordNumber) {
        return recordNumber < 0 || recordNumber >= _details.size() ? null : _details.get(recordNumber);
        // int index = 0;
        // for (final Iterator<VehicleLocationDetails> it =
        // _details.descendingIterator(); it.hasNext();) {
        // final VehicleLocationDetails details = it.next();
        // if (index == historyOffset)
        // return details;
        // index++;
        // }
        // return null;
    }

    public VehicleLocationDetails getTransitionParticleDetails(int parentParticleId, int transParticleNumber,
            int recordIndex) {
        final VehicleLocationDetails details = new VehicleLocationDetails();
        details.setId(_id);

        final Collection<Multiset.Entry<Particle>> particles;
        if (recordIndex < 0) {
            details.setLastObservation(
                    RecordLibrary.getNycTestInferredLocationRecordAsNycRawLocationRecord(_mostRecentRecord));
            particles = _vehicleLocationInferenceService.getCurrentParticlesForVehicleId(_vehicleId).entrySet();
        } else {
            details.setLastObservation(getDetails(recordIndex).getLastObservation());
            particles = getDetails(recordIndex).getParticles();
        }

        if (particles != null) {
            for (final Multiset.Entry<Particle> pEntry : particles) {
                final Particle p = pEntry.getElement();
                if (p.getIndex() == parentParticleId) {
                    final Multiset<Particle> history = HashMultiset.create();
                    history.add(Iterables.get(p.getTransitions().elementSet(), transParticleNumber));
                    details.setParticles(history);
                    details.setHistory(true);
                    break;
                }
            }
        }
        return details;
    }

    public VehicleLocationDetails getParticleDetails(int particleId, int recordIndex) {
        final VehicleLocationDetails details = new VehicleLocationDetails();
        details.setId(_id);

        final Collection<Multiset.Entry<Particle>> particles;
        if (recordIndex < 0) {
            details.setLastObservation(
                    RecordLibrary.getNycTestInferredLocationRecordAsNycRawLocationRecord(_mostRecentRecord));
            particles = _vehicleLocationInferenceService.getCurrentParticlesForVehicleId(_vehicleId).entrySet();
        } else {
            details.setLastObservation(getDetails(recordIndex).getLastObservation());
            particles = getDetails(recordIndex).getParticles();
        }

        if (particles != null) {
            for (final Multiset.Entry<Particle> pEntry : particles) {
                Particle p = pEntry.getElement();
                if (p.getIndex() == particleId) {
                    final Multiset<Particle> history = TreeMultiset.create(Ordering.natural());
                    while (p != null && history.elementSet().size() <= _particleParentSize) {
                        history.add(p, pEntry.getCount());
                        p = p.getParent();
                    }
                    details.setParticles(history);
                    details.setHistory(true);
                    break;
                }
            }
        }
        return details;
    }

    public List<NycTestInferredLocationRecord> getResults() {
        synchronized (_results) {
            return new ArrayList<NycTestInferredLocationRecord>(_results);
        }
    }

    public void resetAllVehiclesAppearingInRecordData() {
        final Set<AgencyAndId> vehicleIds = new HashSet<AgencyAndId>();

        for (final NycTestInferredLocationRecord record : _records)
            vehicleIds.add(record.getVehicleId());

        for (final AgencyAndId vehicleId : vehicleIds)
            _vehicleLocationInferenceService.resetVehicleLocation(vehicleId);
    }

    /****
     * {@link Runnable} Interface
     ****/

    @Override
    public void run() {

        try {
            synchronized (this) {
                _complete = false;
            }

            runInternal();

        } catch (final Throwable ex) {
            _log.error("error in simulation run", ex);
        }

        synchronized (this) {
            _complete = true;
        }
    }

    private void runInternal() {

        while (true) {

            if (Thread.interrupted())
                return;

            if (shouldExitAfterPossiblePause())
                return;

            int nextRecordIndex = getNextRecordIndex();

            if (nextRecordIndex >= _records.size()) {
                if (!_loop)
                    return;

                restart();
                checkReset();
                _paused = false;
                nextRecordIndex = getNextRecordIndex();
            }

            final NycTestInferredLocationRecord record = _records.get(nextRecordIndex);

            if (shouldExitAfterSimulatedWait(record))
                return;

            _log.info(
                    "sending record: index=" + nextRecordIndex + " " + _format.format(record.getTimestampAsDate()));

            if (_bypassInference) {
                _vehicleLocationInferenceService.handleBypassUpdateForNycTestInferredLocationRecord(record);
            } else {
                _vehicleLocationInferenceService.handleNycTestInferredLocationRecord(record);
            }

            if (shouldExitAfterWaitingForInferenceToComplete(record))
                ;
            // return;

            _mostRecentRecord = record;
            _recordsProcessed.incrementAndGet();
        }
    }

    /****
     * {@link EntityHandler} Interface
     ****/

    @Override
    public void handleEntity(Object bean) {
        if (bean instanceof NycRawLocationRecord) {

            final NycRawLocationRecord vlr = (NycRawLocationRecord) bean;

            final long t = RecordLibrary.getBestTimestamp(vlr.getTime(), vlr.getTimeReceived());

            final NycTestInferredLocationRecord record = new NycTestInferredLocationRecord();
            record.setDsc(vlr.getDestinationSignCode());
            record.setLat(vlr.getLatitude());
            record.setLon(vlr.getLongitude());
            record.setTimestamp(t);
            record.setVehicleId(vlr.getVehicleId());

            bean = record;
        }
        final NycTestInferredLocationRecord record = (NycTestInferredLocationRecord) bean;
        addRecord(record);
    }

    private boolean shouldExitAfterSimulatedWait(NycTestInferredLocationRecord record) {

        if (_runInRealtime && _mostRecentRecord != null) {

            final long time = record.getTimestamp() - _mostRecentRecord.getTimestamp();

            try {
                Thread.sleep(time);
            } catch (final InterruptedException e) {
                return true;
            }
        }

        return false;
    }

    private synchronized boolean shouldExitAfterPossiblePause() {

        checkReset();

        while (_paused && !_stepForOne) {
            try {
                wait();
            } catch (final InterruptedException e) {
                return true;
            }

            checkReset();
        }

        if (_stepRecordIndex == -1 || _stepRecordIndex < _recordsProcessed.intValue()) {
            _stepForOne = false;
            _stepRecordIndex = -1;
        }

        return false;
    }

    private synchronized int getNextRecordIndex() {
        checkReset();
        return _recordIndex++;
    }

    private void checkReset() {
        if (_resetRecordIndex) {
            resetAllVehiclesAppearingInRecordData();
            _recordIndex = 0;
            _resetRecordIndex = false;
            _results.clear();
            _recordsProcessed.set(0);
        }
    }

    private boolean shouldExitAfterWaitingForInferenceToComplete(NycTestInferredLocationRecord record) {

        for (int i = 0; i < 20; i++) {

            if (processResultRecord(record))
                return false;

            try {
                Thread.sleep(10 * i);
            } catch (final InterruptedException ex) {
                return true;
            }
        }

        _log.warn("vehicle location inference never completed");
        return true;
    }

    private boolean processResultRecord(NycTestInferredLocationRecord record) {

        final NycTestInferredLocationRecord rr = _vehicleLocationInferenceService
                .getNycTestInferredLocationRecordForVehicle(record.getVehicleId());

        if (rr == null || rr.getTimestamp() < record.getTimestamp())
            return false;

        rr.setVehicleId(_vehicleId);

        if (_fillActualProperties) {
            rr.setActualRunId(rr.getInferredRunId());
            rr.setActualBlockId(rr.getInferredBlockId());
            rr.setActualTripId(rr.getInferredTripId());
            rr.setActualBlockLat(rr.getInferredBlockLat());
            rr.setActualBlockLon(rr.getInferredBlockLon());
            rr.setActualDistanceAlongBlock(rr.getInferredDistanceAlongBlock());
            rr.setActualScheduleTime(rr.getInferredScheduleTime());
            rr.setActualDsc(rr.getInferredDsc());
            rr.setActualPhase(rr.getInferredPhase());
            rr.setActualServiceDate(rr.getInferredServiceDate());
            rr.setActualStatus(rr.getInferredStatus());

            rr.clearInferredValues();
        }

        rr.setAssignedRunId(record.getAssignedRunId());
        rr.setActualIsRunFormal(record.getActualIsRunFormal());
        rr.setActualRunId(record.getActualRunId());
        rr.setActualBlockId(record.getActualBlockId());
        rr.setActualTripId(record.getActualTripId());
        rr.setActualBlockLat(record.getActualBlockLat());
        rr.setActualBlockLon(record.getActualBlockLon());
        rr.setActualDistanceAlongBlock(record.getActualDistanceAlongBlock());
        rr.setActualScheduleTime(record.getActualScheduleTime());
        rr.setActualDsc(record.getActualDsc());
        rr.setActualPhase(record.getActualPhase());
        rr.setActualServiceDate(record.getActualServiceDate());
        rr.setActualStatus(record.getActualStatus());

        synchronized (_results) {
            _results.add(rr);
            rr.setRecordNumber(_results.size() - 1);
        }

        final VehicleLocationDetails details = new VehicleLocationDetails();
        details.setId(_id);
        details.setVehicleId(_vehicleId);
        details.setLastObservation(RecordLibrary.getNycTestInferredLocationRecordAsNycRawLocationRecord(record));

        final Multiset<Particle> weightedParticles = TreeMultiset.create(Ordering.natural());
        weightedParticles.addAll(_vehicleLocationInferenceService.getCurrentParticlesForVehicleId(_vehicleId));
        if (!weightedParticles.isEmpty()) {
            details.setParticles(weightedParticles);
            details.setSampledParticles(weightedParticles);
        }

        if (_details.size() < _maxParticleHistorySize)
            _details.add(details);

        return true;
    }

    public String getFilename() {
        return _filename;
    }

    public void setFilename(String filename) {
        _filename = filename;
    }

    public int getMaxParticleHistorySize() {
        return _maxParticleHistorySize;
    }

    public void setMaxParticleHistorySize(int maxParticleHistorySize) {
        _maxParticleHistorySize = maxParticleHistorySize;
    }

    public int getParticleParentSize() {
        return _particleParentSize;
    }

    public void setParticleParentSize(int particleParentSize) {
        _particleParentSize = particleParentSize;
    }

    public AgencyAndId getVehicleId() {
        return this._vehicleId;
    }
}