PACKET basic net DEFINES (* D. Heinrichs *) (* 02.10.85 *) nam, max verbindungsnummer, neuer start, packet eingang, neue sendung, zeitueberwachung, verbindung, loesche verbindung: TEXT PROC nam (TASK CONST t): IF t = collector THEN name (t) ELIF station (t) <> station (myself) THEN "** fremd **" ELSE name (t) FI END PROC nam; INT PROC tasknr (TASK CONST t): IF t = collector THEN maxtasks ELSE index (t) FI END PROC tasknr; LET maxtasks = 127, max strom = 20, max strom 1 = 21, stx = ""2"", code stx = 2, ack = 0, nak = 1, error nak = 2, zeichen eingang = 4, list code = 15, fetch code = 11, inspect code = 30, continue code = 100, erase code = 14, report code = 99, seiten groesse = 512, dr verwaltungslaenge = 8, dr verwaltungslaenge2=10, nutzlaenge = 64, openlaenge = 20, vorspannlaenge = 10, neue ack laenge = 10, ack laenge = 8, (* Typen von Kommunikationsströmen *) send wait = 0, zustellung = 1, call pingpong = 2, call im wait = 3, call im abbruch = 4, call in zustellung = 5, (*quittungscodes*) ok = 0, von vorne = 1, wiederhole = 2, loesche = 3, beende = 4; LET STEUER = STRUCT ( INT head, rechner nummern, strom, sequenz, seitennummer, TASK quelle,ziel, INT sende code); BOUND STEUER VAR open block; BOUND STRUCT (STEUER steuer, INT typ) VAR info block; BOUND STRUCT ( INT head, rechner nummern, strom, sequenz, seitennummer) VAR vorspann ; BOUND STRUCT ( INT head, rechner nummern, strom, code) VAR ack packet ; INT CONST max verbindungsnummer := max strom; BOOL PROC blockin (DATASPACE VAR ds, INT CONST seite, abstand, laenge): INT VAR hilfslaenge:=laenge, code:= abstand+laenge+512; REAL VAR time out := clock (1) + 10.0; REP blockin (ds,seite,code-hilfslaenge, hilfslaenge, hilfslaenge); UNTIL hilfslaenge = 0 OR clock (1) > time out PER ; hilfslaenge = 0 END PROC blockin; PROC blockout (DATASPACE CONST ds, INT CONST seite, abstand, laenge): INT VAR hilfslaenge:=laenge, code:= abstand+laenge+512; REP blockout (ds,seite,code-hilfslaenge, hilfslaenge, hilfslaenge); UNTIL hilfslaenge = 0 PER END PROC blockout; DATASPACE VAR work space; INT CONST packete pro seite:= seitengroesse DIV nutzlaenge, packete pro seite minus 1 := packete pro seite -1, datenpacketlaenge := vorspannlaenge + nutzlaenge; INT VAR err,strom; INT VAR own:=station (myself) , quit max := 3, quit zaehler := 3, own256 := 256*own; INT CONST stx open := code stx+256*openlaenge, stx quit := code stx+256*acklaenge; ROW maxstrom1 STEUER VAR verbindungen; ROW maxstrom1 DATASPACE VAR netz dr; ROW maxstrom1 INT VAR zeit, typ; FOR strom FROM 1 UPTO maxstrom1 REP vdr := nilspace; forget (vdr) PER; ROW maxstrom INT VAR dr page ; ROW maxtasks INT VAR alter call; STEUER VAR opti; .vx : verbindungen (strom). vdr: netz dr (strom). falsche stromnummer: strom < 1 OR strom > maxstrom. call aufruf: typ(strom) >= call pingpong. alles raus: vx.seitennummer = -1 AND letztes packet der seite . letztes packet der seite : (vx.sequenz AND packete pro seite minus 1) = packete pro seite minus 1. PROC neuer start (INT CONST empfangsstroeme): workspace := nilspace; open block := workspace; info block := workspace; vorspann := workspace; ack packet := workspace; FOR strom FROM 1 UPTO maxstrom1 REP vx.strom := 0; forget (vdr) PER; INT VAR i; FOR i FROM 1 UPTO maxtasks REP alter call (i) := 0 PER; quitmax := empfangsstroeme; own:=station (myself); quit zaehler := quit max; own256 := 256*own; reset box. reset box: out (90*""4""); REP UNTIL incharety (1) = "" PER. END PROC neuer start; DATASPACE PROC verbindung (INT CONST nr): infoblock.steuer := verbindungen (nr); infoblock.typ := typ (nr); workspace END PROC verbindung; PROC neue sendung (TASK CONST q,z, INT CONST cod, DATASPACE CONST dr): naechste verbindung vorbereiten; forget (vdr); vdr := dr; IF z = collector THEN verbindungsebene ELSE sendung starten (q,z,cod) FI. verbindungsebene: IF cod = 256 THEN name von fremdstation ELIF cod > 256 THEN taskinfo fremd ELSE task id von fremd FI. taskinfo fremd: sendung starten (q, collector, cod-256, -8). task id von fremd: sendung starten (q,collector, zielstation,-6) . name von fremdstation: BOUND TASK VAR tsk := vdr; TASK VAR tsk1 := tsk; forget (vdr); vdr := nilspace; sendung starten (q, tsk1, -7). zielstation: cod. END PROC neue sendung; PROC zeitueberwachung (INT VAR snr, TASK VAR q, z, INT VAR ant,DATASPACE VAR dr): snr INCR 1; FOR strom FROM snr UPTO maxstrom REP zeitkontrolle PER; snr := 0. zeitkontrolle: IF vx.strom <> 0 AND zeit(strom) > 0 THEN zeit(strom) DECR 1; IF sendung noch nicht zugestellt THEN IF zeit(strom) = 0 THEN report ("Nicht zustellbar. """+nam (vx.ziel)+""". "+ text (vx.rechnernummernDIV256)); loesche verbindung (strom) ELSE snr := strom; q := vx.quelle; z := vx.ziel; ant := vx.sendecode; dr := vdr; LEAVE zeitueberwachung FI ELIF zeit(strom) = 0 THEN wiederholen FI FI. sendung noch nicht zugestellt: typ (strom) = zustellung. wiederholen: IF sendeeintrag THEN sendung wiederholen ELSE empfangseintrag freigeben FI. sendeeintrag : vx.rechnernummern DIV 256 = own . sendung wiederholen: IF wiederholung noch sinnvoll THEN IF frisch THEN time out bei open ELSE datenteil wiederholen FI ELSE sendung loeschen FI. wiederholung noch sinnvoll: task noch da AND bei call noch im call. task noch da: vx.quelle = collector OR exists (vx.quelle). bei call noch im call: IF call aufruf THEN callee (vx.quelle) = vx.ziel ELSE TRUE FI. frisch: vx.sequenz = -1. time out bei open: IF vx.sendecode > -4 THEN open wiederholen ELSE nak an quelle senden FI. nak an quelle senden: forget (vdr); vdr := nilspace; BOUND TEXT VAR erm := vdr; erm := "Station "+text(vx.rechnernummernMOD256)+" antwortet nicht"; snr := strom; q := collector; z := vx.quelle; ant := error nak; dr := vdr; sendung loeschen; LEAVE zeitueberwachung . open wiederholen: sendereport ("wdh open"); zeit(strom) := 20; openblock := vx; openblock.head := stx open; ab die post. datenteil wiederholen: sendereport ("wdh data. sqnr "+text (vx.sequenz)); senden . empfangseintrag freigeben: IF antwort auf call THEN weiter warten ELSE empfangsreport ("Empfangseintrag freigegeben"); empfang loeschen FI. antwort auf call: callee (vx.ziel) = vx.quelle. weiter warten: zeit (strom) := 200. END PROC zeitueberwachung; PROC sendereport (TEXT CONST txt): report (text (strom)+":"+txt+". Absender: """+nam (vx.quelle)+ """. Ziel "+text(vx.rechnernummernMOD256)); END PROC sendereport; PROC empfangsreport (TEXT CONST txt): report (text (strom)+":"+txt+". Empfänger: """ +nam (vx.ziel)+""". Quelle "+text (vx.rechnernummernDIV256)); END PROC empfangsreport ; PROC sendung loeschen: IF callaufruf CAND alter call (tasknr (vx.quelle)) = strom THEN alter call (tasknr (vx.quelle)) := 0 FI; vx.strom := 0; forget (vdr) END PROC sendung loeschen; PROC empfang loeschen: quit zaehler INCR 1; IF callaufruf AND alter call (tasknr (vx.ziel)) = strom THEN alter call (tasknr (vx.ziel)) := 0 FI; forget (vdr); vx.strom := 0 END PROC empfang loeschen; PROC loesche verbindung (INT CONST nr): strom := nr; IF sendeeintrag THEN sendung loeschen ELSE gegenstelle zum loeschen auffordern; empfang loeschen FI. gegenstelle zum loeschen auffordern: IF verbindung aktiv THEN quittieren (-loesche) FI. verbindung aktiv: vx.strom > 0. sendeeintrag: vx.rechnernummern DIV 256 = own . END PROC loesche verbindung; PROC weiter senden: IF NOT alles raus THEN sequenz zaehlung; IF neue seite THEN seitennummer eintragen FI; senden FI. sequenz zaehlung: vx.sequenz INCR 1. neue seite: (vx.sequenz AND packete pro seite minus 1) = 0. seitennummer eintragen: dr page (strom) := vx.seiten nummer; vx.seitennummer := next ds page (vdr, dr page (strom)). END PROC weiter senden; PROC senden: zeit(strom) := 3; vorspann senden; daten senden. vorspann senden: openblock := vx; blockout (workspace, 1, dr verwaltungslaenge, vorspannlaenge). daten senden: blockout (vdr,dr page (strom),distanz,nutzlaenge). distanz: nutzlaenge* (vx.sequenz AND (packete pro seite minus 1)). END PROC senden; PROC naechste verbindung vorbereiten: FOR strom FROM 1 UPTO maxstrom REP UNTIL vx.strom = 0 PER; IF vx.strom <> 0 THEN errorstop ("Verbindungsengpass") FI. END PROC naechste verbindung vorbereiten; PROC sendung starten (TASK CONST quelle, ziel, INT CONST code): sendung starten (quelle,ziel, station(ziel), code) END PROC sendung starten; PROC sendung starten (TASK CONST quelle, ziel, INT CONST ziel station,code): IF ziel station = own THEN report ("Irrläufer: Sendung an eigene Station. Absender:"""+ nam (quelle)+"""."); vx.strom := 0; forget (vdr) ELSE openblock.ziel := ziel; openblock.quelle :=quelle; openblock.sendecode := code; openblock.rechnernummern:= ziel station + own256; alten call loeschen (quelle); IF call oder ping pong THEN typ (strom) := call pingpong; call merken ELSE typ (strom) := send wait FI; sendung neu starten FI. call oder pingpong: openblock.ziel = callee (openblock.quelle). call merken: alter call (tasknr (quelle)) := strom. END PROC sendung starten; PROC sendung neu starten: openblock.head:= stx open; openblock.sequenz := -1; openblock.seitennummer:= next ds page (vdr,-1); openblock.strom := strom; vx := open block; zeit(strom) := 3; ab die post; vx.head:=code stx+256*(vorspannlaenge+nutzlaenge). END PROC sendung neu starten; . ab die post: block out (work space,1, dr verwaltungslaenge,open laenge). PROC alten call loeschen (TASK CONST quelle): IF alter call aktiv THEN INT VAR lstrom := strom; vx:=openblock; strom := alter call (tasknr (quelle)); IF in ausfuehrungsphase THEN sendereport ("Call-Löschung vorgemerkt"); loeschung vormerken ELSE report ("Call gelöscht."""+nam(quelle)+""". Strom "+text(strom)); loesche verbindung (strom) FI; strom := lstrom; openblock := vx FI. in ausfuehrungsphase: typ(strom) = call im wait OR typ (strom) = call in zustellung. loeschung vormerken: typ(strom) := call im abbruch; alter call (tasknr (quelle)) := 0. alter call aktiv: alter call (tasknr (quelle)) > 0. END PROC alten call loeschen; PROC packet eingang (TEXT CONST ft, INT VAR snr, TASK VAR q, z, INT VAR ant,DATASPACE VAR dr): snr := 0; vorspann holen; IF NOT ring logik THEN daten teil FI. ring logik: FALSE. # IF selbst quelle THEN daten aus puffer entfernen ; TRUE ELIF NOT selbst ziel THEN weitergeben; TRUE ELSE FALSE FI. selbst quelle: openblock.rechnernummern DIV 256 = station (myself). selbst ziel: (openblock.rechnernummern AND 255) = own. # daten aus puffer entfernen: IF code (t) > nutzlaenge THEN BOOL VAR dummy :=blockin (workspace, 1, drverwaltungslaenge, nutzlaenge) FI. # weitergeben: IF code (t) > nutzlaenge THEN IF NOT blockin (workspace, 2, 0, nutzlaenge) THEN LEAVE test auf packeteingang FI; FI; out (stx+t); blockout (workspace, 1, drverwaltungslaenge2, blocklaenge); IF code (t) > nutzlaenge THEN blockout (workspace, 2, 0, nutzlaenge) FI. # vorspann holen: sync; IF NOT blockin (workspace, 1, dr verwaltungslaenge2, block laenge) THEN LEAVE packeteingang FI. blocklaenge: IF code t > nutzlaenge THEN vorspannlaenge-2 ELSE code t -2 FI. sync: TEXT VAR skipped:=ft , t :=""; REP skipped CAT t; t := incharety (1); IF t = "" THEN report ("skipped",skipped); LEAVE packet eingang FI ; INT VAR codet := code (t); UNTIL blockanfang PER; IF skipped <> stx THEN report ("skipped bei sync:", skipped) FI. blockanfang: (skipped SUB length(skipped)) = stx AND (codet = datenpacketlaenge OR codet = ack laenge OR codet = neue ack laenge OR code t = openlaenge). daten teil: IF neue verbindung THEN verbindung bereitstellen ELIF quittung THEN strom := ack packet.strom; IF falsche stromnummer THEN report ("Strom falsch in Quittung"); LEAVE datenteil FI; IF vx.strom = 0 THEN LEAVE datenteil FI; IF ackpacket.code >= ok THEN weiter senden ELIF ackpacket.code = -von vorne THEN sendereport ("Neustart"); openblock := vx; sendung neu starten ELIF ackpacket.code = -wiederhole THEN back 16 ELIF ackpacket.code = -loesche THEN fremdloeschung ELIF ackpacket.code = -beende AND alles raus THEN strom abschliessen FI ELIF verbindung festgestellt THEN zeit(strom) := 200; opti := vx; datenpacket ELSE strom := maxstrom1; vx:=openblock; report ("Daten ohne Eroeffnung von " +text(vx.rechnernummernDIV256) +" Sequenznr "+text(openblock.sequenz)); daten aus puffer entfernen; IF alles raus THEN quittieren (-beende) ELSE quittieren(-von vorne) FI FI. verbindung bereitstellen: IF openblock.ziel = collector OR station (openblock.ziel) = own THEN freie verbindungsnummer; vdr := nilspace; vx := open block; zeit(strom) := 10; quittieren falls genug pufferplatz; vx.sequenz := 0 ; IF loeschung vorgemerkt THEN loesche verbindung (strom) ELSE opti := vx; abschluss testen FI; FI. loeschung vorgemerkt: typ(strom) = call im abbruch. strom abschliessen: IF call aufruf THEN zeit(strom) := 80; ausfuehrungsphase merken ELSE vx.strom := 0; forget (vdr) FI. ausfuehrungsphase merken: typ(strom) := call in zustellung. back16: datenraum etwas rueckspulen; nicht sofort senden (* wegen vagabundierender Quittungen *). nicht sofort senden: zeit(strom) := 2. datenraum etwas rueckspulen: sendereport ("etwas rueckgespult"); INT VAR sk , vs :=-1; dr page (strom) := -1; INT VAR i; FOR i FROM 1 UPTO vx.sequenz DIV packete pro seite - etwas REP vs INCR packete pro seite; dr page (strom) := next ds page (vdr, dr page (strom)) PER; vx.seiten nummer := next ds page (vdr, dr page (strom)) ; vx.sequenz := vs. etwas: 3. fremdloeschung: IF fremdrechner ok und sendung THEN IF typ (strom) = call in zustellung THEN typ (strom) := call im wait ELSE sendereport ("Sendung von Gegenstelle geloescht"); sendung loeschen FI FI. fremdrechner ok und sendung: (ackpacket.rechnernummern DIV 256) = (vx.rechnernummern AND 255). quittieren falls genug pufferplatz: IF quit zaehler > 0 THEN quit zaehler DECR 1; open quittieren; block vorab quittieren FI. open quittieren: quittieren (ok). block vorab quittieren: quittieren (ok). quittung: code t <= neue ack laenge. neue verbindung: code t = open laenge. verbindung festgestellt: FOR strom FROM maxstrom DOWNTO 1 REP IF bekannter strom THEN LEAVE verbindung festgestellt WITH TRUE FI PER; FALSE. bekannter strom: vx.strom = vorspann.strom AND vom selben rechner. vom selben rechner: vx.rechnernummern = vorspann.rechnernummern. daten: IF NOT blockin (vdr, opti.seiten nummer, distanz, nutzlaenge) THEN quittieren (-wiederhole); LEAVE packeteingang FI; sequenz zaehlung; IF neue seite kommt THEN vx.seiten nummer := vorspann.seiten nummer FI. datenpacket: IF sendung wartet auf zustellung THEN auffrischen ELSE daten holen FI. sendung wartet auf zustellung: typ (strom) = zustellung. auffrischen: zeit (strom) := 100; daten aus puffer entfernen. daten holen: IF opti.sequenz >= vorspann.sequenz AND opti.sequenz < vorspann.sequenz+100 THEN IF opti.sequenz <> vorspann.sequenz THEN empfangsreport ("Sequenzreset von "+text(opti.sequenz)+" auf "+ text (vorspann.sequenz)); vx.sequenz := vorspann.sequenz; vorabquittung regenerieren FI; quittieren(ok); daten ; abschluss testen ELSE empfangsreport ("Sequenzfehler: soll "+text(vx.sequenz)+" ist "+ text(vorspann.sequenz)); quittieren (-wiederhole); daten aus puffer entfernen FI. vorabquittung regenerieren: quittieren (ok). distanz: (opti.sequenz AND packete pro seite minus 1 ) * nutzlaenge. sequenz zaehlung: vx.sequenz INCR 1. neue seite kommt: (vx.sequenz AND packete pro seite minus1) = 0. freie verbindungsnummer: INT VAR h strom :=0; FOR strom FROM 1 UPTO maxstrom REP IF vx.strom = 0 THEN h strom := strom ELIF bekannter strom THEN empfangsreport ("Reopen"); quit zaehler INCR 1; forget (vdr); LEAVE freie verbindungsnummer ELIF antwort auf call THEN typ (strom) := call pingpong; forget (vdr); LEAVE freie verbindungsnummer FI PER; strom := h strom; IF strom = 0 THEN error stop ("Zuviele simulatane Verbindungen") FI; typ(strom) := send wait. antwort auf call: openblock.sendecode >= 0 AND call aufruf AND vx.quelle = openblock.ziel AND vx.ziel = openblock.quelle. abschluss testen: IF neue seite kommt AND vx.seiten nummer = -1 THEN quittieren (-beende); an ziel weitergeben FI. an ziel weitergeben: IF tasknummerfrage THEN taskfrage beantworten ;pufferplatz freigeben; ELIF tasknamenfrage THEN name senden ;pufferplatz freigeben; ELIF taskinfofrage THEN task info senden;pufferplatz freigeben; ELSE senden FI. pufferplatz freigeben: quitzaehler INCR 1. senden: max 100 versuche; snr := strom; IF NOT callaufruf THEN typ (strom) := zustellung FI; q := vx.quelle; z := vx.ziel; ant := vx.sendecode; dr := vdr; LEAVE packet eingang. tasknummerfrage:opti.sendecode = -6. tasknamenfrage: opti.sendecode = -7. taskinfofrage: opti.sendecode = -8. max 100 versuche: zeit(strom) := 100. taskfrage beantworten: BOUND TEXT VAR tsk := vdr; TEXT VAR save tsk := tsk; forget (vdr); vdr := nilspace; BOUND TASK VAR task id := vdr; disable stop; task id := task(save tsk); IF is error THEN clear error; enable stop; forget (vdr); vdr := nilspace; BOUND TEXT VAR errtxt := vdr; errtxt := text(station(myself))+"/"""+save tsk+""" gibt es nicht"; sendung starten (collector, opti.quelle, 2) ELSE enable stop; sendung starten (collector, opti.quelle, 0) FI. name senden: forget (vdr); vdr := nilspace; tsk := vdr; disable stop; tsk := nam (opti.ziel); clear error; enable stop; sendung starten (collector, opti.quelle, 0). task info senden: BOUND INT VAR ti code := vdr; INT VAR ti cd := ti code; forget (vdr); vdr := nilspace; FILE VAR task inf := sequential file (output,vdr); head line (task inf,"Station "+text(own)); task info (ti cd, task inf); sendung starten (collector,opti.quelle,0). END PROC packet eingang; PROC quittieren(INT CONST code) : quellrechner wird zielrechner; ackpacket.code := code; ackpacket.head := stx quit; ackpacket.strom := vx.strom; blockout (workspace,1,dr verwaltungslaenge, ack laenge). quellrechner wird zielrechner: ack packet.rechnernummern := vx.rechnernummern DIV 256 + own256. END PROC quittieren; END PACKET basic net;