
/*
 *  NETSTAT.C
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <graphics/gfxmacros.h>
#include <graphics/rastport.h>
#include <libraries/dosextens.h>
#include <proto/intuition.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "netstat.h"

#define MAXSTATS 300
#define XOFF	  10
#define YOFF	  40
#define TIMEINTERVAL 1
#define YMARGIN   12
#define MINPEAK  400
#define TEXT1	(6*8)
#define TEXT2	(25*8)
#define XMARGIN   12
#define MKBADDR(x)      ((BPTR)((long)x >> 2))

struct NewWindow NewWindowStructure1 = {
   0,0,
   XOFF+MAXSTATS+XMARGIN, YOFF+YMARGIN+50,
   0,1,
   MENUPICK+CLOSEWINDOW+REFRESHWINDOW+NEWSIZE,
   WINDOWDRAG+WINDOWDEPTH+WINDOWCLOSE+WINDOWSIZING+SIMPLE_REFRESH,
   NULL,
   NULL,
   "Software Distillery NET: Status",
   NULL,
   NULL,
   XOFF+MAXSTATS+XMARGIN,YOFF+YMARGIN+20,
   9999,9999,
   WBENCHSCREEN
};

int xsize = 300;
int ysize = 100;
int yscale = 100;
long peak = 0;

void main(int, char **);
void MemCleanup(void);
void refresh(void);
void QueueTimer(struct timerequest *, ULONG);
void setscale(void);
void update(void);
int dispvals(void);
void DeleteIOReq(struct IOStdReq *);
long sendpkt(struct MsgPort *, long, long*, long);
struct IOStdReq *CreateIOReq(struct MsgPort *, int);

/* Global variables */

struct Window *Window;
struct TmpRas tmpras;
long wstats[MAXSTATS], rstats[MAXSTATS];
int statpos;


void main(argc, argv)
int argc;
char **argv;
{
   WORD areabuffer[300];
   struct IntuiMessage *message;
   int run;
   ULONG portmask, waitmask, windmask, timemask;
/* struct MenuItem *item; */
   struct MsgPort *port;
   struct AreaInfo myAreaInfo;
   struct statmsg *statmsg;
   struct MsgPort *proc;
   long args[8];
   struct timerequest *timerreq;
   struct MsgPort     *timerport;

   timerreq	       = NULL;
   timerport	       = NULL;
   proc 	       = NULL;
   Window	       = NULL;
   port 	       = NULL;
   args[0]	       = 0;
   if ((IntuitionBase = (struct IntuitionBase *)
		       OpenLibrary("intuition.library", 0)) == NULL) goto done;
   if ((GfxBase = (struct GfxBase *)
		 OpenLibrary("graphics.library", 0)) == NULL) goto done;
   if ((Window = OpenWindow(&NewWindowStructure1)) == NULL) goto done;

   if ((port = CreatePort("netstat_port", 0)) == NULL) goto done;
   if ((timerport = CreatePort(NULL, 0)) == NULL) goto done;
   if ((timerreq  = (struct timerequest *)
	   CreateIOReq(timerport, sizeof(struct timerequest))) == NULL)
	   goto done;
   if (OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest *)timerreq, 0))
	   goto done;

   InitArea(&myAreaInfo, areabuffer, 175);
   Window->RPort->AreaInfo = &myAreaInfo;
   Window->RPort->TmpRas = InitTmpRas(&tmpras, AllocRaster(600, 175),
					       RASSIZE(600, 175));
   if ((argc > 1) &&
       ((proc = (struct MsgPort *)DeviceProc(argv[1])) != NULL))
      {
      args[0] = 0x40000002;
      args[1] = MKBADDR(port);
      sendpkt(proc, 2010, args, 2);
      }

   memset((char *)wstats, 0, sizeof(wstats));
   memset((char *)rstats, 0, sizeof(rstats));
   statpos = 0;
   peak = 0;
   ysize = Window->Height-(YOFF+YMARGIN);
   if (ysize < 20) ysize = 20;
   setscale();
   refresh();

   run = 0;

   windmask = 1 << Window->UserPort->mp_SigBit;
   portmask = 1 << port->mp_SigBit;
   timemask = (1 << timerport->mp_SigBit);
   QueueTimer(timerreq, TIMEINTERVAL);
   while(run == 0)
      {
      waitmask = Wait(windmask | portmask | timemask);
      if (waitmask & timemask)
	 {
	 /* get rid of the message */
	 (void)GetMsg(timerport);
	 QueueTimer(timerreq, TIMEINTERVAL);
	 /* Also, we want to scroll the entire thing left one pixel */
	 update();

	 statpos ++;
	 if (statpos >= MAXSTATS)
	    statpos = 0;
	 wstats[statpos] = rstats[statpos] = 0;
	 }
      if (waitmask & portmask)
	 {
	 statmsg = (struct statmsg *)GetMsg(port);
	 if (statmsg->direction)
	    rstats[statpos] += statmsg->count;
	 else
	    wstats[statpos] += statmsg->count;
	 ReplyMsg((struct Message *)statmsg);
	 }
      if (waitmask & windmask)
	 {
	 while ((message = (struct IntuiMessage *)
			   GetMsg(Window->UserPort)) != NULL)
	    {
	    switch(message->Class)
	       {
	       case CLOSEWINDOW:
		  run = -1;
		  break;
#if 0
	       case MENUPICK:
		  item = ItemAddress(&MenuList1, message->Code);
		  switch(item->Command)
		     {
		     case 1:
			 run = 1;
		     default:
			 run = 1;
			 break;
		     }
		  break;
#endif

	       case NEWSIZE:
	       case REFRESHWINDOW:
		  ysize = Window->Height-(YOFF+YMARGIN);
		  if (ysize < 20) ysize = 20;
		  setscale();
		  refresh();
		  break;

	       default:
		  break;
	       }
	    ReplyMsg((struct Message *)message);
	    }
	 }
      }

   Wait(timemask);

   Window->RPort->TmpRas = NULL;
   FreeRaster(tmpras.RasPtr, 600, 175);

done:
   if (args[0] && proc != NULL)
      {
      args[1] = 0;
      sendpkt(proc,2010,args,2);
      }

   /* Reply to any outstanding messages */
   while(statmsg = (struct statmsg *)GetMsg(port))
      ReplyMsg((struct Message *)statmsg);

   if (timerreq != NULL)
      {
      if (timerreq->tr_node.io_Device != NULL)
	 {
	 AbortIO( (struct IORequest *)timerreq );
	 CloseDevice((struct IORequest *)timerreq);
	 }
      DeleteIOReq((struct IOStdReq *)timerreq);
      }
  if (timerport != NULL)     DeletePort(timerport);
  if (Window != NULL) CloseWindow(Window);
  if (IntuitionBase != NULL) CloseLibrary((struct Library *)IntuitionBase);
  if (GfxBase != NULL)       CloseLibrary((struct Library *)GfxBase);
  if (port != NULL)          DeletePort(port);
  XCEXIT(0L);
}

void setscale()
{
/* Now recalculate the scale */
if (peak < MINPEAK)
   yscale = MINPEAK/ysize;
else
   yscale = (peak*5)/(ysize*4);

}
int dispvals()
{
char buf[80];
long reads, writes, perf;
struct RastPort *rp = Window->RPort;
static long lr, lw, lp, lk;

reads = rstats[statpos];
writes = wstats[statpos];
perf = reads+writes;

/* Have we hit a new high? */
if (perf > peak)
   {
   peak = perf;
   /* Will this shoot off the end of the graph ? */
   if ((peak/yscale) > ysize)
      {
      setscale();
      return(1);
      }
   }

SetAPen(rp, 0);
if (lp != perf || lr != reads)
   RectFill(rp, XOFF+TEXT1, YOFF-28, XOFF+TEXT1+(7*8), YOFF-1);
if (lk != peak || lw != writes)
   RectFill(rp, XOFF+TEXT2, YOFF-28, XOFF+TEXT2+(7*8), YOFF-1);

lp = perf;
lr = reads;
lk = peak;
lw = writes;

SetAPen(rp, 3);
sprintf(buf, "%7d", perf);
Move(rp, XOFF+TEXT1, YOFF-20);
Text(rp, buf, 7);

sprintf(buf, "%7d", peak);
Move(rp, XOFF+TEXT2, YOFF-20);
Text(rp, buf, 7);

sprintf(buf, "%7d", reads);
Move(rp, XOFF+TEXT1, YOFF-10);
Text(rp, buf, 7);

sprintf(buf, "%7d", writes);
Move(rp, XOFF+TEXT2, YOFF-10);
Text(rp, buf, 7);
return(0);
}

void update()
{
short rsize, wsize;
struct RastPort *rp = Window->RPort;

if (dispvals())
   {
   refresh();
   return;
   }

ScrollRaster(rp, 1, 0, XOFF, YOFF, XOFF+xsize, YOFF+ysize);
/* Now update the data for the one we just finished */
rsize = rstats[statpos]/yscale;
wsize = wstats[statpos]/yscale;
/* Now display the line at the right place for the read and then the write */
Move(rp, XOFF+MAXSTATS, YOFF+ysize);
if (rsize)
   {
   SetAPen(rp, 2);
   Draw(rp, XOFF+MAXSTATS, YOFF+ysize-rsize);
   }
if (wsize)
   {
   SetAPen(rp, 3);
   Draw(rp, XOFF+MAXSTATS, YOFF+ysize-rsize-wsize);
   }
/* And put a point where the limit is */
SetAPen(rp, 1);
WritePixel(rp, XOFF+MAXSTATS, YOFF+ysize-(peak/yscale));
}

void refresh()
{
int i, j;
short rsize, wsize;
struct RastPort *rp = Window->RPort;
char *p;

SetAPen(rp, 0);
RectFill(rp, XOFF, YOFF, XOFF+MAXSTATS+1, YOFF+ysize);

p = "Total:        Bps    Peak:        Bps";
SetAPen(rp, 1);
Move(rp, XOFF, YOFF-20);
Text(rp, p, strlen(p));

p = "Reads:        Bps  Writes:        Bps";
Move(rp, XOFF, YOFF-10);
Text(rp, p, strlen(p));

SetAPen(rp, 3);
p = " ) 1989 The Software Distillery";
Move(rp, XOFF, YOFF+ysize+9);
Text(rp, p, strlen(p));
dispvals();

SetAPen(rp, 1);
SetDrMd(rp, JAM1);
Move(rp, XOFF-1, YOFF);
Draw(rp, XOFF-1, YOFF+1+ysize);
Draw(rp, XOFF+1+xsize, YOFF+1+ysize);

i = statpos+1;
for (j = 0; j < MAXSTATS; j++)
   {
   if (i >= MAXSTATS) i = 0;
   rsize = rstats[i]/yscale;
   wsize = wstats[i]/yscale;
   /* Now display the line at the right place for the read and then the write */
   Move(rp, XOFF+j, YOFF+ysize);
   if (rsize)
      {
      SetAPen(rp, 2);
      Draw(rp, XOFF+j, YOFF+ysize-rsize);
      }
   if (wsize)
      {
      SetAPen(rp, 3);
      Draw(rp, XOFF+j, YOFF+ysize-rsize-wsize);
      }
   i++;
   }
SetAPen(rp, 1);
Move(rp, XOFF, YOFF+ysize-(peak/yscale));
Draw(rp, XOFF+MAXSTATS, YOFF+ysize-(peak/yscale));
}

/************************************************************************/
/* Queue a timer to go off in a given number of seconds 		*/
/************************************************************************/
void QueueTimer(tr, seconds)
struct timerequest *tr;
ULONG seconds;
{
   tr->tr_node.io_Command = TR_ADDREQUEST;   /* add a new timer request */
   tr->tr_time.tv_secs =  seconds;		/* seconds */
   tr->tr_time.tv_micro = 0;
   SendIO( (struct IORequest *)tr );
}


struct IOStdReq *CreateIOReq(port, size)
struct MsgPort *port;
int size;
{
   register struct IOStdReq *ioReq;

   if ((ioReq = (struct IOStdReq *)
		AllocMem(size, MEMF_CLEAR | MEMF_PUBLIC)) != NULL)
      {
      ioReq->io_Message.mn_Node.ln_Type = NT_MESSAGE;
      ioReq->io_Message.mn_Node.ln_Pri	= 0;
      ioReq->io_Message.mn_Length	= size;
      ioReq->io_Message.mn_ReplyPort	= port;
      }
   return(ioReq);
}

void DeleteIOReq(ioReq)
register struct IOStdReq *ioReq;
{
   ioReq->io_Message.mn_Node.ln_Type = 0xff;
   ioReq->io_Device = (struct Device *) -1;
   ioReq->io_Unit = (struct Unit *) -1;

   FreeMem( (char *)ioReq, ioReq->io_Message.mn_Length);
}

LONG sendpkt(pid,action,args,nargs)
struct MsgPort *pid;  /* process indentifier ... (handlers message port ) */
LONG action,	      /* packet type ... (what you want handler to do )   */
     args[],	      /* a pointer to a argument list */
     nargs;	      /* number of arguments in list  */
   {
   struct MsgPort	 *replyport;
   struct StandardPacket *packet;

   LONG  count, *pargs, res1;

   replyport = (struct MsgPort *) CreatePort(NULL,0);
   if(!replyport) return(NULL);

   packet = (struct StandardPacket *)
      AllocMem((long)sizeof(struct StandardPacket),MEMF_PUBLIC|MEMF_CLEAR);
   if(!packet)
      {
      DeletePort(replyport);
      return(NULL);
      }

   packet->sp_Msg.mn_Node.ln_Name = (char *)&(packet->sp_Pkt);
   packet->sp_Pkt.dp_Link	  = &(packet->sp_Msg);
   packet->sp_Pkt.dp_Port	  = replyport;
   packet->sp_Pkt.dp_Type	  = action;

   /* copy the args into the packet */
   pargs = &(packet->sp_Pkt.dp_Arg1);       /* address of first argument */
   for(count=0;count < nargs;count++)
      pargs[count]=args[count];

   PutMsg(pid,(struct Message *)packet); /* send packet */

   WaitPort(replyport);
   GetMsg(replyport);

   res1 = packet->sp_Pkt.dp_Res1;

   FreeMem((char *)packet,(long)sizeof(struct StandardPacket));
   DeletePort(replyport);

   return(res1);
}

void MemCleanup(){}
