XML

kent logo

CO538 Anonymous Questions and Answers

This page lists the various questions and answers. To submit a question, use the anonymous questions page. You may find the keyword index and/or top-level index useful for locating past questions and answers.

We have taken the liberty of making some minor typographical corrections to some of the questions as originally put. Although most of the questions here will have been submitted anonymously, this page also serves to answer some questions of general interest to those on the course.

When asking questions, please do not simply paste in your code and ask what is wrong with it, as these types of question are hard to answer in a public forum (resulting in either no answer, or an answer which will only be of use to the person asking the question). In many cases, a question about code can be formulated in general terms. For example, if you have an `IF' block that results in an `incorrect indentation' error, you might ask, ``what is the correct syntax for an occam `if' statement ?''. Questions of this form are easier to answer, and are meaningful to all.

Questions that are not suitable for these public pages (i.e. those that contain assignment-specific code), should be mailed to your seminar-leader.


Question 41:

Submission reference: IN2072

Hey, while writing my dining philosophers' animation, I'm having an issue with my variant protocol. In the display process, I keep getting the error message: "tagname" is not declared. It's declared in the protocol, and I'm using the case keyword when reading in data. I can't seem to figure out what's wrong. thanks.

Answer 41:

You are just getting a compiler error. If you cannot see what's wrong, you have not understood how to declare and use the tags in a variant protocol correctly. We have to see your code to tell you what you are doing wrong – mail me <phw@kent.ac.uk>.

Keywords: q7


Question 42:

Submission reference: IN2074

Thanks for your response to Question 40 (2011) (I am the original question poser).

I understand the concept, however the line of your "set.my.string" example:

      [s[str] FOR s[len]] := [str FOR s[len]]

... produces the error:

      `s' cannot be used (it has been abbreviated)

Why?

Ah! I found the following workaround:

  PROC set.my.string (MY.STRING s, VAL []BYTE str)
    VAL INT str.len IS (SIZE str):
    SEQ
      IF
        str.len > MAX.STRING.LEN
	  s[len] := MAX.STRING.LEN
        TRUE
	  s[len] := str.len
      [s[str] FOR s[len]] := [str FOR str.len]
  :

Can you explain why?

Answer 42:

Sorry about this! When compiling:

      [s[str] FOR s[len]] := [str FOR s[len]]

for some reason, the compiler first transforms it into:

      VAL INT n IS s[len]:
      [s[str] FOR n] := [str FOR n]

Unfortunately, VAL-abbreviating one field of a record variable freezes the whole record, making all its fields un-assignable! (If it is going to do that, it should work a bit harder and apply the freeze rules about VAL-abbreviation just to the field that's been abbreviated. Small part of a project next year?)

Your workaround is not quite correct though. It compiles OK, but always tries to copy over all of the str parameter into the s[str] field in its last line – if the str is too long, this will crash. A correct workaround is to do almost what the compiler is doing, but to use an INITIAL-declaration (which does not freeze the variables making up the initial value) rather than a VAL-abbreviation (which does):

  PROC set.my.string (MY.STRING s, VAL []BYTE str)
    SEQ
      IF
        str.len > MAX.STRING.LEN
	  s[len] := MAX.STRING.LEN
        TRUE
	  s[len] := str.len
      INITIAL INT n IS s[len]:
      [s[str] FOR n] := [str FOR n]
  :

I agree that this is not very elegant!

Keywords: q7


Question 43:

Submission reference: IN2077

I get:

    Error-occ21-q7.occ(133)- delay is not declared

I looked at the occam documentation and it is there! What am I doing wrong?

Answer 43:

Indeed it is: delay. However, if you scroll up from this documentation, you will see that it's in the module time. To use this, you will need the line:

    #INCLUDE "time.module"

somewhere before you use delay in your code (next to the #INCLUDE line for "course.module").

We did not put the delay in the course module. It seemed too simple and you have been using it in many of your provious assignments (where its code was given in the starter files). We did not put it in the starter file for the Dining Philosophers' animation, since many solutions would use TIMERs directly.

POSTSCRIPT: apologies, I was confused by the incosistency in the names we chose! The starter files for q1..q4 have a delay process called pause. This has the same functionality as the delay process called delay in the "time.module".

Keywords: q7 , delay , timers

Referrers: Question 46 (2011)


Question 44:

Submission reference: IN2076

I'm currently trying to get me animation processes running in parallel and I'm not sure how to go about this.

I have the display process receiving inputs on a shared channel from the forks, philosophers, and security that is correctly interpreting the input. But anytime I use one of my animation processes it is done in sequence due to the display process not except another input until the animation process has finished, so everything is animated in sequence.

I've tried all that I can think of to fix this. Any advice?

Answer 44:

See Question 39 (2011). All the forks, philosophers and the security guard processes must have separate report channels (not a shared one) to separate animation processes that can all operate in parallel. The animation processes then compete with each other to send incremental movement messages over the same shared channel to a display process that handles them.

For example, a philosopher animation process might want to move its philosopher icon from one part of the screen to another by some interesting route (e.g. when its philosopher process reports it is hungry). It animates by a sequence of incremental moves (e.g. erase the icon from its current position, which it has to remember, and re-draw at slightly further on along its route). It can hold its philosopher process till this is complete by using an extended input to receive its messages and doing the animation in the extended rendezvous block (see Shared-etc slide 59). The same effect can be achieved without using the extended input syntax (see Shared-etc slide 61, where the ack channel is not needed in this case if you make the philosopher (or fork or security) send each of its messages twice).

Keywords: q7 , shared-channel , animation


Question 45:

Submission reference: IN2078

hye, ive been racking my brains for ages trying to figure out hows to set the minuum number in the randm feature, ive set the maximum, but at the minute i have an if statement that checks whether the number is big enough, and if not, gnerates another one, i can't help thinking that there is a better way of doing this thhats more efficient.

Answer 45:

PLEASE make an attempt to write English when asking these questions. Writing is an important part of the engineering process and correct use of spelling and grammar is necessary in order to be understood. English (and all natural language) is imprecise and full of ambiguities even when well written. Poor English makes things so much worse. When working for real money, poor English will not be accepted!   (End of rant.)

To choose a number randomly between min and max, you are correct to be concerned that using an IF (or some loop) is inelegant and inefficient. Instead: use the random function to choose a number randomly between 0 and (max - min) and, then, add min to it.

Keywords: random , q7

Referrers: Question 47 (2011)


Question 46:

Submission reference: IN2079

I get:

    Error-occ21-q7.occ(40)- cannot open file "time.module"

I've looked in lib and sure enough it isn't there...

Answer 46:

This looks like a response to the answer in Question 43 (2011). It looks like the search path set up for you on the Transterpreter doesn't reach all the libraries – which would be our fault.

However, that question was seeking a delay process and I'm assuming that that is what you are still seeking? The function of delay in the "time.module" is, as its documentation says, identical to the pause process given in your starter files for q1, q2, q3 and q4 (and e1). Just copy and use that – it's only these 7 lines of code:

    PROC pause (VAL INT delay)
      TIMER tim:
      INT t:
      SEQ
        tim ? t
        tim ? AFTER t PLUS delay
    :

Keywords: q7 , delay , timers


Question 47:

Submission reference: IN2080

ok the good news is that i managed to get things moving inside my prgram, ie, things move to diffrent parts of the screen, the bad news however, is that all that are moving are words rperesenting thte various itmes i have some mulitdimensional arrays set up that create stickmen like figures to use instead of the words, but the problem is, that out.string doesnt seem to acept multidimenisonal arrays, is there a way around this?

Answer 47:

You can easily write your own process to output a 2D array of characters (BYTEs) to the screen. For example, here's one (with a testrig program):

    #INCLUDE "course.module"

    VAL [][]BYTE stickman IS [" O ",
                              " | ",
                              "/ \"]:

    PROC out.icon (VAL BYTE x, y, VAL [][]BYTE icon, CHAN BYTE out!)
      SEQ
        INITIAL BYTE yy IS y:
        SEQ i = 0 FOR SIZE icon
          SEQ
            cursor.x.y (x, yy, out!)
            out.string (icon[i], 0, out!)
            yy := yy + 1                    -- move down a line
        out ! FLUSH
    :

    PROC icon (CHAN BYTE keyboard?, screen!, error!)
      SEQ
        erase.screen (screen!)
        out.icon (39, 11, stickman, screen!)
        out.string ("*c*n*n*n*n*n*n", 0, screen!)
    :

Please see opening rant in the answer to Question 45 (2011).

Keywords: q7 , animation , cursor


Question 48:

Submission reference: IN2081

If you have a CHAN TYPE e.g.

    CHAN TYPE TEST.a
      MOBILE RECORD
        CHAN TEST.a.one?:
        CHAN TEST.a.two!:
        CHAN TEST.a.three!:
    :

Is it then possible to do the following;

    PROC a ()
      [1000]TEST.a! bundle:
      SKIP
    :

instead of;

    PROC a ()
      TEST.a! b1,b2,b3,...:
      SKIP
    :

Answer 48:

There are syntax errors in your CHAN TYPE declaration. I'm assuming you are not wanting a recursive channel type (i.e. one that can carry its own channel-type-ends in some of its channel fields – although this is possible)?   A syntax-legal declaration is:

    PROTOCOL TEST.a IS INT:    -- say

    CHAN TYPE TEST
      MOBILE RECORD
        CHAN TEST.a one?:
        CHAN TEST.a two!:
        CHAN TEST.a three!:
    :

We can declare an array of channel-type-ends ... but because only MOBILE channel types are currently allowed, such an array must also be declared MOBILE.

However, there is no point declaring an array of just one end of a channel type – we need two arrays: one for each end. Further, declaring channel-type end variables (or arrays) does not construct the channel bundles. As in Java, this is done dynamically:

    VAL INT N.SERVERS IS 1000:

    PROC a ()

      MOBILE [N.SERVERS]TEST? bundle.server:                    -- elements not initialised
      MOBILE [N.SERVERS]SHARED TEST! bundle.client:             -- elements not initialised

      SEQ

        SEQ i = 0 FOR N.SERVERS
          bundle.client[i], bundle.server[i] := MOBILE TEST     -- construct a channel bundle

	-- all elements of both bundle arrays are now initialised
	-- 
	-- the number of channel bundles constructed is N.SERVERS
	-- 
	-- for each i, bundle.client[i] and bundle.server[i] hold
	-- opposite ends of the same channel bundle
	-- 
	-- for each i, any number of (client) processes may share
	-- use of bundle.client[i] (because it's SHARED)
	-- 
	-- for each i, only one (server) process may be given and
	-- use bundle.server[i] (because it's not SHARED)

        ...  etc.

    :

Now we can give each bundle.server[i] to its own server process and each bundle.client[i] to as many client processes (for the server at the other end) as we like.

Note: the client end array was declared to hold SHARED ends only because that is a common paradigm. Neither end need be shared ... or both can be shared ... or just the server side.

Note: the material on MOBILE data and channel types is not an examinable part of this course module.

Keywords: chan-type

Referrers: Question 49 (2011)


Question 49:

Submission reference: IN2082

Hi, this is a follow-up to Question 48 (2011).

Maybe I should be a little more specific. Consider the following;

    [snipped code]
    
    [n]OCCADE.SPRITE! bundle:
    SEQ
      PAR --This could be replicated.
        occade.start.sprite (occade, bundle[0], -1)
        bundle[0][req] ! load.image; "someimage.bmp"
        bundle[0][req] ! move; 20; 10; FALSE
        bundle[0][req] ! visible; TRUE
      PAR
        occade.start.sprite (occade, bundle[1], -1)
        bundle[1][req] ! load.image; "someimage.bmp"
        bundle[1][req] ! move; 20; 10; FALSE
        bundle[1][req] ! visible; TRUE
      ...

    [snipped code]

Currently I have it working in a very horrible way involving the following and a lot of not replicated stuff. (it's currently not very dynamic!);

    [snip]
    
    OCCADE.SPRITE! t0,t1,t2,t3,t4:                  -- The "Philosophers" sat down
    OCCADE.SPRITE! node0,node1,node2,node3,node4:   -- ... and eating
    OCCADE.SPRITE! f0,f1,f2,f3,f4:                  -- Forks when they are at rest
    OCCADE.SPRITE! l0,l1,l2,l3,l4:                  -- Forks when they are left forks
    OCCADE.SPRITE! r0,r1,r2,r3,r4:                  -- Forks when they are right forks
     
    [snip]

Answer 49:

The declaration of your bundle array should be:

    MOBILE [n]OCCADE.SPRITE! bundle:

The latter declarations should also be arrays (which enables us to replicate code):

    MOBILE [n.philosophers]OCCADE.SPRITE! t:      -- The "Philosophers" sat down
    MOBILE [n.philosophers]OCCADE.SPRITE! node:   -- ... and eating
    MOBILE [n.philosophers]OCCADE.SPRITE! f:      -- Forks when they are at rest
    MOBILE [n.philosophers]OCCADE.SPRITE! l:      -- Forks when they are left forks
    MOBILE [n.philosophers]OCCADE.SPRITE! r:      -- Forks when they are right forks

But ... you haven't asked a question?

Keywords: occade , chan-type

Referrers: Question 50 (2011)


Question 50:

Submission reference: IN2083

Hi, this is a follow-up to Question 49 (2011). This is the code that I've managed to get to. It compiles but then I get a runtime error in occade on line 1006 "Mobile type error":

    [Snipped]

so that later on I can call:

    [Snipped]

I am trying to condense my code into a more elegant solution by making it replicate in this way. However, I'm having issues with the array of OCCADE.SPRITE chans. To compound this, I am unable to find example code of anything that is trying to do something similar – this makes me think my approach is wrong – but I can not see anything way of achieving this functionality.

Answer 50:

Did the error message say which file in occade that line was from? Or was it from the code you quoted – in which case, which line was line 1006?

You've worked hard getting this far – and I can see you have found workarounds for at least one restriction in occade concerning file names for icon images (which must be mobile byte arrays!). Please submit what you have, with a cover note on your plan for it and what is and is not working. If you have an ASCII art version, submit that as well, again with a cover note saying you have two versions. Thanks.

Keywords: q7 , occade


Question 51:

Submission reference: IN2084

Hi, I'm currently doing Task 3 for the life on mars assessment. Whenever I run the program and attempt to enter a colour for the robot to find, the screen simply flashes and doesn't accept the input.

Answer 51:

This is a new improved user interface for selecting from a long list of options. Try pressing the TAB key, as the initial prompt suggests. You will be shown the legal colours and be prompted again. You will only be able to type letters that go towards those colour names. Press TAB at any time and either you will be shown remaining possible options (given what you have typed so far) or characters will be supplied for you (as far as possible towards an option, until there is a choice). If only one option is possible (given what you have typed so far), pressing TAB completes the word. Of course, you can type BACKSPACE at any time. Press SPACE or ENTER to complete your selection – this will be ignored if you still have an incomplete word.

This user interface is modelled on file and command name completion in the bash shell of Unix. It is implemented by the ask.select process defined in the "ask_select.occ" file and invoked by the mission.control process in "mars-sim.occ" (and supported by the small function in "alpha_numeric.occ"). The ask.select logic is fairly complex, but made amenable through the declaration of loop invariants (a general and extremely useful programming mechanism that can be used in any language).

Keywords: mars , loop-invariant


Question 52:

Submission reference: IN2086

Do we need to submit network diagrams this year if we have implemented the basic animation?

Answer 52:

Yes, 'fraid so! The question details (in the Assessment 4 box on the Moodle page) says: "You must also submit relevant network diagrams of your solution ...".

Submission directories will actually remain open until 01:05:00 Tuesday morning. The time now is 23:50:00 Monday evening ...

Keywords: q7


Question 53:

Submission reference: IN2085

Is there a way to run a one-off process in parallel with the execution of the currently running process in a "fire-and-forget" kind of way? I think my question may be clearer in code. Is there a way to do the following, as an example?:

    PROC b ()
      ...  do potentially long-winded stuff (possibly non-terminating)
    :

    PROC a ()
      WHILE TRUE
        SEQ
          IF
	    condition
	      b ()
	    TRUE
	      SKIP
          ...  do stuff
    :

Is there a way to start process b running, then in carry on doing a in parallel, while b finishes doing whatever it is that b does?

Answer 53:

Yes, there is! But we need FORKing – see Mobiles slides 102-106 and the example slides that follow.

For your example, you just need one new keyword:

    PROC b ()
      ...  do potentially long-winded stuff (possibly non-terminating)
    :

    PROC a ()
      WHILE TRUE
        SEQ
          IF
	    condition
	      FORK b ()
	    TRUE
	      SKIP
          ...  do stuff
    :

We don't have to have the FORKING block shown on slides 104-106. If we don't, the whole program is a default FORKING block – i.e. all FORKed process must terminate for the whole program to terminate. Explicit FORKING blocks control this with finer granularity.

If the FORKed process takes parameters, be aware of the different semantics when passing arguments to those parameters – they have the semantics of channel communcation (slides 105-106), rather than conventional parameter passing.

Note: a FORKed process must take parameters (e.g. shared and/or mobile channel-ends). Otherwise, there is no way anything it does can have any effect! FORKed processes cannot access globals (anything declared outside its PROC header).

Note: the material on forking is not part of the examinable material for this module.

Keywords: forking


Question 54:

Submission reference: IN2087

I have problem moving the mars robot on different position on the map accurately: distance.travelled is returned as a random value initially, even when it is initialised to 0. Making move 50mm being reported as moved -840mm or 70mm and such, and sometimes it just moves all the way without stopping until it hits the map border. The code responsible:

    INITIAL INT distance.travelled IS 0:
    motor.feedback ? clicks
    distance.travelled := distance.travelled + clicks

I also have a problem where the PRI ALT timeout guard just executes directly without even waiting for the timeout to happen.

    IF
      (degree.turned\90) = 0
        SEQ
          motor.cmd ! stop
          tim ? t
          WHILE waiting
            PRI ALT
              tim ? AFTER (t PLUS CAMERA.SCAN.TIME)
                SEQ
                  out.string ("Timeout!", 0, log!)
                  waiting := FALSE
              camera ? camera.feedback
                SEQ
                  out.string ("BLOB detected...Do Something!*n", 0, log!)
      TRUE
        waiting := TRUE

Answer 54:

Your first code fragment just waits for one message from the motor.feedback channel and adds the number received to distance.travelled. If a motor command (to drive) has not been made, no message will be sent on motor.feedback and nothing will happen. If a motor command (to drive) has been made, your code processes the first motor.feedback message and nothing else – the motor will keep going and the rover will move through all obstacles to the image boundary.

To move the rover the commanded distance, you must have more code than this ... to keep processing motor.feedback and the hazard channels until either the distance.travelled reaches (or exceeds) the commanded distance or the rover gets too close to a hazard. You have not shown such code so we cannot say why the distance.travelled you return is unrelated to the commanded distance. Check that all variables that need initialising are initialised correctly.

For your second code fragment, CAMERA.SCAN.TIME is defined to be 2000000 (i.e. 2 seconds). So, the timeout guard cannot execute until 2 seconds after reading the timer value into t just before the WHILE-loop.

Your code does not show the value of waiting before it starts. Are you sure it's TRUE? Has it been initialised? If it is FALSE, then that whole WHILE-loop will be skipped.

You say that that timeout guard executes directly ... but how do you know that ... it only gets executed every 90 degrees of turn (maybe, see below) ... do you ever see the "Timeout!" message on the log channel (which is routed to the screen)?

Also, I don't trust your (degree.turned\90) test against zero. I'm assuming your degree.turned variable is accumulating ticks from motor.feedback? But those ticks could be any number (i.e. not necessarilly 1 or 2). Note the remark in the "Starting out on Mars" website about the motor.feedback information: "You can't (and shouldn't) rely on these ticks being a fixed value anywhere in your code". So, your increasing degree.turned variable could get larger than 90 without ever equalling 90 (i.e. degree.turned\90 may never be zero).

Check the Warning messages from the compiler. Those about use of uninitialised variables ought to be upgraded to Error messages and compilation refused. Unfortunately, the compiler logic is woefully too cautious and gives too many false positives regarding uninitialised variables – which is why they are warnings only and compilation continues (and for which we apologise). But you should check those warning messages and either see that they are correct (and fix the problem) or deduce they are incorrect (and ignore).

Keywords: mars

Referrers: Question 55 (2011) , Question 55 (2011)


Question 55:

Submission reference: IN2088

Follow up of Question 54 (2011).

This is the entire block of my move command. What it does is move the motor while monitoring for hazards, and it can only exit if the move is completed or obstructed. After days of investigating the problem, I was still unable to figure out why the robot stops randomly and sometimes even "motor.cmd ! stop" does not even stop the robot, even after the "opr.resp!" block has executed.

    PROC move (...)
      ...  code skipped
    :

As for the PRI ALT timeout guard, the degree.turned\90 condition is always triggered when degree.turned reaches 90, 180, 270 and 360 degrees during testing, but I will look into it. The PRI ALT guard prints out "Timeout!" everytime without ever waiting for CAMERA.SCAN.TIME (2sec), such as below. I'm very lost at this one. Some Output:

    ...
    ?robot 0: 90
    ?robot 0: Timeout!92
    ?robot 0: 94
    ...
    ?robot 0: 180
    ?robot 0: Timeout!182
    ?robot 0: 184
    ...
    ?robot 0: 270
    ?robot 0: Timeout!272
    ?robot 0: 274
    ...
    ?robot 0: 358
    ?robot 0: 360
    ?robot 0: Timeout!blob not found; turned = 360
    Please select a command (t)urn, (m)ove, (f)ind blob, (d)eploy sensor: 

The code:

    PROC find.blob (...)
      ...  code skipped
    :

Answer 55:

[Note: although this answer is about code that is not shown, it should be worth reading by those other than the questioner since it describes misunderstandings about the problem and errors in approach that may be of help generally. Some fragments of the code under discussion are shown in Question 54 (2011).]

Your code shows a misunderstanding of the way the motors on the rover operate. In the section on motor commands and feedback in the "Starting out on Mars" website, it says:

"Motor commands continue acting until you send a stop command. When the motors are active, tickers in the motors will send values along the motor feedback channels telling you how much the robot has turned in each step."

So, you only need to send a drive.forward (or drive.backward or turn.right or turn.left) command once and the rover with start and continue driving (or turning). As the rover drives or turns, a series of click counts are sent on the motor.feedback channel from time to time, with values representing the amount driven or turned since the last message (or the original drive/turn command). These counts are usually 10 or -10 (when driving) and 2 or -2 (when turning) in the current simulator, but you cannot rely on that: they could be anything. The rover will continue to drive (or turn) – and motor feedback clicks will continue to be sent – until the motor is commanded to stop.

Your code repeatedly issues a drive (or turn) command followed by a wait to receive feedback clicks. This is wrong. The drive (or turn) command should only be sent once.

There are other problems though. In the loop in your move process (Task 2), you wait in parallel for one motor.feedback message and one hazard report (from any one of the four hazard channels). This is wrong for the following reason:

The message rates on the motor feedback and hazard channels are unrelated. For example, if the rover is in clear space all round and driving, there will be no warning messages incoming on any hazard channels. Your loop will receive the first motor feedback clicks message and will wait for a hazard message that doesn't arrive. Meanwhile, the rover will continue driving and clicks will continue to be fed back ... but your code will not be processing them ... because it's stuck waiting for a hazard message!

Eventually, the rover will get close enough to an obstacle and a hazard message (probably below your set threshold) will arrive: the first loop of your move process will now take that report and complete. In the next loop, it will receive in clicks the accumulated total of all the clicks that it missed whilst waiting for that hazard message – i.e. your distance.travelled will suddenly jump by the amount the rover travelled before the first hazard was detected. That loop iteration now has to wait for another hazard message before it loops around to discover the distance travelled (probably) exceeds that requested. That excess depends upon the distance to the first obstacle that caused a hazard warning and will be unrelated (apparently random) to the distance in the original command. I think this accounts for the behaviour you report.

The motor feedback and hazard channels should be monitored independently. The nicest way to do this is in parallel monitoring processes, each with their own loop. The simplest way, given your existing code structure, is for the loop body to have a single ALT between the motor feedback channel and all the hazard channels (done by a nested replicated ALT). Of course, the motor command to drive forward or backward should be done just the once – before the loop starts. [Your code also crashes if the distance it is asked to drive is zero and needs fixing.]

By the way, you should be aware of relevant hazards before issuing any drive command. Otherwise, you may crash into something. Look before driving!

From the software engineering principle of cohesion, your move process should just move the rover, reporting back its success and distance moved through reference data parameters. It should not have the additional responsibility of reporting that result back to mission control (which it currently does via the opr.resp channel). The latter should be done separately by whatever has invoked the move. In this way, you will be able to reuse this move code for other purposes – e.g. for Task 4.

Your find.blob process makes the same mistake of repeatedly issuing motor.cmd commands. Your (degree.turned\90) only works because the clicks value you receive is always 2, so that degree.turned does go though even numbers that include 90, 180, 270 and 360. This is unsafe since the clicks received could sometimes be 4 or 6 or 8 or 33 or 42 ... or anything.

I don't yet know why your code seems to timeout straight away (at least, within 2 clicks of turning), but its structure is wrong. Every 90 degrees of turn, your code tries to spend up to CAMERA.SCAN.TIME (2 seconds, unless you changed it!) processing camera data. While doing this, the rover is still turning! So, the bearing angles on any blobs reported will be out of date.

You must stop the turn every 90 degrees and, then, process camera data for 2 (and a bit more, to be sure) seconds. Simplest would be to invoke the process written for Task 1 (excluding any report back to mission control!) to turn the rover 90 degrees – where the turn command and stop command logic is already done. Of course, you will need to correct that Task 1 logic so that the turn command is only issued once.

Keywords: mars

Referrers: Question 47 (2012) , Question 47 (2012) , Question 49 (2012) , Question 56 (2011) , Question 58 (2011) , Question 65 (2011) , Question 67 (2011) , Question 71 (2011)


Question 56:

Submission reference: IN2089

In Question 55 (2011), you wrote:

"From the software engineering principle of cohesion, your move process should just move the rover, reporting back its success and distance moved through reference data parameters. It should not have the additional responsibility of reporting that result back to mission control (which it currently does via the opr.resp channel). The latter should be done separately by whatever has invoked the move. In this way, you will be able to reuse this move code for other purposes – e.g. for Task 4."

I'm not quite sure what you mean by "reference data parameters", please could you explain?

Answer 56:

See basics slides 37, 67 and 68. The parameter result is a reference data parameter and, in this example, is expected to be set by the code within the body of the foo process before that terminates. When foo is invoked, an INT variable must be supplied as its third argument (slide 68) – anything the foo code does with result will actually be done to the variable supplied. See also this relevant bit from the occam-pi Reference wiki (which is linked from the "Practical Resources" box on the Moodle page for this course).

In this way, occam-pi PROCs can return results (plural: there can be any number of reference parameters), as well as interact with their environments (through channel communications, barrier syncs etc.). [Aside: occam-pi FUNCTIONs also return results (plural), but they are not allowed side-effects (e.g. interacting with their environments).] For the example provoking this discussion, your move process could have a reference boolean parameter (for success) and a reference integer (for distance).

Revision: VALue data parameters cannot be changed by their PROC body (i.e. they are constants). Supplied arguments may be variables, constant literals or expressions (basics slides 37and 67).

Constant literals or expressions may not, of course, be supplied as arguments for reference parameters.

Also, the answer to Question 57 (2010) is highly relevant to this question – so, do check this as well. The question in Question 57 (2010) may be best ignored!

Final note: better than reference parameters for returning PROC results are RESULT parameters. RESULT parameters are new in occam-pi, not in any course materials and, of course, not examinable. However, they are very simple, easy to use and improve security – see Question 58 (2010) and (the end bit of) Question 60 (2010).

Keywords: mars , parameter , reference , value , result

Referrers: Question 58 (2011)


Question 57:

Submission reference: IN2090

I'm receiving a "spurious hazard.detected" warning, but unsure why? I see something similar in Question 44 (2010), but it doesn't particularly help in my case. Here's the full log:

    Commanding robot 0: move 444
    Response: ... hazard.detected; moved 320
    
    Please select a command (t)urn, (m)ove, (f)ind blob, (d)eploy sensor:
    Warning: spurious hazard.detected

Answer 57:

There's not much more to say other than the answer to Question 44 (2010). Mission control sends the Mars rover commands and, eventually, the rover replies with an appropriate report. If the rover (i.e. your robot.control logic) replies with the wrong variant of message, the system will crash. If the rover sends a message back to mission control without mission control sending it a command, mission control issues a "Warning: spurious ..." (where the "..." refers to whatever variant of message was incorrectly sent).

From the log you report, it looks like your move logic sends back a hazard.detected message twice?

Keywords: mars


Question 58:

Submission reference: IN2091

This question is related to the movement of the robot in Task 2 of the mars assessment.

After trying to solve this Task for several days and looking at other questions I am still at square one. The robot moves but stops only when a hazard is detected even though the clicks of the motor are monitored to break the loop if and when the required clicks have been met.

The following code fragment is what I have for the move process:

    PROC move.robot (...)
      ...  code omitted
    :

Where am I going wrong?

Furthermore, relating to Task 3, the following code in theory should work but does not. The robot continues to rotate regardless:

    ...  code omitted

If you could clarify my errors, I would be most appreciative.

Answer 58:

[Note: although this answer is about code that is not shown, it should be worth reading by those other than the questioner since it describes misunderstandings about the problem and errors in approach that may be of help generally.]

The first thing wrong with your move.robot is that the OR in your WHILE-condition should be an AND. This is why the loop does not exit when the required distance has been travelled.

There are other problems though. Why do you look at three hazard channels when moving ... and always the same three (indices 0, 1 and 2) whether moving forwards or backwards? Your ALT replicator is "i = 0 FOR 3" ... which runs "i" though 3 values only (0, 1 and 2). You should be listening on all four channels ... which needs the replicator to be "i = 0 FOR 4". Of course, you should only respond to danger on channels 0 and 1 when moving forwards ... and 2 and 3 when moving backwards. You always need to listen to all hazard channels in order to keep an up-to-date array of the latest warning levels (see the last paragraph of this answer).

You almost duplicate the code for moving forward and backwards: the only differences are a ">" instead of a "<" and adding 9 to the motor.feedback click count (why do you do this?!). Long sections of logic should never be repeated in programs (and will lose marks)! In this case, it can be avoided by using the IABS function mentioned in "Tips for Task 1" in the Starting out on Mars webpage (look at its coding in the file mars-robot.inc).

You report the wrong value in your hazard.detected report back to mission control ... which shouldn't be done here anyway ... see the quotation in Question 56 (2011).

What theory says that your code for Task 3 should work? The robot continues to rotate because your code for this Task has the same mistake as your Task 2 code – the OR in the WHILE-condition should be an AND.

But your Task 3 code also contains mistakes similar to those reported in the answer to Question 55 (2011), even though that answer was about Task 2 code. You repeatedly issue a turn.right command in each loop body ... when that should be done once only, before entering the loop. Inside the loop, you wait each time for motor.feedback and poll the camera channel. The message rates on these channels are unrelated ... but this code only checks the camera once for each motor.feedback. If the camera tries to report blobs faster than the motor feedback reports clicks, you will miss some of the blobs (which will be lost in overwriting buffers because your code didn't take them).

You must always be processing messages from the camera and from all hazard channels and (if the motor is running) from the motor feedback. To do this, your loop should be ALTing between the motor feedback and camera channel, not reading one in parallel with a poll on the other! You should also include the hazard channels in the ALT and be saving the values that are sent somewhere ... so that you always have up-to-date information on nearby obstacles (which is for useful when the rover is next commanded to move).

Keywords: mars

Referrers: Question 65 (2011) , Question 71 (2011)


Question 59:

Submission reference: IN2092

I have been trying to implement the hazard detection for the last few days, specifically ensuring that the values are monitored at all times. However I am struggling to think of a solution that will allow me to read the latest values of the hazard channels when necessary.

Currently I have:

    PROC hazard.monitor ([]CHAN P.HAZARD.DETECTOR hazard?, []INT hazard.value)
      WHILE TRUE
        ALT i = 0 FOR SIZE hazard
          hazard[i] ? hazard.value[i]
    :

This updates an array in the main robot.control to always contain the latest hazard values. The move process takes in this array as a reference data parameter, and attempts to read values when required.

My problem is I can't have the values being updated at the same time as they are read, as it complains they an array cannot be read and assigned to in parallel – this is understandable but I can't work out how to solve my implementation.

Answer 59:

Your hazard.monitor and robot.control are non-terminating processes and must be running in parallel. As you report, parallel processes cannot communicate by shared access to the same data – an array of hazard values in this case. As always, parallel processes communicate by sending information over channels.

Your hazard.monitor and robot.control need to be be connected by channels (not by a shared piece of data). Your hazard.monitor needs to be a server, responding to hazard warnings and saving them (as it currently does) ... but saving those warning values in its own locally declared array (not a reference parameter). This hazard.monitor also has to service enquiries from robot.control: when robot.control (or some PROC that it invokes to do some commanded action) needs to know about hazard levels, it simply asks hazard.monitor, which then replies with its latest values. This server is trivial:

    PROC hazard.monitor ([]CHAN P.HAZARD.DETECTOR hazard?,
                         CHAN BOOL enquire?, CHAN [HAZARD.SECTIONS]INT reply!)
      [HAZARD.SECTIONS]INT hazard.value:
      SEQ
        ...  initialise hazard.value array to zeroes (indicating no hazards so far)
        WHILE TRUE
          ALT
            BOOL any:
            enquire ? any
              reply ! hazard.value
            ALT i = 0 FOR SIZE hazard
              hazard[i] ? hazard.value[i]
    :

Simples, :).   [Note: HAZARD.SECTIONS is declared in the file "mars-robot.inc" and is set to 4.]

Now, all your movement code needs is its own array of hazard levels in which to receive data from hazard.monitor (whenever it asks for it). Of course, this code cannot have connections to the hazard channels itself – they are held by hazard.monitor and the occam parallel usage rules do not (for good reason) allow any parallel process also to hold them – so it has no choice but to ask hazard.monitor.

Postscript: the above hazard.monitor server is a little awkward to use ... because the robot.control driving logic has to decide when and how often to make its enquiries (e.g. every 100 milliseconds, depending on how fast it is moving). Better would be to put more intelligence into hazard.monitor – for example, programming it to check the hazard values it receives and, if they are over some threshold, send a warning (e.g. hazard channel index and over-threshold value) to robot.control without being asked. Then, the robot.control driving logic just needs to ALT between motor feedback and the warning channel from hazard.monitor.

However, hazard.monitor will in that case need to be told whether the rover is driving forwards or backwards or, indeed, whether it is driving at all (so that it knows which channels, if any, should be checked to trigger warnings to robot.control). Unfortunately, the process that can do that telling is robot.control. So, there is a danger of deadlock – e.g. if robot.control decides to tell hazard.monitor that it is stopping (because the rover has travelled the requested distance) just as hazard.monitor detects an obstacle is too close and decides to send robot.control a warning.

There is an elegant solution to this ... but we'll leave you to think about it. Meanwhile, asking the server (as programmed above) for the hazard levels every so often when driving (i.e. ALTing between motor feedback and a timeout) will work.

Keywords: mars

Referrers: Question 49 (2012) , Question 64 (2011) , Question 64 (2011) , Question 68 (2011) , Question 68 (2011)


Question 60:

Submission reference: IN2093

Regarding the Mars assessment:

As turning an odd number of degrees always turns the robot one more than the amount specified e.g. input is 39 robot turns 40. Is it ok to check for an odd number and then subtract one from that number so the correct number of degrees is always turned?

Answer 60:

Nope – don't worry about it.

The click counts when turning tend to be plus-or-minus 2 (and, when driving, plus-or-minus 10) – but you don't need to know that. Just turn or drive till the total click counts is greater than or equal to the amount commanded (or less than or equal if going anti-clockwise or backwards). If asked to turn 39 and your robot turns 40, report what actually happened (i.e. 40) and that's fine. If asked to drive 41 and your robot drives 50, just report 50 and that will be OK.

[Note: when driving, we cannot assume that the click counts will always plus-or-minus 10. So, there's no need to do something clever ... like backing up if the rover overshoots (although you will not be penalised, of course, for doing this).]

Keywords: mars

Valid CSS!

Valid XHTML 1.0!

This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Last modified Mon May 20 13:50:22 2013
This document is maintained by Fred Barnes, to whom any comments and corrections should be addressed.