-- RS232ServerImpl.mesa
-- Copyright (C) 1984, 1985 Xerox Corporation.  All rights reserved.
-- Tim Diebert:     8-Nov-85 10:15:58
  
DIRECTORY
  DicentraRS232CAsync,
  Process,
  PupDefs USING [PupAddressLookup, PupNameTrouble],
  PupStream USING [CloseReason, CreatePupByteStreamListener, PupAddress, PupListener, RejectThisRequest, SecondsToTocks, StreamClosing],
  Stream,
  String USING [Copy],
  TTY;
  
RS232ServerImpl: MONITOR
  IMPORTS DicentraRS232CAsync, Process, PupDefs, PupStream, Stream, String =
  BEGIN
  
  rs232Stream: Stream.Handle ← NIL;
  busy: ARRAY DicentraRS232CAsync.PortNumber OF BOOL ← ALL [FALSE];
  listener: PupStream.PupListener ← NIL;
  active: BOOL ← FALSE;
  kill, killKeyWatcher: BOOL ← FALSE;
  currentUser: LONG STRING ← [255];
  wakeUp, bufferFlushed: CONDITION; -- set their timeouts
  buffer: ARRAY [0 .. 255) OF CHAR;
  bufferPtr: CARDINAL ← 0;
    
  KeyWatcher: PROC = {
    PutIt: ENTRY PROC [c: CHAR] = {
      WHILE bufferPtr > 255 DO 
        NOTIFY wakeUp; 
	EXIT;
--	WAIT bufferFlushed;
--	IF killKeyWatcher THEN RETURN;
	ENDLOOP;
      IF bufferPtr <= 255 THEN {
        buffer[bufferPtr] ← c; bufferPtr ← bufferPtr + 1};
      };
    DO
      c: CHAR ← rs232Stream.GetChar[];
--      IF killKeyWatcher THEN
--        {killKeyWatcher ← FALSE; Stream.Delete[rs232Stream]; active ← FALSE; RETURN};
      PutIt[c];
--      IF killKeyWatcher THEN
--       {killKeyWatcher ← FALSE; Stream.Delete[rs232Stream]; active ← FALSE; RETURN};
      ENDLOOP;
    };
  keySenderRunning: BOOL ← FALSE;  
  KeySender: ENTRY PROC [s: Stream.Handle] = {
    ENABLE PupStream.StreamClosing => {GOTO Out};
    keySenderRunning ← TRUE;
    DO
      WAIT wakeUp;
      IF kill THEN
         { kill ← FALSE;
	 -- killKeyWatcher ← TRUE;
	 GOTO Out};
      IF bufferPtr = 0 THEN LOOP;
      FOR i: CARDINAL IN [0 .. bufferPtr) DO s.PutChar[buffer[i]] ENDLOOP;
      Stream.SendNow[s];
      bufferPtr ← 0; NOTIFY bufferFlushed;
      ENDLOOP;
    EXITS Out =>
      {-- killKeyWatcher ← TRUE;
      kill ← FALSE;
      NOTIFY bufferFlushed;
      keySenderRunning ← FALSE;
      RETURN}; -- and kill process
    };
  
  NetWatcher: PROC [s: Stream.Handle] = {
    ENABLE PupStream.StreamClosing => {kill ← TRUE;
      Note[];
      GOTO Out};
    DO
      c: CHAR ← s.GetChar[];
      rs232Stream.PutChar[c];
      ENDLOOP;
    EXITS Out => {Stream.Delete[s]; RETURN};
    };
    
  Note: ENTRY PROC [] = {NOTIFY wakeUp};

  InitCommand: PROC [stream: Stream.Handle, pupAddress: PupStream.PupAddress] =
    BEGIN ENABLE {
      PupStream.StreamClosing => GOTO Exit;
      Stream.TimeOut => GOTO Exit};
--    name: STRING = "DicentraRS232.log"L;
--    ttyImpl, backing: Stream.Handle;
    port: DicentraRS232CAsync.PortNumber ← LOOPHOLE[Stream.GetChar[stream]];
    charLength: DicentraRS232CAsync.CharLength ← LOOPHOLE[Stream.GetChar[stream]];
    speed: DicentraRS232CAsync.LineSpeed ← LOOPHOLE[Stream.GetChar[stream]];
    parity: DicentraRS232CAsync.Parity ← LOOPHOLE[Stream.GetChar[stream]];
    stop: DicentraRS232CAsync.StopBits ← LOOPHOLE[Stream.GetChar[stream]];
    currentUser.length ← 0;
    PupDefs.PupAddressLookup[currentUser, pupAddress
      !  PupDefs.PupNameTrouble => {String.Copy[to: currentUser, from: e]; CONTINUE}];
    bufferPtr ← 0;
    active ← TRUE;
--    [ttyImpl, backing] ← DicentraRS232CAsync.CreateRS232Instance[name, NIL,
--      TTY.nullHandle, [charLength, speed, parity, stop]];
--    rs232Stream ← ttyImpl;
    Stream.PutChar[stream, 'k];
    Stream.SendNow[stream];
    bufferPtr ← 0;
--    Process.Detach[FORK KeyWatcher[]];
    Process.Detach[FORK KeySender[stream]];
--    Process.Detach[FORK NetWatcher[stream]];
    BEGIN ENABLE PupStream.StreamClosing, Stream.TimeOut =>
      {IF keySenderRunning THEN kill ← TRUE; Note[]; GOTO Out};
        DO
          c: CHAR ← stream.GetChar[];
          rs232Stream.PutChar[c];
          ENDLOOP;
        EXITS Out => {Stream.Delete[stream]; GOTO Exit};
      END;
    EXITS Exit => {active ← FALSE; RETURN};
    END;
    
  CheckBusy: PROC [pupAddress: PupStream.PupAddress] = BEGIN
    IF active THEN ERROR PupStream.RejectThisRequest[currentUser];
    END;
  
  Init: PROC [] = BEGIN
    ttyImpl, backing: Stream.Handle;
    name: STRING = "DicentraRS232.log"L;
    bufferPtr ← 0;
    [ttyImpl, backing] ← DicentraRS232CAsync.CreateRS232Instance[name, NIL,
      TTY.nullHandle, -- [charLength, speed, parity, stop] --];
    rs232Stream ← ttyImpl;
    IF listener = NIL THEN listener ← PupStream.CreatePupByteStreamListener[
      local: [0, 32B], 
      proc: InitCommand,
      ticks: PupStream.SecondsToTocks[30*60],
      filter: CheckBusy];
    Process.SetTimeout[@wakeUp, Process.MsecToTicks[250]];
    Process.SetTimeout[@bufferFlushed, Process.MsecToTicks[1000]];
    
    Process.Detach[FORK KeyWatcher[]];
    END;
  
  Init[];
  
  END.