JavaScript解析SECS GEM报文
下面用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
<!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>