artdaq_node_server  v1_01_01b
artdaq-runcontrol_module.js
1 // artdaq-runcontrol_module.js
2 // Author: Eric Flumerfelt, FNAL RSI
3 // Modified: May 8, 2015
4 //
5 // This module implements a basic Run Control system for the artdaq-demo
6 //
7 
8 var spawn = require( 'child_process' ).spawn;
9 var emitter = require( 'events' ).EventEmitter;
10 var fs = require( 'fs' );
11 var os = require( 'os');
12 var arc = new emitter( );
13 var xml = require( 'xml2js' );
14 var parser = new xml.Parser( {explicitArray: false});
15 var xmlrpc = require( 'xmlrpc' );
16 
17 // System Status
18 var Status = function ( partition ) {
19  this.state = "Shutdown";
20  this.runNumber = 1000;
21  this.systemPID = null;
22  this.systemRunning = false;
23  this.systemOutputBuffer = "";
24  this.systemErrorBuffer = "";
25  this.commandPID = null;
26  this.commandRunning = false;
27  this.commandOutputBuffer = "";
28  this.commandErrorBuffer = "";
29  this.stopPending = false;
30  this.WFPlotsUpdated = Date.now( );
31  this.WFFileSize = 0;
32  this.WFFileMtime = 0;
33  this.partition = partition;
34  this.config = "";
35  this.okToStartOnMon = false;
36  //this.events = [];
37 };
38 
39 var configuration = {};
40 configuration.artdaqDir = "~/artdaq-demo-base";
41 configuration.setupScript = "setupARTDAQDEMO";
42 configuration.runValue = 0;
43 configuration.xmlObj;
44 
45 function readConfiguration( systemStatus ) {
46  var fileName = __dirname + "/../../artdaq-configuration/server/" + systemStatus.config;
47  console.log( "Going to read configuration " + fileName );
48  if ( fs.existsSync( fileName ) ) {
49  var res = true;
50  var config = "" + fs.readFileSync( fileName );
51  var xmlObj;
52  parser.parseString( config, function(err,result) {xmlObj = result;});
53  configuration.xmlObj = xmlObj;
54  configuration.artdaqDir = xmlObj['artdaq-configuration'].artdaqDir;
55  configuration.setupScript = xmlObj['artdaq-configuration'].setupScript;
56  configuration.runValue = xmlObj['artdaq-configuration'].dataLogger.runValue;
57 
58  return res;
59  }
60  console.log( "Configuration file not found!" );
61  return false;
62 }
63 
64 arc.MasterInitFunction = function ( workerData ) {
65  var output = {};
66  output.p0 = new Status( 0 );
67  output.p1 = new Status( 1 );
68  output.p2 = new Status( 2 );
69  output.p3 = new Status( 3 );
70  output.p0evt = [];
71  output.p1evt = [];
72  output.p2evt = [];
73  output.p3evt = [];
74  output.p0onmon = false;
75  output.p1onmon = false;
76  output.p2onmon = false;
77  output.p3onmon = false;
78 
79  if ( !fs.existsSync( __dirname + "/../client/P0" ) ) {
80  fs.mkdirSync( __dirname + "/../client/P0" );
81  }
82  if ( !fs.existsSync( __dirname + "/../client/P1" ) ) {
83  fs.mkdirSync( __dirname + "/../client/P1" );
84  }
85  if ( !fs.existsSync( __dirname + "/../client/P2" ) ) {
86  fs.mkdirSync( __dirname + "/../client/P2" );
87  }
88  if ( !fs.existsSync( __dirname + "/../client/P3" ) ) {
89  fs.mkdirSync( __dirname + "/../client/P3" );
90  }
91 
92  fs.chmodSync( __dirname + "/runARTDAQ.sh",'777' );
93  fs.chmodSync( __dirname + "/killArtdaq.sh",'777' );
94  fs.chmodSync( __dirname + "/cleanupArtdaq.sh",'777' );
95 
96  workerData["artdaq-runcontrol"] = output;
97 };
98 
99 function checkCommand( systemStatus ) {
100  if ( systemStatus.commandPID !== null ) {
101  try {
102  process.kill( systemStatus.commandPID,0 );
103  systemStatus.commandRunning = true;
104  } catch ( err ) {
105  systemStatus.commandRunning = false;
106  systemStatus.commandPID = null;
107  }
108  } else {
109  systemStatus.commandRunning = false;
110  }
111 }
112 
113 function checkSystem( systemStatus ) {
114  if ( systemStatus.systemPID !== null ) {
115  try {
116  process.kill( systemStatus.systemPID,0 );
117  systemStatus.systemRunning = true;
118  } catch ( err ) {
119  systemStatus.systemRunning = false;
120  systemStatus.systemPID = null;
121  }
122  } else {
123  systemStatus.systemRunning = false;
124  }
125 }
126 
127 function startCommand( args,systemStatus ) {
128  if ( readConfiguration( systemStatus ) ) {
129  var port = ( systemStatus.partition * 100 ) + 5600;
130  var configName = configuration.artdaqDir + "/P" + systemStatus.partition + "Config.xml";
131  fs.createReadStream(__dirname + "/../../artdaq-configuration/server/" + systemStatus.config).pipe(fs.createWriteStream(configName));
132  var commandArray = [systemStatus.config,configName,port,"manageSystem.sh", "-C", configName].concat( args );
133  systemStatus.commandErrorBuffer = "";
134  systemStatus.commandOutputBuffer = "";
135  var out = fs.openSync( __dirname + "/../client/P" + systemStatus.partition + "/comm.out.log",'w' );
136  var err = fs.openSync( __dirname + "/../client/P" + systemStatus.partition + "/comm.err.log",'w' );
137  console.log( "Spawning: " + __dirname + "/runARTDAQ.sh " + commandArray );
138  var command = spawn( __dirname + "/runARTDAQ.sh",commandArray,{ detached: true, stdio: ['ignore',out,err] } );
139  /*
140  var client = xmlrpc.createClient({ host: 'localhost', port: 9090, path: '/'})
141 
142  // Sends a method call to the XML-RPC server
143  client.methodCall('anAction', ['aParam'], function (error, value) {
144  // Results of the method response
145  console.log('Method response for \'anAction\': ' + value)
146  })
147  */
148  systemStatus.commandPID = command.pid;
149  command.unref( );
150  systemStatus.commandRunning = true;
151  checkCommand( systemStatus );
152  } else {
153  console.log( "CANNOT READ CONFIGURATION. COMMAND NOT STARTED!!!!" );
154  }
155 }
156 
157 function startSystem( systemStatus ) {
158  if ( readConfiguration( systemStatus ) ) {
159  console.log( "Starting System, Partition " + systemStatus.partition );
160  var port = ( systemStatus.partition * 100 ) + 5600;
161 
162  var logRoot = configuration.xmlObj['artdaq-configuration'].logDir;
163  var eventBuilderHostnames = configuration.xmlObj['artdaq-configuration'].eventBuilders.hostnames.hostname;
164  var aggregatorHostname = configuration.xmlObj['artdaq-configuration'].dataLogger.hostname;
165  var boardReaderHostnames = configuration.xmlObj['artdaq-configuration'].boardReaders.boardReader.hostname;
166 
167  console.log("Making PMT Log Directories");
168  spawn("/bin/mkdir", ["-p","-m","0777",logRoot + "/pmt"]);
169  spawn("/bin/mkdir", ["-p","-m","0777",logRoot + "/masterControl"]);
170  if(aggregatorHostname != "localhost" && aggregatorHostname != os.hostname()) {
171  spawn("/usr/bin/ssh", [aggregatorHostname, "/bin/mkdir -p -m 0777 " + logRoot + "/aggregator"]);
172  } else {
173  spawn("/bin/mkdir", ["-p","-m","0777",logRoot + "/aggregator"]);
174  }
175  for(var hn in eventBuilderHostnames) {
176  if(hn != "localhost" && hn != os.hostname()) {
177  spawn("/usr/bin/ssh", [hn, "/bin/mkdir -p -m 0777 " + logRoot + "/eventbuilder"]);
178  } else {
179  spawn("/bin/mkdir", ["-p","-m","0777",logRoot + "/eventbuilder"]);
180  }
181  }
182  for(var hn in boardReaderHostnames) {
183  if(hn != "localhost" && hn != os.hostname()) {
184  spawn("/usr/bin/ssh", [hn, "/bin/mkdir -p -m 0777 " + logRoot + "/boardreader"]);
185  } else {
186  spawn("/bin/mkdir", ["-p","-m","0777",logRoot + "/boardreader"]);
187  }
188  }
189 
190  var configName = configuration.artdaqDir + "/P" + systemStatus.partition + "Config.xml";
191  fs.createReadStream(__dirname + "/../../artdaq-configuration/server/" + systemStatus.config).pipe(fs.createWriteStream(configName));
192  var displayNumber = process.env.DISPLAY;
193 
194  var commandArray = [systemStatus.config, configName,port,"pmt.rb","-p", port, "-C",configName,"--logpath", logRoot, "--display", displayNumber];
195 
196  var out = fs.openSync( __dirname + "/../client/P" + systemStatus.partition + "/out.log",'w' );
197  var err = fs.openSync( __dirname + "/../client/P" + systemStatus.partition + "/err.log",'w' );
198  systemStatus.systemErrorBuffer = "";
199  systemStatus.systemOutputBuffer = "";
200  console.log( "Spawning: " + __dirname + "/runARTDAQ.sh " + commandArray.join( ' ' ) );
201  var system = spawn( __dirname + "/runARTDAQ.sh",commandArray,{ detached: true, stdio: ['ignore',out,err] } );
202  systemStatus.systemPID = system.pid;
203  system.unref( );
204  systemStatus.systemRunning = true;
205  console.log( "Command Spawned" );
206 
207  systemStatus.state = "Started";
208  } else {
209  console.log( "CANNOT READ CONFIGURATION. COMMAND NOT STARTED!!!!" );
210  }
211 }
212 
213 function initialize( systemStatus ) {
214  if ( systemStatus.commandRunning ) {
215  setTimeout( function () { initialize( systemStatus ); } );
216  }
217  else {
218  var onmonDir = __dirname + "/../client/P" + systemStatus.partition + "/artdaqdemo_onmon.root";
219  var args = ["-M", onmonDir, "init"];
220  startCommand( args,systemStatus );
221  systemStatus.state = "Initialized";
222  }
223 }
224 
225 function startRun( systemStatus ) {
226  if ( systemStatus.commandRunning ) {
227  setTimeout( function () { startRun( systemStatus ); },500 );
228  }
229  else {
230  var args = ["-N",systemStatus.runNumber,"start"];
231  startCommand( args,systemStatus );
232  systemStatus.state = "Running";
233  if ( readConfiguration( systemStatus ) && configuration.runValue > 0 ) {
234  systemStatus.stopPending = true;
235  startCommand( ["stop"],systemStatus );
236  }
237  }
238 }
239 
240 function pauseRun( systemStatus ) {
241  if ( systemStatus.commandRunning ) {
242  setTimeout( function () { pauseRun( systemStatus ); },500 );
243  }
244  else {
245  var args = ["pause"];
246  startCommand( args,systemStatus );
247  systemStatus.state = "Paused";
248  }
249 }
250 
251 function resumeRun( systemStatus ) {
252  if ( systemStatus.commandRunning ) {
253  setTimeout( function () { resumeRun( systemStatus ); },500 );
254  }
255  else {
256  var args = ["resume"];
257  startCommand( args,systemStatus );
258  systemStatus.state = "Running";
259  }
260 }
261 
262 function endRun( systemStatus ) {
263  if ( systemStatus.commandRunning ) {
264  setTimeout( function () { endRun( systemStatus ); },500 );
265  }
266  else {
267  var args = ["stop"];
268  startCommand( args,systemStatus );
269  systemStatus.state = "Initialized";
270  }
271 }
272 
273 function killSystem( systemStatus ) {
274  checkCommand( systemStatus );
275  if ( !systemStatus.commandRunning ) {
276  checkSystem( systemStatus );
277  if ( systemStatus.systemRunning ) {
278  console.log( "Killing System, PID: " + systemStatus.systemPID );
279  spawn( __dirname + '/killArtdaq.sh',[systemStatus.systemPID] );
280  setTimeout( function () { killSystem( systemStatus ); },1000 );
281  }
282  } else {
283  console.log( "Command running, spinning..." );
284  setTimeout( function () { killSystem( systemStatus ); },1000 );
285  }
286 }
287 
288 function shutdownSystem( systemStatus ) {
289  if ( systemStatus.commandRunning ) {
290  setTimeout( function () { shutdownSystem( systemStatus ); },500 );
291  }
292  else {
293  console.log( "Shutting down system, Partition " + systemStatus.partition );
294  var args = ["shutdown"];
295  startCommand( args,systemStatus );
296  spawn( __dirname + '/cleanupArtdaq.sh',[__dirname,systemStatus.partition] );
297  systemStatus.state = "Shutdown";
298  }
299 
300  setTimeout( function () { killSystem( systemStatus ); },4000 );
301 }
302 
303 function getStatus( systemStatuses,partition ) {
304  var systemStatus = systemStatuses["p" + partition];
305  //console.log("okToStartOnMon is " + systemStatuses["p" + partition + "onmon"]);
306  systemStatus.okToStartOnMon = systemStatuses["p" + partition + "onmon"];
307  while(systemStatuses["p"+partition+"evt"].length > 120) {
308  systemStatuses["p"+partition+"evt"].shift();
309  }
310 
311  checkCommand( systemStatus );
312  checkSystem( systemStatus );
313  if ( fs.existsSync( __dirname + "/../client/P" + systemStatus.partition + "/artdaqdemo_onmon.root" ) ) {
314  var stats = fs.statSync( __dirname + "/../client/P" + systemStatus.partition + "/artdaqdemo_onmon.root" );
315  var statSize = stats.size;
316  if ( statSize !== systemStatus.WFFileSize ) {
317  systemStatus.WFFileSize = statSize;
318  systemStatus.WFPlotsUpdated = Date.now( );
319  console.log( "Plots Updated at " + systemStatus.WFPlotsUpdated );
320  }
321  if ( stats.mtime - systemStatus.WFFileMtime ) {
322  systemStatus.WFFileMtime = stats.mtime;
323  systemStatus.WFPlotsUpdated = Date.now( );
324  console.log( "Plots Updated at " + systemStatus.WFPlotsUpdated );
325  }
326  } else {
327  systemStatus.WFPlotsUpdated = null;
328  }
329  if ( fs.existsSync( __dirname + "/../client/P" + systemStatus.partition + "/out.log" ) ) {
330  systemStatus.systemOutputBuffer = "" + fs.readFileSync( __dirname + "/../client/P" + systemStatus.partition + "/out.log" );
331  }
332  if ( fs.existsSync( __dirname + "/../client/P" + systemStatus.partition + "/err.log" ) ) {
333  systemStatus.systemErrorBuffer = "" + fs.readFileSync( __dirname + "/../client/P" + systemStatus.partition + "/err.log" );
334  }
335  if ( fs.existsSync( __dirname + "/../client/P" + systemStatus.partition + "/comm.out.log" ) ) {
336  systemStatus.commandOutputBuffer = "" + fs.readFileSync( __dirname + "/../client/P" + systemStatus.partition + "/comm.out.log" );
337  }
338  if ( fs.existsSync( __dirname + "/../client/P" + systemStatus.partition + "/comm.err.log" ) ) {
339  systemStatus.commandErrorBuffer = "" + fs.readFileSync( __dirname + "/../client/P" + systemStatus.partition + "/comm.err.log" );
340  }
341  if ( systemStatus.stopPending && !systemStatus.commandRunning ) {
342  systemStatus.state = "Initialized";
343  }
344  systemStatuses["p"+partition] = systemStatus;
345  arc.emit( 'message', {name:"artdaq-runcontrol",data:systemStatus, target:"p"+partition} );
346  //console.log(JSON.stringify(systemStatus));
347  arc.emit( 'end',JSON.stringify( systemStatus ) );
348 }
349 
350 arc.GET_P0 = function ( systemStatuses ) {
351  getStatus( systemStatuses,0 );
352 };
353 
354 arc.GET_P1 = function ( systemStatuses ) {
355  getStatus( systemStatuses,1 );
356 };
357 
358 arc.GET_P2 = function ( systemStatuses ) {
359  getStatus( systemStatuses,2 );
360 };
361 
362 arc.GET_P3 = function ( systemStatuses ) {
363  getStatus( systemStatuses,3 );
364 };
365 
366 arc.RO_GetEvent = function( POST, systemStatuses ){
367  //return systemStatuses["p" + POST.partition].events[POST.event];z
368  //console.log(POST);
369  var events = systemStatuses["p"+POST.partition+"evt"];
370  if(events[0]) {
371  //console.log("First event in buffer: " + events[0].event + ", last event in buffer: " + events[events.length - 1].event + ", Requested event: " + POST.event);
372 
373  if(POST.event == 0 || POST.event < events[0].event) {
374  var data = events[0];
375  //console.log("Sending client event " + events[0].event);
376  data.lastEvent = events[events.length - 1].event;
377  return JSON.stringify(data);
378  } else {
379  for(var event in events) {
380  //console.log(events[event]);
381  //console.log("This event: " + events[event].event + ", requested: " + POST.event);
382  if(events[event].event == POST.event) {
383  //console.log("Sending client event " + events[event].event);
384  var data = events[event];
385  data.lastEvent = events[events.length - 1].event;
386  return JSON.stringify(data);
387  }
388  }
389  if(POST.event < events[events.length -1 ].event) {
390  return "ENOEVT";
391  }
392  }
393  }
394 
395  return "";
396 };
397 
398 arc.RW_GetEvent = function( POST, systemStatuses) {
399  return arc.RO_GetEvent(POST, systemStatuses);
400 };
401 
402 arc.RW_Start = function ( POST,systemStatuses ) {
403  systemStatuses["p" + POST.partition + "evt"] = [];
404  systemStatuses["p" + POST.partition].config = POST.config;
405  if ( systemStatuses["p" + POST.partition].state === "Shutdown" ) {
406  startSystem( systemStatuses["p" + POST.partition] );
407  }
408  getStatus( systemStatuses,POST.partition );
409 };
410 
411 arc.RW_Init = function ( POST,systemStatuses ) {
412  systemStatuses["p" + POST.partition].config = POST.config;
413  if ( systemStatuses["p" + POST.partition].state === "Started" ) {
414  initialize( systemStatuses["p" + POST.partition] );
415  }
416  getStatus( systemStatuses,POST.partition );
417 };
418 
419 arc.RW_Run = function ( POST,systemStatuses ) {
420  systemStatuses["p" + POST.partition].config = POST.config;
421  systemStatuses["p" + POST.partition].runNumber = POST.runNumber;
422  if ( systemStatuses["p" + POST.partition].state === "Initialized" ) {
423  startRun( systemStatuses["p" + POST.partition] );
424  }
425  getStatus( systemStatuses,POST.partition );
426 };
427 
428 arc.RW_Pause = function ( POST,systemStatuses ) {
429  systemStatuses["p" + POST.partition].config = POST.config;
430  if ( systemStatuses["p" + POST.partition].state === "Running" ) {
431  pauseRun( systemStatuses["p" + POST.partition] );
432  }
433  getStatus( systemStatuses,POST.partition );
434 };
435 
436 arc.RW_Resume = function ( POST,systemStatuses ) {
437  systemStatuses["p" + POST.partition].config = POST.config;
438  if ( systemStatuses["p" + POST.partition].state === "Paused" ) {
439  resumeRun( systemStatuses["p" + POST.partition] );
440  }
441  getStatus( systemStatuses,POST.partition );
442 };
443 
444 arc.RW_End = function ( POST,systemStatuses ) {
445  systemStatuses["p" + POST.partition + "onmon"] = false;
446  systemStatuses["p" + POST.partition].config = POST.config;
447  if ( systemStatuses["p" + POST.partition].state === "Running" || systemStatuses["p" + POST.partition].state === "Paused" ) {
448  endRun( systemStatuses["p" + POST.partition] );
449  }
450  getStatus( systemStatuses,POST.partition );
451 };
452 
453 arc.RW_Shutdown = function ( POST,systemStatuses ) {
454  systemStatuses["p" + POST.partition].config = POST.config;
455  if ( systemStatuses["p" + POST.partition].state === "Started" || systemStatuses["p" + POST.partition].state === "Initialized" || systemStatuses["p" + POST.partition].state === "Paused" ) {
456  shutdownSystem( systemStatuses["p" + POST.partition] );
457  }
458  getStatus( systemStatuses,POST.partition );
459 };
460 
461 module.exports = function ( module_holder ) {
462  module_holder["artdaq-runcontrol"] = arc;
463 };