capture
[rrq/gorite.git] / com / intendico / gorite / Action.java
1 /*********************************************************************
2 Copyright 2012, Ralph Ronnquist.
3
4 This file is part of GORITE.
5
6 GORITE is free software: you can redistribute it and/or modify it
7 under the terms of the Lesser GNU General Public License as published
8 by the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 GORITE is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14 License for more details.
15
16 You should have received a copy of the Lesser GNU General Public
17 License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
18 **********************************************************************/
19
20 package com.intendico.gorite;
21
22 /**
23  * The Action class is a utility base class for implementation of task
24  * goals that operate on a common entity. The created action object
25  * becomes a factory for goals that refer back to it, and that end up
26  * invoking the Action's {@link #execute} method as way of achieving
27  * the task goal.
28  *
29  * <p> Generally, an action is a named definition of a function
30  * performed on data. It would occur in a process model in terms of a
31  * task goal, which is recognised as a usage point of the action,
32  * linking it to particular input and output data. The same action may
33  * be used elsewhere, with other data names.
34  *
35  * <p> Task goals referring to the same action object will invoke the
36  * same {@link #execute} method.
37  *
38  * <p>Example of use:
39  * <pre>
40  * public class MachineControlAdapter {
41  *     Action open_lid = new Action( "open lid" ) {
42  *         public boolean execute(
43  *             boolean reentry,Data.Element [] ins,Data.Element [] * outs ) {
44  *                 .. code to perform the "open lid" action
45  *             }
46  *     }
47  *     Action status = new Action( "check status" ) {
48  *         public boolean execute(
49  *             boolean reentry,Data.Element [] ins,Data.Element [] * outs ) {
50  *                 .. code to perform the "check status" action
51  *             }
52  *     }
53  *     public Capability actions() {
54  *         return new Capability() {{
55  *             open_lid.create( new String [] { "angle" }, null );
56  *             on_off.create( null, new String [] { "status" } );
57  *         }};
58  *     }
59  * }
60  * </pre>
61  *
62  * The example code illustrates a "machine adapter" with two actions:
63  * to "open lid" to a certain "angle", and to "check status" giving a
64  * "status". The "machine adapter" includes an "actions()" method that
65  * creates a control capability of two goals for invoking the actions.
66  */
67 public class Action {
68
69     /**
70      * The name of the action. This is also the name of the goal for
71      * the action created via the {@link #create} method.
72      */
73     public String name;
74
75     /**
76      * Nominates the {@link Performer.TodoGroup} to use for performing
77      * this action.
78      */
79     private String group;
80
81     /**
82      * Constructor without group.
83      */
84     public Action(String n) {
85         name = n;
86     }
87
88     /**
89      * Constructor.
90      */
91     public Action(String n,String g) {
92         name = n;
93         group = g;
94     }
95
96     /**
97      * This is a utility class to represent the appearance of the
98      * action within a goal hierarchy. Each such apprearance involves
99      * its own data connections, which are contained within the Usage
100      * class. When this task goal is executed, it firstly ensures that
101      * all input data is available, or otherwise delays the execution
102      * until they are available. Thereafter it ensures that some
103      * output data needs to be produces, before invoking the action's
104      * exectution. If all output data is already produced, the task
105      * goal execution succeeds immediately without the actual action
106      * execution.
107      */
108     public class Usage extends Goal {
109
110         /**
111          * The names of data elements that are inputs to the goal; the
112          * performance of a goal will be postponed until all inputs
113          * are available, although maybe not ready.
114          */
115         public String [] inputs;
116
117         /**
118          * The names of data elements that are outputs from the
119          * action. The task goal for an action considers these: if
120          * they are all ready and available when the goal is
121          * performed, then the action execution is skipped, and the
122          * goal terminates successfully.  However, an action without
123          * outputs is always executed.
124          */
125         public String [] outputs;
126
127         /**
128          * Constructor.
129          */
130         public Usage(String [] ins,String [] outs) {
131             super( Action.this.name );
132             inputs = ins;
133             outputs = outs;
134             group = getGoalGroup();
135         }
136
137         /**
138          * Creates and returns an instance object for achieving
139          * a Usage.
140          */
141         public Instance instantiate(String head,Data d) {
142             return new Invoke( head, d );
143         }
144
145         /**
146          * The Invoke class represents the intention to performa Usage
147          * goal.
148          */
149         public class Invoke extends Instance {
150
151             /**
152              * Constructor.
153              */
154             public Invoke(String head,Data d) {
155                 super( head );
156             }
157
158             /**
159              * Tracking of first or sub sequence perform calls.
160              */
161             public boolean reentry = false;
162
163             /**
164              * The execution of an action involves an initial step of
165              * establishing the data connections. This will postpone the
166              * actual action execution until all input data is
167              * available. It will also skip the action execution if all
168              * outputs are already marked ready. It is the responsibility
169              * of the actual action execution to mark outputs as ready.
170              */
171             public States action(String head,Data d) {
172                 Data.Element [] ins = null;
173                 Data.Element [] outs = null;
174
175                 // Synchronise with existence and readiness of inputs.
176                 if ( inputs != null ) {
177                     ins = new Data.Element [ inputs.length ];
178                     for ( int i = 0; i < inputs.length; i++ ) {
179                         ins[ i ] = d.find( inputs[ i ] );
180                         if ( ins[ i ] == null )
181                             return States.BLOCKED;
182                         if ( ! ins[ i ].ready )
183                             return States.BLOCKED;
184                     }
185                 }
186
187                 if ( outputs != null ) {
188                     outs = new Data.Element [ outputs.length ];
189                     for ( int i = 0; i < outputs.length; i++ ) {
190                         outs[ i ] = d.create( outputs[ i ] );
191                     }
192                 }
193
194                 // Invoke action implementation 
195                 Goal.States s = Action.this.execute( reentry, ins, outs );
196                 reentry = true;
197
198                 if ( s == Goal.States.PASSED ) {
199                     // Mark all outputs as ready.
200                     if ( outs != null ) {
201                         for ( int i = 0; i < outs.length; i++ ) {
202                             outs[ i ].markReady();
203                         }
204                     }
205                 }
206                 return s;
207             }
208         }
209
210         /**
211          * Makes a string representation of a string array.
212          */
213         public String toString(String [] data)
214         {
215             StringBuffer s = new StringBuffer( "[ " );
216             if ( data != null ) {
217                 String sep = "";
218                 for ( int i = 0; i < data.length; i++ ) {
219                     s.append( sep );
220                     s.append( nameString( data[i] ) );
221                     sep = ", ";
222                 }
223             }
224             s.append( " ]" );
225             return s.toString();
226         }
227
228         /**
229          * Makes a textual representation of this action's goal.
230          */
231         public String toString(String counter) {
232             StringBuffer s = new StringBuffer( super.toString( counter ) );
233             s.append( toString( inputs ) );
234             s.append( " >> " );
235             s.append( toString( outputs ) );
236             //s.append( "\n" );
237             return s.toString();
238         }
239     }
240
241     /**
242      * A factory method that makes a "usage object" for this action,
243      * representing its usage in a goal sentence. When performed,
244      * the action's execute method is invoked with the actual in and out
245      * Data.Element objects.
246      */
247     public Goal create(String [] ins,String [] outs)
248     {
249         return new Usage( ins, outs );
250     }
251
252     /**
253      * The generic action performance method. Should be overridden by
254      * extension class for action implementation.
255      */
256     public Goal.States execute(
257         boolean reentry,Data.Element [] ins,Data.Element [] outs) {
258
259         StringBuffer s = new StringBuffer( "Action " );
260         s.append( Goal.nameString( name ) );
261         s.append( " [" );
262         String sep = "";
263         if ( ins != null && ins.length > 0 ) {
264             for ( int i=0; i < ins.length; i++ ) {
265                 s.append( sep );
266                 sep = ", ";
267                 s.append( ins[i].toString() );
268             }
269         }
270         s.append( "] >> [" );
271         sep = "";
272         if ( outs != null && outs.length > 0 ) {
273             for ( int i=0; i < outs.length; i++ ) {
274                 s.append( sep );
275                 sep = ", ";
276                 s.append( outs[i].toString() );
277             }
278         }
279         s.append( "]" );
280         System.err.println( s.toString() );
281         return Goal.States.PASSED;
282     }
283
284     /**
285      * Returns the {@link #group} attribute.
286      */
287     public String getTodoGroup() {
288         return group;
289     }
290
291     /**
292      * Assigns the {@link #group} attribute.
293      */
294     public void setTodoGroup(String v) {
295         group = v;
296     }
297
298 }