import { globalLookup } from "@/hook/global"; /** * @description 计算 value 的渲染格式 * @param {string} link * @returns {number} */ function getValueFormatCode(link) { const renderOptions = globalLookup.currentSignalRenderOptions; if (renderOptions.has(link)) { const option = renderOptions.get(link); if (typeof option.valueFormat === 'number') { return option.valueFormat; } } // 默认 16 进制 return 2; } /** * @description 高效计算 2 的 n 次方,n可以是负数 * @param {number} n 整数 */ function efficentPow2(n) { if (n === 0) { return 1; } if (n > 0) { return 1 << n; } if (n < 0) { return 1 / (1 << (-n)); } } /** * @description 16进制数字转换成有符号浮点数 * @param {string} hex * @returns {number} */ export function hexToSignedInt(hex) { if (hex.length % 2 != 0) { hex = "0" + hex; } var num = parseInt(hex, 16); var maxVal = Math.pow(2, hex.length / 2 * 8); if (num > maxVal / 2 - 1) { num = num - maxVal } return num; } export class FormatValueRender { /** * * @param {string} link 信号的ID * @param {number} width 信号的位置宽度 * @param {string} replacer 位置不足的地方会用 replacer 补足,比如 ... */ constructor(link, width, replacer = '') { this.formatCode = getValueFormatCode(link); this.width = BigInt(width); this.replacer = replacer; } /** * @description 根据当前的设置进行数值的渲染 * @param {number} value 波形当前的数值 * @param {number} pos 当前的位置宽度 * @returns {string} 转换后需要渲染的字符串 */ render(value, pos) { const formatCode = this.formatCode; switch (formatCode) { // 二进制 case 0: return this.radixTransform(2, value, pos); // 八进制 case 1: return this.radixTransform(8, value, pos); // 十六进制 case 2: return this.radixTransform(16, value, pos); // 有符号整型 case 3: return this.radixTransform(10, value, pos, true); // 无符号整型 case 4: return this.radixTransform(10, value, pos); // 半精度浮点数 case 5: return this.floatTransform(16, value, pos); // 单精度浮点数 case 6: return this.floatTransform(32, value, pos); // 双精度浮点数 case 7: return this.floatTransform(64, value, pos); // 未知 default: return '?'; } } /** * @description 整数的进制转换 * @param {number} radix 进制 * @param {number} value 值 * @param {number} pos 位置宽度 * @param {boolean} sign 是否是有符号数,默认为 false */ radixTransform(radix, value, pos, sign = false) { const width = this.width; const replacer = this.replacer; switch (value) { case 'x': return 'x'; break; case '?': return '?'; break; default: value = BigInt(value); break; } // 如果是有符号数 if (sign) { if ((value >> (width - 1n)) & 1n) { value = value - (2n ** width); } let valueString = value.toString(radix); if (pos === -1 || valueString.length <= pos) { return valueString; } const valSign = (value < 0) ? '-' : ''; if (pos === 1) { return valSign; } if (pos === 2) { return valSign + replacer; } return valSign + replacer + valueString.slice(2 - pos); } // 无符号的各类进制数字 let valueString = value.toString(radix); // 如果是 2, 8, 16 进制,需要根据宽度自动完全前面的 0 的填充 let displayWidth, padding; switch (radix) { case 2: displayWidth = Number(width); padding = "0".repeat(displayWidth - valueString.length); valueString = padding + valueString; break; case 8: displayWidth = Math.ceil(Number(width) / 3); padding = "0".repeat(displayWidth - valueString.length); valueString = padding + valueString; break; case 16: displayWidth = Math.ceil(Number(width) / 4); padding = "0".repeat(displayWidth - valueString.length); valueString = padding + valueString; break; default: break; } if (pos === -1 || valueString.length <= pos) { return valueString; } if (pos === 1) { return replacer; } return replacer + valueString.slice(1 - pos); } /** * @description IEEE 浮点数的转换 * 为什么没有 sign 参数?因为 IEEE 定义下的 float 第一个位置就是符号 * @param {number} fWidth 16, 32 或者 64 * @param {number} value 值 * @param {number} pos 位置 */ floatTransform(fWidth, value, pos) { const replacer = this.replacer; if (fWidth === 16) { value = this.calcIEEEFloat(value, 5, 10); } else if (fWidth === 32) { value = this.calcIEEEFloat(value, 8, 23); } else if (fWidth === 64) { value = this.calcIEEEFloat(value, 11, 52); } const valueString = value.toString(); if (pos === -1 || valueString.length <= pos) { return valueString; } const valSign = (value < 0) ? '-' : ''; if (pos === 1) { return valSign; } if (pos === 2) { return valSign + replacer; } return valSign + replacer + valueString.slice(2 - pos); } /** * @description 根据 e 和 m 得到计算 IEEE 浮点数的函数,默认第一个位置代表符号 * @param {number} value 值 * @param {number} eWidth 代表指数部分的位数 * @param {number} mWidth 代表有效尾数的位数 * @returns {(value: number) => number} */ calcIEEEFloat(value, eWidth, mWidth) { const offset = (1 << (eWidth - 1)) - 1; const mFrac = 1 << mWidth; // 计算参考博客:https://blog.csdn.net/leo0308/article/details/117398166 let bits = value.toString(2); const width = 1 + eWidth + mWidth; // 应对 bits 长度和 预定长度不一致的情况 // 不足的地方补上 0,否则进行截断 if (bits.length < width) { bits = '0'.repeat(width - bits.length) + bits; } if (bits.length > width) { bits = bits.substring(bits.length - width); } const s = parseInt(bits[0], 2); const e = parseInt(bits.substring(1, 1 + eWidth), 2); const m = parseInt(bits.substring(2 + eWidth), 2); const sign = (s === 1) ? -1 : 1; // 全 0 if (e === 0) { return sign * efficentPow2(-offset + 1 - mWidth) * m; } // 全 1 if (e === ((1 << eWidth) - 1)) { if (m === 0) { return sign * Infinity; } return NaN; } return sign * efficentPow2(e - offset) * (1 + m / mFrac); } } export class JSValueRender { /** * * @param {string} link 信号的ID * @param {number} width 信号的位置宽度 * @param {string} replacer 位置不足的地方会用 replacer 补足,比如 ... */ constructor(link, width) { this.formatCode = getValueFormatCode(link); this.width = BigInt(width); } /** * @description 根据当前的设置进行数值的渲染 * @param {number} value 波形当前的数值 * @returns {string} 转换后需要渲染的字符串 */ explainAsNumber(value) { const formatCode = this.formatCode; switch (formatCode) { // 二进制 case 0: return this.radixTransform(2, value); // 八进制 case 1: return this.radixTransform(8, value); // 十六进制 case 2: return this.radixTransform(16, value); // 有符号整型 case 3: return this.radixTransform(10, value, true); // 无符号整型 case 4: return this.radixTransform(10, value); // 半精度浮点数 case 5: return this.floatTransform(16, value); // 单精度浮点数 case 6: return this.floatTransform(32, value); // 双精度浮点数 case 7: return this.floatTransform(64, value); // 未知 default: return '?'; } } /** * @description 整数的进制转换 * @param {number} radix 进制 * @param {number} value 值 * @param {number} pos 位置宽度 * @param {boolean} sign 是否是有符号数,默认为 false */ radixTransform(radix, value, sign = false) { const width = this.width; const replacer = this.replacer; switch (value) { case 'x': return -1; break; case '?': return -1; break; default: value = BigInt(value); break; } // 如果是有符号数 if (sign) { if ((value >> (width - 1n)) & 1n) { value = value - (2n ** width); } return parseInt(value); } // 无符号的各类进制数字 return parseInt(value); } /** * @description IEEE 浮点数的转换 * 为什么没有 sign 参数?因为 IEEE 定义下的 float 第一个位置就是符号 * @param {number} fWidth 16, 32 或者 64 * @param {number} value 值 * @param {number} pos 位置 */ floatTransform(fWidth, value) { const replacer = this.replacer; if (fWidth === 16) { value = this.calcIEEEFloat(value, 5, 10); } else if (fWidth === 32) { value = this.calcIEEEFloat(value, 8, 23); } else if (fWidth === 64) { value = this.calcIEEEFloat(value, 11, 52); } return value; } /** * @description 根据 e 和 m 得到计算 IEEE 浮点数的函数,默认第一个位置代表符号 * @param {number} value 值 * @param {number} eWidth 代表指数部分的位数 * @param {number} mWidth 代表有效尾数的位数 * @returns {(value: number) => number} */ calcIEEEFloat(value, eWidth, mWidth) { const offset = (1 << (eWidth - 1)) - 1; const mFrac = 1 << mWidth; // 计算参考博客:https://blog.csdn.net/leo0308/article/details/117398166 let bits = value.toString(2); const width = 1 + eWidth + mWidth; // 应对 bits 长度和 预定长度不一致的情况 // 不足的地方补上 0,否则进行截断 if (bits.length < width) { bits = '0'.repeat(width - bits.length) + bits; } if (bits.length > width) { bits = bits.substring(bits.length - width); } const s = parseInt(bits[0], 2); const e = parseInt(bits.substring(1, 1 + eWidth), 2); const m = parseInt(bits.substring(2 + eWidth), 2); const sign = (s === 1) ? -1 : 1; // 全 0 if (e === 0) { return sign * efficentPow2(-offset + 1 - mWidth) * m; } // 全 1 if (e === ((1 << eWidth) - 1)) { if (m === 0) { return sign * Infinity; } return NaN; } return sign * efficentPow2(e - offset) * (1 + m / mFrac); } }