Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions src/org/openlcb/implementations/BitProducerConsumer.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public class BitProducerConsumer extends MessageDecoder {
private final int flags;

public final static EventID nullEvent = new EventID(new byte[]{0, 0, 0, 0, 0, 0, 0, 0});
//private final static Logger log = Logger.getLogger(VersionedValue.class.getCanonicalName());

/// Flag bit to set default value. (set: true; clear: false).
public final static int DEFAULT_TRUE = 1;
Expand Down Expand Up @@ -83,6 +84,21 @@ public VersionedValue<Boolean> getValue() {
return value;
}

/**
* Resets the producer/consumer to its default state. This will not change the actual state (also not trigger listeners), but will start reporting unknown state to the network, and enables sending a new query message to the network using @link sendQuery(), assuming the flags are set up for that.
*/
public void resetToDefault() {
value.setVersionToDefault();
}

/**
* Sends out query messages to the bus. Useful to be called after resetToDefault().
*/
public void sendQuery() {
sendMessage(new IdentifyProducersMessage(iface.getNodeId(), eventOn));
sendMessage(new IdentifyConsumersMessage(iface.getNodeId(), eventOn));
}

/**
* Sends out an event message
* @param <T> the message type to send.
Expand All @@ -98,7 +114,7 @@ <T extends EventMessage> void sendMessage(T msg) {
* default value passed in.
*/
public boolean isValueAtDefault() {
return (value.getVersion() == value.DEFAULT_VERSION);
return (value.isVersionAtDefault());
}

private EventState getOnEventState() {
Expand Down Expand Up @@ -135,8 +151,7 @@ private void sendIdentifiedMessages(boolean queryState) {
getOffEventState()));
}
if (queryState) {
sendMessage(new IdentifyProducersMessage(iface.getNodeId(), eventOn));
sendMessage(new IdentifyConsumersMessage(iface.getNodeId(), eventOn));
sendQuery();
}
}

Expand Down
60 changes: 55 additions & 5 deletions src/org/openlcb/implementations/VersionedValue.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.openlcb.implementations;

import java.util.logging.Logger;

/**
* Created by bracz on 12/30/15.
*/
Expand All @@ -8,14 +10,19 @@ public class VersionedValue<T> {
int version;
int nextVersion;
java.beans.PropertyChangeSupport pcs = new java.beans.PropertyChangeSupport(this);
public static int DEFAULT_VERSION = 1;
static int DEFAULT_VERSION = 1;
private int defaultVersion = DEFAULT_VERSION;
private final static Logger log = Logger.getLogger(VersionedValue.class.getCanonicalName());

public VersionedValue(T t) {
version = DEFAULT_VERSION;
nextVersion = DEFAULT_VERSION + 1;
data = t;
}

/**
* @return a strictly monotonically increasing version number to be used for setting the value.
*/
public int getNewVersion() {
int newVersion;
synchronized (this) {
Expand All @@ -24,17 +31,54 @@ public int getNewVersion() {
return newVersion;
}

/**
* Resets the state of the listener to construction state, without changing the value. The
* next 'set' will force updates to be called, and isVersionAtDefault will return true.
*/
public synchronized void setVersionToDefault() {
defaultVersion = version;
}

/**
* @return true if we are currently at the default version (either just after construction or
* due to ra eset to default call).
*/
public synchronized boolean isVersionAtDefault() {
return version == defaultVersion;
}

/**
* Sets the current value; possibly calling listeners. Handles versioning internally.
* @param t new value of data stored.
*/
public void set(T t) {
int version;
synchronized(this) {
int version = getNewVersion();
set(version, t);
version = getNewVersion();
}
set(version, t);
}

/**
* Sets the value using a pre-requested version. The set has no effect if the version has
* already been exceeded.
* @param atVersion proposed new version number
* @param t new value of data stored
* @return true if data was updated. False if the version is already outdated and no change
* was made.
*/
public boolean setWithForceNotify(int atVersion, T t) {
return setInternal(atVersion, t, true);
}

/**
* Sets the value using a pre-requested version. The set has no effect if the version has
* already been exceeded.
* @param atVersion proposed new version number
* @param t new value of data stored
* @return true if data was updated. False if the version is already outdated and no change
* was made.
*/
public boolean set(int atVersion, T t) {
return setInternal(atVersion, t, false);
}
Expand All @@ -49,11 +93,11 @@ private boolean setInternal(int atVersion, T t, boolean forceNotify) {
if (nextVersion <= atVersion) {
nextVersion = atVersion + 1;
}
if (data.equals(t) && oldVersion != DEFAULT_VERSION && !forceNotify) {
if (data.equals(t) && oldVersion != defaultVersion && !forceNotify) {
return true;
}
old = data;
if (oldVersion == DEFAULT_VERSION || forceNotify) {
if (oldVersion == defaultVersion || forceNotify) {
old = null;
}
data = t;
Expand All @@ -62,10 +106,16 @@ private boolean setInternal(int atVersion, T t, boolean forceNotify) {
return true;
}

/**
* @return data stored at the current version.
*/
public T getLatestData() {
return data;
}

/**
* @return current version number.
*/
public int getVersion() {
return version;
}
Expand Down
87 changes: 87 additions & 0 deletions test/org/openlcb/implementations/BitProducerConsumerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,93 @@ public void testOneEventNull() throws Exception {

}

public void testSendQuery() {
createWithDefaults();

pc.sendQuery();
expectFrame(":X19914333N0504030201000708;");
expectFrame(":X198F4333N0504030201000708;");
expectNoFrames();
}

public void testResetToDefault() {
createWithDefaults();
MockVersionedValueListener<Boolean> listener = new MockVersionedValueListener<>(pc
.getValue());

// baseline: at default.
sendFrameAndExpectResult( //
":X198F4444N0504030201000709;",
":X194C7333N0504030201000709;");
sendFrameAndExpectResult( //
":X198F4444N0504030201000708;",
":X194C7333N0504030201000708;");

verifyNoMoreInteractions(listener.stub);

// Sets to off. Callback comes.
sendFrame(":X195B4444N0504030201000709;");

verify(listener.stub).update(false);
verifyNoMoreInteractions(listener.stub);
reset(listener.stub);
// queries return definite state
sendFrameAndExpectResult( //
":X198F4444N0504030201000709;",
":X194C4333N0504030201000709;");
sendFrameAndExpectResult( //
":X198F4444N0504030201000708;",
":X194C5333N0504030201000708;");
// fun starts here
pc.resetToDefault();
// queries return unknown
sendFrameAndExpectResult( //
":X198F4444N0504030201000709;",
":X194C7333N0504030201000709;");
sendFrameAndExpectResult( //
":X198F4444N0504030201000708;",
":X194C7333N0504030201000708;");

verifyNoMoreInteractions(listener.stub);
reset(listener.stub);

// Sets to off with a PCER
sendFrame(":X195B4444N0504030201000709;");
// queries return definite state
sendFrameAndExpectResult( //
":X198F4444N0504030201000709;",
":X194C4333N0504030201000709;");
sendFrameAndExpectResult( //
":X198F4444N0504030201000708;",
":X194C5333N0504030201000708;");

verify(listener.stub).update(false);
verifyNoMoreInteractions(listener.stub);
reset(listener.stub);

pc.resetToDefault();
// queries return unknown
sendFrameAndExpectResult( //
":X198F4444N0504030201000709;",
":X194C7333N0504030201000709;");
sendFrameAndExpectResult( //
":X198F4444N0504030201000708;",
":X194C7333N0504030201000708;");
// Sets to off with a consumer identified. This usually happens when sendQuery() is
// invoked and the layout responds. We will get a callback too.
sendFrame(":X19545333N0504030201000708;");
verify(listener.stub).update(false);
verifyNoMoreInteractions(listener.stub);
reset(listener.stub);
// queries return definite state
sendFrameAndExpectResult( //
":X198F4444N0504030201000709;",
":X194C4333N0504030201000709;");
sendFrameAndExpectResult( //
":X198F4444N0504030201000708;",
":X194C5333N0504030201000708;");
}

@Override
protected void tearDown() throws Exception {
expectNoFrames();
Expand Down
37 changes: 36 additions & 1 deletion test/org/openlcb/implementations/VersionedValueTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,20 @@ public class VersionedValueTest extends TestCase {
public void testDefaultAndSet() throws Exception {
assertEquals("default value", Integer.valueOf(42), v.getLatestData());
assertEquals(v.DEFAULT_VERSION, v.getVersion());
assertTrue(v.isVersionAtDefault());
assertEquals(42, (int)v.getLatestData());

v.set(13);
assertEquals("set value", Integer.valueOf(13), v.getLatestData());
assert(v.DEFAULT_VERSION < v.getVersion());
}
assertFalse(v.isVersionAtDefault());
assertEquals(13, (int)v.getLatestData());

v.setVersionToDefault();
assert(v.DEFAULT_VERSION < v.getVersion());
assertTrue(v.isVersionAtDefault());
assertEquals(13, (int)v.getLatestData());
}

public void testSetWithForceNotify() throws Exception {
v.set(33); // gets rid of the default value condition
Expand Down Expand Up @@ -84,6 +92,33 @@ public void testSet() throws Exception {
verifyNoMoreInteractions(l2.stub);
verifyNoMoreInteractions(l1.stub);
reset(l1.stub, l2.stub);

// skips duplicate notification
l1.setFromOwner(33);
verifyNoMoreInteractions(l2.stub);
verifyNoMoreInteractions(l1.stub);
reset(l1.stub, l2.stub);

// After reset to default we will get an update notification.
v.setVersionToDefault();
l1.setFromOwner(33);
verify(l2.stub).update(33);
verifyNoMoreInteractions(l1.stub);
reset(l1.stub, l2.stub);

// After reset to default we will get both update notifications.
v.setVersionToDefault();
v.set(33);
verify(l1.stub).update(33);
verify(l2.stub).update(33);
reset(l1.stub, l2.stub);

// After reset to default we will get an update notification.
v.setVersionToDefault();
l1.setFromOwner(42);
verify(l2.stub).update(42);
verifyNoMoreInteractions(l1.stub);
reset(l1.stub, l2.stub);
}

public void testOutOfOrder() throws Exception {
Expand Down