1import {dialog, invoke} from "@tauri-apps/api";
2import JSON2XML from "./json2xml"
3import {OpenDialogOptions} from "@tauri-apps/api/dialog";
4import _ from "lodash";
5import {vueUtils} from "@lljj/vue3-form-naive";
6
7function all(arr: boolean[]): boolean {
8    return arr.every(element => element === true);
9}
10
11function count(source, target) {
12    return (source.match(new RegExp(target, 'g')) || []).length;
13}
14
15
16declare global {
17    interface Window {
18        configurator: Configurator;
19        getSchemaData: () => any;
20        getCurrentScenarioData: () => any;
21        getBoardData: () => any;
22        pyodide: {
23            pyimport: (name: string) => { main: (...any) => any },
24            runPython: (code: string) => string
25        };
26    }
27}
28
29enum HistoryTypeEnum {
30    WorkingFolder,
31    Board,
32    Scenario
33}
34
35export type HistoryTypes = keyof typeof HistoryTypeEnum;
36
37
38enum PolicyTypeEnum {
39    Unified,
40    Code,
41    Data
42}
43
44type PolicyType = keyof typeof PolicyTypeEnum
45
46type Policy = {
47    VM: string,
48    VCPU: number,
49    TYPE: PolicyType,
50    CLOS_MASK: string
51}
52
53type CATDBRecord = {
54    CACHE_LEVEL: number,
55    CACHE_ID: string,
56    META: { vmid: number },
57    VM: string,
58    VCPU: number,
59    TYPE: PolicyType,
60    CLOS_MASK: string
61}
62
63type CATUIDataObject = {
64    errorMsg: string,
65    regions: {
66        level: number,
67        id: string,
68        capacity_mask_length: number,
69        type: string,
70        cache_size: number,
71        processors: number[],
72        data: {
73            RTCore: Policy[],
74            Standard: Policy[],
75            VCAT: Policy[],
76        },
77    }[],
78    summary: {
79        [CATRegionLevel: string]: {
80            count: number,
81            [CATRegionID: string]: number
82        }
83    }
84}
85
86
87type vmID = number;
88
89class CAT {
90    private scenario: any;
91    private schemaData: any;
92    private CAT_REGION_INFO: any;
93
94    private switches: {
95        RDT_ENABLED: boolean,
96        CDP_ENABLED: boolean,
97        VCAT_ENABLED: boolean
98    };
99
100    private preLaunchedVMCPUs: string[];
101    private serviceVM: any;
102    private serviceVMCPUs: string[];
103
104    public CATDB: CATDBRecord[];
105    private vmIDs: { [vmName: string]: vmID };
106
107    hexToRange(hexValue, maxValue) {
108        let str_bin = Number.parseInt(hexValue).toString(2);
109        let block_length = str_bin.length;
110        let block_enabled_length = count(str_bin, "1");
111
112        let start: number
113        let end: number
114
115        if (block_length > maxValue) {
116            if (block_enabled_length >= maxValue) {
117                str_bin = "1".repeat(maxValue);
118            } else {
119                str_bin = "0".repeat(maxValue - block_enabled_length) + "1".repeat(block_enabled_length);
120            }
121        } else {
122            if (block_length < maxValue) {
123                str_bin = "0".repeat(maxValue - block_length) + str_bin;
124            }
125        }
126
127        start = str_bin.indexOf("1") !== -1 ? str_bin.indexOf("1") : 0;
128        end = start + count(str_bin, "1");
129
130        return [start, end]
131    }
132
133    rangeToHex(value, max) {
134        let newHexValue = '0'.repeat(value[0]) + '1'.repeat(value[1] - value[0]) + '0'.repeat(max - value[1])
135        newHexValue = (parseInt(newHexValue, 2).toString(16))
136        let zeroPadding = '0'.repeat(Number.parseInt('1'.repeat(max), 2).toString(16).length - newHexValue.length)
137        newHexValue = '0x' + zeroPadding + newHexValue;
138        return newHexValue;
139    }
140
141    formDataProxy(name, data = null, update = false) {
142        let path = {
143            'RDT_ENABLED': 'FEATURES.RDT.RDT_ENABLED',
144            'CDP_ENABLED': 'FEATURES.RDT.CDP_ENABLED',
145            'VCAT_ENABLED': 'FEATURES.RDT.VCAT_ENABLED',
146        }[name]
147
148        // check parent node exists
149        let oldValue = vueUtils.getPathVal(this.scenario.hv, path);
150        if (oldValue === undefined) {
151            let t = path.split('.');
152            let parentPath = t.splice(0, t.length - 1).join('.');
153            if (!vueUtils.getPathVal(this.scenario.hv, parentPath)) {
154                vueUtils.setPathVal(this.scenario.hv, parentPath, {});
155            }
156            // set to checkbox default value
157            vueUtils.setPathVal(this.scenario.hv, path, 'n');
158        }
159        // if data is not empty, set value
160        if (data !== null) {
161            vueUtils.setPathVal(this.scenario.hv, path, data)
162
163            // if data is not empty, set value as expected and update CAT_INFO
164            if (update) {
165                switch (name) {
166                    case 'RDT_ENABLED':
167                        if (data === 'n') {
168                            this.formDataProxy('CDP_ENABLED', 'n');
169                            this.formDataProxy('VCAT_ENABLED', 'n');
170                        }
171                        break;
172                    case 'CDP_ENABLED':
173                        if (data === 'y') {
174                            this.formDataProxy('RDT_ENABLED', 'y');
175                            this.formDataProxy('VCAT_ENABLED', 'n');
176                        }
177                        break;
178                    case 'VCAT_ENABLED':
179                        if (data === 'y') {
180                            this.formDataProxy('RDT_ENABLED', 'y');
181                            this.formDataProxy('CDP_ENABLED', 'n');
182                        }
183                        break;
184                }
185            }
186        }
187        let result: string;
188        // @ts-ignore
189        result = vueUtils.getPathVal(this.scenario.hv, path);
190        if (typeof result !== 'string') {
191            console.log(`Unexpected result of ${name}: `, result)
192        }
193        return result
194    }
195
196
197    scenarioLoaded() {
198        // get CAT schema && scenario data
199        this.schemaData = window.getSchemaData();
200        this.scenario = window.getCurrentScenarioData();
201        this.vmIDs = this.getVMIDs()
202        // get cat scenario data
203        this.CATDB = this.getCATDataFromScenario();
204    }
205
206    getScenarioDataFromCAT() {
207        let CATUIData = this.getCATUIData();
208        let ScenarioCATData: {
209            CACHE_ALLOCATION: {
210                CACHE_ID: string,
211                CACHE_LEVEL: number,
212                POLICY: Policy[]
213            }[]
214        }
215        if (CATUIData.regions.length === 0) {
216            return null;
217        }
218        ScenarioCATData = {CACHE_ALLOCATION: []}
219        for (const region of CATUIData.regions) {
220            let policies: Policy[] = region.data.RTCore.concat(region.data.Standard, region.data.VCAT);
221            ScenarioCATData.CACHE_ALLOCATION.push({
222                CACHE_ID: region.id,
223                CACHE_LEVEL: region.level,
224                POLICY: policies
225            })
226        }
227        return ScenarioCATData;
228    }
229
230
231    getCATUIData(): CATUIDataObject {
232        // get CAT schema && scenario && board basic data
233        this.schemaData = window.getSchemaData();
234        this.scenario = window.getCurrentScenarioData();
235        this.CAT_REGION_INFO = window.getBoardData().CAT_INFO;
236
237        // check scenario data is empty
238        // usually, this happens when user has no scenario loaded, then import a board
239        if (!this.scenario.hv) {
240            return null;
241        }
242
243
244        // get switches status from scenario
245        // @ts-ignore
246        this.switches = new Proxy({}, {
247            get: (target: {}, switchName: string | symbol): any => {
248                return this.formDataProxy(switchName) === 'y'
249            },
250            set: (target: {}, switchName: string | symbol, value: boolean): boolean => {
251                return this.formDataProxy(switchName, value ? 'y' : 'n', true) === 'y';
252            }
253        })
254
255
256        // if no CAT REGION INFO from board xml,
257        // means this board(or CPU) not support CAT, or all support CAT region only have one CPU core
258        if (this.CAT_REGION_INFO.length === 0) {
259            let errorMsg = `This board(or CPU) doesn't support CAT which means there is no any CAT capability.</br>There is no Cache Region was shared which means \"all support CAT region only have one CPU core\".`;
260            console.log(errorMsg);
261            return {
262                errorMsg,
263                regions: [],
264                summary: {}
265            };
266        }
267
268        // correct switches and return rdt_enabled result
269        if (!this.correctSwitches()) {
270            return {
271                errorMsg: '',
272                regions: [],
273                summary: {}
274            }
275        }
276
277
278        // CPU affinity data checks
279        // If error, only show error message
280        let errorMsg = this.checkCPUAffinity()
281
282
283        // get CPU data
284        this.preLaunchedVMCPUs = this.getPreLaunchedVMCPUs();
285        this.serviceVM = this.getServiceVM();
286        this.serviceVMCPUs = this.getServiceVMVCPUs()
287        this.vmIDs = this.getVMIDs()
288
289
290        let CATUIData: CATUIDataObject = {
291            errorMsg, regions: [], summary: {}
292        };
293        // mapping CAT region info
294        this.CAT_REGION_INFO.map(region => {
295            let regionData = _.cloneDeep(region);
296            if (!CATUIData.summary.hasOwnProperty(regionData.level)) {
297                CATUIData.summary[regionData.level] = {count: 0}
298            }
299            CATUIData.summary[regionData.level].count++;
300            CATUIData.summary[regionData.level][regionData.id] = CATUIData.summary[regionData.level].count;
301
302            regionData['data'] = {
303                RTCore: this.getRTCoreData(regionData),
304                Standard: this.getStandardData(regionData),
305                VCAT: this.getVCATData(regionData)
306            }
307            CATUIData.regions.push(regionData);
308        })
309
310
311        return CATUIData
312    }
313
314    haveCPUAffinity(vmConfig) {
315        if (vmConfig.load_order === 'SERVICE_VM') {
316            return false
317        }
318        return (
319            vmConfig.hasOwnProperty('cpu_affinity') &&
320            vmConfig.cpu_affinity.hasOwnProperty('pcpu') &&
321            _.isArray(vmConfig.cpu_affinity.pcpu)
322        )
323    }
324
325    checkCPUAffinity() {
326        // check cpu affinity
327        let errMsg = ['CPU affinity is not set for the following VMs:'];
328        let result = all(this.scenario.vm.map(vmConfig => {
329            if (vmConfig.load_order === 'SERVICE_VM') {
330                return true
331            }
332            let haveCPUAffinitySetting = this.haveCPUAffinity(vmConfig);
333            if (!haveCPUAffinitySetting) {
334                errMsg.push(`VM ${vmConfig.name} has no CPU affinity setting`);
335            }
336            return haveCPUAffinitySetting;
337        }))
338        if (result) {
339            return '';
340        }
341        errMsg.push('Please set CPU affinity for all VMs');
342        return errMsg.join('\n')
343    }
344
345    correctSwitches() {
346        if (this.switches.RDT_ENABLED) {
347            if (this.switches.CDP_ENABLED) {
348                if (this.switches.VCAT_ENABLED) {
349                    this.switches.VCAT_ENABLED = false
350                }
351            }
352        } else if (this.switches.VCAT_ENABLED) {
353            if (!this.switches.RDT_ENABLED) {
354                this.switches.RDT_ENABLED = true
355            }
356        }
357        return this.switches.RDT_ENABLED
358    }
359
360    getPreLaunchedVMCPUs() {
361        let preLaunchedVMCPUs = [];
362
363        this.scenario.vm.map(vmConfig => {
364            if (vmConfig.load_order === 'PRE_LAUNCHED_VM' && this.haveCPUAffinity(vmConfig)) {
365                let vmCPUIDs = vmConfig.cpu_affinity.pcpu.map(pcpu => {
366                    return pcpu.pcpu_id;
367                })
368                preLaunchedVMCPUs.concat(vmCPUIDs)
369            }
370        })
371
372        return preLaunchedVMCPUs;
373    }
374
375    newPolicy(CACHE_ID, CACHE_LEVEL, vmConfig, VCPU, TYPE: PolicyType, maxLength): Policy {
376        let originPolicy = {
377            VM: vmConfig.name,
378            VCPU, TYPE,
379            CLOS_MASK: this.getCLOSMask(CACHE_ID, CACHE_LEVEL, vmConfig['@id'], vmConfig.name, VCPU, TYPE, maxLength)
380        }
381        return new Proxy(originPolicy, {
382            set: (target, key, value) => {
383                target[key] = value;
384                if (key === 'CLOS_MASK') {
385                    console.log(`${CACHE_ID} ${CACHE_LEVEL} ${vmConfig.name} ${VCPU} ${TYPE} CLOS_MASK: ${value}`);
386                    this.setCLOSMask(CACHE_ID, CACHE_LEVEL, vmConfig['@id'], vmConfig.name, VCPU, TYPE, value);
387                }
388                return true;
389            }
390        })
391    }
392
393    selectCATData(CACHE_ID, CACHE_LEVEL, vmID, vmName, VCPU, TYPE: PolicyType) {
394        for (let i = 0; i < this.CATDB.length; i++) {
395            let CATData = this.CATDB[i];
396            if (
397                CATData.CACHE_ID === CACHE_ID && CATData.CACHE_LEVEL === CACHE_LEVEL &&
398                CATData.META.vmid === vmID && CATData.VCPU === VCPU && CATData.TYPE === TYPE
399            ) {
400                return CATData
401            }
402        }
403        return false;
404    }
405
406    setCLOSMask(CACHE_ID, CACHE_LEVEL, vmID, vmName, VCPU, TYPE: PolicyType, CLOS_MASK: string) {
407        let CATData = this.selectCATData(CACHE_ID, CACHE_LEVEL, vmID, vmName, VCPU, TYPE);
408        if (CATData !== false) {
409            CATData.CLOS_MASK = CLOS_MASK;
410            return true;
411        }
412
413        this.CATDB.push({
414            META: {vmid: vmID},
415            CACHE_ID, CACHE_LEVEL,
416            CLOS_MASK,
417            VM: vmName, VCPU, TYPE,
418        })
419        return true;
420    }
421
422    getCLOSMask(CACHE_ID, CACHE_LEVEL, vmID, vmName, VCPU, TYPE: PolicyType, maxLength: number) {
423        let CATData = this.selectCATData(CACHE_ID, CACHE_LEVEL, vmID, vmName, VCPU, TYPE);
424        let CLOS_MASK
425        if (CATData !== false) {
426            CLOS_MASK = CATData.CLOS_MASK;
427            // ensure CLOS_MASK length is shorter or equal to maxLength
428            CLOS_MASK = this.rangeToHex(this.hexToRange(CLOS_MASK, maxLength), maxLength);
429            return CLOS_MASK;
430        } else CLOS_MASK = "0x" + parseInt('1'.repeat(maxLength), 2).toString(16)
431        this.CATDB.push({
432            META: {vmid: vmID},
433            CACHE_ID, CACHE_LEVEL,
434            CLOS_MASK,
435            VM: vmName, VCPU, TYPE,
436        })
437        return CLOS_MASK;
438    }
439
440    getRTCoreData(regionData): Policy[] {
441        let RTCoreData: Policy[] = [];
442        this.scenario.vm.map(vmConfig => {
443            if (vmConfig.vm_type === 'RTVM' && this.haveCPUAffinity(vmConfig)) {
444                vmConfig.cpu_affinity.pcpu.map(
445                    (pcpu, index) => {
446                        if (
447                            regionData.processors.indexOf(pcpu.pcpu_id) !== -1 &&
448                            pcpu.hasOwnProperty('real_time_vcpu') &&
449                            pcpu.real_time_vcpu === 'y'
450                        ) {
451                            if (!this.switches.CDP_ENABLED) {
452                                RTCoreData.push(this.newPolicy(regionData.id, regionData.level, vmConfig, index, 'Unified', regionData.capacity_mask_length))
453                            } else {
454                                RTCoreData.push(this.newPolicy(regionData.id, regionData.level, vmConfig, index, 'Code', regionData.capacity_mask_length))
455                                RTCoreData.push(this.newPolicy(regionData.id, regionData.level, vmConfig, index, 'Data', regionData.capacity_mask_length))
456                            }
457                        }
458                    }
459                )
460            }
461        })
462
463        _.sortBy(RTCoreData, ['VM', 'VCPU', 'TYPE']);
464
465        return RTCoreData;
466    }
467
468
469    getStandardData(regionData): Policy[] {
470        let StandardData: Policy[] = [];
471        this.scenario.vm.map(vmConfig => {
472            if (this.haveCPUAffinity(vmConfig)) {
473                vmConfig.cpu_affinity.pcpu.map(
474                    (pcpu, index) => {
475                        if (regionData.processors.indexOf(pcpu.pcpu_id) !== -1) {
476                            if (!pcpu.hasOwnProperty('real_time_vcpu') ||
477                                (pcpu.real_time_vcpu === 'n') ||
478                                (pcpu.real_time_vcpu === 'y' && vmConfig.vm_type !== 'RTVM')
479                            ) {
480                                if (!this.switches.CDP_ENABLED) {
481                                    StandardData.push(this.newPolicy( regionData.id, regionData.level,vmConfig, index, 'Unified', regionData.capacity_mask_length))
482                                } else {
483                                    StandardData.push(this.newPolicy(regionData.id, regionData.level, vmConfig, index, "Code", regionData.capacity_mask_length))
484                                    StandardData.push(this.newPolicy( regionData.id, regionData.level,vmConfig, index, "Data", regionData.capacity_mask_length))
485                                }
486                            }
487                        }
488                    })
489            }
490        })
491
492        // add service vm policy
493        StandardData = StandardData.concat(
494            this.getServiceData(regionData),
495        )
496
497        _.sortBy(StandardData, ['VM', 'VCPU', 'TYPE']);
498        return StandardData;
499    }
500
501    getServiceData(regionData): Policy[] {
502        let ServiceData: Policy[] = [];
503
504        this.serviceVMCPUs.map((pcpuID, index) => {
505            if (regionData.processors.indexOf(pcpuID) !== -1) {
506                if (!this.switches.CDP_ENABLED) {
507                    ServiceData.push(this.newPolicy(regionData.id, regionData.level, this.serviceVM, index, "Unified", regionData.capacity_mask_length))
508                } else {
509                    ServiceData.push(this.newPolicy(regionData.id, regionData.level, this.serviceVM, index, "Code", regionData.capacity_mask_length))
510                    ServiceData.push(this.newPolicy(regionData.id, regionData.level, this.serviceVM, index, "Data", regionData.capacity_mask_length))
511                }
512            }
513        })
514        return ServiceData;
515    }
516
517    getVCATData(regionData): Policy[] {
518        let VCATData: Policy[] = [];
519        // VCAT is only available for CPU 0
520        if (this.switches.VCAT_ENABLED && regionData.processors.indexOf(0) !== -1) {
521            this.scenario.vm.map(vmConfig => {
522                if (
523                    this.haveCPUAffinity(vmConfig) &&
524                    vmConfig.hasOwnProperty('virtual_cat_support') &&
525                    vmConfig.virtual_cat_support === "y"
526                ) {
527                    VCATData.push(
528                        this.newPolicy(regionData.id, regionData.level, vmConfig, 0, "Unified", vmConfig.virtual_cat_number)
529                    )
530                }
531            })
532        }
533        _.sortBy(VCATData, ['VM']);
534        return VCATData;
535    }
536
537    private getServiceVM() {
538        let serviceVM = null;
539        this.scenario.vm.map(vmConfig => {
540            if (vmConfig.load_order === 'SERVICE_VM') {
541                serviceVM = vmConfig;
542            }
543        })
544        return serviceVM;
545    }
546
547    private getServiceVMVCPUs() {
548        let serviceVMCPUs = [];
549        if (this.serviceVM !== null) {
550            // noinspection JSUnresolvedVariable
551            this.schemaData.HV.BasicConfigType.definitions.CPUAffinityConfiguration.properties.pcpu_id.enum.map((pcpu_id) => {
552                // if pcpu_id in preLaunchedVMCPUIDs, it's used by pre launched vm, we need skip it
553                if (this.preLaunchedVMCPUs.indexOf(pcpu_id) !== -1) {
554                    return;
555                }
556                serviceVMCPUs.push(pcpu_id);
557            })
558        }
559        return serviceVMCPUs;
560    }
561
562
563    private getCATDataFromScenario() {
564        let hv = this.scenario.hv;
565
566        let scenarioCATData: CATDBRecord[] = []
567         // noinspection JSUnresolvedVariable
568        if (
569            hv !== null &&
570            hv.hasOwnProperty('CACHE_REGION')
571        ) {
572            let cacheRegion = hv.CACHE_REGION
573            if (cacheRegion !== null && cacheRegion.hasOwnProperty('CACHE_ALLOCATION') &&
574                _.isArray(cacheRegion.CACHE_ALLOCATION)) {
575                // noinspection JSUnresolvedVariable
576
577                cacheRegion.CACHE_ALLOCATION.map((cache_allocation) => {
578                    if (
579                        cache_allocation.hasOwnProperty('POLICY') &&
580                        cache_allocation.POLICY.length > 0
581                    ) {
582                        cache_allocation.POLICY.map(policy => {
583                            scenarioCATData.push({
584                                CACHE_ID: cache_allocation.CACHE_ID,
585                                CACHE_LEVEL: cache_allocation.CACHE_LEVEL,
586                                CLOS_MASK: policy.CLOS_MASK,
587                                META: {vmid: this.vmIDs[policy.VM]},
588                                TYPE: policy.TYPE,
589                                VCPU: policy.VCPU,
590                                VM: policy.VM
591                            })
592                        })
593                    }
594
595                })
596
597            }
598        }
599        return scenarioCATData
600    }
601
602    private getVMIDs(): { [vmName: string]: vmID } {
603        let vmIDs = {}
604        this.scenario.vm.map(vmConfig => {
605            vmIDs[vmConfig.name] = vmConfig['@id']
606        })
607        return vmIDs
608    }
609}
610
611class PythonObject {
612    api(scriptName, output_format, ...params) {
613        let pythonFunction = window.pyodide.pyimport(`configurator.pyodide.${scriptName}`);
614        let result = pythonFunction.main(...params);
615        if (output_format === 'json') {
616            return JSON.parse(result);
617        } else {
618            return result;
619        }
620    }
621
622    loadBoard(boardXMLText, path) {
623        return this.api('loadBoard', 'json', boardXMLText, path)
624
625    }
626
627    loadScenario(scenarioXMLText) {
628        return this.api('loadScenario', 'json', scenarioXMLText)
629    }
630
631    validateBoardStructure(boardXMLText) {
632        return this.api('validateBoardStructure', 'plaintext', boardXMLText)
633    }
634
635    validateScenarioStructure(scenarioXMLText) {
636        return this.api('validateScenarioStructure', 'plaintext', scenarioXMLText)
637    }
638
639    validateScenario(boardXMLText, scenarioXMLText) {
640        return this.api('validateScenario', 'json', boardXMLText, scenarioXMLText)
641    }
642
643    generateLaunchScript(boardXMLText, scenarioXMLText) {
644        return this.api('generateLaunchScript', 'json', boardXMLText, scenarioXMLText)
645    }
646
647    populateDefaultValues(scenarioXMLText) {
648        return this.api('populateDefaultValues', 'json', scenarioXMLText)
649    }
650
651    generateConfigSummary(boardXMLText, scenarioXMLText) {
652        return this.api('generateConfigSummary', 'plaintext', boardXMLText, scenarioXMLText)
653    }
654
655    updateSchema(boardXMLText, scenarioXMLText) {
656        return this.api('updateSchema', 'plaintext', boardXMLText, scenarioXMLText)
657    }
658}
659
660class Configurator {
661    public pythonObject: PythonObject;
662    public cat: CAT;
663
664    constructor() {
665        this.pythonObject = new PythonObject()
666        this.cat = new CAT()
667    }
668
669    getHistory(historyType: HistoryTypes): Promise<String[] | []> {
670        return invoke("get_history", {historyType})
671            .then((historyJsonText) => {
672                if (typeof historyJsonText === "string") {
673                    return JSON.parse(historyJsonText);
674                }
675                return [];
676            })
677    }
678
679    addHistory(historyType: HistoryTypes, historyPath: string) {
680        return invoke("add_history", {historyType, historyPath})
681    }
682
683    openDialog(options: OpenDialogOptions) {
684        return dialog.open(options)
685    }
686
687    readFile(filePath: string): Promise<String> {
688        return invoke("acrn_read", {filePath})
689    }
690
691    writeFile(filePath: string, contents: string) {
692        return invoke("acrn_write", {filePath, contents})
693    }
694
695    isFile(filePath: string): Promise<Boolean> {
696        return invoke("acrn_is_file", {path: filePath})
697    }
698
699    readDir(path: string, recursive: Boolean) {
700        return invoke('acrn_read_dir', {path, recursive})
701    }
702
703    creatDir(path: string, recursive = true) {
704        return invoke('acrn_create_dir', {path, recursive})
705    }
706
707    removeDir(path: string) {
708        return invoke('acrn_remove_dir', {path})
709    }
710
711    removeFile(path: string) {
712        return invoke('acrn_remove_file', {path})
713    }
714
715    runPython(code: string, isJSON = false): string | Object {
716        let result = window.pyodide.runPython(code);
717        if (isJSON) {
718            result = JSON.parse(result)
719        }
720        return result
721    }
722
723    loadBoard(path: string) {
724        return this.readFile(path)
725            .then((fileContent) => {
726                let syntactical_errors = this.pythonObject.validateBoardStructure(fileContent);
727                if (syntactical_errors !== "") {
728                    throw Error("The file has broken structure.");
729                }
730                return this.pythonObject.loadBoard(fileContent, path);
731            })
732    }
733
734    loadScenario(path: string): Object {
735        return this.readFile(path).then((fileContent) => {
736            let syntactical_errors = this.pythonObject.validateScenarioStructure(fileContent);
737            if (syntactical_errors !== "") {
738                throw Error("The loaded file does not look like a valid ACRN scenario XML.\n\n" +
739                    "If that file is used with ACRN 2.x, try upgrading it following the instructions at https://projectacrn.github.io/latest/tutorials/upgrading_configuration.html.\n");
740            }
741            return this.pythonObject.loadScenario(fileContent)
742        })
743    }
744
745    newVM(vmid, load_order) {
746        let newVMData = {
747            '@id': vmid,
748            load_order: load_order,
749            name: `VM${vmid}`,
750            cpu_affinity: null
751        }
752        if (load_order !== 'SERVICE_VM') {
753            newVMData['cpu_affinity'] = {
754                pcpu: [
755                    {
756                        pcpu_id: null,
757                        real_time_vcpu: 'n'
758                    }
759                ]
760            }
761        }
762        return newVMData
763    }
764
765    createNewScenario(pre, service, post) {
766        let newScenario = {
767            hv: {},
768            vm: []
769        }
770        let vmid = 0
771        let vmNums = {'PRE_LAUNCHED_VM': pre, 'SERVICE_VM': service, 'POST_LAUNCHED_VM': post}
772        for (let key in vmNums) {
773            for (let i = 0; i < vmNums[key]; i++) {
774                newScenario.vm.push(this.newVM(vmid, key))
775                vmid++;
776            }
777        }
778        return newScenario;
779    }
780
781    convertScenarioToXML(scenarioData: Object) {
782        let json2xml = new JSON2XML();
783        return json2xml.convert(scenarioData)
784    }
785
786}
787
788let configurator = new Configurator()
789
790window.configurator = configurator
791export default configurator
792