Pārlūkot izejas kodu

Handle any dead-lock that may hang the UI and stop it forcefully

Henrik Kunzelmann 3 gadi atpakaļ
vecāks
revīzija
42ff189e48

+ 104 - 63
src/holeg/HolegGateway.java

@@ -13,11 +13,17 @@ import holeg.ui.SolveResultMessageBox;
 import ui.controller.FlexManager;
 import ui.controller.SingletonControl;
 import ui.model.*;
+import ui.view.GUI;
 
+import javax.swing.*;
+import java.awt.*;
 import java.util.*;
+import java.util.List;
 import java.util.stream.Collectors;
 
 public class HolegGateway {
+    private static ThreadLocal<Boolean> onlyUpdateVisual = new ThreadLocal<Boolean>();
+
     private static Grid convert(MinimumNetwork network, int iteration, FlexManager flexManager) {
         SimpleGridBuilder gridBuilder = new SimpleGridBuilder();
         HashMap<AbstractCanvasObject, SimpleGridNode> canvasToGrid = new HashMap<>();
@@ -74,9 +80,9 @@ public class HolegGateway {
 
                 // Create supplier, consumer or passiv object
                 if (node.getPowerGeneration().lenSquared() > 0)
-                    decoratedNetwork.getSupplierList().add(new Supplier((HolonObject) simpleNode.tag, (float) node.getPowerGeneration().real, 0, simpleNode.voltage, simpleNode.phaseDegrees, simpleNode.typeSolved == NodeType.Slack));
+                    decoratedNetwork.getSupplierList().add(new Supplier((HolonObject) simpleNode.tag, (float) node.getPowerGeneration().real, 0, simpleNode.getVoltage(), simpleNode.phaseDegrees, simpleNode.getTypeByDesign() == NodeType.Slack));
                 else if (node.getPowerConsumption().lenSquared() > 0)
-                    decoratedNetwork.getConsumerList().add(new Consumer((HolonObject) simpleNode.tag, (float) node.getPowerConsumption().real, simpleNode.voltage, simpleNode.phaseDegrees, Math.cos(simpleNode.getPowerConsumption().angle()), simpleNode.typeSolved == NodeType.Slack));
+                    decoratedNetwork.getConsumerList().add(new Consumer((HolonObject) simpleNode.tag, (float) node.getPowerConsumption().real, simpleNode.getVoltage(), simpleNode.phaseDegrees, Math.cos(simpleNode.getPowerConsumption().angle()), simpleNode.getTypeByDesign() == NodeType.Slack));
                 else if (simpleNode.tag instanceof HolonObject)
                     decoratedNetwork.getPassivNoEnergyList().add(new Passiv((HolonObject) simpleNode.tag));
             }
@@ -133,7 +139,28 @@ public class HolegGateway {
         return context;
     }
 
-    public static synchronized void solve(PowerFlowSettings settings, MinimumNetwork minimumNetwork, int iteration, FlexManager flexManager, DecoratedNetwork network) {
+    public static void solve(PowerFlowSettings settings, MinimumNetwork minimumNetwork, int iteration, FlexManager flexManager, DecoratedNetwork network) {
+        Thread solverWrapper = new Thread(() -> {
+            try {
+                solveInternal(settings, minimumNetwork, iteration, flexManager, network);
+            } catch (Exception e) {
+                SolveResultMessageBox.showInternalError(e);
+            }
+        });
+        try {
+            solverWrapper.setName("Solver Wrapper " + Long.toHexString(solverWrapper.getId()));
+            solverWrapper.start();
+            solverWrapper.join(4000);
+            if (solverWrapper.isAlive()) {
+                solverWrapper.stop();
+                SolveResultMessageBox.showSolverTookTooLongToStart();
+            }
+        } catch (Exception e) {
+            SolveResultMessageBox.showInternalError(e);
+        }
+    }
+
+    private static void solveInternal(PowerFlowSettings settings, MinimumNetwork minimumNetwork, int iteration, FlexManager flexManager, DecoratedNetwork network) {
         // Find context
         HolegPowerFlowContext context = getContextFromHolonObjects(minimumNetwork.getHolonObjectList());
         if (context == null)
@@ -143,7 +170,7 @@ public class HolegGateway {
         solve(settings, context, minimumNetwork, iteration, flexManager, network);
     }
 
-    private static synchronized void solve(PowerFlowSettings settings, HolegPowerFlowContext context, MinimumNetwork minimumNetwork, int iteration, FlexManager flexManager, DecoratedNetwork network) {
+    private static void solve(PowerFlowSettings settings, HolegPowerFlowContext context, MinimumNetwork minimumNetwork, int iteration, FlexManager flexManager, DecoratedNetwork network) {
         if (settings == null)
             throw new IllegalArgumentException("settings is null");
         if (context == null)
@@ -160,73 +187,87 @@ public class HolegGateway {
             return;
         }
 
-        boolean solve = !PowerFlowAnalysisMenu.getInstance().areUpdatesDisabled();
-        Grid grid = convert(minimumNetwork, iteration, flexManager);
-
-        // Check if the grid is equal to one already solved
-        if (settings.onlyUpdateGridWhenChanged) {
-            for (Grid lastSolved : context.lastSolvedGrids) {
-                if (GridComparator.isEqual(lastSolved, grid)) {
-                    decorateNetwork(minimumNetwork, iteration, flexManager, network, lastSolved);
-                    return;
+        synchronized (context.lock) {
+            boolean solve = !PowerFlowAnalysisMenu.getInstance().areUpdatesDisabled();
+            Grid grid = convert(minimumNetwork, iteration, flexManager);
+
+            // Check if the grid is equal to one already solved
+            if (settings.onlyUpdateGridWhenChanged) {
+                for (Grid lastSolved : context.lastSolvedGrids) {
+                    if (GridComparator.isEqual(lastSolved, grid)) {
+                        decorateNetwork(minimumNetwork, iteration, flexManager, network, lastSolved);
+                        return;
+                    }
                 }
-            }
 
-            // Only remember grid when solving
-            if (solve) {
-                // Keep size constrained to 32 saved grids
-                if (context.lastSolvedGrids.size() >= 32)
-                    context.lastSolvedGrids.remove(0);
+                // Only remember grid when solving
+                if (solve) {
+                    // Keep size constrained to 32 saved grids
+                    if (context.lastSolvedGrids.size() >= 32)
+                        context.lastSolvedGrids.remove(0);
 
-                // Grid is updated by solver directly, so we can save the reference to the grid
-                context.lastSolvedGrids.add(grid);
+                    // Grid is updated by solver directly, so we can save the reference to the grid
+                    context.lastSolvedGrids.add(grid);
+                }
             }
-        }
-
-        // Stop old solver
-        context.stopSolver();
-
-        // Create solver job
-        Thread solverJob = new Thread(() -> {
-            long start = System.nanoTime();
-            GridSolverResult result = null;
 
-            // Starting solving when requested
-            if (solve) {
-                HolegPowerFlow powerFlow = new HolegPowerFlow();
-                result = powerFlow.solve(grid, settings);
+            // We got no solution for this grid
+            if (onlyUpdateVisual.get() != null && onlyUpdateVisual.get()) {
+                decorateNetwork(minimumNetwork, iteration, flexManager, network, grid);
+                return;
             }
 
-            // Decorate network
-            decorateNetwork(minimumNetwork, iteration, flexManager, network, grid);
-            context.solverTimeMilliseconds = (System.nanoTime() - start) / 1e6f;
-
-            // Update canvas and other visuals
-            context.showGridForVisual = grid;
-            SingletonControl.getInstance().getControl().calculateStateAndVisualForCurrentTimeStep();
-
-            // Show result message box
-            if (result != null && PowerFlowAnalysisMenu.getInstance().shouldShowResult())
-                SolveResultMessageBox.show(result);
-        });
-
-
-        // Wait or save solver job
-        try {
-            if (settings.waitForSolverJob) {
-                // Start and wait till solver job is finished
-                solverJob.start();
-                solverJob.join();
-            }
-            else {
-                // Render the non-solved grid and start the real solver job
-                decorateNetwork(minimumNetwork, iteration, flexManager, network, grid);
-                context.solverJob = solverJob;
-                solverJob.start();
+            // Stop old solver
+            context.stopSolver();
+
+            // Create solver job
+            Thread solverJob = new Thread(() -> {
+                try {
+                    long start = System.nanoTime();
+                    GridSolverResult result = null;
+
+                    // Starting solving when requested
+                    if (solve) {
+                        HolegPowerFlow powerFlow = new HolegPowerFlow();
+                        result = powerFlow.solve(grid, settings);
+                    }
+
+                    // Decorate network
+                    decorateNetwork(minimumNetwork, iteration, flexManager, network, grid);
+                    context.solverTimeMilliseconds = (System.nanoTime() - start) / 1e6f;
+
+                    // Update canvas and other visuals
+                    context.showGridForVisual = grid;
+                    onlyUpdateVisual.set(true);
+                    SingletonControl.getInstance().getControl().calculateStateAndVisualForCurrentTimeStep();
+                    onlyUpdateVisual.set(false);
+
+                    // Show result message box
+                    if (result != null && PowerFlowAnalysisMenu.getInstance().shouldShowResult())
+                        SolveResultMessageBox.show(result);
+                }
+                catch(Exception e) {
+                    SolveResultMessageBox.showInternalError(e);
+                }
+            });
+            solverJob.setName("Solver 0x" + Integer.toHexString(context.hashCode()));
+
+
+            // Wait or save solver job
+            try {
+                if (settings.waitForSolverJob) {
+                    // Start and wait till solver job is finished
+                    solverJob.start();
+                    solverJob.join();
+                } else {
+                    // Render the non-solved grid and start the real solver job
+                    decorateNetwork(minimumNetwork, iteration, flexManager, network, grid);
+                    context.solverJob = solverJob;
+                    solverJob.start();
+                }
+            } catch (InterruptedException e) {
+                e.printStackTrace();
             }
         }
-        catch(InterruptedException e) {
-            e.printStackTrace();
-        }
     }
 }

+ 16 - 10
src/holeg/HolegPowerFlowContext.java

@@ -6,30 +6,36 @@ import java.util.ArrayList;
 import java.util.List;
 
 public class HolegPowerFlowContext {
+    public final Object lock = new Object();
     public List<Grid> lastSolvedGrids = new ArrayList<>();
     public Thread solverJob;
     public float solverTimeMilliseconds;
     public Grid showGridForVisual;
 
     public void clearCache() {
-        lastSolvedGrids.clear();
+        synchronized (lock) {
+            lastSolvedGrids.clear();
+        }
     }
 
     public boolean isSolving() {
-        return solverJob != null && solverJob.isAlive();
+        synchronized (lock) {
+            return solverJob != null && solverJob.isAlive();
+        }
     }
 
     public void stopSolver() {
-        if (solverJob != null) {
-            try {
-                solverJob.interrupt();
-                // wait till old solver job has finished or is interrupted
-                solverJob.join(100);
-            }
-            catch(InterruptedException ignored) {
+        synchronized (lock) {
+            if (solverJob != null) {
+                try {
+                    solverJob.interrupt();
+                    // wait till old solver job has finished or is interrupted
+                    solverJob.join(100);
+                } catch (InterruptedException ignored) {
 
+                }
+                solverJob = null;
             }
-            solverJob = null;
         }
     }
 }

+ 9 - 0
src/holeg/ui/SolveResultMessageBox.java

@@ -22,4 +22,13 @@ public class SolveResultMessageBox {
                 break;
         }
     }
+
+    public static void showSolverTookTooLongToStart() {
+        JOptionPane.showMessageDialog(null,"Could not solve grid.","Solver took too long to start", JOptionPane.ERROR_MESSAGE);
+    }
+
+    public static void showInternalError(Exception exception) {
+        exception.printStackTrace();
+        JOptionPane.showMessageDialog(null,"Could not solve grid.","Internal error.", JOptionPane.ERROR_MESSAGE);
+    }
 }

+ 1 - 1
src/ui/controller/Control.java

@@ -550,7 +550,7 @@ public class Control {
      * calculates the flow of the edges and the supply for objects for the
      * current Timestep.
      */
-    public void calculateStateAndVisualForCurrentTimeStep() {
+    public synchronized void calculateStateAndVisualForCurrentTimeStep() {
     	calculateStateAndVisualForTimeStep(model.getCurIteration());
     }