/*******************************************************************************
* 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 28.10.2003
*
* To change the template for this generated file go to
* Window - Preferences - Java - Code Generation - Code and Comments
*/
package eniac.data.model;
import org.xml.sax.Attributes;
import eniac.data.PulseInteractor;
import eniac.data.model.parent.Configuration;
import eniac.data.type.EType;
import eniac.io.XMLUtil;
import eniac.simulation.EEvent;
import eniac.simulation.EEventListener;
import eniac.util.Status;
/**
* @author zoppke
*
* To change the template for this generated type comment go to Window -
* Preferences - Java - Code Generation - Code and Comments
*/
public class Connector extends EData implements PulseInteractor, EEventListener {
// static key for observation messages
public static final String CABLE_TRANSMITTION = "cable_transmittion"; //$NON-NLS-1$
// static keys for pulse direction
// private static final EnumSet<Tag> DIRECTION = EnumSet.of(Tag.IN, Tag.OUT,
// Tag.BOTH);
// pulse direction of this connector
private Tag _direction;
// id of the connector, that we are connected with by a cable.
// if no cable, then this is -1.
private int _partner;
// flag indicating whether this connector has a cable.
// note: This is another thing than having a partner. If a cable ist just
// created by users click, the connector is plugged, but the cable ends
// at the mouse-dragging-point. So no partner in this case.
private boolean _plugged = false;
// timeslot where we got the last pulse.
// note: if choose -1 for this, the connector will be painted in highlight
// mode at startup. I don't know why.
private long _lastPulse = -11;
// value of the pulse. If this is a program connector, it is 0 or 1.
// in case of a digit connector, it is according to the pulse transmission.
private long _pulseValue = 0;
// ============================ lifecycle
// ===================================
public Connector() {
// empty constructor
}
public void setAttributes(Attributes attrs) {
super.setAttributes(attrs);
_direction = XMLUtil.parseEnum(attrs, Tag.IO, Tag.class);
_partner = XMLUtil.parseInt(attrs, Tag.PARTNER);
// _plugged = _partner >= 0;
// note: the connector is plugged, when both partners are set to
// the cable
}
// ================================ methods
// =================================
public String getAttributes() {
return super.getAttributes() + XMLUtil.wrapAttribute(Tag.IO, _direction.toString())
+ XMLUtil.wrapAttribute(Tag.PARTNER, Integer.toString(_partner));
}
public int getPartner() {
return _partner;
}
public void setPartnerID(int id) {
_partner = id;
}
public boolean isPlugged() {
return _plugged;
}
public void setPlugged(boolean b) {
if (_plugged != b) {
_plugged = b;
setChanged();
notifyObservers(REPAINT);
}
}
public Tag getDirection() {
return _direction;
}
private boolean isSender() {
return (_direction == Tag.OUT || _direction == Tag.BOTH);
}
private boolean isReceiver() {
return (_direction == Tag.IN || _direction == Tag.BOTH);
}
private boolean isDigit() {
return _type == EType.DIGIT_CONNECTOR || _type == EType.DIGIT_CONNECTOR_CROSS || _type == EType.INTER_CONNECTOR;
}
private boolean isProgram() {
return _type == EType.PROGRAM_CONNECTOR;
}
public void setLastPulse(long time) {
if (_lastPulse < time) {
// adjust pulse time and value
_lastPulse = time;
// if we are hightlighting, call for repaint
if ((boolean) Status.HIGHLIGHT_PULSE.getValue()) {
setChanged();
notifyObservers(EData.PAINT_IMMEDIATELY);
}
// set alarm clock for changing our highlightning in the following
// eevent time slot.
// note: maybe the highlightning-flag is not set now.
// But if we are in stepping mode it might switched on before our
// call-back occures. So we have to set alarm in any case.
Configuration config = (Configuration) Status.CONFIGURATION.getValue();
config.getCyclingLights().setAlarmClock(time, this);
}
}
public long getLastPulse() {
return _lastPulse;
}
// ========================= pulseInteractor methods
// ========================
/**
* @param time
* @param source
* @see eniac.data.PulseInteractor#canReceiveDigit(long,
* eniac.data.PulseInteractor)
*/
public boolean canReceiveDigit(long time, PulseInteractor source) {
return isReceiver() && isDigit() && ((PulseInteractor) getParent()).canReceiveDigit(time, this);
}
private long checkDigit(long time, long value) {
// check, if this is the first pulse within this time slot
if (_lastPulse < time) {
// adjust pulse time and value
setLastPulse(time);
_pulseValue = value;
}
else {
// there has been a pulse before. Adjust values
long oldPulseValue = _pulseValue;
_pulseValue |= value;
value = _pulseValue ^ oldPulseValue;
}
return value;
}
/**
* @param time
* @param value
* @param source
* @see eniac.data.PulseInteractor#receive(long, int,
* eniac.data.PulseInteractor)
*/
public void receiveDigit(long time, long value, PulseInteractor source) {
// if value contains no pulse, there is nothing to do
if (value == 0) {
return;
}
// check value
value = checkDigit(time, value);
// transmitt pulse to parent
// note: we already asked parent if it can receive
if (value > 0) {
((PulseInteractor) _parent).receiveDigit(time, value, this);
}
}
/**
* @param time
* @param value
* @param source
* @see eniac.data.PulseInteractor#sendDigit(long, long, PulseInteractor)
*/
public void sendDigit(long time, long value, PulseInteractor source) {
// if (getType() == EType.INTER_CONNECTOR) {
// System.out.println("jkjkj"); //$NON-NLS-1$
// }
// check that we potentially can send a digit pulse and haven't done
// in current timeslice
if (isDigit() && isSender() && value > 0) {
// check value
value = checkDigit(time, value);
// if value contains no pulse, we don't need to send
if (value == 0) {
return;
}
// check, if we are plugged and have a valid partner
if (_plugged && _partner >= 0) {
// send pulse to partner if he can receive.
Connector c = (Connector) getConfiguration().getIDManager().get(_partner);
if (c.canReceiveDigit(time, this)) {
c.receiveDigit(time, value, this);
}
// notify cable for highlightning
setChanged();
notifyObservers(CABLE_TRANSMITTION);
}
}
}
/**
* @param time
* @param source
* @see eniac.data.PulseInteractor#canReceiveProgram(long,
* eniac.data.PulseInteractor)
*/
public boolean canReceiveProgram(long time, PulseInteractor source) {
return isReceiver() && isProgram() && _lastPulse < time
&& ((PulseInteractor) getParent()).canReceiveProgram(time, this);
}
/**
* @param time
* @param source
* @see eniac.data.PulseInteractor#receiveProgram(long,
* eniac.data.PulseInteractor)
*/
public void receiveProgram(long time, PulseInteractor source) {
// adjust pulse time
setLastPulse(time);
// transmitt pulse to parent.
// note: it has already been checked, that parent can receive.
// so we don't ask again.
((PulseInteractor) _parent).receiveProgram(time, this);
}
/**
* @param time
* @param source
* @see eniac.data.PulseInteractor#sendProgram(long,
* eniac.data.PulseInteractor)
*/
public void sendProgram(long time, PulseInteractor source) {
// check if pulse can go and haven't already gone this time
if (isProgram() && isSender() && _lastPulse < time) {
// adjust pulse time
setLastPulse(time);
// if we are plugged and have a valid partner, transmitt pulse.
if (_plugged && _partner >= 0) {
// TODO: synchronize this. maybe cable is removed.
Connector c = (Connector) getConfiguration().getIDManager().get(_partner);
if (c.canReceiveProgram(time, this)) {
c.receiveProgram(time, this);
}
// notify cable to paint
setChanged();
notifyObservers(CABLE_TRANSMITTION);
}
}
}
// ============================= event listening
// ============================
/**
* @param e
* @see eniac.simulation.EEventListener#process(eniac.simulation.EEvent)
*/
public void process(EEvent e) {
if (e.type == EEvent.ALARM && (boolean) Status.HIGHLIGHT_PULSE.getValue()) {
// we are called for downlightning
setChanged();
notifyObservers(EData.PAINT_IMMEDIATELY);
}
}
}