У меня есть устройство, которое выводит различные данные датчика, парень, который сделал прошивку, прислал мне «образец ответа» и что это значит, в основном у него есть шестнадцатеричный ключ 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();
}
}
ОК, теперь я понимаю, что означает ESC>> Клавиша ESC, которая добавляется XOR, чтобы избежать путаницы, когда одно и то же шестнадцатеричное значение должно быть «посередине». можешь уточнить
Кстати - полезный инструмент gregtoll.com/~gregstoll/floattohex
во-первых, я предполагаю, что c800 - это длина, не включая заголовок и трейлер c800 = 200, ваши данные составляют 208 байт, но все еще работают над остальными.
первый float начинается со смещения 5 => 3e7f4a64 = 0,249307, это явно заголовок 0200 c800 01 для начала
Ну вот
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(....)
на. нравится
Код для шестнадцатеричного преобразования в двоичный, возможно, не очень хорошо использует 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 хорошо, OP сказал, что значения лежат между 0,2 и 0,3, поэтому я думаю, что получил правильные значения (с первого раза не было обратного)
О, ваши результаты определенно выглядят правильными. Я как раз думал о том, зачем нужен реверс. Каждый современный процессор использует прямой порядок байтов, но некоторые устройства Arm могут использовать и то, и другое. Пожимайте плечами.
@3Dave, потому что передача была отправлена с обратным порядком байтов, а в моем окне Windows — с обратным порядком байтов.
Как было бы, если бы это была Windows или Linux, телефон, консоль и т. д. Древние микроконтроллеры Motorola используют big endian, но... в этом столетии мало что еще производилось. При отправке через сокет, SPI и т. д. мы используем обратный порядок байтов. Это надоедливая необходимость.
@ 3Dave «При отправке мы используем обратный порядок байтов» - прямой порядок байтов является сетевым стандартом. BIg endian — это человеческий способ записи, возможно, поэтому
Святое гуак, братан, я пытаюсь понять это, это первый раз, когда я использую С# (из Swift и Java). Если вы не возражаете, не могли бы вы подробнее рассказать обо всем выражении Enumerable? Что делает (x должен быть равен или больше остатка от x/2? и должен равняться 0?) не слишком хорошо знакомым с предикатами (которые, как я полагаю, похожи на лямбда-выражения?)
@DavidBoydston Я взял этот код прямо у вас, вы, вероятно, нашли его, погуглив. Это довольно причудливо и не так, как я бы это сделал. Я отредактирую свой ответ а) как бы я это сделал б) как работает код Enumerable (позже)
@DavidBoydston Кроме того, отметьте ответ как правильный, так как я расшифровал эту строку для вас.
@DavidBoydston вот, вероятно, откуда вы взяли это от stackoverflow.com/questions/321370/…
Блин, даже не заметил, ха-ха. Спасибо! эта обратная вещь также является еще одной стеной, о которую я, вероятно, скоро врежусь.
Спасибо за ускоренный курс LINQ @pm100
Должен ли я преобразовать его в отдельные байты? (2 символа?) без группировки их в 8-байтовые массивы? Это то, что ты имеешь в виду? (Я чувствую, что мне нужно проверить каждый символ, является ли он SOF/EOF/ESC, но я только «жестко кодирую» удаление хвоста и начала)