/**
* SOES EEPROM generator
* ESI XML code generation 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'
// ####################### ESI.xml generating ####################### //
function esiDTbitsize(dtype) {
return ESI_DT[dtype].bitsize;
}
//See ETG2000 for ESI format
function esi_generator(form, od, indexes, dc)
{
//VendorID
var esi =`\n\n \n ${parseInt(form.VendorID.value).toString()}\n`;
//VendorName
esi += ` ${form.VendorName.value}\n \n \n`;
//Groups
esi += ` \n \n ${form.TextGroupType.value}\n ${form.TextGroupName5.value}\n \n \n \n`;
//Physics
esi += ` \n ${form.TextDeviceType.value}\n`;
//Add Name info
esi += ` ${form.TextDeviceName.value}\n`;
//Add in between
esi += ` ${form.TextGroupType.value}\n`;
//Add profile
esi += ` \n ${form.ProfileNo.value}\n 0\n \n `;
const customTypes = {};
const variableTypes = {};
function addVariableType(element) {
if (element && element.otype && (element.otype != OTYPE.VAR && element.otype != OTYPE.ARRAY)) {
alert(`${element.name} is not OTYPE VAR, cannot treat is as variable type`); return;
}
if (!element || !element.dtype) {
alert(`${element.name} has no DTYPE, cannot treat is as variable type`); return;
}
let el_name = esiVariableTypeName(element);
if (!variableTypes[el_name]) {
const bitsize = (element.dtype == DTYPE.VISIBLE_STRING) ? esiBitsize(element) : esiDTbitsize(element.dtype);
variableTypes[el_name] = bitsize;
}
}
function addObjectDictionaryDataType(od, index) {
const objd = od[index];
const dtName = esiDtName(objd, index);
var result = '';
if (objd.otype == OTYPE.VAR) {
addVariableType(objd); // variable types will have to be be done later anyway, add to that queue
} else if (!customTypes[dtName]) {
// generate data types code for complex objects
const bitsize = esiBitsize(objd);
customTypes[dtName] = true;
result += `\n `;
let flags = `\n ro`; // PDO assign flags for variables are set in dictionary objects section
if (objd.otype == OTYPE.ARRAY) {
addVariableType(objd); // queue variable type to add after array code is generated
let esi_type = ESI_DT[objd.dtype];
let arr_bitsize = (objd.items.length - 1) * esi_type.bitsize
result += `\n ${dtName}ARR\n ${esi_type.name}\n ${arr_bitsize}`;
result += `\n \n 1\n ${objd.items.length - 1}\n `;
result += `\n `;
result += `\n `;
}
result += `\n ${dtName}\n ${bitsize}`;
result += `\n \n 0\n Max SubIndex\n USINT`
+ `\n 8\n 0\n ${flags}\n \n `;
flags += getPdoMappingFlags(objd); // PDO assign flags for composite type
switch (objd.otype) {
case OTYPE.ARRAY: {
let arr_bitsize = (objd.items.length - 1) * esiDTbitsize(objd.dtype);
result += `\n \n Elements\n ${dtName}ARR\n ${arr_bitsize}`
+`\n 16\n ${flags}\n \n `;
break;
} case OTYPE.RECORD: {
let subindex = 0;
let bits_offset = 16;
objd.items.forEach(subitem => {
if (subindex > 0) { // skipped Max Subindex
addVariableType(subitem); // cannot add variable type now that record code is being generated
let subitem_dtype = ESI_DT[subitem.dtype];
let subitem_bitsize = subitem_dtype.bitsize
const subitemFlags = getSubitemFlags(objd, subitem);
result += `\n \n ${subindex}\n ${subitem.name}`
+ `\n ${subitem_dtype.name}\n ${subitem_bitsize}\n ${bits_offset}`
+ `\n ${subitemFlags}\n `
+ `\n `;
bits_offset += subitem_bitsize;
}
subindex++;
});
break;
} default: {
alert(`Object ${index} "${objd.name}" has unexpected OTYPE ${objd.otype}`);
alert;
}}
result += `\n `;
}
return result;
function getSubitemFlags(objd, subitem) {
let access = 'ro';
let modifier = '';
if (subitem.access) {
access = subitem.access.slice(0,2).toLowerCase();
modifier = ' WriteRestrictions="PreOP"';
}
let flags = `\n ${access}`; // PDO assign flags for variables are set in dictionary objects section
flags += getPdoMappingFlags(objd); // PDO assign flags for composite type
return flags;
}
}
// Add objects dictionary data types
indexes.forEach(index => { esi += addObjectDictionaryDataType(od, index); });
// Add variable type
Object.entries(variableTypes).forEach(variableType => {
esi += `\n `;
esi += `\n ${variableType[0]}\n ${variableType[1]}`;
esi += `\n `;
});
esi += `\n \n `;
// Add objects dictionary
function addDictionaryObject(od, index) {
const objd = od[index];
const el_dtype = esiDtName(objd, index);
const bitsize = esiBitsize(objd);
let result = `\n `;
return result;
function addDictionaryObjectSubitems(element_items) {
const max_subindex_value = element_items.length - 1;
var result = ""
let subindex = 0;
element_items.forEach(subitem => {
var defaultValue = (subindex > 0) ? subitem.value : max_subindex_value;
result += `\n \n ${subitem.name}\n \n ${toEsiHexValue(defaultValue)}\n \n `;
subindex++;
});
return result;
}
}
indexes.forEach(index => { esi += addDictionaryObject(od, index); });
const is_rxpdo = isPdoWithVariables(od, indexes, rxpdo);
const is_txpdo = isPdoWithVariables(od, indexes, txpdo);
esi += `\n \n \n \n Outputs\n Inputs\n MBoxState\n`;
//Add Rxmailbox sizes
esi += ` MBoxOut\n`;
//Add Txmailbox sizes
esi += ` MBoxIn\n`;
//Add SM2
esi += ` Outputs\n`;
//Add SM3
esi += ` Inputs\n`;
if (is_rxpdo) {
var memOffset = getSM2_MappingOffset(form);
indexes.forEach(index => {
const objd = od[index];
if (isInArray(objd.pdo_mappings, rxpdo)) {
esi += addEsiDevicePDO(objd, index, rxpdo, memOffset);
++memOffset;
}
});
}
if (is_txpdo) {
var memOffset = form.SM3Offset.value;
indexes.forEach(index => {
const objd = od[index];
if (isInArray(objd.pdo_mappings, txpdo)) {
esi += addEsiDevicePDO(objd, index, txpdo, memOffset);
++memOffset;
}
});
}
//Add Mailbox DLL
esi += ` \n \n \n`;
//Add DCs
esi += getEsiDCsection(dc);
//Add EEPROM
const configdata = hex_generator(form, true);
esi +=` \n ${parseInt(form.EEPROMsize.value)}\n ${configdata}\n \n`;
//Close all items
esi +=` \n \n \n`;
return esi;
function addEsiDevicePDO(objd, index, pdo, memOffset) {
var esi = '';
const PdoName = pdo[0].toUpperCase();
const SmNo = (pdo == txpdo) ? 3 : 2;
const memoryOffset = indexToString(memOffset);
esi += ` <${PdoName}xPdo Fixed="true" Mandatory="true" Sm="${SmNo}">\n #x${memoryOffset}\n ${objd.name}`;
var subindex = 0;
switch (objd.otype) {
case OTYPE.VAR: {
const esiType = esiVariableTypeName(objd);
const bitsize = esiDTbitsize(objd.dtype);
esi += `\n \n #x${index}\n #x${subindex.toString(16)}\n ${bitsize}\n ${objd.name}\n ${esiType}\n `;
esi += pdoBooleanPadding(objd);
break;
}
case OTYPE.ARRAY: {
const esiType = esiVariableTypeName(objd);
const bitsize = esiDTbitsize(objd.dtype);
subindex = 1; // skip 'Max subindex'
objd.items.slice(subindex).forEach(subitem => {
esi += `\n \n #x${index}\n #x${subindex.toString(16)}\n ${bitsize}\n ${subitem.name}\n ${esiType}\n `;
// TODO handle padding for array of booleans
++subindex;
});
break;
}
case OTYPE.RECORD: {
subindex = 1; // skip 'Max subindex'
objd.items.slice(subindex).forEach(subitem => {
const esiType = esiVariableTypeName(subitem);
const bitsize = esiDTbitsize(subitem.dtype);
esi += `\n \n #x${index}\n #x${subindex.toString(16)}\n ${bitsize}\n ${subitem.name}\n ${esiType}\n `;
esi += pdoBooleanPadding(subitem);
++subindex;
});
break;
}
default: {
alert(`Unexpected OTYPE ${objd.otype} for ${index} ${objd.name} in ESI ${PdoName}PDOs`);
break;
}}
esi += `\n ${PdoName}xPdo>\n`;
return esi;
function pdoBooleanPadding(item) {
if (item.dtype == DTYPE.BOOLEAN) {
return `\n \n ${0}\n ${0}\n ${7}\n `;
}
return ``;
}
}
function toEsiHexValue(value) {
if (!value) {
return 0;
}
if (value.startsWith && value.startsWith('0x')) {
value = `#x${value.slice(2)}`;
}
return value;
}
function getPdoMappingFlags(item) {
var flags = '';
if (item.pdo_mappings) {
if (item.pdo_mappings.length > 1) {
alert(`Object ${index} "${objd.name}" has multiple PDO mappings, that is not supported by this version of tool`
+ `, only first ${pdoMappingFlag}XPDO will be used`);
}
const pdoMappingFlag = item.pdo_mappings[0].slice(0,1).toUpperCase();
flags += `\n ${pdoMappingFlag}`;
}
return flags;
}
function getEsiDCsection(dc) {
if (!dc) {
return '';
}
var dcSection = ' ';
dc.forEach(opMode => {
dcSection += `\n \n ${opMode.Name}\n ${opMode.Description}\n ${opMode.AssignActivate}`;
if (opMode.Sync0cycleTime && opMode.Sync0cycleTime != 0) {
dcSection += `\n ${opMode.Sync0cycleTime}`;
}
if (opMode.Sync0shiftTime && opMode.Sync0shiftTime != 0) {
dcSection += `\n ${opMode.Sync0shiftTime}`;
}
if (opMode.Sync1cycleTime && opMode.Sync1cycleTime != 0) {
dcSection += `\n ${opMode.Sync1cycleTime}`;
}
if (opMode.Sync1shiftTime && opMode.Sync1shiftTime != 0) {
dcSection += `\n ${opMode.Sync1shiftTime}`;
}
dcSection += `\n `;
});
dcSection += `\n \n`;
return dcSection;
}
//See Table 40 ETG2000
function getCoEString(form)
{
var result = ""
// if(form.CoeDetailsEnableSDO.checked)
// result += 'SdoInfo="true" ';
// else
// result += 'SdoInfo="false" ';
if(form.CoeDetailsEnableSDOInfo.checked)
result += 'SdoInfo="true" ';
else
result += 'SdoInfo="false" ';
if(form.CoeDetailsEnablePDOAssign.checked)
result += 'PdoAssign="true" ';
else
result += 'PdoAssign="false" ';
if(form.CoeDetailsEnablePDOConfiguration.checked)
result += 'PdoConfig="true" ';
else
result += 'PdoConfig="false" ';
if(form.CoeDetailsEnableUploadAtStartup.checked)
result += 'PdoUpload="true" ';
else
result += 'PdoUpload="false" ';
if(form.CoeDetailsEnableSDOCompleteAccess.checked)
result += 'CompleteAccess="true" ';
else
result +='CompleteAccess="false" ';
return result;
}
function esiVariableTypeName(element) {
let el_name = ESI_DT[element.dtype].name;
if (element.dtype == DTYPE.VISIBLE_STRING) {
return `${el_name}(${element.data.length})`;
}
return el_name;
}
function esiDtName(element, index) {
switch (element.otype) {
case OTYPE.VAR:
return esiVariableTypeName(element);
case OTYPE.ARRAY:
case OTYPE.RECORD:
return `DT${index}`;
default:
alert(`Element 0x${index} has unexpected OTYPE ${element.otype}`);
break;
}
}
function esiBitsize(element) {
switch (element.otype) {
case OTYPE.VAR: {
let bitsize = esiDTbitsize(element.dtype);
if (element.dtype == DTYPE.VISIBLE_STRING) {
return bitsize * element.data.length;
}
return bitsize;
}
case OTYPE.ARRAY: {
const maxsubindex_bitsize = esiDTbitsize(DTYPE.UNSIGNED8);
let bitsize = esiDTbitsize(element.dtype);
let elements = element.items.length - 1; // skip max subindex
return maxsubindex_bitsize * 2 + elements * bitsize;
}
case OTYPE.RECORD: {
const maxsubindex_bitsize = esiDTbitsize(DTYPE.UNSIGNED8);
let bitsize = maxsubindex_bitsize * 2;
for (let subindex = 1; subindex < element.items.length; subindex++) {
const subitem = element.items[subindex];
bitsize += esiDTbitsize(subitem.dtype);
if(subitem.dtype == DTYPE.BOOLEAN) {
bitsize += booleanPaddingBitsize;
}
}
return bitsize;
}
default:
alert(`Element ${element} has unexpected OTYPE ${element.otype}`);
break;
}
}
}