Разбор гигантской шестнадцатеричной строки

У меня есть устройство, которое выводит различные данные датчика, парень, который сделал прошивку, прислал мне «образец ответа» и что это значит, в основном у него есть шестнадцатеричный ключ SOF, шестнадцатеричный ключ EOF и ключ ESC, который добавлен XOR, чтобы избежать путаницы, когда одно и то же шестнадцатеричное значение должно быть «посередине». Теперь из этой гигантской строки 0x..., которая является моим «примером ответа», каждое «значение» представлено 4 байтами (8 символов), поэтому я сделал метод, в котором он добавляет каждые 8 ​​символов в массив и удаляет его из полученного ответа каждый 8-байтовый набор переходит в массив, а затем «переводится», однако, как мне сказали, каждое число должно переводиться примерно в 0,24xxxx Таким образом, в конце я должен получить массив из 50 датчиков со значениями от 0,2 до 0,3, однако, когда я анализирую эти данные, мой вывод показывает значения в диапазоне от 1,95E-37 до 265209,9. . .что совершенно безумно.

В настоящее время я знаю, что мой код отстой, и, когда я больше думаю об этом, я чувствую, что полностью игнорирую эту клавишу ESC, которая есть для чего-то, я предполагаю, что мне следует анализировать каждый байт по мере его поступления, а не сначала «группировать его»? но я в недоумении, как даже упорядочить данные.

    readonly string SOF = "0x02";
    readonly string EOF = "7E";
    readonly string ESC = "2D";

    private void testParsingBtn(object sender, EventArgs e)
    {
        string exResponse = "0x0200C800013E7F4A643E7F766E3E7F8DFC3E7F6C603E7F59DC3E7F60663E7F57903E7F5F823E7F502E3E7F60303E7F612C3E7F61683E7F43923E7F4ABE3E7F7CDA3E7F5D783D392F383E7F4AF43E7F68703E7F59BE3E7F531C3E7F60783E7F62A03E7F6C903E7F7A883E7F4D523E7F4E783E7F3A983E7F6FBA3E7F52AA3E7F6D983E7F6F543E7F6AE03D392A703E7F750C3E7F6C423E7F64A43E7F4F323E7F61863E7F60A83E7F711C3E7F81483E7F68E83E7F56163E7F732C3E7F6CA83E7F65823E7F72963E7F668A3E7F48C0C05C7E";
        parseResponse(exResponse);
    }
    private void parseResponse (string receivedString)
    {
        String[] receivedBytes = new String[50];

        if (receivedString.Substring(0,4) == SOF)
        {
            Console.WriteLine("Start of File (SOF) detected");
            receivedString = receivedString.Remove(0, 4);
        }

        if (receivedString.Substring(receivedString.Length - 2, 2) == EOF)
        {
            Console.WriteLine("End of File (EOF) detected");
            receivedString = receivedString.Remove(receivedString.Length-2, 2);
        }

        //Convert the Hex to decimal and set it to the probe array and update the GUI
        for (int i = 0; i < receivedBytes.Length; i++)
        {
            receivedBytes[i] = receivedString.Substring(0, 8);
            receivedString = receivedString.Remove(0, 8);
        }

        foreach (String str in receivedBytes)
        {
            Console.WriteLine(str);
        }

        
        for (int i = 0; i < probeArray.Length; i++)
        {
            byte[] arr = StringToByteArray(receivedBytes[i]);
            probeArray[i].Temp = BitConverter.ToSingle(arr,0);
        }
        foreach (Probe probe in probeArray)
        {
            Console.WriteLine(probe.Temp);
        }
        updateTemps();
    }


    private static byte[] StringToByteArray(string hex)
    {
        return Enumerable.Range(0, hex.Length)
                         .Where(x => x % 2 == 0)
                         .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
                         .ToArray();
    }
}

Должен ли я преобразовать его в отдельные байты? (2 символа?) без группировки их в 8-байтовые массивы? Это то, что ты имеешь в виду? (Я чувствую, что мне нужно проверить каждый символ, является ли он SOF/EOF/ESC, но я только «жестко кодирую» удаление хвоста и начала)

David Boydston 22.03.2022 22:56

ОК, теперь я понимаю, что означает ESC>> Клавиша ESC, которая добавляется XOR, чтобы избежать путаницы, когда одно и то же шестнадцатеричное значение должно быть «посередине». можешь уточнить

pm100 22.03.2022 23:10

Кстати - полезный инструмент gregtoll.com/~gregstoll/floattohex

pm100 22.03.2022 23:17

во-первых, я предполагаю, что c800 - это длина, не включая заголовок и трейлер c800 = 200, ваши данные составляют 208 байт, но все еще работают над остальными.

pm100 22.03.2022 23:26

первый float начинается со смещения 5 => 3e7f4a64 = 0,249307, это явно заголовок 0200 c800 01 для начала

pm100 22.03.2022 23:31
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
48
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ну вот

internal class Program {
    static float decode(string hex) {
        var k = Enumerable.Range(0, hex.Length)
               .Where(x => x % 2 == 0)
               .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
               .ToArray();

        Array.Reverse(k);
        return BitConverter.ToSingle(k, 0);
    }

    static void Main(string[] args) {

        string hex = "0x0200C800013E7F4A643E7F766E3E7F8DFC3E7F6C603E7F59DC3E7F60663E7F57903E7F5F823E7F502E3E7F60303E7F612C3E7F61683E7F43923E7F4ABE3E7F7CDA3E7F5D783D392F383E7F4AF43E7F68703E7F59BE3E7F531C3E7F60783E7F62A03E7F6C903E7F7A883E7F4D523E7F4E783E7F3A983E7F6FBA3E7F52AA3E7F6D983E7F6F543E7F6AE03D392A703E7F750C3E7F6C423E7F64A43E7F4F323E7F61863E7F60A83E7F711C3E7F81483E7F68E83E7F56163E7F732C3E7F6CA83E7F65823E7F72963E7F668A3E7F48C0C05C7E";
        for(int i = 12; i < hex.Length - 12; i+= 8) {

            var f = decode(hex.Substring(i, 8));
            Console.WriteLine(f);
        
        }
    }
}

выход

0.24930722
0.24947521
0.24956506
0.24943686
0.24936622
0.24939117
0.24935746
0.24938777
0.2493293
0.24939036
0.24939412
0.24939501
0.2492812
0.24930856
0.24949971
0.24937999
0.045211047
0.24930936
0.24942183
0.24936578
0.24934047
0.24939144
0.24939966
0.24943757
0.24949086
0.24931839
0.24932277
0.24924695
0.24944964
0.24933878
0.2494415
0.24944812
0.24943113
0.045206487
0.24946994
0.24943641
0.24940735
0.24932554
0.24939546
0.24939215
0.24945492
0.2495166
0.24942362
0.24935183
0.24946278
0.24943793
0.24941066
0.24946055
0.2494146
0.24930096

Обратите внимание на array.reverse, по-видимому, BitConverter.ToSingle читает в обратном направлении

The order of bytes in the array must reflect the endianness of the computer system's architecture.

не знал, что "порядок байтов" тоже учитывается для чисел с плавающей запятой


Как работает это преобразование строки в двоичный файл?

Это конвейер LINQ, чтобы стать компетентным программистом на С#, вам нужно знать LINQ.

Это работает так

IEnumerable<T>->F1()->F2()->F3....

Fns — это методы расширения, определенные в IEnumerable<T>, которые возвращают IEnumerable<T>.

IEnumerable<T> это то, что вы можете сделать foreach(....) на. нравится

  • массивы
  • Список
  • Куча
  • Строка (перечислите символы)
  • Словарь<T1,T2>
  • ....

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

Вот простой пример.

У нас есть класс Сотрудник

public class Employee {
    public string Name;
    public string ID;
    public int Salary;
}

и их список

 var emps = new List<Employee>() {
            new Employee{Name  = "Simon", ID = "x1", Salary= 500},
            new Employee{Name  = "Sarah", ID = "g4", Salary= 600},
            new Employee{Name  = "Adam", ID = "h4", Salary= 700}

        };

теперь нам нужен отсортированный список имен emps с > 500

        var names = emps.Where(e => e.Salary > 500).Select(e => e.Name).OrderBy(s => s).ToList();

«emps» — наш источник IEnumerable

  emps=>Where

Where фильтрует на основе аргумента лямбда-предиката

  Where=>Select

Select «проецирует» вход на выход, вы преобразуете запись через лямбду. В этом случае введенный сотрудник «e» преобразуется в string, e.Name

  Select=>OrderBy

OrderBy сортирует по ключу, выбранному лямбдой, в данном случае по строке 's' (то есть по имени)

  OrderBy=>ToList

Конвейерная линия потеряла свою Listспособность, она просто передает один элемент за раз, нам нужно материализовать его обратно в полезный контейнер. ToList превращает его в список (потому что это то, что получилось из OrderBy)

В функциях LINQ есть два типа:

 IEnum->F->IEnum

Затем можно соединить вместе, плюс

IEnum->F->One thing

Они завершают цепочку

Функции ToXXX завершают цепочку

Также другие, как Граф

   var richCount = emps.Where(e => e.Salary > 500).Select(e => e.Name).OrderBy(s => s).Count();

Дает нам количество emps с зарплатой> 500 (выбор и заказ здесь бесполезны). Проще

   var richCount = emps.Count(e => e.Salary > 500);

Count может принимать предикат.

Если вы хотите увидеть, как эти вещи работают, смотрите здесь

https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,e449fbc07f49dc52

LINQ удивительно полезен и должен быть в вашем наборе инструментов С#. Всякий раз, когда вы чувствуете, что тянетесь за какими-то данными, подумайте о LINQ. Это очень эффективно, и все разработчики С#, которые придут после вас, поймут, что вы делаете.


ОК, учебник по LINQ закончен, как работает шестнадцатеричная строка -> массив байтов

 var k = Enumerable.Range(0, hex.Length)
               .Where(x => x % 2 == 0)
               .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
               .ToArray();

Нам нужен IEnumerable для запуска конвейера LINQ. У нас есть строка, но единственное, что мы можем легко с ней сделать, это перечислить каждый символ, нам нужны пары, поэтому писатель выбрал другую тактику.

Enumerable — это класс методов статического генератора, они генерируют IEnumerables, см. https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable?view=net-6.0

Enumerable.Range генерирует последовательность чисел, в данном случае от 0 до 8 (шестнадцатеричная строка состоит из 8 символов)

 Range->Where

Где выбирает только четные числа

 Where->Select

Выбор говорит

  x => Convert.ToByte(hex.Substring(x, 2), 16)

т.е. сопоставьте число ввода int (0,2,4,6) с этой подстрокой, преобразуйте комбо

Окончательно

  ToArray()

Чтобы получить массив байтов

Маленький или большой порядок байтов? Bitconverter ненадежен при работе со встроенными платформами.

3Dave 22.03.2022 23:47

@3Dave хорошо, OP сказал, что значения лежат между 0,2 и 0,3, поэтому я думаю, что получил правильные значения (с первого раза не было обратного)

pm100 22.03.2022 23:49

О, ваши результаты определенно выглядят правильными. Я как раз думал о том, зачем нужен реверс. Каждый современный процессор использует прямой порядок байтов, но некоторые устройства Arm могут использовать и то, и другое. Пожимайте плечами.

3Dave 22.03.2022 23:49

@3Dave, потому что передача была отправлена ​​с обратным порядком байтов, а в моем окне Windows — с обратным порядком байтов.

pm100 22.03.2022 23:51

Как было бы, если бы это была Windows или Linux, телефон, консоль и т. д. Древние микроконтроллеры Motorola используют big endian, но... в этом столетии мало что еще производилось. При отправке через сокет, SPI и т. д. мы используем обратный порядок байтов. Это надоедливая необходимость.

3Dave 22.03.2022 23:52

@ 3Dave «При отправке мы используем обратный порядок байтов» - прямой порядок байтов является сетевым стандартом. BIg endian — это человеческий способ записи, возможно, поэтому

pm100 23.03.2022 17:31

Святое гуак, братан, я пытаюсь понять это, это первый раз, когда я использую С# (из Swift и Java). Если вы не возражаете, не могли бы вы подробнее рассказать обо всем выражении Enumerable? Что делает (x должен быть равен или больше остатка от x/2? и должен равняться 0?) не слишком хорошо знакомым с предикатами (которые, как я полагаю, похожи на лямбда-выражения?)

David Boydston 23.03.2022 17:36

@DavidBoydston Я взял этот код прямо у вас, вы, вероятно, нашли его, погуглив. Это довольно причудливо и не так, как я бы это сделал. Я отредактирую свой ответ а) как бы я это сделал б) как работает код Enumerable (позже)

pm100 23.03.2022 17:40

@DavidBoydston Кроме того, отметьте ответ как правильный, так как я расшифровал эту строку для вас.

pm100 23.03.2022 17:40

@DavidBoydston вот, вероятно, откуда вы взяли это от stackoverflow.com/questions/321370/…

pm100 23.03.2022 17:42

Блин, даже не заметил, ха-ха. Спасибо! эта обратная вещь также является еще одной стеной, о которую я, вероятно, скоро врежусь.

David Boydston 23.03.2022 17:54

Спасибо за ускоренный курс LINQ @pm100

David Boydston 23.03.2022 22:12

Другие вопросы по теме