Package org.jcsp.awt

# Class ActiveButtonControl

java.lang.Object
org.jcsp.awt.ActiveButtonControl

All Implemented Interfaces:
  `CSProcess`

public class ActiveButtonControl extends Object implements CSProcess
This is a user-programmable finite state machine for controlling an array of `ActiveButton`s.

## Process Diagram

## Description

`ActiveButtonControl` is a user-programmable finite state machine for controlling an array of `ActiveButton`s. It is connected to the array by its `event` and `configure` channels, matching pairs of which must be attached to individual buttons. A button push generates a message down the attached `event` channel, which triggers a state transition in the `ActiveButtonControl` and a report that is sent down its `report` channel. The report consists of the new state just reached (an `Integer`) and, optionally, the channel index identifying which button was pushed (also an `Integer`) and the message generated by the button (the `String` that was labelling it). Also, the labels on all the buttons may be reset on entry to a new state, together with their enable/disable status (via messages sent back by `ActiveButtonControl` on its `configure` channels).

All external channels must be supplied as parameters to a constructor. The constructor must also be passed an array of `String` arrays (one `String` array defining the labels allowed for each attached button), the state table and a starting state.

The state table is an array of `ActiveButtonState`s. Each `ActiveButtonState` has three arrays, all of size equal to the number of buttons being controlled. The first array holds the indices defining the label to be displayed on each button when in that state. The second array holds the booleans defining the enable/disable status for each button for that state. The third array holds the states to change into if the corresponding button is pushed when in that state.

## Channel Protocols

Input Channels
event[i] | String | The label on the attached `ActiveButton` (when it is pressed and released).
Output Channels
configure[i] | String | Defines the label on the attached `ActiveButton`.
Boolean | Defines the enable/disable status on the attached `ActiveButton`.
report | Integer | The new state (after a state transition). Note: depending on the state transition table, this may be the same as the previous state..
Integer | The channel index identifying which button caused the state transition. Note: this will be output after the above state information only if requested - see `setReportButtonIndex`.
String | The label on the button that caused the state transition. Note: this will be output after the above information only if requested - see `setReportButtonLabel`.

## Example

     import java.awt.*;
     import org.jcsp.lang.*;
     import org.jcsp.util.*;
     import org.jcsp.awt.*;

     public class ActiveButtonControlExample {

       public static void main (String argv[]) {

         final Frame root = new Frame ("ActiveButtonControl Example");

         final String[][] labels = {
           new String[] {"Hello World", "JCSP", "Restart"},
           new String[] {"Rocket Science", "JCSP", "occam"},
           new String[] {"Deadlock", "JCSP", "occam"},
           new String[] {"Race Hazard", "JCSP", "occam"},
           new String[] {"Starvation", "JCSP", "Quit", "Back"},
           new String[] {"Threads", "JCSP", "occam"},
           new String[] {"Livelock", "JCSP", "occam"},
           new String[] {"Monitors", "JCSP", "occam"},
           new String[] {"Alchemy", "JCSP", "Smile"}
         };

         final int nButtons = labels.length;

         final One2OneChannel[] fromButton = Channel.one2oneArray (nButtons, new OverWriteOldestBuffer (1));
         final Any2OneChannel[] toButton = Channel.any2oneArray (nButtons);

         final One2OneChannel report = Channel.one2one ();

         final ActiveButton[] button = new ActiveButton[nButtons];
         for (int i = 0; i invalid input: '<' nButtons; i++) {
           button[i] = new ActiveButton (toButton[i].in (), fromButton[i].out (), "XXXXXXXXX");
           button[i].setBackground (Color.green);
         }

         root.setSize (450, 200);
         root.setLayout (new GridLayout (nButtons/3, 3));
         for (int i = 0; i invalid input: '<' nButtons; i++) {
           root.add (button[i]);
         }
         root.setVisible (true);

         final int initial   = 0;                    // state names
         final int diagonal  = 1;
         final int opposite  = 2;
         final int centre    = 3;
         final int full      = 4;
         final int terminal  = 5;

         final String[] stateName = {
           "initial", "diagonal", "opposite", "centre", "full", "terminal"
         };

         final ActiveButtonState[] state = new ActiveButtonState[stateName.length];

         try {

           state[initial] =
             new ActiveButtonState (
               new int[] {
                 0, 0, 1,
                 0, 1, 1,                              // label index
                 1, 1, 1
               },
               new boolean[] {
                 true,  true,  false,
                 true,  false, false,                  // enable/disable
                 false, false, false
               },
               new int[] {
                 diagonal, initial,  initial,
                 initial,  initial,  initial,          // next state
                 initial,  initial,  initial
               }
             );

[In this example, nine buttons are laid out in a 3x3 grid in the root frame. The layout of the above state definition reflects this grid. Note that this code is enclosed within a `try`-`catch` block since the `ActiveButtonState` `constructor` performs numerous consistency checks on its parameters and may throw an exception.

The label index array specifies that, in this initial state, the north-west button and its east and south neighbours display their 0-index labels (i.e. "Hello World", "Rocket Science" and "Race Hazard" respectively). All the other buttons display their 1-index labels (which all happen to be "JCSP").

The enable/disable array specifies that, in this initial state, all those buttons displaying "JCSP" are disabled and all the others are enabled.

The next state array specifies that, in this initial state, if the top-left button is pushed, then the state should change to diagonal. However, if any other button is pushed, the state remains as initial. Note that it doesn't actually matter what is defined for those buttons that are disabled in this state.

The diagonal, opposite, centre and full states are defined similarly. We pick up the program for the definition of its terminal state (where all the labels are "JCSP" and all the buttons are disabled).]

           state[terminal] =
             new ActiveButtonState (
               new int[] {
                 1, 1, 1,
                 1, 1, 1,                              // label index
                 1, 1, 1
               },
               new boolean[] {
                 false, false, false,
                 false, false, false,                  // enable/disable
                 false, false, false
               },
               new int[] {
                 terminal, terminal, terminal,
                 terminal, terminal, terminal,         // next state
                 terminal, terminal, terminal
               }
             );

         } catch (ActiveButtonState.BadArguments e) {

          System.out.println (e);
          System.exit (0);

         };

         new Parallel (
           new CSProcess[] {
             new Parallel (button),
             new CSProcess () {
               public void run () {
                 final ActiveButtonControl control;
                 try {
                   control = new ActiveButtonControl (
                     fromButton.in (), toButton.out (), report.out (), labels, state, initial
                   );
                   control.setReportButtonIndex (true);
                   control.setReportButtonLabel (true);
                   control.run ();
                 } catch (ActiveButtonControl.BadArguments e) {
                   System.out.println (e);
                   System.exit (0);
                 }
               }
             },
             new CSProcess () {
               public void run () {
                 boolean running = true;
                 while (running) {
                   final int newState = ((Integer) report.in ().read ()).intValue ();
                   final int buttonIndex = ((Integer) report.in ().read ()).intValue ();
                   final String buttonString = (String) report.in ().read ();
                   System.out.println (
                     "Button " + buttonIndex +
                     " (" + buttonString + ") pressed ==> " + stateName[newState]
                   );
                   running = (newState != terminal);
                 }
                 final CSTimer tim = new CSTimer ();        // countdown to exit
                 final long interval = 1000;            // one second
                 long timeout = tim.read ();
                 for (int i = 10; i >= 0; i--) {
                   timeout += interval;
                   tim.after (timeout);
                   final String iString = (new Integer (i)).toString ();
                   for (int j = 0; j invalid input: '<' nButtons; j++) {
                     toButton[j].write (iString);
                   }
                 }
                 root.setVisible (false);
                 System.exit (0);
               }
             }
           }
         ).run ();

       }

     }

The countdown to exit sequence is just for fun. In its terminal state, this `ActiveButtonControl` disables all its buttons and will never change state again. This leaves the buttons free to be configured by some other process, such as the last one above once it has detected that the controller is dead. This is why the `toButton` configure channels were created as `Any2OneChannelImpl`, rather than the more usual `One2OneChannelImpl`.

Author:
  P.H. Welch
See Also:
  - `ActiveButton`
  - `ActiveButtonState`

## Nested Class Summary

Nested Classes
Modifier and Type
Class
Description
`static class`
`ActiveButtonControl.BadArguments`
This gets thrown if a consistency check fails in the `ActiveButtonControl` constructor.

## Constructor Summary

Constructors
Constructor
Description
`ActiveButtonControl(AltingChannelInput[] event, ChannelOutput[] configure, ChannelOutput report, String[][] label, ActiveButtonState[] state, int startState)`
Constructs a new `ActiveButtonControl`, performing consistency checks on its supplied arguments.
`ActiveButtonControl(AltingChannelInput[] event, ChannelOutput[] configure, ChannelOutput report, String[][] label, ActiveButtonState[] state, int startState, boolean verbose)`
Constructs a new `ActiveButtonControl`, performing consistency checks on its supplied arguments, with a verbose reporting option.

## Method Summary

Modifier and Type
Method
Description
`void`
`run()`
Main body of the process.
`void`
`setReportButtonIndex(boolean condition)`
Defines whether the index of the pushed button causing a state transition should be included in the report.
`void`
`setReportButtonLabel(boolean condition)`
Defines whether the label on the pushed button causing a state transition should be included in the report.

### Methods inherited from class java.lang.Object

`clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

## Constructor Details

### ActiveButtonControl

public ActiveButtonControl(AltingChannelInput[] event, ChannelOutput[] configure, ChannelOutput report, String[][] label, ActiveButtonState[] state, int startState) throws ActiveButtonControl.BadArguments
Constructs a new `ActiveButtonControl`, performing consistency checks on its supplied arguments.

Parameters:
  `event` - equal-indexed elements of event and configure must be connected to the same `ActiveButton`.
  `configure` - equal-indexed elements of event and configure must be connected to the same `ActiveButton`.
  `report` - the channel on which state transitions are reported. A report has up to three messages: the new state, the index of the button triggering the transition (`optional`) and its label (`optional`).
  `label` - an array of string arrays (one for each controlled botton). Each string array defines the label set allowed for each button.
  `state` - the state transition table.
  `startState` - the starting state for this finite state machine.
Throws:
  `ActiveButtonControl.BadArguments` - if the consistency check fails. The exception contains details of the error.

### ActiveButtonControl

public ActiveButtonControl(AltingChannelInput[] event, ChannelOutput[] configure, ChannelOutput report, String[][] label, ActiveButtonState[] state, int startState, boolean verbose) throws ActiveButtonControl.BadArguments
Constructs a new `ActiveButtonControl`, performing consistency checks on its supplied arguments, with a verbose reporting option.

Parameters:
  `event` - equal-indexed elements of event and configure must be connected to the same `ActiveButton`.
  `configure` - equal-indexed elements of event and configure must be connected to the same `ActiveButton`.
  `report` - the channel on which state transitions are reported. A report has up to three messages: the new state, the index of the button triggering the transition (`optional`) and its label (`optional`).
  `label` - an array of string arrays (one for each controlled botton). Each string array defines the label set allowed for each button.
  `state` - the state transition table.
  `startState` - the starting state for this finite state machine.
  `verbose` - if true, a running commentary is printed on the consistency checks and state transitions as they occur.
Throws:
  `ActiveButtonControl.BadArguments` - if the consistency check fails. The exception contains details of the error.

## Method Details

### setReportButtonIndex

public void setReportButtonIndex(boolean condition)
Defines whether the index of the pushed button causing a state transition should be included in the report. The default is that it should not be reported.

Parameters:
  `condition` - if true, the button index is reported - otherwise it is not reported.

### setReportButtonLabel

public void setReportButtonLabel(boolean condition)
Defines whether the label on the pushed button causing a state transition should be included in the report. The default is that it should not be reported.

Parameters:
  `condition` - if true, the button label is reported - otherwise it is not reported.

### run

public void run()
Main body of the process.

Specified by:
  `run` in interface `CSProcess`