forsyth20121205-174063a80cc13 years agoHistory
384 lines · 8.1 KB · Brainfuck
RawDownload
# Model 1implement Clockface; include "sys.m";	sys: Sys; include "draw.m"; Clockface: module {	init:	fn(ctxt: ref Draw->Context, argv: list of string);}; hmpath:	con "motor/0";		# hour-hand motormmpath:	con "motor/2";		# minute-hand motorallmpath:	con "motor/012";	# all motors (for stopall msg) hbpath:	con "sensor/0";	# hour-hand sensormbpath:	con "sensor/2";	# minute-hand sensorlspath:	con "sensor/1";	# light sensor; ONTHRESH:	con 780;		# light sensor thresholdsOFFTHRESH:	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;	time:		int;}; lightsensor:	ref Sys->FD;allmotors:		ref Sys->FD;hourhand:	ref Hand;minutehand:	ref Hand;timedata:		array of byte;readq:		list of Sys->Rread;verbose		:= 0; init(nil: ref Draw->Context, argv: list of string){	sys = load Sys Sys->PATH; 	argv = tl argv;	if (len argv > 0 && hd argv == "-v") {		verbose++;		argv = tl argv;	}	if (len argv != 1) {		sys->print("usage: [-v] legodir\n");		raise "fail:usage";	}	legodir := hd argv + "/"; 	# set up our control file	f2c := sys->file2chan("/chan", "clockface");	if (f2c == nil) {		sys->print("cannot create clockface channel: %r\n");		return;	} 	# get the motor files	log("opening motor files");	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	log("opening sensor files");	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 "r7", array of byte "f7", array of byte "s7", 0, 00);	minutehand = ref Hand(mm, mb, array of byte "f7", array of byte "r7", array of byte "s7", 0, 00); 	log("setting sensor types");	setsensortypes(hourhand, minutehand, lightsensor); 	# get the hands to 12 o'clock	reset();	log(sys->sprint("H %d, M %d", hourhand.pos, minutehand.pos));	spawn srvlink(f2c);} srvlink(f2c: ref Sys->FileIO){	tick := chan of int;	spawn eggtimer(tick); 	for (;;) alt {	(nil, count, fid, rc) := <-f2c.read =>		if (rc == nil) {			close(fid);			continue;		}		if (count < len timedata) {			rc <-= (nil, "read too small");			continue;		}		if (open(fid))			readq = rc :: readq;		else			rc <-= (timedata, nil); 	(nil, data, fid, wc) := <-f2c.write =>		if (wc == nil) {			close(fid);			continue;		}		(nil, toks) := sys->tokenize(string data, ": \t\n");		if (len toks == 2) {			wc <-= (len data, nil);			hourhand.time = int hd toks % 12;			minutehand.time = int hd tl toks % 60;			sethands();		} else if (len toks == 1 && hd toks == "reset") {			wc <-= (len data, nil);			reset();		} else			wc <-= (0, "syntax is hh:mm or `reset'"); 	<-tick =>		if (++minutehand.time == 60) {			minutehand.time = 0;			hourhand.time++;			hourhand.time %= 12;		}		sethands();	}} readers: list of int; open(fid: int): int{	for (rlist := readers; rlist != nil; rlist = tl rlist)		if (hd rlist == fid)			return 1;	readers = fid :: readers;	return 0;} close(fid: int){	rlist: list of int;	for (; readers != nil; readers = tl readers)		if (hd readers != fid)			rlist = hd readers :: rlist;	readers = rlist;} eggtimer(tick: chan of int){	next := sys->millisec();	for (;;) {		next += 60*1000;		sys->sleep(next - sys->millisec());		tick <-= 1;	}} clicks(): (int, int){	h := hourhand.time;	m := minutehand.time;	h = ((h * NCLICKS) / 12) + ((m * NCLICKS) / (12 * 60));	m = (m * NCLICKS) / 60;	return (h, m);} sethands(){	timedata = array of byte sys->sprint("%2d:%.2d\n", (hourhand.time+11) % 12 + 1, minutehand.time);	for (; readq != nil; readq = tl readq)		alt {		(hd readq) <-= (timedata, nil) => ;		* => ;		} 	(hclk, mclk) := clicks();	for (i := 0; i < 6; i++) {		hdelta := clickdistance(hourhand.pos, hclk, NCLICKS);		mdelta := clickdistance(minutehand.pos, mclk, NCLICKS);		if (hdelta != 0)			sethand(hourhand, hdelta);		else if (mdelta != 0)			sethand(minutehand, mdelta);		else			break;	}	releaseall();} 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->write(h1.sensor, button, len button);	sys->write(h2.sensor, button, len button);	sys->write(ls, light, len light);} HOUR_ADJUST: con 1;MINUTE_ADJUST: con 2; reset(){	# run the motors until hands are well away from 12 o'clock (below threshold) 	val := readsensor(lightsensor);	if (val > OFFTHRESH) {		triggered := chan of int;		log("wait for hands clear of light sensor");		spawn lightwait(triggered, lightsensor, 0);		forward(minutehand);		reverse(hourhand);		val = <-triggered;		stopall();		log("sensor "+string val);	} 	resethand(hourhand);	hourhand.pos += HOUR_ADJUST;	resethand(minutehand);	minutehand.pos += MINUTE_ADJUST;	sethands();} 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	log("running hand until light sensor activated");	spawn lightwait(triggered, lightsensor, 1);	forward(hand);	val = <-triggered;	stop(hand);	log("sensor "+string val); 	startclick := readsensor(hand.sensor); 	# advance until light sensor drops below threshold	log("running hand until light sensor clear");	spawn lightwait(triggered, lightsensor, 0);	forward(hand);	val = <-triggered;	stop(hand);	log("sensor "+string val);		stopclick := readsensor(hand.sensor);	nclicks := stopclick - startclick;	log(sys->sprint("startpos %d, endpos %d (nclicks %d)", 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[:n];} handwait(reply: chan of int, hand: ref Hand, clicks: int){	blk := array of byte ("b" + string clicks);	log("handwait "+string blk);	sys->seek(hand.sensor, big 0, Sys->SEEKSTART);	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;	log("lightwait "+string blk);	sys->seek(fd, big 0, Sys->SEEKSTART);	sys->write(fd, blk, len blk);	reply <-= readsensor(fd);} log(msg: string){	if (verbose)		sys->print("%s\n", msg);}