summaryrefslogtreecommitdiff
path: root/appl/demo/lego/clockface.b
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/demo/lego/clockface.b
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/demo/lego/clockface.b')
-rw-r--r--appl/demo/lego/clockface.b367
1 files changed, 367 insertions, 0 deletions
diff --git a/appl/demo/lego/clockface.b b/appl/demo/lego/clockface.b
new file mode 100644
index 00000000..1f2aa105
--- /dev/null
+++ b/appl/demo/lego/clockface.b
@@ -0,0 +1,367 @@
+# Model 1
+implement Clockface;
+
+include "sys.m";
+include "draw.m";
+
+Clockface : module {
+ init : fn (ctxt : ref Draw->Context, argv : list of string);
+};
+
+sys : Sys;
+
+hmpath : con "motor/0"; # hour-hand motor
+mmpath : con "motor/2"; # minute-hand motor
+allmpath : con "motor/012"; # all motors (for stopall msg)
+
+hbpath : con "sensor/0"; # hour-hand sensor
+mbpath : con "sensor/2"; # minute-hand sensor
+lspath: con "sensor/1"; # light sensor;
+
+ONTHRESH : con 780; # light sensor thresholds
+OFFTHRESH : con 740;
+NCLICKS : con 120;
+MINCLICKS : con 2; # min number of clicks required to stop a motor
+
+Hand : adt {
+ motor : ref Sys->FD;
+ sensor : ref Sys->FD;
+ fwd : array of byte;
+ rev : array of byte;
+ stop : array of byte;
+ pos : int;
+};
+
+lightsensor : ref Sys->FD;
+allmotors : ref Sys->FD;
+hourhand : ref Hand;
+minutehand : ref Hand;
+
+reqch: chan of (string, chan of int);
+
+init(nil : ref Draw->Context, argv : list of string)
+{
+ sys = load Sys Sys->PATH;
+ sys->pctl(Sys->NEWPGRP, nil);
+
+ argv = tl argv;
+ if (len argv != 1) {
+ sys->print("usage: lego_dir\n");
+ raise("fail:usage");
+ }
+
+ # set up our control file
+ if (sys->bind("#s", ".", Sys->MBEFORE) == -1) {
+ sys->print("failed to bind srv device: %r\n");
+ return;
+ }
+ f2c := sys->file2chan(".", "clockface");
+ if (f2c == nil) {
+ sys->print("cannot create legolink channel: %r\n");
+ return;
+ }
+
+ legodir := hd argv;
+ if (legodir[len legodir -1] != '/')
+ legodir[len legodir] = '/';
+
+ # get the motor files
+ sys->print("opening motor files\n");
+ hm := sys->open(legodir + hmpath, Sys->OWRITE);
+ mm := sys->open(legodir +mmpath, Sys->OWRITE);
+ allmotors = sys->open(legodir + allmpath, Sys->OWRITE);
+ if (hm == nil || mm == nil || allmotors == nil) {
+ sys->print("cannot open motor files\n");
+ raise("fail:error");
+ }
+
+ # get the sensor files
+ sys->print("opening sensor files\n");
+ hb := sys->open(legodir + hbpath, Sys->ORDWR);
+ mb := sys->open(legodir + mbpath, Sys->ORDWR);
+ lightsensor = sys->open(legodir + lspath, Sys->ORDWR);
+
+ if (hb == nil || mb == nil) {
+ sys->print("cannot open sensor files\n");
+ raise("fail:error");
+ }
+
+ hourhand = ref Hand(hm, hb, array of byte "f7", array of byte "r7", array of byte "s7", 0);
+ minutehand = ref Hand(mm, mb, array of byte "f7", array of byte "r7", array of byte "s7", 0);
+
+ sys->print("setting sensor types\n");
+ setsensortypes(hourhand, minutehand, lightsensor);
+
+ reqch = chan of (string, chan of int);
+ spawn sethands();
+# reqch <-= ("reset", nil);
+ spawn srvlink(f2c);
+}
+
+srvlink(f2c : ref Sys->FileIO)
+{
+ for (;;) alt {
+ (offset, count, fid, rc) := <- f2c.read =>
+ if (rc == nil)
+ continue;
+ if (offset != 0) {
+ rc <-= (nil, nil);
+ continue;
+ }
+ rc <- = (array of byte gettime(), nil);
+
+ (offset, data, fid, wc) := <- f2c.write =>
+ if (wc == nil)
+ continue;
+ if (offset != 0) {
+ wc <-= (0, "bad offset");
+ continue;
+ }
+ spawn settime(wc, string data, len data);
+ }
+}
+
+gettime(): string
+{
+ hpos := hourhand.pos;
+ mpos := minutehand.pos;
+
+ h := 12 * hpos / NCLICKS;
+ m := 60 * mpos / NCLICKS;
+
+ time := "??:??";
+ for (hadj := -1; hadj <= 1; hadj++) {
+ hpos2 := (((h+hadj) * NCLICKS) / 12) + ((m * NCLICKS) / (12 * 60));
+ dhpos := hpos - hpos2;
+ if (dhpos >= -2 && dhpos <= 2) {
+ # allow 2 clicks of imprecision either way
+ time = sys->sprint("%.2d:%.2d", h+hadj, m);
+ break;
+ }
+ }
+ return sys->sprint("%s %d %d", time, hpos*360/NCLICKS, mpos*360/NCLICKS);
+}
+
+settime(wc: Sys->Rwrite, time: string, wn: int)
+{
+ done := chan of int;
+ reqch <-= (time, done);
+ <- done;
+ wc <-= (wn, nil);
+}
+
+str2clicks(s : string) : (int, int)
+{
+ h, m : int = 0;
+ (n, toks) := sys->tokenize(s, ":");
+ if (n > 1) {
+ h = int hd toks;
+ toks = tl toks;
+ n--;
+ }
+ if (n > 0) {
+ m = int hd toks;
+ }
+ h = ((h * NCLICKS) / 12) + ((m * NCLICKS) / (12 * 60));
+ m = (m * NCLICKS)/60;
+ return (h, m);
+}
+
+sethands()
+{
+ for (;;) {
+ (time, rc) := <- reqch;
+ if (time == "reset" || time == "reset\n") {
+ reset();
+ time = "12:00";
+ }
+ (hclk, mclk) := str2clicks(time);
+ for (i := 0; i < 6; i++) {
+ hdelta := clickdistance(hourhand.pos, hclk, NCLICKS);
+ mdelta := clickdistance(minutehand.pos, mclk, NCLICKS);
+ if (hdelta == 0 && mdelta == 0)
+ break;
+ if (hdelta != 0)
+ sethand(hourhand, hdelta);
+ if (mdelta != 0)
+ sethand(minutehand, mdelta);
+ }
+ releaseall();
+ if (rc != nil)
+ rc <- = 1;
+ }
+}
+
+clickdistance(start, stop, mod : int) : int
+{
+ if (start > stop)
+ stop += mod;
+ d := (stop - start) % mod;
+ if (d > mod/2)
+ d -= mod;
+ return d;
+}
+
+setsensortypes(h1, h2 : ref Hand, ls : ref Sys->FD)
+{
+ button := array of byte "b0";
+ light := array of byte "l0";
+
+ sys->seek(h1.sensor, big 0, Sys->SEEKSTART);
+ sys->write(h1.sensor, button, len button);
+ sys->seek(h2.sensor, big 0, Sys->SEEKSTART);
+ sys->write(h2.sensor, button, len button);
+ sys->seek(ls, big 0, Sys->SEEKSTART);
+ sys->write(ls, light, len light);
+}
+
+HOUR_ADJUST : con 1;
+MINUTE_ADJUST : con 3;
+reset()
+{
+ # run the motors until hands are well away from 12 o'clock (below threshold)
+ setsensortypes(hourhand, minutehand, lightsensor);
+ val := readsensor(lightsensor);
+ if (val > OFFTHRESH) {
+ triggered := chan of int;
+ sys->print("wait for hands clear of light sensor\n");
+ spawn lightwait(triggered, lightsensor, 0);
+ forward(minutehand);
+ reverse(hourhand);
+ val = <- triggered;
+ stopall();
+ sys->print("sensor %d\n", val);
+ }
+
+ resethand(hourhand);
+ hourhand.pos += HOUR_ADJUST;
+ resethand(minutehand);
+ minutehand.pos += MINUTE_ADJUST;
+}
+
+sethand(hand : ref Hand, delta : int)
+{
+ triggered := chan of int;
+ dir := 1;
+ if (delta < 0) {
+ dir = -1;
+ delta = -delta;
+ }
+ if (delta > MINCLICKS) {
+ spawn handwait(triggered, hand, delta - MINCLICKS);
+ if (dir > 0)
+ forward(hand);
+ else
+ reverse(hand);
+ <- triggered;
+ stop(hand);
+ hand.pos += dir * readsensor(hand.sensor);
+ } else {
+ startval := readsensor(hand.sensor);
+ if (dir > 0)
+ forward(hand);
+ else
+ reverse(hand);
+ stop(hand);
+ hand.pos += dir * (readsensor(hand.sensor) - startval);
+ }
+ if (hand.pos < 0)
+ hand.pos += NCLICKS;
+ hand.pos %= NCLICKS;
+}
+
+resethand(hand : ref Hand)
+{
+ triggered := chan of int;
+ val : int;
+
+ # run the hand until the light sensor is above threshold
+ sys->print("running hand until light sensor activated\n");
+ spawn lightwait(triggered, lightsensor, 1);
+ forward(hand);
+ val = <- triggered;
+ stop(hand);
+ sys->print("sensor %d\n", val);
+
+ startclick := readsensor(hand.sensor);
+
+ # advance until light sensor drops below threshold
+ sys->print("running hand until light sensor clear\n");
+ spawn lightwait(triggered, lightsensor, 0);
+ forward(hand);
+ val = <- triggered;
+ stop(hand);
+ sys->print("sensor %d\n", val);
+
+ stopclick := readsensor(hand.sensor);
+ nclicks := stopclick - startclick;
+ sys->print("startpos %d, endpos %d (nclicks %d)\n", startclick, stopclick, nclicks);
+
+ hand.pos = nclicks/2;
+}
+
+stop(hand : ref Hand)
+{
+ sys->seek(hand.motor, big 0, Sys->SEEKSTART);
+ sys->write(hand.motor, hand.stop, len hand.stop);
+}
+
+stopall()
+{
+ msg := array of byte "s0s0s0";
+ sys->seek(allmotors, big 0, Sys->SEEKSTART);
+ sys->write(allmotors, msg, len msg);
+}
+
+releaseall()
+{
+ msg := array of byte "F0F0F0";
+ sys->seek(allmotors, big 0, Sys->SEEKSTART);
+ sys->write(allmotors, msg, len msg);
+}
+
+forward(hand : ref Hand)
+{
+ sys->seek(hand.motor, big 0, Sys->SEEKSTART);
+ sys->write(hand.motor, hand.fwd, len hand.fwd);
+}
+
+reverse(hand : ref Hand)
+{
+ sys->seek(hand.motor, big 0, Sys->SEEKSTART);
+ sys->write(hand.motor, hand.rev, len hand.rev);
+}
+
+readsensor(fd : ref Sys->FD) : int
+{
+ buf := array [4] of byte;
+ sys->seek(fd, big 0, Sys->SEEKSTART);
+ n := sys->read(fd, buf, len buf);
+ if (n <= 0)
+ return -1;
+ return int string buf[0:n];
+}
+
+handwait(reply : chan of int, hand : ref Hand, clicks : int)
+{
+ blk := array of byte ("b" + string clicks);
+ sys->seek(hand.sensor, big 0, Sys->SEEKSTART);
+ sys->print("handwait(%s)\n", string blk);
+ if (sys->write(hand.sensor, blk, len blk) != len blk)
+ sys->print("handwait write error: %r\n");
+ reply <- = readsensor(hand.sensor);
+}
+
+lightwait(reply : chan of int, fd : ref Sys->FD, on : int)
+{
+ thresh := "";
+ if (on)
+ thresh = "l>" + string ONTHRESH;
+ else
+ thresh = "l<" + string OFFTHRESH;
+ blk := array of byte (thresh);
+ sys->print("lightwait(%s)\n", string blk);
+ sys->seek(fd, big 0, Sys->SEEKSTART);
+ sys->write(fd, blk, len blk);
+ reply <- = readsensor(fd);
+} \ No newline at end of file