// GateConRoute.bcpl -- talks to the Gateway forwarder process
// Last modified July 2, 1983  10:06 PM by Boggs

get "Pup0.decl"
get "Pup1.decl"
get "GateForward.decl"

external
[
// outgoing procedures
CreateRouteCtx; RestartRoute; Route
RouteSummary; TransitMatrix; GetNetTable

// incoming procedures
OpenLevel1Socket; CloseLevel1Socket; GetBuf; ReleasePBI
InitializeContext; Block; SetTimer; TimerHasExpired
Zero; MoveBlock; Allocate; Free; Enqueue; Dequeue; ReturnFrom
TopLevel; ResetCmdMenu; CreateCmdBox; BoxProc
Ws; Wss; Puts; PutTemplate
SendCommand; HEnumerate

// incoming statics
dsp; ctxQ; sysZone; pupRT; gcHost; oldStatsQ
]

static @ru

structure RU:		// ru -> this 'global frame' for this module
[
soc word		// -> PupSoc
stats word		// -> Router Stats block
timer word		// update stats when this expires
]
manifest lenRU = size RU/16

//----------------------------------------------------------------------------
let CreateRouteCtx() be
//----------------------------------------------------------------------------
[
Enqueue(ctxQ, InitializeContext(Allocate(sysZone, 150), 150, RouteCtx))
ru = Allocate(sysZone, lenRU); Zero(ru, lenRU)
ru>>RU.soc = Allocate(sysZone, lenPupSoc); OpenLevel1Socket(ru>>RU.soc)
]

//----------------------------------------------------------------------------
and RestartRoute() be
//----------------------------------------------------------------------------
[
SetTimer(lv ru>>RU.timer, 0)
if ru>>RU.stats ne 0 then
   [ Enqueue(oldStatsQ, ru>>RU.stats); ru>>RU.stats = 0 ]
]

//----------------------------------------------------------------------------
and Route() be
//----------------------------------------------------------------------------
[
ResetCmdMenu()
CreateCmdBox(TopLevel, "TopLevel")
if gcHost ne 0 then CreateCmdBox(FrnRoutingTable, "FrnRoutingTable")
CreateCmdBox(LclRoutingTable, "LclRoutingTable")
// ResetCmdMenu destroyed the BoxQ which BoxProc is following.
// If we just return now, BoxProc will get horribly confused.
// So don't let it continue: force a return from BoxProc.
ReturnFrom(BoxProc)
]

//----------------------------------------------------------------------------
and RouteCtx(ctx) be  //a context
//----------------------------------------------------------------------------
[
Block() repeatuntil TimerHasExpired(lv ru>>RU.timer) & gcHost ne 0
SetTimer(lv ru>>RU.timer, 500)
let pbi = GetBuf(ru>>RU.soc)
pbi>>PBI.pup.dPort.socket↑2 = psRouteInfo
pbi = SendCommand(pbi, ptStatsRequest, pupOvBytes, (ru>>RU.stats? 1, 3))
if pbi ne 0 then
   [
   let stats = lv pbi>>PBI.pup.words
   if pbi>>PBI.pup.type eq ptStatsReply &
    stats>>Stats.version eq gfStatsVersion then
      [
      if ru>>RU.stats then Enqueue(oldStatsQ, ru>>RU.stats)
      let lenStatBlock = (pbi>>PBI.pup.length-pupOvBytes)/2
      ru>>RU.stats = Allocate(sysZone, lenStatBlock)
      MoveBlock(ru>>RU.stats, stats, lenStatBlock)
      ]
   ReleasePBI(pbi)
   ]
] repeat

//----------------------------------------------------------------------------
and RouteSummary(stream) be
//----------------------------------------------------------------------------
[
if ru>>RU.stats ne 0 then
   PutTemplate(stream, "Route: $EUD  ", lv ru>>RU.stats>>Stats.routeReqs)
]

//----------------------------------------------------------------------------
and GetNetTable(v) = valof
//----------------------------------------------------------------------------
// called from Level0Stats to get info to build its menu.
[
if ru>>RU.stats eq 0 resultis false
v!0 = ru>>RU.stats>>Stats.numNets
v!1 = ru>>RU.stats + lenStats
]

//----------------------------------------------------------------------------
and TransitMatrix(stream) be
//----------------------------------------------------------------------------
[
let stats = ru>>RU.stats; if stats eq 0 return
let netTable = stats + lenStats
let numNets = stats>>Stats.numNets

Wss(stream, "     Discard")
for i = 0 to numNets-1 if netTable!i ne 0 then 
   PutTemplate(stream, "$10UO", netTable!i)

for sNet = 0 to numNets-1 do
   [
   if netTable!sNet eq 0 loop
   PutTemplate(stream, "*N$3O", netTable!sNet)
   PrintCount(stream, stats, (netTable!sNet)&377b, 0)  //discard column
   for dNet = 0 to numNets-1 if netTable!dNet ne 0 then
      PrintCount(stream, stats, (netTable!sNet)&377b, (netTable!dNet)&377b)
   ]
]

//----------------------------------------------------------------------------
and PrintCount(stream, stats, sNet, dNet) be
//----------------------------------------------------------------------------
[
for i = 0 to stats>>Stats.numTMEs-1 do
   [
   let tme = stats + lenStats + stats>>Stats.numNets + i*lenTME
   if tme>>TME.sNet eq sNet & tme>>TME.dNet eq dNet then
      [
      PutTemplate(stream, "$10UED", lv tme>>TME.count)
      return
      ]
   ]
Wss(stream, "        - ")
]

//----------------------------------------------------------------------------
and FrnRoutingTable() be
//----------------------------------------------------------------------------
[
let soc = vec lenPupSoc; OpenLevel1Socket(soc)
let pbi = GetBuf(soc)
pbi>>PBI.pup.dPort.socket↑2 = psRouteInfo
pbi = SendCommand(pbi, ptRouteRequest, pupOvBytes, 1)
if pbi then
   [
   Enqueue(lv soc>>PupSoc.iQ, pbi)
   RTTitle("*NForeign routing table:")
   ]
let count = 0
let timer = nil; SetTimer(lv timer, 1000)  // 5 seconds
   [
   Block() repeatuntil soc>>PupSoc.iQ.head ne 0 % TimerHasExpired(lv timer)
   let pbi = Dequeue(lv soc>>PupSoc.iQ.head); if pbi eq 0 break
   if pbi>>PBI.pup.type eq ptRouteReply then
      for i = 1 to pbi>>PBI.pup.length-pupOvBytes by 4 do
         RTEntry(pbi>>PBI.pup.bytes↑i, pbi>>PBI.pup.bytes↑(i+1),
          pbi>>PBI.pup.bytes↑(i+2), pbi>>PBI.pup.bytes↑(i+3), lv count)
   ReleasePBI(pbi)
   ] repeat
CloseLevel1Socket(soc)
]

//----------------------------------------------------------------------------
and LclRoutingTable() be
//----------------------------------------------------------------------------
[
let PerNet(rte, lvCount) be
   RTEntry(rte>>RTE.net, rte>>RTE.ndb>>NDB.localNet,
    rte>>RTE.host, rte>>RTE.hops, lvCount)
let count = 0
RTTitle("*NLocal routing table:")
HEnumerate(pupRT, PerNet, lv count)
]

//----------------------------------------------------------------------------
and RTTitle(string) be
//----------------------------------------------------------------------------
[
Ws(string)
Ws("*N Net   Via   Hops | Net   Via   Hops | Net   Via   Hops | Net   Via   Hops")
Ws("*N -----------------|------------------|------------------|-----------------")
]

//----------------------------------------------------------------------------
and RTEntry(dNet, viaNet, viaHost, hops, lvCount) be
//----------------------------------------------------------------------------
[
if @lvCount rem 4 eq 0 then Ws("*N ")
PutTemplate(dsp, "$U3O $U3O#$U3F0O# $U2O ", dNet, viaNet, viaHost, hops)
unless @lvCount rem 4 eq 3 do Ws(" | ")
@lvCount = @lvCount +1
]