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 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
- if(block > this.block_number) {
186
- // Mark advance to nex block with a blank line.
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: each receiver instance listens to a "channel", being the directory
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 command file
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 command file
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
- // Logs a UI message so it will appear in the log file
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
- // Returns log lines as a single string, and clears the log
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
- // Sets channel path and (optional) call-back script
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, checks with local server whether there is a new command
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: model will NOT be encrypted, so it can be parsed
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
- // Saves the run results in the channel, or signals an error
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 while is not active, report the
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 + '-' + compactClockTime();
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: for experiments, call-back is performed upon completion by
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
- // Deletes the file in the channel directory (to prevent executing it again)
603
- // and activates the call-back script on the local server
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: these functions are adapted versions of those having the same
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
- // "Listens" at the channel, i.e., looks for work to do
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
- // Sets default settings, and then checks the command line arguments
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
- // NOTE: use original argument to preserve upper/lower case
876
- const x = process.argv[i].split('=')[1].split('#');
877
- settings.x_title = x[0];
878
- settings.x_runs = [];
879
- x.splice(0, 1);
880
- // In case of multiple #, interpret them as commas
881
- const r = x.join(',').split(',');
882
- for(let i = 0; i < r.length; i++) {
883
- if(/^\d+$/.test(r[i])) {
884
- settings.x_runs.push(parseInt(r[i]));
885
- } else {
886
- console.log(`WARNING: Invalid run number "${r[i]}"`);
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
- const xi = MODEL.indexOfExperiment(SETTINGS.x_title);
1072
- if(xi < 0) {
1073
- console.log(`WARNING: Unknown experiment "${SETTINGS.x_title}"`);
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
- EXPERIMENT_MANAGER.selectExperiment(SETTINGS.x_title);
1076
- EXPERIMENT_MANAGER.callback = () => {
1077
- const od = model.outputData;
1078
- console.log(od[0]);
1079
- console.log(od[1]);
1080
- VM.callback = null;
1081
- };
1082
- if(SETTINGS.x_runs.length === 0) {
1083
- // Perform complete experiment
1084
- EXPERIMENT_MANAGER.startExperiment();
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
- // Announce, and then perform, only the selected runs
1087
- console.log('Experiment:', SETTINGS.x_title,
1088
- 'Runs:', SETTINGS.x_runs);
1089
- for(let i = 0; i < SETTINGS.x_runs.length; i++) {
1090
- EXPERIMENT_MANAGER.startExperiment(SETTINGS.x_runs[i]);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linny-r",
3
- "version": "1.7.0",
3
+ "version": "1.7.2",
4
4
  "description": "Executable graphical language with WYSIWYG editor for MILP models",
5
5
  "main": "server.js",
6
6
  "scripts": {
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
- // Performs post-processing after run results have been added.
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: when executing a remote command, wait for 1 second to
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: if call-back is successful, the receiver will resume listening
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: clear chart only when done (charts do not update during experiment)
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() {}