/*
- * declarations for multiplexer daemon
+ * declarations for realtime daemon
*/
#ifndef REALTIME_H
#include <sys/types.h>
#include <sys/time.h>
+/*---------- from retransmit.c ----------*/
+
typedef struct RetransmitNode RetransmitNode;
+struct RetransmitNode {
+ /* set by caller: */
+ PicInsn pi;
+ /* internal: */
+ unsigned long speedyduetime;
+ struct { RetransmitNode *back, *next; } relaxed, speedy;
+};
+
+void retransmit_queue(RetransmitNode *rn);
+void retransmit_cancel(RetransmitNode *rn);
+
+/*---------- global variables, in realtime.c ----------*/
extern CommandInput cmdi;
+#define UPO (&(cmdi.out))
+
+/*---------- from/for startup.c ----------*/
+
typedef enum { /* sta_toev ping_toev */
Sta_Flush, /* R 300 I ? */
Sta_Off, /* I ? I ? */
Sta_Run /* I ? R set */
} StartupState;
-#define UPO (&(cmdi.out))
+void sta_startup(void);
+void serial_moredata(PicInsn *buf);
+
+extern StartupState sta_state;
+
+/*---------- from/for realtime.c ----------*/
void oupicio(const char *dirn, const PicInsnInfo *pii, int objnum);
void ouhex(const char *word, const Byte *command, int length);
-void serial_moredata(PicInsn *buf);
void serial_transmit(const PicInsn *pi);
-void sta_startup(void);
-extern StartupState sta_state;
+/*---------- tbi ----------*/
void abandon_run(void);
/*
+ * 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.
+ */
+
+#define RETRANS_SPEEDYCOUNT 20
+#define RETRANS_SPEEDYEXP 1.3
+
+#define HALFWAY ((~(Retransmit__Time)0) >> 1)
+
+typedef struct {
+ DLIST2_HEAD(RetransmitUrgentNode) queue; /* msgs transmitted ix times */
+ Retransmit__Time interval; /* interval after this retransmission */
+} PerSpeedyTrans;
+
+static const PicInsn linefill= { { 0xff, 0x7f }, 2 };
+
+static DLIST2_HEAD(RetransmitRelaxedNode) relaxed;
+static elapsed;
+static PerSpeedyTrans speedies[RETRANS_SPEEDYCOUNT];
+
+static void retransmit_this(const PicInsn *pi) {
+ serial_transmit(pi);
+ elapsed++;
}
-void retransmit_cancel(RetransmitNode *rn) {
+static void retransmit_something() {
+ PerSpeedyTrans *spd;
+ RetransmitUrgentNode *urg;
+ RetransmitRelaxedNode *rlx;
+
+ for (ix=0, spd=speedies;
+ ix < RETRANS_SPEEDYCOUNT;
+ ix++, spd++) {
+ urg= spd->head;
+ if (!urg) continue;
+ if (elapsed - urg->u.when > HALFWAY) continue;
+
+ /* found one to transmit: */
+ DLIST2_REMOVE(spd->queue,urg,u.queue);
+ if (++ix < RETRANS_SPEEDYCOUNT) {
+ urg->u.ix= ix;
+ urg->u.when= elapsed + spd->interval;
+ spd++;
+ DLIST2_APPEND(spd->queue,urg,u.queue);
+ } else {
+ urg->d.urgent_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 *rlx) {
+ DLIST2_PREPEND(relaxed,rn,rr);
+}
+void retransmit_relaxed_cancel(RetransmitRelaxedNode *rlx) {
+ DLIST2_REMOVE(relaxed,rlx,rr);
+}
+
+void retransmit_urgent_queue(RetransmitUrgentNode *urg) {
+ urg->u.ix= 0;
+ urg->u.when= elapsed;
+ DLIST2_APPEND(speedies[0].queue,urg,u.queue);
+ retransmit_relaxed_queue(&urg->u.relaxed);
+}
+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);
}
logmsg(ec, tran, NOTA(Segment), "countermanded motion start");
}
setspeed(tran, oldspeed);
+ oprintf(UPO, "countermanded %s %l %l\n",
+ info_trains[tran].pname, oldspeed, newspeed);
ec= lay_train(tran, 0);
if (ec)
safety_panic(tran, NOTA(Segment), "countermanding"
setspeed(tran, newspeed);
}
-
-
-int main(void) {
- printf("%d\n",(int)sizeof(State));
- return 0;
-}
void safety_emergencystop(TrainNum);
/* Callable directly in response to application command. */
+void safety_setdirection(TrainNum tran, int sense_fixme_define_this_properly);
void safety_requestspeed(TrainNum tran, long newspeed);
/* To be called only by the speed manager, thus indirectly from
* user request.
* Will result in a call to speedmanager_speedchange_notify (and of
- * course to actual_setspeed. Speed manager must apply accel/decel
+ * course to actual_setspeed). Speed manager must apply accel/decel
* curve so that if safety.c agrees the command, the actual speed of
* the train changes straight away (at least for decel).
*/
void safety_notify_detection(SegmentNum segn);
- /* To be called by actual.c when new train detection occurs. */
+ /* Called by startup.c when new train detection occurs in state Run. */
/*========== speedmgr.c ==========*/
* repeating the NMRA commands and redacting detection information.
*
* In general, when State information is shared between actual.c
- * and safety.c, actual.c is responsible for modifying State, and
+ * and safety.c, safety.c is responsible for modifying State, and
* will then call an actual_... function to notify the change.
*/
void on_pic_aaargh(const PicInsnInfo *pii, const PicInsn *pi, int objnum)
{ abort(); }
+void on_pic_detect1(const PicInsnInfo *pii, const PicInsn *pi, int segn) {
+ if (segn >= NUM_SEGMENTS) die("PIC sent detect @#%d out of range",segn);
+ if (sta_state < Sta_Run) {
+ oprintf(UPO, "warning fixme ignored non-Run detection @%s\n",
+ info_segments[segn].pname);
+ return;
+ }
+ safety_notify_detection(segn);
+}
+
/*---------- fixme move these to where they want to go ----------*/
void on_pic_nmradone(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
void on_pic_detect0(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
}
-void on_pic_detect1(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
- /* fixme do something here */
-}
-
void abandon_run(void) {
/* fixme do something here */
}