|
1 /* |
|
2 * Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * Filename: UCCS_CBatchEngine.cpp |
|
16 * System Includes |
|
17 * |
|
18 */ |
|
19 |
|
20 |
|
21 |
|
22 #include <stdio.h> |
|
23 #include <stdlib.h> |
|
24 #include <assert.h> |
|
25 #include <io.h> |
|
26 |
|
27 |
|
28 /*********************************************************************************** |
|
29 * |
|
30 * Local Includes |
|
31 * |
|
32 **********************************************************************************/ |
|
33 #include "UCCS_CBatchEngine.h" |
|
34 #include "UCCS_ErrorCodes.h" |
|
35 |
|
36 |
|
37 /*********************************************************************************** |
|
38 * |
|
39 * Definitions |
|
40 * |
|
41 **********************************************************************************/ |
|
42 #define IS_WHITESPACE(c) (((c) == '\t')||((c) == '\n')||((c) == ' ')) |
|
43 #define MAXCOMMANDLENGTH 2048 |
|
44 |
|
45 |
|
46 /*********************************************************************************** |
|
47 * |
|
48 * Definitions |
|
49 * |
|
50 **********************************************************************************/ |
|
51 DWORD WINAPI ThreadProc(LPVOID lpParameter); |
|
52 |
|
53 |
|
54 /*********************************************************************************** |
|
55 * |
|
56 * PUBLIC METHOD: Construction |
|
57 * |
|
58 **********************************************************************************/ |
|
59 CBatchEngine::CBatchEngine( IRetrieveCommand *aRetrieveCommand, IOutput *aOutput ) |
|
60 { |
|
61 // check parameters |
|
62 assert( aRetrieveCommand != NULL ); |
|
63 assert( aOutput != NULL ); |
|
64 |
|
65 // init the vars |
|
66 iRetrieveCommand = aRetrieveCommand; |
|
67 iOutput = aOutput; |
|
68 |
|
69 // set all other state |
|
70 iExecutionThreadState = ETS_IDLE; |
|
71 iControlThreadState = CTS_IDLE; |
|
72 |
|
73 // the rest are set according to the state -- for idle their value is irrelevant |
|
74 // but I'd like to set them anyway for completeness |
|
75 iLastError = 0; |
|
76 iSync = NULL; |
|
77 iUsecaseID = INVALID_USECASE_ID; |
|
78 hThreadHandle = NULL; |
|
79 iExecuteCommand = NULL; |
|
80 } |
|
81 |
|
82 |
|
83 /*********************************************************************************** |
|
84 * |
|
85 * PUBLIC METHOD: Destruction |
|
86 * |
|
87 **********************************************************************************/ |
|
88 CBatchEngine::~CBatchEngine() |
|
89 { |
|
90 // clean up any memory holding state variables |
|
91 if( iExecuteCommand != NULL ) { |
|
92 delete iExecuteCommand; |
|
93 iExecuteCommand = NULL; |
|
94 } |
|
95 if( hThreadHandle != NULL ) { |
|
96 CloseHandle(hThreadHandle); |
|
97 hThreadHandle = NULL; |
|
98 } |
|
99 if( iSync != NULL ) { |
|
100 delete iSync; |
|
101 iSync = NULL; |
|
102 } |
|
103 } |
|
104 |
|
105 |
|
106 /*********************************************************************************** |
|
107 * |
|
108 * PUBLIC METHOD: StartUseCase |
|
109 * |
|
110 **********************************************************************************/ |
|
111 int CBatchEngine::StartUsecase( int aUsecaseID ) |
|
112 { |
|
113 int err; |
|
114 DWORD dwThreadID; |
|
115 |
|
116 // check that the control thread is currently in idle |
|
117 if( iControlThreadState != CTS_IDLE ) { |
|
118 return UCCS_ALREADYSTARTEDUSECASE; |
|
119 } |
|
120 assert( iExecutionThreadState == ETS_IDLE ); |
|
121 |
|
122 // ask the retriever to get the use-case description |
|
123 err = iRetrieveCommand->StartUseCase( aUsecaseID ); |
|
124 if( err != UCCS_OK ) { |
|
125 return err; |
|
126 } |
|
127 |
|
128 // set all the state appropriatley |
|
129 assert( iSync == NULL ); |
|
130 iSync = new CSynchronisation( iOutput ); |
|
131 assert( iSync != NULL ); |
|
132 assert( iExecuteCommand == NULL ); |
|
133 iExecuteCommand = new CExecuteCommand( iSync, iOutput ); |
|
134 assert( iExecuteCommand != NULL ); |
|
135 iLastError = 0; |
|
136 iUsecaseID = aUsecaseID; |
|
137 |
|
138 // set the state |
|
139 iExecutionThreadState = ETS_EXECUTING_SCRIPT; |
|
140 iControlThreadState = CTS_USECASE_STARTED; |
|
141 |
|
142 // output that we have started |
|
143 iOutput->StartUsecase( aUsecaseID ); |
|
144 |
|
145 // start the thread that goes and executes the steps |
|
146 #ifndef TESTCASEBATCH |
|
147 hThreadHandle = CreateThread( NULL, 0, ThreadProc, this, 0, &dwThreadID ); |
|
148 #else |
|
149 hThreadHandle = 0; |
|
150 #endif |
|
151 if( hThreadHandle == 0 ) { |
|
152 delete iSync; |
|
153 iSync = NULL; |
|
154 delete iExecuteCommand; |
|
155 iExecuteCommand = NULL; |
|
156 iUsecaseID = INVALID_USECASE_ID; |
|
157 iExecutionThreadState = ETS_IDLE; |
|
158 iControlThreadState = CTS_IDLE; |
|
159 return UCCS_FAILEDTOCREATEEXECUTETHREAD; |
|
160 } |
|
161 |
|
162 // done - return OK to the external controller |
|
163 return UCCS_OK; |
|
164 } |
|
165 |
|
166 |
|
167 /*********************************************************************************** |
|
168 * |
|
169 * PUBLIC METHOD: EndUsecase |
|
170 * |
|
171 **********************************************************************************/ |
|
172 int CBatchEngine::EndUsecase( int aUsecaseID, int aResult, int *aScriptResult ) |
|
173 { |
|
174 int err; |
|
175 |
|
176 // check that the control thread is in the correct state |
|
177 if( iControlThreadState != CTS_USECASE_STARTED ) { |
|
178 *aScriptResult = UCCS_NOUSECASERUNNING; |
|
179 return UCCS_NOUSECASERUNNING; |
|
180 } |
|
181 |
|
182 // update the state of the control thread to ended -- this will cause the execution |
|
183 // thread to exit on it's next iteration. |
|
184 iControlThreadState = CTS_USECASE_ENDED; |
|
185 |
|
186 // We clear the synchronisation so that if the execution thread is (or is just about |
|
187 // to) wait on a semaphore then it will not get stuck forever |
|
188 iSync->ClearSynchronisation(); |
|
189 |
|
190 // Wait for the thread to really exit |
|
191 err = WaitForSingleObject( hThreadHandle, INFINITE ); |
|
192 if( err != WAIT_OBJECT_0 ) { |
|
193 iOutput->Error( UCCS_SYSTEMERROR, "An error occured while waiting for the executing script thread to finish." ); |
|
194 } |
|
195 CloseHandle( hThreadHandle ); |
|
196 hThreadHandle = NULL; |
|
197 |
|
198 // cleanup the rest of the state |
|
199 assert( iExecutionThreadState == ETS_IDLE ); |
|
200 iControlThreadState = CTS_IDLE; |
|
201 delete iSync; |
|
202 iSync = NULL; |
|
203 delete iExecuteCommand; |
|
204 iExecuteCommand = NULL; |
|
205 iUsecaseID = INVALID_USECASE_ID; |
|
206 |
|
207 // output that endusecase has been called |
|
208 iOutput->EndUsecase( aUsecaseID, aResult ); |
|
209 |
|
210 // done -- return the information |
|
211 *aScriptResult = iLastError; |
|
212 iLastError = 0; |
|
213 return UCCS_OK; |
|
214 } |
|
215 |
|
216 /*********************************************************************************** |
|
217 * |
|
218 * PUBLIC METHOD: GetVariableName |
|
219 * |
|
220 **********************************************************************************/ |
|
221 int CBatchEngine::GetEnvVariable( char *aVariableName, char *aOutputBuffer, int aOutputBufferLen ) |
|
222 { |
|
223 // check params |
|
224 assert ( aVariableName != NULL ); |
|
225 assert ( aOutputBuffer != NULL ); |
|
226 assert ( aOutputBufferLen > 0 ); |
|
227 |
|
228 // check that there is an actual usecase running |
|
229 if( iControlThreadState != CTS_USECASE_STARTED ) { |
|
230 return UCCS_NOUSECASERUNNING; |
|
231 } |
|
232 |
|
233 // check that there is actually a command around |
|
234 if( iExecuteCommand == NULL ) { |
|
235 return UCCS_COMMANDEXECUTIONNOTSTARTEDYET; |
|
236 } |
|
237 |
|
238 // change aVariableName to uppercase -a s it is stored in the data record as uppercase. |
|
239 _strupr( aVariableName ); |
|
240 |
|
241 // now go get the environment var |
|
242 return iExecuteCommand->GetEnvironmentVariable( aVariableName, aOutputBuffer, aOutputBufferLen ); |
|
243 } |
|
244 |
|
245 /*********************************************************************************** |
|
246 * |
|
247 * PUBLIC METHOD: RunCommand |
|
248 * |
|
249 **********************************************************************************/ |
|
250 int CBatchEngine::RunCommand( char* aCommandLine ) |
|
251 { |
|
252 // check params |
|
253 assert ( aCommandLine != NULL ); |
|
254 |
|
255 // check that there is actually a command around |
|
256 if( iSync == NULL ) { |
|
257 iSync = new CSynchronisation( iOutput ); |
|
258 } |
|
259 if( iExecuteCommand == NULL ) { |
|
260 iExecuteCommand = new CExecuteCommand( iSync, iOutput ); |
|
261 } |
|
262 |
|
263 // now go get the environment var |
|
264 return iExecuteCommand->ExecuteCommand( aCommandLine ); |
|
265 } |
|
266 /*********************************************************************************** |
|
267 * |
|
268 * PUBLIC METHOD: Signal |
|
269 * |
|
270 **********************************************************************************/ |
|
271 |
|
272 int CBatchEngine::Signal( int aUsecaseID ) |
|
273 { |
|
274 // check that the state is valid |
|
275 if( iControlThreadState != CTS_USECASE_STARTED ) { |
|
276 return UCCS_NOUSECASERUNNING; |
|
277 } |
|
278 return iSync->SignalFromDevice(); |
|
279 } |
|
280 |
|
281 |
|
282 /*********************************************************************************** |
|
283 * |
|
284 * PUBLIC METHOD: Rendezvous |
|
285 * |
|
286 **********************************************************************************/ |
|
287 int CBatchEngine::Rendezvous( int aUseCaseID ) |
|
288 { |
|
289 // check that the control state is valid |
|
290 if( iControlThreadState != CTS_USECASE_STARTED ) { |
|
291 return UCCS_NOUSECASERUNNING; |
|
292 } |
|
293 |
|
294 // check that the execution thread is still running |
|
295 if( iExecutionThreadState != ETS_EXECUTING_SCRIPT ) { |
|
296 return UCCS_SCRIPTFINISHED; |
|
297 } |
|
298 |
|
299 // do the sync |
|
300 return iSync->RendezvousFromDevice(); |
|
301 } |
|
302 |
|
303 |
|
304 /*********************************************************************************** |
|
305 * |
|
306 * PUBLIC METHOD: Wait |
|
307 * |
|
308 **********************************************************************************/ |
|
309 int CBatchEngine::Wait( int aUseCaseID ) |
|
310 { |
|
311 // check that the control state is valid |
|
312 if( iControlThreadState != CTS_USECASE_STARTED ) { |
|
313 return UCCS_NOUSECASERUNNING; |
|
314 } |
|
315 |
|
316 // check that the execution thread is still running |
|
317 if( iExecutionThreadState != ETS_EXECUTING_SCRIPT ) { |
|
318 return UCCS_SCRIPTFINISHED; |
|
319 } |
|
320 |
|
321 // do the sync |
|
322 return iSync->WaitFromDevice(); |
|
323 } |
|
324 |
|
325 |
|
326 /*********************************************************************************** |
|
327 * |
|
328 * PUBLIC METHOD: ExecuteScript |
|
329 * |
|
330 **********************************************************************************/ |
|
331 int CBatchEngine::ExecuteScript( void ) |
|
332 { |
|
333 int err; |
|
334 char *c; |
|
335 char command_buffer[MAXCOMMANDLENGTH]; |
|
336 int rv = 0; |
|
337 |
|
338 // execute all the commands |
|
339 while( 1 ) { |
|
340 |
|
341 // if the controller has ended the usecase then we stop executing commands |
|
342 if( iControlThreadState != CTS_USECASE_STARTED ) { |
|
343 break; |
|
344 } |
|
345 |
|
346 // get the next command to execute |
|
347 err = iRetrieveCommand->GetNextCommand( command_buffer, MAXCOMMANDLENGTH ); |
|
348 if( err == UCCS_NOMORECOMMANDS ) { |
|
349 iOutput->CompletedScript(); |
|
350 break; |
|
351 } |
|
352 assert( err == UCCS_OK ); |
|
353 |
|
354 // NOTE: the code below is the correct implementation of handling generic errors from the input |
|
355 // module. It has been taken out because the input modules don't return anything except UCCS_NOMORECOMMANDS |
|
356 // and UCCS_OK so there is no way to test the condition (and it messes up our coverage results!). But |
|
357 // if new error codes are put in this implementation should be used. |
|
358 // else if( err != UCCS_OK ) { |
|
359 // iOutput->Error( err, "GetNextCommand returned error. Stopping script execution." ); |
|
360 // rv = err; |
|
361 // break; |
|
362 // } |
|
363 |
|
364 // if the first not whitespace char is 0, or #, or // then return the comment |
|
365 for( c = command_buffer; IS_WHITESPACE(*c); c++ ) |
|
366 ; |
|
367 if( (*c == 0) || (*c == '#') || ((c[0] == '/') && (c[1] == '/'))) { |
|
368 continue; |
|
369 } |
|
370 |
|
371 // now execute the command |
|
372 iOutput->ExecuteString( c ); |
|
373 err = iExecuteCommand->ExecuteCommand( c ); |
|
374 iOutput->ExecuteStringResult( err ); |
|
375 if( (err != UCCS_OK) && (err != UCCS_QUIT) ) { |
|
376 iOutput->Error( err, NULL ); |
|
377 } |
|
378 |
|
379 // if the return value from the command was quit (i.e. the script had a |
|
380 // quit command) then we print the message and break from the loop. |
|
381 if( err == UCCS_QUIT ) { |
|
382 iOutput->CompletedScript(); |
|
383 break; |
|
384 } |
|
385 |
|
386 // save the last error -- this is so we can notify the device is an error occured |
|
387 if( err != UCCS_OK ) { |
|
388 iLastError = err; |
|
389 } |
|
390 |
|
391 // if we do a require or requirenot that fails then make a point about it! |
|
392 if( (err == UCCS_REQUIREDVALUEERROR) || (err == UCCS_REQUIREDVALUEINCORRECT) || (err == UCCS_REQUIREDNOTVALUEERROR) || (err == UCCS_REQUIREDNOTVALUEMATCH) ) { |
|
393 // should break out here -- problem is that at the moment there are no reset calls so we can't recover!! |
|
394 } |
|
395 } |
|
396 |
|
397 // set the state of this thread to completed and clear the synchronisation state so that |
|
398 // the control thread won't wait forever. The state of the execution thread should stop |
|
399 // the control thread from being able to wait again |
|
400 iExecutionThreadState = ETS_COMPLETED_SCRIPT; |
|
401 iSync->ClearSynchronisation(); |
|
402 |
|
403 // tell the input module that we are done with it |
|
404 err = iRetrieveCommand->EndUseCase(); |
|
405 assert( err == UCCS_OK ); |
|
406 |
|
407 // set the state to idle |
|
408 iExecutionThreadState = ETS_IDLE; |
|
409 |
|
410 // done |
|
411 return rv; |
|
412 } |
|
413 |
|
414 |
|
415 /*********************************************************************************** |
|
416 * |
|
417 * FUNCTION: Entry point for second thread -- call executescript on the passed |
|
418 * batch engine object. |
|
419 * |
|
420 **********************************************************************************/ |
|
421 DWORD WINAPI ThreadProc(LPVOID lpParameter) |
|
422 { |
|
423 CBatchEngine* aLocalBatchEngine; |
|
424 aLocalBatchEngine = (CBatchEngine*)lpParameter; |
|
425 return aLocalBatchEngine->ExecuteScript(); |
|
426 } |
|
427 |
|
428 |