From dd0a6aa8f76740bedc81d770d6ddc2dc5eddaa22 Mon Sep 17 00:00:00 2001 From: kotik-coder Date: Wed, 11 Jan 2023 12:44:35 +0300 Subject: [PATCH] General usability changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fixed sample name parsing in Netzsch files - Made some changes to the text displayed in the GUI - Tooltips now more informative and well-formatted - Added missing SwingUtilities.invokeLater(…) method when updating graphical logs - Added safety checks for graphical logging - Added ‘finished’ boolean in the Log class, which helps graphical logs to be displayed correctly - Migrated to JfreeChart 1.5.4, which fixes issues with graphs in resized windows - Switched from using ExecutionServices and ForkJoinPools to using CompletableFutures. Removed redundant blocks where concurrency has been poorly handled. - Removed argument from checkProblems() in SearchTask - Fixed execution of queued tasks hanging because of conflicting ForkJoinPools - Fixed tracker dialogs not closing on end of scheme selection - Fixed focus lost problem in ProblemStatementFrame --- pom.xml | 4 +- src/main/java/pulse/input/Metadata.java | 4 +- .../pulse/io/readers/NetzschCSVReader.java | 6 + src/main/java/pulse/problem/schemes/Grid.java | 12 +- .../java/pulse/problem/schemes/Grid2D.java | 5 - .../java/pulse/problem/statements/Pulse.java | 10 +- .../pulse/problem/statements/Pulse2D.java | 3 +- .../statements/model/AbsorptionModel.java | 5 +- .../java/pulse/properties/SampleName.java | 34 +++-- .../pulse/search/direction/LMOptimiser.java | 2 +- src/main/java/pulse/tasks/Calculation.java | 70 ++++++---- src/main/java/pulse/tasks/SearchTask.java | 117 ++++++++-------- src/main/java/pulse/tasks/TaskManager.java | 65 ++++----- src/main/java/pulse/tasks/logs/Log.java | 7 + .../pulse/ui/components/GraphicalLogPane.java | 24 ++-- .../java/pulse/ui/components/LogChart.java | 10 +- .../pulse/ui/components/TaskPopupMenu.java | 4 +- .../components/buttons/ExecutionButton.java | 2 +- .../controllers/AccessibleTableRenderer.java | 44 +++--- .../ui/components/panels/ProblemToolbar.java | 2 +- src/main/java/pulse/ui/frames/LogFrame.java | 5 +- .../ui/frames/ProblemStatementFrame.java | 132 ++++++++---------- .../pulse/ui/frames/SearchOptionsFrame.java | 2 +- src/main/java/pulse/util/Accessible.java | 19 +-- .../java/pulse/util/InstanceDescriptor.java | 5 +- src/main/resources/NumericProperty.xml | 6 +- 26 files changed, 293 insertions(+), 306 deletions(-) diff --git a/pom.xml b/pom.xml index 9605d72..f7140a2 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ kotik-coder PULsE - 1.97b + 1.97c PULsE Processing Unit for Laser flash Experiments @@ -23,7 +23,7 @@ org.jfree jfreechart - 1.5.0 + 1.5.4 com.formdev diff --git a/src/main/java/pulse/input/Metadata.java b/src/main/java/pulse/input/Metadata.java index cd9369c..ae00b92 100644 --- a/src/main/java/pulse/input/Metadata.java +++ b/src/main/java/pulse/input/Metadata.java @@ -61,7 +61,7 @@ public class Metadata extends PropertyHolder implements Reflexive { * experimental setup. */ public Metadata(NumericProperty temperature, int externalId) { - sampleName = new SampleName(); + sampleName = new SampleName(null); setExternalID(externalId); pulseDescriptor.setSelectedDescriptor(RectangularPulse.class.getSimpleName()); data = new TreeSet<>(); @@ -186,7 +186,7 @@ public void set(NumericPropertyKeyword type, NumericProperty property) { @Override public List listedTypes() { List list = super.listedTypes(); - list.add(new SampleName()); + list.add(new SampleName("")); list.add(pulseDescriptor); return list; } diff --git a/src/main/java/pulse/io/readers/NetzschCSVReader.java b/src/main/java/pulse/io/readers/NetzschCSVReader.java index 85c4f1c..d9837f1 100644 --- a/src/main/java/pulse/io/readers/NetzschCSVReader.java +++ b/src/main/java/pulse/io/readers/NetzschCSVReader.java @@ -23,6 +23,7 @@ import pulse.input.Metadata; import pulse.input.Range; import pulse.properties.NumericPropertyKeyword; +import pulse.properties.SampleName; import pulse.ui.Messages; /** @@ -49,6 +50,7 @@ public class NetzschCSVReader implements CurveReader { private final static String DIAMETER = "Diameter"; private final static String L_PULSE_WIDTH = "Laser_pulse_width"; private final static String PULSE_WIDTH = "Pulse_width"; + private final static String MATERIAL = "Material"; /** * Note comma is included as a delimiter character here. @@ -115,6 +117,9 @@ public List read(File file) throws IOException { int shotId = determineShotID(reader, file); + String name = findLineByLabel(reader, MATERIAL, DETECTOR_SPOT_SIZE, true) + .substring(MATERIAL.length() + 1) + .replaceAll(delims, ""); String spot = findLineByLabel(reader, DETECTOR_SPOT_SIZE, THICKNESS, false); double spotSize = 0; @@ -164,6 +169,7 @@ public List read(File file) throws IOException { if (pulseWidth > 1e-10) { met.set(NumericPropertyKeyword.PULSE_WIDTH, derive(NumericPropertyKeyword.PULSE_WIDTH, pulseWidth)); } + met.setSampleName(new SampleName(name)); met.set(NumericPropertyKeyword.THICKNESS, derive(NumericPropertyKeyword.THICKNESS, thickness)); met.set(NumericPropertyKeyword.DIAMETER, derive(NumericPropertyKeyword.DIAMETER, diameter)); met.set(NumericPropertyKeyword.FOV_OUTER, derive(NumericPropertyKeyword.FOV_OUTER, spotSize != 0 ? spotSize : 0.85 * diameter)); diff --git a/src/main/java/pulse/problem/schemes/Grid.java b/src/main/java/pulse/problem/schemes/Grid.java index 2d7128d..7bbfc9a 100644 --- a/src/main/java/pulse/problem/schemes/Grid.java +++ b/src/main/java/pulse/problem/schemes/Grid.java @@ -211,15 +211,9 @@ public final double gridAxialDistance(double distance, double lengthFactor) { @Override public String toString() { - var sb = new StringBuilder(); - sb.append(""). - append(getClass().getSimpleName()) - .append(": hx=") - .append(format("%3.2e", hx)) - .append("; "). - append("τ=") - .append(format("%3.2e", tau)) - .append("; "); + var sb = new StringBuilder("Grid"); + sb.append(String.format("%n %-25s", this.getGridDensity())); + sb.append(String.format("%n %-25s", this.getTimeFactor())); return sb.toString(); } diff --git a/src/main/java/pulse/problem/schemes/Grid2D.java b/src/main/java/pulse/problem/schemes/Grid2D.java index af2284b..e35a6e3 100644 --- a/src/main/java/pulse/problem/schemes/Grid2D.java +++ b/src/main/java/pulse/problem/schemes/Grid2D.java @@ -99,11 +99,6 @@ public double gridRadialDistance(double radial, double lengthFactor) { return rint((radial / lengthFactor) / hy) * hy; } - @Override - public String toString() { - return super.toString() + "hy=" + format("%3.3f", hy); - } - public double getYStep() { return hy; } diff --git a/src/main/java/pulse/problem/statements/Pulse.java b/src/main/java/pulse/problem/statements/Pulse.java index eac31f9..2b5b988 100644 --- a/src/main/java/pulse/problem/statements/Pulse.java +++ b/src/main/java/pulse/problem/statements/Pulse.java @@ -163,12 +163,10 @@ public void setLaserEnergy(NumericProperty laserEnergy) { @Override public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(getPulseShape()); - sb.append(" ; "); - sb.append(getPulseWidth()); - sb.append(" ; "); - sb.append(getLaserEnergy()); + StringBuilder sb = new StringBuilder("Pulse:"); + sb.append(String.format("%n %-25s", getPulseShape())); + sb.append(String.format("%n %-25s", getPulseWidth())); + sb.append(String.format("%n %-25s", getLaserEnergy())); return sb.toString(); } diff --git a/src/main/java/pulse/problem/statements/Pulse2D.java b/src/main/java/pulse/problem/statements/Pulse2D.java index 16895b6..11d4bef 100644 --- a/src/main/java/pulse/problem/statements/Pulse2D.java +++ b/src/main/java/pulse/problem/statements/Pulse2D.java @@ -60,8 +60,7 @@ public void setSpotDiameter(NumericProperty spotDiameter) { @Override public String toString() { StringBuilder sb = new StringBuilder(super.toString()); - sb.append(" ; "); - sb.append(getSpotDiameter()); + sb.append(String.format("%n %-25s", getSpotDiameter())); return sb.toString(); } diff --git a/src/main/java/pulse/problem/statements/model/AbsorptionModel.java b/src/main/java/pulse/problem/statements/model/AbsorptionModel.java index 9165b08..aacbd12 100644 --- a/src/main/java/pulse/problem/statements/model/AbsorptionModel.java +++ b/src/main/java/pulse/problem/statements/model/AbsorptionModel.java @@ -95,7 +95,10 @@ public void set(NumericPropertyKeyword type, NumericProperty property) { @Override public String toString() { - return getClass().getSimpleName() + " : " + absorptionMap.get(LASER) + " ; " + absorptionMap.get(THERMAL); + var sb = new StringBuilder(getSimpleName()); + sb.append(String.format("%n %-25s", absorptionMap.get(LASER))); + sb.append(String.format("%n %-25s", absorptionMap.get(THERMAL))); + return sb.toString(); } @Override diff --git a/src/main/java/pulse/properties/SampleName.java b/src/main/java/pulse/properties/SampleName.java index 5ef7419..2f9f6cc 100644 --- a/src/main/java/pulse/properties/SampleName.java +++ b/src/main/java/pulse/properties/SampleName.java @@ -6,8 +6,8 @@ public class SampleName implements Property { private String name; - public SampleName() { - //null name + public SampleName(String name) { + this.name = name; } @Override @@ -41,22 +41,28 @@ public String toString() { } @Override - public boolean equals(Object o) { - if (o == this) { + public int hashCode() { + int hash = 5; + hash = 43 * hash + Objects.hashCode(this.name); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { return true; } - - if (o == null) { + if (obj == null) { return false; } - - boolean result = false; - - if (o instanceof SampleName) { - result = name.equals(((SampleName) o).getValue()); + if (getClass() != obj.getClass()) { + return false; } - - return result; + final SampleName other = (SampleName) obj; + if (!Objects.equals(this.name, other.name)) { + return false; + } + return true; } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/search/direction/LMOptimiser.java b/src/main/java/pulse/search/direction/LMOptimiser.java index 86ee9e4..d3b22c7 100644 --- a/src/main/java/pulse/search/direction/LMOptimiser.java +++ b/src/main/java/pulse/search/direction/LMOptimiser.java @@ -43,7 +43,7 @@ public class LMOptimiser extends GradientBasedOptimiser { * Up to {@value MAX_FAILED_ATTEMPTS} failed attempts are allowed. */ - public final static int MAX_FAILED_ATTEMPTS = 4; + public final static int MAX_FAILED_ATTEMPTS = 5; private LMOptimiser() { super(); diff --git a/src/main/java/pulse/tasks/Calculation.java b/src/main/java/pulse/tasks/Calculation.java index 2e38a79..40d857d 100644 --- a/src/main/java/pulse/tasks/Calculation.java +++ b/src/main/java/pulse/tasks/Calculation.java @@ -79,7 +79,7 @@ public Calculation(Calculation c) { } instanceDescriptor.addListener(() -> initModelCriterion(rs)); } - + public void conformTo(UpwardsNavigable owner) { problem.setParent(owner); scheme.setParent(owner); @@ -87,13 +87,13 @@ public void conformTo(UpwardsNavigable owner) { os.setParent(owner); result.setParent(owner); } - + public void clear() { this.status = INCOMPLETE; this.problem = null; this.scheme = null; } - + /** *

* After setting and adopting the {@code problem} by this @@ -201,31 +201,40 @@ public Status getStatus() { */ public boolean setStatus(Status status) { - boolean changeStatus = true; - - switch (this.status) { - case QUEUED: - case IN_PROGRESS: - switch (status) { - case QUEUED: - case READY: - case INCOMPLETE: - changeStatus = false; - break; - default: - } - break; - case FAILED: - case EXECUTION_ERROR: - case INCOMPLETE: - //if the TaskManager attempts to run this calculation - changeStatus = status != Status.QUEUED; - break; - default: - } + boolean changeStatus = false; + + if (this.getStatus() != status) { + + changeStatus = true; + + //current status is given by ** this.status ** + //new status is the ** argument ** of this method + switch (this.status) { + case QUEUED: + //do not change status to queued, ready or incomplete if already in progress + case IN_PROGRESS: + switch (status) { + case QUEUED: + case READY: + case INCOMPLETE: + changeStatus = false; + break; + default: + } + break; + case FAILED: + case EXECUTION_ERROR: + case INCOMPLETE: + //if the TaskManager attempts to run this calculation + changeStatus = status != Status.QUEUED; + break; + default: + } + + if (changeStatus) { + this.status = status; + } - if (changeStatus) { - this.status = status; } return changeStatus; @@ -350,14 +359,14 @@ public void setResult(Result result) { public double evaluate(double t) { return problem.getHeatingCurve().interpolateSignalAt(t); } - + @Override public Segment accessibleRange() { var hc = problem.getHeatingCurve(); return new Segment(hc.timeAt(0), hc.timeLimit()); } - /** + /** * This will use the current {@code DifferenceScheme} to solve the * {@code Problem} for this {@code SearchTask} and calculate the SSR value * showing how well (or bad) the calculated solution describes the @@ -367,7 +376,6 @@ public Segment accessibleRange() { * @return the value of SSR (sum of squared residuals). * @throws pulse.problem.schemes.solvers.SolverException */ - @Override public double objectiveFunction(GeneralTask task) throws SolverException { process(); @@ -408,4 +416,4 @@ public boolean equals(Object obj) { return true; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/tasks/SearchTask.java b/src/main/java/pulse/tasks/SearchTask.java index 91633c8..9cf3e22 100644 --- a/src/main/java/pulse/tasks/SearchTask.java +++ b/src/main/java/pulse/tasks/SearchTask.java @@ -103,7 +103,7 @@ public SearchTask(ExperimentalData curve) { clear(); addListeners(); } - + private void addListeners() { InterpolationDataset.addListener(e -> { if (current.getProblem() != null) { @@ -153,13 +153,13 @@ public void clear() { //this.path = null; current.clear(); - this.checkProblems(true); + this.checkProblems(); } - + public List alteredParameters() { - return activeParameters().stream().map(key -> - this.numericProperty(key)).collect(Collectors.toList()); - } + return activeParameters().stream().map(key + -> this.numericProperty(key)).collect(Collectors.toList()); + } public void addTaskListener(DataCollectionListener toAdd) { listeners.add(toAdd); @@ -181,7 +181,7 @@ public void removeStatusChangeListeners() { public String toString() { return getIdentifier().toString(); } - + /** * Adopts the {@code curve} by this {@code SearchTask}. * @@ -209,39 +209,37 @@ public void setExperimentalCurve(ExperimentalData curve) { * using the {@code status.getDetails()} method. *

* - * @param updateStatus */ - public void checkProblems(boolean updateStatus) { + public void checkProblems() { var status = getStatus(); - if (status == DONE) { - return; - } - - var pathSolver = getInstance(); - var s = INCOMPLETE; - - if (current.getProblem() == null) { - s.setDetails(MISSING_PROBLEM_STATEMENT); - } else if (!current.getProblem().isReady()) { - s.setDetails(INSUFFICIENT_DATA_IN_PROBLEM_STATEMENT); - } else if (current.getScheme() == null) { - s.setDetails(MISSING_DIFFERENCE_SCHEME); - } else if (curve == null) { - s.setDetails(MISSING_HEATING_CURVE); - } else if (pathSolver == null) { - s.setDetails(MISSING_OPTIMISER); - } else if (getBuffer() == null) { - s.setDetails(MISSING_BUFFER); - } else if (!getInstance().compatibleWith(current.getOptimiserStatistic())) { - s.setDetails(INCOMPATIBLE_OPTIMISER); - } else { - s = READY; - } + if (status != DONE) { + + var pathSolver = getInstance(); + var s = INCOMPLETE; + + if (current.getProblem() == null) { + s.setDetails(MISSING_PROBLEM_STATEMENT); + } else if (!current.getProblem().isReady()) { + s.setDetails(INSUFFICIENT_DATA_IN_PROBLEM_STATEMENT); + } else if (current.getScheme() == null) { + s.setDetails(MISSING_DIFFERENCE_SCHEME); + } else if (curve == null) { + s.setDetails(MISSING_HEATING_CURVE); + } else if (pathSolver == null) { + s.setDetails(MISSING_OPTIMISER); + } else if (getBuffer() == null) { + s.setDetails(MISSING_BUFFER); + } else if (!getInstance().compatibleWith(current.getOptimiserStatistic())) { + s.setDetails(INCOMPATIBLE_OPTIMISER); + } else { + s = READY; + } - if (updateStatus) { setStatus(s); + } + } public Identifier getIdentifier() { @@ -263,12 +261,12 @@ private void notifyStatusListeners(StateEntry e) { l.onStatusChange(e); } } - + @Override public void run() { correlationBuffer.clear(); current.setResult(null); - + /* check of status */ switch (getStatus()) { case READY: @@ -278,12 +276,12 @@ public void run() { default: return; } - + current.getProblem().parameterListChanged(); // get updated list of parameters super.run(); } - + /** * If the current task is either {@code IN_PROGRESS}, {@code QUEUED}, or * {@code READY}, terminates it by setting its status to {@code TERMINATED}. @@ -321,15 +319,15 @@ public CorrelationBuffer getCorrelationBuffer() { public CorrelationTest getCorrelationTest() { return correlationTest; } - + public List getStoredCalculations() { return this.stored; - } + } public void storeCalculation() { var copy = new Calculation(current); stored.add(copy); - } + } public void switchTo(Calculation calc) { current.setParent(null); @@ -366,19 +364,19 @@ private void fireRepositoryEvent(TaskRepositoryEvent e) { l.onTaskListChanged(e); } } - + @Override public boolean isInProgress() { return getStatus() == IN_PROGRESS; } - + @Override public void intermediateProcessing() { correlationBuffer.inflate(this); notifyDataListeners(new DataLogEntry(this)); } - - @Override + + @Override public void onSolverException(SolverException e) { setStatus(Status.troubleshoot(e)); } @@ -394,8 +392,8 @@ public void onSolverException(SolverException e) { */ @Override public ParameterVector searchVector() { - var ids = activeParameters().stream().map(id -> - new ParameterIdentifier(id)).collect(Collectors.toList()); + var ids = activeParameters().stream().map(id + -> new ParameterIdentifier(id)).collect(Collectors.toList()); var optimisationVector = new ParameterVector(ids); current.getProblem().optimisationVector(optimisationVector); @@ -455,8 +453,7 @@ public void postProcessing() { } } - - + /** * Finds what properties are being altered in the search of this SearchTask. * @@ -467,16 +464,14 @@ public void postProcessing() { public List activeParameters() { var flags = ActiveFlags.getAllFlags(); //problem dependent - var allActiveParams = ActiveFlags.selectActiveAndListed - (flags, current.getProblem()); + var allActiveParams = ActiveFlags.selectActiveAndListed(flags, current.getProblem()); //problem independent (lower/upper bound) - var listed = ActiveFlags.selectActiveAndListed - (flags, curve.getRange() ); - allActiveParams.addAll(listed); + var listed = ActiveFlags.selectActiveAndListed(flags, curve.getRange()); + allActiveParams.addAll(listed); return allActiveParams; } - - /** + + /** * Will return {@code true} if status could be updated. * * @param status the status of the task @@ -485,20 +480,18 @@ public List activeParameters() { * be updated at this time. * @see Calculation.setStatus() */ - public boolean setStatus(Status status) { Objects.requireNonNull(status); Status oldStatus = getStatus(); - boolean changed = current.setStatus(status) - && (oldStatus != getStatus()); + boolean changed = current.setStatus(status) && oldStatus != status; if (changed) { notifyStatusListeners(new StateEntry(this, status)); } return changed; } - + public Status getStatus() { return current.getStatus(); } @@ -525,7 +518,7 @@ public boolean equals(Object o) { return curve.equals(((SearchTask) o).curve); } - + @Override public String describe() { @@ -553,4 +546,4 @@ public Calculation getResponse() { return current; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/tasks/TaskManager.java b/src/main/java/pulse/tasks/TaskManager.java index 358403d..397971d 100644 --- a/src/main/java/pulse/tasks/TaskManager.java +++ b/src/main/java/pulse/tasks/TaskManager.java @@ -24,7 +24,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executors; -import java.util.concurrent.ForkJoinPool; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; @@ -66,8 +65,6 @@ public class TaskManager extends UpwardsNavigable { private boolean singleStatement = true; - private final ForkJoinPool taskPool; - private final List selectionListeners; private final List taskRepositoryListeners; @@ -91,7 +88,6 @@ public class TaskManager extends UpwardsNavigable { private TaskManager() { tasks = new ArrayList<>(); - taskPool = new ForkJoinPool(ResourceMonitor.getInstance().getThreadsAvailable()); selectionListeners = new CopyOnWriteArrayList<>(); taskRepositoryListeners = new CopyOnWriteArrayList<>(); addHierarchyListener(statementListener); @@ -115,40 +111,41 @@ public static TaskManager getManagerInstance() { * @param t a {@code SearchTask} that will be executed */ public void execute(SearchTask t) { - t.checkProblems(t.getStatus() != Status.DONE); + t.checkProblems(); - //try to start cmputation + // try to start computation // notify listeners computation is about to start - if (!t.setStatus(QUEUED)) { + if (t.getStatus() != QUEUED && !t.setStatus(QUEUED)) { return; } - + // notify listeners calculation started notifyListeners(new TaskRepositoryEvent(TASK_SUBMITTED, t.getIdentifier())); - + // run task t -- after task completed, write result and trigger listeners CompletableFuture.runAsync(t).thenRun(() -> { - Calculation current = (Calculation)t.getResponse(); + Calculation current = (Calculation) t.getResponse(); var e = new TaskRepositoryEvent(TASK_FINISHED, t.getIdentifier()); if (null == current.getStatus()) { notifyListeners(e); - } - else switch (current.getStatus()) { - case DONE: - current.setResult(new Result(t, ResultFormat.getInstance())); - //notify listeners before the task is re-assigned - notifyListeners(e); - t.storeCalculation(); - break; - case AWAITING_TERMINATION: - t.setStatus(Status.TERMINATED); - break; - default: - notifyListeners(e); - break; + } else { + switch (current.getStatus()) { + case DONE: + current.setResult(new Result(t, ResultFormat.getInstance())); + //notify listeners before the task is re-assigned + notifyListeners(e); + t.storeCalculation(); + break; + case AWAITING_TERMINATION: + t.setStatus(Status.TERMINATED); + break; + default: + notifyListeners(e); + break; + } } }); - + } /** @@ -170,7 +167,7 @@ public void notifyListeners(TaskRepositoryEvent e) { */ public void executeAll() { - var queue = tasks.stream().filter(t -> { + tasks.stream().filter(t -> { switch (t.getStatus()) { case IN_PROGRESS: case EXECUTION_ERROR: @@ -178,11 +175,9 @@ public void executeAll() { default: return true; } - }).collect(toList()); - - for (SearchTask t : queue) { - taskPool.submit(() -> execute(t)); - } + }).forEach(t -> { + execute(t); + }); } @@ -259,7 +254,7 @@ public SampleName getSampleName() { return null; } - return ( (ExperimentalData) optional.get().getInput() ) + return ((ExperimentalData) optional.get().getInput()) .getMetadata().getSampleName(); } @@ -306,7 +301,7 @@ public SearchTask getTask(Identifier id) { */ public SearchTask getTask(int externalId) { var o = tasks.stream().filter(t - -> Integer.compare( ( (ExperimentalData) t.getInput()) + -> Integer.compare(((ExperimentalData) t.getInput()) .getMetadata().getExternalID(), externalId) == 0).findFirst(); return o.isPresent() ? o.get() : null; @@ -510,8 +505,8 @@ public String describe() { public void evaluate() { tasks.stream().forEach(t -> { - var properties = ( (Calculation) t.getResponse() ).getProblem().getProperties(); - var c = (ExperimentalData)t.getInput(); + var properties = ((Calculation) t.getResponse()).getProblem().getProperties(); + var c = (ExperimentalData) t.getInput(); properties.useTheoreticalEstimates(c); }); } diff --git a/src/main/java/pulse/tasks/logs/Log.java b/src/main/java/pulse/tasks/logs/Log.java index 21a32df..b4c7436 100644 --- a/src/main/java/pulse/tasks/logs/Log.java +++ b/src/main/java/pulse/tasks/logs/Log.java @@ -27,6 +27,7 @@ public class Log extends Group { private final Identifier id; private final List listeners; private static boolean graphical = true; + private boolean finished; /** * Creates a {@code Log} for this {@code task} that will automatically store @@ -80,10 +81,12 @@ public Log(SearchTask task) { } private void logFinished() { + finished = true; listeners.stream().forEach(l -> l.onLogFinished(this)); } private void notifyListeners(LogEntry logEntry) { + finished = false; listeners.stream().forEach(l -> l.onNewEntry(logEntry)); } @@ -109,6 +112,10 @@ public final Identifier getIdentifier() { public boolean isStarted() { return logEntries.size() > 0; } + + public boolean isFinished() { + return finished; + } /** * Outputs all log entries consecutively. diff --git a/src/main/java/pulse/ui/components/GraphicalLogPane.java b/src/main/java/pulse/ui/components/GraphicalLogPane.java index 82f8424..bc69d23 100644 --- a/src/main/java/pulse/ui/components/GraphicalLogPane.java +++ b/src/main/java/pulse/ui/components/GraphicalLogPane.java @@ -14,17 +14,17 @@ @SuppressWarnings("serial") public class GraphicalLogPane extends AbstractLogger { - + private final LogChart chart; private final JScrollPane pane; - + public GraphicalLogPane() { pane = new JScrollPane(); pane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); chart = new LogChart(); pane.setViewportView(chart.getChartPanel()); - TaskManager.getManagerInstance().addTaskRepositoryListener( e -> { - if(e.getState() == TaskRepositoryEvent.State.TASK_SUBMITTED) { + TaskManager.getManagerInstance().addTaskRepositoryListener(e -> { + if (e.getState() == TaskRepositoryEvent.State.TASK_SUBMITTED) { chart.clear(); } }); @@ -44,16 +44,18 @@ public void printTimeTaken(Log log) { @Override public void post(LogEntry logEntry) { - if(logEntry instanceof DataLogEntry) { + if (logEntry instanceof DataLogEntry) { var dle = (DataLogEntry) logEntry; double iteration = dle.getData().stream() .filter(p -> p.getIdentifier().getKeyword() == ITERATION) .findAny().get().getApparentValue(); + chart.changeAxis(true); - chart.plot((DataLogEntry)logEntry, iteration); + chart.plot((DataLogEntry) logEntry, iteration); + } } - + @Override public void postAll() { clear(); @@ -64,18 +66,18 @@ public void postAll() { var log = task.getLog(); - if (log.isStarted()) { + if (log.isStarted() && log.isFinished()) { chart.clear(); chart.changeAxis(false); chart.plot(log); - + if (task.getStatus() == DONE) { printTimeTaken(log); } } - + } } @@ -95,4 +97,4 @@ public boolean isEmpty() { return false; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/ui/components/LogChart.java b/src/main/java/pulse/ui/components/LogChart.java index ba40ae4..f1c896c 100644 --- a/src/main/java/pulse/ui/components/LogChart.java +++ b/src/main/java/pulse/ui/components/LogChart.java @@ -157,11 +157,13 @@ private static void adjustRange(XYPlot pl, int iteration, int bufSize) { int lower = (iteration / bufSize) * bufSize; var domainAxis = pl.getDomainAxis(); - var r = domainAxis.getRange(); - var newR = new Range(lower, lower + bufSize); + if (domainAxis != null) { + var r = domainAxis.getRange(); + var newR = new Range(lower, lower + bufSize); - if (!r.equals(newR) && iteration > lower) { - ((XYPlot) pl).getDomainAxis().setRange(lower, lower + bufSize); + if (!r.equals(newR) && iteration > lower) { + ((XYPlot) pl).getDomainAxis().setRange(lower, lower + bufSize); + } } } diff --git a/src/main/java/pulse/ui/components/TaskPopupMenu.java b/src/main/java/pulse/ui/components/TaskPopupMenu.java index 5dc2c26..c29818f 100644 --- a/src/main/java/pulse/ui/components/TaskPopupMenu.java +++ b/src/main/java/pulse/ui/components/TaskPopupMenu.java @@ -80,7 +80,7 @@ public TaskPopupMenu() { var itemShowStatus = new JMenuItem("What is missing?", ICON_MISSING); instance.addSelectionListener(event -> { - instance.getSelectedTask().checkProblems(false); + instance.getSelectedTask().checkProblems(); var details = instance.getSelectedTask().getStatus().getDetails(); itemShowStatus.setEnabled((details != null) & (details != NONE)); }); @@ -102,7 +102,7 @@ public TaskPopupMenu() { getString("TaskTablePopupMenu.EmptySelection"), //$NON-NLS-1$ getString("TaskTablePopupMenu.ErrorTitle"), ERROR_MESSAGE); //$NON-NLS-1$ } else { - t.checkProblems(true); + t.checkProblems(); var status = t.getStatus(); if (status == DONE) { diff --git a/src/main/java/pulse/ui/components/buttons/ExecutionButton.java b/src/main/java/pulse/ui/components/buttons/ExecutionButton.java index 30ea042..5a06530 100644 --- a/src/main/java/pulse/ui/components/buttons/ExecutionButton.java +++ b/src/main/java/pulse/ui/components/buttons/ExecutionButton.java @@ -49,7 +49,7 @@ public ExecutionButton() { return; } var problematicTask = instance.getTaskList().stream().filter(t -> { - t.checkProblems(true); + t.checkProblems(); return t.getStatus() == INCOMPLETE; }).findFirst(); if (problematicTask.isPresent()) { diff --git a/src/main/java/pulse/ui/components/controllers/AccessibleTableRenderer.java b/src/main/java/pulse/ui/components/controllers/AccessibleTableRenderer.java index 04727c0..6f98ec3 100644 --- a/src/main/java/pulse/ui/components/controllers/AccessibleTableRenderer.java +++ b/src/main/java/pulse/ui/components/controllers/AccessibleTableRenderer.java @@ -26,33 +26,29 @@ public AccessibleTableRenderer() { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - Component renderer = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - + Component result = null; + if (value instanceof Flag) { - renderer = new IconCheckBox((boolean) ((Property) value).getValue()); - ((IconCheckBox) renderer).setHorizontalAlignment(CENTER); - } else if (value instanceof PropertyHolder) { - renderer = initButton(value.toString()); + result = new IconCheckBox((boolean) ((Property) value).getValue()); + ((IconCheckBox) result).setHorizontalAlignment(CENTER); } - else if (value instanceof NumericProperty) { - //default - } - else if (value instanceof Property) { - var label = (JLabel) super.getTableCellRendererComponent(table, - ((Property) value).getDescriptor(true), isSelected, - hasFocus, row, column); - label.setHorizontalAlignment(JLabel.CENTER); - label.setFont(label.getFont().deriveFont(Font.BOLD)); - return label; + + else if (value instanceof PropertyHolder) { + var sb = new StringBuilder("Click to Edit/View "); + sb.append(((PropertyHolder) value).getSimpleName()); + sb.append("..."); + result = new JButton(sb.toString()); + ((JButton)result).setToolTipText(value.toString()); + ((JButton)result).setHorizontalAlignment(LEFT); } - return renderer; - } - - private JButton initButton(String str) { - var button = new JButton(str); - button.setToolTipText(str); - return button; + else { + result = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + } + + return result; + } + -} +} \ No newline at end of file diff --git a/src/main/java/pulse/ui/components/panels/ProblemToolbar.java b/src/main/java/pulse/ui/components/panels/ProblemToolbar.java index e06abd0..ca36f67 100644 --- a/src/main/java/pulse/ui/components/panels/ProblemToolbar.java +++ b/src/main/java/pulse/ui/components/panels/ProblemToolbar.java @@ -63,7 +63,7 @@ public static void plot(ActionEvent e) { var calc = (Calculation) t.getResponse(); - t.checkProblems(true); + t.checkProblems(); var status = t.getStatus(); if (status == INCOMPLETE && !status.checkProblemStatementSet()) { diff --git a/src/main/java/pulse/ui/frames/LogFrame.java b/src/main/java/pulse/ui/frames/LogFrame.java index 7eeae66..096bac2 100644 --- a/src/main/java/pulse/ui/frames/LogFrame.java +++ b/src/main/java/pulse/ui/frames/LogFrame.java @@ -78,7 +78,8 @@ public void onLogModeChanged(boolean graphical) { private void scheduleLogEvents() { var instance = TaskManager.getManagerInstance(); - instance.addSelectionListener(e -> logger.postAll()); + instance.addSelectionListener( + e -> SwingUtilities.invokeLater(() -> logger.postAll())); instance.addTaskRepositoryListener(event -> { if (event.getState() != TASK_ADDED) { @@ -128,7 +129,7 @@ private void setGraphicalLogger(boolean graphicalLog) { if (old != logger) { getContentPane().remove(old.getGUIComponent()); getContentPane().add(logger.getGUIComponent(), BorderLayout.CENTER); - logger.postAll(); + SwingUtilities.invokeLater(() -> logger.postAll()); } } diff --git a/src/main/java/pulse/ui/frames/ProblemStatementFrame.java b/src/main/java/pulse/ui/frames/ProblemStatementFrame.java index 6acd7da..525ac3e 100644 --- a/src/main/java/pulse/ui/frames/ProblemStatementFrame.java +++ b/src/main/java/pulse/ui/frames/ProblemStatementFrame.java @@ -14,14 +14,10 @@ import java.awt.BorderLayout; import java.awt.GridLayout; -import java.util.Arrays; import java.util.List; -import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.logging.Level; -import java.util.logging.Logger; import java.util.stream.Collectors; import javax.swing.DefaultListModel; @@ -247,98 +243,62 @@ private void changeSchemes(DifferenceScheme newScheme) { var instance = TaskManager.getManagerInstance(); var selectedTask = instance.getSelectedTask(); - var schemeLoaderTracker = new ProgressDialog(); - schemeLoaderTracker.setTitle("Initialising solution schemes..."); - schemeLoaderTracker.setLocationRelativeTo(null); - schemeLoaderTracker.setAlwaysOnTop(true); + var tracker = new ProgressDialog(); + tracker.setTitle("Initialising solution schemes..."); + tracker.setLocationRelativeTo(null); + tracker.setAlwaysOnTop(true); - List> callableList; + tracker.trackProgress(instance.isSingleStatement() ? instance.getTaskList().size() : 1); + + Runnable finishingTouch = () -> { + var c = (Calculation) selectedTask.getResponse(); + schemeTable.setPropertyHolder(c.getScheme()); + if (c.getProblem().getComplexity() == HIGH) { + showMessageDialog(null, getString("complexity.warning"), + "High complexity", INFORMATION_MESSAGE); + } + ProblemToolbar.plot(null); + tracker.setVisible(false); + problemTable.requestFocus(); + }; if (instance.isSingleStatement()) { - callableList = instance.getTaskList().stream().map(t -> new Callable() { + var runnables = instance.getTaskList().stream().map(t -> new Runnable() { @Override - public DifferenceScheme call() throws Exception { + public void run() { changeScheme(t, newScheme); - schemeLoaderTracker.incrementProgress(); - return ((Calculation) t.getResponse()).getScheme(); + tracker.incrementProgress(); } }).collect(Collectors.toList()); + CompletableFuture.runAsync(() + -> runnables.parallelStream().forEach(c -> c.run())) + .thenRun(finishingTouch); + } else { - callableList = Arrays.asList(() -> { - changeScheme(selectedTask, newScheme); - return selectedTask.getResponse().getScheme(); - }); + CompletableFuture.runAsync(() -> changeScheme(selectedTask, newScheme)).thenRun(finishingTouch); } - schemeLoaderTracker.trackProgress(callableList.size() - 1); - - CompletableFuture.runAsync(() -> { - try { - schemeListExecutor.invokeAll(callableList); - } catch (InterruptedException ex) { - ex.printStackTrace(); - } - }).thenRun(() -> { - - var c = (Calculation) selectedTask.getResponse(); - schemeTable.setPropertyHolder(c.getScheme()); - if (c.getProblem().getComplexity() == HIGH) { - showMessageDialog(null, getString("complexity.warning"), - "High complexity", INFORMATION_MESSAGE); - } - Executors.newSingleThreadExecutor().submit(() -> ProblemToolbar.plot(null)); - }); } private void changeProblems(Problem newlySelectedProblem, Object source) { var instance = TaskManager.getManagerInstance(); - var task = instance.getSelectedTask(); - var selectedCalc = ((Calculation) task.getResponse()); - - var problemLoaderTracker = new ProgressDialog(); - problemLoaderTracker.setTitle("Changing problem statements..."); - problemLoaderTracker.setLocationRelativeTo(null); - problemLoaderTracker.setAlwaysOnTop(true); + var selectedProblem = instance.getSelectedTask(); - List> callableList; + var tracker = new ProgressDialog(); + tracker.setTitle("Changing problem statements..."); + tracker.setLocationRelativeTo(null); + tracker.setAlwaysOnTop(true); if (source != instance) { - //apply to all tasks - if (instance.isSingleStatement()) { - - callableList = instance.getTaskList().stream().map(t -> new Callable() { - @Override - public Problem call() throws Exception { - changeProblem(t, newlySelectedProblem); - var result = ((Calculation) t.getResponse()).getProblem(); - problemLoaderTracker.incrementProgress(); - return result; - } - }).collect(Collectors.toList()); - - } //apply only to this task - else { - callableList = Arrays.asList(() -> { - changeProblem(task, newlySelectedProblem); - return ((Calculation) task.getResponse()).getProblem(); - }); - } + tracker.trackProgress(instance.isSingleStatement() ? instance.getTaskList().size() : 1); - problemLoaderTracker.trackProgress(callableList.size() - 1); - - CompletableFuture.runAsync(() -> { - try { - problemListExecutor.invokeAll(callableList); - } catch (InterruptedException ex) { - ex.printStackTrace(); - } - } - ).thenRun(() -> { + Runnable finishingTouch = () -> { + var selectedCalc = (Calculation) selectedProblem.getResponse(); problemTable.setPropertyHolder(selectedCalc.getProblem()); // after problem is selected for this task, show available difference schemes var defaultModel = (DefaultListModel) (schemeSelectionList.getModel()); @@ -347,7 +307,27 @@ public Problem call() throws Exception { schemes.forEach(s -> defaultModel.addElement(s)); selectDefaultScheme(schemeSelectionList, selectedCalc.getProblem()); schemeSelectionList.setToolTipText(null); - }); + tracker.setVisible(false); + }; + + if (instance.isSingleStatement()) { + + var runnables = instance.getTaskList().stream().map(t -> new Runnable() { + @Override + public void run() { + changeProblem(t, newlySelectedProblem); + tracker.incrementProgress(); + } + + }).collect(Collectors.toList()); + + CompletableFuture.runAsync(() + -> runnables.parallelStream().forEach(c -> c.run())) + .thenRun(finishingTouch); + + } else { + CompletableFuture.runAsync(() -> changeProblem(selectedProblem, newlySelectedProblem)).thenRun(finishingTouch); + } } @@ -372,7 +352,7 @@ private void changeProblem(SearchTask task, Problem newProblem) { np.retrieveData(data); } - task.checkProblems(true); + task.checkProblems(); toolbar.highlightButtons(!np.isReady()); } @@ -413,7 +393,7 @@ private void changeScheme(SearchTask task, DifferenceScheme newScheme) { } - task.checkProblems(true); + task.checkProblems(); } diff --git a/src/main/java/pulse/ui/frames/SearchOptionsFrame.java b/src/main/java/pulse/ui/frames/SearchOptionsFrame.java index f638f4d..7d2c9db 100644 --- a/src/main/java/pulse/ui/frames/SearchOptionsFrame.java +++ b/src/main/java/pulse/ui/frames/SearchOptionsFrame.java @@ -209,7 +209,7 @@ public PathOptimiser getElementAt(int index) { pathTable.setPropertyHolder(optimiser); for (var t : TaskManager.getManagerInstance().getTaskList()) { - t.checkProblems(true); + t.checkProblems(); } }); diff --git a/src/main/java/pulse/util/Accessible.java b/src/main/java/pulse/util/Accessible.java index b9cdf8c..9cc2832 100644 --- a/src/main/java/pulse/util/Accessible.java +++ b/src/main/java/pulse/util/Accessible.java @@ -101,17 +101,18 @@ public List genericProperties() { var methods = this.getClass().getMethods(); for (var m : methods) { - if (m.getParameterCount() > 0) { - continue; - } + //getters only + if (m.getParameterCount() == 0) { - if (Property.class.isAssignableFrom(m.getReturnType()) - && !NumericProperty.class.isAssignableFrom(m.getReturnType())) + if (Property.class.isAssignableFrom(m.getReturnType()) + && !NumericProperty.class.isAssignableFrom(m.getReturnType())) try { - fields.add((Property) m.invoke(this)); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - err.println("Error invoking method " + m); - e.printStackTrace(); + fields.add((Property) m.invoke(this)); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + err.println("Error invoking method " + m); + e.printStackTrace(); + } + } } diff --git a/src/main/java/pulse/util/InstanceDescriptor.java b/src/main/java/pulse/util/InstanceDescriptor.java index bead937..644ad02 100644 --- a/src/main/java/pulse/util/InstanceDescriptor.java +++ b/src/main/java/pulse/util/InstanceDescriptor.java @@ -55,9 +55,10 @@ public boolean attemptUpdate(Object object) { return false; } - if(!allDescriptors.contains(string)) + if(!allDescriptors.contains(string)) { throw new IllegalArgumentException("Unknown descriptor: " + selectedDescriptor); - + } + this.selectedDescriptor = string; listeners.stream().forEach(l -> l.onDescriptorChanged()); return true; diff --git a/src/main/resources/NumericProperty.xml b/src/main/resources/NumericProperty.xml index 06c47f7..cfc9984 100644 --- a/src/main/resources/NumericProperty.xml +++ b/src/main/resources/NumericProperty.xml @@ -441,10 +441,10 @@ descriptor="Buffer size" dimensionfactor="1" keyword="BUFFER_SIZE" maximum="32" minimum="4" value="5" primitive-type="int" discreet="false"/> -