import java.awt.Image; import java.awt.Graphics; import java.awt.Color; import java.awt.Button; import java.awt.Dimension; import java.awt.Container; import java.awt.Window; import java.awt.Panel; import java.awt.BorderLayout; import java.awt.FlowLayout; import java.awt.Event; import java.net.URL; import java.io.DataInputStream; import java.io.DataOutputStream; import java.util.StringTokenizer; /* Copyright (C) 1995 Link=F6ping University, Link=F6ping, Sweden. */ /** * The class NuclearPlant is an applet that lets you run a nuclear power plant * * @version 1.0f * @author Henrik Eriksson */ public class NuclearPlant extends java.applet.Applet implements Runnable { /** The thread that updates the display (for animation) */ protected Thread kicker = null; /** The amount of time too sleep between display updates (ms?) */ protected int kickerDelay; /** The offscreen image */ protected Image im; /** The offscreen graphics context */ protected Graphics offscreen; /** The static background image */ protected Image background; /** The directory that contains the images. This directory is the location for GIFS for the background, plant components, and display animation. */ static String imageDir = "images/"; /** The reactor object */ Reactor reactor; /** Valve object */ Valve valve_1, valve_2, valve_3, valve_4; /** Pump object */ Pump pump_1, pump_2, pump_3; /** The turbine */ Turbine turbine; /** The condenser */ Condenser condenser; /** The generator */ Generator generator; /** The nuclear power plant simulator */ Simulator simulator; /** The window for the sequence control buttons */ Panel controlPanel; /** Flag that is true iff a simulation is running */ boolean isRunningSimulation = false; /** The status message that is display in the applet area */ String message = ""; /** Initialize the applet. Sets the applet size, creates the plant components, and creates the simulator. */ public void init() { //resize(680, 473); // java.applet.Applet size im = createImage(680,473); offscreen = im.getGraphics(); offscreen.setColor(Color.black); background = getImage(getCodeBase(), imageDir + "BACKGROUND.GIF"); Component.plant = this; reactor = new Reactor(); valve_1 = new Valve("SV1", 360, 59); valve_2 = new Valve("SV2", 360, 178); valve_3 = new Valve("WV1", 385, 382); valve_4 = new Valve("WV2", 385, 420); pump_1 = new Pump("Pump 1", 406, 389, 1400); pump_2 = new Pump("Pump 2", 406, 427, 1400); pump_3 = new Pump("Pump 3", 619, 421, 1285); turbine = new Turbine(); condenser = new Condenser(); generator = new Generator(); setLayout(new BorderLayout()); controlPanel = new Panel(); controlPanel.setLayout(new FlowLayout()); controlPanel.add(new Seq_1_Button(this, "Sequence 1")); controlPanel.add(new Seq_2_Button(this, "Sequence 2")); controlPanel.add(new Seq_3_Button(this, "Sequence 3")); controlPanel.add(new Rand_Button(this, "Randomize")); add("North", controlPanel); simulator = new LocalSimulator(this); } /** Update the plant display * @param g Graphics context to paint in */ public void paint(Graphics g) { if (reactor == null) { return; } if (!reactor.overheated) { Dimension d = size(); offscreen.setColor(getBackground()); offscreen.fillRect(0, 0, d.width, d.height); offscreen.drawImage(background,0,50, this); reactor.paint(offscreen); valve_1.paint(offscreen); valve_2.paint(offscreen); valve_3.paint(offscreen); valve_4.paint(offscreen); pump_1.paint(offscreen); pump_2.paint(offscreen); pump_3.paint(offscreen); turbine.paint(offscreen); condenser.paint(offscreen); generator.paint(offscreen); offscreen.drawString(message, 150, 440); } else reactor.paintMeltdown(offscreen); //controlPanel.move(g.originX+20, g.originY+330); g.drawImage(im,0,0, this); } /** Update without erasing background * @param g Graphics context to update in */ public void update(Graphics g) { paint(g); } /** Handle mouseDown events. This method distributes the message by calling the mouseDown methods for the appropriate plant components * @param x The X coordinate * @param y The Y coordinate */ public boolean mouseDown(Event evt, int x, int y) { getAppletContext().showStatus("mouseDown " + x + " " + y); pump_1.mouseDown(evt, x, y); pump_2.mouseDown(evt, x, y); pump_3.mouseDown(evt, x, y); valve_1.mouseDown(evt, x, y); valve_2.mouseDown(evt, x, y); valve_3.mouseDown(evt, x, y); valve_4.mouseDown(evt, x, y); reactor.mouseDown(evt, x, y); return true; } /** Handle mouseDrag events. This method distributes the message by calling the mouseDrag methods for the appropriate plant components * @param x The X coordinate * @param y The Y coordinate */ public boolean mouseDrag(Event evt, int x, int y) { return reactor.mouseDrag(evt, x, y); } /** Handle mouseUp events. This method distributes the message by calling the mouseUp methods for the appropriate plant components * @param x The X coordinate * @param y The Y coordinate */ public boolean mouseUp(Event evt, int x, int y) { return reactor.mouseUp(evt, x, y); } /** Rotate the pumps one step. (Only pumps with rpm > 0 are rotated.) */ protected void rotatePumps() { pump_1.rotate(); pump_2.rotate(); pump_3.rotate(); } /** Move the waves on the water surfaces in the tanks */ protected void waterWave() { reactor.waterWave(); condenser.waterWave(); } /** Start the simulation */ public void startReactor() { getAppletContext().showStatus("The simulation is running..."); simulator.start(); } /** Run the simulation n steps. Uses the crurrent * simulator to calculate the next states * @param n The number of steps */ public void timeStep(int n) { simulator.timeStep(n); } /** Blow up a device. * @param device The device to blow (e.g., a turbine object) */ public void blow(Component device) { simulator.blow(device); } /** Stop the simulation */ public void stopReactor() { getAppletContext().showStatus("The simulation has stopped"); simulator.stop(); } /** Play a crash sound. This method is called when plant components blow up. * @param n Duration */ public void crashSound(int n) { play(getCodeBase(), "audio/crash.au"); } /** Run the animation thread. This method is called when the animation thread is started. */ public void run() { Thread.currentThread().setPriority(Thread.MIN_PRIORITY); while (kicker != null) { rotatePumps(); waterWave(); repaint(); try {Thread.sleep(kickerDelay);} catch (InterruptedException e){} } } /** Start the applet. Creates the animation thread and opens the window for the sequence control buttons */ public void start() { if (kicker == null) { kicker = new Thread(this); kickerDelay = 60; kicker.start(); } } /** Stop the applet. Stops the animation thread and closes the window for the sequence control buttons */ public void stop() { if (kicker != null) { kicker.stop(); kicker = null; } } } /** * The class SeqButton is an abstract class for buttons that control * simulation sequences. * * @version 1.0f * @author Henrik Eriksson */ abstract class SeqButton extends Button implements Runnable { /** Backpointer to the NuclearPlant applet */ protected NuclearPlant plant; /** Construct a button and initialize it. * @param p The plant * @param name The button label * @param x The X coordinate * @param y The Y coordinate */ public SeqButton(NuclearPlant p, String name) { super(name); plant = p; } /** Perform a sequence when the button is selected. This method is called automatically when the button is selected by the user. The method runs the sequence in a separate thread. * @param c the awt.Component selected * @param pos undefined */ public boolean action(Event evt, Object arg) { new Thread(this).start(); return true; } } /** * Class for the Sequence 1 button. * * @version 1.0f * @author Henrik Eriksson */ class Seq_1_Button extends SeqButton { /** Construct a button and initialize it. * @param p The plant * @param name The button label * @param x The X coordinate * @param y The Y coordinate */ public Seq_1_Button(NuclearPlant p, String name) { super(p, name); } /** Run the sequence. This method is called in a separate thread when the button is selected by the user. Modify this method to change the sequence in question. */ public void run() { plant.controlPanel.hide(); plant.isRunningSimulation = true; plant.startReactor(); plant.message = "Sequence 1 running..."; plant.timeStep(25); plant.blow(plant.turbine); plant.timeStep(200); plant.stopReactor(); plant.message = ""; plant.isRunningSimulation = false; plant.controlPanel.show(); } } /** * Class for the Sequence 2 button. * * @version 1.0f * @author Henrik Eriksson */ class Seq_2_Button extends SeqButton { /** Construct a button and initialize it. * @param p The plant * @param name The button label * @param x The X coordinate * @param y The Y coordinate */ public Seq_2_Button(NuclearPlant p, String name) { super(p, name); } /** Run the sequence. This method is called in a separate thread when the button is selected by the user. Modify this method to change the sequence in question. */ public void run() { plant.controlPanel.hide(); plant.startReactor(); plant.isRunningSimulation = true; plant.message = "Sequence 2 running..."; plant.timeStep(25); plant.blow(plant.pump_1); plant.timeStep(140); plant.stopReactor(); plant.message = ""; plant.isRunningSimulation = false; plant.controlPanel.show(); } } /** * Class for the Sequence 3 button. * * @version 1.0f * @author Henrik Eriksson */ class Seq_3_Button extends SeqButton { /** Construct a button and initialize it. * @param p The plant * @param name The button label * @param x The X coordinate * @param y The Y coordinate */ public Seq_3_Button(NuclearPlant p, String name) { super(p, name); } /** Run the sequence. This method is called in a separate thread when the button is selected by the user. Modify this method to change the sequence in question. */ public void run() { plant.controlPanel.hide(); plant.startReactor(); plant.isRunningSimulation = true; plant.message = "Sequence 3 running..."; plant.timeStep(25); plant.blow(plant.pump_3); plant.timeStep(140); plant.stopReactor(); plant.message = ""; plant.isRunningSimulation = false; plant.controlPanel.show(); } } /** * Class for the random sequence button. * * @version 1.0f * @author Henrik Eriksson */ class Rand_Button extends SeqButton { /** Construct a button and initialize it. * @param p The plant * @param name The button label * @param x The X coordinate * @param y The Y coordinate */ public Rand_Button(NuclearPlant p, String name) { super(p, name); } /** Run the sequence. This method is called in a separate thread when the button is selected by the user. Modify this method to change the sequence in question. */ public void run() { plant.controlPanel.hide(); plant.startReactor(); plant.isRunningSimulation = true; plant.message = "Random sequence running..."; plant.timeStep(25); switch ((int)(Math.random()*5)) { case 0: plant.blow(plant.turbine); break; case 1: plant.blow(plant.pump_1); break; case 2: plant.blow(plant.pump_3); break; case 3: plant.blow(plant.condenser); break; case 4: plant.blow(plant.reactor); break; } plant.timeStep(25); switch ((int)(Math.random()*5)) { case 0: plant.blow(plant.turbine); break; case 1: plant.blow(plant.pump_1); break; case 2: plant.blow(plant.pump_3); break; case 3: plant.blow(plant.condenser); break; case 4: plant.blow(plant.reactor); break; } plant.timeStep(140); plant.stopReactor(); plant.message = ""; plant.isRunningSimulation = false; plant.controlPanel.show(); } } /** * Class for components of the nuclear power plant * * @version 1.0f * @author Henrik Eriksson */ class Component { /** Backpointer to the plant */ protected static NuclearPlant plant; /** Component location */ protected int x, y; /** Component label (if any) */ protected String label; /** True iff component broken */ boolean blown = false; /** Returns an image given its name * @param name The image name * @return The image */ private Image getImage(URL url, String name) { return plant.getImage(url, plant.imageDir + name); } /** Returns an image given its name * @param name The image name * @param cache A cached image * @return The image */ protected Image getImage(URL url, String name, Image cache) { return (cache == null) ? getImage(url, name) : cache; } /** No-op at this level */ void blow() {} /** Set the value of a slot (no-op at this level) * @param slot The slot name * @param val The value */ void setIntValue(String slot, int val) { } /** Set the value of a slot (no-op at this level) * @param slot The slot name * @param val The value */ void setValue(String slot, float val) { } /** Set the value of a slot (no-op at this level) * @param slot The slot name * @param val The value */ void setValue(String slot, String val) { } /** Set the value of a slot * @param slot The slot name * @param val The value */ void setValue(String slot, boolean val) { if (slot.equals("blown")) blown = val; } /** Random number generator (pseudo) * @param min The lower bound * @param max The upper bound * @return A random number (between min and max) */ protected int rand(int min, int max) { return (int)(Math.random()*(max-min) + min); } } /** * A generic tank component for the nuclear power plant * * @version 1.0f * @author Henrik Eriksson */ class Tank extends Component { /** The tank pressure (in bar) */ float pressure; /** The tank waterlevel (in mm) */ float waterLevel; /** The number of the water image shown */ protected int imageState = 0; /** Water (surface) images for animation */ protected static Image vatten_bm[]; /** Blow up the tank */ void blow() { plant.crashSound(15000); plant.getAppletContext().showStatus("The " + label + " blow up"); blown = true; } /** Set the value of a slot * @param slot The slot name * @param val The value */ void setValue(String slot, float val) { if (slot.equals("pressure")) pressure = val; else if (slot.equals("waterLevel")) waterLevel = val; else super.setValue(slot, val); } /** Array of bubble depths */ private int bubble_depth[] = new int[100]; /** Array of bubble X positions */ private int bubble_x[] = new int[100]; /** Paint animated bubbles in a region * @param g The graphics context * @param x The X position for the bubble region * @param y The Y position for the bubble region * @param size().width The size().width of the bubble region * @param size().height The size().height of the bubble region * @param noOfBubbles The bubble frequency */ protected void paintBubbles(Graphics g, int x, int y, int width, int depth, int noOfBubbles) { Color oldForeground = g.getColor(); g.setColor(Color.white); if (noOfBubbles > bubble_depth.length) noOfBubbles = bubble_depth.length; for (int i=0; i < noOfBubbles; i++) { if (bubble_depth[i] == 0) { // Create a new bubble bubble_depth[i] = rand(1, depth); bubble_x[i] = rand(x, width); } g.fillRect(bubble_x[i], y+bubble_depth[i]--, 1, 1); Thread.yield(); } g.setColor(oldForeground); } } /** * A reactor tank component for the nuclear power plant * * @version 1.0f * @author Henrik Eriksson */ class Reactor extends Tank { /** The moderator rod level (in percent) */ int moderatorPercent = 50; /** True iff the reactor is overheated */ boolean overheated; /** The state of a meltdown */ private int meltStage; /** Water (surface) images for animation */ protected static Image vatten_bm[] = new Image[3]; /** Image for a blown reactor tank */ protected static Image crashed_reactor_bm; /** Radiation sign (image) */ protected static Image radiak_bm; Reactor() { vatten_bm[0] = getImage(plant.getCodeBase(), "R_VATTEN_A_BM.GIF", vatten_bm[0]); vatten_bm[1] = getImage(plant.getCodeBase(), "R_VATTEN_B_BM.GIF", vatten_bm[1]); vatten_bm[2] = getImage(plant.getCodeBase(), "R_VATTEN_C_BM.GIF", vatten_bm[2]); radiak_bm = getImage(plant.getCodeBase(), "RADIAK_BM.GIF", radiak_bm); label = "reactor"; waterLevel = 1800; } /** Set the value of a slot * @param slot The slot name * @param val The value */ void setIntValue(String slot, int val) { if (slot.equals("moderatorPercent")) moderatorPercent = val; else super.setIntValue(slot, val); } /** Set the value of a slot * @param slot The slot name * @param val The value */ void setValue(String slot, boolean val) { if (slot.equals("overheated")) { overheated = val; if (val) meltdown(); } else super.setValue(slot, val); } /** Advance the water-surface animation one step */ void waterWave () { if (++imageState >= vatten_bm.length) imageState = 0; } /** Perform the meltdown animation. Called when the reactor core is overheated */ public void meltdown() { overheated = true; plant.kickerDelay = 40; meltStage = 5; plant.getAppletContext().showStatus("The " + label + " is overheated"); } /** Paint operation for the meltdown animation * @param g The graphics context */ public void paintMeltdown(Graphics g) { //im = new Image(item.parent); //offscreen = new Graphics(im); // int x0 = 10, y0 = 150; int x0 = -60, y0 = 110; if (meltStage == 5) g.drawImage(radiak_bm, 170, 150, plant); if (meltStage < 1500) { int d = meltStage; int x = Math.max(x0 - (d - 100), 0) + rand(0, Math.min(d, plant.size().width)); int y = Math.max(y0 - (d - 100), 0) + rand(0, Math.min(d, plant.size().height)); // BITBLT g.copyArea(x, y, rand(10, 100), rand(10, 100), ((d < 250) ? rand(-3, 3) : rand(-2, 2)), ((d < 250) ? rand(-3, 3) : rand(-2, 2))); meltStage++; } } /** The color of overheated fuel rods */ private static Color overheatedColor = new Color(200,0,0); /** The color of water */ private static Color waterColor = new Color(127,193,255); /** Paint operation for the reactor tank * @param g The graphics context */ public void paint(Graphics g) { if (pressure > 500) g.setColor(Color.red); g.drawString((int)pressure+" bar", 55, 70); int surfaceY = 175-(int)waterLevel/50; g.setColor(waterColor); g.fillRect(3, surfaceY+1, 135, 279-surfaceY); g.setColor(Color.black); g.drawImage(vatten_bm[imageState], 3, surfaceY, plant); g.drawString("Level:", 160, 110); if (waterLevel < 0) g.setColor(Color.red); g.drawString((int)waterLevel+" mm", 160, 125); g.setColor(Color.gray); for (int i = 0; i < 7; i++) g.fillRect(33 + i*12, 102+moderatorPercent*3/4, 3, 80); if (moderatorOutlinePos > 0) { g.setColor(Color.yellow); for (int i = 0; i < 7; i++) g.drawRect(33 + i*12, moderatorOutlinePos, 2, 80); } if (waterLevel < 0) { Color hotColor = new Color(Math.min(-(int)waterLevel/10,150),0,0); g.setColor(hotColor); } else g.setColor(Color.black); for (int i = 0; i < 8; i++) g.fillRect(25 + i*12, 177, 7, 80); if (waterLevel < 0) { g.setColor(overheatedColor); for (int i = 0; i < 8; i++) g.fillRect(25 + i*12, 177, 7, Math.max(-(int)waterLevel/50-1, 0)); } g.setColor(Color.black); paintBubbles(g, 3, surfaceY, 137, 40, 50); if (blown) { crashed_reactor_bm = getImage(plant.getCodeBase(), "CRASHED_REACTOR_BM.GIF",crashed_reactor_bm); g.drawImage(crashed_reactor_bm, 30, 3, plant); g.drawImage(radiak_bm, 170, 150, plant); } } /** Initial Y position for dragged moderator rods */ private int y0 = 0; /** The distance of the drag */ protected int dragDelta = 0; /** The position of the outline of the dragged moderator rods */ protected int moderatorOutlinePos = 0; /** Checks if a certain position is inside the area of the moderator rods * @param x The X coordinate * @param y The Y coordinate * @return True iff the position is inside the moderator-rod area, otherwise false */ private boolean isInsideModerator(int x, int y) { return x > 25 && x < 120 && y > 102+moderatorPercent*3/4 && y < 102+moderatorPercent*3/4 + 80; } /** Handle mouseDown events (i.e., clicks on moderator rods). * @param x The X coordinate * @param y The Y coordinate */ public boolean mouseDown(Event evt, int x, int y) { if (isInsideModerator(x, y)) { y0 = y; } return true; } /** Handle mouseDrag events. This method allows the user to drag moderator rods * @param x The X coordinate * @param y The Y coordinate */ public boolean mouseDrag(Event evt, int x, int y) { if (y0 > 0) { moderatorOutlinePos = 102+moderatorPercent*3/4 - (y0 - y); if (moderatorOutlinePos < 97) moderatorOutlinePos = 97; if (moderatorOutlinePos > 177) moderatorOutlinePos = 177; } return true; } /** Handle mouseUp events. This method sets the moderatorPercent * @param x The X coordinate * @param y The Y coordinate */ public boolean mouseUp(Event evt, int x, int y) { if (y0 > 0) { moderatorPercent = (moderatorOutlinePos - 97) * 5/4; } moderatorOutlinePos = 0; y0 = 0; return true; } } /** * A valve component for the nuclear power plant * * @version 1.0f * @author Henrik Eriksson */ class Valve extends Component { /** True iff the valve is open */ boolean status = false; /** Valve image */ protected static Image ventil_o_bm, ventil_s_bm; /** Construct a valve and initialize it. * @param l The valve label * @param xPos The X coordinate * @param yPos The Y coordinate */ Valve(String l, int xPos, int yPos) { x = xPos; y = yPos; label = l; ventil_o_bm = getImage(plant.getCodeBase(), "VENTIL.O_BM.GIF", ventil_o_bm); ventil_s_bm = getImage(plant.getCodeBase(), "VENTIL.S_BM.GIF", ventil_s_bm); } /** Set the value of a slot * @param slot The slot name * @param val The value */ void setValue(String slot, boolean val) { if (slot.equals("status")) status = val; else super.setValue(slot, val); } /** Paint operation for valve * @param g The graphics context */ public void paint(Graphics g) { if (status) g.drawImage(ventil_o_bm, x, y, plant); else g.drawImage(ventil_s_bm, x, y+6, plant); g.drawString(label, x, y+40); } /** Handle mouseDown events (i.e., clicks on the valve). * @param mx The X coordinate * @param my The Y coordinate */ public boolean mouseDown(Event evt, int mx, int my) { if (mx > x && mx < x+20 && my > y && my < y+40) status = !status; return true; } } /** * A pump component for the nuclear power plant * * @version 1.0f * @author Henrik Eriksson */ class Pump extends Component { /** The state of the pump (0=crashed) */ int status = 1; /** The pump rpm */ int rpm; /** Maximum pump rpm */ int full_rpm; /** Pump images for amimation */ protected static Image pump_bm[] = new Image[6]; /** Crashed pump image */ protected static Image pump_crash_bm; /** Construct a pump and initialize it. * @param l The pump label * @param xPos The X coordinate * @param yPos The Y coordinate * @param maxRpm The maximum pump rpm */ Pump(String l, int xPos, int yPos, int maxRpm) { x = xPos; y = yPos; label = l; full_rpm = maxRpm; pump_bm[0] = getImage(plant.getCodeBase(), "PUMP.A_BM.GIF", pump_bm[0]); pump_bm[1] = getImage(plant.getCodeBase(), "PUMP.B_BM.GIF", pump_bm[1]); pump_bm[2] = getImage(plant.getCodeBase(), "PUMP.C_BM.GIF", pump_bm[2]); pump_bm[3] = getImage(plant.getCodeBase(), "PUMP.D_BM.GIF", pump_bm[3]); pump_bm[4] = getImage(plant.getCodeBase(), "PUMP.E_BM.GIF", pump_bm[4]); pump_bm[5] = getImage(plant.getCodeBase(), "PUMP.F_BM.GIF", pump_bm[5]); pump_crash_bm = getImage(plant.getCodeBase(), "PUMP.CRASH_BM.GIF", pump_crash_bm); } /** Set the value of a slot * @param slot The slot name * @param val The value */ void setIntValue(String slot, int val) { if (slot.equals("status")) status = val; else if (slot.equals("rpm")) rpm = val; else super.setIntValue(slot, val); } /** Blow up the pump */ void blow() { plant.crashSound(800); plant.getAppletContext().showStatus(label + " crashed"); status = 0; rpm = 0; } /** Rotate the pump one step. (Only pumps with rpm > 0 are rotated.) */ public void rotate() { if (rpm > 0 && status != 0) status++; if (status > 6) status = 1; } /** Paint operation for pump * @param g The graphics context */ public void paint(Graphics g) { if (status == 0) g.drawImage(pump_crash_bm, x, y, plant); else g.drawImage(pump_bm[status-1], x, y, plant); g.drawString(rpm+" rpm", x+4, y-2); } /** Handle mouseDown events (i.e., clicks on the pump). * @param mx The X coordinate * @param my The Y coordinate */ public boolean mouseDown(Event evt, int mx, int my) { if (mx > x && mx < x+30 && my > y && my < y+30) if (status != 0) if (rpm == 0) rpm = full_rpm; else rpm = 0; return true; } } /** * Turbine component for the nuclear power plant * * @version 1.0f * @author Henrik Eriksson */ class Turbine extends Component { /** Image of crash turbine */ protected static Image crashed_turbine_bm; /** Blow up the turbine */ void blow() { blown = true; } /** Count down for blow up animation */ private byte blow_countdown = -1; /** Paint operation for turbine * @param g The graphics context */ public void paint(Graphics g) { if (blown && blow_countdown < 0) { blow_countdown = 20; } else if (blown && blow_countdown < 10) { crashed_turbine_bm = getImage(plant.getCodeBase(), "CRASHED_TURBIN_BM.GIF",crashed_turbine_bm); g.drawImage(crashed_turbine_bm, 403, 73, plant); if (blow_countdown == 8) { plant.getAppletContext().showStatus("The turbine crashed"); plant.crashSound(8000); } } if (blown && blow_countdown > 0) { blow_countdown--; g.copyArea(403, 95, 145, 65, 403+rand(-2,2), 95+rand(-4,2)); } else if (!blown) blow_countdown = -1; } } /** * Condenser component for the nuclear power plant * * @version 1.0f * @author Henrik Eriksson */ class Condenser extends Tank { /** Water (surface) images for animation */ protected static Image vatten_bm[] = new Image[3]; /** Image of cooling pipe */ protected static Image kylror_bm; /** Image of crashed condenser */ protected static Image crashed_kondensor_bm; /** Construct a condenser and initialize it. */ Condenser() { vatten_bm[0] = getImage(plant.getCodeBase(), "K_VATTEN_A_BM.GIF", vatten_bm[0]); vatten_bm[1] = getImage(plant.getCodeBase(), "K_VATTEN_B_BM.GIF", vatten_bm[1]); vatten_bm[2] = getImage(plant.getCodeBase(), "K_VATTEN_C_BM.GIF", vatten_bm[2]); kylror_bm = getImage(plant.getCodeBase(), "KYLROR_BM.GIF", kylror_bm); label = "condenser"; waterLevel = 6000; } /** Advance the water-surface animation one step */ void waterWave () { if (++imageState >= vatten_bm.length) imageState = 0; } /** The color of water */ private static Color waterColor = new Color(127,193,255); /** Paint operation for condenser * @param g The graphics context */ public void paint(Graphics g) { if (pressure > 200) g.setColor(Color.red); g.drawString((int)pressure+" bar", 520, 250); g.setColor(waterColor); int surfaceY = 440 - (int)waterLevel / 50; g.fillRect(483, surfaceY+1, 108, 449-surfaceY); g.drawImage(kylror_bm, 500, 349, plant); g.setColor(Color.black); g.drawImage(vatten_bm[imageState], 483, surfaceY, plant); paintBubbles(g, 483, surfaceY, 590, 15, 10); g.drawString("Level:", 615, 290); g.drawString((int)waterLevel+" mm", 615, 305); if (blown) { crashed_kondensor_bm = getImage(plant.getCodeBase(), "CRASHED_KONDENSOR_BM.GIF",crashed_kondensor_bm); g.drawImage(crashed_kondensor_bm, 570, 214, plant); } } } /** * Generator component for the nuclear power plant * * @version 1.0f * @author Henrik Eriksson */ class Generator extends Component { /** Output generator power (in MW) */ int power; /** Set the value of a slot * @param slot The slot name * @param val The value */ void setIntValue(String slot, int val) { if (slot.equals("power")) power = val; else super.setIntValue(slot, val); } /** Paint operation for generator * @param g The graphics context */ public void paint(Graphics g) { g.drawString(power+" MW", 570, 95); } } /** * The class Simulator is an abstact class for power-plant * simulators. The actual simulators are sumclasses of this class. * * @see NuclearPlant * @version 1.0f * @author Henrik Eriksson */ abstract class Simulator { /** The plant */ protected NuclearPlant plant; abstract void start(); abstract void timeStep(int n); abstract void blow(Component device); abstract void stop(); } /** * The class LocalSimulatorServer is a test class that simulates the * behavior of the simulator server. It is used for testing the * java client applet and its user interface. * * @see TestSimulatorServer * @see ClipsSimulatorServer * @version 1.0f * @author Henrik Eriksson */ class LocalSimulator extends Simulator { /** The reactor and its components */ protected Reactor reactor; /** Valve object */ protected Valve valve_1, valve_2, valve_3, valve_4; /** Pump object */ protected Pump pump_1, pump_2, pump_3; /** The turbine */ protected Turbine turbine; /** The condenser */ protected Condenser condenser; /** The generator */ protected Generator generator; /** Constructor for LocalSimulator. Wires objects based on the * NuclearPlant components. * @param p The current NuclearPlant object */ public LocalSimulator(NuclearPlant p) { plant = p; reactor = p.reactor; valve_1 = p.valve_1; valve_2 = p.valve_2; valve_3 = p.valve_3; valve_4 = p.valve_4; pump_1 = p.pump_1; pump_2 = p.pump_2; pump_3 = p.pump_3; turbine = p.turbine; condenser = p.condenser; generator = p.generator; } /** Starts the local simulator. This method initializes * the simulation variables (i.e., the plant is reset * to steady state). */ public void start() { reactor.pressure = 288; reactor.moderatorPercent = 50; reactor.waterLevel = 1800; reactor.blown = false; reactor.overheated = false; turbine.blown = false; condenser.pressure = 40; condenser.waterLevel = 6000; condenser.blown = false; generator.power = 622; pump_1.status = 1; pump_1.rpm = 1400; pump_2.status = 1; pump_2.rpm = 0; pump_3.status = 1; pump_3.rpm = 1285; pump_1.blown = false; pump_2.blown = false; pump_3.blown = false; valve_1.status = true; valve_2.status = false; valve_3.status = true; valve_4.status = false; } /** Calculates the next state of the simulation. This * calculation is perfomed locally in java. * @param n The number of steps to calculate before returning */ public void timeStep(int n) { float v1, v2, v3, v4; for (int i = 0; i < n; i++) { if (!reactor.overheated) { // Compute the flow through valve_1... if (valve_1.status) v1 = (reactor.pressure-condenser.pressure) / 10; else v1 = 0; // Compute the flow through valve_2... if (valve_2.status) v2 = (reactor.pressure-condenser.pressure) / 2.5f; else v2 = 0; // Compute the flow through valve_3 and pump_1... if (valve_3.status) if (pump_1.rpm > 0) if (condenser.waterLevel > 0) v3 = pump_1.rpm * 0.07f; else v3 = 0; else v3 = -30; else v3 = 0; // Compute the flow through valve_4 and pump_2... if (valve_4.status) if (pump_2.rpm > 0) if (condenser.waterLevel > 0) v4 = pump_2.rpm * 0.07f; else v4 = 0; else v4 = -30; else v4 = 0; // Scale the flow levels to allow frequent time steps // (smother animation) float factor = 0.5f; v1 *= factor; v2 *= factor; v3 *= factor; v4 *= factor; // Compute new values for pressure and water levels... float boiledRW = (100 - reactor.moderatorPercent)*2*(900 - reactor.pressure)/620; boiledRW *= factor; float cooledKP = (float)(pump_3.rpm * Math.sqrt(condenser.pressure) * 0.003f); cooledKP *= factor; float newRP = reactor.pressure - v1 - v2 + boiledRW/4; // The steam flow to the condenser stops if the // turbine is blown... if (turbine.blown) v1 = 0; // Compute new values for pressure and water levels... float newKP = condenser.pressure + v1 + v2 - cooledKP; float newRW = reactor.waterLevel + v3 + v4 - boiledRW; float newKW = condenser.waterLevel - v3 - v4 + 4*cooledKP; // Make adjustments for blown tanks... if (reactor.blown) newRP = 0.15f * newRP; if (condenser.blown) newKP = 0.2f * newKP; // Check the computed values for illegal values... if (newKW < 0) newKW = 0; if (newKW > 9900) newKW = 9900; if (newRW > 6000) newRW = 6000; if (newKP < 0) newKP = 0; if (newKP > 300) newKP = 300; if (newRP > 800) newRP = 800; // Adjust the generator power... float newEffect; if (valve_1.status && !turbine.blown) newEffect = (newRP - newKP) * 2.5f; else newEffect = 0; // Assign the computed values... generator.power = (int)newEffect; condenser.pressure = newKP; condenser.waterLevel = newKW; reactor.pressure = newRP; reactor.waterLevel = newRW; // Rules for the plant... if (pump_1.blown) pump_1.rpm = 0; if (pump_2.blown) pump_2.rpm = 0; if (pump_3.blown) pump_3.rpm = 0; if (reactor.waterLevel < -1500) reactor.meltdown(); if (reactor.pressure >= 610) reactor.blow(); if (condenser.pressure >= 225) condenser.blow(); // R1 (safety rule for blown turbine)... /* Disabled for now if (turbine.blown) { valve_1.status = false; valve_2.status = true; reactor.moderatorPercent = 100; pump_1.rpm = 0; valve_3.status = false; } */ } // if try {Thread.sleep(200); } catch (InterruptedException e){} } // for } /** Blow up a device. * @param device The device to blow (e.g., a turbine object) */ public void blow(Component device) { device.blow(); } /** Stop the simulation */ public void stop() { } }