Homework 7 Introduction to Ada I Continuation of tasking 586 Problem: You should be working on Homework 6 and starting Homework 7. Only turn in one listing for the combined homework 6 and 7. (This used to be a three lecture assignment. You are good and can do it in two assignments. Thus HW8 below is correct for your homework 7.) If possible, have program on network or bring a disk. Real time demonstrations of your program running are a desired part of the assignment. Have the completed program ready so you just type $ RUN whatever or C:\> whatever Reading: Finish Barnes Chapter 18 or ISO 8652:1995 9. Notes: Included here are files form ECLUS::DISK2:[ADA.HANDOUT]HW8* They are a more complete starter set if you are having trouble or running out of time. You will learn more by developing from the HW6* starting point. file: HW8.ADA an example main procedure -- This just makes T's P's and M's travel around on screen -- -- Attached is HW8_SCREEN_IO, a package version of previous handout -- -- This is optional information for HW6 and HW7 -- with HW8PKG ; use HW8PKG ; with HW8_SCREEN_IO ; use HW8_SCREEN_IO ; procedure HW8 is -- a possible main procedure begin CLEAR_SCREEN ; -- just start all 3 TARGET.ZOOM ; PLANE.FLY ; MISSILE.ZIP ; -- for homework, start from PLANE -- use shared plot with a space at 0.0, 0.0 in a loop if needed. end HW8 ; -- it is implementation dependent if this terminates before TARGET file: HW8PKG.ADA package HW8PKG is T : FLOAT := 0.0 ; -- global clock, TARGET does updates task TARGET is -- TASK SPECIFICATION FOR TARGET entry ZOOM ; end TARGET ; task PLANE is -- TASK SPECIFICATION FOR PLANE entry FLY ; end PLANE ; task MISSILE is -- TASK SPECIFICATION FOR MISSILE entry ZIP ; end MISSILE ; task SHARED is -- SHARED TASK SPECIFICATION entry REPORT (WHO:INTEGER ; X,Y,VX,VY:FLOAT) ; entry READ_OUT (WHO:INTEGER ; X,Y,VX,VY: out FLOAT) ; entry PLOT (WHO:STRING ; X,Y:FLOAT) ; end SHARED ; end HW8PKG ; with FLOAT_MATH_LIB ; use FLOAT_MATH_LIB ; with TEXT_IO ; use TEXT_IO ; with HW8_SCREEN_IO ; use HW8_SCREEN_IO ; package body HW8PKG is task body TARGET is TGT_X : FLOAT := 2.0 ; TGT_Y : FLOAT := 1.0 ; TGT_XOLD : FLOAT ; TGT_YOLD : FLOAT ; begin accept ZOOM ; loop T := T + 0.03 ; -- change to get reasonable speed TGT_XOLD := TGT_X ; TGT_YOLD := TGT_Y ; TGT_X := COS(3.0*T) + 1.0 ; TGT_Y := SIN(5.0*T) + 1.0 ; SHARED.PLOT (" ",TGT_XOLD,TGT_YOLD) ; SHARED.PLOT ("T",TGT_X,TGT_Y) ; exit when T > 12.0 ; -- several times around screen end loop ; abort PLANE ; -- optional here, could also be in main procedure abort MISSILE ; abort SHARED ; end TARGET ; task body PLANE is -- TASK BODY FOR PLANE PLANE_X : FLOAT := 1.1 ; PLANE_Y : FLOAT := 0.0 ; PLANE_XOLD : FLOAT ; PLANE_YOLD : FLOAT ; begin accept FLY ; loop PLANE_XOLD := PLANE_X ; PLANE_YOLD := PLANE_Y ; PLANE_X := COS(1.0*T) + 1.0 ; -- replace with tracking target PLANE_Y := SIN(1.0*T) + 1.0 ; SHARED.PLOT (" ",PLANE_XOLD,PLANE_YOLD) ; SHARED.PLOT ("P",PLANE_X,PLANE_Y) ; end loop ; end PLANE ; task body MISSILE is -- TASK BODY FOR MISSILE MIS_X : FLOAT ; MIS_Y : FLOAT ; begin accept ZIP ; loop MIS_X := 0.5*COS(2.0*T) + 1.0 ; -- replace with launch MIS_Y := 0.5*SIN(2.0*T) + 1.0 ; -- and tracking target SHARED.PLOT ("M",MIS_X,MIS_Y) ; end loop ; end MISSILE ; task body SHARED is -- SHARED TASK BODY begin loop select accept REPORT (WHO:INTEGER ; X,Y,VX,VY:FLOAT) do null ; -- fill in for homework end ; or accept READ_OUT (WHO:INTEGER ; X,Y,VX,VY: out FLOAT) do null ; -- file in for homework end ; or accept PLOT (WHO:STRING ; X,Y:FLOAT) do declare I : INTEGER := INTEGER ( Y * 12.0 + 0.5 ) ; J : INTEGER := INTEGER ( X * 39.0 + 0.5 ) ; begin PUT_CHAR(I,J,WHO ); end ; end ; or terminate ; end select ; end loop ; end SHARED ; end HW8PKG ; file: HW8_SCREEN_IO.ADA package HW8_SCREEN_IO is procedure PUT_CHAR ( LINE : in INTEGER ; COLUMN : in INTEGER ; PLOT_STR : in STRING ) ; procedure PUT_BOLD ( LINE : in INTEGER ; COLUMN : in INTEGER ; PLOT_STR : in STRING ) ; procedure CLEAR_SCREEN ; end HW8_SCREEN_IO ; with TEXT_IO ; use TEXT_IO ; package body HW8_SCREEN_IO is package INT_IO is new INTEGER_IO ( INTEGER ) ; use INT_IO ; procedure PUT_CHAR ( LINE : in INTEGER ; COLUMN : in INTEGER ; PLOT_STR : in STRING ) is L , C : INTEGER ; LCH , CCH : INTEGER := 1 ; begin -- Validate the value passed for the line number - between 1 and 23 -- 23 is needed because when program terminates it scrolls screen if -- at bottom as is the case here. -- Don't raise, a fuss - just clip at top and bottom L := LINE ; if L < 1 then L := 1 ; elsif L > 23 then L := 23 ; end if ; -- Validate the value passed for the column number - between 0 and 79 -- Don't raise a fuss, just clip at each edge C := COLUMN ; if C < 1 then C := 1 ; elsif C + PLOT_STR'LENGTH > 80 then C := 80 - PLOT_STR'LENGTH ; end if ; -- Now output the whole string, prefixed with [ and -- terminated with "f" (only OK to use separate PUT's if protected) PUT ( ASCII.ESC & "[" ) ; PUT ( L , 0 ) ; -- causes no leading space PUT ( ";" ) ; PUT ( C , 0 ) ; PUT ( "f" & PLOT_STR ) ; end PUT_CHAR ; procedure PUT_BOLD ( LINE : in INTEGER ; COLUMN : in INTEGER ; PLOT_STR : in STRING ) is begin PUT_CHAR( LINE, COLUMN, ASCII.ESC & "[1m" & PLOT_STR & ASCII.ESC & "[0m" ) ; end PUT_BOLD ; procedure CLEAR_SCREEN is begin PUT ( ASCII.ESC & "[2J" ) ; -- Clear the screen and home cursor end CLEAR_SCREEN ; end HW8_SCREEN_IO ; Control of tasks can be very complicated. The language provides several structures for various cases. The skeletons below may be used inside the task body after the task begin. For simplicity the "entry" and "accept" are shown without the optional parameters. package XXX is task YYY is -- or task type YYY is entry ZZZ ... ; end YYY ; end XXX ; package body XXX is task body YYY is -- optional declarations begin *** STRUCTURES BELOW GO HERE *** end YYY ; end XXX ; The user of a task then writes: with XXX; ... XXX.YYY.ZZZ ...; -- the user waits here til ZZZ rendezvous 1. A simple accept, one shot only then task terminates accept ZZZ do accept ZZZ ; -- statements_1 -- statements_2 end ZZZ ; -- statements_2 (This would be executed by XXX.YYY.ZZZ;) 2. A simple accept, can be used many times, exists until sponsor dies loop accept ZZZ do -- statements end ZZZ ; -- statements end loop ; (This would be executed by XXX.YYY.ZZZ; other statements; XXX.YYY.ZZZ;) 3. A pair of entries that must go 1,2,1,2,1,2... loop accept ZZ1 do -- statements end ZZ1 ; -- statements accept ZZ2 do -- statements end ZZ2 ; -- statements end loop ; (The above would be executed by: XXX.YYY.ZZ1; other statements; XXX.YYY.ZZ2; other statements; XXX.YYY.ZZ2; -- a) but this has to wait XXX.YYY.ZZ2; -- b) but this has to wait XXX.YYY.ZZ1; -- now a) can run XXX.YYY.ZZ1; -- now b) can run ...) 4. A pair of entries that can be used in any order, may live forever loop select accept ZZ1 do -- statements end ZZ1 ; -- statements or accept ZZ2 do -- statements end ZZ2 ; -- statements end select ; end loop ; ( XXX.YYY.ZZ1; XXX.YYY.ZZ1; -- OK in this case, will execute) 5. A pair of entries that can be used in any order, the task XXX terminates when there is no possible callers for ZZ1 or ZZ2. loop select accept ZZ1 do -- statements end ZZ1 ; -- statements or accept ZZ2 do -- statements end ZZ2 ; -- statements or terminate ; end select ; end loop ; 6. A pair of entries that can be used in any order, each time through the loop, if neither ZZ1 nor ZZ2 has a caller waiting the statements in the "else" part are executed. Watch out! You have no control over the loop timing or the context switching algorithm. loop select accept ZZ1 do -- statements end ZZ1 ; -- statements or accept ZZ2 do -- statements end ZZ2 ; -- statements else -- statements end select ; end loop ; 7. A pair of entries that can be used in any order, If there are no callers for ZZ1 or ZZ2 control goes to some other task. After a delay of at least T seconds, this task comes around the loop again. loop select accept ZZ1 do -- statements end ZZ1 ; -- statements or accept ZZ2 do -- statements end ZZ2 ; -- statements or delay T ; end select ; end loop ; 8. Guards may be used inside the "select" structure on the "accept", "terminate" and "delay", but not on the "else". Just one of the cases from 5,6 or 7 may be used in any given structure. Guards are of the form : when BOOLEAN_EXPRESSION => If the BOOLEAN_EXPRESSION is true the normal action is taken. If false it is as though the structure following the guard did not exists. The three cases below use the "no rendezvous" form of the "accept" just for brevity. 8a select when BOOL1 => accept ZZ1 ; -- statements or when BOOL2 => accept ZZ2 ; -- statements or when BOOL3 => terminate ; end select ; 8b select when BOOL1 => accept ZZ1 ; -- statements or when BOOL2 => accept ZZ2 ; -- statements or when BOOL3 => delay T ; end select ; 8c no guard is allowed after "else" in select 9. The following constructs can be used anywhere, not just in task bodies delay number_of_seconds; delay until value_of_type_time; 9.7.2 Timed Entry Call select some_task.some_entry; -- canceled if no rendezvous in 2.5 seconds or delay 2.5; end select; 9.7.3 Conditional Entry call select some_task.some_entry; -- if no immediate rendezvous, do else part else -- EXECUTABLE STATEMENTS end select; 9.7.4 Asynchronous Transfer of Control select some_task.some_entry; -- only run if lower part does not finish -- EXECUTABLE STATEMENTS -- before rendezvous then abort -- EXECUTABLE STATEMENTS will run till rendezvous of above end select; select delay 7.2; -- EXECUTABLE STATEMENTS -- only run if lower part takes 7.2+ seconds then abort -- EXECUTABLE STATEMENTS -- killed after 7.2 seconds end select; To have a procedure PERIODIC called approximately every 1 second and desiring no overall slippage over long time intervals, the following task can be set up. Just for variety, the task is defined in a procedure rather than in a package. with CALENDAR ; use CALENDAR ; procedure MAIN is task GO_PERIOD ; -- task specification, short form task body GO_PERIOD is INTERVAL : constant DURATION := 1.0 ; -- seconds NEXT_TIME : TIME ; begin NEXT_TIME := CLOCK ; -- function call returns time now loop delay NEXT_TIME - CLOCK ; -- do this about once per second, good on average NEXT_TIME := NEXT_TIME + INTERVAL ; end loop ; end GO_PERIOD ; begin -- task started just before "do something" -- do something end MAIN ;