Доступ к структуре C в Java - PullRequest
4 голосов
/ 28 января 2010

У меня есть структура C, которая отправляется по некоторым промежуточным сетям и получает через последовательный канал Java-код. Код Java дает мне байтовый массив, который я теперь хочу упаковать в исходную структуру. Теперь, если получаемый код был в C, это было просто. Есть ли простой способ переупаковки байта [] в Java в структуру C. У меня минимальный опыт работы с Java, но это, похоже, не является распространенной проблемой или решается в каких-либо часто задаваемых вопросах.

К вашему сведению структура C

struct data {
     uint8_t        moteID;
    uint8_t     status; //block or not
    uint16_t   tc_1;
    uint16_t   tc_2;
    uint16_t   panelTemp;  //board temp
    uint16_t   epoch#;
    uint16_t   count;    //pkt seq since the start of epoch
    uint16_t   TEG_v;   
    int16_t   TEG_c;    
 }data;

Ответы [ 4 ]

8 голосов
/ 28 января 2010

Я бы порекомендовал вам отправлять номера по проводам в порядке байтов в сети. Это устраняет проблемы:

  1. Генерация границ слов для вашей структуры.
  2. Порядок следования байтов для вашего оборудования (как отправляющий, так и принимающий).

Кроме того, числа Java всегда хранятся в сетевом порядке байтов независимо от платформы, на которой вы запускаете Java (спецификация JVM требует определенного порядка байтов).

Очень хорошим классом для извлечения битов из потока является java.nio.ByteBuffer, который может переносить произвольные байтовые массивы; не только те, которые приходят из класса ввода / вывода в java.nio. Вы действительно не должны вручную кодировать собственное извлечение примитивных значений, если это вообще возможно (т. Е. Сдвиг битов и т. Д.), Поскольку легко ошибиться, код одинаков для каждого экземпляра одного типа, и существует множество стандартных классов, которые предоставляют это для вас.

Например:

public class Data {

    private byte moteId;
    private byte status;
    private short tc_1;
    private short tc_2;
    //...etc...
    private int tc_2_as_int;

    private Data() {
        // empty
    }

    public static Data createFromBytes(byte[] bytes) throws IOException {
        final Data data = new Data();
        final ByteBuffer buf = ByteBuffer.wrap(bytes);

        // If needed...
        //buf.order(ByteOrder.LITTLE_ENDIAN);

        data.moteId = buf.get();
        data.status = buf.get();
        data.tc_1 = buf.getShort();
        data.tc_2 = buf.getShort();
        // ...extract other fields here

        // Example to convert unsigned short to a positive int
        data.tc_2_as_int = buf.getShort() & 0xffff;

        return data;        
    }

}

Теперь, чтобы создать его, просто позвоните Data.createFromBytes(byteArray).

Обратите внимание, что в Java нет целочисленных переменных без знака, но они будут получены с точно такой же битовой комбинацией. Поэтому все, где бит старшего разряда не установлен, при использовании будет точно таким же. Вам нужно будет иметь дело с битом старшего разряда, если вы ожидали этого в своих числах без знака. Иногда это означает сохранение значения в следующем большом целочисленном типе (byte -> short; short -> int; int -> long).

Редактировать: обновлен пример, показывающий, как преобразовать short (16-битная подпись) в int (32-битная подпись) со значением без знака с tc_2_as_int.

Обратите внимание, что если вы не можете изменить порядок байтов и он не находится в сетевом порядке, то java.nio.ByteBuffer может по-прежнему обслуживать вас здесь с помощью buf.order(ByteOrder.LITTLE_ENDIAN); до получения значений.

2 голосов
/ 28 января 2010

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

Если у вас есть структура данных, приведите ее к массиву байтов / символов, а затем просто отправьте ее вслепую, иногда вы можете столкнуться с большими проблемами при декодировании ее на другом конце. Это связано с тем, что иногда компилятор решил оптимизировать способ упаковки данных в структуре, поэтому в необработанных байтах он может выглядеть не совсем так, как вы ожидаете, в зависимости от того, как вы его кодируете. Это действительно зависит от компилятора!

Существуют прагмы компилятора, которые можно использовать для оптимизации упаковки. См. Справочник по препроцессору C / C ++ - pack

Другая проблема - это 32/64-битная проблема, если вы просто используете «int» и «long» без указания количества байтов ... но вы сделали это: -)

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

например. У вас есть класс данных, такой как

class Data
{
    public int        moteID;
    public int        status; //block or not
    public int        tc_1;
    public int        tc_2;
}

Затем, когда вы получите байтовый массив, вы можете сделать что-то вроде этого

Data convertBytesToData(byte[] dataToConvert)
{
    Data d = Data();
    d.moteId = (int)dataToConvert[0];
    d.status = (int)dataToConvert[1];
    d.tc_1 = ((int)dataToConvert[2] << 8) + dataTocConvert[3];  // unpacking 16-bits
    d.tc_2 = ((int)dataToConvert[4] << 8) + dataTocConvert[5];  // unpacking 16-bits
}

У меня может быть 16-битная распаковка в неправильном направлении, это зависит от порядкового номера вашей системы C, но вы сможете поиграть и посмотреть, правильно ли это или нет. Некоторое время я не играл с Java, но, надеюсь, в функции int, встроенные в наши дни, может входить byte []. Во всяком случае, я знаю, что есть для C #.

Учитывая все это, если вы не выполняете передачу данных с высокой скоростью, определенно посмотрите на JSON и Protocol Buffers!

1 голос
/ 28 января 2010

Предполагая, что вы контролируете оба конца ссылки, вместо того, чтобы отправлять необработанные данные, лучше использовать кодировку, которую могут использовать C и Java. Посмотрите на JSON или на буфер протокола.

0 голосов
/ 28 января 2010

То, что вы пытаетесь сделать, проблематично по нескольким причинам:

  • Различные реализации C будут представлять значения uint16_t (и int16_t) по-разному. В некоторых случаях наиболее значимый байт будет первым, когда структура размещена в памяти. В других случаях младший байт будет.

  • Различные компиляторы C могут по-разному упаковывать поля структуры. Таким образом, возможно (например), что поля были переупорядочены или заполнение могло быть добавлено.

Итак, все это означает, что вы должны точно определить, что выложено struct ... и просто надеяться, что это не изменится, когда / если вы поменяете компиляторы C или целевую платформу C.

Сказав это, я не смог найти библиотеку Java для декодирования произвольных потоков двоичных данных, которая позволяла бы вам выбирать "endian-ness". Классы DataInputStream и DataOutputStream могут быть ответом, но они явно определены для отправки / ожидания старшего байта в первую очередь. Если ваши данные поступают по-другому, вам нужно выполнить битовую обработку Java, чтобы исправить это.

РЕДАКТИРОВАТЬ : на самом деле (как указывает @Kevin Brock) java.nio.ByteBuffer позволяет указывать порядковый номер при извлечении различных типов данных из двоичного буфера.

...