Electron_generator/index.html

637 lines
30 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- <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;
color: #333;
line-height: 1.6;
margin: 0;
padding: 20px;
}
.container {
max-width: 1000px;
margin: auto;
background: #fff;
padding: 25px;
border-radius: 10px;
box-shadow: 0 0 20px rgba(0,0,0,0.1);
}
h2 {
color: #2649B2;
text-align: center;
margin-bottom: 25px;
}
fieldset {
border: 1px solid #D4D9F0;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
}
legend {
font-weight: bold;
color: #4A74F3;
padding: 0 10px;
font-size: 1.2em;
}
.form-group {
margin-bottom: 18px;
}
label {
display: block;
margin-bottom: 6px;
font-weight: bold;
}
input[type="text"],
input[type="number"],
input[type="email"],
input[type="date"],
select {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
box-sizing: border-box;
}
input[readonly] {
background-color: #e9ecef;
cursor: not-allowed;
}
.radio-group {
display: flex;
align-items: center; /* This vertically aligns the text with the button */
}
.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;
margin-top: 5px;
color: #6c757d;
}
input[type="submit"] {
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;
}
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 */
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;
}
.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;
border: none;
padding: 10px 15px;
cursor: pointer;
font-size: 1em;
color: #6c757d;
border-bottom: 3px solid transparent;
}
.tab-nav button.active {
color: #2649B2;
border-bottom-color: #2649B2;
font-weight: bold;
}
.tab-content {
display: none;
padding-top: 20px;
}
.tab-content.active {
display: block;
}
.dynamic-table {
width: 100%;
border-collapse: collapse;
margin-top: 15px;
}
.dynamic-table th, .dynamic-table td {
border: 1px solid #D4D9F0;
padding: 8px;
text-align: left;
vertical-align: top;
}
.dynamic-table th {
background-color: #f4f4f9;
}
.dynamic-table input, .dynamic-table textarea {
padding: 5px;
}
.dynamic-table .col-id {
width: 10%;
}
.dynamic-table .col-name {
width: 25%;
}
.dynamic-table .col-actions {
width: 15%;
text-align: center;
}
.add-row-btn {
margin-top: 15px;
padding: 8px 12px;
background-color: #6C8BE0;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
.add-row-btn:hover {
background-color: #5a7dc2;
}
.remove-row-btn {
padding: 5px 10px;
background-color: #e06c6c;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
.remove-row-btn:hover {
background-color: #c55a5a;
}
</style>
</head>
<body>
<div class="container">
<h2>Machine Description</h2>
<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" id="realtime-tab-btn">Real Time Variables</button>
<button class="tab-btn" data-tab="states" id="states-tab-btn">States</button>
</nav>
<form id="machine-form">
<div class="tab-content" id="machine-tab" active>
<fieldset>
<legend>Machine Details</legend>
<div class="form-group">
<label for="machineType">MachineType</label>
<select id="machineType" name="machineType">
<option value="Unspecified">Unspecified</option>
<option value="Accumulation">Accumulation</option>
<option value="BulkFeedingSystem">BulkFeedingSystem</option>
<option value="Bundler">Bundler</option>
<option value="Capper">Capper</option>
<option value="Cartoner">Cartoner</option>
<option value="CasePacker">CasePacker</option>
<option value="CaseSealer">CaseSealer</option>
<option value="CheckWeigher">CheckWeigher</option>
<option value="DePalletizer">DePalletizer</option>
<option value="Feeder">Feeder</option>
<option value="Filler">Filler</option>
<option value="FillerCapper">FillerCapper</option>
<option value="FillSeal">FillSeal</option>
<option value="FormFillSeal">FormFillSeal</option>
<option value="Labeller">Labeller</option>
<option value="LineManagementController">LineManagementController</option>
<option value="OverWrapper">OverWrapper</option>
<option value="Palletizer">Palletizer</option>
<option value="PickAndPlace">PickAndPlace</option>
<option value="ShrinkWrapper">ShrinkWrapper</option>
</select>
</div>
<div class="form-group">
<label for="vendor">Vendor</label>
<input type="text" id="vendor" name="vendor">
<small>Company name of the machine vendor. Avoid whitespaces or special characters.</small>
</div>
<div class="form-group">
<label for="model">Model</label>
<input type="text" id="model" name="model">
</div>
<div class="form-group">
<label for="serialNo">SerialNo</label>
<input type="text" id="serialNo" name="serialNo">
</div>
<div class="form-group">
<label for="buildYear">Build Year</label>
<input type="number" id="buildYear" name="buildYear" placeholder="Ex: 2024" min="1980" max="2100">
</div>
<div class="form-group">
<label for="browseName">BrowseName (OPC UA)</label>
<input type="text" id="browseName" name="browseName">
<small>This is the name of the machine which appears in the OPC UA server. Recommendation: MachineType + Numeric Suffix (e.g., Labeller1).</small>
</div>
</fieldset>
<fieldset>
<legend>Publication & Versioning</legend>
<div class="form-group">
<label for="publicationDate">Publication Date</label>
<input type="date" id="publicationDate" name="publicationDate">
</div>
<div class="form-group">
<label for="authorEmail">Author Email</label>
<input type="email" id="authorEmail" name="authorEmail" placeholder="firstname.lastname@loreal.com">
<small>Email of the person to contact about this spreadsheet.</small>
</div>
<div class="form-group">
<label for="version">Version</label>
<input type="text" id="version" name="version" placeholder="Ex: 1.0.0">
</div>
<div class="form-group">
<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>
<fieldset>
<legend>Alarm Configuration</legend>
<div class="form-group">
<label for="alarmRepresentation">AlarmRepresentation</label>
<select id="alarmRepresentation" name="alarmRepresentation">
<option value="DistinctAlarms">DistinctAlarms (Each alarm is a separate OPC UA variable)</option>
<option value="SingleAlarmsArray">SingleAlarmsArray (Alarms are packed in a Boolean array)</option>
</select>
</div>
<div class="form-group">
<label for="alarmBehavior">AlarmBehavior</label>
<select id="alarmBehavior" name="alarmBehavior">
<option value="STATIC">STATIC (Static allocation, recommended)</option>
<option value="DYNAMIC">DYNAMIC (Alarms are allocated in a dynamic table)</option>
</select>
</div>
<div class="form-group">
<label for="alarmCount">AlarmCount</label>
<input type="number" id="alarmCount" name="alarmCount" min="0">
<small>Used as the size of the array for DYNAMIC alarm behavior.</small>
</div>
<div class="form-group">
<label for="messagesLanguage">MessagesLanguage</label>
<select id="messagesLanguage" name="messagesLanguage">
<option value="EN">EN (English)</option>
<option value="FR">FR (Français)</option>
<option value="DE">DE (Deutsch)</option>
<option value="ES">ES (Español)</option>
<option value="IT">IT (Italiano)</option>
</select>
</div>
</fieldset>
<fieldset>
<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" checked>
<label for="stopReasonYes">Yes</label>
<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. 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">
<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>
<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>
<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>
<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>
</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>
<p>Define all alarms for the machine. If the message is empty, the alarm will not be generated.</p>
<p>You can copy and paste alarms from an Excel spreadsheet directly into the table below. (missing lines will be generated automatically)</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="alarms-table-body"></tbody>
</table>
<input type="number" id="bulk-alarm-count" min="1" placeholder="Number of alarms to add" style="width: 180px;">
<button type="button" id="add-multiple-alarms-btn" class="add-row-btn" style="width:auto;">Add Alarms</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>
<p>You can copy and paste alarms from an Excel spreadsheet directly into the table below. (missing lines will be generated automatically)</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>
<input type="number" id="bulk-warning-count" min="1" placeholder="Number of warnings to add">
<button type="button" id="add-multiple-warnings-btn" class="add-row-btn">Add Warnings</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>
<p>You can copy and paste alarms from an Excel spreadsheet directly into the table below. (missing lines will be generated automatically)</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>
<p>You can copy and paste alarms from an Excel spreadsheet directly into the table below. (missing lines will be generated automatically)</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>
<p>You can copy and paste alarms from an Excel spreadsheet directly into the table below. (missing lines will be generated automatically)</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>
<button type="button" class="next-tab-btn">Next</button>
</div>
<div id="states-tab" class="tab-content">
<fieldset>
<legend>States Exposed by the Machine</legend>
<p>The first four states are predefined by PackML and are mandatory. You can add more states as needed.</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-description">Description</th>
</tr>
</thead>
<tbody> <!-- Predefined states -->
<tr>
<td><input type="text" value="1" disabled></td>
<td><input type="text" value="StateCurrent" disabled></td>
<td><textarea rows="2" cols="50" disabled>Current state of the packML State Machine defined as an enumeration described in EOS-002-OPEN-202</textarea></td>
<td><span style="color: #999;">Predefined</span></td>
</tr>
<tr>
<td><input type="text" value="2" disabled></td>
<td><input type="text" value="ModeCurrent" disabled></td>
<td><textarea rows="2" cols="50" disabled>Current Mode of the packML State Machine defined as an enumeration described in EOS-002-OPEN-202</textarea></td>
<td><span style="color: #999;">Predefined</span></td>
</tr>
<tr>
<td><input type="text" value="3" disabled></td>
<td><input type="text" value="EquipmentInterlock : Blocked" disabled></td>
<td><textarea rows="3" cols="50" disabled>If TRUE, then processing is suspended because downstream equipment is unable to receive material (e.g. downstream buffer is full)</textarea></td>
<td><span style="color: #999;">Predefined</span></td>
</tr>
<tr>
<td><input type="text" value="4" disabled></td>
<td><input type="text" value="EquipmentInterlock : Starved" disabled></td>
<td><textarea rows="2" cols="50" disabled>If TRUE, then processing is suspended because upstream equipment is unable to send material.</textarea></td>
<td><span style="color: #999;">Predefined</span></td>
</tr>
</tbody>
<tbody id="states-table-body"></tbody>
</table>
<button type="button" id="add-state-btn" class="add-row-btn" style="width:auto;">Add States</button>
</fieldset>
<button type="button" class="next-tab-btn" style="visibility: hidden;">Next</button>
<input type="submit" value="Validate and Submit Description">
</div>
</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_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>
<template id="state-row-template">
<tr>
<td><input type="number" name="state_id" min="5" placeholder="e.g., 5"></td>
<td><input type="text" name="state_name" placeholder="e.g., CUSTOM_STATE"></td>
<td><textarea name="state_description" rows="1" cols="50" placeholder="Description of the state"></textarea></td>
<td class="col-actions"><button type="button" class="remove-row-btn">Remove</button></td>
</tr>
</template>
<script src="renderer.js"></script>
</body>
</html>