Как я могу преобразовать отрицательное двоичное число в целое число?

Я хочу прочитать данные через красный узел узла Modbus из источника данных. Диапазон от -20000 до 20000, но узел не может обрабатывать отрицательные числа, поэтому мне пришлось преобразовать их в двоичные числа (DWORD), разделить их на младшее и старшее слово и преобразовать эти слова обратно в целые числа.

var low

function dec2bin(dec){
    return (dec >>> 0).toString(2);
}

var a = msg.payload

if (a >= 0){

    a = dec2bin(a);
    a = parseInt(a,2);

} else {

    a = dec2bin(a);
    a = a.substr(16);
    a = parseInt(a,2);

} 

low = { payload: a };

return low;

Для визуализации я хочу использовать узлы панели мониторинга, но для этого мне нужно соединить две двоичные строки вместе и преобразовать их в целое число.

Проблема:

node red преобразует их как qword, поэтому двоичное число 1111 1111 1111 1111 1111 1100 0001 1000 воспринимается как 4.294.966.296, а не как -1000. Но если я заполню следующую паузу 1 лаймом, так: 1111 1111 1111 1111 1111 1111 1111 1111 1111 1100 0001 1000 получится 18446744073709552000

Спасибо


person c0nr3f    schedule 13.05.2020    source источник


Ответы (3)


parseInt работает со строками переменной длины в качестве входных данных, что означает, что он не может распознать старший бит как бит знака. Другими словами: он анализирует двоичные строки, как десятичные строки, используя - в качестве знака; поэтому вам нужно использовать метод, отличный от parseInt.

Что мешает вам взять это 16-битное значение и просто передать его как беззнаковое? Вам просто нужно проверить, больше ли значение, поступающее в ваш javascript, чем 32767, а затем выполнить преобразование с двумя дополнениями вручную:

Тогда ваш диапазон входящих чисел будет 0..20000 для положительных «оригиналов» и 45536..65535 для исходного диапазона -20000..-1 (если я сейчас думаю правильно, я не проверял это). Это означало бы, что преобразование легко:

if( number > 32767 ) {
  number = -65536 + number;
}

Или для любого типа двоичных чисел (который меньше, чем размер числа языка, на котором вы работаете):

if( number > SIGNED_MAX_INT ) {
  number = -(UNSIGNED_MAX_INT+1) + number;
}

(Эти константы не существуют в том виде, в каком они написаны, здесь они больше похожи на псевдокод)

person orithena    schedule 13.05.2020

Во-первых, ДЕЙСТВИТЕЛЬНО плохая практика создавать новые объекты msg в функциональных узлах, вы должны просто обновить значение msg.payload и передать исходный объект.

Следующим самым простым способом сделать это будет работа с буфером

e.g.

var b = Buffer(4)
b.writeUInt32BE(msg.playload)
msg.payload = b.readInt32BE()

return msg;

Узел соединения в ручном режиме может объединить 2 буфера меньшего размера в один из 16-битных буферов.

person hardillb    schedule 13.05.2020
comment
Хорошо, за ваш ответ, можете ли вы сказать мне, почему создание нового объекта является плохой практикой? Проблема в том, что у меня есть 2 значения uint16. Как я могу объединить буферы в один, чтобы я мог использовать вашу функцию? - person c0nr3f; 13.05.2020
comment
Вот пример старшего слова: {data:[65535],buffer:[0xff,0xff]} и младшего байта: {data:[64536],buffer:[0xfc,0x18]}. - person c0nr3f; 13.05.2020
comment
Хорошо, так как это уже буферы, просто объедините их в один буфер и прочитайте из него с помощью предоставленных методов. Вы не создаете новые сообщения, потому что это отбрасывает любые дополнительные метаданные, которые могут перемещаться с полезной нагрузкой, например. объект msg.resp, добавленный узлом http-in, необходим для работы узла http-response. - person hardillb; 13.05.2020
comment
Я не могу объединить их с узлом соединения, мне нужно как-то вручную это делать и не подскажете как? Извините, я совсем новичок в javascript. - person c0nr3f; 13.05.2020
comment
На самом деле вы можете просто объединить их с узлом соединения (при условии, что они всегда приходят по порядку) - person hardillb; 13.05.2020
comment
Нет, но я могу установить msg.complete, чтобы упорядочить заказ хотя бы во второй телеграмме. Если я присоединяюсь вручную в качестве буфера, я получаю сообщение об ошибке. Но если я присоединяюсь как массив, я получаю 0 обратно. Пример после функции соединения: [{data:[64536],buffer:[252,24]},{data:[65535],buffer:[255,255]}] - person c0nr3f; 13.05.2020
comment
Вам не нужно объединять все msg.payload, вы можете ограничиться только msg.payload.buffer - person hardillb; 13.05.2020

Что ж, на данный момент JavaScript хромает, потому что у него нет сильных типов для этого. Вероятно, не лучший, но один из способов сделать это — создать собственный класс Int.

class Int {
  constructor(length, binaryStr) {
    const cleanBinary = "0".repeat(length - binaryStr.length) + binaryStr
    if (cleanBinary.startsWith("1")) {
      const invertedBinary = cleanBinary.split("")
        .map(char => char === "1" ? "0": "1")
        .join("")
      this.decValue = -parseInt(invertedBinary, 2) - 1
    } else {
      this.decValue = parseInt(cleanBinary, 2)
    }
  }
}

Что-то подобное

new Int(8, "11111100")
> Int {decValue: -4}
strBin = "11111111111111111111110000011000";
new Int(strBin.length, strBin)
> Int {decValue: -1000}

Это может быть преобразовано во что-то более продвинутое и может использоваться в браузере.

person Tim Nimets    schedule 13.05.2020
comment
У меня есть некоторые проблемы с реализацией этого класса в красный узел. Кроме того, я новичок в javascript, поэтому мне не так просто преобразовать это в функцию. Что мне здесь нужно, так это:strBin = msg.payload; новый Int(strBin.length, strBin) msg.payload = Int; Это не работает, потому что это класс. Есть ли у вас предложения ? - person c0nr3f; 13.05.2020
comment
Если у вас есть число фиксированного размера, например 16 бит, вы можете получить ответ таким образом const convertedNumber = new Int(16, msg.playload).decValue. Но обратите внимание, что этот класс принимает двоичные файлы без пробелов. Вы можете добавить некоторое форматирование перед. - person Tim Nimets; 13.05.2020