/*******************************************************************************
* This file is part of logisim-evolution.
*
* logisim-evolution is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* logisim-evolution is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with logisim-evolution. If not, see <http://www.gnu.org/licenses/>.
*
* Original code by Carl Burch (http://www.cburch.com), 2011.
* Subsequent modifications by :
* + Haute École Spécialisée Bernoise
* http://www.bfh.ch
* + Haute École du paysage, d'ingénierie et d'architecture de Genève
* http://hepia.hesge.ch/
* + Haute École d'Ingénierie et de Gestion du Canton de Vaud
* http://www.heig-vd.ch/
* The project is currently maintained by :
* + REDS Institute - HEIG-VD
* Yverdon-les-Bains, Switzerland
* http://reds.heig-vd.ch
*******************************************************************************/
package com.cburch.logisim.circuit;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.cburch.logisim.comp.ComponentDrawContext;
import com.cburch.logisim.prefs.AppPreferences;
public class Simulator {
class PropagationManager extends Thread {
private Propagator propagator = null;
private PropagationPoints stepPoints = new PropagationPoints();
private volatile int ticksRequested = 0;
private volatile int stepsRequested = 0;
private volatile boolean resetRequested = false;
private volatile boolean propagateRequested = false;
private volatile boolean complete = false;
// These variables apply only if PRINT_TICK_RATE is set
int tickRateTicks = 0;
long tickRateStart = System.currentTimeMillis();
private void doTick() {
synchronized (this) {
ticksRequested--;
}
propagator.tick();
}
public Propagator getPropagator() {
return propagator;
}
public synchronized void requestPropagate() {
if (!propagateRequested) {
propagateRequested = true;
notifyAll();
}
}
public synchronized void requestReset() {
if (!resetRequested) {
resetRequested = true;
notifyAll();
}
}
public synchronized void requestTick() {
if (ticksRequested < 16) {
ticksRequested++;
}
notifyAll();
}
@Override
public void run() {
while (!complete) {
synchronized (this) {
while (!complete && !propagateRequested && !resetRequested
&& ticksRequested == 0 && stepsRequested == 0) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
if (resetRequested) {
resetRequested = false;
if (propagator != null) {
propagator.reset();
}
firePropagationCompleted();
propagateRequested |= isRunning;
}
if (propagateRequested || ticksRequested > 0
|| stepsRequested > 0) {
boolean ticked = false;
propagateRequested = false;
if (isRunning) {
stepPoints.clear();
stepsRequested = 0;
if (propagator == null) {
ticksRequested = 0;
} else {
ticked = ticksRequested > 0;
if (ticked) {
doTick();
}
do {
propagateRequested = false;
try {
exceptionEncountered = false;
propagator.propagate();
} catch (UnsupportedOperationException thr) {
exceptionEncountered = true;
setIsRunning(false);
} catch (Exception thr) {
thr.printStackTrace();
exceptionEncountered = true;
setIsRunning(false);
}
} while (propagateRequested);
if (isOscillating()) {
setIsRunning(false);
ticksRequested = 0;
propagateRequested = false;
}
}
} else {
if (stepsRequested > 0) {
if (ticksRequested > 0) {
ticksRequested = 1;
doTick();
}
synchronized (this) {
stepsRequested--;
}
exceptionEncountered = false;
try {
stepPoints.clear();
propagator.step(stepPoints);
} catch (Exception thr) {
thr.printStackTrace();
exceptionEncountered = true;
}
}
}
if (ticked) {
fireTickCompleted();
}
firePropagationCompleted();
}
}
}
public void setPropagator(Propagator value) {
propagator = value;
}
public synchronized void shutDown() {
complete = true;
notifyAll();
}
}
private boolean isRunning = true;
private boolean isTicking = false;
private boolean exceptionEncountered = false;
private double tickFrequency = 1.0;
private PropagationManager manager;
private SimulatorTicker ticker;
private ArrayList<SimulatorListener> listeners = new ArrayList<SimulatorListener>();
public Simulator() {
manager = new PropagationManager();
ticker = new SimulatorTicker(manager);
try {
manager.setPriority(manager.getPriority() - 1);
ticker.setPriority(ticker.getPriority() - 1);
} catch (SecurityException e) {
} catch (IllegalArgumentException e) {
}
manager.start();
ticker.start();
tickFrequency = 0.0;
setTickFrequency(AppPreferences.TICK_FREQUENCY.get().doubleValue());
}
public void addSimulatorListener(SimulatorListener l) {
listeners.add(l);
}
public void drawStepPoints(ComponentDrawContext context) {
manager.stepPoints.draw(context);
}
void firePropagationCompleted() {
SimulatorEvent e = new SimulatorEvent(this);
for (SimulatorListener l : new ArrayList<SimulatorListener>(listeners)) {
l.propagationCompleted(e);
}
}
void fireSimulatorStateChanged() {
SimulatorEvent e = new SimulatorEvent(this);
for (SimulatorListener l : new ArrayList<SimulatorListener>(listeners)) {
l.simulatorStateChanged(e);
}
}
void fireTickCompleted() {
SimulatorEvent e = new SimulatorEvent(this);
for (SimulatorListener l : new ArrayList<SimulatorListener>(listeners)) {
l.tickCompleted(e);
}
}
public CircuitState getCircuitState() {
Propagator prop = manager.getPropagator();
return prop == null ? null : prop.getRootState();
}
public double getTickFrequency() {
return tickFrequency;
}
public boolean isExceptionEncountered() {
return exceptionEncountered;
}
public boolean isOscillating() {
Propagator prop = manager.getPropagator();
return prop != null && prop.isOscillating();
}
public boolean isRunning() {
return isRunning;
}
public boolean isTicking() {
return isTicking;
}
public void removeSimulatorListener(SimulatorListener l) {
listeners.remove(l);
}
private void renewTickerAwake() {
ticker.setAwake(isRunning && isTicking && tickFrequency > 0);
}
public void requestPropagate() {
manager.requestPropagate();
}
public void requestReset() {
manager.requestReset();
}
public void setCircuitState(CircuitState state) {
manager.setPropagator(state.getPropagator());
renewTickerAwake();
}
public void setIsRunning(boolean value) {
if (isRunning != value) {
isRunning = value;
renewTickerAwake();
/*
* DEBUGGING - comment out: if (!value) flushLog(); //
*/
fireSimulatorStateChanged();
}
}
public void setIsTicking(boolean value) {
if (isTicking != value) {
isTicking = value;
renewTickerAwake();
fireSimulatorStateChanged();
}
}
public void setTickFrequency(double freq) {
if (tickFrequency != freq) {
int millis = (int) Math.round(1000 / freq);
int ticks;
if (millis > 0) {
ticks = 1;
} else {
millis = 1;
ticks = (int) Math.round(freq / 1000);
}
tickFrequency = freq;
ticker.setTickFrequency(millis, ticks);
renewTickerAwake();
fireSimulatorStateChanged();
}
}
public void shutDown() {
ticker.shutDown();
manager.shutDown();
}
public void step() {
synchronized (manager) {
manager.stepsRequested++;
manager.notifyAll();
}
}
public void tick() {
ticker.tickOnce();
}
public void tickMain(int count) {
while (count > 0) {
ticker.tickOnce();
count--;
try {
Thread.sleep(50);
} catch (InterruptedException ex) {
Logger.getLogger(Simulator.class.getName()).log(Level.SEVERE,
null, ex);
}
}
}
}