iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 6
2
自我挑戰組

中年失業大叔的耍廢日記系列 第 6

第六天:筆電沒電,手機也沒電,只好出門充電

筆電沒電,手機沒電,只能去有很多插座的星巴克了。兩天沒碰筆電,打開一看,電力只剩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就明天再慢慢來。


上一篇
第五天:想要休息結果電鈴就響了
下一篇
第七天:平淡無奇的週二
系列文
中年失業大叔的耍廢日記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言