chiark / gitweb /
merge changes from trunk into our branch; cvs up -j branchpoint-hostside-wip-2006...
[trains.git] / hostside / retransmit.c
index 54c4fda8efea87341c6d35a8004c9f7a7e891115..d6f16480b026b871cec9e6b9b84cafc0cf4669ce 100644 (file)
 /*
+ * realtime
  * nmra retransmission
  */
 
-#include "hostside.h"
+#include "realtime.h"
 
-void retransmit_queue(RetransmitNode *rn) {
+/*
+ * There are two kinds of messages: urgent and relaxed.
+ *
+ * Urgent messages may be due for speedy transmission.  The due urgent
+ * message with the highest priority is always transmitted as soon as
+ * a slot is available.  This is known as `speedy transmission'.
+ * 
+ * When no urgent messages are due for speedy transmission, messages
+ * (urgent or relaxed) are transmitted in a round-robin fashion.  This
+ * round-robin transmission is completely independent of the speedy
+ * transmission.
+ *
+ * Each urgent message is due for speedy transmission as follows:
+ *   - When it is first queued and has not yet been
+ *     speedily transmitted:
+ *         immediately due, with maximum priority
+ *   - Following each speedy transmission:
+ *         due after an interval since the previous
+ *           transmission; this interval increases exponentially
+ *           for each transmission
+ *         with priority which decreases with later transmissions
+ *         until it has been speedily transmitted a certain number of times
+ *   - Thereafter:
+ *         no longer ever due; subject to round-robin transmission only.
+ * Intervals are measured in messages regardless of length.
+ */
+/*
+ * Implementation:
+ *  All messages are on the single relaxed queue.
+ *  There is one queue for each speady retransmission; messages are on
+ *  it in chronological order of dueness.
+ */
+
+#include "realtime.h"
+#include "nmra.h"
+#include "retransmit-table.h"
+
+#define RETRANSMIT_TIME_SIGNED_MAX ((~(Retransmit__Time)0) >> 1)
+
+static PicInsn linefill;
+
+static DLIST2_HEAD(RetransmitRelaxedNode) relaxed;
+static Retransmit__Time elapsed;
+static PerSpeedyTrans speedies[] = SPEEDIESINIT;
+
+static void retransmit_this(const PicInsn *pi) {
+  serial_transmit(pi);
+  elapsed++;
+}
+
+void retransmit_start(void) {
+  Nmra n;
+
+  enco_nmra_idle(&n);
+  nmra_addchecksum(&n);
+  nmra_encodeforpic(&n, &linefill);
+
+  retransmit_something();
+  retransmit_something();
+  retransmit_something();
 }
 
-void retransmit_cancel(RetransmitNode *rn) {
+void retransmit_something(void) {
+  PerSpeedyTrans *spd;
+  RetransmitUrgentNode *urg;
+  RetransmitRelaxedNode *rlx;
+  int ix;
+  
+  for (ix=0, spd=speedies;
+       ix < SPEEDYCOUNT;
+       ix++, spd++) {
+    urg= spd->queue.head;
+    if (!urg) continue;
+    if (elapsed - urg->u.when > RETRANSMIT_TIME_SIGNED_MAX) continue;
+
+    /* found one to transmit: */
+    DLIST2_REMOVE(spd->queue,urg,u.queue);
+    if (++ix < SPEEDYCOUNT) {
+      urg->u.ix= ix;
+      urg->u.when= elapsed + spd->interval;
+      spd++;
+      DLIST2_APPEND(spd->queue,urg,u.queue);
+    } else {
+      urg->u.ix= -1;
+    }
+    retransmit_this(&urg->pi);
+    return;
+  }
+
+  rlx= relaxed.head;
+  if (rlx) {
+    DLIST2_REMOVE(relaxed,rlx,rr);
+    DLIST2_APPEND(relaxed,rlx,rr);
+    retransmit_this(&rlx->pi);
+    return;
+  }
+
+  serial_transmit(&linefill);
+}
+
+void retransmit_relaxed_queue(RetransmitRelaxedNode *rn, Nmra *n) {
+  if (n) { nmra_addchecksum(n); nmra_encodeforpic(n, &rn->pi); }
+  DLIST2_PREPEND(relaxed,rn,rr);
+}
+void retransmit_relaxed_requeue(RetransmitRelaxedNode *rn, Nmra *n) {
+  retransmit_relaxed_cancel(rn);
+  retransmit_relaxed_queue(rn, n);
+}
+void retransmit_relaxed_cancel(RetransmitRelaxedNode *rlx) {
+  DLIST2_REMOVE(relaxed,rlx,rr);
+}
+
+void retransmit_urgent_queue(RetransmitUrgentNode *urg, Nmra *n) {
+  retransmit_relaxed_queue(&urg->u.relaxed, n);
+  urg->u.ix= 0;
+  urg->u.when= elapsed;
+  DLIST2_APPEND(speedies[0].queue,urg,u.queue);
+}
+void retransmit_urgent_queue_relaxed(RetransmitUrgentNode *urg, Nmra *n){
+  retransmit_relaxed_queue(&urg->u.relaxed, n);
+  urg->u.ix= -1;
+  urg->u.when= elapsed;
+}
+void retransmit_urgent_requeue(RetransmitUrgentNode *rn, Nmra *n) {
+  retransmit_urgent_cancel(rn);
+  retransmit_urgent_queue(rn, n);
+}
+void retransmit_urgent_cancel(RetransmitUrgentNode *urg) {
+  if (urg->u.ix >= 0)
+    DLIST2_REMOVE(speedies[urg->u.ix].queue,urg,u.queue);
+  retransmit_relaxed_cancel(&urg->u.relaxed);
 }