Client of services:
do (vc-do.setl, Section A.11 [vc-do.setl])
notice (vc-do.setl, Section A.11 [vc-do.setl])
Called by parent program:
vc-camera.setl (Section A.4 [vc-camera.setl])
Textually #includes:
vc-decode.setl (Section A.10 [vc-decode.setl])
vc-exit.setl (Section A.15 [vc-exit.setl])
vc-getname.setl (Section A.16 [vc-getname.setl])
vc-msg.setl (Section A.30 [vc-msg.setl])
vc-obtain.setl (Section A.31 [vc-obtain.setl])
Source code: *
const yhwh = `vc-ptz.setl';
-- This program is instantiated as a pumping co-process child of
-- vc-camera.setl for each client that is using the high-level
-- command interface to the Canon VC-C3 pan/tilt/zoom (ptz) camera
-- controller.
--
-- The file descriptor of the connected client's socket, which
-- this program inherits, is identified on the program invocation
-- command line.
const arg_fd = fileno open (val command_line(1), `socket');
const in_fd = arg_fd,
out_fd = arg_fd; -- mnemonic references to the inherited socket
const sigterm_fd = open (`SIGTERM', `signal'); -- catch TERM signals
const do_fd = open_do_server(); -- mid-level command (do) server
var notice_fd := om; -- mid-level event notification service
open (`SIGPIPE', `ignore'); -- retain control on EPIPE output errors
p (`Welcome to the Canon VC-C3 pan/tilt/zoom camera control server.');
p (`Type Help for help. Cavil and whine to dB (bacon@cs.nyu.edu).');
out (`.'); -- ``end of help'' marker
prev_words := [`Help'];
loop
pool := {sigterm_fd, in_fd, notice_fd}; -- notice_fd may be om
[ready] := select ([pool]);
if sigterm_fd in ready then
msg (yhwh + ` (' + str pid + `) caught SIGTERM');
quit_gracefully;
end if;
if in_fd in ready then
if (line := getline in_fd) = om then
quit_gracefully;
end if;
words := split (line);
if #words = 0 then
words := prev_words;
else
prev_words := words;
end if;
cmd := words(1);
case of
--- This command language needs a comment convention! How about ``#''?
(cmd ceq `Help'): -- Help [command-name]
-- Send lines prefixed with ``>'', followed by a line containing
-- a single ``.''
if #words = 1 then
p (`');
p (`Commands are:');
p (`');
p (`Help [command-name]');
p (`Mode {Host | RC}');
p (`Notify {On | Off}');
p (`Zoom {[To] factor | By factor | In | Out} [At speed]');
p (`Move {[To] pan tilt | By pan tilt} [[In] ms | At speed]');
p (`{Up | Down | Left | Right} deg [[In] ms | At speed]');
--- still to document: Jump
p (`Ramp ms');
p (`Show {Mode | Notify | Zoom | Move | Position | Ramp}');
p (`Clear');
p (`Reload');
p (`Setup');
p (`Reset');
p (`Check');
p (`Quit');
p (`');
p (`A null command (empty line) repeats the previous command.');
else
cmd_name := words(2);
case of
(cmd_name ceq `Help'):
p (`');
p (`Help');
q (`Gives a compact synopsis of all commands, with optional'+
` words shown in brackets [ ], grouping indicated by'+
` braces { }, and alternatives separated by bars |.');
q (`All command names and arguments are case-insensitive,'+
` though for clarity they are shown here as literal names'+
` starting with an uppercase letter. Substitute a value'+
` for any (possibly hyphenated) name that begins with a'+
` lowercase letter. Numbers may include signs and decimal'+
` points.');
q (`Help is the only command besides Show which produces'+
` output back to you, the client, when asynchronous'+
` notification is off (see the Notify command). You can'+
` tell where a piece of help ends by where the ">" lines'+
` leave off and the final "." on a line by itself occurs. '+
` Server usage errors (your protocol mistakes) are also'+
` reported in this "help" format. Output from Show always'+
` consists of a single line, as does each asynchronous'+
` notification (event message), so their ends are also'+
` easy to recognize.');
p (`');
p (`Help command-name');
q (`Tells you all about a specific command.');
(cmd_name ceq `Notify'):
p (`');
p (`Notify On');
q (`Turns on asynchronous notification. You (the client)'+
` will get an event message, formatted as a command'+
` recognized by this server for convenience in playback,'+
` whenever there is a change in the mode, zoom, pan/tilt,'+
` or ramp, and whenever a zoom or pan/tilt limit is'+
` reached. '+
` [Other messages, with no corresponding command but'+
` formatted similarly, will later be added. For now,'+
` there is a catch-all message "Canon", showing things the'+
` hardware is saying.]');
p (`');
p (`Notify Off');
q (`Turns off asynchronous notification. You can still get'+
` information synchronously by using the Show command.');
(cmd_name ceq `Zoom'):
p (`');
p (`Note on zoom speeds:');
q (`The Canon has 8 speeds going in (TELE) and out (WIDE),'+
` and accordingly the speed given in any [At speed] clauses'+
` on Zoom commands should be a number in the range 1 to 8. '+
` Zooming in all the way from a zoom factor of 1 to a zoom'+
` factor of 10, or the reverse, seems to take about 2'+
` seconds at the maximum speed of 8, and about 9 seconds'+
` at the minimum speed of 1.');
p (`Zoom [To] factor [At speed]');
q (`Sets the current zoom factor to a floating-point value'+
` between 1 and 10.');
p (`Zoom By factor [At speed]');
q (`Scales the current zoom factor by the specified factor.');
p (`Zoom In [At speed]');
q (`Equivalent to Zoom By 1.618.');
p (`Zoom Out [At speed]');
q (`Equivalent to Zoom By 0.618.');
(cmd_name ceq `Move'):
p (`');
p (`Move [To] pan tilt [[In] ms] | At speed]');
q (`Points the camera at pan degrees azimuth, tilt degrees'+
` elevation, and stores these as the current values.');
q (`Positive means right for pan, up for tilt.');
q (`Range is -90 to 90 for pan, -30 to 25 for tilt.');
q (`Resolution is 0.115 deg.');
q (`The angular trajectory is shaped at each end by the'+
` parabola suggested by the Ramp period. If the angular'+
` distance to move is large enough, maximum speed will be'+
` sustained in the interval between the acceleration and'+
` deceleration ramps unless constrained by the optional In'+
` or At specification.');
q (`If "[In] ms" is specified, the server will try to plan a'+
` camera motion trajectory that takes ms milliseconds.');
q (`If instead "At speed" is specified, the trajectory speed'+
` will be limited to the given maximum during the'+
` constant-speed interval between acceleration and'+
` deceleration ramps.');
q (`The units of speed in "At speed" are deg/sec, with a'+
` resolution of 1 deg/sec and a range of 1 to 70 deg/sec.');
p (`Move By pan tilt [[In] ms] | At speed]');
q (`Adds pan degrees azimuth and tilt degrees elevation to'+
` the current pan and tilt values, and calls Move [To].');
(cmd_name ceq `Up'):
p (`');
p (`Up deg [[In] ms] | At speed]');
q (`Synonymous with "Move By 0 deg" plus In/At options.');
(cmd_name ceq `Down'):
p (`');
p (`Down deg [[In] ms] | At speed]');
q (`Synonymous with "Move By 0 -deg" plus In/At options.');
(cmd_name ceq `Left'):
p (`');
p (`Left deg [[In] ms] | At speed]');
q (`Synonymous with "Move By -deg 0" plus In/At options.');
(cmd_name ceq `Right'):
p (`');
p (`Right deg [[In] ms] | At speed]');
q (`Synonymous with "Move By deg 0" plus In/At options.');
(cmd_name ceq `Ramp'):
p (`');
p (`Ramp ms');
q (`Sets the number of milliseconds the pan/tilt apparatus'+
` will take to get up to maximum speed on Move [To] and'+
` Move By requests, and also how long it will take to slow'+
` down as the destination is approached.');
q (``The default is 500 ms, which shouldn't jerk the platform''+
` very violently. Should look way smooth, too, eh.');
(cmd_name ceq `Show'):
p (`');
p (`All Show commands produce their output in the form of a');
p (`command that could later be fed back in to the server to');
p (`re-establish the state reported by the Show.');
p (`');
p (`Show Mode');
q (`Yields Mode Host or Mode RC.');
p (`Show Notify');
q (`Yields Notify On or Notify Off.');
q (`Each asynchronous notification (event message) and Show'+
` result is sent to you, the client, on a single,'+
` newline-terminated line.');
p (`Show Zoom');
q (`Yields the current zoom factor as a Zoom [To] command.');
p (`Show {Position | Move}');
q (`Yields the current pan and tilt angles as a Move [To]'+
` command.');
p (`Show Ramp');
q (`Yields a Ramp command for the current ramp period.');
p (`');
p (`See also Reload and Check.');
(cmd_name ceq `Mode'):
p (`');
p (`Mode Host');
q (`Puts the pan/tilt and zoom apparatus into a state where'+
` it is receptive to commands from the computer rather'+
` than from the hand-held remote control.');
q (`(1) Uses Clear to toggle the RTS line down and up, (2)'+
` requests the device mode change, and (3) calls Setup'+
` for auto-detection of the home position and for server'+
` state refreshment.');
p (`');
p (`Mode RC');
q (`Zoom and Move commands from the computer (meaning you,'+
` the client!) will be ignored until the next switch to'+
` Mode Host.');
q (`(1) Uses Clear to toggle the RTS line down and up, and'+
` (2) requests the device mode change.');
(cmd_name ceq `Clear'):
p (`');
p (`Clear');
q (`Toggles the RTS line of the RS-232 communications link'+
` down and up in an effort to cancel a "wait state" entered'+
` by the Canon VC-C3.');
q (`Merely wastes a little time if the hardware is not in'+
` such a state.');
q (`Called automatically by the Mode command, which is called'+
` by Reset.');
(cmd_name ceq `Reload'):
p (`');
p (`Reload');
q (`Causes this server to refresh its record of the hardware'+
` state by reading the current zoom and pan/tilt parameters'+
` from the Canon VC-C3.');
q (`If asynchronous notification is on, sends you Zoom and'+
` Move event messages reflecting the newly read values.');
q (`Try this command if you think Show might be lying to'+
` you. See also Check.');
q (`Reload is called automatically by Setup, which is called'+
` by Mode Host, which is called by Reset in host mode.');
q (`Reload cannot detect the control mode (Host or RC). '+
` Hence this parameter is meekly assumed to be unchanged,'+
` and the best you can do for definiteness is to set it'+
` using the Mode command.');
(cmd_name ceq `Setup'):
p (`');
p (`Setup');
q (`Causes the Canon VC-C3 hardware to auto-detect the'+
` pan/tilt "home" position, and then calls Reload to'+
`` refresh the server's record of the hardware state.'');
q (`Done automatically as the final stage of Mode Host,'+
` which is called by Reset in host mode.');
q (`Can take as long as 4 seconds, as the camera swings'+
` wildly to the home position and then back to wherever it'+
` was.');
(cmd_name ceq `Reset'):
p (`');
p (`Reset');
q (`Causes the Canon VC-C3 camera control unit (CCU) to'+
` re-initialize. The CCU will lower the CTS line of the'+
` RS-232 communications link for 3.8 seconds during this'+
` process. When CTS is later raised, the server will'+
` attempt to establish the control mode that obtained'+
` before the Reset, by calling the Mode command.');
(cmd_name ceq `Check'):
p (`');
p (`Check');
q (`Perform a "sanity check" on the Canon VC-C3 camera'+
` control unit (CCU), and do a Reset if it appears to be'+
` malfunctioning. Sometimes, for reasons unknown, the CCU'+
` gets into a state where it responds normally on the'+
` serial line but is in fact ignoring zoom and/or motion'+
` commands; Check effectively does a Show before and after'+
` a Reload, and looks for discrepancies. It is primarily'+
` intended for the use of external automata, but if you'+
` find the CCU not responding, you can see Check in action'+
` by zooming (say) to a setting far from what Show is'+
` reporting, doing a Check, and watching for the effects'+
` of a Reset (e.g., transient camera motion, image'+
` blanking, and, if you have Notify On, the "event"'+
` messages that normally result from a Reset).');
(cmd_name ceq `Quit'):
p (`');
p (`Quit');
q (`Asks the server to drop the network connection.');
q (`For technical reasons, it is mildly preferable that you,'+
` the client, drop the network connection first, usually'+
` simply by closing it. The server will follow suit. '+
` This avoids the compulsory 2MSL wait to retire the'+
`` half-association in the TCP module on the server's''+
` host when the server drops first. So in other words,'+
`` unless you have some good reason to, DON'T USE Quit,''+
` but merely close your socket when you are done with me. '+
` It is really no big deal, though.');
else
p (`');
p (`Invalid argument to Help command - try Help Help.');
end case;
end if;
out (`.'); -- ``end of help'' marker
(cmd ceq `Notify'): -- Notify {On | Off}
if #words = 2 then
switch := words(2);
case of
(switch ceq `On'):
notice_fd ?:= open_notice_server();
(switch ceq `Off'):
if notice_fdom then
close (notice_fd);
notice_fd := om;
end if;
else
help (`Notify argument must be On or Off - try Help Notify');
end case;
else
help (`Notify command requires 1 argument - try Help Notify');
end if;
(cmd ceq `Zoom'): -- Zoom {Start | Stop}
-- Zoom Speed zoom-speed
-- Zoom {[To] factor | By factor | In | Out}
-- [At speed]
case of
((#words = 2) and (words(2) ceq `Start')):
do_zoom (`Start');
((#words = 2) and (words(2) ceq `Stop')):
do_zoom (`Stop');
((#words = 3) and (words(2) ceq `Speed')
and is_num words(3)):
zoom_speed := val words(3);
do_zoom (`Speed', zoom_speed);
else
if #words2 then
words(1..1) := [ ];
if words(1) ceq `To' then
if #words2 and is_num words(2) then
do_fussy_zoom (`To', val words(2), words(3..));
else
help (`Zoom command parameter error - try Help Zoom');
end if;
elseif is_num words(1) then
do_fussy_zoom (`To', val words(1), words(2..));
elseif words(1) ceq `By' then
if #words2 and is_num words(2) then
do_fussy_zoom (`By', val words(2), words(3..));
else
help (`Zoom command parameter error - try Help Zoom');
end if;
elseif words(1) ceq `In' then
do_fussy_zoom (`By', 1.618, words(2..));
elseif words(1) ceq `Out' then
do_fussy_zoom (`By', 0.618, words(2..));
else
help (`Zoom command parameter error - try Help Zoom');
end if;
else
help (`Zoom command parameter error - try Help Zoom');
end if;
end case;
--- The Start, Stop, and Speed subcommands are deprecated and
--- de-documented, and may disappear from a future release of
--- the software:
(cmd ceq `Move'): -- Move {Start | Stop}
-- Move Speed pan-speed tilt-speed
-- Move {[To] pan tilt | By pan tilt}
-- [[In] ms | At speed]
case of
((#words = 2) and (words(2) ceq `Start')):
do_move (`Start');
((#words = 2) and (words(2) ceq `Stop')):
do_move (`Stop');
((#words = 4) and (words(2) ceq `Speed')
and is_num words(3)
and is_num words(4)):
pan_speed := val words(3);
tilt_speed := val words(4);
do_move (`Speed', pan_speed, tilt_speed);
else
if #words3 then
words(1..1) := [ ];
toby := `To';
if words(1) ceq `To' then
words(1..1) := [ ];
elseif words(1) ceq `By' then
words(1..1) := [ ];
toby := `By';
end if;
if is_num words(1) and
is_num words(2) then
pan := val words(1);
tilt := val words(2);
do_fussy_move (toby, pan, tilt, words(3..));
else
help (`Move command parameter error - try Help Move');
end if;
else
help (`Move command parameter error - try Help Move');
end if;
end case;
(cmd ceq `Speed'): -- Speed pan-speed tilt-speed
if #words = 3 and is_num words(2)
and is_num words(3) then
pan_speed := val words(2);
tilt_speed := val words(3);
do_move (`Speed', pan_speed, tilt_speed);
else
help (`Speed command parameter error');
end if;
(cmd ceq `Up'): -- Up deg [[In] ms | At speed]
if #words2 and is_num words(2) then
tilt := val words(2);
do_fussy_move (`By', 0, tilt, words(3..));
else
help (`Up command parameter error - try Help Up');
end if;
(cmd ceq `Down'): -- Down deg [[In] ms | At speed]
if #words2 and is_num words(2) then
tilt := val words(2);
do_fussy_move (`By', 0, -tilt, words(3..));
else
help (`Down command parameter error - try Help Down');
end if;
(cmd ceq `Left'): -- Left deg [[In] ms | At speed]
if #words2 and is_num words(2) then
pan := val words(2);
do_fussy_move (`By', -pan, 0, words(3..));
else
help (`Left command parameter error - try Help Left');
end if;
(cmd ceq `Right'): -- Right deg [[In] ms | At speed]
if #words2 and is_num words(2) then
pan := val words(2);
do_fussy_move (`By', pan, 0, words(3..));
else
help (`Right command parameter error - try Help Right');
end if;
(cmd ceq `Jump'):
if #words = 3 and is_num words(2) and
is_num words(3) then
pan := val words(2);
tilt := val words(3);
do_jump (`To', pan, tilt);
else
help (`Jump command parameter error'); --- later ``try Help Jump''
end if;
--- still to implement: ``Jump By''
(cmd ceq `Ramp'): -- Ramp ms
if #words = 2 and is_num words(2) then
ms := val words(2);
do_ramp (ms);
else
help (`Ramp command requires 1 numeric argument - try Help Ramp');
end if;
(cmd ceq `Show'): -- Show {Mode | Notify |
-- Zoom |
-- Position | Move |
-- Ramp}
case of
(words(2..) ceq [`Mode']):
which_mode := do_get (`mode');
out (`Mode ' + which_mode);
(words(2..) ceq [`Notify']):
out (`Notify ' + if notice_fdom then `On' else `Off' end);
(words(2..) ceq [`Zoom']):
zoom_factor := do_get (`zoom_factor');
out (`Zoom ' + fixed (zoom_factor, 0, 3));
(words(2..) ceq [`Zooming']):
zooming := do_get (`zooming');
out (`Zoom ' + if zooming then `Start' else `Stop' end);
(words(2..) ceq [`Zoom',`Speed']):
zoom_speed := do_get (`zoom_speed');
out (`Zoom Speed ' + fixed (zoom_speed, 0, 1));
(words(2..) ceq [`Position'],
words(2..) ceq [`Move']):
[pan, tilt] := do_get (`position');
out (`Move To ' + fixed (pan, 0, 3) + ` ' + fixed (tilt, 0, 3));
(words(2..) ceq [`Moving']):
moving := do_get (`moving');
out (`Move ' + if moving then `Start' else `Stop' end);
(words(2..) ceq [`Speed'],
words(2..) ceq [`Move',`Speed']):
[pan_speed, tilt_speed] := do_get (`move_speed');
out (`Move Speed ' + fixed (pan_speed, 0, 1) +
` ' + fixed (tilt_speed, 0, 1));
(words(2..) ceq [`Ramp']):
ms := do_get (`ramp');
out (`Ramp ' + str ms);
else
help (`Show command parameter error - try Help Show');
end case;
(cmd ceq `Mode'): -- Mode {Host | RC}
if #words = 2 then
which_mode := words(2);
case of
(which_mode ceq `Host'):
do_mode (`Host'); -- Canon under computer control
(which_mode ceq `RC'):
do_mode (`RC'); -- Canon under zapper control
else
help (`Unrecognized mode "'+which_mode+`" - try Help Mode');
end case;
else
help (`Mode command requires 1 argument - try Help Mode');
end if;
(cmd ceq `Clear'): -- Clear
if #words = 1 then
do_clear;
else
help (`Clear command takes no arguments - try Help Clear');
end if;
(cmd ceq `Reload'): -- Reload
if #words = 1 then
do_reload;
else
help (`Reload command takes no arguments - try Help Reload');
end if;
(cmd ceq `Setup'): -- Setup
if #words = 1 then
do_setup;
else
help (`Setup command takes no arguments - try Help Setup');
end if;
(cmd ceq `Reset'): -- Reset
if #words = 1 then
do_reset;
else
help (`Reset command takes no arguments - try Help Reset');
end if;
(cmd ceq `Check'): -- Check
if #words = 1 then
do_check;
else
help (`Check command takes no arguments - try Help Check');
end if;
(cmd ceq `Hex'): -- Hex cmd
-- This command is not advertised by Help.
if #words = 2 and is_hex words(2) then
do_hex (words(2));
else
help (`Hex command requires 1 hex argument');
end if;
(cmd ceq `Quit'): -- Quit
if #words = 1 then
quit_gracefully;
else
help (`Quit command takes no arguments - try Help Quit');
end if;
else
help (`Unrecognized command - try Help');
end case;
end if in_fd;
if notice_fdom and notice_fd in ready then
reada (notice_fd, notice);
if eof then
help (`Mid-level notice service crashed - sorry.');
msg (`EOF from '+filename notice_fd+` - closing');
close (notice_fd);
notice_fd := om;
else
if is_string notice then
out (`Canon '+notice);
else
out (`Canon '+str notice);
end if;
end if;
end if notice_fd;
end loop;
-- Case-insensitive comparison of strings or aggregates thereof
op ceq (a, b);
return if is_string a then to_upper a = to_upper b
else #a = #b and forall s = a(i) | s ceq b(i)
end if;
end op;
op is_num (a);
return a(`^[+-]?[0-9]+(\\.[0-9]+)?$')om;
end op;
op is_hex (s);
return is_string s and #s mod 2 = 0 and s(`^[0-9a-fA-F]*$') = s;
end op;
proc out (s);
--
-- We take the trouble to make sure there is a `\r' (carriage return)
-- before each `\n' (newline), because that is strictly speaking how
-- line-oriented Internet programs are supposed to communicate, and if
-- the client of this program happens to be telnet running under
-- DOS/Windows, these carriage-return characters will be very welcome.
-- Conversely, they will not hurt such clients running in (say) xterms
-- under Unix.
--
-- There is no check for output errors here, because it is fair to
-- assume that if the client drops the connection, this will be seen
-- soon enough on the input side (as an EOF).
--
printa (out_fd, s+`\r');
end proc;
proc p (s); -- spew a line of a help message
out (`>'+s);
end proc;
proc q (s); -- fill and spew a point-paragraph
para := filter (`fmt -60', s);
mash := ` - ';
for line in split (para(1..#para-1), `\n') loop
p (mash+line);
mash := ` ';
end loop;
end proc;
proc help (s); -- spew a diagnostic in the form of a help message
p (s);
out (`.'); -- ``end of help'' marker
end proc;
proc new_cmd (name);
cmd := {};
cmd.name := name;
return cmd;
end proc;
proc do_fussy_zoom (toby, zoom, words);
if #words = 2 and words(1) ceq `At'
and is_num words(2) then
speed := val words(2);
do_zoom (toby, zoom, speed);
elseif #words = 0 then
do_zoom (toby, zoom);
else
help (`Error in [At speed] option - try Help Zoom');
end if;
end proc;
proc do_zoom (subcmd, x(*));
cmd := new_cmd (`Zoom');
cmd.subcmd := subcmd;
case subcmd of
(`Start'): pass;
(`Stop'): pass;
(`Speed'): [cmd.zoom_speed, - ] := x;
(`To'): [cmd.zoom_factor, cmd.speed] := x;
(`By'): [cmd.zoom_scale, cmd.speed] := x;
end case;
do_cmd (cmd);
end proc;
proc do_fussy_move (toby, pan, tilt, words);
if #words = 1 and is_num words(1) or
#words = 2 and words(1) ceq `In'
and is_num words(2) then
ms := val words(#words);
do_move (toby, pan, tilt, `In', ms);
elseif #words = 2 and words(1) ceq `At'
and is_num words(2) then
speed := val words(#words);
do_move (toby, pan, tilt, `At', speed);
elseif #words = 0 then
do_move (toby, pan, tilt);
else
help (`Error in In or At option - try Help Move');
end if;
end proc;
proc do_move (subcmd, x(*));
cmd := new_cmd (`Move');
cmd.subcmd := subcmd;
case subcmd of
(`Start'): pass;
(`Stop'): pass;
(`Speed'): [cmd.pan_speed, cmd.tilt_speed] := x;
(`To',`By'):
[cmd.pan, cmd.tilt] := x;
if x(3) = `In' then
cmd.ms := x(4);
elseif x(3) = `At' then
cmd.speed := x(4);
end if;
end case;
do_cmd (cmd);
end proc;
proc do_jump (toby, pan, tilt);
cmd := new_cmd (`Jump');
cmd.subcmd := toby;
cmd.pan := pan;
cmd.tilt := tilt;
do_cmd (cmd);
end proc;
proc do_ramp (ms);
cmd := new_cmd (`Ramp');
cmd.ms := ms;
do_cmd (cmd);
end proc;
proc do_mode (which_mode);
cmd := new_cmd (`Mode');
cmd(`mode') := which_mode;
do_cmd (cmd);
end proc;
proc do_clear;
cmd := new_cmd (`Clear');
do_cmd (cmd);
end proc;
proc do_reload;
cmd := new_cmd (`Reload');
do_cmd (cmd);
end proc;
proc do_setup;
cmd := new_cmd (`Setup');
do_cmd (cmd);
end proc;
proc do_reset;
cmd := new_cmd (`Reset');
do_cmd (cmd);
end proc;
proc do_check;
cmd := new_cmd (`Check');
do_cmd (cmd);
end proc;
proc do_hex (device_cmd);
cmd := new_cmd (`Hex');
cmd.cmd := device_cmd;
do_cmd (cmd);
end proc;
proc do_get (what);
cmd := new_cmd (`Get');
cmd.what := what;
value := unstr do_cmd (cmd);
return value;
end proc;
proc do_cmd (cmd);
writea(do_fd, cmd);
geta (do_fd, response_line);
if eof then
help (`Mid-level command service crashed - sorry.');
msg (`EOF from '+filename do_fd+` - closing');
quit_gracefully; -- we can't do much without the do service
end if;
return response_line;
end proc;
proc open_do_server();
fd := obtain_service (`do');
if fd = om then
help (`Mid-level command service down - sorry.');
msg (`could not open "do" service');
quit_gracefully; -- we can't do much without the do service
end if;
return fd;
end proc;
proc open_notice_server();
fd := obtain_service (`notice');
if fd = om then
help (`Mid-level notice service down - sorry.');
msg (`could not open "notice" service');
-- Take this to be a non-fatal error.
end if;
return fd;
end proc;
proc quit_gracefully;
-- Degenerate, since we currently have no pump- or pipe-attached child
exit_gracefully ([ ]);
end proc;
#include ``vc-obtain.setl''
#include ``vc-getname.setl''
#include ``vc-decode.setl''
#include ``vc-exit.setl''
#include ``vc-msg.setl''