next up previous
Next: A.34 vc-push.setl Up: A. WEBeye Source Code Previous: A.32 vc-provide.setl

  
A.33 vc-ptz.setl

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_fdin_fdnotice_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_fd /= om 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 #words >= 2 then
          words(1..1) := [ ];
          if words(1) ceq `Tothen
            if #words >= 2 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 `Bythen
            if #words >= 2 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 `Inthen
            do_fussy_zoom (`By', 1.618, words(2..));
          elseif words(1) ceq `Outthen
            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_speedtilt_speed);
      else
        if #words >= 3 then
          words(1..1) := [ ];
          toby := `To';
          if words(1) ceq `Tothen
            words(1..1) := [ ];
          elseif words(1) ceq `Bythen
            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 (tobypantiltwords(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_speedtilt_speed);
      else
        help (`Speed command parameter error');
      end if;
 
    (cmd ceq `Up'):  -- Up deg [[In] ms | At speed]
      if #words >= 2 and is_num words(2) then
        tilt := val words(2);
        do_fussy_move (`By', 0, tiltwords(3..));
      else
        help (`Up command parameter error - try Help Up');
      end if;
 
    (cmd ceq `Down'):  -- Down deg [[In] ms | At speed]
      if #words >= 2 and is_num words(2) then
        tilt := val words(2);
        do_fussy_move (`By', 0, -tiltwords(3..));
      else
        help (`Down command parameter error - try Help Down');
      end if;
 
    (cmd ceq `Left'):  -- Left deg [[In] ms | At speed]
      if #words >= 2 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 #words >= 2 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', pantilt);
      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_fd /= om then `Onelse `Offend);
      (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 `Startelse `Stopend);
      (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']):
        [pantilt] := 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 `Startelse `Stopend);
      (words(2..ceq [`Speed'],
       words(2..ceq [`Move',`Speed']):
        [pan_speedtilt_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_fd /= om and notice_fd in ready then
    reada (notice_fdnotice);
    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 (ab);
  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_fds+`\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 (tobyzoomwords);
  if #words = 2 and words(1) ceq `At'
                and is_num words(2) then
    speed := val words(2);
    do_zoom (tobyzoomspeed);
  elseif #words = 0 then
    do_zoom (tobyzoom);
  else
    help (`Error in [At speed] option - try Help Zoom');
  end if;
end proc;
 
proc do_zoom (subcmdx(*));
  cmd := new_cmd (`Zoom');
  cmd.subcmd := subcmd;
  case subcmd of
  (`Start'):  pass;
  (`Stop'):   pass;
  (`Speed'):  [cmd.zoom_speed,  -        ] := x;
  (`To'):     [cmd.zoom_factorcmd.speed] := x;
  (`By'):     [cmd.zoom_scale,  cmd.speed] := x;
  end case;
  do_cmd (cmd);
end proc;
 
proc do_fussy_move (tobypantiltwords);
  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 (tobypantilt, `In', ms); 
  elseif #words = 2 and words(1) ceq `At'
                    and is_num words(2) then
    speed := val words(#words);
    do_move (tobypantilt, `At', speed);
  elseif #words = 0 then
    do_move (tobypantilt);
  else
    help (`Error in In or At option - try Help Move');
  end if;
end proc;
 
proc do_move (subcmdx(*));
  cmd := new_cmd (`Move');
  cmd.subcmd := subcmd;
  case subcmd of
  (`Start'):  pass;
  (`Stop'):   pass;
  (`Speed'):  [cmd.pan_speedcmd.tilt_speed] := x;
  (`To',`By'):
    [cmd.pancmd.tilt] := x;
    if x(3) = `Inthen
      cmd.ms := x(4);
    elseif x(3) = `Atthen
      cmd.speed := x(4);
    end if;
  end case;
  do_cmd (cmd);
end proc;
 
proc do_jump (tobypantilt);
  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_fdcmd);
  geta (do_fdresponse_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''


next up previous
Next: A.34 vc-push.setl Up: A. WEBeye Source Code Previous: A.32 vc-provide.setl
David Bacon
1999-12-10