1#![doc(html_logo_url = "https://images.squarespace-cdn.com/content/v1/60416644b68a5453a868e856/1628206791960-M61IU1QP850NRLI9CE07/Stanhope+Etching.jpg?format=2500w")]
2mod read_csv;
14mod read_ebml;
15mod write_ebml;
16mod write_html;
17mod write_script;
18mod write_webmenu;
19
20use std::{
22 process::Command,
23 fs::{self},
26 io::{self, Error, ErrorKind, Result},
27 path::Path,
28};
29use chrono::prelude::*;
30use read_ebml::Process;
31
32use clap::Parser; use read_csv::read_csv; use write_ebml::get_process_folder_name;
35use write_ebml::write_ebml;
36use read_ebml::read_ebml;
37use write_html::generate_complete_html;
38use write_webmenu::generate_complete_webmenu;
39use glob::glob;
40
41
42#[derive(Parser, Debug)]
44#[command(version, about =
45"\n\n\x1b[1;30;47mStanope\x1b[0m\x1b[30;47m, the Easy Button process generation engine.\x1b[0m
46
47Arguments passed into the options should be surrounded by single or double quotes, e.g.
48% ./stanhope -p \"EB-WI-0010\" \x1b[36m<< double quotes are accepted\x1b[0m
49% ./stanhope -a 'EB-WI-*' \x1b[36m<< single quotes are accepted\x1b[0m",
50author = "Strativus Group <contact@strativusgroup.com>",
51long_about = None,
52after_help =
53"
54")]
55struct StanhopeArgs {
56 #[arg(short, long, value_name = "CSV-FILE", default_value_t = String::from(""), verbatim_doc_comment)]
70 listgen: String,
71
72 #[arg(short, long, default_value_t = false, verbatim_doc_comment)]
76 webmenu: bool,
77
78 #[arg(short, long, default_value_t = false, verbatim_doc_comment)]
82 graphics_audit: bool,
83
84 #[arg(short, long, default_value_t = false, verbatim_doc_comment)]
88 filename_audit: bool,
89
90 #[arg(short, long, value_name = "DOCUMENT-NUMBER", default_value_t = String::from(""), verbatim_doc_comment)]
96 process_ebml: String,
97
98 #[arg(short, long, use_value_delimiter = true, value_delimiter = ' ', num_args = 3, value_names = ["DOCUMENT-NUMBER","SCRIPT-FORMAT","PAUSE-AFTER-EACH"], verbatim_doc_comment)]
108 scriptify_process: Vec<String>,
109
110 #[arg(short, long, value_name = "DOCUMENT-NUMBER", default_value_t = String::from(""), verbatim_doc_comment)]
118 archive_process: String,
119
120 #[arg(short, long, value_name = "DOCUMENT-NUMBER", default_value_t = String::from(""), verbatim_doc_comment)]
126 inspect_process: String,
127
128 #[arg(short, long, value_name = "DOCUMENT-NUMBER", default_value_t = String::from(""), verbatim_doc_comment)]
134 cleanup_previous: String,
135
136 #[arg(short, long, default_value_t = false,)]
138 verbose: bool,
139
140 #[arg(short, long, default_value_t = false,)]
142 ebml_help: bool,
143}
144
145fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
147 fs::create_dir_all(&dst)?;
148 for entry in fs::read_dir(src)? {
149 let entry = entry?;
150 let ty = entry.file_type()?;
151 if ty.is_dir() {
152 copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
153 } else {
154 fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
155 }
156 }
157 Ok(())
158}
159
160fn copy_files_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
162 fs::create_dir_all(&dst)?;
163 for entry in fs::read_dir(src)? {
164 let entry = entry?;
165 let ty = entry.file_type()?;
166 if ty.is_dir() {
167 ();
168 } else {
169 fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
170 }
171 }
172 Ok(())
173}
174
175fn archive_single_process(new_proc: Process, verbose: &bool) {
177 let previous_folder = get_process_folder_name(&new_proc).to_owned() + "/previous";
180 match Path::new(&previous_folder).exists() {
181 true => (),
182 false => { let _ = fs::create_dir(&previous_folder); },
183 };
184 let root_assets_folder = "./assets";
185 let previous_assets_folder = previous_folder.clone() + "/assets";
186 match Path::new(&previous_assets_folder).exists() {
187 true => (),
188 false => { let _ = fs::create_dir(&previous_assets_folder); },
189 };
190
191 let rev_string = new_proc.get_revision();
193 let right_now_string = Utc::now().format("UTC-%Y-%m-%d-T%H-%M-%S").to_string();
194 let timestamp_folder = previous_folder.clone() + "/Rev_" + new_proc.get_revision() + "_" + &right_now_string;
195 if *verbose {
196 println!("Revision: {}",&rev_string);
197 println!("SystemTime::now() ...ish: {}",right_now_string);
198 println!("Trying to create this folder: {:?}",timestamp_folder);
199 }
200 let _ = fs::create_dir(×tamp_folder);
201 let _ = copy_dir_all(root_assets_folder,previous_assets_folder);
203 let _ = copy_files_all(get_process_folder_name(&new_proc),timestamp_folder.clone());
205}
206
207fn list_directories_to_cleanup(prev:String) -> Vec<String> {
218
219 let mut all_revs:Vec<String> = vec![];
220 let mut directories_to_cleanup:Vec<String> = vec![];
221 let all_archives = glob(&(prev.clone() + "/Rev_*_UTC-*-*-*-T*-*-*")).expect("Failed to read glob pattern");
222
223 for entry in all_archives {
224 match entry {
225 Ok(path) => {
226 let archive = path.file_name().expect("huh?").to_str().expect("huh?");
227 let pos = archive.find("_UTC-").expect("rust is hard");
228 let rev = String::from(&archive[4..pos]);
229 all_revs.push(rev);
230 },
231 Err(e) => println!("{:?}",e),
232 }
233 }
234
235 all_revs.dedup();
236 println!("For this process, these are all the revisions: {:?}",&all_revs);
237
238 for rev in all_revs {
239 println!("\nHere are all of the archive folders for Rev {}:",rev);
240 let these = glob(&(prev.clone() + "/Rev_" + &rev + "_UTC-*-*-*-T*-*-*")).expect("Failed to read glob pattern");
241 let mut vec:Vec<String> = vec![];
242 for this in these {
243 match this {
244 Ok(path) => vec.push(path.file_name().expect("huh?").to_str().expect("huh?").to_string()),
245 Err(e) => println!("{:?}",e),
246 }
247 }
248 vec.sort_by(|a, b| a.to_lowercase().cmp(&b.to_lowercase()));
249 let keeper = vec.pop().expect("pop?");
250 for d in &vec {
251 println!(" {} \x1b[93mDELETE\x1b[0m",&d);
252 }
253 println!(" 💾 {} 💾 << only one to be saved...",&keeper);
254 directories_to_cleanup.append(&mut vec);
255 }
256 directories_to_cleanup
257}
258
259pub fn globify_document_number(input:&String) -> Vec<String> {
261 let all_results = match input.chars().last().unwrap() {
262 '*' => glob(&(input.to_owned())).expect("Failed to read glob pattern"),
263 _ => glob(&(input.to_owned()+"*")).expect("Failed to read glob pattern"),
264 };
265 let mut matching_process_folders:Vec<String> = vec![];
266
267 for entry in all_results {
268 match entry {
269 Ok(path) => {
270 let process_folder = path.file_name().expect("huh?").to_str().expect("huh?");
271 matching_process_folders.push(String::from(process_folder));
272 },
273 Err(e) => println!("{:?}",e),
274 }
275 }
276 matching_process_folders
277}
278
279fn attempt_fallbacks(fallbacks: Vec<Box<dyn Fn() -> Result<i32>>>) -> Result<i32> {
281 let mut last_error = None;
282
283 for fallback in fallbacks {
284 match fallback() {
285 Ok(value) => return Ok(value),
286 Err(e) => {
287 println!("Call failed, trying next... (Error: {})", e);
288 last_error = Some(e);
289 }
290 }
291 }
292
293 Err(last_error.unwrap_or_else(|| Error::new(ErrorKind::Other, "No fallbacks provided")))
294}
295
296fn print_graphics_audit_to_stdout(v:&bool) {
306 let list_of_process_folders = write_webmenu::find_all_process_folders(".");
307 print_title_block_to_stdout("Library Graphics Audit");
309 for folder in list_of_process_folders {
310 let folder_string = folder.file_name().expect("No bueno?").to_str().expect("Really bueno?").to_string();
311 let new_ebml_str = folder_string.clone() + "/" + &folder_string + ".ebml";
312 let new_process = read_ebml(&("./".to_owned()+&new_ebml_str));
313
314 if new_process.get_missing_image_count() == 0 {
315 println!(" [ No missing images! ] {}",&folder_string);
316 } else {
317 println!(" ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ {}",&folder_string);
318 }
319 if new_process.get_missing_image_count() > 0 {
320 println!(" ▒▒▒▒▒▒▒▒▒▒▒ -> Missing Images");
321 for image in new_process.get_missing_images() {
322 println!(" ▒▒▒▒▒▒▒▒ {}",image);
323 }
324 }
325 if *v {
326 if new_process.get_all_images().len() > 0 {
327 println!(" -> All Images");
328 for image in new_process.get_all_images() {
329 println!(" {}",image.0);
330 }
331 } else { println!(" -> No images at all, in fact..."); }
332 }
333 }
334}
335
336fn print_filename_audit_to_stdout(v:&bool) {
349 let list_of_process_folders = write_webmenu::find_all_process_folders(".");
350 print_title_block_to_stdout("Library Filename Audit");
351 for folder in list_of_process_folders {
352 let expected_folder_string = folder.file_name().expect("No bueno?").to_str().expect("Really bueno?").to_string();
353 let expected_ebml_str = expected_folder_string.clone() + "/" + &expected_folder_string + ".ebml";
354 println!("{}→ {}",if *v {"\n"}else{""},expected_folder_string);
355 match Path::new(&expected_ebml_str).exists() {
356 true => {
357 if *v { println!(" ↳ {}",expected_ebml_str); }
358 let new_proc = read_ebml(&("./".to_owned()+&expected_ebml_str));
359 if *v { println!(" ↳ EBML file exists as named above."); }
360 if *v { println!(" ↳ EBML Contents 'Number' : {}",new_proc.get_number()); }
361 if *v { println!(" ↳ EBML Contents 'Title' : {}",new_proc.get_title()); }
362 if expected_folder_string == (new_proc.get_number().to_owned() + " - " + new_proc.get_title()).to_string() {
363 if *v { println!(" ↳ CLEAN AUDIT: EBML CONTENTS MATCH FOLDER AND FILE NAMES"); }
364 } else {
365 println!(" ↳ \x1b[31mWARNING: SOMETHING IS MISMATCHED BETWEEN FOLDER NAME, FILE NAME, AND EBML CONTENTS\x1b[0m");
366 println!(" ↳ \x1b[33mSpecial characters in EBML title converted to '_' in folder/file names might be OK\x1b[0m");
367 }
368 },
369 false => {
370 println!(" ↳ \x1b[31m{}\x1b[0m",expected_ebml_str);
371 println!(" ↳ \x1b[31mWARNING: LIKELY FOLDER NAME MISMATCH WITH EBML NAME\x1b[0m");
372 },
373 };
374 }
375}
376
377fn print_title_block_to_stdout(s:&str) {
384 let to_print = s.trim();
385 print!("▛▀"); print!("{:▀<1$}", "", to_print.len()); print!("▀▜\n");
386 print!("▌ "); print!("{}",&to_print); print!(" ▐\n");
387 print!("▙▄"); print!("{:▄<1$}", "", to_print.len());print!("▄▟\n");
388}
389
390fn main() {
402
403 let args = StanhopeArgs::parse();
404
405 if !(&args.listgen=="") {
408
409 let needed_processes_list = read_csv(&args.listgen,args.verbose.clone());
411 if args.verbose { println!("Stanhope found {} EBML files to create in the CSV file.",needed_processes_list.len()); }
412 for new_proc_to_create in needed_processes_list {
414 if args.verbose { new_proc_to_create.display_process_to_stdout() };
415 write_ebml(&new_proc_to_create,args.verbose.clone());
416 }
417
418 }
419
420 if !(args.scriptify_process.len()==0) {
423 let matching_processes = globify_document_number(&args.scriptify_process[0]);
425 if args.verbose { println!("{:?}",&matching_processes) };
426
427 if matching_processes.len() > 0 {
428
429 for process in matching_processes {
430
431
432 let file_to_read = "./".to_owned() + &process + "/" + &process + ".ebml";
433
434 if args.verbose {
435 println!("");
436 println!("=== Stanhope Scriptification =========================================================");
437 println!("===");
438 println!("=== >> Process: {}",&process);
439 println!("=== >> Script Format: {}",&args.scriptify_process[1]);
440 println!("=== >> Pause after each? {}",&args.scriptify_process[2]);
441 println!("===");
442 println!("======================================================================================");
443 println!("");
444 }
445
446 if args.verbose { print!("[A] Attempting to read EBML -> {} ...",&file_to_read); }
447 let new_proc = read_ebml(&file_to_read);
448 if args.verbose { print!("success!\n"); }
449
450 let all_commands = new_proc.get_all_command_lines();
451 if args.verbose { print!("[B] Stanhope found {} lines of type \"Command\" in the file.\n",all_commands.len()); }
452
453 if args.verbose { print!("[C] Processing script format \"{}\"\n",&args.scriptify_process[1]); }
454
455
456 let wait:bool = match args.scriptify_process[2].to_uppercase().as_str() {
457 "F" | "FALSE" | "N" | "NO" => false,
458 _ => true,
459 };
460 write_script::script_file_from_process(new_proc,write_script::learn(&args.scriptify_process[1]),wait)
461
462 } }; } if !(&args.process_ebml=="") {
469 let matching_processes = globify_document_number(&args.process_ebml);
471 if args.verbose { println!("{:?}",&matching_processes) };
472
473 if matching_processes.len() > 0 {
474
475 for process in matching_processes {
476
477 let file_to_read = "./".to_owned() + &process + "/" + &process + ".ebml";
478 if args.verbose { println!("Attempting to process this file: {}",&file_to_read) };
479
480 let new_proc = read_ebml(&file_to_read);
481 if args.verbose { new_proc.display_process_to_stdout() };
482
483 if args.verbose { println!("Attempting to delete old HTML file: {:?}",fs::remove_file(&file_to_read.replace(".ebml",".html"))); } else { let _ = fs::remove_file(&file_to_read.replace(".ebml",".html")); }
485
486 generate_complete_html(&String::from(&file_to_read.replace(".ebml",".html")),&new_proc);
487
488 let win_chrome_path = "& 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe' --headless --no-pdf-header-footer --print-to-pdf=\"";
490 let win_process_library = "$PWD\\";
491 let win_chrome_string_to_throw = &(win_chrome_path.to_string() + win_process_library + &file_to_read.replace(".ebml",".pdf") + "\" \"" + win_process_library + &file_to_read.clone().replace(".ebml",".html\""));
492 let posix_chrome = "chrome --headless --print-to-pdf=\"".to_string() + &file_to_read.clone().replace(".ebml",".pdf") + "\" \"" + &file_to_read.clone().replace(".ebml",".html") + "\" --no-pdf-header-footer";
494 let posix_chromium = "chromium --headless --print-to-pdf=\"".to_string() + &file_to_read.clone().replace(".ebml",".pdf") + "\" \"" + &file_to_read.clone().replace(".ebml",".html") + "\" --no-pdf-header-footer";
496
497 let _output = if cfg!(target_os = "windows") {
498
499 let mut fallbacks: Vec<Box<dyn Fn() -> Result<i32>>> = Vec::new();
500 fallbacks.push(Box::new(move || {
501
502 if args.verbose { println!("Here's my command string to chrome:\n\n{}\n\n",win_chrome_string_to_throw); }
503 Command::new("powershell.exe")
504 .args(["-Command", win_chrome_string_to_throw])
505 .output()
506 .expect("failed to execute process");
507
508 Err(Error::new(ErrorKind::Other, "Chrome call to OS attempted."))
509 }));
510
511 } else {
512
513 let mut fallbacks: Vec<Box<dyn Fn() -> Result<i32>>> = Vec::new();
515 fallbacks.push(Box::new(move || {
516
517 Command::new("sh")
518 .arg("-c")
519 .arg(&(posix_chrome))
520 .output()
521 .expect("failed to execute process");
522
523 Err(Error::new(ErrorKind::Other, "Chrome call to OS attempted."))
524 }));
525 fallbacks.push(Box::new(move || {
526
527 Command::new("sh")
528 .arg("-c")
529 .arg(&(posix_chromium))
530 .output()
531 .expect("failed to execute process");
532
533 Err(Error::new(ErrorKind::Other, "Chromium call to OS attempted."))
534 }));
535 let _result = attempt_fallbacks(fallbacks);
536
537 };
538
539 archive_single_process(new_proc,&args.verbose);
540
541 } } } if !(&args.archive_process=="") {
548 let matching_processes = globify_document_number(&args.archive_process);
550 if args.verbose { println!("{:?}",&matching_processes) };
551
552 if matching_processes.len() > 0 {
553
554 for process in matching_processes {
555
556 let file_to_read = "./".to_owned() + &process + "/" + &process + ".ebml";
557
558 let new_proc = read_ebml(&file_to_read);
559 archive_single_process(new_proc,&args.verbose);
560 }
561 }
562 }
563
564 if !(&args.cleanup_previous=="") {
567 let matching_processes = globify_document_number(&args.cleanup_previous);
569 if args.verbose { println!("{:?}",&matching_processes) };
570
571 if matching_processes.len() > 0 {
572
573 for process in matching_processes {
574
575 let prevs = "./".to_owned() + &process + "/previous";
576
577 let delete_these = list_directories_to_cleanup(prevs);
578
579 for dir in delete_these {
580 println!("Attempting to delete {} ... ",&dir);
581 let delete_this = "./".to_owned() + &process + "/previous/" + &dir;
582 let _ = fs::remove_dir_all(delete_this);
583 }
584 }
585 }
586 }
587
588 if !(&args.inspect_process=="") {
591 let matching_processes = globify_document_number(&args.inspect_process);
593 if args.verbose { println!("{:?}",&matching_processes) };
594
595 if matching_processes.len() > 0 {
596
597 for process in matching_processes {
598
599 let file_to_read = "./".to_owned() + &process + "/" + &process + ".ebml";
600
601 let new_proc = read_ebml(&file_to_read);
602 new_proc.display_process_to_stdout();
603 }
604 }
605 }
606
607 if args.webmenu {
610 generate_complete_webmenu(&args.verbose);
611 }
612
613 if args.graphics_audit {
616 print_graphics_audit_to_stdout(&args.verbose);
617 }
618
619 if args.filename_audit {
622 print_filename_audit_to_stdout(&args.verbose);
623 }
624
625 if args.ebml_help {
628 println!("{}",format!("
629
630Note: Process files passed into --process-ebml are specifically-formatted EBML files (Easy Button Markup Language).
631
632\x1b[36m
633=================================================
634== Easy Button Markup Language Cheat Sheet ==
635== ==
636== Everything below this block is valid EBML ==
637=================================================
638\x1b[0m
639
640// <COMMENT>
641Template | <CSS FILENAME FOUND IN ASSETS FOLDER>
642
643Title | <PROCESS TITLE>
644Number | <PROCESS NUMBER>
645Author | <PROCESS AUTHOR>
646Reviewer | <PROCESS REVIEWER>
647Process Type | <CATEGORY>, e.g. Test Procedure
648
649Subject | <THING TO WHICH THE PROCESS APPLIES>
650Subject Image | <FILENAME OF IMAGE OF SUBJECT>
651Product | <THING PRODUCED BY THIS PROCESS> - optional
652Product Image | <FILENAME OF IMAGE OF PRODUCT> - optional
653
654Revision | <LATEST REV NUM> | <DESCRIPTION OF CHANGE>
655Revision | <INTERMEDIATE REV NUM> | <DESCRIPTION OF CHANGE>
656Revision | <FIRST REV NUM> | <INITIAL DESCTIPTION>
657
658Section | <TITLE OF SECTION>
659 Step | <TITLE OF STEP>
660
661 // These all must occur within a STEP
662
663 Context | <PLAIN TEXT STATEMENTS FOR CONTEXT>
664 Command | <VERBATIM COMPUTER CODE>
665 Warning | <BIG BOLD TEXT TO CAPTURE ATTENTION>
666 Image | <FILENAME> | <CAPTION TEXT>
667 Resource | <THING YOU NEED TO COMPLETE THIS STEP> | <CALIBRATED?>
668 Objective | <ACHIEVED PURPOSE OF THIS PROCESS>
669 Out of Scope | <NOTE ABOUT WHAT NOT TO DO HERE>
670
671 // Verification methods can be single letter: A, I, D, T, S
672 // They can also be full words: Analysis, Inspection, Demonstration, Test, Sampling
673 Verification | <REQUIREMENT ID> | <FULL TEXT OF REQUIREMENT> | <VERIFICATION METHOD>
674
675 // Action lines can be grouped together to form consecutive table rows
676 // To optionally include Two-Party Verification, <TPV> can be \"TPV\"
677 // To NOT make an action Two-Party Verified, stop at the <EXPECTED RESULT>
678 Action | <THING TO DO> | <EXPECTED RESULT> | <TPV?> - optional
679 Action | <THING TO DO> | <EXPECTED RESULT> | <TPV?> - optional
680
681// You can reference a different process's section and stanhope will fetch it for you
682Section Reference | <OTHER PROCESS> | <TITLE OF SECTION>
683
684
685// Alternate Aliases for EBML lines are below
686// Everything here continues to be valid EBML
687// Note: the EBML line designators (left of the first pipe) are NOT case sensitive
688// 'section' is the same as 'SECTION'
689// 'rev' is the same as 'REV'
690
691REV | C | 'Revision' has a new alias!
692rev | B | EBML line indicators are NOT case sensitive!
693ReV | A | I mean, you *could* do that...
694Rev | - | This is revision 'dash'
695
696SEC | Section Alias 'SEC' (or 'sec' etc.) works!
697 STP | Step Alias 'STP' (or 'stp' etc.) works!
698
699 // 'Context' has several new aliases
700 Comment | Context alias
701 TXT | Context alias
702 CMT | Context alias, perhaps short for 'comment'
703
704 // 'Command' has a new alias
705 CMD | Command alias
706 > | Command alias
707 % | Command alias
708 $ | Command alias
709 # | Command alias
710
711 // 'Image' has a new alias
712 IMG | image.ext | This caption appears under the image
713 PICTURE | image.ext | This caption appears under the image
714 PIC | image.ext | This caption appears under the image
715 Figure | image.ext | This caption appears under the image
716 FIG | image.ext | This caption appears under the image
717
718 // 'Action' has a new alias
719 // The Two-Party Verification flag has some aliases as well
720 DO | Do this action | Expect this result
721 DO | Do this action | Expect this result | TPV
722 DO | Do this action | Expect this result | T
723 DO | Do this action | Expect this result | TRUE
724 DO | Do this action | Expect this result | Y
725 DO | Do this action | Expect this result | YES
726 DO | Do this action | Expect this result | Two Party Verification
727 DO | Do this action | Expect this result | Two-Party Verification
728 // That makes an eight-row Action table, with TPVs for the last seven actions
729
730 // 'Warning' has new aliases
731 WARN | Warning text
732 WRN | Warning text
733 WAR | Warning text
734 Alert | Warning text
735 ! | Warning text
736
737 // 'Verification' has new aliases
738 VER | ReqID | Verification Text | Method
739 REQUIREMENT | ReqID | Verification Text | Method
740 REQ | ReqID | Verification Text | Method
741 // The methods of verification have a few aliases as well
742 REQ | ReqID | Verification Text | Demonstration
743 REQ | ReqID | Verification Text | Demo
744 REQ | ReqID | Verification Text | D
745 REQ | ReqID | Verification Text | Inspection
746 REQ | ReqID | Verification Text | I
747 REQ | ReqID | Verification Text | Analysis
748 REQ | ReqID | Verification Text | A
749 REQ | ReqID | Verification Text | Sampling
750 REQ | ReqID | Verification Text | Sample
751 REQ | ReqID | Verification Text | S
752 REQ | ReqID | Verification Text | Test
753 REQ | ReqID | Verification Text | T
754
755 // 'Resource' has a new alias
756 // There are several ways to indicate if a resource needs calibration, too!
757 RES | Name of resource
758 RES | Name of calibrated resource | C
759 RES | Name of calibrated resource | CAL
760 RES | Name of calibrated resource | CALIB
761 RES | Name of calibrated resource | CALIBRATE
762 RES | Name of calibrated resource | CALIBRATED
763 RES | Name of calibrated resource | CALIBRATION
764 RES | Name of calibrated resource | Y
765 RES | Name of calibrated resource | YES
766 RES | Name of calibrated resource | T
767 RES | Name of calibrated resource | TRUE
768
769 // 'Objective' has a new alias
770 OBJ | Objective achieved!
771
772 // 'Out of Scope' has a new alias
773 OOS | Description of what is not in this process scope
774
775// 'Section Reference' has a new alias
776SECREF | Process-Number | Title of Section to Fetch
777
778\x1b[36m
779=================================================
780== Everything above this block is valid EBML ==
781== ==
782== Easy Button Markup Language Cheat Sheet ==
783=================================================
784\x1b[0m
785
786For extensive examples of EBML in action, see https://stanhope.strativusgroup.com/latest/stylesheetviewer.html"));
787 }
788
789}