'' 
'' COPYRIGHT (C) 1987, CACI, INC. - FEDERAL, LA JOLLA
''
''
'' Model: port      
''
'' The rest of the routines for the port model
''-----------------------------------------------------------------------

routine SNAP.R

define ptr as a pointer variable

list attributes of ship

read ptr as /, c 8
list time.a(ptr)


read as /
end

''--------------------------------------------------------------------

Main

   ''Define DEVPTR as a pointer variable


   ''Call DEVINIT.R giving "VT,GRAPHICS" yielding DEVPTR
   ''Open 7 for input, device is DEVPTR
   ''Open 8 for output, device is DEVPTR, recordsize is 1024
   ''Use 8 for graphic output

   Call MENU
   Call READ.DATA
   Call INITIALIZE
   Call INITIALIZE.GRAPHICS

   Start simulation


End ''Main

''--------------------------------------------------------------------

Routine CLOCK Given TIME Yielding NEWTIME

  '' Display simulated time in DAY:HOUR:MINUTE format


  Define FIRST.TIME as a saved integer variable
  Define NEWTIME as a double variable
  Define ODAY as a saved integer variable
  Define OHOUR as a saved integer variable
  Define OMINUTE as a saved integer variable
  Define TIME as a double variable


  If TIME.COMPRESS.FLAG eq 1
    Call TIME.COMPRESSION giving TIME yielding NEWTIME
  Else
    Let NEWTIME = TIME
  Endif

  If FIRST.TIME lt 1
    Let ODAY = -1
    Let OHOUR = -1
    Let OMINUTE = -1
    Let FIRST.TIME = 1
  Endif

  Let DAY.A(DAYS) = NDAY.F(NEWTIME)
  If DAY.A(DAYS) ne ODAY
    Let ODAY = DAY.A(DAYS)
    Display DAYS
  Endif

  Let HOUR.A(HOURS) = HOUR.F(NEWTIME)
  If HOUR.A(HOURS) ne OHOUR
    Let OHOUR = HOUR.A(HOURS)
    Display HOURS
  Endif

  Let MINUTE.A(MINUTES) = MINUTE.F(NEWTIME)
  If MINUTE.A(MINUTES) ne OMINUTE
    Let OMINUTE = MINUTE.A(MINUTES)
    Display MINUTES
  Endif


End ''CLOCK

''--------------------------------------------------------------------

Process FINAL.REPORT


  If OUTPUT.FLAG ne 0
    Open 10 for output, name is "output.dat"
    Use 10 for output

    Print 3 lines with NO.NEW.SHIPS thus
            Port Tanker Study

         Number of new ships is  *
    Skip 2 lines
    Print 13 lines with TIME.V, NO.OF.SHIPS.SERVED,
     AVG.IN.PORT.TIME, MAX.IN.PORT.TIME,
     DOCK.UTILIZATION/3 * 100, AVG.DOCK.QUEUE(1), MAX.DOCK.QUEUE(1),
     TUG.UTILIZATION * 100, AVG.TUG.QUEUE and MAX.TUG.QUEUE thus
             After   *.** days

   * ships have been loaded.
The average in-port time for a ship was   *.** hours.
The maximum in-port time for a ship was   *.** hours.

The dock utilization was   *.** %.
The average queue for the dock was   *.** ships.
The maximum queue for the dock was      * ships.

The tug utilization was   *.** %.
The average queue for the tug was   *.** ships.
The maximum queue for the tug was      * ships.

    Close 10
  Endif


End ''REPORT

''--------------------------------------------------------------------

Process GENERATOR

  Define I as an integer variable


  For I = 1 to NO.NEW.SHIPS do
    Activate a SHIP in 2 * I days
    Let SHIP.TYPE(SHIP) = "NEW"
  Loop

  Until .END.OF.SIMULATION do
    Activate a SHIP now
    Let SHIP.TYPE(SHIP) = "OLD"
    Wait uniform.f(4.0,18.0, 2) hours
  Loop


End ''GENERATOR

''--------------------------------------------------------------------

Routine HARBOR.UPDATE

  '' Display the SHIPs in the HARBOR

  Define DONE as an integer variable
  Define I as an integer variable
  Define WAITING.SHIP as an integer variable


  Let DONE = 0
  Until DONE eq 1 do

    Let DONE = 1
    Let I = 0

    For each WAITING.SHIP in the HARBOR until I gt 4 do
      If LOCATION.Y(WAITING.SHIP) ne I * 200
        Let LOCATION.A(WAITING.SHIP) = LOCATION.F(LOCATION.X(WAITING.SHIP)+300,
         LOCATION.Y(WAITING.SHIP)-200)
        Let DONE = 0
      Endif
      Add 1 to I
    Loop

  Loop
  Display SHIP.COUNTER


End ''HARBOR.UPDATE

''--------------------------------------------------------------------

Routine INITIALIZE.GRAPHICS

  Define I as an integer variable


  Let VXFORM.V = 2   ''for text display
  Call SETWORLD.R(1.0, 80.0, 1.0, 25.0)
  Let VXFORM.V = 1   ''for graphics display
  Call SETWORLD.R(-1000.0, 7000.0, -1500.0, 1500.0)

  Call READ.TIME.SCALE
  Let TIMESYNC.V = 'CLOCK'

  For I = 1 to NO.OF.BERTHS do
    Create a BERTH
    File this BERTH in the BACKGROUND.SET
  Loop
  Create a SHORELINE
  Create a SHIP.COUNTER
  Create a DAYS
  Create an HOURS
  Create a MINUTES

  Call INITIALIZE.ICONS

  Display SHORELINE
  For each BERTH in BACKGROUND.SET
    Display BERTH
  Display SHIP.COUNTER
  Display DAYS
  Display HOURS
  Display MINUTES


End ''INITIALIZE.GRAPHICS

''--------------------------------------------------------------------

Routine INITIALIZE.ICONS

  Define X.COORDINATE as a real variable
  Define Y.COORDINATE as a real variable


  Open 1 for input, name is "ICON.DAT"
  Use 1 for input

  ''Initialize Shoreline icon
  Show SHORELINE with "shorelin.icn"

  ''Initialize Berth icons
  For each BERTH in the BACKGROUND.SET do
    .SKIP.TEXT
    Read X.COORDINATE and Y.COORDINATE
    Let LOCATION.A(BERTH) = LOCATION.F(X.COORDINATE, Y.COORDINATE)
  Loop
  For each BERTH in the BACKGROUND.SET
    Show BERTH with "berth.icn"

  ''Locate clock
  .SKIP.TEXT
  Read X.COORDINATE and Y.COORDINATE
  Let LOCATION.A(DAYS) = LOCATION.F(X.COORDINATE, Y.COORDINATE)
  Let LOCATION.A(HOURS) = LOCATION.F(X.COORDINATE+5, Y.COORDINATE)
  Let LOCATION.A(MINUTES) = LOCATION.F(X.COORDINATE+8, Y.COORDINATE)
  Let DRTN.A(DAYS) = 'V.DAYS'
  Let DRTN.A(HOURS) = 'V.HOURS'
  Let DRTN.A(MINUTES) = 'V.MINUTES'

  ''Locate Queue ship counter
  .SKIP.TEXT
  Read X.COORDINATE and Y.COORDINATE
  Let LOCATION.A(SHIP.COUNTER) = LOCATION.F(X.COORDINATE, Y.COORDINATE)
  Let DRTN.A(SHIP.COUNTER) = 'V.SHIP.COUNTER'

  Close 1


End ''INITIALIZE.ICONS

''--------------------------------------------------------------------

Routine INITIALIZE

  Create every DOCK(1)
  Let U.DOCK(1) = NO.OF.BERTHS

  Activate a TUG now
  Activate a GENERATOR now
  Activate a FINAL.REPORT in RUN.LENGTH days


End ''INITIALIZE

''--------------------------------------------------------------------

Routine MENU

   Define FIELD.ID as a text variable
   Define FORM as a pointer variable

   Let FIRSTIME = 1     
   Let NO.NEW.SHIPS = 5
   Let TIMESCALE = -100
   Let RUN.LENGTH = 5

   Show FORM with "port.frm"
   Let DDVAL.A(DFIELD.F("NUMBER NEW SHIPS",FORM)) = NO.NEW.SHIPS
   Let DDVAL.A(DFIELD.F("TIMESCALE",FORM)) = TIMESCALE
   Let DDVAL.A(DFIELD.F("RUN LENGTH",FORM)) = RUN.LENGTH
   Let FIELD.ID = ACCEPT.F(FORM,0)

   If FIELD.ID eq "EXIT"
      Stop
   Endif

   Let NO.NEW.SHIPS = DDVAL.A(DFIELD.F("NUMBER NEW SHIPS",FORM))
   Let TIMESCALE = DDVAL.A(DFIELD.F("TIMESCALE",FORM))
   Let RUN.LENGTH = DDVAL.A(DFIELD.F("RUN LENGTH",FORM))
   Let OUTPUT.FLAG = DDVAL.A(DFIELD.F("OUTPUT",FORM))


End ''MENU

''--------------------------------------------------------------------

Routine READ.DATA

  Open 1 for input, name is "MODEL.DAT"
  Use 1 for input

  .SKIP.TEXT
  Read NO.OF.BERTHS

  .SKIP.TEXT
  Read LOADING.TIME

  Close 1


End ''READ.DATA

''--------------------------------------------------------------------

Routine READ.TIME.SCALE

  Define I as an integer variable


  Let I = TIMESCALE

  If I lt 0
    Let TIME.COMPRESS.FLAG = 1
  Else
    Let TIME.COMPRESS.FLAG = 0
  Endif

  Let TIMESCALE.V = ABS.F(I) * 100


End ''READ.TIME.SCALE

''--------------------------------------------------------------------

Process SHIP

  Define ARRIVAL.TIME as a real variable
  Define BERTH as a pointer variable
  Define LOAD.TIME as a real variable
  Define .SELF to mean PROCESS.V

  Let VXFORM.V = 1
  Show .SELF with "ship.icn"
  Let DRTN.A(.SELF) = 'V.SHIP'

  If SHIP.TYPE(.SELF) eq "OLD"
    Let LOAD.TIME = LOADING.TIME
  Else
    Let LOAD.TIME = exponential.f(21.0,4)
  Endif

  Until .END.OF.SIMULATION do
    Let ARRIVAL.TIME = TIME.V
    Let SHIP.LOCATION(.SELF) = .IN.HARBOR

    If SHIP.TYPE(.SELF) eq "NEW"
      Let SHIP.COLOR(.SELF) = .red
    Else
      Let SHIP.COLOR(.SELF) = .white
    Endif

    Let LOCATION.A(.SELF) = LOCATION.F(-1500, 1000)
    File .SELF in the HARBOR
    Call HARBOR.UPDATE

    Request 1 DOCK(1)

    For each BERTH in BACKGROUND.SET with BERTH.STATUS eq .IDLE
      Find SHIP.BERTH(.SELF) = the first BERTH
    Let BERTH.STATUS(SHIP.BERTH(.SELF)) = .BUSY
    Let SHIP.COLOR(.SELF) = .yellow
    Display .SELF

    File .SELF in the TUG.QUEUE
    If TUG.STATUS(TUG) eq .IDLE
      Let TUG.STATUS(TUG) = .BUSY
      Reactivate the TUG now
    Endif

    Suspend   ''Awaiting TUG to pull SHIP into the berth

    Let SHIP.LOCATION(.SELF) = .AT.DOCK
    Add 1 to DOCKS.IN.USE
    Work LOAD.TIME hours
    Subtract 1 from DOCKS.IN.USE
    File .SELF in the TUG.QUEUE
    If TUG.STATUS(TUG) eq .IDLE
      Let TUG.STATUS(TUG) = .BUSY
      Reactivate the TUG now
    Endif

    Let SHIP.COLOR(.SELF) = .yellow
    Display .SELF

    Suspend   ''Awaiting TUG to pull SHIP out of the berth

    Let LOCATION.A(.SELF) = LOCATION.F(-1000, -400)
    Let BERTH.STATUS(SHIP.BERTH(.SELF)) = .IDLE

    Relinquish 1 DOCK(1)
    Let IN.PORT.TIME = (TIME.V - ARRIVAL.TIME) * HOURS.V

    If SHIP.TYPE(.SELF) eq "OLD"
      Leave   ''the loop
    Endif
    Let LOCATION.A(.SELF) = 0
    Wait normal.f(10.0,1.0, 6) days
  Loop


End ''SHIP

''--------------------------------------------------------------------

Routine TIME.COMPRESSION Given TIME Yielding NEWTIME

  Define BEEP as an alpha variable
  Define I as an integer variable
  Define NEWTIME as a double variable
  Define TIME as a double variable


  If VELOCITY.A(TUG) eq 0
    For I = 1 to events.v with f.ev.s(I) ne 0
      Compute NEWTIME as the minimum of time.a(f.ev.s(I))
    If NEWTIME lt 0
      Let NEWTIME = TIME.V
    Endif

    If NEWTIME ne TIME
      Let I = 7
      Store I in BEEP
      BEEP = BEEP 		''-- shut up warnings
      ''smb Write BEEP as a 1, + using 6
    Endif

  Else
    Let NEWTIME = TIME
  Endif


End ''TIME.COMPRESSION

''--------------------------------------------------------------------

Process TUG

  Define NEWX and NEWY as integer variables
  Define SHIP as integer variable
  Define .OPPOSITE to mean 3-TUG.LOCATION(.SELF)
  Define .SELF to mean PROCESS.V


  Let VXFORM.V = 1
  Show .SELF with "tug.icn"   
  Let DRTN.A(.SELF) = 'V.TUG'

  Let MOTION.A(.SELF) = 'LINEAR.R'
  Let SEGPTY.A(.SELF) = 1
  Let TUG.COLOR(.SELF) = .yellow
  Let LOCATION.A(.SELF) = LOCATION.F(0, 0)
  Let TUG.LOCATION(.SELF) = .IN.HARBOR

  Until .END.OF.SIMULATION do
    Until TUG.QUEUE is empty do
      Let TUG.STATUS(.SELF) = .BUSY
      For each SHIP in the TUG.QUEUE
       with SHIP.LOCATION(SHIP) eq TUG.LOCATION(.SELF)
        Find the first case
      If found
        Remove this SHIP from the TUG.QUEUE
      Else
        Remove the first SHIP from the TUG.QUEUE
        If TUG.LOCATION(.SELF) eq .AT.DOCK
          Let NEWX = 0
          Let NEWY = 0
        Else
          Let NEWX = location.x(SHIP.BERTH(SHIP))
          Let NEWY = location.y(SHIP.BERTH(SHIP))
        Endif

        Call WAIT.AND.MOVE giving 0.5/HOURS.V, .SELF, NEWX, NEWY
        Let TUG.LOCATION(.SELF) = .OPPOSITE
      Endif
      Let LOCATION.A(.SELF) = LOCATION.F(LOCATION.X(SHIP),LOCATION.Y(SHIP))
      If SHIP.TYPE(SHIP) eq "NEW"
        Let SHIP.COLOR(SHIP) = .red
      Else
        Let SHIP.COLOR(SHIP) = .lblue
      Endif

      If TUG.LOCATION(.SELF) eq .IN.HARBOR
        Remove this SHIP from the HARBOR
        Let NEWX = location.x(SHIP.BERTH(SHIP))
        Let NEWY = location.y(SHIP.BERTH(SHIP))
      Else
        Let NEWX = 0
        Let NEWY = -400
      Endif

      Let LOCATION.A(SHIP) = 0
      Let TUG.SHIP.IN.TOW(.SELF) = SHIP
      Call WAIT.AND.MOVE giving 1.0 / HOURS.V, .SELF, NEWX, NEWY
      Let TUG.LOCATION(.SELF) = .OPPOSITE           '' moved..
      If TUG.LOCATION(.SELF) eq .AT.DOCK
        Call HARBOR.UPDATE
      Endif
      Let TUG.SHIP.IN.TOW(.SELF) = 0
      Display .SELF

      If SHIP.TYPE(SHIP) eq "NEW"
        Let SHIP.COLOR(SHIP) = .red
      Else
        Let SHIP.COLOR(SHIP) = .lblue
      Endif
      Let LOCATION.A(SHIP) = LOCATION.F(NEWX, NEWY)

      Reactivate this SHIP now

    Loop
    Let TUG.STATUS(.SELF) = .IDLE
    Suspend   ''Awaiting more SHIPs to serve

  Loop
  Suspend   ''Awaiting end of simulation


End ''TUG

''--------------------------------------------------------------------

Display routine DAYS Given DAYS

  Define DAYS as a pointer variable
  Define STRING as a text variable


  Let vxform.v = 2
  Let STRING = itot.f(DAY.A(DAYS))
  Until length.f(STRING) eq 4
    Let STRING = concat.f(" ", STRING)
  Let STRING = concat.f(STRING, ":")
  Call TEXTCOLOR.R(.white)
  Call TEXTSIZE.R giving 586
  Call WGTEXT.R(STRING, 0, 0)


End ''V.DAYS

''--------------------------------------------------------------------

Display routine HOURS Given HOURS

  Define HOURS as a pointer variable
  Define STRING as a text variable


  Let vxform.v = 2
  Let STRING = CONCAT.F (ITOT.F (HOUR.A(HOURS)), ":")
  If HOUR.A(HOURS) lt 10
    Let STRING = CONCAT.F ("0", STRING)
  Endif
  Call TEXTCOLOR.R(.white)
  Call TEXTSIZE.R giving 586
  Call WGTEXT.R(STRING, 0, 0)


End ''V.HOURS

''--------------------------------------------------------------------

Display routine MINUTES Given MINUTES

  Define MINUTES as a pointer variable
  Define STRING as a text variable


  Let vxform.v = 2
  Let STRING = CONCAT.F( ITOT.F (MINUTE.A(MINUTES)), " ")
  If MINUTE.A(MINUTES) lt 10
    Let STRING = CONCAT.F ("0", STRING)
  Endif
  Call TEXTCOLOR.R(.white)
  Call TEXTSIZE.R giving 586
  Call WGTEXT.R(STRING, 0, 0)


End ''V.MINUTES

''--------------------------------------------------------------------

Display routine SHIP Given SHIP

  Define SHIP as a pointer variable


  Let VXFORM.V = 1
  Let DCOLOR.A(ICON.A(SHIP)) = SHIP.COLOR(SHIP)
  Display ICON.A(SHIP)


End ''V.SHIP

''--------------------------------------------------------------------

Display routine SHIP.COUNTER Given SHIP.COUNTER

  Define SHIP.COUNTER as a pointer variable
  Define STRING as a text variable


  Let VXFORM.V = 2
  Let STRING = CONCAT.F (CONCAT.F (" ", ITOT.F (N.HARBOR)), " ")
  Call TEXTCOLOR.R(.white)
  Call TEXTSIZE.R giving 586
  Call WGTEXT.R(STRING, 0, 0)


End ''V.SHIP.COUNTER

''--------------------------------------------------------------------

Display routine TUG Given TUG

   Define TUG as a pointer variable


   Let VXFORM.V = 1
   Let DCOLOR.A(ICON.A(TUG)) = TUG.COLOR(TUG)
   Display ICON.A(TUG)

   If TUG.SHIP.IN.TOW(TUG) ne 0
     Let DCOLOR.A(ICON.A(TUG.SHIP.IN.TOW(TUG))) =
      SHIP.COLOR(TUG.SHIP.IN.TOW(TUG))
     Display ICON.A(TUG.SHIP.IN.TOW(TUG))
   Endif


End ''V.TUG

''--------------------------------------------------------------------

Routine WAIT.AND.MOVE Given DT, ENTITY, X, and Y

  Define DIRECTION as a real variable
  Define DISTANCE as a real variable
  Define DT as a double variable
  Define DX as an integer variable
  Define DY as an integer variable
  Define ENTITY as an integer variable
  Define X as an integer variable
  Define Y as an integer variable


  Let DX = X - LOCATION.X(ENTITY)
  Let DY = Y - LOCATION.Y(ENTITY)
  Let DISTANCE = SQRT.F (DX ** 2  +  DY ** 2)
  If DISTANCE gt 0.001
    If ABS.F(DX) lt .002   '' vertical
      If DY lt 0.
        Let DIRECTION = - PI.C / 2
      Else
        Let DIRECTION = PI.C / 2
      Endif
    Else
      Let DIRECTION = ARCCOS.F( ABS.F( DX / DISTANCE ) )
      If DX lt 0.
        Let DIRECTION = PI.C - DIRECTION
      Endif
      If DY lt 0.
        Let DIRECTION = - DIRECTION
      Endif
    Endif
  Else   '' zero distance, zero direction
    Let DIRECTION = 0.
  Endif
  Let VELOCITY.A(ENTITY) = VELOCITY.F( DISTANCE / DT, DIRECTION)
  Wait DT units
  Let VELOCITY.A(ENTITY) = 0
  Let LOCATION.A(ENTITY) = LOCATION.F(X, Y)


End ''WAIT.AND.MOVE
