From 6e38a5250afc645e3c5ab94d643a1fdafcf6eccb Mon Sep 17 00:00:00 2001 From: wellenvogel Date: Sun, 21 Nov 2021 12:37:53 +0100 Subject: [PATCH 1/7] generate embedded files automatically --- extra_script.py | 244 +++++++++++++++++++--------------- lib/webserver/GwWebServer.cpp | 13 +- platformio.ini | 1 + 3 files changed, 144 insertions(+), 114 deletions(-) diff --git a/extra_script.py b/extra_script.py index cb0f57d..7e84317 100644 --- a/extra_script.py +++ b/extra_script.py @@ -10,10 +10,18 @@ Import("env") GEN_DIR='generated' CFG_FILE='web/config.json' XDR_FILE='web/xdrconfig.json' -FILES=['web/index.html',CFG_FILE,XDR_FILE,'web/index.js','web/index.css'] CFG_INCLUDE='GwConfigDefinitions.h' XDR_INCLUDE='GwXdrTypeMappings.h' +EMBEDDED_INCLUDE="GwEmbeddedFiles.h" +def getEmbeddedFiles(env): + rt=[] + efiles=env.GetProjectOption("board_build.embed_files") + for f in efiles.split("\n"): + if f == '': + continue + rt.append(f) + return rt def basePath(): #see: https://stackoverflow.com/questions/16771894/python-nameerror-global-name-file-is-not-defined @@ -38,126 +46,150 @@ def isCurrent(infile,outfile): print("%s is newer then %s, no need to recreate"%(outfile,infile)) return True return False -def compressFile(inFile): - outfile=os.path.basename(inFile)+".gz" - inFile=os.path.join(basePath(),inFile) - outfile=os.path.join(outPath(),outfile) +def compressFile(inFile,outfile): if isCurrent(inFile,outfile): return with open(inFile, 'rb') as f_in: with gzip.open(outfile, 'wb') as f_out: shutil.copyfileobj(f_in, f_out) - -def generateCfg(): - outfile=os.path.join(outPath(),CFG_INCLUDE) - infile=os.path.join(basePath(),CFG_FILE) +def generateFile(infile,outfile,callback,inMode='rb',outMode='w'): if isCurrent(infile,outfile): return - print("creating %s"%CFG_INCLUDE) + print("creating %s"%outfile) oh=None - with open(CFG_FILE,'rb') as ch: - config=json.load(ch) - try: - with open(outfile,'w') as oh: - oh.write("//generated from %s\n"%CFG_FILE) - oh.write('#include "GwConfigItem.h"\n') - l=len(config) - oh.write('class GwConfigDefinitions{\n') - oh.write(' public:\n') - oh.write(' int getNumConfig() const{return %d;}\n'%(l)) - for item in config: - n=item.get('name') - if n is None: - continue - if len(n) > 15: - raise Exception("%s: config names must be max 15 caracters"%n) - oh.write(' const String %s=F("%s");\n'%(n,n)) - oh.write(' protected:\n') - oh.write(' GwConfigItem *configs[%d]={\n'%(l)) - first=True - for item in config: - if not first: - oh.write(',\n') - first=False - oh.write(" new GwConfigItem(%s,\"%s\")"%(item.get('name'),item.get('default'))) - oh.write('};\n') - oh.write('};\n') - oh.close() - except Exception as e: - if oh is not None: + with open(infile,inMode) as ch: + with open(outfile,'w') as oh: + try: + callback(ch,oh,inFile=infile) + oh.close() + except Exception as e: try: oh.close() except: pass - os.unlink(outfile) - raise + os.unlink(outfile) + raise -def generateXdrMappings(): - outfile=os.path.join(outPath(),XDR_INCLUDE) - infile=os.path.join(basePath(),XDR_FILE) - if isCurrent(infile,outfile): - return - print("creating %s"%XDR_INCLUDE) - oh=None +def writeFileIfChanged(fileName,data): + if os.path.exists(fileName): + with open(fileName,"r") as ih: + old=ih.read() + ih.close() + if old == data: + return + print("#generating %s"%fileName) + with open(fileName,"w") as oh: + oh.write(data) - with open(infile,"rb") as fp: - jdoc=json.load(fp) - try: - with open(outfile,"w") as oh: - oh.write("static GwXDRTypeMapping* typeMappings[]={\n") - first=True - for cat in jdoc: - item=jdoc[cat] - cid=item.get('id') - if cid is None: - continue - tc=item.get('type') - if tc is not None: - if first: - first=False - else: - oh.write(",\n") - oh.write(" new GwXDRTypeMapping(%d,%d,0) /*%s*/"%(cid,tc,cat)) - fields=item.get('fields') - if fields is None: - continue - idx=0 - for fe in fields: - if not isinstance(fe,dict): - continue - tc=fe.get('t') - id=fe.get('v') - if id is None: - id=idx - idx+=1 - l=fe.get('l') or '' - if tc is None or id is None: - continue - if first: - first=False - else: - oh.write(",\n") - oh.write(" new GwXDRTypeMapping(%d,%d,%d) /*%s:%s*/"%(cid,tc,id,cat,l)) - oh.write("\n") - oh.write("};\n") - except Exception as e: - if oh: - try: - oh.close() - except: - pass - os.unlink(outfile) - raise +def generateCfg(ch,oh,inFile=''): + config=json.load(ch) + oh.write("//generated from %s\n"%inFile) + oh.write('#include "GwConfigItem.h"\n') + l=len(config) + oh.write('class GwConfigDefinitions{\n') + oh.write(' public:\n') + oh.write(' int getNumConfig() const{return %d;}\n'%(l)) + for item in config: + n=item.get('name') + if n is None: + continue + if len(n) > 15: + raise Exception("%s: config names must be max 15 caracters"%n) + oh.write(' const String %s=F("%s");\n'%(n,n)) + oh.write(' protected:\n') + oh.write(' GwConfigItem *configs[%d]={\n'%(l)) + first=True + for item in config: + if not first: + oh.write(',\n') + first=False + oh.write(" new GwConfigItem(%s,\"%s\")"%(item.get('name'),item.get('default'))) + oh.write('};\n') + oh.write('};\n') + +def generateXdrMappings(fp,oh,inFile=''): + jdoc=json.load(fp) + oh.write("static GwXDRTypeMapping* typeMappings[]={\n") + first=True + for cat in jdoc: + item=jdoc[cat] + cid=item.get('id') + if cid is None: + continue + tc=item.get('type') + if tc is not None: + if first: + first=False + else: + oh.write(",\n") + oh.write(" new GwXDRTypeMapping(%d,%d,0) /*%s*/"%(cid,tc,cat)) + fields=item.get('fields') + if fields is None: + continue + idx=0 + for fe in fields: + if not isinstance(fe,dict): + continue + tc=fe.get('t') + id=fe.get('v') + if id is None: + id=idx + idx+=1 + l=fe.get('l') or '' + if tc is None or id is None: + continue + if first: + first=False + else: + oh.write(",\n") + oh.write(" new GwXDRTypeMapping(%d,%d,%d) /*%s:%s*/"%(cid,tc,id,cat,l)) + oh.write("\n") + oh.write("};\n") +def generateEmbedded(elist,outFile): + content="" + for entry in elist: + content+="EMBED_GZ_FILE(\"%s\",%s,\"%s\");\n"%entry + writeFileIfChanged(outFile,content) -if not checkDir(): - sys.exit(1) -for f in FILES: - print("compressing %s"%f) - compressFile(f) -generateCfg() -generateXdrMappings() -version="dev"+datetime.now().strftime("%Y%m%d") -env.Append(CPPDEFINES=[('GWDEVVERSION',version)]) +def getContentType(fn): + if (fn.endswith('.gz')): + fn=fn[0:-3] + if (fn.endswith('html')): + return "text/html" + if (fn.endswith('json')): + return "application/json" + if (fn.endswith('js')): + return "text/javascript" + return "application/octet-stream" + +def prebuild(env): + print("#prebuild running") + if not checkDir(): + sys.exit(1) + embedded=getEmbeddedFiles(env) + filedefs=[] + for ef in embedded: + print("#checking embedded file %s"%ef) + (dn,fn)=os.path.split(ef) + pureName=fn + if pureName.endswith('.gz'): + pureName=pureName[0:-3] + ct=getContentType(pureName) + usname=ef.replace('/','_').replace('.','_') + filedefs.append((pureName,usname,ct)) + inFile=os.path.join(basePath(),"web",pureName) + if os.path.exists(inFile): + print("compressing %s"%inFile) + compressFile(inFile,ef) + else: + print("#WARNING: infile %s for %s not found"%(inFile,ef)) + generateEmbedded(filedefs,os.path.join(outPath(),EMBEDDED_INCLUDE)) + generateFile(os.path.join(basePath(),CFG_FILE),os.path.join(outPath(),CFG_INCLUDE),generateCfg) + generateFile(os.path.join(basePath(),XDR_FILE),os.path.join(outPath(),XDR_INCLUDE),generateXdrMappings) + version="dev"+datetime.now().strftime("%Y%m%d") + env.Append(CPPDEFINES=[('GWDEVVERSION',version)]) + +prebuild(env) diff --git a/lib/webserver/GwWebServer.cpp b/lib/webserver/GwWebServer.cpp index 0dafd09..a09c711 100644 --- a/lib/webserver/GwWebServer.cpp +++ b/lib/webserver/GwWebServer.cpp @@ -16,15 +16,12 @@ class EmbeddedFile { embeddedFiles[name]=this; } } ; -#define EMBED_GZ_FILE(fileName, fileExt, contentType) \ - extern const uint8_t fileName##_##fileExt##_File[] asm("_binary_generated_" #fileName "_" #fileExt "_gz_start"); \ - extern const uint8_t fileName##_##fileExt##_FileLen[] asm("_binary_generated_" #fileName "_" #fileExt "_gz_size"); \ - const EmbeddedFile fileName##_##fileExt##_Config(#fileName "." #fileExt,contentType,(const uint8_t*)fileName##_##fileExt##_File,(int)fileName##_##fileExt##_FileLen); +#define EMBED_GZ_FILE(fileName, binName, contentType) \ + extern const uint8_t binName##_File[] asm("_binary_" #binName "_start"); \ + extern const uint8_t binName##_FileLen[] asm("_binary_" #binName "_size"); \ + const EmbeddedFile binName##_Config(fileName,contentType,(const uint8_t*)binName##_File,(int)binName##_FileLen); -EMBED_GZ_FILE(index,html,"text/html") -EMBED_GZ_FILE(config,json,"application/json") -EMBED_GZ_FILE(index,js,"text/javascript") -EMBED_GZ_FILE(index,css,"text/css") +#include "GwEmbeddedFiles.h" void sendEmbeddedFile(String name,String contentType,AsyncWebServerRequest *request){ std::map::iterator it=embeddedFiles.find(name); diff --git a/platformio.ini b/platformio.ini index 2e5cadf..02ff4d0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -23,6 +23,7 @@ board_build.embed_files = generated/index.js.gz generated/index.css.gz generated/config.json.gz + generated/xdrconfig.json.gz board_build.partitions = partitions_custom.csv extra_scripts = pre:extra_script.py From 1805f5578b4e311bc39fe1415128f86f96eb0bdc Mon Sep 17 00:00:00 2001 From: wellenvogel Date: Sun, 21 Nov 2021 12:58:15 +0100 Subject: [PATCH 2/7] try to set up clean action - does not work yet --- extra_script.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/extra_script.py b/extra_script.py index 7e84317..a47c8ac 100644 --- a/extra_script.py +++ b/extra_script.py @@ -192,4 +192,17 @@ def prebuild(env): version="dev"+datetime.now().strftime("%Y%m%d") env.Append(CPPDEFINES=[('GWDEVVERSION',version)]) +def cleangenerated(source, target, env): + od=outPath() + if os.path.isdir(od): + print("#cleaning up %s"%od) + for f in os.listdir(od): + if f == "." or f == "..": + continue + fn=os.path.join(od,f) + os.unlink(f) + +print("#prescript...") prebuild(env) +#script does not run on clean yet - maybe in the future +env.AddPostAction("clean",cleangenerated) From ef299bd9ba008aa01b770d3a0ea4ee650eccecd7 Mon Sep 17 00:00:00 2001 From: wellenvogel Date: Sun, 21 Nov 2021 18:10:58 +0100 Subject: [PATCH 3/7] correct content-type for css files --- extra_script.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extra_script.py b/extra_script.py index a47c8ac..8dc3ab2 100644 --- a/extra_script.py +++ b/extra_script.py @@ -163,6 +163,8 @@ def getContentType(fn): return "application/json" if (fn.endswith('js')): return "text/javascript" + if (fn.endswith('css')): + return "text/css" return "application/octet-stream" def prebuild(env): From ca55063c8eb7e536098899e996a7ab90266b5163 Mon Sep 17 00:00:00 2001 From: wellenvogel Date: Sun, 21 Nov 2021 19:40:18 +0100 Subject: [PATCH 4/7] intermediate: xdr config handling --- web/index.css | 6 + web/index.html | 11 ++ web/index.js | 390 +++++++++++++++++++++++++++++++++---------------- 3 files changed, 285 insertions(+), 122 deletions(-) diff --git a/web/index.css b/web/index.css index fce2825..0b583f0 100644 --- a/web/index.css +++ b/web/index.css @@ -89,6 +89,12 @@ body{ .hidden{ display: none !important; } + #xdrPage .content.hidden { + display: unset !important; + } + #xdrPage .category .title{ + display: none; + } .msgDetails .value { width: 5em; text-align: right; diff --git a/web/index.html b/web/index.html index 0768854..abe2a4c 100644 --- a/web/index.html +++ b/web/index.html @@ -17,6 +17,7 @@
Status
Config
+
XDR
Data
@@ -56,6 +57,16 @@
+ diff --git a/web/index.js b/web/index.js index d83230c..0b9dd32 100644 --- a/web/index.js +++ b/web/index.js @@ -3,12 +3,14 @@ let lastUpdate = (new Date()).getTime(); let reloadConfig = false; function addEl(type, clazz, parent, text) { let el = document.createElement(type); - if ( ! (clazz instanceof Array)){ - clazz=clazz.split(/ */); + if (clazz) { + if (!(clazz instanceof Array)) { + clazz = clazz.split(/ */); + } + clazz.forEach(function (ce) { + el.classList.add(ce); + }); } - clazz.forEach(function(ce){ - el.classList.add(ce); - }); if (text) el.textContent = text; if (parent) parent.appendChild(el); return el; @@ -230,15 +232,22 @@ function checkChange(el, row) { } } } +let configDefinitions; +let xdrConfig; function createInput(configItem, frame) { let el; if (configItem.type === 'boolean' || configItem.type === 'list') { - el = document.createElement('select') + el=addEl('select','',frame); el.setAttribute('name', configItem.name) let slist = []; if (configItem.list) { configItem.list.forEach(function (v) { - slist.push({ l: v, v: v }); + if (v instanceof Object){ + slist.push({l:v.l,v:v.v}); + } + else{ + slist.push({ l: v, v: v }); + } }) } else { @@ -246,60 +255,16 @@ function createInput(configItem, frame) { slist.push({ l: 'off', v: 'false' }) } slist.forEach(function (sitem) { - let sitemEl = document.createElement('option'); + let sitemEl = addEl('option','',el,sitem.l); sitemEl.setAttribute('value', sitem.v); - sitemEl.textContent = sitem.l; - el.appendChild(sitemEl); }) - frame.appendChild(el); return el; } if (configItem.type === 'filter') { - el = document.createElement('div'); - el.classList.add('filter'); - let ais = createInput({ - type: 'list', - name: configItem.name + "_ais", - list: ['aison', 'aisoff'] - }, el); - let mode = createInput({ - type: 'list', - name: configItem.name + "_mode", - list: ['whitelist', 'blacklist'] - }, el); - let sentences = createInput({ - type: 'text', - name: configItem.name + "_sentences", - }, el); - let data = document.createElement('input'); - data.setAttribute('type', 'hidden'); - el.appendChild(data); - let changeFunction = function () { - let cv = data.value || ""; - let parts = cv.split(":"); - ais.value = (parts[0] == '0') ? "aisoff" : "aison"; - mode.value = (parts[1] == '0') ? "whitelist" : "blacklist"; - sentences.value = parts[2] || ""; - } - let updateFunction = function () { - let nv = (ais.value == 'aison') ? "1" : "0"; - nv += ":"; - nv += (mode.value == 'blacklist') ? "1" : "0"; - nv += ":"; - nv += sentences.value; - data.value = nv; - let chev = new Event('change'); - data.dispatchEvent(chev); - } - mode.addEventListener('change', updateFunction); - ais.addEventListener("change", updateFunction); - sentences.addEventListener("change", updateFunction); - data.addEventListener('change', function (ev) { - changeFunction(); - }); - data.setAttribute('name', configItem.name); - frame.appendChild(el); - return data; + return createFilterInput(configItem,frame); + } + if (configItem.type === 'xdr'){ + return createXdrInput(configItem,frame); } el = document.createElement('input'); el.setAttribute('name', configItem.name) @@ -324,78 +289,259 @@ function createInput(configItem, frame) { } return el; } -let configDefinitions; + +function updateSelectList(item,slist){ + item.innerHTML=''; + slist.forEach(function (sitem) { + let sitemEl = addEl('option','',item,sitem.l); + sitemEl.setAttribute('value', sitem.v); + }) +} +function getXdrCategories(){ + let rt=[]; + for (let c in xdrConfig){ + if (xdrConfig[c].enabled !== false){ + rt.push({l:c,v:xdrConfig[c].id}); + } + } + return rt; +} +function getXdrSelectors(category){ + category=parseInt(category); + for (let c in xdrConfig){ + let base=xdrConfig[c]; + if (parseInt(base.id) == category){ + return base.selector || []; + } + } + return []; +} +function getXdrFields(category){ + category=parseInt(category); + for (let c in xdrConfig){ + let base=xdrConfig[c]; + if (parseInt(base.id) == category){ + return base.fields || []; + } + } + return []; +} + + +function createXdrInput(configItem,frame){ + let el = addEl('div','filter',frame); + let direction=createInput({ + type:'list', + name: configItem.name+"_dir", + list: [ + //GwXDRMappingDef::Direction + {l:'off',v:0}, + {l:'bidir',v:1}, + {l:'to2K',v:2}, + {l:'from2K',v:3} + ] + },el); + let category=createInput({ + type: 'list', + name: configItem.name+"_cat", + list:getXdrCategories() + },el); + let selector=createInput({ + type: 'list', + name: configItem.name+"_sel", + list:[] + },el); + let field=createInput({ + type:'list', + name: configItem.name+'_field', + list: [] + },el); + let imode=createInput({ + type:'list', + name: configItem.name+"_imode", + list:[ + //GwXDRMappingDef::InstanceMode + {l:'single',v:0}, + {l:'ignore',v:1}, + {l:'auto',v:2} + ] + },el); + let instance=createInput({ + type:'number', + name: configItem.name+"_instance", + },el); + let xdrName=createInput({ + type:'text', + name: configItem.name+"_xdr" + },el); + let data = addEl('input',undefined,el); + data.setAttribute('type', 'hidden'); + data.setAttribute('name', configItem.name); + let changeFunction = function () { + let parts=data.value.split(','); + direction.value=parts[1] || 0; + category.value=parts[0] || 0; + updateSelectList(selector,getXdrSelectors(category.value)); + updateSelectList(field,getXdrFields(category.value)); + selector.value=parts[2]||0; + field.value=parts[3]||0; + imode.value=parts[4]||0; + instance.value=parts[5]||0; + xdrName.value=parts[6]||''; + } + let updateFunction = function () { + let txt=category.value+","+direction.value+","+ + selector.value+","+field.value+","+imode.value; + let instanceValue=parseInt(instance.value||0); + if (isNaN(instanceValue)) instanceValue=0; + if (instanceValue<0) instanceValue=0; + if (instanceValue>255) instanceValue=255; + txt+=","+instanceValue; + let xdr=xdrName.value.replace(/[^a-zA-Z0-9]/g,''); + txt+=","+xdr; + data.value=txt; + let ev=new Event('change'); + data.dispatchEvent(ev); + } + category.addEventListener('change',updateFunction); + direction.addEventListener('change',updateFunction); + selector.addEventListener('change',updateFunction); + field.addEventListener('change',updateFunction); + imode.addEventListener('change',updateFunction); + instance.addEventListener('change',updateFunction); + xdrName.addEventListener('change',updateFunction); + data.addEventListener('change',changeFunction); + return data; +} + +function createFilterInput(configItem, frame) { + let el = addEl('div','filter',frame); + let ais = createInput({ + type: 'list', + name: configItem.name + "_ais", + list: ['aison', 'aisoff'] + }, el); + let mode = createInput({ + type: 'list', + name: configItem.name + "_mode", + list: ['whitelist', 'blacklist'] + }, el); + let sentences = createInput({ + type: 'text', + name: configItem.name + "_sentences", + }, el); + let data = addEl('input',undefined,el); + data.setAttribute('type', 'hidden'); + let changeFunction = function () { + let cv = data.value || ""; + let parts = cv.split(":"); + ais.value = (parts[0] == '0') ? "aisoff" : "aison"; + mode.value = (parts[1] == '0') ? "whitelist" : "blacklist"; + sentences.value = parts[2] || ""; + } + let updateFunction = function () { + let nv = (ais.value == 'aison') ? "1" : "0"; + nv += ":"; + nv += (mode.value == 'blacklist') ? "1" : "0"; + nv += ":"; + nv += sentences.value; + data.value = nv; + let chev = new Event('change'); + data.dispatchEvent(chev); + } + mode.addEventListener('change', updateFunction); + ais.addEventListener("change", updateFunction); + sentences.addEventListener("change", updateFunction); + data.addEventListener('change', function (ev) { + changeFunction(); + }); + data.setAttribute('name', configItem.name); + return data; +} + +function createConfigDefinitions(parent, capabilities, defs,includeXdr) { + let category; + let categoryEl; + let frame = parent.querySelector('.configFormRows'); + if (!frame) throw Error("no config form"); + frame.innerHTML = ''; + configDefinitions = defs; + defs.forEach(function (item) { + if (!item.type) return; + if ((item.category === 'xdr') !== includeXdr) return; + if (item.category != category || !categoryEl) { + let categoryFrame = addEl('div', 'category', frame); + let categoryTitle = addEl('div', 'title', categoryFrame); + let categoryButton = addEl('span', 'icon icon-more', categoryTitle); + addEl('span', 'label', categoryTitle, item.category); + categoryEl = addEl('div', 'content', categoryFrame); + categoryEl.classList.add('hidden'); + let currentEl = categoryEl; + categoryTitle.addEventListener('click', function (ev) { + let rs = currentEl.classList.toggle('hidden'); + if (rs) { + categoryButton.classList.add('icon-more'); + categoryButton.classList.remove('icon-less'); + } + else { + categoryButton.classList.remove('icon-more'); + categoryButton.classList.add('icon-less'); + } + }) + category = item.category; + } + if (item.capabilities !== undefined) { + for (let capability in item.capabilities) { + let values = item.capabilities[capability]; + if (!capabilities[capability]) return; + let found = false; + values.forEach(function (v) { + if (capabilities[capability] == v) found = true; + }); + if (!found) return; + } + } + let row = addEl('div', 'row', categoryEl); + let label = item.label || item.name; + addEl('span', 'label', row, label); + let valueFrame = addEl('div', 'value', row); + let valueEl = createInput(item, valueFrame); + if (!valueEl) return; + valueEl.setAttribute('data-default', item.default); + valueEl.addEventListener('change', function (ev) { + let el = ev.target; + checkChange(el, row); + }) + if (item.check) valueEl.setAttribute('data-check', item.check); + let btContainer = addEl('div', 'buttonContainer', row); + let bt = addEl('button', 'defaultButton', btContainer, 'X'); + bt.setAttribute('data-default', item.default); + bt.addEventListener('click', function (ev) { + valueEl.value = valueEl.getAttribute('data-default'); + let changeEvent = new Event('change'); + valueEl.dispatchEvent(changeEvent); + }) + bt = addEl('button', 'infoButton', btContainer, '?'); + bt.addEventListener('click', function (ev) { + showOverlay(item.description); + }); + }) +} function loadConfigDefinitions() { getJson("api/capabilities") .then(function (capabilities) { getJson("config.json") .then(function (defs) { - let category; - let categoryEl; - let frame = document.querySelector('.configFormRows'); - if (!frame) throw Error("no config form"); - frame.innerHTML = ''; - configDefinitions = defs; - defs.forEach(function (item) { - if (!item.type) return; - if (item.category != category || !categoryEl) { - let categoryFrame = addEl('div', 'category', frame); - let categoryTitle = addEl('div', 'title', categoryFrame); - let categoryButton = addEl('span', 'icon icon-more', categoryTitle); - addEl('span', 'label', categoryTitle, item.category); - categoryEl = addEl('div', 'content', categoryFrame); - categoryEl.classList.add('hidden'); - let currentEl = categoryEl; - categoryTitle.addEventListener('click', function (ev) { - let rs = currentEl.classList.toggle('hidden'); - if (rs) { - categoryButton.classList.add('icon-more'); - categoryButton.classList.remove('icon-less'); - } - else { - categoryButton.classList.remove('icon-more'); - categoryButton.classList.add('icon-less'); - } - }) - category = item.category; - } - if (item.capabilities !== undefined) { - for (let capability in item.capabilities) { - let values = item.capabilities[capability]; - if (!capabilities[capability]) return; - let found = false; - values.forEach(function (v) { - if (capabilities[capability] == v) found = true; - }); - if (!found) return; - } - } - let row = addEl('div', 'row', categoryEl); - let label = item.label || item.name; - addEl('span', 'label', row, label); - let valueFrame = addEl('div', 'value', row); - let valueEl = createInput(item, valueFrame); - if (!valueEl) return; - valueEl.setAttribute('data-default', item.default); - valueEl.addEventListener('change', function (ev) { - let el = ev.target; - checkChange(el, row); + getJson("xdrconfig.json") + .then(function(xdr){ + xdrConfig=xdr; + configDefinitions=defs; + let normalConfig=document.getElementById('configPage'); + let xdrParent=document.getElementById('xdrPage'); + if (normalConfig) createConfigDefinitions(normalConfig,capabilities,defs,false); + if (xdrParent) createConfigDefinitions(xdrParent,capabilities,defs,true); + resetForm(); }) - if (item.check) valueEl.setAttribute('data-check', item.check); - let btContainer = addEl('div', 'buttonContainer', row); - let bt = addEl('button', 'defaultButton', btContainer, 'X'); - bt.setAttribute('data-default', item.default); - bt.addEventListener('click', function (ev) { - valueEl.value = valueEl.getAttribute('data-default'); - let changeEvent = new Event('change'); - valueEl.dispatchEvent(changeEvent); - }) - bt = addEl('button', 'infoButton', btContainer, '?'); - bt.addEventListener('click', function (ev) { - showOverlay(item.description); - }); - }) - resetForm(); }) }) .catch(function (err) { alert("unable to load config: " + err) }) From bed9ec32cc892a467c0d62c7acdfa9a22fca7aeb Mon Sep 17 00:00:00 2001 From: wellenvogel Date: Sun, 21 Nov 2021 21:15:58 +0100 Subject: [PATCH 5/7] =?UTF-8?q?xdr=20inputs=20and=20some=20css=20t?= =?UTF-8?q?=E1=BA=83eaks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/index.css | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ web/index.js | 53 +++++++++++++++++++++++++++++++++++---------------- 2 files changed, 88 insertions(+), 16 deletions(-) diff --git a/web/index.css b/web/index.css index 0b583f0..2f250fc 100644 --- a/web/index.css +++ b/web/index.css @@ -1,3 +1,6 @@ +*{ + box-sizing: border-box; +} body{ font-family: Arial, Helvetica, sans-serif; } @@ -61,6 +64,7 @@ body{ flex-direction: row; flex-wrap: wrap; align-items: center; + min-width: 21em; } input,select { border: 1px solid #808080a1; @@ -95,6 +99,53 @@ body{ #xdrPage .category .title{ display: none; } + #xdrPage span.label{ + width: 4em; + } + #xdrPage .value{ + width: 24em; + } + #xdrPage .row{ + min-width: 24em; + } + .xdrline { + padding-top: 0.2em; + padding-bottom: 0.2em; + display: flex; + flex-direction: row; + } + .xdrunused{ + opacity: 0; + pointer-events: none; + } + span.xdrlabel { + width: 8em; + display: inline-block; + } + .xdrinput .xdrdir { + width: 12em; + } + .xdrinput .xdrdir:before { + content: 'Direction'; + } + .xdrinput .xdrcat { + width: 12em; + } + .xdrinput .xdrsel { + width: 12em; + } + .xdrinput .xdrfield { + width: 12em; + } + .xdrinput .xdrimode { + width: 8em; + } + .xdrinput .xdrinstance { + width: 4em; + } + .xdrinput .xdrname { + width: 16em; + } .msgDetails .value { width: 5em; text-align: right; diff --git a/web/index.js b/web/index.js index 0b9dd32..f4cc6e6 100644 --- a/web/index.js +++ b/web/index.js @@ -234,10 +234,10 @@ function checkChange(el, row) { } let configDefinitions; let xdrConfig; -function createInput(configItem, frame) { +function createInput(configItem, frame,clazz) { let el; if (configItem.type === 'boolean' || configItem.type === 'list') { - el=addEl('select','',frame); + el=addEl('select',clazz,frame); el.setAttribute('name', configItem.name) let slist = []; if (configItem.list) { @@ -261,14 +261,13 @@ function createInput(configItem, frame) { return el; } if (configItem.type === 'filter') { - return createFilterInput(configItem,frame); + return createFilterInput(configItem,frame,clazz); } if (configItem.type === 'xdr'){ - return createXdrInput(configItem,frame); + return createXdrInput(configItem,frame,clazz); } - el = document.createElement('input'); + el = addEl('input',clazz,frame); el.setAttribute('name', configItem.name) - frame.appendChild(el); if (configItem.type === 'password') { el.setAttribute('type', 'password'); let vis = addEl('span', 'icon-eye icon', frame); @@ -327,9 +326,20 @@ function getXdrFields(category){ return []; } +function createXdrLine(parent,label){ + let d=addEl('div','xdrline',parent); + addEl('span','xdrlabel',d,label); + return d; +} +function showHideXdr(el,show,useParent){ + if (useParent) el=el.parentElement; + if (show) el.classList.remove('xdrunused'); + else el.classList.add('xdrunused'); +} function createXdrInput(configItem,frame){ - let el = addEl('div','filter',frame); + let el = addEl('div','xdrinput',frame); + let d=createXdrLine(el,'Direction'); let direction=createInput({ type:'list', name: configItem.name+"_dir", @@ -340,22 +350,26 @@ function createXdrInput(configItem,frame){ {l:'to2K',v:2}, {l:'from2K',v:3} ] - },el); + },d,'xdrdir'); + d=createXdrLine(el,'Category'); let category=createInput({ type: 'list', name: configItem.name+"_cat", list:getXdrCategories() - },el); + },d,'xdrcat'); + d=createXdrLine(el,'Source'); let selector=createInput({ type: 'list', name: configItem.name+"_sel", list:[] - },el); + },d,'xdrsel'); + d=createXdrLine(el,'Field'); let field=createInput({ type:'list', name: configItem.name+'_field', list: [] - },el); + },d,'xdrfield'); + d=createXdrLine(el,'Instance'); let imode=createInput({ type:'list', name: configItem.name+"_imode", @@ -365,15 +379,16 @@ function createXdrInput(configItem,frame){ {l:'ignore',v:1}, {l:'auto',v:2} ] - },el); + },d,'xdrimode'); let instance=createInput({ type:'number', name: configItem.name+"_instance", - },el); + },d,'xdrinstance'); + d=createXdrLine(el,'Transducer'); let xdrName=createInput({ type:'text', name: configItem.name+"_xdr" - },el); + },d,'xdrname'); let data = addEl('input',undefined,el); data.setAttribute('type', 'hidden'); data.setAttribute('name', configItem.name); @@ -381,12 +396,17 @@ function createXdrInput(configItem,frame){ let parts=data.value.split(','); direction.value=parts[1] || 0; category.value=parts[0] || 0; - updateSelectList(selector,getXdrSelectors(category.value)); - updateSelectList(field,getXdrFields(category.value)); + let selectors=getXdrSelectors(category.value); + updateSelectList(selector,selectors); + showHideXdr(selector,selectors.length>0); + let fields=getXdrFields(category.value); + updateSelectList(field,fields); + showHideXdr(field,fields.length>0); selector.value=parts[2]||0; field.value=parts[3]||0; imode.value=parts[4]||0; instance.value=parts[5]||0; + showHideXdr(instance,imode.value == 0); xdrName.value=parts[6]||''; } let updateFunction = function () { @@ -398,6 +418,7 @@ function createXdrInput(configItem,frame){ if (instanceValue>255) instanceValue=255; txt+=","+instanceValue; let xdr=xdrName.value.replace(/[^a-zA-Z0-9]/g,''); + xdr=xdr.substr(0,12); txt+=","+xdr; data.value=txt; let ev=new Event('change'); From 84a3faa51ba89d929400ce1e0b38174c349eba22 Mon Sep 17 00:00:00 2001 From: wellenvogel Date: Sun, 21 Nov 2021 22:00:45 +0100 Subject: [PATCH 6/7] some css tweaks for xdr --- web/index.css | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/web/index.css b/web/index.css index 2f250fc..af37f2d 100644 --- a/web/index.css +++ b/web/index.css @@ -64,7 +64,6 @@ body{ flex-direction: row; flex-wrap: wrap; align-items: center; - min-width: 21em; } input,select { border: 1px solid #808080a1; @@ -105,14 +104,13 @@ body{ #xdrPage .value{ width: 24em; } - #xdrPage .row{ - min-width: 24em; - } + .xdrline { padding-top: 0.2em; padding-bottom: 0.2em; display: flex; flex-direction: row; + flex-wrap: wrap; } .xdrunused{ opacity: 0; From 2b91ce71bb2bc8514552a716d49dc43666fe0bb7 Mon Sep 17 00:00:00 2001 From: wellenvogel Date: Sun, 21 Nov 2021 22:01:03 +0100 Subject: [PATCH 7/7] correct type mappings --- extra_script.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extra_script.py b/extra_script.py index 8dc3ab2..8d58014 100644 --- a/extra_script.py +++ b/extra_script.py @@ -124,7 +124,7 @@ def generateXdrMappings(fp,oh,inFile=''): first=False else: oh.write(",\n") - oh.write(" new GwXDRTypeMapping(%d,%d,0) /*%s*/"%(cid,tc,cat)) + oh.write(" new GwXDRTypeMapping(%d,0,%d) /*%s*/"%(cid,tc,cat)) fields=item.get('fields') if fields is None: continue @@ -144,7 +144,7 @@ def generateXdrMappings(fp,oh,inFile=''): first=False else: oh.write(",\n") - oh.write(" new GwXDRTypeMapping(%d,%d,%d) /*%s:%s*/"%(cid,tc,id,cat,l)) + oh.write(" new GwXDRTypeMapping(%d,%d,%d) /*%s:%s*/"%(cid,id,tc,cat,l)) oh.write("\n") oh.write("};\n")