Reshuffle file structure

This commit is contained in:
Hakan Bastedt
2023-12-31 09:17:42 +01:00
parent 1851f4168e
commit 485901120c
125 changed files with 45 additions and 45 deletions

627
EEPROM_generator/src/ui.js Normal file
View File

@@ -0,0 +1,627 @@
/**
* SOES EEPROM generator
* UI behavior logic
* This tool serves as:
- EtherCAT Slave Information XML + EEPROM binary generator
- SOES code generator
* Victor Sluiter 2013-2018
* Kuba Buda 2020-2021
*/
'use strict'
// ####################### UI changes handlers ####################### //
function getForm() {
return document.getElementById("SlaveForm");
}
function getOutputForm() {
return document.getElementById("outCodeForm");
}
function onFormChanged() {
const form = getForm();
saveLocalBackup(form);
processForm(form);
}
/** Shortcuts:
* Ctrl + S to save project
* Ctrl + O to load save file
* Shortcuts start to work after user clicked on page
*/
document.onkeydown = function(e) {
const S_keyCode = 83;
const O_keyCode = 79;
if (e.ctrlKey){
if (e.keyCode === S_keyCode) {
event.preventDefault();
onSaveClick();
return false;
}
else if (e.keyCode == O_keyCode) {
event.preventDefault();
onRestoreClick();
return false;
}
}
};
// When the user clicks anywhere outside of the modal, close it
window.onclick = function(event) {
if (event.target == odModal) {
odModalClose();
}
}
window.onload = (event) => {
odModalSetup();
syncModalSetup();
const form = getForm();
setFormValues(form, getFormDefaultValues());
tryRestoreLocalBackup(form);
// for convinience during tool development, trigger codegen on page refresh
processForm(form);
const _isComputerFast = automaticCodegen;
if (_isComputerFast) {
// code is regenerated on every form change.
// no need to remember to generate before copying or downloading
// app is noticeably slower
processForm(form); // make sure displayed code is up to date at startup, e.g redo, if it came from backup
document.getElementById('GenerateFilesButton').style.display = 'none'; // 'generate' button no longer needed
form.addEventListener('change', function() {
onFormChanged();
});
}
setupDarkMode();
}
// ####################### dark mode logic ####################### //
function setupDarkMode() {
if (!localStorage.darkMode) {
localStorage.darkMode = 'dark'; // dark mode by default
}
document.documentElement.setAttribute("data-theme", localStorage.darkMode);
}
function toggleDarkMode() {
var newMode = (localStorage.darkMode == 'dark') ? "light" : "dark"
localStorage.darkMode = newMode;
document.documentElement.setAttribute("data-theme", localStorage.darkMode);
}
// ####################### code generation UI logic ####################### //
/** Code generation method, triggered by UI */
function processForm(form)
{
const od = buildObjectDictionary(form);
const indexes = getUsedIndexes(od);
var outputCtl = getOutputForm();
outputCtl.objectlist.value = objectlist_generator(form, od, indexes);
outputCtl.ecat_options.value = ecat_options_generator(form, od, indexes);
outputCtl.utypes.value = utypes_generator(form, od, indexes);
outputCtl.HEX.hexData = hex_generator(form);
outputCtl.HEX.value = toIntelHex(outputCtl.HEX.hexData);
outputCtl.ESI.value = esi_generator(form, od, indexes, _dc);
saveLocalBackup(form);
return outputCtl;
}
// ####################### Button handlers ####################### //
function getProjectName(form) {
return variableName(form.TextDeviceName.value);
}
function onGenerateDownloadClick()
{
const form = getForm();
var result = processForm(form);
downloadGeneratedFilesZipped(form, result);
function downloadGeneratedFilesZipped(form, result) {
var zip = new JSZip();
const projectName = getProjectName(form);
zip.file(`${projectName}.xml`, result.ESI.value);
zip.file('eeprom.hex', result.HEX.value);
zip.file('eeprom.bin', result.HEX.hexData);
zip.file('ecat_options.h', result.ecat_options.value);
zip.file('objectlist.c', result.objectlist.value);
zip.file('utypes.h', result.utypes.value);
zip.file('esi.json', prepareBackupFileContent(form));
zip.generateAsync({type:"blob"}).then(function (blob) { // generate the zip file
downloadFile(blob, "esi.zip", "application/zip"); // trigger the download
}, function (err) {
console.log(err);
});
}
function downloadGeneratedFiles(form, result) {
const projectName = getProjectName(form);
downloadFile(result.ESI.value, `${projectName}.xml`, 'text/html');
downloadFile(result.HEX.value, 'eeprom.hex', 'application/octet-stream');
downloadFile(result.ecat_options.value, 'ecat_options.h', 'text/plain');
downloadFile(result.objectlist.value, 'objectlist.c', 'text/plain');
downloadFile(result.utypes.value, 'utypes.h', 'text/plain');
downloadBackupFile(form);
}
}
function onGenerateClick() {
processForm(getForm());
}
function onSaveClick() {
const form = getForm();
downloadBackupFile(form);
saveLocalBackup(form);
}
function onRestoreClick() {
// trigger file input dialog window
document.getElementById('restoreFileInput').click();
}
function onRestoreComplete(fileContent) {
const form = getForm();
restoreBackup(fileContent, form);
processForm(form);
}
function onResetClick() {
if (confirm("Are you sure you want to reset project to default values?")){
resetLocalBackup();
location.reload(true);
}
}
function onDownloadEsiXmlClick() {
const form = getForm();
const projectName = getProjectName(form);
downloadFile(getOutputForm().ESI.value, `${projectName}.xml`, 'text/html');
}
function onDownloadBinClick() {
const record = getOutputForm().HEX.hexData;
if (!record) { alert("Generate code before you can download it"); return; }
downloadFile(record, 'eeprom.bin', 'application/octet-stream');
}
// ####################### Handle modal dialog ####################### //
var odModal = {};
function odModalSetup() {
// Get the modal
odModal = document.getElementById("editObjectModal");
if (odModal) {
odModal.form = document.getElementById('EditObjectForm');
}
else {
alert("Element required to edit Object Dictionary not found!");
}
}
// When the user clicks the button, open the modal
function odModalOpen() {
odModal.style.display = "block";
}
function odModalClose() {
odModal.style.display = "none";
}
/** update control values on OD modal */
function odModalUpdate(index, objd) {
odModal.form.Index.value = `0x${index}`;
odModal.form.ObjectName.value = objd.name;
odModal.form.DTYPE.value = objd.dtype || DTYPE.UNSIGNED8;
odModal.form.Access.value = objd.access || 'RO';
odModal.objd = objd;
}
function odModalHideControls() {
document.getElementById('dialogRowIndex').style.display = 'none';
document.getElementById('dialogRowDtype').style.display = 'none';
document.getElementById('dialogRowValue').style.display = 'none';
document.getElementById('dialogRowAccess').style.display = 'none';
}
// ####################### Modal dialogs for OD edition ####################### //
function editExistingOD_ObjectDialog(odSectionName, index, otype) {
const od = getObjDictSection(odSectionName);
var objd = od[index];
odModal.index_initial_value = index;
checkObjectType(otype, objd);
odModalUpdate(index, objd);
}
function addNewOD_ObjectDialog(odSectionName, otype) {
var objd = getNewObjd(odSectionName, otype);
var index = getFirstFreeIndex(odSectionName);
delete odModal.index_initial_value; // add new object, not replace edited one
odModalUpdate(index, objd);
}
function odModalOpenForObject(otype) {
odModalHideControls();
document.getElementById('dialogRowIndex').style.display = '';
document.getElementById('dialogRowAccess').style.display = '';
switch (otype) {
case OTYPE.VAR: {
document.getElementById('dialogRowDtype').style.display = '';
document.getElementById('dialogRowValue').style.display = '';
break;
}
case OTYPE.ARRAY: {
document.getElementById('dialogRowDtype').style.display = "";
break;
}
case OTYPE.RECORD: {
break;
}
default: {
alert(`Unknown object type ${otype}, cannot open modal for it!`);
return;
}
}
odModalOpen();
document.getElementById('modalInputIndex').focus();
}
function odModalSetTitle(message) {
document.getElementById('editObjectTitle').innerHTML = `<strong>${message}</strong>`;
}
// ####################### Edit Object Dictionary UI logic ####################### //
function editVAR_Click(odSectionName, indexValue = null) {
const otype = OTYPE.VAR;
const index = indexToString(indexValue);
var actionName = "Edit";
odModal.odSectionName = odSectionName;
if (objectExists(odSectionName, index)) {
editExistingOD_ObjectDialog(odSectionName, index, otype);
odModal.form.DTYPE.value = odModal.objd.dtype;
} else {
addNewOD_ObjectDialog(odSectionName, otype);
actionName = "Add"
}
odModalSetTitle(`${actionName} ${odSectionName.toUpperCase()} variable`);
odModalOpenForObject(otype);
}
function editARRAY_Click(odSectionName, indexValue = null) {
const otype = OTYPE.ARRAY;
const index = indexToString(indexValue);
var actionName = "Edit";
odModal.odSectionName = odSectionName;
odModal.form.Access
if (objectExists(odSectionName, index)) {
editExistingOD_ObjectDialog(odSectionName, index, otype);
odModal.form.DTYPE.value = odModal.objd.dtype;
} else {
addNewOD_ObjectDialog(odSectionName, otype);
actionName = "Add"
}
odModalSetTitle(`${actionName} ${odSectionName.toUpperCase()} array`);
odModalOpenForObject(otype);
}
function editRECORD_Click(odSectionName, indexValue = null) {
const otype = OTYPE.RECORD;
const index = indexToString(indexValue);
var actionName = "Edit";
odModal.odSectionName = odSectionName;
if (objectExists(odSectionName, index)) {
editExistingOD_ObjectDialog(odSectionName, index, otype);
} else {
addNewOD_ObjectDialog(odSectionName, otype);
actionName = "Add"
}
odModalSetTitle(`${actionName} ${odSectionName.toUpperCase()} record`);
odModalOpenForObject(otype);
}
function onEditObjectSubmit(modalform) {
if (odModal.subitem) {
onEditSubitemSubmit(odModal.subitem);
delete odModal.subitem;
return;
}
const objd = odModal.objd;
const objectType = objd.otype;
const index = indexToString(modalform.Index.value);
objd.name = modalform.ObjectName.value;
switch (objectType) {
case OTYPE.VAR:
objd.dtype = modalform.DTYPE.value;
if (objd.dtype == DTYPE.VISIBLE_STRING) {
objd.data = ''
} else {
objd.value = modalform.InitalValue.value;
}
break;
case OTYPE.ARRAY:
objd.dtype = modalform.DTYPE.value;
break;
case OTYPE.RECORD:
break;
default:
alert(`Unexpected type ${objectType} on object ${modalform.ObjectName} being edited!`);
break;
}
const odSection = getObjDictSection(odModal.odSectionName);
if (odModal.index_initial_value) {
removeObject(odSection, odModal.index_initial_value); // detach from OD, to avoid duplicate if index changed
}
addObject(odSection, objd, index); // attach updated object
odModalClose();
reloadOD_Section(odModal.odSectionName);
delete odModal.odSectionName;
odModal.objd = {};
onFormChanged();
}
function onRemoveClick(odSectionName, indexValue, subindex = null) {
const index = indexToString(indexValue);
const odSection = getObjDictSection(odSectionName);
const objd = odSection[index];
if(!objd) { alert(`${odSectionName.toUpperCase()} object ${index} does not exist!`); return; }
if(subindex) {
if(!objd.items) { alert(`Object 0x${index} "${objd.name}" does not have any items!`); return; }
if(objd.items.length < subindex) { alert(`Object 0x${index} "${objd.name}" does not have enough items!`); return; }
if(objd.items.length < 3) { // only max subindex and one more subitem
alert(`Object 0x${index} "${objd.name}" has only 1 subitem, it should not be empty. Remove entire object instead.`); return; }
}
if (confirm(getConfirmMessage(objd, index, subindex))) {
if (subindex) {
const subitemsToRemove = 1;
objd.items.splice(subindex, subitemsToRemove);
} else {
removeObject(odSection, index);
}
reloadOD_Section(odSectionName);
onFormChanged();
}
function getConfirmMessage(objd, index, subindex) {
if (subindex) {
return `Are you sure you want to remove subitem ${subindex} "${objd.items[subindex].name }" from object 0x${index} "${objd.name}"?`
}
return `Are you sure you want to remove object 0x${index} "${objd.name}"?`
}
}
function addSubitemClick(odSectionName, indexValue) {
const index = indexToString(indexValue);
const odSection = getObjDictSection(odSectionName);
const objd = odSection[index];
// we expect objd to have items array with at least [{ name: 'Max SubIndex' }]
if(!objd.items || !objd.items.length ) { alert(`Object ${index} "${objd.name}" has no subitems!`); return; }
switch(objd.otype) {
case OTYPE.ARRAY: {
addArraySubitem(objd);
break;
}
case OTYPE.RECORD: {
addRecordSubitem(objd);
break;
}
default: {
alert(`Object ${index} "${objd.name}" has OTYPE ${objd.otype} so cannot add subitems`);
}
}
const subindex = objd.items.length - 1; // subitem is added to end of items list
editSubitemClick(odSectionName, indexValue, subindex, "Add");
}
function editSubitemClick(odSectionName, indexValue, subindex, actionName = "Edit") {
const index = indexToString(indexValue);
const odSection = getObjDictSection(odSectionName);
const objd = odSection[index];
if(!objd.items || objd.items.length <= subindex ) { alert(`Object ${index} "${objd.name}" does not have ${subindex} subitems!`); return; }
odModalSetTitle(`${actionName} ${odSectionName.toUpperCase()} object 0x${index} "${objd.name}" subitem 0x${indexToString(subindex)}`);
const subitem = objd.items[subindex];
odModalHideControls();
document.getElementById('dialogRowValue').style.display = "";
odModal.form.InitalValue.value = subitem.value ?? 0;
if (objd.otype == OTYPE.RECORD) {
document.getElementById('dialogRowDtype').style.display = "";
odModal.form.DTYPE.value = subitem.dtype;
document.getElementById('dialogRowAccess').style.display = ''; // access for record subitems can differ
odModal.form.Access.value = subitem.access || 'RO';
}
odModal.form.ObjectName.value = subitem.name;
odModal.subitem = { odSectionName: odSectionName, index: index, subindex: subindex };
odModalOpen();
document.getElementById('modalInputObjectName').focus();
}
function onEditSubitemSubmit(modalSubitem) {
const odSection = getObjDictSection(modalSubitem.odSectionName);
const objd = odSection[modalSubitem.index];
const subitem = objd.items[modalSubitem.subindex];
subitem.name = odModal.form.ObjectName.value;
subitem.value = odModal.form.InitalValue.value;
if (objd.otype == OTYPE.RECORD) {
subitem.dtype = odModal.form.DTYPE.value;
subitem.access = odModal.form.Access.value;
}
odModalClose();
onFormChanged();
reloadOD_Section(modalSubitem.odSectionName);
}
// ####################### Display Object Dictionary state ####################### //
function reloadOD_Sections() {
reloadOD_Section(sdo);
reloadOD_Section(txpdo);
reloadOD_Section(rxpdo);
}
function reloadOD_Section(odSectionName) {
const odSection = getObjDictSection(odSectionName);
var indexes = getUsedIndexes(odSection);
var section = '';
indexes.forEach(index => {
const objd = odSection[index];
section += `<div class="odItem"><span class="odItemContent"><strong>0x${index}</strong> &nbsp; &nbsp; "${objd.name}" ${objd.otype} ${objd.dtype ?? ''}</span><span>`;
if (objd.otype == OTYPE.ARRAY || objd.otype == OTYPE.RECORD) {
section += `<button onClick='addSubitemClick(${odSectionName}, 0x${index})'>&nbsp; Add subitem &nbsp;</button>`;
}
section += `<button onClick='onRemoveClick(${odSectionName}, 0x${index})'>&nbsp; ❌ Remove &nbsp;</button>`;
section += `<button onClick='edit${objd.otype}_Click(${odSectionName}, 0x${index})'>&nbsp; 🛠️ &nbsp; Edit &nbsp;</button>`;
section += `</span></div>`;
if (objd.items) {
var subindex = 1; // skip Max Subindex
objd.items.slice(subindex).forEach(subitem => {
var subindexHex = subindex < 16 ? `0${subindex.toString(16)}` : subindex.toString(16);
section += `<div class="odSubitem"><span class="odSubitemContent"><strong>:0x${subindexHex}</strong>&nbsp;&nbsp; "${subitem.name}" ${subitem.dtype ?? ''}</span>`;
section += `<span><button onClick='onRemoveClick(${odSectionName}, 0x${index}, ${subindex})'>&nbsp; ❌ Remove &nbsp;</button>`;
section += `<button onClick='editSubitemClick(${odSectionName}, 0x${index}, ${subindex})'>&nbsp; 🛠️ &nbsp; Edit &nbsp;</button>`;
section += `</span></div>`;
++subindex;
});
}
});
const odSectionElement = document.getElementById(`tr_${odSectionName}`);
if (odSectionElement) {
odSectionElement.innerHTML = section;
}
}
// ####################### Synchronization settings UI ####################### //
var syncModal = {};
var _dc = []
function syncModalSetup() {
// Get the modal
syncModal = document.getElementById("syncModal");
if (syncModal) {
syncModal.form = document.getElementById('syncModalForm');
}
else {
alert("Element required to edit Object Dictionary not found!");
}
}
function syncModalClose() {
syncModal.style.display = "none";
delete syncModal.edited;
reloadSyncModes();
}
function syncModalOpen() {
syncModal.style.display = "block";
}
function syncModeEdit(sync) {
syncModal.edited = sync;
syncModal.form.Name.value = sync.Name;
syncModal.form.Description.value = sync.Description;
syncModal.form.AssignActivate.value = sync.AssignActivate;
syncModal.form.Sync0cycleTime.value = sync.Sync0cycleTime;
syncModal.form.Sync0shiftTime.value = sync.Sync0shiftTime;
syncModal.form.Sync1cycleTime.value = sync.Sync1cycleTime;
syncModal.form.Sync1shiftTime.value = sync.Sync1shiftTime;
syncModalOpen();
}
function onSyncSubmit(syncForm) {
syncModal.edited.Name = syncForm.Name.value;
syncModal.edited.Description = syncForm.Description.value;
syncModal.edited.AssignActivate = syncForm.AssignActivate.value;
syncModal.edited.Sync0cycleTime = syncForm.Sync0cycleTime.value;
syncModal.edited.Sync0shiftTime = syncForm.Sync0shiftTime.value;
syncModal.edited.Sync1cycleTime = syncForm.Sync1cycleTime.value;
syncModal.edited.Sync1shiftTime = syncForm.Sync1shiftTime.value;
syncModalClose();
onFormChanged();
}
// ####################### Synchronization settings button handlers ####################### //
function addSyncClick() {
const newSyncMode = {
Name: "DcOff",
Description: "DC unused",
AssignActivate: "#x000",
Sync0cycleTime: 0,
Sync0shiftTime: 0,
Sync1cycleTime: 0,
Sync1shiftTime: 0,
}
_dc.push(newSyncMode);
syncModeEdit(newSyncMode);
}
function onEditSyncClick(i) {
const syncMode = _dc[i];
syncModeEdit(syncMode);
}
function onRemoveSyncClick(i) {
_dc.splice(i, 1);
reloadSyncModes();
onFormChanged();
}
// ####################### Synchronization settings UI ####################### //
function reloadSyncModes() {
var section = '';
var i = 0;
_dc.forEach(sync => {
section += `<div class="odItem"><span class="odItemContent"><strong>${sync.Name}</strong> &nbsp; &nbsp; ${sync.Description} &nbsp; [${sync.AssignActivate}]</span><span>`;
section += `<button onClick='onRemoveSyncClick(${i})'>&nbsp; ❌ Remove &nbsp;</button>`;
section += `<button onClick='onEditSyncClick(${i})'>&nbsp; 🛠️ &nbsp; Edit &nbsp;</button>`;
section += `</span></div>`;
++i;
});
const sectionElement = document.getElementById(`dcSyncModes`);
if (sectionElement) {
sectionElement.innerHTML = section;
}
}