summaryrefslogtreecommitdiff
path: root/appl/demo/camera/camera.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/demo/camera/camera.b')
-rw-r--r--appl/demo/camera/camera.b2557
1 files changed, 2557 insertions, 0 deletions
diff --git a/appl/demo/camera/camera.b b/appl/demo/camera/camera.b
new file mode 100644
index 00000000..9bfff91b
--- /dev/null
+++ b/appl/demo/camera/camera.b
@@ -0,0 +1,2557 @@
+implement Camera;
+
+include "sys.m";
+ sys : Sys;
+include "daytime.m";
+ daytime: Daytime;
+include "styx.m";
+ styx: Styx;
+ Rmsg, Tmsg: import styx;
+include "styxservers.m";
+ styxservers: Styxservers;
+ Fid, Navigator, Navop: import styxservers;
+ Styxserver, Eexists, Eperm, Ebadfid, Enotdir, Enotfound, Ebadarg: import styxservers;
+ nametree: Nametree;
+ Tree: import nametree;
+include "string.m";
+ str : String;
+include "draw.m";
+include "arg.m";
+
+Camera : module {
+ init : fn (nil : ref Draw->Context, argv : list of string);
+};
+
+cdp_get_product_info: con 16r01;
+cdp_get_image_specifications: con 16r02;
+cdp_get_camera_status: con 16r03;
+cdp_set_product_info: con 16r05;
+cdp_get_camera_capabilities: con 16r10;
+cdp_get_camera_state: con 16r11;
+cdp_set_camera_state: con 16r12;
+cdp_get_camera_defaults: con 16r13;
+cdp_set_camera_defaults: con 16r14;
+cdp_restore_camera_states: con 16r15;
+cdp_get_scene_analysis: con 16r18;
+cdp_get_power_mode: con 16r19;
+cdp_set_power_mode: con 16r1a;
+cdp_get_s1_mode: con 16r1d;
+cdp_set_s1_mode: con 16r1e;
+cdp_start_capture: con 16r30;
+cdp_get_file_list: con 16r40;
+cdp_get_new_file_list: con 16r41;
+cdp_get_file_data: con 16r42;
+cdp_erase_file: con 16r43;
+cdp_get_storage_status: con 16r44;
+cdp_set_file_data: con 16r47;
+cdp_get_file_tag: con 16r48;
+cdp_set_user_file_tag: con 16r49;
+cdp_get_clock: con 16r70;
+cdp_set_clock: con 16r71;
+cdp_get_error: con 16r78;
+cdp_get_interface_timeout: con 16r90;
+cdp_set_interface_timeout: con 16r91;
+
+cdp_header_len: con 12;
+
+T_DIR: con 0;
+T_CTL: con 1;
+T_ABILITIES: con 2;
+T_TIME: con 3;
+T_JPGDIR: con 4;
+T_JPG: con 5;
+T_STORAGE: con 6;
+T_POWER: con 7;
+T_THUMB: con 8;
+T_THUMBDIR: con 9;
+T_STATE: con 10;
+T_INTERFACE: con 11;
+
+MAXFILESIZE : con 5000000;
+TIMEOUT : con 4000;
+
+nextjpgqid, nexttmbqid, dirqid, Qctl, Qabl, Qstore: int;
+Qstate, Qtime, Qjpgdir, Qpwr, Qthumbdir, Qinterface : int;
+
+error_table := array [] of {
+ "No Error",
+ "Unimplemented",
+ "Unsupported Version",
+ "Application Timeout",
+ "Internal Error",
+ "Parameter Error",
+ "File System Null",
+ "File Not Found",
+ "Data Section Not Found",
+ "Invalid File Type",
+ "Unknown Drive",
+ "Drive Not Mounted",
+ "System Busy",
+ "Battery Low",
+};
+
+bintro := array [] of {
+ byte 16ra5,
+ byte 16r5a,
+ byte 16r00,
+ byte 16rc8,
+ byte 16r00,
+ byte 16r02,
+ byte 16rc9,
+};
+
+bak := array [] of {
+ byte 16r5a, # 2 byte header
+ byte 16ra5,
+ byte 16r55, # I/F Type
+ byte 16r00, # Comm Flag
+ byte 16r00,
+ byte 16r00,
+ byte 16r00,
+ byte 16r00,
+ byte 16r00,
+ byte 16r00,
+ byte 16r00,
+ byte 16r00,
+ byte 16r00,
+};
+
+pwl := array[] of {
+ byte 0,
+ byte 0,
+};
+
+pak := array [] of {
+ byte 0,
+ byte 0,
+};
+
+SERIAL, USB, IRDA: con (1<<iota);
+BEACON, BEACONRESULT: con (1<<iota);
+
+Camera_adt: adt {
+ port_type: int;
+ port_num: int;
+ command: int;
+ mode: int;
+ fd: ref Sys->FD;
+ ctlfd: ref Sys->FD;
+ cdp: array of byte;
+ bufbytes: int;
+ baud: int;
+ dfs, hfs: int; # device and host frame sizes
+ stat: int; # eia status file
+};
+
+statopt := array[] of {
+ "status",
+ "stat",
+};
+
+DL_QUANTA: con 20000;
+
+TOUT: con -1729;
+
+Partialtag: adt {
+ offset, length, filesize: int;
+};
+
+Cfile: adt {
+ driveno: int;
+ pathname: array of byte;
+ dosname: array of byte;
+ filelength: int;
+ filestatus: int;
+ thumblength: int;
+ thumbqid: int;
+};
+
+Fitem : adt {
+ qid: Sys->Qid;
+ cf: Cfile;
+};
+
+C: Camera_adt;
+
+filelist: array of Fitem;
+reslength: int;
+currentstate := "";
+wait : int;
+usecache := 0;
+connected : int;
+recon := 0;
+verbosity := 4;
+interfacepath := "";
+interfacepaths : array of string;
+camname := "";
+gpid : int;
+
+init(nil : ref Draw->Context, argv : list of string)
+{
+ err: string;
+ sys = load Sys Sys->PATH;
+ gpid = sys->pctl(Sys->NEWPGRP, nil);
+
+ str = load String String->PATH;
+ daytime = load Daytime Daytime->PATH;
+
+ styx = load Styx Styx->PATH;
+ styx->init();
+ styxservers = load Styxservers Styxservers->PATH;
+ styxservers->init(styx);
+ nametree = load Nametree Nametree->PATH;
+ nametree->init();
+ arg := load Arg Arg->PATH;
+
+ filelist = array[200] of Fitem;
+ C.port_num = 0; # XXXXX from argv
+ C.port_type = SERIAL; # Serial only for now
+ C.baud = 115200;
+ C.dfs = C.hfs = 1023;
+ C.cdp = array [DL_QUANTA] of byte;
+ C.mode = BEACON;
+
+ ex.pnum = -1;
+ ex.offset = -1;
+ cachelist = nil;
+
+ nextjpgqid = getqid(1, T_JPG);
+ nexttmbqid = getqid(1, T_THUMB);
+ dirqid = getqid(1,T_JPGDIR);
+ Qctl = getqid(Qroot,T_CTL);
+ Qabl = getqid(Qroot,T_ABILITIES);
+ Qstore = getqid(Qroot,T_STORAGE);
+ Qtime = getqid(Qroot,T_TIME);
+ Qstate = getqid(Qroot,T_STATE);
+ Qpwr = getqid(Qroot,T_POWER);
+ Qjpgdir = getqid(Qroot,T_JPGDIR);
+ Qthumbdir = getqid(Qroot,T_THUMBDIR);
+ Qinterface = getqid(Qroot,T_INTERFACE);
+
+ camname = "Camera";
+ extrafilelist: list of string = nil;
+ arg->init(argv);
+ arg->setusage("camera [-b baud] [-F framesize] [-f extrafiles] [-p port] [-n name] [-v verbosity]");
+ while ((opt := arg->opt()) != 0) {
+ case opt {
+ 'n' =>
+ camname = arg->earg();
+ 'v' =>
+ verbosity = int arg->earg();
+ 'F' =>
+ C.dfs = C.hfs = int arg->earg();
+ 'b' =>
+ C.baud = int arg->earg();
+ 'p' =>
+ C.port_num = int arg->earg();
+ 'c' =>
+ usecache = 1;
+ 'f' =>
+ extrafilelist = arg->earg() :: extrafilelist;
+ * =>
+ arg->usage();
+ }
+ }
+ arg = nil;
+ interfacepaths = array[len extrafilelist] of string;
+ # sys->print("INTERFACEPATHS: %d\n", len extrafilelist);
+ for (i := 0; i < len interfacepaths; i++) {
+ interfacepaths[i] = hd extrafilelist;
+ # sys->print("INTERFACEPATH %d: %s\n", i, hd extrafilelist);
+ extrafilelist = tl extrafilelist;
+ }
+
+ print(sys->sprint("Trying to connect to eia%d...\n",C.port_num),2);
+ case C.port_type {
+ SERIAL =>
+ # open port and return fd
+ (C.fd, C.ctlfd, err) = serialport(C.port_num);
+ if (C.fd == nil) {
+ print("Could not open serial port\n",1);
+ exit;
+ }
+ USB =>
+ ;
+ IRDA =>
+ ;
+ * =>
+ ;
+ }
+ if (connect() != 0) {;
+ print("Connection failed\n",1);
+ exit;
+ }
+ recon = 0;
+ print("Connected!\n",2);
+ set_interface_timeout();
+ set_camera_properties();
+ get_file_list();
+ connected = 1;
+ ignoreabls = nil;
+ get_camera_capabilities();
+ sync := chan of int;
+ spawn serveloop(sys->fildes(0), sync);
+ <-sync;
+}
+
+set_camera_properties()
+{
+ for (i := 0; i < len set_camera_props; i++)
+ set_camera_state(set_camera_props[i].t0,set_camera_props[i].t1);
+}
+
+set_camera_props := array[] of {
+ ("mcap", 0),
+ ("acpd", 65535),
+ ("actc", 65535),
+ ("btpd", 65535),
+ ("bttc", 65535),
+ ("flty", 1246774599),
+ ("ssvl", 0),
+};
+
+argval(argv: list of string, arg: string): string
+{
+ if (arg == "") return "";
+ if (arg[0] != '-') arg = "-" + arg;
+ while (argv != nil) {
+ if (hd argv == arg && tl argv != nil && (hd tl argv)[0] != '-')
+ return tonext(tl argv);
+ argv = tl argv;
+ }
+ return "";
+}
+
+tonext(los: list of string): string
+{
+ s := "";
+ while (los != nil) {
+ if ((hd los)[0] != '-') s += " " + hd los;
+ else break;
+ los = tl los;
+ }
+ if (s != "") s = s[1:];
+ return s;
+}
+
+int2hex(i:int): int
+{
+ i2 := 0;
+ s := string i;
+ for (k := 0; k < len s; k++)
+ i2 = (i2 * 16) + int s[k:k+1];
+ return i2;
+}
+
+connect(): int
+{
+ connected = 0;
+ datain := chan of array of byte;
+ pchan := chan of int;
+ tick := chan of int;
+ reset(C.ctlfd);
+
+ spawn timer2(tick,TIMEOUT * 2);
+ tpid := <-tick;
+
+ spawn beacon_intro(datain, pchan, C.fd);
+ pid := <- pchan;
+ # beacon phase
+ Beacon: for (;;) {
+ alt {
+ buf := <- datain =>
+ # got some data
+ case C.mode {
+ BEACON =>
+ if (beacon_ok(buf)) {
+ print("Got beacon\n",3);
+ beacon_ack(C);
+ spawn beacon_result(datain, pchan, C.fd);
+ pid = <-pchan;
+ C.mode = BEACONRESULT;
+ break;
+ }
+ else {
+ print("resetting\n",3);
+ reset(C.ctlfd);
+ }
+ BEACONRESULT =>
+ kill(tpid);
+
+ print("Checking beacon result\n",3);
+ if (beacon_comp(buf, C) == 0) {
+ return 0;
+ break Beacon;
+ }
+ return -1;
+ }
+ <- tick =>
+ kill(pid);
+ return -1; # failure
+ }
+ }
+}
+
+CTL, ABILITIES, DATA, JPG, PIC, TIME, CONV: con iota;
+NAME, FSIZE, PHOTO, THUMB: con iota;
+
+Qdir : con iota;
+
+contains(s: string, test: string): int
+{
+ num :=0;
+ if (len test > len s) return 0;
+ for (i := 0; i < (1 + (len s) - (len test)); i++) {
+ if (test == s[i:i+len test]) num++;
+ }
+ return num;
+}
+
+abilitiesfilter := array[] of {
+ "Time Format",
+ "Date Format",
+ "File Type",
+ "Video",
+ "Media",
+ "Sound",
+ "Volume",
+ "Reset Camera",
+ "Slide",
+ "Timelapse",
+ "Burst",
+ "Power",
+ "Sleep",
+};
+
+ignoreabls : list of string;
+
+defattr : list of (string, int);
+defaultattr, currentattr: array of (string, int);
+
+filterabls(pname, desc: string): int
+{
+ for (i := 0; i < len abilitiesfilter; i++) {
+ if (contains(desc, abilitiesfilter[i])) {
+ ignoreabls = pname :: ignoreabls;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+mountit(dfd, mountfd: ref sys->FD, sync: chan of int)
+{
+ sys->pctl(sys->NEWNS | sys->NEWFD, 2 :: dfd.fd :: mountfd.fd :: nil);
+ sync <-= 1;
+ mountfd = sys->fildes(mountfd.fd);
+ dfd = sys->fildes(dfd.fd);
+ if (sys->mount(mountfd, nil, "/", sys->MREPL | sys->MCREATE, nil) == -1) {
+ sys->fprint(sys->fildes(2), "cannot mount\n");
+ spawn exporterror(dfd, sys->sprint("%r"));
+ } else {
+ sync = chan of int;
+ spawn exportpath(sync, dfd);
+ <-sync;
+ }
+}
+
+exporterror(dfd: ref Sys->FD, error: string)
+{
+ tmsg := Tmsg.read(dfd, 0);
+ if (tmsg == nil) {
+ sys->fprint(sys->fildes(2), "exporterror() EOF\n");
+ exit;
+ }
+ pick t := tmsg {
+ Readerror =>
+ sys->fprint(sys->fildes(2), "exporterror() Readerror\n");
+ * =>
+ reply: ref Rmsg = ref Rmsg.Error(tmsg.tag, error);
+ data := reply.pack();
+ sys->write(dfd, data, len data);
+ }
+}
+
+exportpath(sync: chan of int, dfd: ref sys->FD)
+{
+ sync <-= 1;
+ sys->export(dfd, "/", Sys->EXPWAIT);
+}
+
+Qroot : con int iota;
+
+ss : ref Styxserver;
+uid: string;
+
+exitfid := -1;
+
+getuid()
+{
+ buf := array [100] of byte;
+ fd := sys->open("/dev/user", Sys->OREAD);
+ uidlen := sys->read(fd, buf, len buf);
+ uid = string buf[0: uidlen];
+}
+
+dir(name: string, perm: int, length: int, qid: int): Sys->Dir
+{
+ d := sys->zerodir;
+ d.name = name;
+ d.uid = uid;
+ d.gid = uid;
+ d.qid.path = big qid;
+ if (perm & Sys->DMDIR)
+ d.qid.qtype = Sys->QTDIR;
+ else {
+ d.qid.qtype = Sys->QTFILE;
+ d.length = big length;
+ }
+ d.mode = perm;
+ d.atime = d.mtime = daytime->now();
+ return d;
+}
+
+User: adt {
+ attachfid: int;
+ attr: array of (string, int);
+};
+
+users : array of User;
+
+getuser(fid: int): int
+{
+ for (i := 0; i < len users; i++)
+ if (users[i].attachfid == fid)
+ return i;
+ return -1;
+}
+
+getattr(pname: string): int
+{
+ for (i := 0; i < len defaultattr; i++)
+ if (defaultattr[i].t0 == pname)
+ return i;
+ return -1;
+}
+
+serveloop(fd : ref sys->FD, sync: chan of int)
+{
+ tchan: chan of ref Tmsg;
+ srv: ref Styxserver;
+ echan := chan of string;
+ users = array[20] of { * => User (-1, nil) };
+ sys->pctl(Sys->FORKNS, nil);
+ sync <-= 1;
+ print("serveloop\n",5);
+ getuid();
+ (tree, treeop) := nametree->start();
+ tree.create(big Qroot, dir(".",8r555 | sys->DMDIR,0,Qroot));
+ tree.create(big Qroot, dir("ctl",8r222,0,Qctl));
+ tree.create(big Qroot, dir("abilities",8r444,0,Qabl));
+ tree.create(big Qroot, dir("storage",8r444,0,Qstore));
+ tree.create(big Qroot, dir("power",8r444,0,Qpwr));
+ tree.create(big Qroot, dir("date",8r666,0,Qtime));
+ tree.create(big Qroot, dir("state",8r666,0,Qstate));
+ tree.create(big Qroot, dir("jpg",8r777 | sys->DMDIR,0,Qjpgdir));
+ tree.create(big Qroot, dir("thumb",8r777 | sys->DMDIR,0,Qthumbdir));
+ for (j := 0; j < len interfacepaths; j++) {
+ (n, idir) := sys->stat(interfacepaths[j]);
+ if (n != -1) {
+ idir.qid.path = big Qinterface;
+ # intdir := dir("",8r777,0,Qinterface);
+ # intdir.name = idir.name;
+ # intdir.length = idir.length;
+ # intdir.atime = idir.atime;
+ # intdir.mtime = idir.mtime;
+ tree.create(big Qroot, idir);
+ Qinterface += 1<<4;
+ }
+ }
+
+ tmsgqueue := Tmsgqueue.new(50);
+
+ (tchan, srv) = Styxserver.new(fd,Navigator.new(treeop), big Qroot);
+ fd = nil;
+
+ gm, lastgm: ref Tmsg;
+ gm = nil;
+
+ oldfiles = nil;
+ updatetree(tree);
+
+ print("serveloop loop\n",5);
+ alivechan := chan of int;
+ spawn keepalive(alivechan);
+ alivepid := <-alivechan;
+ retryit := 0;
+ notries := 0;
+ readfid := -1;
+ serveloop: for (;;) {
+ wait = daytime->now();
+ if (notries > 5) retryit = 0;
+ if (retryit) {
+ gm = lastgm;
+ notries++;
+ }
+ else {
+ notries = 0;
+ loop: for (;;) {
+ gm = tmsgqueue.pop(readfid);
+ if (gm != nil)
+ break;
+ alt {
+ gm = <-tchan =>
+ break loop;
+ c := <-alivechan =>
+ for (;;) {
+ s := get_clock();
+ wait = daytime->now();
+ # print(sys->sprint("got alivechan: %s",s),1);
+ if (recon) {
+ killchan := chan of int;
+ spawn noresponse(tchan,srv,killchan);
+ reconnect(-1);
+ killchan <-= 1;
+ }
+ else
+ break;
+ }
+ }
+ }
+ }
+ lastgm = gm;
+ retryit = 0;
+ if (gm == nil) {
+ sys->print("exiting!\n");
+ break serveloop; # nil => EOF => last mount was unmounted
+ }
+ print(sys->sprint("Got new GM %s tag: %d\n", gm.text(), gm.tag),4);
+ # print(sys->sprint("Got new GM %s tag: %d\n", gm.text(), gm.tag),2);
+
+ if (!connected) {
+ srv.reply(ref Rmsg.Error(gm.tag, "Could not connect to camera"));
+ print("Error: not connected to camera\n",1);
+ }
+ else pick m := gm {
+ Readerror =>
+ print(sys->sprint( "camera: fatal read error: %s\n", m.error),1);
+ break serveloop;
+ Attach =>
+ nu := getuser(-1);
+ if (nu == -1) {
+ srv.reply(ref Rmsg.Error(m.tag, "Camera in use"));
+ break;
+ }
+ m.uname = string nu;
+ srv.default(m);
+ myattr := array[len currentattr] of (string, int);
+ for (i := 0; i < len myattr; i++)
+ myattr[i] = currentattr[i];
+ users[nu] = User (m.fid, myattr);
+ print("adding user "+string nu, 2);
+ Clunk =>
+ nu := getuser(m.fid);
+ if (nu != -1) {
+ users[nu] = User (-1, nil);
+ print("removing user "+string nu, 2);
+ }
+ if (m.fid == readfid) {
+ # sys->print("readfid clunk: %d\n",readfid);
+ readfid = -1;
+ }
+ srv.default(gm);
+ Remove =>
+ print("Removing file\n",3);
+ f := srv.getfid(m.fid);
+ if (f == nil) {
+ print("Remove: Invalid fid\n",1);
+ srv.reply(ref Rmsg.Error(m.tag, Ebadfid));
+ break;
+ }
+ ftype := gettype(int f.path);
+ if (ftype != T_JPG) {
+ srv.reply(ref Rmsg.Error(m.tag, "Cannot remove file"));
+ break;
+ }
+ else {
+ for (i := 0; i < reslength; i++) {
+ if (f.path == filelist[i].qid.path) {
+ print("removing filelist\n",5);
+ if (erase_file(filelist[i].cf) != 0) {
+ if (!recon)
+ srv.reply(ref Rmsg.Error(m.tag, "Cannot remove file"));
+ break;
+ }
+
+ srv.delfid(f);
+ if (get_file_list() != 0)
+ srv.reply(ref Rmsg.Error(m.tag, "Cannot read files"));
+ else {
+ updatetree(tree);
+ srv.reply(ref Rmsg.Remove(m.tag));
+ }
+ break;
+ }
+ }
+ }
+ Read =>
+ print("got read request in serveloop\n",6);
+ (f,e) := srv.canread(m);
+ if(f == nil)
+ break;
+ if (f.qtype & Sys->QTDIR) {
+ print("reading directory\n",5);
+ srv.read(m);
+ break;
+ }
+ data : array of byte;
+ case gettype(int f.path) {
+ T_INTERFACE =>
+ (dir, intdata) := readinterface(int f.path, m.offset, m.count);
+ if (dir != nil && m.offset == big 0) {
+ dir.qid.path = f.path;
+ tree.wstat(f.path, *dir);
+ }
+ srv.reply(ref Rmsg.Read(m.tag, intdata));
+ T_POWER =>
+ print("reading power mode...\n",3);
+ data = array of byte get_power_mode();
+ if (!recon) srv.reply(styxservers->readbytes(m, data));
+
+ T_TIME =>
+ print("reading clock...\n",3);
+ data = array of byte get_clock();
+ if (!recon)
+ srv.reply(styxservers->readbytes(m, data));
+
+ T_ABILITIES =>
+ data = array of byte get_camera_capabilities();
+ if (!recon)
+ srv.reply(styxservers->readbytes(m, data));
+
+ T_JPG =>
+ # sys->print("Read Jpg: user %d\n", int f.uname);
+ if (readfid != -1 && readfid != m.fid) {
+ tmsgqueue.push(m);
+ # sys->print("in use!\n");
+ # srv.reply(ref Rmsg.Error(m.tag, "Camera in use, please wait"));
+ break;
+ }
+ readfid = m.fid;
+ data = photoread2(f.path, m,tree,0);
+ if (!recon)
+ srv.reply(ref Rmsg.Read(m.tag, data));
+
+ T_THUMB =>
+ if (readfid != -1 && readfid != m.fid) {
+ # srv.reply(ref Rmsg.Error(m.tag, "Camera in use, please wait"));
+ tmsgqueue.push(m);
+ break;
+ }
+ readfid = m.fid;
+ # sys->print("Read Thumb: user %d\n", int f.uname);
+ data = photoread2(f.path, m,tree,1);
+ if (!recon)
+ srv.reply(ref Rmsg.Read(m.tag, data));
+
+ T_STATE =>
+ if (currentstate == "") srv.reply(ref Rmsg.Error(m.tag, "No state requested"));
+ else {
+ data = array of byte get_camera_state(currentstate,int m.offset);
+ if (!recon)
+ srv.reply(ref Rmsg.Read(m.tag, data));
+ }
+
+ T_STORAGE =>
+ data = array of byte get_storage_status();
+ if (!recon) {
+ if (len data == 0)
+ srv.reply(ref Rmsg.Error(m.tag, "Could not read storage status"));
+ else
+ srv.reply(styxservers->readbytes(m, data));
+ }
+ * =>
+ srv.reply(ref Rmsg.Error(m.tag, "Cannot read file"));
+ }
+ # if (readfid != -1)
+ # sys->print("readfid set: %d\n",readfid);
+ Write =>
+ print("got write request in serveloop\n",6);
+
+ (f,e) := srv.canwrite(m);
+ if(f == nil) {
+ print("cannot write to file\n",1);
+ break;
+ }
+ wtype := gettype(int f.path);
+ (n, s) := sys->tokenize(string m.data, " \t\n");
+ if (wtype == T_TIME) {
+ if (set_clock(string m.data) != 0)
+ srv.reply(ref Rmsg.Error(m.tag, "Invalid date time format\n" +
+ "Usage: MM/DD/YY HH/MM/SS\n"));
+ else srv.reply(ref Rmsg.Write(m.tag, len m.data));
+
+ }
+ else if (wtype == T_CTL) {
+ err := "";
+ case hd s {
+ "refresh" =>
+ # for (i := 0; i < reslength; i++) {
+ # tree.remove(filelist[i].qid.path);
+ # tree.remove(big filelist[i].cf.thumbqid);
+ # }
+ if (get_file_list() != 0)
+ err = "Error: Could not read from camera";
+ else
+ updatetree(tree);
+ # for (i = 0; i < reslength; i++)
+ # buildfilelist(tree, i);
+ "snap" =>
+ nu := int f.uname;
+ print(sys->sprint("User %d taking photo\n",nu),2);
+ for (i := 0; i < len currentattr; i++) {
+ # sys->print("user: %s=%d current: %s=%d\n",
+ # users[nu].attr[i].t0,users[nu].attr[i].t1,
+ # currentattr[i].t0,currentattr[i].t1);
+ if (users[nu].attr[i].t1 != currentattr[i].t1) {
+ set_camera_state(users[nu].attr[i].t0, users[nu].attr[i].t1);
+ sys->sleep(100);
+ }
+ }
+ e1 := capture();
+ if (e1 == -1) {
+ err = "Cannot communicate with camera";
+ break;
+ }
+ if (e1 != 0) {
+ err = "Error: "+error_table[e1];
+ break;
+ }
+ sys->sleep(4000);
+ if (get_file_list() != 0) {
+ err = "Error: Could not read from camera";
+ break;
+ }
+ updatetree(tree);
+ * =>
+ if (n == 2) { # assume that it is a (string, int) tuple
+ na := getattr(hd s);
+ if (na == -1)
+ err = "Invalid command name '"+hd s+"'";
+ else {
+ e1 := set_camera_state(hd s, int hd tl s);
+ if (e1 != nil)
+ err = e;
+ else
+ users[int f.uname].attr[na].t1 = int hd tl s;
+ }
+ }
+
+ }
+
+ if (!recon) {
+ if (err != "") {
+ print(err+"\n",1);
+ srv.reply(ref Rmsg.Error(m.tag, err));
+ }
+ else srv.reply(ref Rmsg.Write(m.tag, len m.data));
+ }
+ }
+ else if (wtype == T_STATE) {
+ if (s != nil)
+ currentstate = hd s;
+ srv.reply(ref Rmsg.Write(m.tag, len m.data));
+ }
+ else srv.reply(ref Rmsg.Error(m.tag, "Could not write to file"));
+ Wstat =>
+ print("Got Wstat command in serveloop\n",6);
+ srv.reply(ref Rmsg.Error(m.tag, "Wstat failed"));
+ * =>
+ srv.default(gm);
+ }
+ if (recon) {
+ retryit = 1;
+ ok := reconnect(4);
+ if (!ok) {
+ srv.reply(ref Rmsg.Error(gm.tag, "Could not connect to camera"));
+ killchan := chan of int;
+ spawn noresponse(tchan,srv,killchan);
+ reconnect(-1);
+ killchan <-= 1;
+ retryit = 0;
+ sys->sleep(100);
+ }
+ }
+ }
+ tree.quit();
+ kill(alivepid);
+ killg(gpid);
+}
+
+Tmsgqueue: adt {
+ start, end, length: int;
+ a : array of ref Tmsg.Read;
+ new: fn (n: int): ref Tmsgqueue;
+ push: fn (t: self ref Tmsgqueue, t: ref Tmsg.Read): int;
+ pop: fn (t: self ref Tmsgqueue, readfid: int): ref Tmsg.Read;
+};
+
+Tmsgqueue.new(n: int): ref Tmsgqueue
+{
+ t : Tmsgqueue;
+ t.start = 0;
+ t.end = 0;
+ t.length = 0;
+ t.a = array[n] of ref Tmsg.Read;
+ return ref t;
+}
+
+Tmsgqueue.push(t: self ref Tmsgqueue,newt: ref Tmsg.Read): int
+{
+ if (t.length >= len t.a)
+ return -1;
+ t.a[t.end] = newt;
+ t.end++;
+ if (t.end >= len t.a)
+ t.end = 0;
+ t.length++;
+ return 0;
+}
+
+Tmsgqueue.pop(t: self ref Tmsgqueue, readfid: int): ref Tmsg.Read
+{
+ if (t.length == 0)
+ return nil;
+ m := t.a[t.start];
+ if (readfid != -1 && readfid != m.fid)
+ return nil;
+ t.start++;
+ if (t.start >= len t.a)
+ t.start = 0;
+ t.length--;
+ return m;
+}
+
+noresponse(tchan: chan of ref Tmsg, srv: ref Styxservers->Styxserver, killchan : chan of int)
+{
+ for (;;) alt {
+ k := <- killchan =>
+ return;
+ gm := <- tchan =>
+ print("noresponse: Returning Error\n",1);
+ srv.reply(ref Rmsg.Error(gm.tag, "Could not connect to camera"));
+ sys->sleep(100);
+ }
+}
+
+photoread2(qid: big, m: ref Tmsg.Read, tree: ref Nametree->Tree, isthumb: int): array of byte
+{
+ photonum := -1;
+ data : array of byte;
+ # sys->print("photoread: qid: %d resl: %d\n",int qid,reslength);
+ for (i := 0; i < reslength; i++) {
+ # sys->print("%d: %s %d\n",i, sconv(filelist[i].cf.dosname),int filelist[i].qid.path);
+ if (!isthumb && qid == filelist[i].qid.path) {
+ photonum = i;
+ break;
+ }
+ else if (isthumb && int qid == filelist[i].cf.thumbqid) {
+ photonum = i;
+ break;
+ }
+ }
+ if (photonum >= reslength || photonum < 0) {
+ print(sys->sprint( "error: photonum = %d (reslength = %d)\n", photonum,reslength),1);
+ return nil;
+ }
+ offset := int m.offset;
+ dosname := filelist[photonum].cf.dosname;
+ filelen := filelist[photonum].cf.filelength;
+ for (k := 0; k < 5; k++) {
+ if (filelen == 0) {
+ get_file_size(photonum);
+ print(sys->sprint("\tFilelen: %d => ",filelen),5);
+ filelen = filelist[photonum].cf.filelength;
+ print(sys->sprint("%d\n",filelen),5);
+ tree.wstat(qid,
+ dir(str->tolower(sconv(filelist[photonum].cf.dosname)),
+ 8r444,
+ filelen,
+ int qid));
+ sys->sleep(1000);
+ }
+ else break;
+ }
+ if (filelen == 0 && !isthumb) return nil; # doesn't matter if filesize is wrong for thumbnail
+ if (isthumb) filelen = filelist[photonum].cf.thumblength;
+ if (usecache && cachesize(dosname, isthumb) == filelen) {
+# print(sys->sprint("Is cached!\n");
+ n := m.count;
+ filesize := cachesize(dosname,isthumb);
+ if (offset >= filesize) return nil;
+ if (offset+m.count >= filesize) n = filesize - offset;
+ data = array[n] of byte;
+ fd := sys->open(cachename(dosname,isthumb), sys->OREAD);
+ if (fd == nil) cachedel(dosname,isthumb);
+ else {
+ sys->seek(fd,m.offset,sys->SEEKSTART);
+ sys->read(fd,data,len data);
+ fd = nil;
+ return data;
+ }
+ }
+# print(sys->sprint("Is NOT cached!\n");
+
+ if (photonum == ex.pnum && offset == ex.offset && ex.isthumb == isthumb)
+ data = ex.data;
+ else if (isthumb)
+ data = getthumb(photonum, offset, m.count);
+ else if (!isthumb)
+ data = getpicture2(photonum, offset, m.count);
+ if (len data > m.count) {
+ ex.pnum = photonum;
+ ex.offset = offset + m.count;
+ ex.data = array[len data - m.count] of byte;
+ ex.data[0:] = data[m.count:len data];
+ ex.isthumb = isthumb;
+ data = data[:m.count];
+ }
+ if (usecache) {
+ fd : ref sys->FD;
+ cname := cachename(dosname,isthumb);
+
+ if (offset == 0)
+ fd = sys->create(cname,sys->OWRITE,8r666);
+ else {
+ fd = sys->open(cname,sys->OWRITE);
+ if (fd != nil)
+ sys->seek(fd,big 0,sys->SEEKEND);
+ }
+ if (fd != nil) {
+ i = sys->write(fd,data,len data);
+ fd = nil;
+ }
+ (n, dir) := sys->stat(cname);
+ if (n == 0) {
+ cacheadd(dosname,isthumb,int dir.length);
+ }
+ }
+ return data;
+}
+
+cachelist : list of (string, int, int);
+
+cacheprint()
+{
+ tmp := cachelist;
+ print("cache:\n",3);
+ while (tmp != nil) {
+ (dn,i1,i2) := hd tmp;
+ print(sys->sprint("\t%s %d %d\n",dn,i1,i2),3);
+ tmp = tl tmp;
+ }
+}
+
+cacheclean()
+{
+ tmp : list of (string, int,int);
+ tmp = nil;
+ while (cachelist != nil) {
+ (dosnm,it,fl) := hd cachelist;
+ for (i := 0; i < reslength; i++) {
+ filelen := filelist[i].cf.filelength;
+ if (it) filelen = filelist[i].cf.thumblength;
+ if (sconv(filelist[i].cf.dosname) == dosnm && filelen == fl) {
+ tmp = (dosnm,it,fl) :: tmp;
+ break;
+ }
+ }
+ cachelist = tl cachelist;
+ }
+ cachelist = tmp;
+}
+
+cacheadd(dosname1: array of byte, isthumb, filelen: int)
+{
+ dosname := sconv(dosname1);
+ tmp : list of (string, int,int);
+ tmp = nil;
+ updated := 0;
+ while (cachelist != nil) {
+ (dosnm,it,fl) := hd cachelist;
+ if (dosname == dosnm && it == isthumb) {
+ updated = 1;
+ tmp = (dosnm,it,filelen) :: tmp;
+ }
+ else
+ tmp = (dosnm,it,fl) :: tmp;
+ cachelist = tl cachelist;
+ }
+ if (updated == 0)
+ tmp = (dosname,isthumb,filelen) :: tmp;
+ cachelist = tmp;
+}
+
+
+cachedel(dosname1: array of byte, isthumb: int)
+{
+ dosname := sconv(dosname1);
+ tmp : list of (string, int,int);
+ tmp = nil;
+ while (cachelist != nil) {
+ (dosnm,it,filelen) := hd cachelist;
+ if (dosname != dosnm || it != isthumb)
+ tmp = (dosnm,it,filelen) :: tmp;
+ cachelist = tl cachelist;
+ }
+ cachelist = tmp;
+}
+
+cachesize(dosname1: array of byte, isthumb: int): int
+{
+ dosname := sconv(dosname1);
+ tmp := cachelist;
+ while (tmp != nil) {
+ (dosnm,it,filelen) := hd tmp;
+ if (dosname == dosnm && isthumb == it) return filelen;
+ tmp = tl tmp;
+ }
+ return -1;
+}
+
+cachename(dosname: array of byte, isthumb: int): string
+{
+ name := "/tmp/" + str->tolower(sconv(dosname));
+ if (isthumb) name = jpg2bit(name);
+ name[len name - 1] = '~';
+ return name;
+}
+
+poll_and_wait(): int
+{
+ print("poll and wait\n",7);
+ write_n(C.fd, pwl, len pwl);
+# sys->sleep(100);
+ if (read_n_to(C.fd, pak, len pak,TIMEOUT) < 0) {
+ print("poll_and_wait: unexpected read failure, exiting...\n",1);
+ return -1;
+ }
+ return 0;
+}
+
+send_packet(): int
+{
+ # computing packet size
+ to_write := C.bufbytes;
+
+ # send the first packet
+ pwl[0] = byte ((1<<5)|(1<<4)|(1<<3)|(1<<2)|(to_write>>8));
+ pwl[1] = byte (to_write&16rff);
+
+ if (poll_and_wait() != 0)
+ return -1;
+# pak[1] == byte 2; ?
+ pak[1] = byte 2;
+
+ wrote_here := write_n(C.fd, C.cdp, to_write);
+ if (wrote_here != to_write)
+ return -1;
+ return 0;
+}
+
+send_message(): int
+{
+ v:= 0;
+ rc := chan of int;
+ tc := chan of int;
+
+ spawn timer2(tc,6000);
+ tpid := <- tc;
+ spawn write_message(rc);
+ rpid := <- rc;
+
+ try := 0;
+ alt {
+ <- tc =>
+ kill(rpid);
+ print("error: write timeout\n",1);
+ v = -2;
+ break;
+ v = <- rc =>
+ kill(tpid);
+ break;
+ }
+ return v;
+}
+
+write_message(rc: chan of int)
+{
+ print("writing msg...\n",6);
+ rc <- = sys->pctl(0, nil);
+ if (send_packet() != 0) {
+ rc <-= -1;
+ return;
+ }
+ pwl[0] = byte 0;
+ pwl[1] = byte 0;
+ wrote_here := write_n(C.fd, pwl, 2);
+ if (wrote_here != 2) {
+ rc <-= -1;
+ return;
+ }
+ rc <-= 0;
+ print("written\n",6);
+}
+
+extra: adt {
+ pnum: int;
+ offset: int;
+ length: int;
+ data: array of byte;
+ isthumb: int;
+};
+
+ex : extra;
+
+getthumb(photonum, offset, maxlength: int): array of byte
+{
+ if (offset != 0) return nil;
+ print("getting thumbnail\n",3);
+ thumbdata: array of byte;
+ err, h, w, ttype: int;
+ file := filelist[photonum].cf;
+ filesize := 13020;
+ if (offset > 0) {
+ filesize = file.thumblength;
+ if (offset >= filesize) return nil;
+ }
+ for(;;){
+ print(sys->sprint("Filesize: %d offset: %d\n",filesize, offset),5);
+ if (offset + maxlength > filesize)
+ maxlength = filesize - offset;
+ l := maxlength;
+
+ C.command = cdp_get_file_data;
+ C.bufbytes = build_cdp_header(C.cdp, 68);
+ off := cdp_header_len;
+ off = set_int(C.cdp[off:], file.driveno, off);
+ off = set_fstring(C.cdp[off:], file.pathname, off);
+ off = set_dosname(C.cdp[off:], file.dosname, off);
+ off = set_int(C.cdp[off:], 1, off);
+
+ off = set_int(C.cdp[off:], offset, off);
+ off = set_int(C.cdp[off:], l, off);
+ off = set_int(C.cdp[off:], filesize, off);
+
+ print(sys->sprint( "getthumbdata %d %d %d\n", offset, maxlength, filesize),5);
+ send_message();
+# sys->sleep(2000);
+ if ((err = receive_message()) != 0) {
+ print(sys->sprint("Error %d\n", err),1);
+ return nil;
+ }
+ off = cdp_header_len;
+ print(sys->sprint( "bufbytes = %d\n", C.bufbytes),5);
+ tmpoffset: int;
+ (tmpoffset, off) = get_int(C.cdp[off:], off);
+ (l, off) = get_int(C.cdp[off:], off);
+ (filesize, off) = get_int(C.cdp[off:], off);
+ print(sys->sprint( "getthumbdata returning %d %d %d\n", offset, l, filesize),5);
+
+ if (offset == 0) {
+ (filesize, off) = get_int(C.cdp[off:off+4], off);
+ (h, off) = get_int(C.cdp[off:off+4], off);
+ (w, off) = get_int(C.cdp[off:off+4], off);
+ (ttype, off) = get_int(C.cdp[off:off+4], off);
+ filelist[photonum].cf.thumblength = filesize;
+ thumbdata = array[filesize] of byte;
+ print(sys->sprint("Thumb (%d,%d) size: %d type: %d\n",w,h,filesize,ttype),5);
+ }
+ if (offset + l > filesize) l = filesize - offset;
+ print(sys->sprint( "Making array of size: %d\n", l),5);
+ thumbdata[offset:] = C.cdp[off:off+l];
+ offset += l;
+ if (offset >= filesize) break;
+ }
+ return thumb2bit(thumbdata,w,h);
+}
+
+getpicture2(photonum, offset, maxlength: int): array of byte
+{
+ file := filelist[photonum].cf;
+ filesize := int file.filelength;
+ print("getting image\n",3);
+ print(sys->sprint("Filesize: %d offset: %d\n",filesize, offset),5);
+ if (offset >= filesize) return nil;
+ if (offset + maxlength > filesize)
+ maxlength = filesize - offset;
+ l := maxlength;
+ C.command = cdp_get_file_data;
+ C.bufbytes = build_cdp_header(C.cdp, 68);
+ off := cdp_header_len;
+ off = set_int(C.cdp[off:], file.driveno, off);
+ off = set_fstring(C.cdp[off:], file.pathname, off);
+ off = set_dosname(C.cdp[off:], file.dosname, off);
+ off = set_int(C.cdp[off:], 0, off);
+
+ off = set_int(C.cdp[off:], offset, off);
+ off = set_int(C.cdp[off:], l, off);
+ off = set_int(C.cdp[off:], filesize, off);
+
+ print(sys->sprint( "getfiledata %d %d %d\n", offset, maxlength, filesize),5);
+ send_message();
+ if ((err := receive_message()) != 0) {
+ print(sys->sprint("Error %d\n", err),1);
+ return nil;
+ }
+ off = cdp_header_len;
+ print(sys->sprint( "bufbytes = %d\n", C.bufbytes),5);
+ (offset, off) = get_int(C.cdp[off:], off);
+ (l, off) = get_int(C.cdp[off:], off);
+ (filesize, off) = get_int(C.cdp[off:], off);
+ print(sys->sprint( "getfiledata returning %d %d %d\n", offset, maxlength, filesize),5);
+ filedata := array[l] of byte;
+ filedata[0:] = C.cdp[off:off+l];
+ return filedata;
+}
+
+erase_file(file: Cfile): int
+{
+ C.command = cdp_erase_file;
+ C.bufbytes = build_cdp_header(C.cdp, 52);
+
+ off := cdp_header_len;
+ off = set_int(C.cdp[off:], file.driveno, off);
+ off = set_fstring(C.cdp[off:], file.pathname, off);
+ off = set_dosname(C.cdp[off:], file.dosname, off);
+ send_message();
+# sys->sleep(1000);
+ if (receive_message() != 0)
+ return -1;
+ return 0;
+}
+
+
+set_power_mode(): int
+{
+ C.command = cdp_set_power_mode;
+ C.bufbytes = build_cdp_header(C.cdp, 0);
+ return (send_message());
+}
+
+get_storage_status(): string
+{
+ s := "";
+
+ C.command = cdp_get_storage_status;
+ C.bufbytes = build_cdp_header(C.cdp, 0);
+ send_message();
+# sys->sleep(2000);
+ if (receive_message() != 0) return "";
+ off := cdp_header_len;
+ taken, available, raw : int;
+ (taken, off) = get_int(C.cdp[off:], off);
+ (available, off) = get_int(C.cdp[off:], off);
+ (raw, off) = get_int(C.cdp[off:], off);
+ s += sys->sprint("Picture Memory\n\tused:\t%d\n\tfree:\t%d",taken,available);
+ if (raw == -1)
+ s += "\n";
+ else
+ s += sys->sprint(" (compressed)\n\t\t%d (raw)\n",raw);
+
+ return s;
+}
+
+get_power_mode(): string
+{
+ mode: int;
+
+ C.command = cdp_get_power_mode;
+ C.bufbytes = build_cdp_header(C.cdp, 0);
+ send_message();
+# sys->sleep(2000);
+ if (receive_message() != 0) return "Could not read power mode";
+ off := cdp_header_len;
+ (mode, off) = get_int(C.cdp[off:], off);
+ return sys->sprint("Power Mode = %d\n", mode);
+}
+
+set_clock_data(s:string): int
+{
+ err := 0;
+ if (s == "") {
+ tm := daytime->local(daytime->now());
+ off := cdp_header_len;
+ C.cdp[cdp_header_len+0] = byte 0;
+ C.cdp[cdp_header_len+1] = byte int2hex(tm.mon+1);
+ C.cdp[cdp_header_len+2] = byte int2hex(tm.mday);
+ C.cdp[cdp_header_len+3] = byte int2hex(tm.year);
+ C.cdp[cdp_header_len+4] = byte 0;
+ C.cdp[cdp_header_len+5] = byte int2hex(tm.hour);
+ C.cdp[cdp_header_len+6] = byte int2hex(tm.min);
+ C.cdp[cdp_header_len+7] = byte int2hex(tm.sec);
+ }
+ else {
+ (n,datetime) := sys->tokenize(s," ");
+ if (n != 2) return 1;
+ off := 0;
+ for (i := 0; i < 2; i++) {
+ (n2,data) := sys->tokenize(hd datetime, "./:");
+ if (n2 != 3) return 1;
+ off++;
+ for (i2 := 0; i2 < 3; i2++) {
+ C.cdp[cdp_header_len+off] = byte int2hex(int hd data);
+ off++;
+ data = tl data;
+ }
+ datetime = tl datetime;
+ }
+ }
+ return 0;
+}
+
+set_clock(s:string): int
+{
+ C.command = cdp_set_clock;
+ C.bufbytes = build_cdp_header(C.cdp, 8);
+ if (set_clock_data(s)) return 1;
+ send_message();
+ if (receive_message() != 0) return 1;
+ return 0;
+}
+
+addzeros(s: string): string
+{
+ s[len s] = ' ';
+ rs := "";
+ start := 0;
+ isnum := 0;
+ for (i := 0; i < len s; i++) {
+ if (s[i] < '0' || s[i] > '9') {
+ if (isnum && i - start < 2) rs[len rs] = '0';
+ rs += s[start:i+1];
+ start = i+1;
+ isnum = 0;
+ }
+ else isnum = 1;
+ }
+ i = len rs - 1;
+ while (i >= 0 && rs[i] == ' ') i--;
+ return rs[:i+1];
+}
+
+get_clock(): string
+{
+ C.command = cdp_get_clock;
+ C.bufbytes = build_cdp_header(C.cdp, 0);
+ send_message();
+ if (receive_message() != 0)
+ return "Could not read clock\n";
+ s := sys->sprint("%x/%x/%x %x:%x:%x", int C.cdp[13],int C.cdp[14],
+ int C.cdp[15], int C.cdp[17], int C.cdp[18], int C.cdp[19]);
+ return "date is "+addzeros(s)+"\n";
+}
+
+get_file_list(): int
+{
+ getoldfiledata();
+ print("getting file list\n",3);
+ C.command = cdp_get_file_list;
+ C.bufbytes = build_cdp_header(C.cdp, 56);
+ setfiledata();
+ send_message();
+ if (receive_message() != 0)
+ return -1;
+ display_filelist();
+ return 0;
+}
+
+setfiledata()
+{
+ off := cdp_header_len;
+ off = set_int(C.cdp[off:], 1, off); # ascending order
+ off = set_int(C.cdp[off:], 1, off); # drive a: internal RAM disk
+ off = set_fstring(C.cdp[off:], array of byte "", off); # set pathname to null
+ off = set_dosname(C.cdp[off:], array of byte "", off); # set Dos filename to null
+}
+
+get_file_size(i: int): int
+{
+ C.command = cdp_get_file_list;
+ C.bufbytes = build_cdp_header(C.cdp, 56);
+ setfiledata2(i);
+ send_message();
+ if (receive_message() != 0) return -1;
+ display_filelist();
+ return 0;
+}
+
+setfiledata2(i: int)
+{
+ off := cdp_header_len;
+ off = set_int(C.cdp[off:], 1, off); # ascending order
+ off = set_int(C.cdp[off:], 1, off); # drive a: internal RAM disk
+ off = set_fstring(C.cdp[off:], filelist[i].cf.pathname, off); # set pathname
+ off = set_dosname(C.cdp[off:], filelist[i].cf.dosname, off); # set Dos filename
+}
+
+set_interface_timeout()
+{
+ print("Setting Interface timeout\n",3);
+ C.command = cdp_set_interface_timeout;
+ C.bufbytes = build_cdp_header(C.cdp, 8);
+ off := cdp_header_len;
+ off = set_int(C.cdp[off:], 100, off);
+ off = set_int(C.cdp[off:], 5, off);
+ send_message();
+# sys->sleep(1000);
+ receive_message();
+}
+
+display_filelist(): string
+{
+ off, i: int;
+
+ off = cdp_header_len;
+ (reslength, off) = get_int(C.cdp[off:], off);
+ s := sys->sprint("Number of entries: %d\n", reslength);
+ for (i = 0; i < reslength; i++) {
+ (filelist[i].cf.driveno, off) = get_int(C.cdp[off:], off);
+ (filelist[i].cf.pathname, off) = get_fstring(C.cdp[off:], off);
+ (filelist[i].cf.dosname, off) = get_dosname(C.cdp[off:], off);
+ (filelist[i].cf.filelength, off) = get_int(C.cdp[off:], off);
+ (filelist[i].cf.filestatus, off) = get_int(C.cdp[off:], off);
+ if (filelist[i].cf.filelength < 0 || filelist[i].cf.filelength > MAXFILESIZE)
+ filelist[i].cf.filelength = 0;
+ s += sys->sprint("\t%d, %s, %s, %d\n", filelist[i].cf.driveno,
+ string filelist[i].cf.pathname,
+ string filelist[i].cf.dosname,
+ filelist[i].cf.filelength);
+ }
+ print(s,5);
+ if (usecache)
+ cacheclean();
+ return s;
+}
+
+get_camera_capabilities(): string
+{
+ print("Get capabilities\n",3);
+ C.command = cdp_get_camera_capabilities;
+ C.bufbytes = build_cdp_header(C.cdp, 0);
+ send_message();
+# sys->sleep(500);
+ if (receive_message() != -1)
+ return capabilities();
+ print("Error recieving abilities message\n",1);
+ return "";
+}
+
+Capability: adt {
+ pname: string;
+ d: string;
+ pick {
+ List =>
+ t: list of (string, int);
+ Range =>
+ min, max, default, current: int;
+ }
+};
+
+caplist: list of ref Capability;
+
+print_camera_capabilities(): string
+{
+ rs := "";
+# p : ref Capability;
+
+ pick p := hd caplist{
+ List =>
+ rs += sys->sprint("Pname = %s ", p.pname);
+ Range =>
+ rs += sys->sprint("Pname = %s min = %d max = %d default = %d ", p.pname,
+ p.min, p.max, p.default);
+ }
+# p := tl p;
+ return rs;
+}
+
+capabilities(): string
+{
+ off, i, ncaps, t: int;
+ l, m, n: int;
+ pname, desc: array of byte;
+ s: array of byte;
+ rs := "";
+ off = cdp_header_len;
+ (ncaps, off) = get_int(C.cdp[off:], off);
+ if (ncaps > 200)
+ return "error reading capabilities\n";
+ rs += sys->sprint("i = %d\n", i);
+ firsttime := 0;
+ if (ignoreabls == nil)
+ firsttime = 1;
+ for (j := 0; j < ncaps; j++) {
+ line := "";
+ (pname, off) = get_pname(C.cdp[off:], off);
+ line += sys->sprint("%s, ", string pname);
+ (t, off) = get_int(C.cdp[off:], off);
+ (desc, off) = get_fstring(C.cdp[off:], off);
+ line += sys->sprint("%s: ", string desc);
+ fact := "";
+ case t {
+ 1 =>
+ t: list of (string, int);
+
+ (l, off) = get_int(C.cdp[off:], off);
+ (m, off) = get_int(C.cdp[off:], off);
+ line += sys->sprint("items: %d factory: %d\n", l, m);
+
+ for (k := 0; k < l; k++) {
+ (s, off) = get_fstring(C.cdp[off:], off);
+ (n, off) = get_int(C.cdp[off:], off);
+ line += sys->sprint(" %s: %d\n", string s, n);
+ if (m == n)
+ fact = sconv(s);
+ t = (sconv(s), n) :: t;
+ }
+ cl := ref Capability.List (sconv(pname), sconv(desc), t);
+ 2 =>
+ (l, off) = get_int(C.cdp[off:], off);
+ (m, off) = get_int(C.cdp[off:], off);
+ (n, off) = get_int(C.cdp[off:], off);
+ line += sys->sprint("min: %d max: %d factory:%d\n", l, m, n);
+ fact = string n;
+ 3 =>
+ (l, off) = get_int(C.cdp[off:], off);
+ case l {
+ 7 =>
+ (s, off) = get_dosname(C.cdp[off:], off);
+ 8 =>
+ (s, off) = get_fstring(C.cdp[off:], off);
+ * =>
+ line += sys->sprint("Invalid type %d\n", l);
+ break;
+ }
+ fact = string s;
+ line += sys->sprint("%s\n", string s);
+ 4 to 8 =>
+ break;
+ 9 =>
+ break;
+ * =>
+ line += sys->sprint("Invalid type %d\n", t);
+ break;
+ }
+ if (firsttime) {
+ if (!filterabls(sconv(pname), string desc))
+ defattr = (sconv(pname), int fact) :: defattr;
+ }
+ if (!isin(ignoreabls, string pname))
+ rs += line;
+ }
+ if (firsttime) {
+ defaultattr = array[len defattr] of (string, int);
+ currentattr = array[len defattr] of (string, int);
+ i = 0;
+ for (;defattr != nil; defattr = tl defattr) {
+ defaultattr[i] = hd defattr;
+ currentattr[i++] = hd defattr;
+ }
+ }
+ return rs;
+}
+
+isin(los: list of string, s: string): int
+{
+ for (;los !=nil; los = tl los)
+ if (hd los == s)
+ return 1;
+ return 0;
+}
+
+set_capture_data(): int
+{
+ C.cdp[cdp_header_len+0] = byte 0;
+ C.cdp[cdp_header_len+1] = byte 0;
+ C.cdp[cdp_header_len+2] = byte 0;
+ C.cdp[cdp_header_len+3] = byte 0;
+ return 4;
+}
+
+get_camera_state(pname: string,offset: int): string
+{
+ if (offset != 0) return "";
+ print(sys->sprint( "get_camera_state(%s)\n", pname),3);
+ C.command = cdp_get_camera_state;
+ off := cdp_header_len;
+ if (pname == "")
+ C.bufbytes = build_cdp_header(C.cdp, 0);
+ else {
+ if (len pname != 4)
+ return "Invalid command name: "+pname+"\n";
+ C.cdp[off+0] = byte pname[0];
+ C.cdp[off+1] = byte pname[1];
+ C.cdp[off+2] = byte pname[2];
+ C.cdp[off+3] = byte pname[3];
+ C.bufbytes = build_cdp_header(C.cdp, 4);
+ }
+ send_message();
+ if (receive_message() != 0) return "Could not read state: "+pname+"\n";
+ off = cdp_header_len;
+ rlen: int;
+ (rlen, off) = get_int(C.cdp[off:],off);
+ s := "";
+ rlen = 1;
+ if (pname == "") {
+ for (q := off; q < len C.cdp; q++) {
+ s[0] = int C.cdp[q];
+ if (s[0] > 0) print(sys->sprint("%s",s),5);
+ }
+ print("\n",5);
+ }
+ for (i := 0; i < rlen; i++) {
+ name, data: array of byte;
+ type1, tmp: int;
+ (name,off) = get_pname(C.cdp[off:],off);
+ (type1,off) = get_int(C.cdp[off:],off);
+ print(sys->sprint( "%d: %s - %d\n", i,pname,type1),5);
+ case type1 {
+ 1 to 5 =>
+ (tmp,off) = get_int(C.cdp[off:],off);
+ data = array of byte string tmp;
+ 6 =>
+ (data,off) = get_pname(C.cdp[off:],off);
+ 7 =>
+ (data,off) = get_dosname(C.cdp[off:],off);
+ 8 =>
+ (data,off) = get_fstring(C.cdp[off:],off);
+ * =>
+ data = array of byte "!ERROR!";
+ }
+ # if (string data == "!ERROR!") return "";
+# if (rlen == 1)
+# s = string data;
+# else s += sys->sprint("%s: %s\n",string name, string data);
+ s += sys->sprint("%s: %s\n",string name, string data);
+ }
+ return s;
+}
+
+
+set_camera_state(pname: string, val: int): string
+{
+ print(sys->sprint( "set_camera_state(%s, %d)\n", pname, val),3);
+ if (len pname != 4)
+ return "Command name must be 4 characters";
+ off := cdp_header_len;
+ C.cdp[off+0] = byte pname[0];
+ C.cdp[off+1] = byte pname[1];
+ C.cdp[off+2] = byte pname[2];
+ C.cdp[off+3] = byte pname[3];
+ off += 4;
+ off = set_int(C.cdp[off:], val, off);
+
+ C.command = cdp_set_camera_state;
+ C.bufbytes = build_cdp_header(C.cdp, 8);
+ send_message();
+# sys->sleep(1000);
+ if ((e := receive_message()) == 0) {
+ na := getattr(pname);
+ if (na != -1)
+ currentattr[na].t1 = val;
+ return nil;
+ }
+ else
+ return error_table[e];
+}
+
+capture(): int
+{
+ C.command = cdp_get_camera_status;
+ C.bufbytes = build_cdp_header(C.cdp, 0);
+ send_message();
+# sys->sleep(1000);
+ if (receive_message() != 0)
+ return -1;
+
+ d := set_capture_data();
+ C.command = cdp_start_capture;
+ C.bufbytes = build_cdp_header(C.cdp, d);
+ send_message();
+# sys->sleep(3000);
+ return receive_message();
+}
+
+dump_message()
+{
+ print(sys->sprint(" Message length = %d\n", C.bufbytes),5);
+ print(sys->sprint(" CDP Length = %d\n", (int C.cdp[2]<<8)+(int C.cdp[3])),5);
+ print(sys->sprint(" CDP Version = %d\n", int C.cdp[4]),5);
+ print(sys->sprint(" CDP Command = %x\n", int ((C.cdp[8]<<8)|(C.cdp[9]))),5);
+ print(sys->sprint(" CDP Result Code = %d\n", int ((C.cdp[10]<<8)|(C.cdp[11]))),5);
+}
+
+build_cdp_header(cdp: array of byte, x: int): int
+{
+ cdp[4] = byte 0;
+ cdp[5] = byte 0;
+ cdp[6] = byte 0;
+ cdp[7] = byte 0;
+ cdp[8] = byte ((C.command>>8)&16rff);
+ cdp[9] = byte (C.command&16rff);
+ cdp[10] = byte 0;
+ cdp[11] = byte 0;
+
+ l := 8 + x;
+ cdp[0] = byte ((l>>24)&16rff);
+ cdp[1] = byte ((l>>16)&16rff);
+ cdp[2] = byte ((l>>8)&16rff);
+ cdp[3] = byte (l&16rff);
+
+ return 12+x;
+}
+
+poll_and_reply(nak: int): int
+{
+ print("poll and reply\n",7);
+ if ((read_n_to(C.fd, pwl, len pwl,TIMEOUT) < 0) && nak) {
+ pak[0] = byte 0;
+ pak[1] = byte 2; # reject
+ write_n(C.fd, pak, len pak);
+ return 0;
+ }
+ pak[0] = byte 0;
+ pak[1] = byte 1;
+ write_n(C.fd, pak, len pak);
+
+ return 1;
+}
+
+receive_packet(buf: array of byte): int
+{
+ print("receive_packet\n",6);
+ if (!poll_and_reply(!0)) {
+ print("Poll and reply failed\n",1);
+ return -1;
+ }
+
+ l := int (((int pwl[0]&3)<<8)|(int pwl[1]));
+ C.bufbytes += l;
+ r := read_n_to(C.fd, buf, l,TIMEOUT);
+ if (r != l) {
+ print(sys->sprint( "could not read packet (read %d, expected %d)\n", r, l),1);
+ return -1;
+ }
+ return 0;
+}
+
+receive_message(): int
+{
+ print("read_message\n",6);
+ C.bufbytes = 0;
+ if (receive_packet(C.cdp[0:]) != 0) {
+ recon = 1;
+ print("receive packet failed\n",1);
+ return 3;
+ # raise "error: receive packet failed";
+ }
+ dump_message();
+ rc := int C.cdp[9];
+ if ((~rc&16rff) != (C.command&16rff)) {
+ print("command & return are different\n",1);
+ consume(C.fd);
+ return 3;
+ # raise "error: command and return command are not the same\n";
+ }
+ message_len := (int C.cdp[2]<<8)+(int C.cdp[3]);
+
+ while (C.bufbytes < message_len) {
+ if (receive_packet(C.cdp[C.bufbytes:]) != 0) {
+ print("Packet is too short\n",1);
+ recon = 1;
+ return 3;
+ # raise "error: receive packet2 failed";
+ }
+ }
+# sys->sleep(500);
+ read_n_to(C.fd, pak, len pak, TIMEOUT);
+ return (int ((C.cdp[10]<<8)|(C.cdp[11]))); # result code
+}
+
+reset(fd: ref Sys->FD)
+{
+ sys->fprint(fd, "d1");
+ sys->sleep(20);
+ sys->fprint(fd, "d0");
+ sys->fprint(fd, "b9600");
+}
+
+kill(pid: int)
+{
+ pctl := sys->open("/prog/" + string pid + "/ctl", Sys->OWRITE);
+ if (pctl != nil)
+ sys->write(pctl, array of byte "kill", len "kill");
+}
+
+killg(pid: int)
+{
+ if ((fd := sys->open("/prog/" + string pid + "/ctl", Sys->OWRITE)) != nil)
+ sys->fprint(fd, "killgrp");
+}
+
+#dump_buf(buf: array of byte, i: int)
+#{
+# for (j := 0; j < i; j++)
+# sys->fprint(sys->fildes(2), "%x ", int buf[j]);
+# sys->fprint(sys->fildes(2), "\n");
+#}
+
+serialport(port : int) : (ref Sys->FD, ref Sys->FD, string)
+{
+ C.fd = nil;
+ C.ctlfd = nil;
+ C.mode = BEACON;
+
+ serport := "/dev/eia" + string port;
+ serctl := serport + "ctl";
+
+ for (i := 0; i < len statopt; i++) {
+ statfd := sys->open("/dev/eia"+string port+statopt[i],sys->OREAD);
+ if (statfd != nil)
+ C.stat = i;
+ statfd = nil;
+ }
+ readstat();
+
+ fd := sys->open(serport, Sys->ORDWR);
+ if (fd == nil)
+ return (nil, nil, sys->sprint("cannot read %s: %r", serport));
+ ctlfd := sys->open(serctl, Sys->OWRITE);
+ if (ctlfd == nil)
+ return (nil, nil, sys->sprint("cannot open %s: %r", serctl));
+
+ config := array [] of {
+ "b9600",
+ "l8",
+ "p0",
+ "m0",
+ "s1",
+ "r1",
+ "i1",
+ "f",
+ };
+
+ for (i = 0; i < len config; i++) {
+ if (sys->fprint(ctlfd,"%s", config[i]) < 0)
+ print(sys->sprint("serial config (%s): %r\n", config[i]),3);
+ }
+ sys->sleep(100);
+ consume(fd);
+ sys->fprint(ctlfd, "d1");
+ sys->sleep(40);
+ sys->fprint(ctlfd, "d0");
+ return (fd, ctlfd, nil);
+}
+
+consume(fd: ref sys->FD)
+{
+ if (fd != nil) {
+ print("Consuming...\n",6);
+ read_n_to(fd, array[1000] of byte, 1000, 1000);
+ }
+}
+
+beacon_intro(data: chan of array of byte, pchan: chan of int, fd: ref Sys->FD)
+{
+ buf := array[64] of byte;
+ cbuf: array of byte;
+ pid := sys->pctl(0, nil);
+# print(sys->sprint("b_intro: starting %d\n",pid);
+ pchan <-= pid;
+ failed := array[len bintro] of { * => byte 0 };
+ # discard characters until lead in character reached
+ print(sys->sprint("\tWaiting for: %d...\n",int bintro[0]),3);
+ do {
+ n := read_n_to(fd, buf, 1, TIMEOUT);
+ if (n == -1) {
+ data <- = failed;
+ return;
+ }
+ print(sys->sprint("\tGot: %d\n",int buf[0]),5);
+ } while (buf[0] != bintro[0]);
+ print("Getting beacon\n",3);
+ # read the next 6 bytes of beacon
+ i := read_n_to(fd, buf[1:], 6,TIMEOUT);
+ for (k := 0; k < i; k++)
+ print(sys->sprint("\tRead %d: %d (wanted %d)\n",k+1, int buf[1+k], int bintro[1+k]),5);
+ if (i != 6) {
+ print("Error reading beacon\n",3);
+ exit;
+ }
+ else {
+ print("sending beacon\n",3);
+ cbuf = buf[0:7];
+ data <- = cbuf;
+ }
+
+}
+
+beacon_result(data: chan of array of byte, pchan: chan of int, fd: ref Sys->FD)
+{
+ buf := array[64] of byte;
+ cbuf: array of byte;
+ pid := sys->pctl(0, nil);
+ pchan <-= pid;
+
+ # read the next 10 bytes of beacon
+ p := 0;
+ intro := 1;
+ for (;;) {
+ i := read_n_to(fd, buf[p:], 1, TIMEOUT);
+ if (intro) {
+ if (buf[p] != bintro[p]) {
+ intro = 0;
+ buf[0] = buf[p];
+ p = 1;
+ }
+ else {
+ p++;
+ if (p >= len bintro) p = 0;
+ }
+ }
+ else p++;
+ if (p == 10) break;
+ }
+
+ for (k := 0; k < p; k++) print(sys->sprint("\tRead %d: %d\n",k, int buf[k]),5);
+ if (p != 10) {
+ print("Error reading beacon result\n",3);
+ exit;
+ }
+ else {
+ print("reading beacon result\n",3);
+ cbuf = buf[0:10];
+ data <- = cbuf;
+ }
+}
+
+beacon_comp(buf: array of byte, C: Camera_adt): int
+{
+ speed: string;
+
+ case int buf[0] {
+ 0 =>
+ C.baud = (int buf[2]<<24)|(int buf[3]<<16)|(int buf[4]<<8)|(int buf[5]);
+ C.dfs = (int buf[6]<<8)|(int buf[7]);
+ C.hfs = (int buf[8]<<8)|(int buf[9]);
+ # do baud rate change here
+ sys->sleep(1000);
+
+ case C.baud {
+ 115200 =>
+ speed = "b115200";
+ 57600 =>
+ speed = "b57600";
+ 38400 =>
+ speed = "b38400";
+ 19200 =>
+ speed = "b19200";
+ * =>
+ speed = "b9600";
+ }
+ print(sys->sprint("Connection Details:\n Baud rate:\t%dbps\n",C.baud),3);
+ print(sys->sprint(" Host frame size:\t%dbytes\n",C.hfs),3);
+ print(sys->sprint(" Device frame size:\t%dbytes\n",C.dfs),3);
+ if (sys->fprint(C.ctlfd,"%s", speed) < 0) {
+ print(sys->sprint("Error setting baud rate %s\n", speed),3);
+ return -1;
+ }
+ -1 =>
+ print("Incompatible Data Rate\n",1);
+ return -1;
+ -2 =>
+ print("Device does not support these modes\n",1);
+ return -2;
+ * =>
+ print(sys->sprint("I'm here!? buf[0] = %d\n",int buf[0]),1);
+ return -1;
+ }
+ return 0;
+}
+
+read_n(fd: ref Sys->FD, buf: array of byte, n: int, res: chan of int)
+{
+ pid := sys->pctl(0, nil);
+# print(sys->sprint("read_n: starting %d\n",pid);
+ res <-= pid;
+ print(sys->sprint( "read_n %d\n", n),7);
+ nread := 0;
+ while (nread < n) {
+ i := sys->read(fd, buf[nread:], n-nread);
+ sys->sleep(1);
+ if (i <= 0)
+ break;
+ nread += i;
+ }
+ res <-= nread;
+# print(sys->sprint("read_n: ending %d\n",pid);
+}
+
+read_n2(fd: ref Sys->FD, buf: array of byte, n: int): int
+{
+ print(sys->sprint( "read_n2 %d\n", n),7);
+ nread := 0;
+ while (nread < n) {
+ i := sys->read(fd, buf[nread:], n-nread);
+ sys->sleep(1);
+ if (i <= 0)
+ break;
+ nread += i;
+ }
+ return nread;
+}
+
+read_n_to(fd: ref Sys->FD, buf: array of byte, n,t : int): int
+{
+ v:= 0;
+ rc := chan of int;
+ tc := chan of int;
+
+ spawn timer2(tc,t);
+ tpid := <- tc;
+ spawn read_n(fd, buf, n, rc);
+ rpid := <- rc;
+
+ try := 0;
+ alt {
+ <- tc =>
+ kill(rpid);
+ print(sys->sprint( "error: read_n timeout\n"),1);
+ recon = 1;
+ return -1;
+ v = <- rc =>
+ kill(tpid);
+ break;
+ }
+ return v;
+}
+
+write_n(fd: ref Sys->FD, buf: array of byte, n: int): int
+{
+ print(sys->sprint("write_n %d\n", n),7);
+ nwrite := 0;
+ while (nwrite < n) {
+ i := sys->write(fd, buf[nwrite:], n-nwrite);
+ sys->sleep(1);
+ if (i <= 0) {
+ print(sys->sprint("Error returned by write: %r\n"),1);
+ readstat();
+# recon = 1;
+ return nwrite;
+ }
+ nwrite += i;
+ }
+ print(sys->sprint("write_n returning %d\n", nwrite),7);
+ return nwrite;
+}
+
+readstat()
+{
+ consume(C.fd);
+ print("Serial status: ",5);
+ statfd := sys->open("/dev/eia"+string C.port_num+statopt[C.stat], sys->OREAD);
+ buf := array[100] of byte;
+ if (statfd != nil) {
+ for (;;) {
+ k := sys->read(statfd,buf,len buf);
+ if (k > 0) print(string buf[:k],2);
+ else break;
+ }
+ print("\n",2);
+ }
+ else print("cannot read serial status\n",1);
+}
+
+beacon_ack(C: Camera_adt)
+{
+ # set speed
+ i := C.baud;
+ bak[4] = byte ((i>>24)&16rff);
+ bak[5] = byte ((i>>16)&16rff);
+ bak[6] = byte ((i>>8)&16rff);
+ bak[7] = byte (i&16rff);
+
+ # set frame size to device
+ i = C.dfs;
+ bak[8] = byte ((i>>8)&16rff);
+ bak[9] = byte (i&16rff);
+
+ # set frame size to host
+ i = C.hfs;
+ bak[10] = byte ((i>>8)&16rff);
+ bak[11] = byte (i&16rff);
+ bak[12] = check_sum(bak, 12);
+
+ if (write_n(C.fd, bak, len bak) != len bak) {
+ print("Error writing beacon acknowledgement\n",3);
+ exit;
+ }
+ print("beacon acknowledgement written\n",3);
+}
+
+# timer thread send tick <- = 0 to kill
+
+timer2(tick: chan of int, delay: int)
+{
+ pid := sys->pctl(0, nil);
+ tick <-= pid;
+ sys->sleep(delay);
+ tick <- = TOUT;
+}
+
+beacon_ok(buf: array of byte): int
+{
+
+ for (i := 0; i < len bintro; i++) {
+ if (buf[i] != bintro[i]) {
+ print(sys->sprint("Beacon failed on byte %d: %d (wanted %d)\n",i,int buf[i],int bintro[i]),3);
+ return 0;
+ }
+ }
+ print("Beacon passed\n",3);
+ return 1;
+}
+
+check_sum(buf: array of byte, l: int): byte
+{
+ sum := 0;
+ for (i := 0; i < l; i++)
+ sum += int buf[i];
+ return byte (sum&16rff);
+}
+
+
+set_int(b: array of byte, i, off: int): int
+{
+ b[0] = byte (i>>24&16rff);
+ b[1] = byte (i>>16&16rff);
+ b[2] = byte (i>>8&16rff);
+ b[3] = byte (i&16rff);
+
+ return (off+4);
+}
+
+set_fstring(b: array of byte, s: array of byte, off: int): int
+{
+ for (i := 0; i < 32; i++)
+ b[i] = byte 0;
+ for (i = 0; i < len s; i++)
+ b[i] = s[i];
+ return (off+32);
+}
+
+set_dosname(b: array of byte, s: array of byte, off: int): int
+{
+ for (i := 0; i < 16; i++)
+ b[i] = byte 0;
+ for (i = 0; i < len s; i++)
+ b[i] = s[i];
+ return (off+16);
+}
+
+get_tag(b: array of byte, off: int): (int, Partialtag)
+{
+ tag: Partialtag;
+ (off, tag.offset) = get_int(b, off);
+ (off, tag.length) = get_int(b, off);
+ (off, tag.filesize) = get_int(b, off);
+ return (off, tag);
+}
+
+get_int(b: array of byte, off: int): (int, int)
+{
+ return (get_int2(b), off+4);
+}
+
+get_int2(b: array of byte): int
+{
+ i := (int b[0]<<24)|(int b[1]<<16)|(int b[2]<<8)|(int b[3]);
+ return i;
+}
+
+
+get_pname(b: array of byte, off: int): (array of byte, int)
+{
+ return get_string(b, off, 4);
+}
+
+get_dosname(b: array of byte, off: int): (array of byte, int)
+{
+ return get_string(b, off, 16);
+}
+
+get_string(b: array of byte, off: int, l: int): (array of byte, int)
+{
+ s := array[l] of byte;
+ s[0:] = b[0:l];
+ return (s, off+l);
+}
+
+get_fstring(b: array of byte, off: int): (array of byte, int)
+{
+ return get_string(b, off, 32);
+}
+
+sconv(b: array of byte): string
+{
+ s := string b;
+ i := len s-1;
+ while (i >= 0 && s[i] == 0)
+ i--;
+ return s[0:i+1];
+}
+
+name2dos(s: string): array of byte
+{
+ return array of byte str->toupper(s);
+}
+
+getqid(i, ftype: int): int
+{
+ qid := (i<<4) + ftype;
+ return qid;
+}
+
+gettype(qid: int): int
+{
+ ftype := qid & 15;
+ return ftype;
+}
+
+cutdir(ab:array of byte): string
+{
+ s := sconv(ab);
+ for (i := 0; i < len s-1; i++)
+ if (s[i] == '/')
+ return s[i+1:len s - 1];
+ return "";
+}
+
+convert_thumb(w,h: int, data: array of byte): array of byte
+{
+ rgb := array[w * h * 3] of byte;
+ index := 0;
+ rgbi := 0;
+ for (i := 0; i < (w * h) / 2; i++) {
+
+ cb := real data[index];
+ y := real data[index+1];
+ cr := real data[index+2];
+
+ rb := conv(y + (1.77200 * (cb - 128.0)));
+ gb := conv(y - (0.34414 * (cb - 128.0)) - (0.71414 * (cr - 128.0)));
+ bb := conv(y + (1.4020 * (cr - 128.0)));
+
+ for (loop := 0; loop < 2; loop++) {
+ rgb[rgbi++] = rb;
+ rgb[rgbi++] = gb;
+ rgb[rgbi++] = bb;
+ }
+ index += 4;
+ }
+ return rgb;
+}
+
+conv(a: real): byte
+{
+ r := int a;
+ if (r < 0) r = -r;
+ if (r > 255) r = 255;
+ return byte r;
+}
+
+thumb2bit(buf: array of byte, w,h: int): array of byte
+{
+ convbuf := convert_thumb(w,h,buf);
+ # assume thumbs are small so we wont gain much by compressing them
+ bitarray := array [60+len convbuf] of byte;
+ # assume chans = RGB24
+ bitarray[:] = array of byte sys->sprint("%11s %11d %11d %11d %11d ", "r8g8b8", 0, 0, w, h);
+ bitarray[60:] = convbuf;
+ return bitarray;
+}
+
+jpg2bit(s: string): string
+{
+ if (len s < 4) return s;
+ if (s[len s - 4:] != ".jpg") return s;
+ return s[:len s - 4]+".bit";
+}
+
+oldfiles : list of (string, int, int);
+
+getoldfiledata()
+{
+ oldfiles = nil;
+ for(i := 0; i < reslength; i++)
+ oldfiles = (str->tolower(sconv(filelist[i].cf.dosname)),
+ int filelist[i].qid.path,
+ filelist[i].cf.thumbqid) :: oldfiles;
+}
+
+updatetree(tree: ref Nametree->Tree)
+{
+ for (i := 0; i < reslength; i++) {
+ name := str->tolower(sconv(filelist[i].cf.dosname));
+ found := 0;
+ tmp : list of (string, int, int) = nil;
+ for (; oldfiles != nil; oldfiles = tl oldfiles) {
+ (oldname, oldqid, oldthumbqid) := hd oldfiles;
+ # sys->print("'%s' == '%s'?\n",name,oldname);
+ if (name == oldname) {
+ found = 1;
+ filelist[i].qid = (big oldqid, 0, sys->QTFILE);
+ filelist[i].cf.thumbqid = oldthumbqid;
+ }
+ else
+ tmp = hd oldfiles :: tmp;
+ }
+ oldfiles = tmp;
+ # sys->print("len oldfiles: %d\n",len oldfiles);
+ if (found)
+ updateintree(tree, name, i);
+ else
+ addtotree(tree, name, i);
+
+ }
+ for (; oldfiles != nil; oldfiles = tl oldfiles) {
+ (oldname, oldqid, oldthumbqid) := hd oldfiles;
+ # sys->print("remove from tree: %s\n",oldname);
+ tree.remove(big oldqid);
+ tree.remove(big oldthumbqid);
+ }
+}
+
+updateintree(tree: ref Nametree->Tree, name: string, i: int)
+{
+ # sys->print("update tree: %s\n",name);
+ tree.wstat(filelist[i].qid.path,
+ dir(name,
+ 8r444,
+ filelist[i].cf.filelength,
+ int filelist[i].qid.path));
+ tree.wstat(big filelist[i].cf.thumbqid,
+ dir(jpg2bit(name),
+ 8r444,
+ 13020,
+ filelist[i].cf.thumbqid));
+}
+
+addtotree(tree: ref Nametree->Tree, name: string, i: int)
+{
+ # sys->print("addtotree: %s\n",name);
+ nextjpgqid += 1<<4;
+ filelist[i].qid = (big nextjpgqid, 0, sys->QTFILE);
+ parentqid := Qjpgdir;
+ tree.create(big parentqid,
+ dir(name,
+ 8r444,
+ filelist[i].cf.filelength,
+ nextjpgqid));
+
+ nexttmbqid += 1<<4;
+ filelist[i].cf.thumbqid = nexttmbqid;
+ tree.create(big Qthumbdir,
+ dir(jpg2bit(name),
+ 8r444,
+ 13020,
+ nexttmbqid));
+}
+
+keepalive(alivechan: chan of int)
+{
+ alivechan <-= sys->pctl(0,nil);
+ for (;;) {
+ sys->sleep(300000);
+ now := daytime->now();
+ print(sys->sprint("Alive: %d idle seconds\n",now-wait),6);
+ if (now < wait)
+ wait = now - 300;
+ if (now - wait >= 300)
+ alivechan <-= 1;
+ }
+}
+
+reconnect(n: int): int
+{
+ attempt := 0;
+ connected = 0;
+ delay := 100;
+ to5 := 0;
+ for (;;) {
+ print(sys->sprint( "Attempting to reconnect (attempt %d)\n",++attempt),2);
+ sys->sleep(100);
+ (C.fd, C.ctlfd, nil) = serialport(C.port_num);
+ if (C.fd == nil || C.ctlfd == nil)
+ print(sys->sprint("Could not open serial port\n"),3);
+ else if (connect() == 0) {
+ set_interface_timeout();
+ connected = 1;
+ print("Reconnected!\n",2);
+ break;
+ }
+ if (n != -1 && attempt >= n)
+ break;
+ if (++to5 >= 5) {
+ delay *= 2;
+ to5 = 0;
+ if (delay > 600000)
+ delay = 600000;
+ }
+ sys->sleep(delay);
+ }
+ recon = 0;
+ return connected;
+}
+
+# 1: errors
+# 2: connection
+# 3: main procs
+
+print(s: string, v: int)
+{
+ if (s != nil && s[len s - 1] == '\n')
+ s = s[:len s - 1];
+ if (v <= verbosity)
+ sys->fprint(sys->fildes(2), "%s (%s)\n",s,camname);
+}
+
+readinterface(qid : int, offset: big, size: int): (ref sys->Dir, array of byte)
+{
+ i := qid >> 4;
+ buf := array[size] of byte;
+ fd := sys->open(interfacepaths[i], sys->OREAD);
+ if (fd == nil)
+ return (nil,nil);
+ (n, dir) := sys->fstat(fd);
+ if (offset >= dir.length)
+ return (nil,nil);
+ sys->seek(fd, offset, sys->SEEKSTART);
+ i = sys->read(fd,buf,size);
+ return (ref dir, buf[:i]);
+}
+
+readfile(f: string): string
+{
+ fd := sys->open(f, sys->OREAD);
+ if(fd == nil)
+ return nil;
+
+ buf := array[8192] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return nil;
+
+ return string buf[0:n];
+}