us.ihmc.scs2.sessionVisualizer.jfx.managers.YoCompositeSearchManager Maven / Gradle / Ivy
package us.ihmc.scs2.sessionVisualizer.jfx.managers;
import com.google.common.base.CaseFormat;
import javafx.application.Platform;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableMap;
import javafx.collections.ObservableSet;
import us.ihmc.log.LogTools;
import us.ihmc.messager.SynchronizeHint;
import us.ihmc.messager.javafx.JavaFXMessager;
import us.ihmc.scs2.session.Session;
import us.ihmc.scs2.sessionVisualizer.jfx.SessionVisualizerIOTools;
import us.ihmc.scs2.sessionVisualizer.jfx.SessionVisualizerTopics;
import us.ihmc.scs2.sessionVisualizer.jfx.charts.ChartGroupModel;
import us.ihmc.scs2.sessionVisualizer.jfx.charts.ChartIdentifier;
import us.ihmc.scs2.sessionVisualizer.jfx.tools.JavaFXMissingTools;
import us.ihmc.scs2.sessionVisualizer.jfx.xml.XMLTools;
import us.ihmc.scs2.sessionVisualizer.jfx.yoComposite.YoComposite;
import us.ihmc.scs2.sessionVisualizer.jfx.yoComposite.YoCompositeCollection;
import us.ihmc.scs2.sessionVisualizer.jfx.yoComposite.YoCompositePattern;
import us.ihmc.scs2.sessionVisualizer.jfx.yoComposite.YoCompositeTools;
import us.ihmc.yoVariables.listener.YoRegistryChangedListener;
import us.ihmc.yoVariables.registry.YoRegistry;
import us.ihmc.yoVariables.variable.YoBoolean;
import us.ihmc.yoVariables.variable.YoDouble;
import us.ihmc.yoVariables.variable.YoInteger;
import us.ihmc.yoVariables.variable.YoLong;
import us.ihmc.yoVariables.variable.YoVariable;
import jakarta.xml.bind.JAXBException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Predicate;
import static us.ihmc.scs2.sessionVisualizer.jfx.yoComposite.YoCompositeTools.*;
public class YoCompositeSearchManager implements Manager
{
public static final YoCompositePattern yoVariablePattern = YoCompositePattern.singleton(YO_VARIABLE);
public static final YoCompositePattern yoBooleanPattern = YoCompositePattern.singleton(YO_BOOLEAN);
public static final YoCompositePattern yoDoublePattern = YoCompositePattern.singleton(YO_DOUBLE);
public static final YoCompositePattern yoIntegerPattern = YoCompositePattern.singleton(YO_INTEGER);
public static final YoCompositePattern yoLongPattern = YoCompositePattern.singleton(YO_LONG);
private final Set primitivePatterns = new LinkedHashSet<>(Arrays.asList(yoVariablePattern,
yoBooleanPattern,
yoDoublePattern,
yoIntegerPattern,
yoLongPattern));
private final Map> primitivePatternToClass = new HashMap<>();
private final Property yoTuple2DCollection = new SimpleObjectProperty<>(this, "yoTuple2DCollectionProperty", null);
private final Property yoTuple3DCollection = new SimpleObjectProperty<>(this, "yoTuple3DCollectionProperty", null);
private final Property yoQuaternionCollection = new SimpleObjectProperty<>(this, "yoQuaternionCollectionProperty", null);
private final Property yoYawPitchRollCollection = new SimpleObjectProperty<>(this, "yoYawPitchRollCollectionProperty", null);
private final List defaultCompositePatterns = new ArrayList<>();
private final ObservableSet yoCompositePatterns = FXCollections.observableSet(new LinkedHashSet<>());
private final ObservableSet customYoCompositePatterns = FXCollections.observableSet(new LinkedHashSet<>());
private final ObservableMap typeToCompositePattern = FXCollections.observableMap(new LinkedHashMap<>());
private final ObservableMap> typeToCompositeCollection = FXCollections.observableMap(new LinkedHashMap<>());
private final Map>> listOfYoCompositeMaps = new HashMap<>();
private final YoRegistryChangedListener rootRegistryChangeListener = change -> refreshYoCompositesInBackground();
private final Property includeSCS2YoVariables;
private final YoManager yoManager;
private final BackgroundExecutorManager backgroundExecutorManager;
/** Using this map as a set to keep track of the ongoing searches. */
private final ConcurrentHashMap activeSearches = new ConcurrentHashMap<>();
private volatile boolean isSessionActive = false;
public YoCompositeSearchManager(JavaFXMessager messager,
SessionVisualizerTopics topics,
YoManager yoManager,
BackgroundExecutorManager backgroundExecutorManager)
{
this.yoManager = yoManager;
this.backgroundExecutorManager = backgroundExecutorManager;
List compositePatterns;
try
{
compositePatterns = XMLTools.loadYoCompositePatterns(SessionVisualizerIOTools.getConfigurationResource(SessionVisualizerIOTools.DEFAULT_YO_COMPOSITE_PATTERNS_FILE));
}
catch (JAXBException | IOException e)
{
throw new RuntimeException("Failed to load the default " + YoCompositePattern.class.getSimpleName() + "s.", e);
}
typeToCompositePattern.addListener((MapChangeListener) change ->
{
if (change.wasAdded())
yoCompositePatterns.add(change.getValueAdded());
if (change.wasRemoved())
yoCompositePatterns.remove(change.getValueRemoved());
});
primitivePatternToClass.put(yoVariablePattern, YoVariable.class);
primitivePatternToClass.put(yoBooleanPattern, YoBoolean.class);
primitivePatternToClass.put(yoDoublePattern, YoDouble.class);
primitivePatternToClass.put(yoIntegerPattern, YoInteger.class);
primitivePatternToClass.put(yoLongPattern, YoLong.class);
for (YoCompositePattern primitivePattern : primitivePatterns)
{
String namePrefix = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, primitivePattern.getType());
Property collection = new SimpleObjectProperty<>(this, namePrefix + "CollectionProperty", null);
typeToCompositeCollection.put(primitivePattern.getType(), collection);
typeToCompositePattern.put(primitivePattern.getType(), primitivePattern);
primitivePattern.getPreferredChartConfigurations().add(new ChartGroupModel("single", Collections.singletonList(new ChartIdentifier(0, 0))));
}
for (YoCompositePattern compositePattern : compositePatterns)
{
typeToCompositePattern.put(compositePattern.getType(), compositePattern);
}
typeToCompositeCollection.put(YO_TUPLE2D, yoTuple2DCollection);
typeToCompositeCollection.put(YO_TUPLE3D, yoTuple3DCollection);
typeToCompositeCollection.put(YO_QUATERNION, yoQuaternionCollection);
typeToCompositeCollection.put(YO_YAW_PITCH_ROLL, yoYawPitchRollCollection);
defaultCompositePatterns.addAll(typeToCompositePattern.values());
typeToCompositePattern.addListener((MapChangeListener) change ->
{
if (change.wasAdded())
customYoCompositePatterns.add(change.getValueAdded());
if (change.wasRemoved())
customYoCompositePatterns.remove(change.getValueRemoved());
});
messager.addTopicListenerBase(topics.getYoCompositePatternLoadRequest(),
m -> loadYoCompositePatternFromFile(m.getMessageContent(), m.getSynchronizeHint()));
messager.addTopicListener(topics.getYoCompositePatternSaveRequest(), this::saveYoCompositePatternToFile);
messager.addTopicListener(topics.getYoCompositeRefreshAll(), m -> refreshYoCompositesInBackground());
includeSCS2YoVariables = messager.createPropertyInput(topics.getShowSCS2YoVariables(), false);
includeSCS2YoVariables.addListener((o, oldValue, newValue) -> refreshYoCompositesInBackground());
}
@Override
public void startSession(Session session)
{
LogTools.info("Searching default YoComposite.");
isSessionActive = true;
typeToCompositePattern.values().forEach(this::searchYoCompositeNow);
yoManager.getRootRegistry().addListener(rootRegistryChangeListener);
LogTools.info("Initialized default YoComposite.");
}
@Override
public void stopSession()
{
isSessionActive = false;
yoManager.getRootRegistry().removeListener(rootRegistryChangeListener);
typeToCompositePattern.entrySet().removeIf(entry -> !defaultCompositePatterns.contains(entry.getValue()));
typeToCompositeCollection.entrySet().removeIf(entry -> !typeToCompositePattern.containsKey(entry.getKey()));
for (YoCompositePattern pattern : defaultCompositePatterns)
typeToCompositeCollection.get(pattern.getType()).setValue(null);
customYoCompositePatterns.clear();
listOfYoCompositeMaps.clear();
}
@Override
public boolean isSessionLoaded()
{
for (YoCompositePattern pattern : defaultCompositePatterns)
{
if (typeToCompositeCollection.get(pattern.getType()).getValue() == null)
return false;
}
return true;
}
public void refreshYoCompositesInBackground()
{
typeToCompositePattern.values().forEach(this::searchYoCompositeInBackground);
}
public void searchYoCompositeInBackground(YoCompositePattern pattern)
{
searchYoCompositeInBackground(pattern, null);
}
public void searchYoCompositeInBackground(YoCompositePattern pattern, Consumer callback)
{
backgroundExecutorManager.queueTaskToExecuteInBackground(this, () -> searchYoCompositeNow(pattern, callback));
}
public void searchYoCompositeNow(YoCompositePattern pattern)
{
searchYoCompositeNow(pattern, null);
}
public void searchYoCompositeNow(YoCompositePattern pattern, Consumer callback)
{
if (activeSearches.containsKey(pattern))
{ // We have an active search, let's reschedule this search.
searchYoCompositeInBackground(pattern, callback);
return;
}
activeSearches.put(pattern, Object.class);
try
{
YoRegistry rootRegistry = yoManager.getRootRegistry();
if (!isSessionActive || rootRegistry == null)
return; // Stopping the session
Predicate registryFilter;
if (includeSCS2YoVariables.getValue())
registryFilter = reg -> true;
else
registryFilter = reg -> !reg.getNamespace().equals(Session.SESSION_INTERNAL_NAMESPACE);
Class extends YoVariable> primitiveClass = primitivePatternToClass.get(pattern);
String type = pattern.getType();
YoCompositeCollection collection;
if (primitiveClass != null)
{
try
{
collection = new YoCompositeCollection(pattern, collectPrimitiveYoComposites(pattern, primitiveClass, rootRegistry, registryFilter));
if (callback != null)
callback.accept(collection);
}
catch (ConcurrentModificationException e)
{
// If we have a concurrent modification, then it means we have another search scheduled waiting. Let's return.
return;
}
catch (Exception e)
{
e.printStackTrace();
return;
}
if (isSessionActive)
JavaFXMissingTools.runLaterIfNeeded(getClass(), () -> typeToCompositeCollection.get(type).setValue(collection));
}
else
{
List result;
try
{
result = searchYoComposites(pattern, rootRegistry, registryFilter);
}
catch (ConcurrentModificationException e)
{
// If we have a concurrent modification, then it means we have another search scheduled waiting. Let's return.
return;
}
catch (Exception e)
{
if (isSessionActive)
e.printStackTrace();
return;
}
if (result != null)
{
collection = new YoCompositeCollection(pattern, result);
if (isSessionActive)
{
JavaFXMissingTools.runLaterIfNeeded(getClass(), () ->
{
Property property = typeToCompositeCollection.get(type);
if (property == null)
{
String propertyName = type + "CollectionProperty";
property = new SimpleObjectProperty<>(this, propertyName, null);
typeToCompositeCollection.put(type, property);
typeToCompositePattern.put(type, pattern);
}
property.setValue(collection);
});
if (callback != null)
callback.accept(collection);
}
}
}
}
finally
{
activeSearches.remove(pattern);
}
}
private static List collectPrimitiveYoComposites(YoCompositePattern pattern,
Class extends YoVariable> primitiveClass,
YoRegistry start,
Predicate registryFilter)
{
return collectPrimitiveYoComposites(pattern, primitiveClass, start, registryFilter, new ArrayList<>());
}
private static List collectPrimitiveYoComposites(YoCompositePattern pattern,
Class extends YoVariable> primitiveClass,
YoRegistry start,
Predicate registryFilter,
List compositesToPack)
{
if (registryFilter.test(start))
{
for (YoVariable variable : start.getVariables())
{
if (primitiveClass.isInstance(variable))
compositesToPack.add(new YoComposite(pattern, variable));
}
for (YoRegistry child : start.getChildren())
collectPrimitiveYoComposites(pattern, primitiveClass, child, registryFilter, compositesToPack);
}
return compositesToPack;
}
public void discardYoComposite(String typeToDiscard)
{
discardYoComposite(getPatternFromType(typeToDiscard));
}
public void discardYoComposite(YoCompositePattern patternToDiscard)
{
if (patternToDiscard == null || !customYoCompositePatterns.contains(patternToDiscard))
return;
typeToCompositePattern.remove(patternToDiscard.getType());
Property collectionProperty = typeToCompositeCollection.remove(patternToDiscard.getType());
if (collectionProperty != null)
collectionProperty.setValue(null);
listOfYoCompositeMaps.remove(patternToDiscard);
}
private void loadYoCompositePatternFromFile(File file, SynchronizeHint hint)
{
if (file == null)
return;
LogTools.info("Loading file: " + file);
try
{
List newPatterns = XMLTools.loadYoCompositePatterns(new FileInputStream(file));
if (hint == SynchronizeHint.SYNCHRONOUS)
newPatterns.forEach(this::searchYoCompositeNow);
else
newPatterns.forEach(this::searchYoCompositeInBackground);
}
catch (IOException | JAXBException e)
{
e.printStackTrace();
}
}
public void saveYoCompositePatternToFile(File file)
{
if (!Platform.isFxApplicationThread())
throw new IllegalStateException("Save must only be used from the FX Application Thread");
if (file == null)
return;
LogTools.info("Saving file: " + file);
ArrayList patternsToExport = new ArrayList<>(typeToCompositePattern.values());
patternsToExport.removeAll(defaultCompositePatterns);
try
{
XMLTools.saveYoCompositePatterns(new FileOutputStream(file), patternsToExport);
}
catch (IOException | JAXBException e)
{
e.printStackTrace();
}
}
public void requestSearchListOfYoComposites(String compositeType, Consumer
© 2015 - 2025 Weber Informatics LLC | Privacy Policy