/*
* Copyright (C) 2013 Serdar
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.fub.maps.project.detector.model;
import de.fub.maps.project.api.process.ProcessState;
import de.fub.maps.project.api.statistics.StatisticProvider;
import de.fub.maps.project.detector.filetype.DetectorDataObject;
import de.fub.maps.project.detector.model.gpx.TrackSegment;
import de.fub.maps.project.detector.model.inference.AbstractInferenceModel;
import de.fub.maps.project.detector.model.pipeline.postprocessors.PostProcessorPipeline;
import de.fub.maps.project.detector.model.pipeline.postprocessors.tasks.Task;
import de.fub.maps.project.detector.model.pipeline.preprocessors.FilterProcess;
import de.fub.maps.project.detector.model.pipeline.preprocessors.PreProcessorPipeline;
import de.fub.maps.project.detector.model.process.DetectorProcess;
import de.fub.maps.project.detector.model.xmls.DetectorDescriptor;
import de.fub.maps.project.detector.model.xmls.InferenceModelDescriptor;
import de.fub.maps.project.detector.model.xmls.PostProcessors;
import de.fub.maps.project.detector.model.xmls.PreProcessors;
import de.fub.maps.project.detector.model.xmls.ProcessDescriptor;
import de.fub.maps.project.detector.model.xmls.Profile;
import de.fub.utilsmodule.synchronizer.ModelSynchronizer;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.xml.bind.JAXBException;
import org.openide.nodes.Node.Cookie;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.openide.util.lookup.ProxyLookup;
/**
*
* @author Serdar
*/
public class Detector extends ModelSynchronizer implements Lookup.Provider, TrainingsDataProvider, Cookie {
public static final String PROP_NAME_DETECTOR_STATE = "detector.state";
private static final Logger LOG = Logger.getLogger(Detector.class.getName());
private DetectorDataObject dataObject;
private final PreProcessorPipeline preProcessorPipeline = new PreProcessorPipeline(this);
private final PostProcessorPipeline postProcessorPipeline = new PostProcessorPipeline(this);
private final Object MUTEX_PROCESS_RUNNING = new Object();
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private AbstractInferenceModel inferenceModel;
private ProcessState detectorState = ProcessState.INACTIVE;
private ModelSynchronizerClient dataObjectModelSynchronizerClient;
private Profile currentActiveProfile = null;
private DetectorRunSupport detectorRunSupport;
private ProxyLookup lookup;
public Detector(DetectorDataObject dataObject) {
assert dataObject != null;
this.dataObject = dataObject;
init();
}
/**
*
*/
private void init() {
this.lookup = new ProxyLookup(Lookups.fixed(Detector.this, dataObject), dataObject.getLookup());
this.dataObject.addChangeListener(new ChangeListenerImpl());
reinit();
}
/**
* Initialized the this Detector the underlying Descriptor.
*/
private void reinit() {
setDetectorState(ProcessState.INACTIVE);
DetectorDescriptor detectorDescriptor = getDetectorDescriptor();
if (detectorDescriptor != null) {
// initialize the inference model
initializeInferenceModel();
// initialize the preprocessors
initalizePreProcessors();
// initialize the postprocessors
initializePostProcessors();
// determine active profile
initializeProfile();
} else {
setDetectorState(ProcessState.ERROR);
}
}
/**
* Initializes the InferenceModel of this Detector.
*/
private void initializeInferenceModel() {
DetectorDescriptor detectorDescriptor = getDetectorDescriptor();
if (detectorDescriptor != null) {
// initialize the inference model
InferenceModelDescriptor inferenceModelDescriptor = detectorDescriptor.getInferenceModel();
if (inferenceModelDescriptor != null) {
try {
inferenceModel = AbstractInferenceModel.find(inferenceModelDescriptor.getJavaType(), Detector.this);
} catch (DetectorProcess.DetectorProcessNotFoundException ex) {
Exceptions.printStackTrace(ex);
}
}
}
}
/**
* initializes the pre processors, which are provided by the Descriptor, and
* the pipeline.
*/
private void initalizePreProcessors() {
DetectorDescriptor detectorDescriptor = getDetectorDescriptor();
if (detectorDescriptor != null) {
// initialize the preprocessors
PreProcessors preprocessors = detectorDescriptor.getPreprocessors();
if (preprocessors != null) {
FilterProcess filter = null;
getPreProcessorPipeline().clear();
for (ProcessDescriptor processDescriptor : preprocessors.getPreprocessorList()) {
try {
filter = FilterProcess.find(processDescriptor, Detector.this);
getPreProcessorPipeline().add(filter);
} catch (DetectorProcess.DetectorProcessNotFoundException ex) {
Exceptions.printStackTrace(ex);
}
}
}
}
}
/**
* Initializes the post processors and the pipeline.
*/
private void initializePostProcessors() {
DetectorDescriptor detectorDescriptor = getDetectorDescriptor();
if (detectorDescriptor != null) {
// initialize the postprocessors
PostProcessors postprocessors = detectorDescriptor.getPostprocessors();
if (postprocessors != null) {
Task task = null;
getPostProcessorPipeline().clear();
for (ProcessDescriptor processDescriptor : postprocessors.getPostprocessorList()) {
try {
task = Task.find(processDescriptor, Detector.this);
getPostProcessorPipeline().add(task);
} catch (DetectorProcess.DetectorProcessNotFoundException ex) {
Exceptions.printStackTrace(ex);
}
}
}
}
}
/**
* Initializes the Profile.
*/
private void initializeProfile() {
DetectorDescriptor detectorDescriptor = getDetectorDescriptor();
if (detectorDescriptor != null) {
// determine active profile
if (!detectorDescriptor.getProfiles().getProfileList().isEmpty()) {
if (detectorDescriptor.getProfiles() == null) {
currentActiveProfile = detectorDescriptor.getProfiles().getProfileList().iterator().next();
} else {
for (Profile profile : detectorDescriptor.getProfiles().getProfileList()) {
if (profile.getName() != null && detectorDescriptor.getProfiles().getActiveProfile().equals(profile.getName())) {
currentActiveProfile = profile;
break;
}
}
if (currentActiveProfile == null) {
currentActiveProfile = detectorDescriptor.getProfiles().getProfileList().iterator().next();
}
}
}
}
}
/**
* Executes the Detector.
*/
@NbBundle.Messages({
"# {0} - detectorName",
"# {1} - processName",
"CLT_Running_Process={0}: Running {1}..."})
public void start() {
synchronized (MUTEX_PROCESS_RUNNING) {
getDetectorRunSupport().start();
}
}
@Override
public Map<String, List<TrackSegment>> getData() {
return getTrainingsSet();
}
/**
* Returns the name of this Detector
*
* @return
*/
@Override
public String getName() {
return Detector.this.getDataObject().getName();
}
/**
* Convience method to access the underlying controller of the Detector.
*
* @return DetectorRunSupport.
*/
private DetectorRunSupport getDetectorRunSupport() {
synchronized (MUTEX_PROCESS_RUNNING) {
if (detectorRunSupport == null) {
detectorRunSupport = new DetectorRunSupport(Detector.this);
}
return detectorRunSupport;
}
}
/**
* Returns the Training dataset of this Detector, which will be used for the
* AbstractInferenceModel to train the InferenceModel.
*
* @return Map
*/
public Map<String, List<TrackSegment>> getTrainingsSet() {
synchronized (MUTEX_PROCESS_RUNNING) {
return getDetectorRunSupport().getTrainingsSet();
}
}
/**
* Returns the inference set of this Detector, which will be used by the
* used AbstractInferenceModel to classify in transport modes.
*
* @return List
*/
public List<TrackSegment> getInferenceSet() {
return getDetectorRunSupport().getInferenceSet();
}
/**
* Returns the current used profile
*
* @return A profile instance or null.
*/
public Profile getCurrentActiveProfile() {
return currentActiveProfile;
}
/**
* Sets the new active profile, which will be used with the next execution
* of this Detector.
*
* @param currentActiveProfile
*/
public void setCurrentActiveProfile(Profile currentActiveProfile) {
if (currentActiveProfile != null) {
getDetectorDescriptor().getProfiles().setActiveProfile(currentActiveProfile.getName());
this.currentActiveProfile = currentActiveProfile;
}
}
/**
* Returns the PreProcess Pipeline, which contains all registed
* FilterProcesses that will be used to filter the input data.
*
* @return PreProcessorPipeline.
*/
public PreProcessorPipeline getPreProcessorPipeline() {
return preProcessorPipeline;
}
/**
* Returns the PostProcessor Pipeline, which contains all registed Task that
* will be used after the classification process.
*
* @return postProcessorPipeline
*/
public PostProcessorPipeline getPostProcessorPipeline() {
return postProcessorPipeline;
}
/**
* Returns the underlying AbstractInferenceModel implementation.
*
* @return an AbstractInferenceModel instance.
*/
public AbstractInferenceModel getInferenceModel() {
return inferenceModel;
}
/**
* Returns the current DetectorState of this Detector.
*
* @return ProcessState
*/
public ProcessState getDetectorState() {
return detectorState;
}
/**
* Set the current DetecotState and fires a PropertyChangeEvent if the
* specified detectorState is not null.
*
* @param detectorState a ProcessState instance.
*/
synchronized void setDetectorState(ProcessState detectorState) {
if (detectorState != null) {
Object oldValue = this.detectorState;
this.detectorState = detectorState;
pcs.firePropertyChange(PROP_NAME_DETECTOR_STATE, oldValue, detectorState);
}
}
/**
* Returns the underlying DataObject, which represents the
* DetectorDescriptor xml file.
*
* @return DetectorDataObject
*/
public DetectorDataObject getDataObject() {
return dataObject;
}
/**
* Convience method to access the DetectorDescriptor
*
* @return DetectorDescriptor.
*/
public DetectorDescriptor getDetectorDescriptor() {
DetectorDescriptor detectorDescriptor = null;
try {
detectorDescriptor = dataObject.getDetectorDescriptor();
} catch (JAXBException ex) {
setDetectorState(ProcessState.ERROR);
} catch (IOException ex) {
setDetectorState(ProcessState.ERROR);
}
return detectorDescriptor;
}
/**
*
*/
public void notifyModified() {
dataObject.modifySourceEditor();
}
/**
*
* @param listener
*/
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
/**
*
* @param listener
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
}
/**
* Returns a list of all StatisticProvides from both Pipelines.
*
* @return a list of StatisticProviders
*/
public List<StatisticProvider> getStatistics() {
List<StatisticProvider> statisticProviders = new ArrayList<StatisticProvider>();
for (FilterProcess process : getPreProcessorPipeline().getProcesses()) {
if (process instanceof StatisticProvider) {
statisticProviders.add(((StatisticProvider) process));
}
}
for (Task process : getPostProcessorPipeline().getProcesses()) {
if (process instanceof StatisticProvider) {
statisticProviders.add(((StatisticProvider) process));
}
}
return statisticProviders;
}
@Override
public void updateSource() {
if (dataObject != null) {
dataObject.modifySourceEditor();
}
}
@Override
public Lookup getLookup() {
return lookup;
}
@Override
public String toString() {
return MessageFormat.format("Detector[ name={0}, description={1}{2}]",
getDetectorDescriptor().getName(),
getDetectorDescriptor().getDescription());
}
private class ChangeListenerImpl implements ChangeListener {
public ChangeListenerImpl() {
}
@Override
public void stateChanged(ChangeEvent e) {
reinit();
modelChanged(null);
}
}
}