linny-r 1.7.0 → 1.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/console.js +150 -89
- package/package.json +1 -1
- package/server.js +1 -1
- package/static/scripts/linny-r-ctrl.js +16 -13
- package/static/scripts/linny-r-gui-paper.js +43 -38
- package/static/scripts/linny-r-gui-receiver.js +5 -5
- package/static/scripts/linny-r-model.js +188 -155
- package/static/scripts/linny-r-utils.js +20 -3
- package/static/scripts/linny-r-vm.js +581 -327
package/console.js
CHANGED
@@ -132,7 +132,6 @@ Possible options are:
|
|
132
132
|
workspace=[path] will create workspace in [path] instead of (main)/user
|
133
133
|
xrun=[title#list] will perform experiment runs in given range
|
134
134
|
(list is comma-separated sequence of run numbers)
|
135
|
-
(FUTURE OPTION)
|
136
135
|
`;
|
137
136
|
|
138
137
|
const SETTINGS = commandLineSettings();
|
@@ -181,14 +180,19 @@ class ConsoleMonitor {
|
|
181
180
|
|
182
181
|
logMessage(block, msg) {
|
183
182
|
// Output a solver message to the console if logging is activated.
|
183
|
+
let new_block = false;
|
184
|
+
if(block > this.block_number) {
|
185
|
+
this.block_number = block;
|
186
|
+
new_block = true;
|
187
|
+
}
|
184
188
|
if(this.show_log) {
|
185
|
-
|
186
|
-
|
187
|
-
console.log('\nBlock #', block);
|
188
|
-
this.block_number = block;
|
189
|
-
}
|
189
|
+
// Mark advance to nex block with a blank line.
|
190
|
+
if(new_block) console.log('\nBlock #', block);
|
190
191
|
console.log(msg);
|
191
192
|
}
|
193
|
+
// Always log solver message to receiver report.
|
194
|
+
if(new_block) RECEIVER.log('Block #' + block, true);
|
195
|
+
RECEIVER.log(msg, true);
|
192
196
|
}
|
193
197
|
|
194
198
|
logOnToServer() {
|
@@ -453,18 +457,19 @@ class ConsoleFileManager {
|
|
453
457
|
|
454
458
|
} // END of class ConsoleFileManager
|
455
459
|
|
456
|
-
// CLASS ConsoleReceiver defines a listener/interpreter for channel commands
|
460
|
+
// CLASS ConsoleReceiver defines a listener/interpreter for channel commands.
|
457
461
|
class ConsoleReceiver {
|
458
462
|
constructor() {
|
459
|
-
// NOTE:
|
460
|
-
// on the local host specified by the modeler
|
463
|
+
// NOTE: Each receiver instance listens to a "channel", being the
|
464
|
+
// directory on the local host specified by the modeler.
|
461
465
|
this.channel = '';
|
462
|
-
// The file name is the name of the first Linny-R model file or
|
463
|
-
// that was found in the channel directory
|
466
|
+
// The file name is the name of the first Linny-R model file or
|
467
|
+
// command file that was found in the channel directory.
|
464
468
|
this.file_name = '';
|
465
|
-
// The name of the experiment to be run can be specified in a
|
469
|
+
// The name of the experiment to be run can be specified in a
|
470
|
+
// command file.
|
466
471
|
this.experiment = '';
|
467
|
-
// The call-back script is the path to file with a shell command
|
472
|
+
// The call-back script is the path to file with a shell command.
|
468
473
|
this.call_back_script = '';
|
469
474
|
this.active = false;
|
470
475
|
this.solving = false;
|
@@ -474,16 +479,16 @@ class ConsoleReceiver {
|
|
474
479
|
}
|
475
480
|
|
476
481
|
setError(msg) {
|
477
|
-
// Record and display error message, and immediately stop listening
|
482
|
+
// Record and display error message, and immediately stop listening.
|
478
483
|
this.error = msg;
|
479
484
|
UI.warn(this.error);
|
480
485
|
this.deactivate();
|
481
486
|
}
|
482
487
|
|
483
|
-
log(msg) {
|
484
|
-
//
|
485
|
-
if(this.active) {
|
486
|
-
if(!msg.startsWith('[')) {
|
488
|
+
log(msg, running=false) {
|
489
|
+
// Log a UI message so it will appear in the log file.
|
490
|
+
if(this.active || running) {
|
491
|
+
if(!(msg.startsWith('[') || running)) {
|
487
492
|
const
|
488
493
|
d = new Date(),
|
489
494
|
now = d.getHours() + ':' +
|
@@ -496,17 +501,17 @@ class ConsoleReceiver {
|
|
496
501
|
}
|
497
502
|
|
498
503
|
get logReport() {
|
499
|
-
//
|
504
|
+
// Return log lines as a single string, and clear the log.
|
500
505
|
const report = this.log_lines.join('\n');
|
501
506
|
this.log_lines.length = 0;
|
502
507
|
return report;
|
503
508
|
}
|
504
509
|
|
505
510
|
activate() {
|
506
|
-
//
|
511
|
+
// Set channel path and (optional) call-back script.
|
507
512
|
this.channel = SETTINGS.channel;
|
508
513
|
this.call_back_script = SETTINGS.callback;
|
509
|
-
// Clear experiment, error message and log
|
514
|
+
// Clear experiment, error message and log.
|
510
515
|
this.experiment = '';
|
511
516
|
this.error = '';
|
512
517
|
this.log_lines.length = 0;
|
@@ -516,7 +521,8 @@ class ConsoleReceiver {
|
|
516
521
|
}
|
517
522
|
|
518
523
|
listen() {
|
519
|
-
// If active,
|
524
|
+
// If active, check whether there is a new command in the channel
|
525
|
+
// directory.
|
520
526
|
if(!this.active) return;
|
521
527
|
const jsr = rcvrListen(this.channel);
|
522
528
|
if(jsr.error) {
|
@@ -525,10 +531,10 @@ class ConsoleReceiver {
|
|
525
531
|
console.log('Receiver deactivated by script');
|
526
532
|
this.deactivate();
|
527
533
|
} else if(jsr.file === '') {
|
528
|
-
// Nothing to do => check again after the set time interval
|
534
|
+
// Nothing to do => check again after the set time interval.
|
529
535
|
setTimeout(() => RECEIVER.listen(), this.interval);
|
530
536
|
} else if(jsr.file && jsr.model) {
|
531
|
-
// NOTE:
|
537
|
+
// NOTE: Model will NOT be encrypted, so it can be parsed.
|
532
538
|
this.file_name = jsr.file;
|
533
539
|
let msg = '';
|
534
540
|
if(!MODEL.parseXML(jsr.model)) {
|
@@ -544,12 +550,12 @@ class ConsoleReceiver {
|
|
544
550
|
if(msg) {
|
545
551
|
this.setError(msg);
|
546
552
|
rcvrReport(this.channel, this.file_name);
|
547
|
-
// Keep listening, so check again after the time interval
|
553
|
+
// Keep listening, so check again after the time interval.
|
548
554
|
setTimeout(() => RECEIVER.listen(), this.interval);
|
549
555
|
} else {
|
550
556
|
this.log('Executing: ' + this.file_name);
|
551
557
|
// NOTE: Virtual Machine will trigger the receiver's reporting
|
552
|
-
// action each time the model has been solved
|
558
|
+
// action each time the model has been solved.
|
553
559
|
if(this.experiment) {
|
554
560
|
this.log('Starting experiment: ' + this.experiment);
|
555
561
|
EXPERIMENT_MANAGER.startExperiment();
|
@@ -561,13 +567,13 @@ class ConsoleReceiver {
|
|
561
567
|
}
|
562
568
|
|
563
569
|
report() {
|
564
|
-
//
|
570
|
+
// Save the run results in the channel, or signal an error.
|
565
571
|
let run = '',
|
566
572
|
rpath = this.channel,
|
567
573
|
file = this.file_name;
|
568
|
-
// NOTE: Always set `solving` to FALSE
|
574
|
+
// NOTE: Always set `solving` to FALSE.
|
569
575
|
this.solving = false;
|
570
|
-
// NOTE: When reporting receiver
|
576
|
+
// NOTE: When reporting while the receiver is not active, report the
|
571
577
|
// results of the running experiment.
|
572
578
|
if(this.experiment || !this.active) {
|
573
579
|
if(MODEL.running_experiment) {
|
@@ -578,20 +584,22 @@ class ConsoleReceiver {
|
|
578
584
|
// NOTE: If receiver is not active, path and file must be set.
|
579
585
|
if(!this.active) {
|
580
586
|
rpath = 'user/reports';
|
587
|
+
// Zero-pad the run number.
|
581
588
|
file = REPOSITORY_BROWSER.asFileName(MODEL.name || 'model') +
|
582
|
-
run
|
589
|
+
(run === '' ? '' : '-' + run.toString().padStart(3, '0')) +
|
590
|
+
`-${compactClockTime()}`;
|
583
591
|
}
|
584
592
|
if(MODEL.solved && !VM.halted) {
|
585
|
-
// Normal execution termination => report results
|
593
|
+
// Normal execution termination => report results.
|
586
594
|
const data = MODEL.outputData;
|
587
595
|
rcvrReport(rpath, file, run, data[0], data[1]);
|
588
|
-
// If execution completed, perform the call-back action
|
589
|
-
// NOTE:
|
590
|
-
// the Experiment Manager
|
596
|
+
// If execution completed, perform the call-back action.
|
597
|
+
// NOTE: For experiments, call-back is performed upon completion by
|
598
|
+
// the Experiment Manager.
|
591
599
|
if(!this.experiment) this.callBack();
|
592
600
|
} else {
|
593
601
|
if(!VM.halted && !this.error) {
|
594
|
-
// No apparent cause => log this irregularity
|
602
|
+
// No apparent cause => log this irregularity.
|
595
603
|
this.setError('ERROR: Unknown solver problem');
|
596
604
|
rcvrAbort();
|
597
605
|
}
|
@@ -599,26 +607,26 @@ class ConsoleReceiver {
|
|
599
607
|
}
|
600
608
|
|
601
609
|
callBack() {
|
602
|
-
//
|
603
|
-
//
|
604
|
-
rcvrCallBack(this.call_back_script);
|
610
|
+
// Run the call-back script (if specified) only when the receiver is
|
611
|
+
// active (so not when its reporting function is called by the VM).
|
612
|
+
if(this.active) rcvrCallBack(this.call_back_script);
|
605
613
|
}
|
606
614
|
|
607
615
|
} // END of class ConsoleReceiver
|
608
616
|
|
609
|
-
// Receiver helper functions
|
610
|
-
// NOTE:
|
617
|
+
// Receiver helper functions.
|
618
|
+
// NOTE: These functions are adapted versions of those having the same
|
611
619
|
// name in file `server.js`; the main difference is that those functions
|
612
|
-
// respond to HTTP requests, whereas now they return objects
|
620
|
+
// respond to HTTP requests, whereas now they return objects.
|
613
621
|
|
614
622
|
function rcvrListen(rpath) {
|
615
|
-
// "
|
623
|
+
// "Listen" at the channel, i.e., look for work to do.
|
616
624
|
let mdl = '',
|
617
625
|
cmd = '';
|
618
626
|
try {
|
619
|
-
// Look for a model file and/or a command file in the channel directory
|
627
|
+
// Look for a model file and/or a command file in the channel directory.
|
620
628
|
const flist = fs.readdirSync(rpath);
|
621
|
-
// NOTE: `flist` contains file names relative to the channel path
|
629
|
+
// NOTE: `flist` contains file names relative to the channel path.
|
622
630
|
for(let i = 0; i < flist.length; i++) {
|
623
631
|
const f = path.parse(flist[i]);
|
624
632
|
if(f.ext === '.lnr' && !mdl) mdl = flist[i];
|
@@ -628,7 +636,7 @@ function rcvrListen(rpath) {
|
|
628
636
|
console.log(err);
|
629
637
|
return {error: `Failed to get file list from ${rpath}`};
|
630
638
|
}
|
631
|
-
// Model files take precedence over command files
|
639
|
+
// Model files take precedence over command files.
|
632
640
|
if(mdl) {
|
633
641
|
try {
|
634
642
|
const data = fs.readFileSync(path.join(rpath, mdl), 'utf8');
|
@@ -645,7 +653,7 @@ function rcvrListen(rpath) {
|
|
645
653
|
console.log(err);
|
646
654
|
return {error: `Failed to read command file ${cmd}`};
|
647
655
|
}
|
648
|
-
// Special command to deactivate the receiver
|
656
|
+
// Special command to deactivate the receiver.
|
649
657
|
if(cmd === 'STOP LISTENING') {
|
650
658
|
return {stop: 1};
|
651
659
|
} else {
|
@@ -655,24 +663,24 @@ function rcvrListen(rpath) {
|
|
655
663
|
r = '',
|
656
664
|
x = '';
|
657
665
|
const m_r = cmd.split('@');
|
658
|
-
// Repository `r` is local host unless specified
|
666
|
+
// Repository `r` is local host unless specified.
|
659
667
|
if(m_r.length === 2) {
|
660
668
|
r = m_r[1];
|
661
669
|
} else if(m_r.length === 1) {
|
662
670
|
r = 'local host';
|
663
671
|
} else {
|
664
|
-
// Multiple occurrences of @
|
672
|
+
// Multiple occurrences of @ are not allowed.
|
665
673
|
return {error: `Invalid command "${cmd}"`};
|
666
674
|
}
|
667
675
|
m = m_r[0];
|
668
|
-
// Module `m` can be prefixed by an experiment title
|
676
|
+
// Module `m` can be prefixed by an experiment title.
|
669
677
|
const x_m = m.split('|');
|
670
678
|
if(x_m.length === 2) {
|
671
679
|
x = x_m[0];
|
672
680
|
m = x_m[1];
|
673
681
|
}
|
674
682
|
// Call the repository helper function `repoLoad` with its callback
|
675
|
-
// function to get the model XML
|
683
|
+
// function to get the model XML.
|
676
684
|
return {
|
677
685
|
file: path.parse(cmd).name,
|
678
686
|
model: repoLoad(r.trim(), m.trim()),
|
@@ -680,12 +688,13 @@ function rcvrListen(rpath) {
|
|
680
688
|
};
|
681
689
|
}
|
682
690
|
} else {
|
683
|
-
// Empty fields will be interpreted as "nothing to do"
|
691
|
+
// Empty fields will be interpreted as "nothing to do".
|
684
692
|
return {file: '', model: '', experiment: ''};
|
685
693
|
}
|
686
694
|
}
|
687
695
|
|
688
696
|
function rcvrAbort() {
|
697
|
+
// Log that receiver actions have been aborted.
|
689
698
|
const log_path = path.join(RECEIVER.channel, RECEIVER.file_name + '-log.txt');
|
690
699
|
fs.writeFile(log_path, RECEIVER.logReport, (err) => {
|
691
700
|
if(err) {
|
@@ -698,6 +707,7 @@ function rcvrAbort() {
|
|
698
707
|
}
|
699
708
|
|
700
709
|
function rcvrReport(rpath, file, run='', data='no data', stats='no statistics') {
|
710
|
+
// Write series data, statistics and log to files.
|
701
711
|
try {
|
702
712
|
let fp = path.join(rpath, file + run + '-data.txt');
|
703
713
|
fs.writeFileSync(fp, data);
|
@@ -725,6 +735,8 @@ function rcvrReport(rpath, file, run='', data='no data', stats='no statistics')
|
|
725
735
|
}
|
726
736
|
|
727
737
|
function rcvrCallBack(script) {
|
738
|
+
// Delete the file in the channel directory (to prevent executing it
|
739
|
+
// again) and activate the call-back script on the local server.
|
728
740
|
let file_type = '',
|
729
741
|
cpath = path.join(RECEIVER.channel, RECEIVER.file_name + '.lnr');
|
730
742
|
try {
|
@@ -778,7 +790,7 @@ function rcvrCallBack(script) {
|
|
778
790
|
//
|
779
791
|
|
780
792
|
function commandLineSettings() {
|
781
|
-
//
|
793
|
+
// Set default settings, and then check the command line arguments.
|
782
794
|
const settings = {
|
783
795
|
cli_name: (PLATFORM.startsWith('win') ? 'Command Prompt' : 'Terminal'),
|
784
796
|
check: false,
|
@@ -786,6 +798,8 @@ function commandLineSettings() {
|
|
786
798
|
preferred_solver: '',
|
787
799
|
report: '',
|
788
800
|
run: false,
|
801
|
+
x_title: '',
|
802
|
+
x_list: false,
|
789
803
|
solver: '',
|
790
804
|
solver_path: '',
|
791
805
|
user_dir: path.join(WORKING_DIRECTORY, 'user'),
|
@@ -872,24 +886,29 @@ function commandLineSettings() {
|
|
872
886
|
// Check is repository exists, etc.
|
873
887
|
// @@@TO DO!
|
874
888
|
} else if(av[0] === 'xrun') {
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
889
|
+
if(!av[1].trim()) {
|
890
|
+
// NOTE: `x_title` = TRUE indicates: list available experiments.
|
891
|
+
settings.x_title = true;
|
892
|
+
} else {
|
893
|
+
// NOTE: use original argument to preserve upper/lower case
|
894
|
+
const x = process.argv[i].split('=')[1].split('#');
|
895
|
+
settings.x_title = x[0].trim();
|
896
|
+
if(!settings.x_title) settings.x_title = true;
|
897
|
+
settings.x_runs = [];
|
898
|
+
x.splice(0, 1);
|
899
|
+
// In case of multiple #, interpret them as commas.
|
900
|
+
const r = (x.length > 0 ? x.join(',').split(',') : []);
|
901
|
+
for(let i = 0; i < r.length; i++) {
|
902
|
+
if(/^\d+$/.test(r[i])) {
|
903
|
+
settings.x_runs.push(parseInt(r[i]));
|
904
|
+
} else {
|
905
|
+
console.log(`WARNING: Invalid run number "${r[i]}"`);
|
906
|
+
}
|
907
|
+
}
|
908
|
+
// If only invalid numbers, do not run the experiment at all.
|
909
|
+
if(r.length > 0 && settings.x_runs.length === 0) {
|
910
|
+
settings.x_runs = false;
|
887
911
|
}
|
888
|
-
}
|
889
|
-
// If only invalid numbers, do not run the experiment at all
|
890
|
-
if(r.length > 0 && settings.x_runs === 0) {
|
891
|
-
console.log(`Experiment "${settings.x_title}" will not be run`);
|
892
|
-
settings.x_title = '';
|
893
912
|
}
|
894
913
|
} else {
|
895
914
|
// Terminate script
|
@@ -899,12 +918,12 @@ function commandLineSettings() {
|
|
899
918
|
}
|
900
919
|
}
|
901
920
|
}
|
902
|
-
// If help is asked for, or command is invalid, show usage and then quit
|
921
|
+
// If help is asked for, or command is invalid, show usage and then quit.
|
903
922
|
if(show_usage) {
|
904
923
|
console.log(usage);
|
905
924
|
process.exit();
|
906
925
|
}
|
907
|
-
// Perform version check only if asked for
|
926
|
+
// Perform version check only if asked for.
|
908
927
|
if(settings.check) checkForUpdates();
|
909
928
|
return settings;
|
910
929
|
}
|
@@ -1068,26 +1087,68 @@ if(SETTINGS.model_path) {
|
|
1068
1087
|
}
|
1069
1088
|
VM.solveModel();
|
1070
1089
|
} else if(SETTINGS.x_title) {
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1090
|
+
if(SETTINGS.x_title === true) {
|
1091
|
+
// List titles of experiments in model.
|
1092
|
+
if(MODEL.experiments.length === 0) {
|
1093
|
+
console.log('NOTE: Model defines no experiments');
|
1094
|
+
} else {
|
1095
|
+
console.log('No experiment specified. Options are:');
|
1096
|
+
for(let i = 0; i < MODEL.experiments.length; i++) {
|
1097
|
+
console.log(`${i+1}. ${MODEL.experiments[i].title}`);
|
1098
|
+
}
|
1099
|
+
}
|
1074
1100
|
} else {
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1101
|
+
// Check whether experiment exists.
|
1102
|
+
let xi = MODEL.indexOfExperiment(SETTINGS.x_title);
|
1103
|
+
// NOTE: Experiments can also be specified by their index number.
|
1104
|
+
if(xi < 0) {
|
1105
|
+
xi = safeStrToInt(SETTINGS.x_title, 0) - 1;
|
1106
|
+
if(xi >= MODEL.experiments.length) xi = -1;
|
1107
|
+
if(xi >= 0) SETTINGS.x_title = MODEL.experiments[xi].title;
|
1108
|
+
}
|
1109
|
+
if(xi < 0) {
|
1110
|
+
console.log(`WARNING: Unknown experiment "${SETTINGS.x_title}"`);
|
1085
1111
|
} else {
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1112
|
+
console.log('Experiment:', SETTINGS.x_title);
|
1113
|
+
EXPERIMENT_MANAGER.selectExperiment(SETTINGS.x_title);
|
1114
|
+
const x = EXPERIMENT_MANAGER.selected_experiment;
|
1115
|
+
if(!x) {
|
1116
|
+
console.log('ERROR: Experiment not found');
|
1117
|
+
return;
|
1118
|
+
}
|
1119
|
+
// NOTE: Only set callback when model does not auto-report runs.
|
1120
|
+
if(!MODEL.report_results) EXPERIMENT_MANAGER.callback = () => {
|
1121
|
+
const od = model.outputData;
|
1122
|
+
console.log(od[0]);
|
1123
|
+
console.log(od[1]);
|
1124
|
+
VM.callback = null;
|
1125
|
+
};
|
1126
|
+
if(SETTINGS.x_runs.length === 0) {
|
1127
|
+
// Perform complete experiment.
|
1128
|
+
EXPERIMENT_MANAGER.startExperiment();
|
1129
|
+
} else {
|
1130
|
+
// Announce, and then perform, only the selected runs.
|
1131
|
+
console.log('Runs:', SETTINGS.x_runs);
|
1132
|
+
for(let i = SETTINGS.x_runs.length - 1; i >= 0; i--) {
|
1133
|
+
const rc = x.combinations[SETTINGS.x_runs[i]];
|
1134
|
+
if(!rc) {
|
1135
|
+
console.log(
|
1136
|
+
'WARNING: For this experiment, run number range is ' +
|
1137
|
+
`[0 - ${x.combinations.length - 1}]`);
|
1138
|
+
return;
|
1139
|
+
}
|
1140
|
+
}
|
1141
|
+
SETTINGS.run_index = 0;
|
1142
|
+
EXPERIMENT_MANAGER.callback = () => {
|
1143
|
+
SETTINGS.run_index++;
|
1144
|
+
if(SETTINGS.run_index < SETTINGS.x_runs.length) {
|
1145
|
+
EXPERIMENT_MANAGER.startExperiment(
|
1146
|
+
SETTINGS.x_runs[SETTINGS.run_index]);
|
1147
|
+
} else {
|
1148
|
+
VM.callback = null;
|
1149
|
+
}
|
1150
|
+
};
|
1151
|
+
EXPERIMENT_MANAGER.startExperiment(SETTINGS.x_runs[0]);
|
1091
1152
|
}
|
1092
1153
|
}
|
1093
1154
|
}
|
package/package.json
CHANGED
package/server.js
CHANGED
@@ -979,7 +979,7 @@ function receiver(res, sp) {
|
|
979
979
|
rcvrAbort(res, rpath, rfile, sp.get('log') || 'NO EVENT LOG');
|
980
980
|
} else if(action === 'report') {
|
981
981
|
let run = sp.get('run');
|
982
|
-
// Zero-pad run number to permit sorting run report file names in sequence
|
982
|
+
// Zero-pad run number to permit sorting run report file names in sequence.
|
983
983
|
run = (run ? '-' + run.padStart(3, '0') : '');
|
984
984
|
let data = sp.get('data') || '',
|
985
985
|
stats = sp.get('stats') || '',
|
@@ -1291,6 +1291,8 @@ class ExperimentManager {
|
|
1291
1291
|
// Only now compute the simulation run time (number of time steps)
|
1292
1292
|
xr.time_steps = MODEL.end_period - MODEL.start_period + 1;
|
1293
1293
|
VM.callback = this.callback;
|
1294
|
+
// NOTE: Asynchronous call. All follow-up actions must be performed
|
1295
|
+
// by the callback function.
|
1294
1296
|
VM.solveModel();
|
1295
1297
|
}
|
1296
1298
|
}
|
@@ -1317,11 +1319,11 @@ class ExperimentManager {
|
|
1317
1319
|
}
|
1318
1320
|
|
1319
1321
|
processRestOfRun() {
|
1320
|
-
//
|
1322
|
+
// Perform post-processing after run results have been added.
|
1321
1323
|
const x = MODEL.running_experiment;
|
1322
1324
|
if(!x) return;
|
1323
1325
|
const aci = x.active_combination_index;
|
1324
|
-
// Always add solver messages
|
1326
|
+
// Always add solver messages.
|
1325
1327
|
x.runs[aci].addMessages();
|
1326
1328
|
const n = x.combinations.length;
|
1327
1329
|
if(!VM.halted && aci < n - 1 && aci != x.single_run) {
|
@@ -1332,8 +1334,8 @@ class ExperimentManager {
|
|
1332
1334
|
} else {
|
1333
1335
|
x.active_combination_index++;
|
1334
1336
|
let delay = 5;
|
1335
|
-
// NOTE:
|
1336
|
-
// allow enough time for report writing
|
1337
|
+
// NOTE: When executing a remote command, wait for 1 second to
|
1338
|
+
// allow enough time for report writing.
|
1337
1339
|
if(RECEIVER.active && RECEIVER.experiment) {
|
1338
1340
|
UI.setMessage('Reporting run #' + (x.active_combination_index - 1));
|
1339
1341
|
delay = 1000;
|
@@ -1354,8 +1356,8 @@ class ExperimentManager {
|
|
1354
1356
|
`Experiment <em>${x.title}</em> terminated during run #${aci}`);
|
1355
1357
|
RECEIVER.deactivate();
|
1356
1358
|
}
|
1357
|
-
// No more runs => stop experiment, and perform call-back
|
1358
|
-
// NOTE:
|
1359
|
+
// No more runs => stop experiment, and perform call-back.
|
1360
|
+
// NOTE: If call-back is successful, the receiver will resume listening.
|
1359
1361
|
if(RECEIVER.active) {
|
1360
1362
|
RECEIVER.experiment = '';
|
1361
1363
|
RECEIVER.callBack();
|
@@ -1365,34 +1367,35 @@ class ExperimentManager {
|
|
1365
1367
|
MODEL.parseSettings(x.original_model_settings);
|
1366
1368
|
MODEL.round_sequence = x.original_round_sequence;
|
1367
1369
|
// Reset the Virtual Machine so t=0 at the status line,
|
1368
|
-
// and ALL expressions are reset as well
|
1370
|
+
// and ALL expressions are reset as well.
|
1369
1371
|
VM.reset();
|
1370
1372
|
this.readyButtons();
|
1371
1373
|
}
|
1372
1374
|
this.drawTable();
|
1373
|
-
// Reset the model, as results of last run will be showing still
|
1375
|
+
// Reset the model, as results of last run will be showing still.
|
1374
1376
|
UI.resetModel();
|
1375
1377
|
CHART_MANAGER.resetChartVectors();
|
1376
|
-
// NOTE:
|
1378
|
+
// NOTE: Clear chart only when done; charts do not update when an
|
1379
|
+
// experiment is running.
|
1377
1380
|
if(!MODEL.running_experiment) CHART_MANAGER.updateDialog();
|
1378
1381
|
}
|
1379
1382
|
|
1380
1383
|
stopExperiment() {
|
1381
|
-
// Interrupt solver but retain data on server (and no resume)
|
1384
|
+
// Interrupt solver but retain data on server (and no resume).
|
1382
1385
|
VM.halt();
|
1383
1386
|
}
|
1384
1387
|
|
1385
1388
|
showProgress(ci, p, n) {
|
1386
|
-
// Report progress on the console
|
1389
|
+
// Report progress on the console.
|
1387
1390
|
console.log('\nRun', ci, `(${p}% of ${n})`);
|
1388
1391
|
}
|
1389
1392
|
|
1390
1393
|
resumeButtons() {
|
1391
|
-
// Console experiments cannot be paused, and hence not resumed
|
1394
|
+
// Console experiments cannot be paused, and hence not resumed.
|
1392
1395
|
return false;
|
1393
1396
|
}
|
1394
1397
|
|
1395
|
-
// Dummy methods: actions that are meaningful only for the graphical UI
|
1398
|
+
// Dummy methods: actions that are meaningful only for the graphical UI.
|
1396
1399
|
drawTable() {}
|
1397
1400
|
readyButtons() {}
|
1398
1401
|
pausedButtons() {}
|