JavaScript解析SECS GEM報文

浅绿色i發表於2024-04-21

下面用JavaScript來解析,因為最近在寫前端順便就用js來解析,一般行業都用C# , C++, Java 來開發Drive。

1. 協議手冊參考SEMI E5官方文件:

2. 報文訊息:

包含三部分: MessageLength,MessageHeader, MessageBody

3. Message Body 詳情:

4 .Body Format:

5. 寫個demo方便大家理解

S1F13 報文:00 00 81 0D 00 00 00 00 00 22 01 02 41 04 54 45 53 54 41 03 31 2E 31

JavaScript解析SECS GEM報文
<!DOCTYPE html>
<html>

<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <!-- vue -->
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <!-- element引入樣式 -->
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <!-- element引入元件庫 -->
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <!--Axios-->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

</head>

<body>
    <div id="App">
        <div style="margin-top: 15px;">
            <el-input placeholder="請輸入內容" size="small" v-model="BodyMessage" class="input-with-select">
                <el-switch v-model="isHeadMeaasge" slot="prepend" active-text="包含頭報文" inactive-text="不包含頭報文">
                </el-switch>
                <el-button slot="append" icon="el-icon-search" @click.prevent.stop="but1"></el-button>
            </el-input>
            <div>
                <p style="white-space: pre-wrap;">{{ showlog }}</p>
              </div>
        </div>
    </div>
</body>
<script>
    class Logger {
        constructor() {
            this.indentationLevel = 0;
            this.logContainer = '';
        }

        Indent() {
            this.indentationLevel++;
        }

        Unindent() {
            if (this.indentationLevel > 0) {
                this.indentationLevel--;
            }
        }

        Print(message) {
            let spaces = " ".repeat(this.indentationLevel * 3);
            console.log(spaces + message);
            this.logContainer += spaces + message + '\n'
        }
        ShowLog(){
            return this.logContainer;
        }
        clear(){
            this.logContainer = '';
        }
    }
    var vm = new Vue({
        el: "#App",
        data: {
            BodyMessage: '',
            isHeadMeaasge: false,
            SECSFormat: {
                Ascii: 'Ascii',
                Binary: 'Binary',
                Boolean: 'Boolean',
                F4: 'F4',
                F8: 'F8',
                I1: 'I1',
                I2: 'I2',
                I4: 'I4',
                I8: 'I8',
                List: 'List',
                U1: 'U1',
                U2: 'U2',
                U4: 'U4',
                U8: 'U8',
                Char: 'Char',
                UNRECOGNIZE_FORMAT: 'UNRECOGNIZE_FORMAT'
            },
            log: new Logger(),
            showlog: '',

        },
        created() {
        },
        mounted() {
        },
        methods: {
            but1() {
                this.log.clear();
                this.InMsg(this.hexStringToUint8Array(this.BodyMessage), false, this.isHeadMeaasge)
                console.log(this.log.ShowLog());
                this.showlog = this.log.ShowLog();
                
            },
            /**
             * 
             * @param {number} decimalValue 十進位制值
             * @param {number} numBits 要求返回8位的二進位制
             * @returns 
             */
            decimalToBinary(decimalValue, numBits) {
                // 將十進位制數轉換為二進位制字串
                var binaryString = parseInt(decimalValue).toString(2);

                // 如果二進位制字串的長度小於指定位數,則在前面補零,使其長度達到指定位數
                while (binaryString.length < numBits) {
                    binaryString = '0' + binaryString;
                }

                return binaryString;
            },
            /**
             * 
             * @param {number} bits 8位的二進位制
             * @param {number} start 開始索引
             * @param {number} end 結束索引
             * @returns 
             */
            AssembleBits(bits, index, length) {
                var stringBuilder = "";
                for (var i = index; i < index + length; i++) {
                    stringBuilder += bits.charAt(i);
                }
                return stringBuilder;
            },

            ReverseByte(buffer) {
                var array = new Uint8Array(buffer.length);
                for (var i = 0; i < buffer.length; i++) {
                    array[i] = buffer[buffer.length - i - 1];
                }
                return array;
            },


            /**
             * 
             * @param { 0x } hexString 
             * @returns 
             */
            hexStringToUint8Array(hexString) {
                // 將十六進位制字串拆分為十六進位制數字陣列
                //const hexArray = hexString.split(' ');
                const hexArray = hexString.split(/\s+/).filter(Boolean);

                // 將十六進位制數字轉換為位元組值,並儲存在 Uint8Array 中
                const bytes = new Uint8Array(hexArray.map(hex => parseInt(hex, 16)));

                return bytes;
            },

            GetFormat(formatCode, formatLength) {
                var bytes = new Uint8Array([formatCode]);
                var bits = this.decimalToBinary(bytes, 8);
                var length = this.AssembleBits(bits, 6, 8)
                switch (length) {
                    case "01":
                        formatLength[0] = 1;
                        break;
                    case "10":
                        formatLength[0] = 2;
                        break;
                    case "11":
                        formatLength[0] = 3;
                        break;
                }
                var text = this.AssembleBits(bits, 0, 6);
                var result = this.SECSFormat.UNRECOGNIZE_FORMAT;
                switch (text) {
                    case "000000":
                        result = this.SECSFormat.List;
                        break;
                    case "001000":
                        result = this.SECSFormat.Binary;
                        break;
                    case "001001":
                        result = this.SECSFormat.Boolean;
                        break;
                    case "010000":
                        result = this.SECSFormat.Ascii;
                        break;
                    case "010010":
                        result = this.SECSFormat.Char;
                        break;
                    case "011000":
                        result = this.SECSFormat.I8;
                        break;
                    case "011001":
                        result = this.SECSFormat.I1;
                        break;
                    case "011010":
                        result = this.SECSFormat.I2;
                        break;
                    case "011100":
                        result = this.SECSFormat.I4;
                        break;
                    case "100000":
                        result = this.SECSFormat.F8;
                        break;
                    case "100100":
                        result = this.SECSFormat.F4;
                        break;
                    case "101000":
                        result = this.SECSFormat.U8;
                        break;
                    case "101001":
                        result = this.SECSFormat.U1;
                        break;
                    case "101010":
                        result = this.SECSFormat.U2;
                        break;
                    case "101100":
                        result = this.SECSFormat.U4;
                        break;
                    default:
                        result = this.SECSFormat.UNRECOGNIZE_FORMAT;
                        break;
                    case "010001":
                        break;
                }
                return result;
            },

            MessageHeader(header) {
                let bytes = header.subarray(2, 3);
                var bitArray = this.decimalToBinary(bytes, 8);
                var wait = bitArray.charAt(0)
                var streamID = 0;
                var functionID = 0;

                if (wait == '1') {
                    streamID = header[2] - 128;
                    wait = 'W'
                } else {
                    streamID = header[2];
                    wait = ''
                }
                functionID = header[3];

                return { 'streamID': streamID, 'functionID': functionID, 'wait': wait }
            }
            ,
            InMsg(message, Messagelength, MessageHead) {
                if (Messagelength) {
                    message = message.slice(4);
                }
                if (MessageHead) {
                    const headBytes = message.subarray(0, 10);
                    var resultHead = this.MessageHeader(headBytes)
                    this.log.Print("S" + resultHead.streamID + "F" + resultHead.functionID + " " + resultHead.wait);
                    message = message.slice(10);
                }


                let index = 0;
                let formatLength = 1;
                while (index < message.length) {
                    let incrementer = 0;
                    let formatCode = message[index];
                    let format = this.GetFormat(formatCode, formatLength);
                    index++;
                    let length;
                    switch (format) {
                        case this.SECSFormat.List: {
                            length = message[index];
                            if (length === 0) {
                                return;
                            }
                            index++;
                            this.log.Print("<L[" + length + "]");
                            this.log.Indent();

                            var result = this.InMsgChild(message, index, incrementer, length);
                            index = result.index;
                            incrementer = result.incrementer;
                            length = result.length;

                            this.log.Unindent();
                            this.log.Print(">");
                            break;
                        }
                        case this.SECSFormat.UNRECOGNIZE_FORMAT:
                            console.log("UNRECOGNIZE_FORMAT");
                            return;
                        default: {
                            length = message[index];
                            index++;
                            let array = new Uint8Array(length);
                            for (let i = 0; i < length; i++) {
                                array[i] = message[index + i];
                            }

                            if (array.length === 0) {
                                this.log.Print("<" + format + "[" + 0 + "] " + '' + ">");
                            } else if (format === this.SECSFormat.Binary) {
                                if (array.length === 1) {
                                    let b = array[0];
                                } else {
                                    console.log(array)
                                }
                                this.log.Print("<" + format + "[" + array.length + "] " + Array.from(array).map(byte => byte.toString(16).padStart(2, '0')).join(' ') + ">");
                            } else {
                                let SECSValue = this.GetMessageValue(format, array);
                                if (format === this.SECSFormat.Ascii) {
                                    this.log.Print("<" + format + "[" + SECSValue.toString().length + "] '" + SECSValue + "'>");
                                } else {
                                    this.log.Print("<" + format + "[" + SECSValue.toString().length + "] " + SECSValue + ">");
                                }
                            }
                            break;
                        }
                    }
                    if (incrementer !== length) {
                        index += length;
                    }
                }
            }
            ,
            InMsgChild(message, index, incrementer, length) {
                let length2 = 0;
                let formatLength = 1;
                while (index < message.length && incrementer < length) {
                    let incrementer2 = 0;
                    let formatCode = message[index];
                    let format = this.GetFormat(formatCode, formatLength);
                    switch (format) {
                        case this.SECSFormat.List:
                            index++;
                            length2 = message[index];
                            index++;
                            this.log.Print("<L[" + length2 + "]");
                            this.log.Indent();

                            var result = this.InMsgChild(message, index, incrementer2, length2);
                            index = result.index;
                            incrementer2 = result.incrementer;
                            length2 = result.length;

                            this.log.Unindent();
                            this.log.Print(">");
                            incrementer++;
                            break;
                        case this.SECSFormat.UNRECOGNIZE_FORMAT:
                            return;
                        default:
                            incrementer++;
                            index++;
                            let array = new Uint8Array(formatLength);
                            array.set(message.slice(index, index + formatLength), 0);
                            array = this.ReverseByte(array);
                            if (array.length === 1) {
                                length2 = array[0];
                            } else if (array.length === 2) {
                                let value = new Int16Array(array.buffer)[0];
                                length2 = value;
                            }
                            index += formatLength;
                            let array2 = new Uint8Array(length2);
                            array2.set(message.slice(index, index + length2), 0);
                            if (array2.length === 0) {
                                this.log.Print("<" + format + "[" + 0 + "] " + '' + ">");
                            } else if (format === this.SECSFormat.Binary) {
                                if (array2.length === 1) {
                                    let b = array2[0];
                                } else {
                                    console.log(array)
                                }
                                this.log.Print("<" + format + "[" + array2.length + "] " + Array.from(array2).map(byte => byte.toString(16).padStart(2, '0')).join(' ') + ">");
                            } else {
                                let SECSValue = this.GetMessageValue(format, array2);
                                if (format === this.SECSFormat.Ascii) {
                                    this.log.Print("<" + format + "[" + SECSValue.toString().length + "] '" + SECSValue + "'>");
                                } else {
                                    this.log.Print("<" + format + "[" + SECSValue.toString().length + "] " + SECSValue + ">");
                                }
                            }
                            break;
                    }
                    if (incrementer2 !== length2) {
                        index += length2;
                    }
                }
                return { 'index': index, 'incrementer': incrementer, 'length': length }
            }
            ,
            GetMessageValue(formatCode, message) {
                let obj = null;
                switch (formatCode) {
                    case this.SECSFormat.Ascii:
                        obj = String.fromCharCode.apply(null, message);
                        break;
                    case this.SECSFormat.Boolean:
                        obj = !!message[0];
                        break;
                    case this.SECSFormat.Char:
                        obj = String.fromCharCode(message[0]);
                        break;
                    case this.SECSFormat.F4:
                        message = this.ReverseByte(message);
                        obj = new DataView(new Uint8Array(message).buffer).getFloat32(0, true);
                        break;
                    case this.SECSFormat.F8:
                        message = this.ReverseByte(message);
                        obj = new DataView(new Uint8Array(message).buffer).getFloat64(0, true);
                        break;
                    case this.SECSFormat.I1:
                        message = this.ReverseByte(message);
                        let b2 = message[0];
                        obj = (b2 <= 127) ? b2 : (b2 - 256);
                        break;
                    case this.SECSFormat.I2:
                        message = this.ReverseByte(message);
                        obj = new DataView(new Uint8Array(message).buffer).getInt16(0, true);
                        break;
                    case this.SECSFormat.I4:
                        message = this.ReverseByte(message);
                        obj = new DataView(new Uint8Array(message).buffer).getInt32(0, true);
                        break;
                    case this.SECSFormat.I8:
                        message = this.ReverseByte(message);
                        obj = new DataView(new Uint8Array(message).buffer).getBigInt64(0, true);
                        break;
                    case this.SECSFormat.U1:
                        message = this.ReverseByte(message);
                        let b = message[0];
                        obj = b;
                        break;
                    case this.SECSFormat.U2:
                        message = this.ReverseByte(message);
                        obj = new DataView(new Uint8Array(message).buffer).getUint16(0, true);
                        break;
                    case this.SECSFormat.U4:
                        message = this.ReverseByte(message);
                        obj = new DataView(new Uint8Array(message).buffer).getUint32(0, true);
                        break;
                    case this.SECSFormat.U8:
                        message = this.ReverseByte(message);
                        obj = new DataView(new Uint8Array(message).buffer).getBigUint64(0, true);
                        break;
                }
                return obj.toString();
            },



        }
    })
</script>

</html>
View Code

相關文章