--- /dev/null
+apply plugin: 'java'
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+}
+
+sourceCompatibility = "1.7"
+targetCompatibility = "1.7"
--- /dev/null
+package com.acmetensortoys.ctfwstimer.lib;
+
+import java.util.NoSuchElementException;
+import java.util.Scanner;
+
+public class CtFwSGameState {
+ public boolean configured;
+ public long startT; // NTP seconds for game start
+ public int setupD;
+ public int rounds;
+ public int roundD;
+ public long endT = 0; // NTP seconds for game end (if >= startT)
+
+ public int flagsTotal;
+ public boolean flagsVisible = false;
+ public int flagsRed = 0;
+ public int flagsYel = 0;
+
+ public void setFlags(boolean visible) {
+ flagsVisible = visible;
+ }
+ public void setFlags(int red, int yel) {
+ flagsRed = red; flagsYel = yel;
+ }
+
+ public void mqttConfigMessage(String st) {
+ String tm = st.trim();
+ switch (tm) {
+ case "none":
+ this.configured = false;
+ break;
+ default:
+ try {
+ Scanner s = new Scanner(tm);
+ this.startT = s.nextLong();
+ this.setupD = s.nextInt();
+ this.rounds = s.nextInt();
+ this.roundD = s.nextInt();
+ this.flagsTotal = s.nextInt();
+ this.configured = true;
+ } catch (NoSuchElementException e) {
+ this.configured = false;
+ }
+ break;
+ }
+ }
+ public void mqttFlagsMessage(String st) {
+ String tm = st.trim();
+ switch(tm) {
+ case "?":
+ this.setFlags(false);
+ break;
+ default:
+ Scanner s = new Scanner(tm);
+ try {
+ this.setFlags(true);
+ this.setFlags(s.nextInt(),s.nextInt());
+ } catch (NumberFormatException e) {
+ this.setFlags(false);
+ }
+ }
+ }
+
+ public class Now {
+ public String rationale = null; // null if game is in play, otherwise other fields invalid
+ public int round = 0; // 0 for setup
+ public long roundStart = 0, roundEnd = 0; // NTP seconds
+ public boolean stop = false;
+ }
+ public Now getNow(long now) {
+ Now res = new Now();
+ if (!configured) {
+ res.rationale = "Game not configured";
+ res.stop = true;
+ } else if (endT >= startT) {
+ res.rationale = "Game over!";
+ res.stop = true;
+ } else if (now <= startT) {
+ res.rationale = "Start time in the future!";
+ }
+ if (res.rationale != null) {
+ return res;
+ }
+ long elapsed = now - startT;
+ if (elapsed < setupD) {
+ res.round = 0;
+ res.roundStart = startT;
+ res.roundEnd = startT + setupD;
+ return res;
+ }
+ elapsed -= setupD;
+ res.round = (int)(elapsed / roundD);
+ if (res.round >= rounds) {
+ res.rationale = "Game over!";
+ res.stop = true;
+ return res;
+ }
+ res.roundStart = startT + setupD + (res.round * roundD);
+ res.roundEnd = res.roundStart + roundD;
+ res.round += 1;
+ return res;
+ }
+}
--- /dev/null
+package com.acmetensortoys.ctfwstimer.lib;
+
+import java.util.NoSuchElementException;
+import java.util.Scanner;
+
+public class CtFwSMessageFilter {
+ final private CtFwSGameState mCgs;
+ public CtFwSMessageFilter(CtFwSGameState cgs) {
+ mCgs = cgs;
+ }
+
+ public class Msg {
+ public long when;
+ public String msg;
+
+ public Msg(long when, String msg) {
+ this.when = when;
+ this.msg = msg;
+ }
+ }
+
+ private long lastMsgTimestamp = 0;
+
+ public Msg filter(String str) {
+ Scanner s = new Scanner(str);
+ long t;
+ try {
+ t = s.nextLong();
+ } catch (NoSuchElementException nse) {
+ // Maybe they forgot a time stamp. That's not ideal, but... fake it?
+ // XXX Back off a bit, for time sync reasons
+ lastMsgTimestamp = System.currentTimeMillis()/1000 - 30;
+ return new Msg(lastMsgTimestamp, str);
+ }
+
+ // If there is no configuration, assume the message is new enough
+ // If there *is* a configuration, check the time.
+ if ((!mCgs.configured || t >= mCgs.startT) && (lastMsgTimestamp <= t)) {
+ s.useDelimiter("\\z");
+ lastMsgTimestamp = t;
+ return new Msg(lastMsgTimestamp, s.next().trim());
+ }
+
+ return null;
+ }
+}
})
wearApp project(':wear')
+ compile project(":lib")
+
compile('org.eclipse.paho:org.eclipse.paho.android.service:1.1.1') {
exclude module: 'support-v4'
}
import android.util.Log;
+import com.acmetensortoys.ctfwstimer.lib.CtFwSGameState;
+import com.acmetensortoys.ctfwstimer.lib.CtFwSMessageFilter;
+
import org.eclipse.paho.client.mqttv3.IMqttMessageListener;
import org.eclipse.paho.client.mqttv3.MqttMessage;
class CtFwSCallbacksMQTT {
final private CtFwSDisplay mCdl;
final private CtFwSGameState mCgs;
+ final private CtFwSMessageFilter mCmf;
CtFwSCallbacksMQTT(CtFwSDisplay cdl, CtFwSGameState cgs) {
mCdl = cdl;
mCgs = cgs;
+ mCmf = new CtFwSMessageFilter(mCgs);
}
IMqttMessageListener onConfig = new IMqttMessageListener() {
public void messageArrived(String topic, MqttMessage message) throws Exception {
String tm = message.toString().trim();
Log.d("CtFwS", "Message(Config): " + tm);
-
- switch (tm) {
- case "none":
- mCgs.configured = false;
- break;
- default:
- try {
- Scanner s = new Scanner(tm);
- mCgs.startT = s.nextLong();
- mCgs.setupD = s.nextInt();
- mCgs.rounds = s.nextInt();
- mCgs.roundD = s.nextInt();
- mCgs.flagsTotal = s.nextInt();
- mCgs.configured = true;
- } catch (NoSuchElementException e) {
- mCgs.configured = false;
- }
- break;
- }
+ mCgs.mqttConfigMessage(tm);
mCdl.notifyGameState();
}
};
public void messageArrived(String topic, MqttMessage message) throws Exception {
String tm = message.toString().trim();
Log.d("CtFwS", "Message(Flags): " + tm);
-
- switch(tm) {
- case "?":
- mCgs.setFlags(false);
- break;
- default:
- Scanner s = new Scanner(tm);
- try {
- mCgs.setFlags(true);
- mCgs.setFlags(s.nextInt(),s.nextInt());
- } catch (NumberFormatException e) {
- mCgs.setFlags(false);
- }
- }
+ mCgs.mqttFlagsMessage(tm);
mCdl.notifyFlags();
}
};
- private long lastMsgTimestamp = 0;
private void onMessageCommon(String str) {
- Scanner s = new Scanner(str);
- try {
- long t = s.nextLong();
- // If there is no configuration, assume the message is new enough
- // If there *is* a configuration, check the time.
- if ((!mCgs.configured || t >= mCgs.startT) && (lastMsgTimestamp <= t)) {
- s.useDelimiter("\\z");
- lastMsgTimestamp = t;
- mCdl.notifyMessage(t, s.next().trim());
- }
- } catch (NoSuchElementException nse) {
- // Maybe they forgot a time stamp. That's not ideal, but... fake it?
- lastMsgTimestamp = System.currentTimeMillis()/1000;
- mCdl.notifyMessage(lastMsgTimestamp, str);
- lastMsgTimestamp -= 30; // XXX Back off a bit, for time sync reasons
+ CtFwSMessageFilter.Msg m = mCmf.filter(str);
+ if (m != null) {
+ mCdl.notifyMessage(m.when, m.msg);
}
}
import android.widget.ProgressBar;
import android.widget.TextView;
+import com.acmetensortoys.ctfwstimer.lib.CtFwSGameState;
+
// TODO nwf is bad at UI design; someone who isn't him should improve this
class CtFwSDisplay {
+++ /dev/null
-package com.acmetensortoys.ctfwstimer;
-
-import java.util.NoSuchElementException;
-import java.util.Scanner;
-
-class CtFwSGameState {
- boolean configured;
- long startT; // NTP seconds for game start
- int setupD;
- int rounds;
- int roundD;
- long endT = 0; // NTP seconds for game end (if >= startT)
-
- int flagsTotal;
- boolean flagsVisible = false;
- int flagsRed = 0;
- int flagsYel = 0;
-
- void setFlags(boolean visible) {
- flagsVisible = visible;
- }
- void setFlags(int red, int yel) {
- flagsRed = red; flagsYel = yel;
- }
-
- class Now {
- String rationale = null; // null if game is in play, otherwise other fields invalid
- int round = 0; // 0 for setup
- long roundStart = 0, roundEnd = 0; // NTP seconds
- boolean stop = false;
- }
- public Now getNow(long now) {
- Now res = new Now();
- if (!configured) {
- res.rationale = "Game not configured";
- res.stop = true;
- } else if (endT >= startT) {
- res.rationale = "Game over!";
- res.stop = true;
- } else if (now <= startT) {
- res.rationale = "Start time in the future!";
- }
- if (res.rationale != null) {
- return res;
- }
- long elapsed = now - startT;
- if (elapsed < setupD) {
- res.round = 0;
- res.roundStart = startT;
- res.roundEnd = startT + setupD;
- return res;
- }
- elapsed -= setupD;
- res.round = (int)(elapsed / roundD);
- if (res.round >= rounds) {
- res.rationale = "Game over!";
- res.stop = true;
- return res;
- }
- res.roundStart = startT + setupD + (res.round * roundD);
- res.roundEnd = res.roundStart + roundD;
- res.round += 1;
- return res;
- }
-}
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
+import com.acmetensortoys.ctfwstimer.lib.CtFwSGameState;
+
public class MainActivity extends AppCompatActivity {
static private final String mqttClientId = MqttClient.generateClientId();
-include ':mobile', ':wear'
+include ':mobile', ':wear', ':lib'