筆電沒電,手機沒電,只能去有很多插座的星巴克了。兩天沒碰筆電,打開一看,電力只剩20%,看來關機時也沒注意... (很奇怪,把Facebook的通知清空,移到一個靜態網頁的頁籤,然後把焦點轉到Finder,休眠時電就會少用很多)
早上是一定要處理一些作業,先來寫xsd的處理程式好了。根據日前做好的雛形,大致上會用到AAS.xsd
以及IEC61360.xsd
這兩個相關的xml schema。就讓程式跑一些結構的資訊出來,來熟悉一下XML Schema的語法元素。
目前寫好的部分,範例是用AAS.xsd以及IEC61360.xsd跑出來的:
const args = require('minimist')(process.argv.slice(2));
const DOMParser = require('xmldom').DOMParser;
const fs = require('fs');
Array.prototype.asyncReduce = async function(cb, init) {
let pre = init;
for(let i=0; i<this.length; i++) {
pre = await cb(pre, this[i], i, this);
}
return pre;
};
main(args)
.catch(reason=>console.log(reason));
async function main(args) {
if(args._.length < 1) return help();
let result = await args._[0].split(',').asyncReduce(async (pre, cur) => {
let data = await readFile(cur);
return deepMerge(pre, xsd_parser(new DOMParser().parseFromString(data)));
}, {});
console.log(JSON.stringify(result, null, 2));
}
function deepMerge(target, source) {
Object.keys(source).forEach(k => {
if(source.hasOwnProperty(k)) {
if(!!target[k] && typeof target[k] === 'object' && typeof source[k] === 'object') {
target[k] = deepMerge(target[k], source[k]);
} else {
if(!!target[k] && typeof target[k] !== typeof source[k]) throw new Error(`object merge error: ${k}, ${typeof source[k]}`);
target[k] = source[k];
}
}
});
return target;
}
function xsd_parser(doc) {
let ctypes = doc.getElementsByTagName('complexType');
let complexTypes = Array.prototype.reduce.call(ctypes, (pre, cur) => {
let name = cur.getAttribute('name');
if(!!name) {
let o = {};
Array.prototype.forEach.call(cur.childNodes, (elm => {
if(!!elm.tagName)
o.contentType = elm.tagName;
}));
pre[name] = o;
}
return pre;
}, {});
let elems = doc.getElementsByTagName('element');
let elements = Array.prototype.reduce.call(elems, (pre, cur) => {
let name = cur.getAttribute('name');
if(!!name) {
let o = {};
o.type = cur.getAttribute('type');
o.minOccurs = cur.getAttribute('minOccurs');
o.maxOccurs = cur.getAttribute('maxOccurs');
if(!pre[name]) pre[name] = [];
if(pre[name].every(e => e.type !== o.type))
pre[name].push(o);
}
return pre;
}, {});
return {elements, complexTypes};
}
async function readFile(filename) {
return new Promise((resolve, reject) => {
fs.readFile(filename, 'utf8', (err, data) => {
if(!!err) {
reject(err);
} else {
resolve(data);
}
});
});
}
function help() {
console.log('[Usage] node xsdrun [xsd_file_1,xsd_file_2...]');
}
xml是由element組織起來的,所以首先就是把element全部找出來:
{
"elements": {
"aasenv": [
{
"type": "aas:aasenv_t",
"minOccurs": "",
"maxOccurs": ""
}
],
"assetAdministrationShells": [
{
"type": "aas:assetAdministrationShells_t",
"minOccurs": "0",
"maxOccurs": "1"
}
],
"assets": [
{
"type": "aas:assets_t",
"minOccurs": "0",
"maxOccurs": "1"
}
],
"submodels": [
{
"type": "aas:submodels_t",
"minOccurs": "0",
"maxOccurs": "1"
}
],
"conceptDescriptions": [
{
"type": "aas:conceptDescriptions_t",
"minOccurs": "0",
"maxOccurs": "1"
}
],
...
]
}
更關鍵的結構是complexType,先來確認一下每個complexType底下是用那個tag來定義結構:
{
...
"complexTypes": {
"aasenv_t": {
"contentType": "sequence"
},
"assetAdministrationShells_t": {
"contentType": "sequence"
},
"assets_t": {
"contentType": "sequence"
},
"asset_t": {
"contentType": "sequence"
},
"assetAdministrationShell_t": {
"contentType": "sequence"
},
"submodel_t": {
"contentType": "sequence"
},
"conceptDescription_t": {
"contentType": "sequence"
},
...
"submodelElement_t": {
"contentType": "choice"
},
"property_t": {
"contentType": "complexContent"
},
"file_t": {
"contentType": "complexContent"
},
...
]
}
觀察了一下,比較麻煩的是一個element可能會有不同的type,例如value
:
"value": [
{
"type": "IEC61360:valueDataType_t",
"minOccurs": "0",
"maxOccurs": "1"
},
{
"type": "aas:pathType_t",
"minOccurs": "",
"maxOccurs": ""
},
{
"type": "aas:blobType_t",
"minOccurs": "",
"maxOccurs": ""
},
{
"type": "aas:reference_t",
"minOccurs": "",
"maxOccurs": ""
},
{
"type": "aas:submodelElements_t",
"minOccurs": "",
"maxOccurs": ""
},
{
"type": "aas:submodelElement_t",
"minOccurs": "",
"maxOccurs": ""
}
],
還有qualifier
:
"qualifier": [
{
"type": "string",
"minOccurs": "0",
"maxOccurs": "unbounded"
},
{
"type": "aas:constraint_t",
"minOccurs": "0",
"maxOccurs": "1"
},
{
"type": "aas:qualifier_t",
"minOccurs": "",
"maxOccurs": ""
}
],
難怪當初處理Asset Administration Shell時碰到問題,因為一個tag可能因為上下文改變他的意義與型別。之前只處理了value,之後還得處理qualifier還有其他。
======
今天本來ESO伺服器停機維修,結果竟然還沒晚上七點就恢復上線了,那就先上工打Game。處理pptx就明天再慢慢來。