Warnings and Counters fully functionnal, made the navbar sticky, and improved UX to navigate through tabs
This commit is contained in:
parent
8e006526b3
commit
3f6d55d36b
181
index.html
181
index.html
@ -6,6 +6,10 @@
|
||||
<!-- <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'"> -->
|
||||
<title>Machine Description Form</title>
|
||||
<style>
|
||||
html {
|
||||
scroll-behavior: smooth; /* Enables smooth scrolling */
|
||||
scroll-padding-top: 80px; /* Prevents section titles from hiding under the sticky nav */
|
||||
}
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #f4f4f9;
|
||||
@ -15,7 +19,7 @@
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 800px;
|
||||
max-width: 1000px;
|
||||
margin: auto;
|
||||
background: #fff;
|
||||
padding: 25px;
|
||||
@ -91,9 +95,29 @@
|
||||
margin-top: 20px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
input[type="submit"]:hover {
|
||||
background-color: #2649B2;
|
||||
}
|
||||
|
||||
.next-tab-btn {
|
||||
background-color: #4A74F3;
|
||||
color: white;
|
||||
padding: 12px 20px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.next-tab-btn:hover {
|
||||
background-color: #2649B2;
|
||||
}
|
||||
|
||||
.checkbox-container {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr; /* Checkbox column, then text takes remaining space */
|
||||
@ -120,6 +144,12 @@
|
||||
.tab-nav {
|
||||
border-bottom: 2px solid #D4D9F0;
|
||||
display: flex;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background-color: white;
|
||||
padding: 10px 0;
|
||||
border-bottom: 2px solid #D4D9F0;
|
||||
z-index: 1000;
|
||||
}
|
||||
.tab-nav button {
|
||||
background: none;
|
||||
@ -205,6 +235,10 @@
|
||||
<nav class="tab-nav">
|
||||
<button class="tab-btn active" data-tab="machine" id="machine-tab-btn">Machine</button>
|
||||
<button class="tab-btn" data-tab="alarms" id="alarms-tab-btn">Alarms</button>
|
||||
<button class="tab-btn" data-tab="warnings" id="warnings-tab-btn">Warnings</button>
|
||||
<button class="tab-btn" data-tab="counters" id="counters-tab-btn">Counters</button>
|
||||
<button class="tab-btn" data-tab="setpoints" id="setpoints-tab-btn">Setpoints</button>
|
||||
<button class="tab-btn" data-tab="realtime-variables" id="realtime-tab-btn">Real Time Variables</button>
|
||||
</nav>
|
||||
|
||||
<form id="machine-form">
|
||||
@ -365,7 +399,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<button type="button" class="next-tab-btn">Next</button>
|
||||
</div>
|
||||
|
||||
<div id="alarms-tab" class="tab-content">
|
||||
<fieldset>
|
||||
<legend>Alarms Exposed by the Machine</legend>
|
||||
@ -378,22 +414,159 @@
|
||||
</table>
|
||||
<button type="button" class="add-row-btn" id="add-alarm-btn">Add Alarm</button>
|
||||
</fieldset>
|
||||
<button type="button" class="next-tab-btn">Next</button>
|
||||
</div>
|
||||
|
||||
<div id="warnings-tab" class="tab-content">
|
||||
<fieldset>
|
||||
<legend>Warnings Exposed by the Machine</legend>
|
||||
<p>Define all warnings for the machine. If the message is empty, the warning will not be generated.</p>
|
||||
<table class="dynamic-table">
|
||||
<thead>
|
||||
<tr><th class="col-id">ID</th><th class="col-name">Name</th><th>Message</th><th class="col-actions">Actions</th></tr>
|
||||
</thead>
|
||||
<tbody id="warnings-table-body"></tbody>
|
||||
</table>
|
||||
<button type="button" class="add-row-btn" id="add-warning-btn">Add Warning</button>
|
||||
</fieldset>
|
||||
<button type="button" class="next-tab-btn">Next</button>
|
||||
</div>
|
||||
|
||||
<div id="counters-tab" class="tab-content">
|
||||
<fieldset>
|
||||
<legend>Counters</legend>
|
||||
<p>
|
||||
Define all counters for the machine. If the description is empty, the counter will not be generated.
|
||||
<br>
|
||||
</p>
|
||||
<p>
|
||||
<strong>Note:</strong> The first three counters are predefined by the PackML standard
|
||||
and are automatically included in the generated nodeset.
|
||||
<br>PackML counters described by default contain only the first entry of each array.
|
||||
</p>
|
||||
<p>
|
||||
You can add more counters as needed. Additional defective unit counter should be provided per type of defect (bad open flap, underfill, not printed, bad pump orientation, ...etc.) and station (head 1, nozzleX,....etc.).
|
||||
</p>
|
||||
<table class="dynamic-table">
|
||||
<thead>
|
||||
<tr><th class="col-id">ID</th><th class="col-name">Name</th><th>Type</th><th class="col-description">Description</th><th class="col-actions">Actions</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><input type="text" value="-" disabled></td>
|
||||
<td><input type="text" value="ProdProcessedCount" disabled></td>
|
||||
<td><input type="text" value="ProductionCounter" disabled></td>
|
||||
<td><textarea rows="2" cols="30" disabled>Total products processed (PackML standard)</textarea></td>
|
||||
<td><span style="color: #999;">Predefined</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="text" value="-" disabled></td>
|
||||
<td><input type="text" value="ProdDefectiveCount" disabled></td>
|
||||
<td><input type="text" value="DefectiveCounter" disabled></td>
|
||||
<td><textarea rows="2" cols="30" disabled>Total defective products (PackML standard)</textarea></td>
|
||||
<td><span style="color: #999;">Predefined</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="text" value="-" disabled></td>
|
||||
<td><input type="text" value="CycleCount" disabled></td>
|
||||
<td><input type="text" value="CycleCounter" disabled></td>
|
||||
<td><textarea rows="2" cols="30" disabled>Total machine cycles (PackML standard)</textarea></td>
|
||||
<td><span style="color: #999;">Predefined</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody id="counters-table-body"></tbody>
|
||||
</table>
|
||||
<button type="button" class="add-row-btn" id="add-counter-btn">Add Counter</button>
|
||||
</fieldset>
|
||||
<button type="button" class="next-tab-btn">Next</button>
|
||||
</div>
|
||||
|
||||
<div id="setpoints-tab" class="tab-content">
|
||||
<fieldset>
|
||||
<legend>Setpoints</legend>
|
||||
<p>Define all setpoints for the machine. If the description is empty, the setpoint will not be generated.</p>
|
||||
<table class="dynamic-table">
|
||||
<thead>
|
||||
<tr><th class="col-id">ID</th><th class="col-name">Name</th><th>Description</th><th class="col-actions">Actions</th></tr>
|
||||
</thead>
|
||||
<tbody id="setpoints-table-body"></tbody>
|
||||
</table>
|
||||
<button type="button" class="add-row-btn" id="add-setpoint-btn">Add Setpoint</button>
|
||||
</fieldset>
|
||||
<button type="button" class="next-tab-btn">Next</button>
|
||||
</div>
|
||||
|
||||
<div id="realtime-tab" class="tab-content">
|
||||
<fieldset>
|
||||
<legend>Real Time Variables</legend>
|
||||
<p>Define all real time variables for the machine. If the description is empty, the variable will not be generated.</p>
|
||||
<table class="dynamic-table">
|
||||
<thead>
|
||||
<tr><th class="col-id">ID</th><th class="col-name">Name</th><th>Description</th><th class="col-actions">Actions</th></tr>
|
||||
</thead>
|
||||
<tbody id="realtime-table-body"></tbody>
|
||||
</table>
|
||||
<button type="button" class="add-row-btn" id="add-realtime-btn">Add Real Time Variable</button>
|
||||
</fieldset>
|
||||
<input type="submit" value="Validate and Submit Description">
|
||||
</div>
|
||||
|
||||
<input type="submit" value="Validate and Submit Description">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<template id="alarm-row-template">
|
||||
<tr>
|
||||
<td><input type="number" name="alarm_id" min="1" placeholder="e.g., 1"></td>
|
||||
<td><input type="text" name="alarm_name" placeholder="e.g., ALARM_1"></td>
|
||||
<td><textarea name="alarm_message" rows="1" cols="50" placeholder="Message displayed on dashboard"></textarea></td>
|
||||
<td><input type="text" name="alarm_name" placeholder="e.g., ALARM_001"></td>
|
||||
<td><textarea name="alarm_message" rows="1" cols="50" placeholder="Message displayed to the operator"></textarea></td>
|
||||
<td class="col-actions"><button type="button" class="remove-row-btn">Remove</button></td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<template id="warning-row-template">
|
||||
<tr>
|
||||
<td><input type="number" name="warning_id" min="1" placeholder="e.g., 1"></td>
|
||||
<td><input type="text" name="warning_name" placeholder="e.g., WARNING_001"></td>
|
||||
<td><textarea name="warning_message" rows="1" cols="50" placeholder="Message displayed to the operator"></textarea></td>
|
||||
<td class="col-actions"><button type="button" class="remove-row-btn">Remove</button></td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<template id="realtime-row-template">
|
||||
<tr>
|
||||
<td><input type="number" name="realtime_id" min="1" placeholder="e.g., 1"></td>
|
||||
<td><input type="text" name="realtime_name" placeholder="e.g., REALTIME_001"></td>
|
||||
<td><textarea name="realtime_message" rows="1" cols="50" placeholder="Message displayed to the operator"></textarea></td>
|
||||
<td class="col-actions"><button type="button" class="remove-row-btn">Remove</button></td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<template id="counter-row-template">
|
||||
<tr>
|
||||
<td><input type="number" name="counter_id" min="1" placeholder="e.g., 1"></td>
|
||||
<td><input type="text" name="counter_name" placeholder="e.g., COUNTER_001"></td>
|
||||
<td><select name="counter_type">
|
||||
<option value="productionCounter">Production Counter</option>
|
||||
<option value="defectiveCounter">Defective Counter</option>
|
||||
<option value="cycleCounter">Cycle Counter</option>
|
||||
<option value="otherCounter">Other</option>
|
||||
</select></td>
|
||||
<td><textarea name="counter_description" rows="1" cols="30" placeholder="Description of the counter"></textarea></td>
|
||||
<td class="col-actions"><button type="button" class="remove-row-btn">Remove</button></td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<template id="setpoint-row-template">
|
||||
<tr>
|
||||
<td><input type="number" name="setpoint_id" min="1" placeholder="e.g., 1"></td>
|
||||
<td><input type="text" name="setpoint_name" placeholder="e.g., SETPOINT_001"></td>
|
||||
<td><textarea name="setpoint_message" rows="1" cols="30" placeholder="Message displayed to the operator"></textarea></td>
|
||||
<td class="col-actions"><button type="button" class="remove-row-btn">Remove</button></td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
<script src="renderer.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
51
main.js
51
main.js
@ -53,18 +53,37 @@ app.whenReady().then(() => {
|
||||
yamlLines.push(`${sp2}version: "${data.version || 'Unset'}"`);
|
||||
|
||||
// 2. Build the 'alarms' section from the alarms data
|
||||
if (data.alarms && data.alarms.length > 0) {
|
||||
if (data.alarms) {
|
||||
yamlLines.push(''); // Blank line for separation
|
||||
yamlLines.push(`${sp2}alarms:`);
|
||||
yamlLines.push(`${sp4}packing: OBJECTS`); // As per PowerShell script logic
|
||||
yamlLines.push(`${sp4}behavior: ${data.alarmBehavior || 'STATIC'}`);
|
||||
yamlLines.push(`${sp4}count: ${data.alarms.length}`);
|
||||
yamlLines.push(`${sp4}alarms: `); // Note the trailing space
|
||||
yamlLines.push(`${sp4}count: ${data.alarmCount}`);
|
||||
if (data.alarms.length > 0) {
|
||||
yamlLines.push(`${sp4}alarms: `); // Note the trailing space
|
||||
data.alarms.forEach(alarm => {
|
||||
const id = alarm.id || 0;
|
||||
const name = alarm.name || `Alarm_${id}`;
|
||||
const message = alarm.message.replace(/:/g, ';'); // Sanitize message as in PowerShell
|
||||
|
||||
data.alarms.forEach(alarm => {
|
||||
const id = alarm.id || 0;
|
||||
const name = alarm.name || `Alarm_${id}`;
|
||||
const message = alarm.message.replace(/:/g, ';'); // Sanitize message as in PowerShell
|
||||
yamlLines.push(`${sp6}- ID: ${id}`);
|
||||
yamlLines.push(`${sp8}name: ${name}`);
|
||||
yamlLines.push(`${sp8}message: ${message}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//3. Build the 'warning' section from the warnings data
|
||||
if (data.warnings && data.warnings.length > 0) {
|
||||
yamlLines.push(''); // Blank line for separation
|
||||
yamlLines.push(`${sp2}warnings:`);
|
||||
yamlLines.push(`${sp4}count: ${data.warnings.length}`);
|
||||
yamlLines.push(`${sp4}warnings: `); // Note the trailing space
|
||||
|
||||
data.warnings.forEach(warning => {
|
||||
const id = warning.id || 0;
|
||||
const name = warning.name || `Warning_${id}`;
|
||||
const message = warning.message.replace(/:/g, ';'); // Sanitize message as in PowerShell
|
||||
|
||||
yamlLines.push(`${sp6}- ID: ${id}`);
|
||||
yamlLines.push(`${sp8}name: ${name}`);
|
||||
@ -72,6 +91,24 @@ app.whenReady().then(() => {
|
||||
});
|
||||
}
|
||||
|
||||
//Build the 'counters' section from the counters data
|
||||
if (data.counters && data.counters.length > 0) {
|
||||
yamlLines.push(''); // Blank line for separation
|
||||
yamlLines.push(`${sp2}counters:`);
|
||||
|
||||
data.counters.forEach(counter => {
|
||||
const id = counter.id || 0;
|
||||
const name = counter.name || `Counter_${id}`;
|
||||
const type = counter.type || '';
|
||||
const description = counter.description || '';
|
||||
|
||||
yamlLines.push(`${sp4}- name: ${name}`);
|
||||
yamlLines.push(`${sp6}ID: ${id}`);
|
||||
yamlLines.push(`${sp6}type: ${type}`);
|
||||
yamlLines.push(`${sp6}description: ${description}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Build and Append the 'optionals' section if needed
|
||||
if (data.stopReason === 'Yes') {
|
||||
yamlLines.push(''); // Add a blank line for spacing
|
||||
|
||||
115
renderer.js
115
renderer.js
@ -53,6 +53,58 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
alarmsTableBody.appendChild(clone);
|
||||
});
|
||||
|
||||
const addWarningBtn = document.getElementById('add-warning-btn');
|
||||
const warningsTableBody = document.getElementById('warnings-table-body');
|
||||
const warningTemplate = document.getElementById('warning-row-template');
|
||||
|
||||
addWarningBtn.addEventListener('click', () => {
|
||||
const currentIdInputs = warningsTableBody.querySelectorAll('[name="warning_id"]');
|
||||
let maxId = 0;
|
||||
currentIdInputs.forEach(input => {
|
||||
const currentId = parseInt(input.value, 10);
|
||||
if (!isNaN(currentId) && currentId > maxId) {
|
||||
maxId = currentId;
|
||||
}
|
||||
});
|
||||
const nextWarningId = maxId + 1;
|
||||
const paddedWarningId = nextWarningId.toString().padStart(3, '0');
|
||||
|
||||
const clone = warningTemplate.content.cloneNode(true);
|
||||
clone.querySelector('[name="warning_id"]').value = nextWarningId;
|
||||
clone.querySelector('[name="warning_name"]').value = `WARNING_${paddedWarningId}`;
|
||||
const removeBtn = clone.querySelector('.remove-row-btn');
|
||||
removeBtn.addEventListener('click', (e) => {
|
||||
e.target.closest('tr').remove();
|
||||
});
|
||||
warningsTableBody.appendChild(clone);
|
||||
});
|
||||
|
||||
//dynamic row for the counter tab
|
||||
const addCounterBtn = document.getElementById('add-counter-btn');
|
||||
const countersTableBody = document.getElementById('counters-table-body');
|
||||
const counterTemplate = document.getElementById('counter-row-template');
|
||||
|
||||
addCounterBtn.addEventListener('click', () => {
|
||||
const currentIdInputs = countersTableBody.querySelectorAll('[name="counter_id"]');
|
||||
let maxId = 0;
|
||||
currentIdInputs.forEach(input => {
|
||||
const currentId = parseInt(input.value, 10);
|
||||
if (!isNaN(currentId) && currentId > maxId) {
|
||||
maxId = currentId;
|
||||
}
|
||||
});
|
||||
const nextCounterId = maxId + 1;
|
||||
const paddedCounterId = nextCounterId.toString().padStart(3, '0');
|
||||
|
||||
const clone = counterTemplate.content.cloneNode(true);
|
||||
clone.querySelector('[name="counter_id"]').value = nextCounterId;
|
||||
clone.querySelector('[name="counter_name"]').value = `COUNTER_${paddedCounterId}`;
|
||||
const removeBtn = clone.querySelector('.remove-row-btn');
|
||||
removeBtn.addEventListener('click', (e) => {
|
||||
e.target.closest('tr').remove();
|
||||
});
|
||||
countersTableBody.appendChild(clone);
|
||||
});
|
||||
|
||||
// --- 3. FORM SUBMISSION LOGIC ---
|
||||
const form = document.getElementById('machine-form');
|
||||
@ -86,8 +138,37 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
});
|
||||
|
||||
// D. Collect all warning rows from the table
|
||||
const warningRows = warningsTableBody.querySelectorAll('tr');
|
||||
const warningsData = [];
|
||||
warningRows.forEach(row => {
|
||||
const warning = {
|
||||
id: row.querySelector('[name="warning_id"]').value,
|
||||
name: row.querySelector('[name="warning_name"]').value,
|
||||
message: row.querySelector('[name="warning_message"]').value,
|
||||
};
|
||||
if (warning.message) {
|
||||
warningsData.push(warning);
|
||||
}
|
||||
});
|
||||
|
||||
// E. Collect all counter rows from the table
|
||||
const counterRows = countersTableBody.querySelectorAll('tr');
|
||||
const countersData = [];
|
||||
counterRows.forEach(row => {
|
||||
const counter = {
|
||||
id: row.querySelector('[name="counter_id"]').value,
|
||||
name: row.querySelector('[name="counter_name"]').value,
|
||||
type: row.querySelector('[name="counter_type"]').value,
|
||||
description: row.querySelector('[name="counter_description"]').value,
|
||||
};
|
||||
if (counter.type) {
|
||||
countersData.push(counter);
|
||||
}
|
||||
});
|
||||
|
||||
// C. Combine all data into a single object
|
||||
const finalData = { ...mainData, alarms: alarmsData };
|
||||
const finalData = { ...mainData, alarms: alarmsData, warnings: warningsData, counters: countersData };
|
||||
|
||||
console.log('Sending all collected data to main process:', finalData);
|
||||
|
||||
@ -106,4 +187,36 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --- 4. TAB NAVIGATION BUTTONS ---
|
||||
const tabOrder = [
|
||||
'machine-tab',
|
||||
'alarms-tab',
|
||||
'warnings-tab',
|
||||
'counters-tab',
|
||||
'setpoints-tab',
|
||||
'realtime-tab'
|
||||
];
|
||||
|
||||
// Add event listeners to all "Next" buttons
|
||||
document.querySelectorAll('.next-tab-btn').forEach((btn, idx) => {
|
||||
btn.addEventListener('click', () => {
|
||||
// Move to the next tab
|
||||
const nextTabId = tabOrder[idx + 1];
|
||||
if (nextTabId) {
|
||||
// Switch tab button active state
|
||||
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
|
||||
document.querySelector(`[data-tab="${nextTabId.replace('-tab','')}"]`).classList.add('active');
|
||||
// Switch tab content active state
|
||||
document.querySelectorAll('.tab-content').forEach(tc => tc.classList.remove('active'));
|
||||
document.getElementById(nextTabId).classList.add('active');
|
||||
// Show submit button only on last tab
|
||||
if (nextTabId === 'realtime-tab') {
|
||||
document.getElementById('submit-container').style.display = '';
|
||||
} else {
|
||||
document.getElementById('submit-container').style.display = 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user