/*******************************************************************************
* Copyright (c) 2003-2005, 2013 Till Zoppke.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* Till Zoppke - initial API and implementation
******************************************************************************/
/*
* Created on 11.08.2003
*
* To change the template for this generated file go to
* Window - Preferences - Java - Code Generation - Code and Comments
*/
package eniac.data.model.unit;
import java.util.Observable;
import eniac.data.IDManager;
import eniac.data.PulseInteractor;
import eniac.data.model.Connector;
import eniac.data.model.CyclingLights;
import eniac.data.model.EData;
import eniac.data.model.parent.BlinkenLights;
import eniac.data.model.sw.Switch;
import eniac.data.model.sw.SwitchAndFlag;
import eniac.data.type.EType;
import eniac.simulation.EEvent;
import eniac.simulation.EEventListener;
/**
* @author zoppke
*
* To change the template for this generated type comment go to Window -
* Preferences - Java - Code Generation - Code and Comments
*/
public class Accumulator extends Unit implements EEventListener {
// static keys for left or right interconnection partner detection
private static final int PARTNER_LEFT = 0;
private static final int PARTNER_RIGHT = 2;
// static keys definig the accumulator's interconnection status
private static final short NONE = 0;
private static final short SINGLE = 1;
private static final short LEFT_HAND = 2;
private static final short RIGHT_HAND = 3;
// static keys for operation switch setting
private static final int EPSILON = 4, A = 6, AS = 7, S = 8;
// current program we are working on
private SwitchAndFlag _currentOperation = null;
private long _programStartTime = 1;
// stop time of the current program
private int _repeatCounter = 0;
// current transmission cycle if we are transmitting
private int _transmissionCycle = 0;
// =========================== lifecycle //=================================
public Accumulator() {
// empty
}
public void init() {
// super inits children and data-listening to heaters
super.init();
// add this as eventlistener
CyclingLights lights = getConfiguration().getCyclingLights();
lights.addEEventListener(this, EEvent.PULSE_10P);
lights.addEEventListener(this, EEvent.PULSE_9P);
lights.addEEventListener(this, EEvent.CPP);
lights.addEEventListener(this, EEvent.PULSE_1AP);
lights.addEEventListener(this, EEvent.RP);
}
// =========================== unit methods //==============================
public Switch getHeaters() {
return (Switch) getGarten().getKind(EType.ACCU_HEATERS, 0);
}
// ============================= methods //=================================
private boolean hasActiveProgram(long time) {
return _programStartTime < time && _repeatCounter > 0;
}
private void maybeSendProgram(long time, PulseInteractor source) {
// if we have been performing a repeating program that is finished now,
// reset current program and send a program pulse.
if (_currentOperation != null && _currentOperation.getIndex() >= 4 && _repeatCounter == 1
&& time % 200 == CyclingLights.CPP_TIME) {
sendProgram(time, source);
}
}
private boolean isTransmitting(long time) {
// if no program, then we are not transmitting
if (!hasActiveProgram(time)) {
return false;
}
// if cycle is > 0, then we are transmitting
if (_transmissionCycle > 0) {
return true;
}
// if operation switch is set to sending, then we are transmitting
return (_currentOperation.getValue() >= A);
}
private boolean isReceiving(long time) {
// if no program, then we are not transmitting
if (!hasActiveProgram(time)) {
return false;
}
// if operation switch is set to receiving, then we are.
return (_currentOperation.getValue() < A);
}
public void clear() {
if (hasPower()) {
_currentOperation = null;
_programStartTime = 0;
_repeatCounter = 0;
_transmissionCycle = 0;
clearSignificiant();
}
}
private void clearSignificiant() {
// reset blinkenlights according to significiant figures switch
EData sw = getGarten().getKind(EType.SIGNIFICIANT_FIGURES_SWITCH, 0);
int significiant = ((Switch) sw).getValue();
long l = 5 * (long) Math.pow(10, 9 - significiant);
BlinkenLights blinkens = (BlinkenLights) getBlinkens();
blinkens.setNumber(l);
blinkens.clearCarry();
}
private boolean hasLoadBox(long time) {
Connector con = (Connector) getGarten().getKind(EType.INTER_CONNECTOR, 2);
boolean loadBox = con.getPartner() == con.getID();
if (loadBox) {
con.setLastPulse(time);
}
return loadBox;
}
private boolean isLeftEnd(long time) {
// determine partners of our left interconnectors
Connector c1 = (Connector) getGarten().getKind(EType.INTER_CONNECTOR, 0);
Connector c2 = (Connector) getGarten().getKind(EType.INTER_CONNECTOR, 1);
boolean leftEnd = c1.getPartner() == c2.getID() && c2.getPartner() == c1.getID();
if (leftEnd) {
c1.setLastPulse(time);
c2.setLastPulse(time);
}
return leftEnd;
}
private Accumulator getInterconnectionPartner(int partner, long time) {
// determine partners of the specified interconnectors
Connector c1 = (Connector) getGarten().getKind(EType.INTER_CONNECTOR, partner);
Connector c2 = (Connector) getGarten().getKind(EType.INTER_CONNECTOR, partner + 1);
int id1 = c1.getPartner();
int id2 = c2.getPartner();
// check, if ids point to real components
if (id1 < 0 || id2 < 0) {
return null;
}
IDManager idman = getConfiguration().getIDManager();
EData d1 = idman.get(id1);
EData d2 = idman.get(id2);
// determine parents. Only Accumulators have Interconnectors,
// so no ClasscastException can occure
Accumulator p1 = (Accumulator) d1.getParent();
Accumulator p2 = (Accumulator) d2.getParent();
// if both connector-partners have different parents,
// or if we are the parent, then no parner.
if (p1 != p2 || p1 == this) {
return null;
}
// if connectors are at the correct indices at its parent,
// then we have a partner
int i1 = (partner + 2) % 4;
int i2 = (partner + 3) % 4;
if (d1.getIndex() == i1 && d2.getIndex() == i2) {
c1.setLastPulse(time);
c2.setLastPulse(time);
return p1;
}
return null;
}
private short getInterconnectionState(long time) {
if (isLeftEnd(time) && hasLoadBox(time)) {
return SINGLE;
}
Accumulator partner = getInterconnectionPartner(PARTNER_LEFT, time);
if (partner != null && partner.isLeftEnd(time) && hasLoadBox(time)) {
return RIGHT_HAND;
}
partner = getInterconnectionPartner(PARTNER_RIGHT, time);
if (partner != null && isLeftEnd(time) && partner.hasLoadBox(time)) {
return LEFT_HAND;
}
return NONE;
}
public void setOperation(SwitchAndFlag operationSwitch, int repeat) {
_currentOperation = operationSwitch;
_repeatCounter = repeat;
}
// ============================ observer methods
// ============================
/**
* @param data
* @see eniac.data.DataListener#dataChanged(eniac.data.EData)
*/
public void update(Observable o, Object args) {
// TODO: map blinkenlights directly to heaters.
// unit should not be an observer any more.
if (((EData) o).getType() == EType.ACCU_HEATERS) {
// power changed. clear accumulator and en- or disable blinkenlights
clear();
((BlinkenLights) getBlinkens()).setEnabled(hasPower());
}
}
// ========================== pulseInteractor methods
// =======================
/**
* @param time
* @return @see eniac.data.PulseInteractor#canReceiveProgram(long)
*/
public boolean canReceiveProgram(long time, PulseInteractor source) {
return hasPower() && getInterconnectionState(time) > NONE;
}
/**
* @param time
* @param source
* @see eniac.data.PulseInteractor#receiveProgram(long,
* eniac.data.PulseInteractor)
*/
public void receiveProgram(long time, PulseInteractor source) {
// before starting a new program, check whether a program just ended
// now. The appropriate process()-call maybe will occure later.
// We don't want to overwrite any information.
if (_programStartTime < time) {
maybeSendProgram(time, this);
}
// adjust program start time
_programStartTime = time;
// determine index of program
EData d = (EData) source;
int index = d.getIndex();
// assume that program was triggered by a single program connector.
// so program will be executed only once.
_repeatCounter = 1;
_currentOperation = (SwitchAndFlag) getGarten().getKind(EType.OPERATION_SWITCH, index);
// TODO: rewrite determination of currentOperation
// check assumption.
if (d.getType() == EType.PROGRAM_CONNECTOR_PAIR) {
// so assumption was wrong and we have to correct the settings.
// program might be repeated multiple times.
EData sw = getGarten().getKind(EType.REPEAT_SWITCH, index);
int repeat = ((Switch) sw).getValue();
_repeatCounter += repeat;
_currentOperation = (SwitchAndFlag) getGarten().getKind(EType.OPERATION_SWITCH, index + 4);
}
// if we have a partner, we must set its operation
Accumulator partner = getInterconnectionPartner(PARTNER_LEFT, time);
if (partner == null) {
partner = getInterconnectionPartner(PARTNER_RIGHT, time);
if (partner == null) {
return;
}
// System.out.println(
// "Accu"
// + getIndex()
// + " has right partner "
// + partner.getIndex());
}
else {
// System.out.println(
// "Accu"
// + getIndex()
// + " has left partner "
// + partner.getIndex());
}
partner.setOperation(_currentOperation, _repeatCounter);
partner.setProgramStartTime(time);
}
public void setProgramStartTime(long time) {
_programStartTime = time;
}
/**
* @param time
* @param source
* @see eniac.data.PulseInteractor#sendProgram(long,
* eniac.data.PulseInteractor)
*/
public void sendProgram(long time, PulseInteractor source) {
// if operationSwitch is not our child, return.
// note: it could be the child of our partner, too
if (_currentOperation.getParent() != this) {
return;
}
// sending program pulse through connector pairs
int index = _currentOperation.getIndex() - 4;
EData d = getGarten().getKind(EType.PROGRAM_CONNECTOR_PAIR, index);
((PulseInteractor) d).sendProgram(time, this);
}
/**
* @param time
* @return @see eniac.data.PulseInteractor#canReceiveDigit(long)
*/
public boolean canReceiveDigit(long time, PulseInteractor source) {
// check simple conditions:
// - having power
// - program is active
if (!hasPower() || !hasActiveProgram(time)) {
return false;
}
// if this is a pulse coming through a interconnector,
// then we can receive.
if (((EData) source).getType() == EType.INTER_CONNECTOR) {
return true;
}
// check if we are currently listening to this digitConnector
return ((EData) source).getIndex() == _currentOperation.getValue();
}
/**
* @param time
* @param value
* @param source
* @see eniac.data.PulseInteractor#receiveDigit(long, int,
* eniac.data.PulseInteractor)
*/
public void receiveDigit(long time, long value, PulseInteractor source) {
// if common digit received, modify blinkenlights number by the value
((BlinkenLights) getBlinkens()).rotateNumber(value);
}
private void carryAsLeftPartner(boolean rotate) {
BlinkenLights blinkens = (BlinkenLights) getBlinkens();
if (rotate) {
// System.out.println("receiving code 11");
EData number = blinkens.getGarten().getKind(EType.BLINKEN_NUMBER_SWITCH, 9);
((SwitchAndFlag) number).rotateValue();
}
boolean carry = blinkens.carryOver();
// System.out.println("partner-carryover performed");
// check, if further carry adjustment is needed.
if (carry) {
// toggle the sign
EData sign = blinkens.getGarten().getKind(EType.BLINKEN_SIGN_SWITCH, 0);
((Switch) sign).toggleValue();
}
// clear carryover flags
blinkens.clearCarry();
}
/**
* @param time
* @param value
* @param source
* @see eniac.data.PulseInteractor#sendDigit(long, int,
* eniac.data.PulseInteractor)
*/
public void sendDigit(long time, long value, PulseInteractor source) {
int operation = _currentOperation.getValue();
if (operation == A || operation == AS) {
// send positive
EData con = getGarten().getKind(EType.DIGIT_CONNECTOR, 5);
((PulseInteractor) con).sendDigit(time, value, this);
}
if (operation == AS || operation == S) {
// send negative
EData con = getGarten().getKind(EType.DIGIT_CONNECTOR, 6);
((PulseInteractor) con).sendDigit(time, 11111111111L - value, this);
}
}
// =========================== eeventListener methods
// =======================
/**
* @param e
* @see eniac.simulation.EEventListener#process(eniac.simulation.EEvent)
*/
public void process(EEvent e) {
switch (e.type) {
// CPP
case EEvent.CPP :
// maybe send program, if a program finished just now
if (_programStartTime < e.time) {
maybeSendProgram(e.time, this);
}
// if program didn't started right now, decrease repeatCounter.
// note: programStartTime could be changed by "maybeSendProgram"
// above, so it must be checked again.
if (_programStartTime < e.time) {
_repeatCounter--;
}
break;
// PULSE_10P
case EEvent.PULSE_10P :
// rotate number, if operation switch is set to transmission
if (isTransmitting(e.time)) {
((BlinkenLights) getBlinkens()).rotateNumber(1111111111L);
_transmissionCycle++;
_transmissionCycle %= 10;
}
break;
// PULSE_9P
case EEvent.PULSE_9P :
// transmitt pulse, if operation switch is set to transmission
if (isTransmitting(e.time)) {
BlinkenLights blinkens = (BlinkenLights) getBlinkens();
long pulse = blinkens.computePulse(_transmissionCycle);
sendDigit(e.time, pulse, this);
}
break;
// PULSE_1AP:
// if complementary transmittion, send correcting pulse
// if receiving/O and clear correct is set and we have loadbox,
// pick up as correcting pulse
case EEvent.PULSE_1AP :
perform1AP(e);
break;
// RP:
// clear accus if carryClear is set and we are O,A,AS,S
// perform carryOver computation according to interconnection
// state
case EEvent.RP :
performRP(e);
break;
}
}
private void perform1AP(EEvent e) {
// check, if transmitting complementary
if (isTransmitting(e.time) && (_currentOperation.getValue() == AS || _currentOperation.getValue() == S)) {
// determine setting of significiant figures switch
EData d = getGarten().getKind(EType.SIGNIFICIANT_FIGURES_SWITCH, 0);
int figure = ((Switch) d).getValue();
// if fugure is 0, then we don't need to send
if (figure == 0) {
return;
}
// otherwise compute pulse to send
long pulse = (long) Math.pow(10, 10 - figure);
// send correcting pulse through S output
EData con = getGarten().getKind(EType.DIGIT_CONNECTOR, 6);
((Connector) con).sendDigit(e.time, pulse, this);
// check, if we are receiving, if clear-correct-switch is set
// and if we have a load box
}
else if (isReceiving(e.time) && _currentOperation.isFlag() && hasLoadBox(e.time)) {
// send correcting pulse throug our input connector
// note: this asserts, that just one pulse is received
// in this timeslot. We might receive a complementary
// number and get a pulse at PULSE_1AP anyway.
// but this would be blocked by the commector.
int v = _currentOperation.getValue();
EData con = getGarten().getKind(EType.DIGIT_CONNECTOR, v);
((Connector) con).receiveDigit(e.time, 1, this);
}
}
private void performRP(EEvent e) {
// if carry clear gate is low, there is nothing to do.
if (!getConfiguration().getCyclingLights().isCCG()) {
return;
}
// so carry clear is set.
// check, if the carry clear switch is set for current operation
if (hasActiveProgram(e.time) && _currentOperation.isFlag()) {
// check, that we are sending or performing zero and the program
// just finished
if (_currentOperation.getValue() > EPSILON && _repeatCounter == 1) {
// clear decade counters
clearSignificiant();
}
}
// carryover
switch (getInterconnectionState(e.time)) {
case NONE :
// not wired, so nothing to do
break;
case SINGLE :
BlinkenLights blinkens = (BlinkenLights) getBlinkens();
if (isReceiving(e.time)) {
// perform carryover
boolean carry = blinkens.carryOver();
// check, if further carry adjustment is needed.
if (isLeftEnd(e.time) && carry) {
// if we are single, toggle the sign
EData sign = blinkens.getGarten().getKind(EType.BLINKEN_SIGN_SWITCH, 0);
((Switch) sign).toggleValue();
}
}
// clear carryover flags
blinkens.clearCarry();
break;
case LEFT_HAND :
// wait for the carryover-call of our right hand partner
break;
case RIGHT_HAND :
blinkens = (BlinkenLights) getBlinkens();
Accumulator partner = getInterconnectionPartner(PARTNER_LEFT, e.time);
if (isReceiving(e.time)) {
// perform carryover
boolean carry = blinkens.carryOver();
// send program pulse through interconnector
// note: If we have 1 carryover, we send it as 2.
// because this also will trigger the carry-over
// at our partner and we send 1 to encode no carry.
partner.carryAsLeftPartner(carry);
// long value = carry ? 11 : 1;
// System.out.println("sending carryover, code: " + value);
// if (value == 11) {
// System.out.println("poupou"); //$NON-NLS-1$
// }
// EData con =
// getGarten().getKind(EType.INTER_CONNECTOR, 0);
// ((Connector) con).sendDigit(e.time, value, this);
}
blinkens.clearCarry();
((BlinkenLights) partner.getBlinkens()).clearCarry();
break;
}
}
}