MODULE CommObxStreamsServer;
(**

   project   = "BlackBox"
   organization   = "www.oberon.ch"
   contributors   = "Oberon microsystems"
   version   = "System/Rsrc/About"
   copyright   = "System/Rsrc/About"
   license   = "Docu/BB-License"
   changes   = ""
   issues   = ""

**)

   IMPORT Services, CommStreams, Views, TextModels, TextViews, StdLog;

   CONST

      protocol = "CommTCP";         (* driver for TCP/IP communication, over Winsock *)
      localAdr = "900";               (* this is the port on which the server listens *)
   TYPE

      Server = POINTER TO RECORD (Services.Action)
         listener: CommStreams.Listener   (* the server listens using this object and accepts incoming connections*)
      END;
      Processor = POINTER TO RECORD (Services.Action)

         prev, succ: Processor;   (* linked to allow stopping of all active processors with Stop command *)
         stream: CommStreams.Stream;   (* for each connection, a receiver communicates through this object *)
         writer: TextModels.Writer
      END;
   VAR

      server: Server;
      processors: Processor;
   PROCEDURE (server: Server) Do;

      VAR s: CommStreams.Stream; p: Processor; t: TextModels.Model;
   BEGIN
      server.listener.Accept(s);   (* poll for a connection *)
      IF s # NIL THEN   (* upon accept create and start processor for new connection *)
         NEW(p); p.prev := NIL; p.succ := processors; processors := p;
         IF p.succ # NIL THEN p.succ.prev := p END;
         p.stream := s;
         t := TextModels.dir.New(); p.writer := t.NewWriter(NIL);
         Views.OpenAux(TextViews.dir.New(t), "Message");
         Services.DoLater(p, Services.now)
      END;
      Services.DoLater(server, Services.now)
   END Do;
   PROCEDURE (p: Processor) Do;

      VAR buf: ARRAY 256 OF BYTE; read, i: INTEGER;
   BEGIN
      p.stream.ReadBytes(buf, 0, LEN(buf), read);   (* poll for incoming data *)
      IF read > 0 THEN   (* write received data into log *)
         i := 0; WHILE i # read DO p.writer.WriteChar(CHR(buf[i])); INC(i) END;
         Services.DoLater(p, Services.now)
      ELSIF p.stream.IsConnected() THEN
         Services.DoLater(p, Services.now)
      ELSE
         IF p.prev # NIL THEN p.prev.succ := p.succ ELSE processors := p.succ END;
         IF p.succ # NIL THEN p.succ.prev := p.prev END
      END
   END Do;
   PROCEDURE Start*;


      VAR l: CommStreams.Listener; res: INTEGER;
   BEGIN
      IF server = NIL THEN
         CommStreams.NewListener(protocol, localAdr, l, res);
         IF l # NIL THEN
            NEW(server); server.listener := l; Services.DoLater(server, Services.now);
            StdLog.String("server: server started"); StdLog.Ln
         ELSE
            StdLog.String("server: error starting the server ("); StdLog.Int(res); StdLog.Char(")"); StdLog.Ln
         END
      END
   END Start;
   PROCEDURE Stop*;

   BEGIN
      WHILE processors # NIL DO
         processors.stream.Close; Services.RemoveAction(processors);
         processors := processors.succ
      END;
      IF server # NIL THEN
         Services.RemoveAction(server);
         server.listener.Close;
         server := NIL;
         StdLog.String("server: server stopped"); StdLog.Ln
      END
   END Stop;
CLOSE

   Stop   (* prevent the server from trapping after module unloading *)
END CommObxStreamsServer.
CommObxStreamsServer.Start

CommObxStreamsServer.Stop