Final Version of the machine description, fully working

This commit is contained in:
LAHAY Damien 2025-07-03 14:34:43 +02:00
parent a1dcf26b85
commit 1f42ce3ad2
3 changed files with 139 additions and 93 deletions

View File

@ -61,12 +61,16 @@
background-color: #e9ecef;
cursor: not-allowed;
}
.radio-group label, .checkbox-group label {
font-weight: normal;
margin-right: 15px;
.radio-group {
display: flex;
align-items: center; /* This vertically aligns the text with the button */
}
input[type="radio"], input[type="checkbox"] {
margin-right: 5px;
.radio-group label {
display: inline-block; /* This prevents the label from taking a full line */
font-weight: normal;
margin-bottom: 0; /* Remove bottom margin for inline elements */
margin-left: 4px; /* Space between the button and the text */
margin-right: 20px; /* Space between the options */
}
small {
display: block;
@ -89,6 +93,29 @@
input[type="submit"]:hover {
background-color: #2649B2;
}
.checkbox-container {
display: grid;
grid-template-columns: auto 1fr; /* Checkbox column, then text takes remaining space */
gap: 0 10px; /* 10px space between checkbox and text */
align-items: start;
margin-bottom: 12px; /* Space between each checkbox item */
}
.checkbox-container input[type="checkbox"] {
margin-top: 5px; /* Nudges the checkbox down to align with the text */
}
.checkbox-container label {
font-weight: normal;
margin-bottom: 0;
display: block;
}
.checkbox-container label strong {
font-weight: bold; /* Make the label title bold */
display: block;
}
.checkbox-container label small {
margin-top: 2px;
line-height: 1.3;
}
</style>
</head>
<body>
@ -165,9 +192,10 @@
<input type="text" id="version" name="version" placeholder="Ex: 1.0.0">
</div>
<div class="form-group">
<label for="outputFile">Output File</label>
<input type="text" id="outputFile" name="outputFile" value="Automatically generated (YAML)" readonly>
<small>This field is automatically generated and cannot be edited.</small>
<label>Output Filename</label>
<!-- This is where the generated filename will be displayed -->
<p id="output-filename-display" style="font-family: monospace; background-color: #e9ecef; padding: 10px; border-radius: 5px;"></p>
<small>The filename is automatically generated from the BrowseName above.</small>
</div>
</fieldset>
@ -205,56 +233,50 @@
</fieldset>
<fieldset>
<legend>Features & ADD-INs (Boolean Variables)</legend>
<legend>Optional Features</legend>
<div class="form-group">
<label>StopReason Generation</label>
<div class="radio-group">
<input type="radio" id="stopReasonYes" name="stopReason" value="Yes">
<input type="radio" id="stopReasonYes" name="stopReason" value="Yes" checked>
<label for="stopReasonYes">Yes</label>
<input type="radio" id="stopReasonNo" name="stopReason" value="No" checked>
<input type="radio" id="stopReasonNo" name="stopReason" value="No">
<label for="stopReasonNo">No</label>
</div>
<small>Generate the special variable for the root cause of the machine stop (recommended if supported by the PLC).</small>
<small>Generate the special variable for the root cause of the machine stop. Boolean variable to trigger the read of properties alarmCode and alarmMessage. The StopReason is defined by PackML.</small>
</div>
</fieldset>
<fieldset>
<legend>ADD-INs</legend>
<div class="form-group">
<label>AGV ADDIN</label>
<div class="radio-group">
<input type="radio" id="agvYes" name="agvAddin" value="Yes">
<label for="agvYes">Yes</label>
<input type="radio" id="agvNo" name="agvAddin" value="No" checked>
<label for="agvNo">No</label>
<div class="checkbox-container">
<input type="checkbox" id="addins-acpower" name="addins" value="AcPower">
<label for="addins-acpower">
<strong>ACPower ADDIN</strong>
<small>Provides information about electrical consumption.</small>
</label>
</div>
<small>Applies to Palletizers which are interfaced with AGVs.</small>
</div>
<div class="form-group">
<label>TrackAdvance ADDIN</label>
<div class="radio-group">
<input type="radio" id="trackYes" name="trackAddin" value="Yes">
<label for="trackYes">Yes</label>
<input type="radio" id="trackNo" name="trackAddin" value="No" checked>
<label for="trackNo">No</label>
<div class="checkbox-container">
<input type="checkbox" id="addins-utilities" name="addins" value="Utilities">
<label for="addins-utilities">
<strong>Utilities ADDIN</strong>
<small>Used for machines which consume or produce different sources of energy.</small>
</label>
</div>
<small>Applies to filling machines which are supporting a cleaning in place (CIP) process.</small>
</div>
<div class="form-group">
<label>ACPower ADDIN</label>
<div class="radio-group">
<input type="radio" id="acYes" name="acpowerAddin" value="Yes">
<label for="acYes">Yes</label>
<input type="radio" id="acNo" name="acpowerAddin" value="No" checked>
<label for="acNo">No</label>
<div class="checkbox-container">
<input type="checkbox" id="addins-trackadvance" name="addins" value="TrackAdvance">
<label for="addins-trackadvance">
<strong>TrackAdvance ADDIN</strong>
<small>Applies to filling machines supporting a cleaning in place (CIP) process.</small>
</label>
</div>
<small>Provides information about electrical consumption.</small>
</div>
<div class="form-group">
<label>Utilities ADDIN</label>
<div class="radio-group">
<input type="radio" id="utilYes" name="utilities" value="Yes">
<label for="utilYes">Yes</label>
<input type="radio" id="utilNo" name="utilities" value="No" checked>
<label for="utilNo">No</label>
<div class="checkbox-container">
<input type="checkbox" id="addins-agv" name="addins" value="AGV">
<label for="addins-agv">
<strong>AGV ADDIN</strong>
<small>Applies to Palletizers which are interfaced with AGVs.</small>
</label>
</div>
<small>Used for machines which consume or produce different sources of energy.</small>
</div>
</fieldset>

41
main.js
View File

@ -67,21 +67,27 @@ app.whenReady().then(() => {
}
// 3. Build and Append the 'addins' section if needed
const hasAddins = data.acpowerAddin === 'Yes' || data.utilities === 'Yes' || data.trackAddin === 'Yes';
if (hasAddins) {
yamlLines.push(''); // Add a blank line for spacing
const selectedAddins = data.addins || []; // Default to an empty array
if (selectedAddins.length > 0) {
yamlLines.push('');
yamlLines.push(`${sp2}addins:`);
if (data.acpowerAddin === 'Yes') {
if (selectedAddins.includes('AcPower')) {
yamlLines.push(`${sp4}- name: AcPower`);
}
if (data.utilities === 'Yes') {
if (selectedAddins.includes('Utilities')) {
yamlLines.push(`${sp4}- name: EnergyUtilitiesAddin`);
yamlLines.push(`${sp6}# Requires data from a future 'Utilities ADDIN' UI.`);
}
if (data.trackAddin === 'Yes') {
if (selectedAddins.includes('TrackAdvance')) {
yamlLines.push(`${sp4}- name: TrackAdvance`);
yamlLines.push(`${sp6}# Requires data from a future 'TrackAdvance ADDIN' UI.`);
}
if (selectedAddins.includes('AGV')) {
yamlLines.push(`${sp4}- name: AGV`);
yamlLines.push(`${sp6}# Requires data from a future 'AGV ADDIN' UI.`);
}
}
// 4. Join all the generated lines into a single string
@ -91,10 +97,25 @@ app.whenReady().then(() => {
console.log('--- Generated YAML Content ---\n', generatedContent);
// File saving logic (remains the same)
const browseName = data.browseName || 'default-machine';
const safeBrowseName = browseName.replace(/[^a-zA-Z0-9_-]/g, '_');
const yamlFileName = `${safeBrowseName}.yml`;
// File saving logic
const nameParts = [
data.machineType,
data.vendor,
data.model,
data.serialNo,
data.browseName
];
// Filter out any empty or null values
const nonEmptyParts = nameParts.filter(Boolean);
// Join the parts with an underscore
const baseName = nonEmptyParts.join('_');
// Sanitize the final string to remove characters invalid for filenames, but keep underscores and hyphens.
const safeBaseName = baseName.replace(/[/\\?%*:|"<>]/g, '-');
const yamlFileName = `${safeBaseName}.yml`;
const documentsPath = app.getPath('documents');
const yamlFilePath = path.join(documentsPath, yamlFileName);

View File

@ -1,63 +1,66 @@
// renderer.js - DEFINITIVE VERSION
// renderer.js
// We wrap our entire script in a 'DOMContentLoaded' event listener.
// This guarantees that the code inside will only run AFTER the whole
// HTML document has been loaded and is ready. This prevents race conditions.
document.addEventListener('DOMContentLoaded', () => {
console.log('DOM fully loaded. renderer.js is now running.');
// Set the default publication date to today
// --- Real-time Filename Generation ---
const browseNameInput = document.getElementById('BrowseName');
const filenameDisplay = document.getElementById('output-filename-display');
const updateFilename = () => {
const browseName = browseNameInput.value;
if (browseName) {
// Sanitize the name for the file system
const safeBrowseName = browseName.replace(/[^a-zA-Z0-9_-]/g, '_');
filenameDisplay.textContent = `${safeBrowseName}.yml`;
} else {
filenameDisplay.textContent = ''; // Clear if BrowseName is empty
}
};
if (browseNameInput && filenameDisplay) {
// Listen for any input in the BrowseName field and update the display
browseNameInput.addEventListener('input', updateFilename);
// Initial call to set the name if the field is pre-filled
updateFilename();
}
const publicationDateInput = document.getElementById('publicationDate');
if (publicationDateInput) {
publicationDateInput.value = new Date().toISOString().split('T')[0];
console.log('Publication date set to today.');
}
// Find the form element
const form = document.getElementById('machine-form');
if (form) {
console.log('Form element #machine-form found. Attaching listener...');
// Attach the event listener to the form's 'submit' event
form.addEventListener('submit', async (event) => {
event.preventDefault(); // Stop the default page reload
// THIS IS THE LOG WE WANT TO SEE WHEN THE BUTTON IS CLICKED
console.log('Form submit event triggered! Collecting data...');
event.preventDefault();
const formData = new FormData(form);
const data = Object.fromEntries(formData.entries());
const radioButtons = form.querySelectorAll('input[type="radio"]');
const radioState = {};
const radioNames = [...new Set([...radioButtons].map(rb => rb.name))];
radioNames.forEach(name => {
const checkedRadio = form.querySelector(`input[name="${name}"]:checked`);
radioState[name] = checkedRadio ? checkedRadio.value : 'No';
});
// The FormData API handles checkboxes and dropdowns with the same name identically.
// We just need to make sure the data object is built correctly.
const data = {};
for (const [key, value] of formData.entries()) {
// This check prevents multi-value fields from overwriting themselves
if (!data[key]) {
data[key] = value;
}
}
// Use .getAll('addins') to get an array of all CHECKED addins
data.addins = formData.getAll('addins');
const finalData = { ...data, ...radioState };
console.log('Collected form data:', finalData);
console.log('Collected form data:', data);
try {
console.log('Sending data to main process via electronAPI...');
const result = await window.electronAPI.submitForm(finalData);
console.log('Response from main process:', result);
const result = await window.electronAPI.submitForm(data);
if (result.success) {
form.reset();
document.getElementById('publicationDate').value = new Date().toISOString().split('T')[0];
// The success dialog is shown from main.js
// ... (reset logic remains the same)
}
} catch (error) {
console.error('Error during form submission process:', error);
}
});
} else {
// If you see this, the ID in index.html is still wrong.
console.error('CRITICAL ERROR: Form element with ID "machine-form" was not found.');
}
});