398 lines
12 KiB
JavaScript
398 lines
12 KiB
JavaScript
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);
|
||
}
|
||
} |