// This file contains function that are Electron/Node.JS specific // Load required packages const Plotly = require('plotly.js-dist'); const ipcRenderer = require('electron').ipcRenderer; const fs = require('fs'); const { execSync, execFile } = require('child_process'); const os = require('os'); const path = require('path'); function getFiles(dir, filelist){ fileList = []; var files = fs.readdirSync(dir); for(var i of files){ if (!files.hasOwnProperty(i)) continue; var name = dir+'/'+files[i]; if (!fs.statSync(name).isDirectory()){ fileList.push(name); } } return fileList; } // Run one TrimSP binary asynchronously in a given working directory so the // scan loop can keep several local jobs in flight at once. function executeFileAsync(binary, args, workdir) { return new Promise((resolve, reject) => { execFile(binary, args, {cwd: workdir}, (err) => { if (err) { reject(err); return; } resolve(); }); }); } // Use the available CPU parallelism as the default worker-pool size for local // scan jobs. The caller still caps this against the number of scan points. function getParallelJobCount() { if (typeof os.availableParallelism === 'function') { return Math.max(1, os.availableParallelism()); } return Math.max(1, os.cpus().length); } function checkDir(directory) { // Check whether directory exists, if not create it if (typeof directory !== 'string' || directory.length === 0) { console.log('Invalid directory passed to checkDir:', directory); return(0); } if (!fs.existsSync(directory)) { // Folder does not exist try to create it fs.mkdir(directory,{recursive: true}, (err) => { if (err) { alert('Cannot create data folder '+directory); return(0); } }); } return(1); } function writeAsciiFile(filename,content) { // Write string content into ascii file filename try { fs.writeFileSync(filename, content, 'utf-8'); } catch(e) { alert('Failed to save '+filename); return(0); } return(1); } function readAsciiFile(filename) { // Read ascii file filename and return content in a string var content; // If an array of filenames was sent take the first one only if (Array.isArray(filename)) {filename=filename[0];} try { content = fs.readFileSync(filename, 'utf8'); } catch(e) { alert('Failed to read '+filename); return(0); } return(content); } function fileExists(filename) { if (typeof filename !== 'string' || filename.length === 0) { return 0; } try { if (fs.existsSync(filename)) { return 1; } }catch(err) { console.log(err); } return 0; } function getTrimBinaryPath(configuredPath) { const candidates = []; if (configuredPath) { candidates.push(path.join(configuredPath, "trimspNL")); } if (process.resourcesPath) { candidates.push(path.join(process.resourcesPath, "bin", "trimspNL")); candidates.push(path.join(process.resourcesPath, "trimspNL")); } candidates.push(path.join(process.cwd(), "trimspNL")); for (const candidate of candidates) { if (fileExists(candidate)) { return candidate; } } return "trimspNL"; } function setWorkFolder(foldername) { const folder = Array.isArray(foldername) ? foldername[0] : foldername; if (folder) { document.getElementById("workPath").value = folder; if (typeof updateStatusBar === 'function') { updateStatusBar({ workPath: folder }); } ipcRenderer.send('updateWorkPath', folder); try { // Change process directory process.chdir(folder); } catch (err) { console.log('Failed to change directory:', err); } } } function browseForFolder() { ipcRenderer.send('browseFolder'); } function fallbackBrowseForFolder() { const picker = document.getElementById("folderPicker"); if (!picker) { return; } picker.value = ''; picker.click(); } function getConfigValue(content, key) { const lines = content.toString().split('\n'); for (const line of lines) { const trimmed = line.trim(); if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('[')) { continue; } const separator = trimmed.indexOf('='); if (separator === -1) { continue; } const param = trimmed.slice(0, separator).trim(); if (param === key) { return trimmed.slice(separator + 1).trim(); } } return null; } function resolveConfigWorkPath(filename, content) { const configuredWorkPath = getConfigValue(content, 'workPath'); if (!configuredWorkPath) { return null; } if (configuredWorkPath.startsWith('./')) { return path.resolve(configuredWorkPath); } if (path.isAbsolute(configuredWorkPath)) { return configuredWorkPath; } return null; } function loadConfigFile(filename, autoRun = false) { const configPath = Array.isArray(filename) ? filename[0] : filename.toString(); console.log('Open file ' + configPath); return new Promise((resolve, reject) => { fs.readFile(configPath, function read(err, data) { if (err) { reject(err); return; } setValues(data); const resolvedWorkPath = resolveConfigWorkPath(configPath, data); if (resolvedWorkPath) { setWorkFolder([resolvedWorkPath]); } if (autoRun) { setTimeout(() => { startSim().then(resolve).catch(reject); }, 0); } else { resolve(); } }); }); } async function runStartupQueue(configPaths, autoRun) { if (!configPaths || configPaths.length === 0) { return; } if (!autoRun) { await loadConfigFile(configPaths[0], false); return; } for (let i = 0; i < configPaths.length; i++) { const configPath = configPaths[i]; const queueLabel = `Queue ${i + 1}/${configPaths.length}`; if (typeof updateStatusBar === 'function') { updateStatusBar({ run: `${queueLabel}: loading ${path.basename(configPath)}` }); } await loadConfigFile(configPath, false); if (typeof updateStatusBar === 'function') { updateStatusBar({ run: `${queueLabel}: running ${path.basename(configPath)}` }); } await startSim(); } if (typeof updateStatusBar === 'function') { updateStatusBar({ run: `Completed queue of ${configPaths.length} config file(s)` }); } } function firstLoad() { document.getElementById("trimPath").value = process.cwd(); if (typeof syncStatusBar === 'function') { syncStatusBar(); } const folderPicker = document.getElementById("folderPicker"); if (folderPicker) { folderPicker.addEventListener('change', function(event) { const files = event.target.files; if (!files || files.length === 0) { return; } const folder = path.dirname(files[0].path); setWorkFolder([folder]); }); } // Catch calls for open file ipcRenderer.on('openFile', function(event, filename) { loadConfigFile(filename, false); }); ipcRenderer.on('openFileAndMaybeRun', function(event, payload) { if (!payload || !payload.filename) { return; } loadConfigFile(payload.filename, !!payload.autoRun); }); // Catch calls for selectfolder ipcRenderer.on('selectFolder', function(event, foldername) { setWorkFolder(foldername); console.log("currentdir",process.cwd()); }); // Catch calls for save as ipcRenderer.on('saveFile', function(event, filename) { const workPath = document.getElementById("workPath").value || process.cwd(); if (filename === undefined || filename === null) { return; } // If filename is empty use default value in the selected work folder if (filename == '') {filename = path.join(workPath, 'TrimSP.cfg');} if (path.extname(filename) === '') { filename += '.cfg'; } // Get values from all fields and prepare config file let trimSPcfg=prep_cfg(0); // Save file to filename console.log('Save file to '+filename); try { fs.writeFileSync(filename, trimSPcfg, 'utf-8'); if (typeof updateStatusBar === 'function') { updateStatusBar({ lastSave: filename }); } if (typeof showTransientWarning === 'function') { showTransientWarning('Saved configuration to ' + filename, document.getElementById('workPath'), 4000); } } catch(e) { alert('Failed to save the file !'); } }); // Catch calls for plotProf ipcRenderer.on('plotProf', function(event, filename) { console.log("filename="+filename); plotProfiles(filename); }); // Catch calls for plotFrac ipcRenderer.on('plotFrac', function(event, filename) { console.log("filename="+filename); plotFractions(filename); }); // Catch calls for plotMean ipcRenderer.on('plotMean', function(event, filename) { console.log("filename="+filename); plotMean(filename); }); // Catch clicks for Browse button ipcRenderer.on('browseFolder', function(event, foldername) { console.log('browseFolder reply', foldername); setWorkFolder(foldername); }); ipcRenderer.on('browseFolderFallback', function() { fallbackBrowseForFolder(); }); } window.addEventListener('load', async function() { try { const startupOptions = await ipcRenderer.invoke('getStartupOptions'); if (startupOptions && startupOptions.configPaths && startupOptions.configPaths.length > 0) { setTimeout(() => { runStartupQueue(startupOptions.configPaths, !!startupOptions.autoRun).catch((err) => { console.log('Failed to run startup queue:', err); }); }, 0); } } catch (err) { console.log('Failed to read startup options:', err); } });