+++ /dev/null
-package net.mograsim.logic.model.am2900;
-
-import static org.junit.jupiter.api.Assertions.fail;
-
-import java.lang.reflect.Field;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.Objects;
-import java.util.Queue;
-import java.util.Set;
-import java.util.TreeSet;
-
-import net.mograsim.logic.core.components.BitDisplay;
-import net.mograsim.logic.core.components.ManualSwitch;
-import net.mograsim.logic.core.timeline.Timeline;
-import net.mograsim.logic.model.am2900.TestableCircuit.Result;
-import net.mograsim.logic.model.model.ViewModel;
-import net.mograsim.logic.model.model.ViewModelModifiable;
-import net.mograsim.logic.model.model.components.GUIComponent;
-import net.mograsim.logic.model.model.components.atomic.GUIBitDisplay;
-import net.mograsim.logic.model.model.components.atomic.GUIManualSwitch;
-import net.mograsim.logic.model.model.components.submodels.SubmodelComponent;
-import net.mograsim.logic.model.model.wires.GUIWire;
-import net.mograsim.logic.model.model.wires.Pin;
-import net.mograsim.logic.model.modeladapter.LogicModelParameters;
-import net.mograsim.logic.model.modeladapter.ViewLogicModelAdapter;
-import net.mograsim.logic.model.serializing.IndirectGUIComponentCreator;
-import net.mograsim.logic.model.util.ModellingTool;
-
-public class TestEnvironmentHelper
-{
- private final TestableCircuit testEnvInstance;
- private final Class<?> testEnvClass;
- private final String modelId;
- private Field componentField;
- private Field timelineField;
-
- private GUIComponent component;
- private Timeline timeline;
- private ViewModelModifiable viewModel;
- private ModellingTool modellingTool;
- private HashMap<String, GUIManualSwitch> idSwitchMap = new HashMap<>();
- private HashMap<String, GUIBitDisplay> idDisplayMap = new HashMap<>();
-
- private DebugState debug = DebugState.NO_DEBUG;
- private Set<String> wireDebugChangeSet;
- private boolean debugWires = false;
- public int debugEventThreshold = 10_000;
- public int debugEventCount = 500;
- private int eventCounter;
-
- public TestEnvironmentHelper(TestableCircuit testEnv, String modelId)
- {
- this.testEnvInstance = testEnv;
- this.modelId = modelId;
- this.testEnvClass = testEnvInstance.getClass();
- for (Field f : testEnvClass.getDeclaredFields())
- {
- if (GUIComponent.class.isAssignableFrom(f.getType()))
- {
- componentField = f;
- componentField.setAccessible(true);
- } else if (Timeline.class.isAssignableFrom(f.getType()))
- {
- timelineField = f;
- timelineField.setAccessible(true);
- }
- }
- if (componentField == null || timelineField == null)
- throw new IllegalStateException("No component or timeline field found!");
- }
-
- public void setup(DebugState debug)
- {
- this.debug = debug;
- // Create view model
- viewModel = new ViewModelModifiable();
- modellingTool = ModellingTool.createFor(viewModel);
- component = IndirectGUIComponentCreator.createComponent(viewModel, modelId);
- setField(componentField, component);
-
- component.getPins().values().forEach(this::extendModelPin);
-
- // Create logic model
- LogicModelParameters params = new LogicModelParameters();
- params.gateProcessTime = 50;
- params.wireTravelTime = 10;
- timeline = ViewLogicModelAdapter.convert(viewModel, params);
- setField(timelineField, timeline);
-
- // Bind switches/displays to this test class
- component.getPins().values().forEach(this::bindModelPin);
-
- if (debug == DebugState.DEBUG_AT_PERFORMANCE_COST)
- {
- setupDebugging();
- }
- timeline.addEventAddedListener(te -> eventCounter++);
- }
-
- private void extendModelPin(Pin p)
- {
- String javaIdentId = idToJavaIdentifier(p.name);
- try
- {
- Field f = testEnvClass.getDeclaredField(javaIdentId);
- Class<?> type = f.getType();
- if (ManualSwitch.class.isAssignableFrom(type))
- {
- GUIManualSwitch gms = new GUIManualSwitch(viewModel, p.logicWidth);
- modellingTool.connect(p, gms.getOutputPin());
- idSwitchMap.put(p.name, gms);
- } else if (BitDisplay.class.isAssignableFrom(type))
- {
- GUIBitDisplay gbd = new GUIBitDisplay(viewModel, p.logicWidth);
- modellingTool.connect(p, gbd.getInputPin());
- idDisplayMap.put(p.name, gbd);
- } else if (SwitchWithDisplay.class.isAssignableFrom(type))
- {
- SwitchWithDisplay swd = new SwitchWithDisplay(viewModel, p);
- setField(f, swd);
- } else
- {
- fail("unkown field type " + type);
- }
- }
- catch (NoSuchFieldException | SecurityException e)
- {
- fail(e);
- }
- }
-
- private void bindModelPin(Pin p)
- {
- String javaIdentId = idToJavaIdentifier(p.name);
- if (idDisplayMap.containsKey(p.name))
- setField(javaIdentId, idDisplayMap.get(p.name).getBitDisplay());
- if (idSwitchMap.containsKey(p.name))
- setField(javaIdentId, idSwitchMap.get(p.name).getManualSwitch());
- }
-
- private void setupDebugging()
- {
- // Debug code
- HashSet<GUIWire> wiresIncludingSubmodels = new HashSet<>();
- Queue<ViewModel> modelsToIterate = new LinkedList<>();
- modelsToIterate.add(viewModel);
- while (modelsToIterate.size() > 0)
- {
- ViewModel model = modelsToIterate.poll();
- wiresIncludingSubmodels.addAll(model.getWiresByName().values());
- for (GUIComponent comp : model.getComponentsByName().values())
- if (comp instanceof SubmodelComponent)
- modelsToIterate.offer(((SubmodelComponent) comp).submodel);
- }
- System.out.println(wiresIncludingSubmodels.size());
- viewModel.setRedrawHandler(() -> wiresIncludingSubmodels.forEach(w ->
- {
- if (debugWires)
- {
- wireDebugChangeSet.add(w.toString());
- }
- }));
- }
-
- public Result run()
- {
- // Normal execution until completion or eventLimit
- int eventLimit = debugEventThreshold;
- eventCounter = 0;
- debugWires = false;
- while (eventCounter < eventLimit)
- {
- timeline.executeNext();
- if (!timeline.hasNext())
- return Result.SUCCESS;
- }
-
- // Start debugging if event limit is reached (if debug is active)
- if (debug == DebugState.DEBUG_AT_PERFORMANCE_COST)
- return debugThisRun();
-
- return Result.OUT_OF_TIME;
- }
-
- private Result debugThisRun()
- {
- int eventLimit = debugEventThreshold;
- debugWires = true;
- wireDebugChangeSet = new TreeSet<>();
- Set<String> oldChangeSet;
- // observe wire changes to detect, if we are really stuck in an endless loop
- do
- {
- eventLimit += debugEventCount;
- oldChangeSet = wireDebugChangeSet;
- wireDebugChangeSet = new TreeSet<>();
- while (eventCounter < eventLimit)
- {
- timeline.executeNext();
- if (!timeline.hasNext())
- {
- // no endless loop, but more events needed than expected
- System.out.println("run() took longer than expected: " + eventCounter);
- return Result.SUCCESS;
- }
- }
- } while (!oldChangeSet.equals(wireDebugChangeSet));
- // if stuck, abort execution and print wires
- System.err.print("Problematic Wire updates:");
- wireDebugChangeSet.forEach(System.out::println);
- System.err.println("run() failed: " + eventCounter);
- return Result.OUT_OF_TIME;
- }
-
- private static String idToJavaIdentifier(String s)
- {
- StringBuilder sb = new StringBuilder(s.length());
- char c = s.charAt(0);
- sb.append(Character.isJavaIdentifierStart(c) ? c : '_');
- for (int i = 1; i < s.length(); i++)
- sb.append(Character.isJavaIdentifierPart(c = s.charAt(i)) ? c : '_');
- return sb.toString();
- }
-
- private <S> void setField(Field f, S value)
- {
- try
- {
- f.set(testEnvInstance, Objects.requireNonNull(value));
- }
- catch (Exception e)
- {
- fail(e);
- }
- }
-
- private <S> void setField(String name, S value)
- {
- try
- {
- Field f = testEnvClass.getDeclaredField(name);
- f.setAccessible(true);
- f.set(testEnvInstance, Objects.requireNonNull(value));
- }
- catch (Exception e)
- {
- fail(e);
- }
- }
-
- public enum DebugState
- {
- NO_DEBUG, DEBUG_AT_PERFORMANCE_COST;
- }
-}