import java.awt.Image; import java.awt.image.ImageObserver; //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.awt.event.*; import java.awt.*; import java.net.URL; import java.net.*; import java.io.DataInputStream; import java.io.DataOutputStream; import java.util.StringTokenizer; import java.applet.*; import java.net.Socket; // Updated by Erik Berglund import java.net.InetAddress; import java.io.IOException; import java.util.NoSuchElementException; import java.sql.*; public class NuclearPlant extends java.applet.Applet implements Runnable { NuclearPlant plant; /** The thread that updates the display (for animation) */ protected Thread kicker = null; /** The thread that runs the sequences */ protected Thread buttonThread; /** The thread that runs the simulation */ public Thread simulationThread; /** 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; /** Panel for the sequence control buttons */ Panel razPanel, controlPanel, sequencePanel, textPanel ; Stop_Button stopButton; /** Flag that is true if a simulation is running */ boolean isRunningSimulation = true; /** 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(690,473); // java.applet.Applet size im = createImage(690,800); offscreen = im.getGraphics(); offscreen.setColor(Color.black); background = getImage(getCodeBase(), imageDir + "BACKGROUND.GIF"); System.out.println(getCodeBase()); 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("Pompe 1", 406, 389, 1400); pump_2 = new Pump("Pompe 2", 406, 427, 1400); pump_3 = new Pump("Pompe 3", 619, 421, 1285); turbine = new Turbine(); condenser = new Condenser(); generator = new Generator(); // Button Photos = new Button("Photos"); setLayout(new BorderLayout()); sequencePanel = new Panel(); sequencePanel.setLayout(new FlowLayout()); sequencePanel.add(new Seq_1_Button(this, "Incident 1")); sequencePanel.add(new Seq_2_Button(this, "Incident 2")); sequencePanel.add(new Seq_3_Button(this, "Incident 3")); sequencePanel.add(new Rand_Button(this, "Aléatoire")); // textPanel = new Panel(); // String commentaire=new String("coucou"); // TextArea tf = new TextArea(commentaire,2,50,0); // tf.setEditable(false); // textPanel.add(new Panel(commentaire)); controlPanel = new Panel(); controlPanel.setLayout(new FlowLayout()); // controlPanel.add("North",textPanel); controlPanel.add("South",sequencePanel); razPanel = new Panel(); stopButton = new Stop_Button(this, "RaZ"); //stopButton.disable(); razPanel.add(stopButton); add("North", razPanel); add("South", controlPanel); if (getParameter("server") == null) simulator = new LocalSimulator(this); else if (getParameter("server").equals("localhost")) try { simulator = new RemoteSimulatorClient(this,InetAddress.getLocalHost().getHostName(), Integer.parseInt(getParameter("port"))); } catch (java.net.UnknownHostException e) { getAppletContext().showStatus("Cannot get the name of the local host"); return; } else simulator = new RemoteSimulatorClient(this, getParameter("server"), Integer.parseInt(getParameter("port"))); } /** 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,30,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, 50, 440); } else reactor.paintMeltdown(offscreen); Thread.yield(); g.drawImage(im,0,0, this); } /** Update without erasing background * @param g Graphics context to update in */ public void update(Graphics g) { paint(g); } public boolean action(Event evt, Object arg) { // if ("Photos".equals(arg)) { // try { // System.out.println(getDocumentBase()); // URL url = new URL(getDocumentBase(),"ImageCentrale.html"); // getAppletContext().showDocument(url,"_top"); // } catch (MalformedURLException e) {} // } return true; } /** 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); if (isRunningSimulation) { if (simulator instanceof LocalSimulator) { 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); this.stopButton.enable(); getAppletContext().showStatus("Simulation en cours..."); } } else getAppletContext().showStatus("débute la simulation en cliquant sur un bouton sequence"); 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); } public boolean mouseMove(Event evt, int x, int y) { // if (x >42 && x<142 && y>290 && y<310) { // getAppletContext().showStatus("Photos de reacteur"); // setCursor(new Cursor(Cursor.HAND_CURSOR)); // } else if (x >520 && x<620 && y>460 && y<480) { // getAppletContext().showStatus("Photos de condenseur"); // setCursor(new Cursor(Cursor.HAND_CURSOR)); // } else if (x >395 && x<405 && y>60 && y<89) { // getAppletContext().showStatus("Valve 1"); // setCursor(new Cursor(Cursor.HAND_CURSOR)); // } else if (x >395 && x<405 && y>180 && y<209) { // getAppletContext().showStatus("Valve 2"); // setCursor(new Cursor(Cursor.HAND_CURSOR)); // } else if (x >420 && x<430 && y>383 && y<410) { // getAppletContext().showStatus("Valve 3"); // setCursor(new Cursor(Cursor.HAND_CURSOR)); // } else if (x >420 && x<430 && y>421 && y<450) { // getAppletContext().showStatus("Valve 4"); // setCursor(new Cursor(Cursor.HAND_CURSOR)); // } else if (x >656 && x<672 && y>426 && y<442) { // getAppletContext().showStatus("Pompe 1"); // setCursor(new Cursor(Cursor.HAND_CURSOR)); // } else if (x >443 && x<460 && y>392 && y<412) { // getAppletContext().showStatus("Pompe 2"); // setCursor(new Cursor(Cursor.HAND_CURSOR)); // } else if (x >443 && x<460 && y>430 && y<450) { // getAppletContext().showStatus("Pompe 3"); // setCursor(new Cursor(Cursor.HAND_CURSOR)); // } else if (x>52 && x<147 && y>120 && y<255) { // getAppletContext().showStatus("Reacteur"); // setCursor(new Cursor(Cursor.HAND_CURSOR)); // } else if (x >592 && x<630 && y>102 && y<152) // getAppletContext().showStatus("Generateur"); // else if (x >440 && x<570 && y>100 && y<140) // getAppletContext().showStatus("Turbine"); // else { // getAppletContext().showStatus("Simulation en cours "); // setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); // } return true; } /** 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("Simulation en cours..."); simulator.start(); } /** Run the simulation n steps. Uses the current * 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("Simulation réinitialisée"); simulator.stop(); } /** Play a crash sound. This method is called when plant components blow up. * @param n Duration */ public void crashSound(int n) { if (n < 1000) { play(getCodeBase(), "audio/breakdown.au"); System.out.println(getCodeBase()); } else if (n < 10000) play(getCodeBase(), "audio/Explosion-2.au"); else play(getCodeBase(), "audio/Explosion-1.au"); } /** Run the animation thread. This method is called when the animation thread is started. */ public void run() { //int n = 400; //this.startReactor(); //for (int i=0; i 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 { Graphics g; /** 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 water_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() { water_bm[0] = getImage("R_VATTEN_A_BM.GIF", water_bm[0]); water_bm[1] = getImage("R_VATTEN_B_BM.GIF", water_bm[1]); water_bm[2] = getImage("R_VATTEN_C_BM.GIF", water_bm[2]); crashed_reactor_bm = getImage("CRASHED_REACTOR_BM.GIF", crashed_reactor_bm); radiak_bm = getImage("RADIAK_BM.GIF", radiak_bm); label = "reacteur"; 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 >= water_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("Le " + label + " a fondu "); } /** 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, 2000, 150, plant);//170 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(128,192,255); /** different colors of pressure */ private static Color greenPressure = new Color(0,215,0); private static Color orangePressure = new Color(255,150,0); private static Color redPressure = new Color(255,0,0); /** Paint operation for the reactor tank * @param g The graphics context */ public void paint(Graphics g) { int surfaceP1 = ((int)pressure*67)/400; g.setColor(greenPressure); g.drawRect(47, 70, 67, 15); g.setColor(orangePressure); g.drawRect(114, 70, 17, 15); g.setColor(redPressure); g.drawRect(131, 70, 18, 15); if (pressure < 400) { g.setColor(greenPressure); g.fillRect(47, 70, surfaceP1, 15); } if (pressure > 400) { int surfaceP2 = (((int)pressure - 400) *17)/100; g.setColor(greenPressure); g.fillRect(47, 70, 67, 15); g.setColor(orangePressure); g.fillRect(114, 70, surfaceP2, 15); } if (pressure > 500) { int surfaceP3 = (((int)pressure -500)*16)/100; g.setColor(greenPressure); g.fillRect(47, 70, 67, 15); g.setColor(orangePressure); g.fillRect(114, 70, 17, 15); g.setColor(redPressure); g.fillRect(131, 70, surfaceP3, 15); } g.setColor(Color.black); g.drawString((int)pressure+" bar", 65, 83); int surfaceY = 175-(int)waterLevel/50; g.setColor(waterColor); g.fillRect(33, surfaceY+1, 135, 279-surfaceY); g.setColor(Color.black); g.drawImage(water_bm[imageState], 33, surfaceY, plant); g.drawString("Niveau:", 190, 110); if (waterLevel < 0) g.setColor(Color.red); g.drawString((int)waterLevel+" mm", 190, 125); g.setColor(Color.gray); for (int i = 0; i < 7; i++) g.fillRect(63 + i*12, 102+moderatorPercent*3/4, 3, 80); if (moderatorOutlinePos > 0) { g.setColor(Color.yellow); for (int i = 0; i < 7; i++) g.drawRect(63 + 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(55 + i*12, 177, 7, 80); if (waterLevel < 0) { g.setColor(overheatedColor); for (int i = 0; i < 8; i++) g.fillRect(55 + i*12, 177, 7, Math.max(-(int)waterLevel/50-1, 0)); } g.setColor(Color.black); if (waterLevel > 0) paintBubbles(g, 43, surfaceY, 137, 50, 50); else paintBubbles(g, 43, surfaceY, 137, 60, 100); if (blown) { g.drawImage(crashed_reactor_bm, 60, 3, plant); g.drawImage(radiak_bm, 200, 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 > 55 && x < 150 && 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 && moderatorOutlinePos > 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("VENTIL.O_BM.GIF", ventil_o_bm); ventil_s_bm = getImage("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+30, y, plant); else g.drawImage(ventil_s_bm, x+30, y+6, plant); // g.drawString(label, x+30, 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+30 && mx < x+50 && 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("PUMP.A_BM.GIF", pump_bm[0]); pump_bm[1] = getImage("PUMP.B_BM.GIF", pump_bm[1]); pump_bm[2] = getImage("PUMP.C_BM.GIF", pump_bm[2]); pump_bm[3] = getImage("PUMP.D_BM.GIF", pump_bm[3]); pump_bm[4] = getImage("PUMP.E_BM.GIF", pump_bm[4]); pump_bm[5] = getImage("PUMP.F_BM.GIF", pump_bm[5]); pump_crash_bm = getImage("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 + " cassée"); 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+30, y, plant); else g.drawImage(pump_bm[status-1], x+30, y, plant); g.drawString(rpm+" rpm", x+34, 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+30 && mx < x+60 && 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; /** Construct a turbine and initialize it. */ Turbine() { crashed_turbine_bm = getImage("CRASHED_TURBIN_BM.GIF", 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) { g.drawImage(crashed_turbine_bm, 433, 73, plant); if (blow_countdown == 8) { plant.getAppletContext().showStatus("La turbine est cassée"); 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 water_bm[] = new Image[3]; /** Image of cooling pipe */ protected static Image kylror_bm; /** Image of crashed condenser */ protected static Image crashed_condenser_bm; /** Construct a condenser and initialize it. */ Condenser() { water_bm[0] = getImage("K_VATTEN_A_BM.GIF", water_bm[0]); water_bm[1] = getImage("K_VATTEN_B_BM.GIF", water_bm[1]); water_bm[2] = getImage("K_VATTEN_C_BM.GIF", water_bm[2]); crashed_condenser_bm = getImage("CRASHED_KONDENSOR_BM.GIF",crashed_condenser_bm); kylror_bm = getImage("KYLROR_BM.GIF", kylror_bm); label = "condenseur"; waterLevel = 6000; } /** Advance the water-surface animation one step */ void waterWave () { if (++imageState >= water_bm.length) imageState = 0; } /** The color of water */ private static Color waterColor = new Color(128,192,255); /** different colors of pressure */ private static Color greenPressure = new Color(0,215,0); private static Color orangePressure = new Color(255,200,0); private static Color redPressure = new Color(255,0,0); /** Paint operation for condenser * @param g The graphics context */ public void paint(Graphics g) { int surfaceP1 = ((int)pressure*50)/150; g.setColor(greenPressure); g.drawRect(523, 245, 50, 15); g.setColor(orangePressure); g.drawRect(573, 245, 17, 15); g.setColor(redPressure); g.drawRect(590, 245, 8, 15); if (pressure < 150) { g.setColor(greenPressure); g.fillRect(523, 245, surfaceP1, 15); } if (pressure > 150) { int surfaceP2 = (((int)pressure - 150) *17)/50; g.setColor(greenPressure); g.fillRect(523, 245, 50, 15); g.setColor(orangePressure); g.fillRect(573, 245, surfaceP2, 15); } if (pressure > 200) { int surfaceP3 = (((int)pressure -500)*16)/100; g.setColor(greenPressure); g.fillRect(523, 245, 50, 15); g.setColor(orangePressure); g.fillRect(573, 245, 17, 15); g.setColor(redPressure); g.fillRect(590, 245, surfaceP3, 15); } g.setColor(Color.black); g.drawString((int)pressure+" bar", 530, 259); g.setColor(waterColor); int surfaceY = 440 - (int)waterLevel / 50; g.fillRect(513, surfaceY+1, 108, 449-surfaceY); g.drawImage(kylror_bm, 530, 349, plant); g.setColor(Color.black); g.drawImage(water_bm[imageState], 513, surfaceY, plant); paintBubbles(g, 513, surfaceY, 590, 12, 8); g.drawString("Niveau:", 637, 290); g.drawString((int)waterLevel+" mm", 637, 305); if (blown) { g.drawImage(crashed_condenser_bm, 600, 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 subclasses 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 raz(); 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 > 9600) newKW = 9600; if (newRW > 4700) newRW = 4700; 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 (condenser.waterLevel <= 0 && pump_1.rpm > 0) pump_1.blow(); if (condenser.waterLevel <= 0 && pump_2.rpm > 0) pump_2.blow(); 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 Thread.yield(); try { Thread.sleep(100); } catch (InterruptedException e) {} Thread.yield(); try { Thread.sleep(100); } catch (InterruptedException e) {} Thread.yield(); } // 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 raz() { 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; } /** Stop the simulation */ public void stop() { 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; } } /** * The class RemoteSimulatorClient is a client class that controls a * simulator server remotely. The class is a wrapper for the * communication with the server. * * @see RemoteSimulatorServer * @see TestSimulatorServer * @see ClipsSimulatorServer * @version 1.0 * @author Henrik Eriksson */ class RemoteSimulatorClient extends Simulator { /** The socket object */ // protected NetworkClient nc; protected Socket socket; /** The output TCP/IP stream */ protected DataOutputStream so; /** The input TCP/IP stream (back stream) */ protected DataInputStream si; /** The internet address of the server host */ protected String server; /** The socket number */ protected int port; /** Construct a RemoteSimulatorClient and initialize it. * @param p the NuclearPlant applet * @param s the server internet address (or localhost) * @param prt the socket number (port number) to connect to */ public RemoteSimulatorClient(NuclearPlant p, String s, int prt) { plant = p; server = s; port = prt; } /** Start the remote simulator. This method opens a TCP/IP stream to the simulator server, and sends a "start" message to the server. */ synchronized void start() { try { socket = new Socket(server,port); so = new DataOutputStream(socket.getOutputStream()); si = new DataInputStream(socket.getInputStream()); so.writeUTF("start"); so.flush(); readValue(); } catch (IOException e) { plant.showStatus("Cannot connect to simulator server"); } } /** Translate a string to a component object. * @param obj the string representing the component object * @return The component object */ protected Component decodeComponent(String obj) { if (obj.equals("reactor")) return plant.reactor; else if (obj.equals("turbine")) return plant.turbine; else if (obj.equals("condenser")) return plant.condenser; else if (obj.equals("generator")) return plant.generator; else if (obj.equals("pump_1")) return plant.pump_1; else if (obj.equals("pump_2")) return plant.pump_2; else if (obj.equals("pump_3")) return plant.pump_3; else if (obj.equals("valve_1")) return plant.valve_1; else if (obj.equals("valve_2")) return plant.valve_2; else if (obj.equals("valve_3")) return plant.valve_3; else if (obj.equals("valve_4")) return plant.valve_4; return null; } /** Read a value from the simulator server */ protected synchronized void readValue() { try { for (;;) { if (socket==null) return; String s = si.readUTF(); char type = si.readChar(); if (type == 'r') return; if (type == 'x') { if (s.equals("blow")) { String device = si.readUTF(); Component comp = decodeComponent(device); if (comp != null) comp.blow(); } continue; } StringTokenizer st = new StringTokenizer(s, "."); try { String obj = st.nextToken(); String slot = st.nextToken(); Component comp = decodeComponent(obj); if (comp != null) { switch (type) { case 'i': { comp.setIntValue(slot, si.readInt()); // 980209 Changed setValue to setIntValue (bug found by Erik Berglund) break; } case 'f': { comp.setValue(slot, si.readFloat()); break; } case 's': { comp.setValue(slot, si.readUTF()); break; } case 'b': { comp.setValue(slot, si.readBoolean()); break; } case 'r': return; } } } catch (NoSuchElementException e) { return; } } // for } catch (IOException e) { return; } } /** Close the simulator server */ public synchronized void close() { if (socket==null) return; try { so.writeUTF("stop"); so.flush(); so.close(); si.close(); socket.close(); } catch (IOException e) { return; } } /** Stop the simulation */ public void raz() {} /** Stop the remote simulation */ public void stop() { close(); } /** Calculates the next state of the simulation. This * calculation is perfomed by the simulator server. The * results are then returned and received by the method * readValue(). * @param n The number of steps to calculate before returning */ public synchronized void timeStep(int n) { for (int i = 0; i < n; i++) { if (socket==null) return; try { so.writeUTF("timeStep"); so.flush(); } catch (IOException e) { return; } readValue(); try { Thread.sleep(200); } catch (InterruptedException e) {} // Time delay for each tick } } /** Blow up a device. This method sends a "blow" message to the simulator server. * @param device The device to blow (e.g., a turbine object) */ public void blow(Component device) { device.blow(); try { so.writeUTF("blow"); if (device == plant.turbine) so.writeUTF("turbine"); else if (device == plant.reactor) so.writeUTF("reactor"); else if (device == plant.condenser) so.writeUTF("condenser"); else if (device == plant.pump_1) so.writeUTF("pump_1"); else if (device == plant.pump_2) so.writeUTF("pump_2"); else if (device == plant.pump_3) so.writeUTF("pump_3"); else so.writeUTF(""); so.flush(); } catch (IOException e) { return; } } }