Convert array<Byte> to Struct in managed CPP - arrays

This has to be one of the most common things to do when working with IP Sockets. You get the data as a array and then you probably need to convert it to a struct.
The code looks something like this:
array<Byte> ^ buffer = gcnew array<Byte>(2048);
while( nDataLen < 16 )
{
nDataLen += m_Socket->Receive( buffer, 16-nDataLen, SocketFlags::None );
m_Socket->Poll( 100000, SelectMode::SelectRead );
}
Now I need to take the 16 Bytes I just received and convert them to the following struct;
[StructLayout(LayoutKind::Explicit, Size=16, Pack=1,CharSet=CharSet::Ansi)]
public ref struct ResponseHeader
{
public:
[FieldOffset(0)]
UInt32 m_StartMessage; // STRP
[FieldOffset(4)]
Int32 m_MessageLength; // Total message length
[FieldOffset(8),MarshalAs(UnmanagedType::ByValTStr,SizeConst=6)]
String ^ m_Status; // Message Status
[FieldOffset(14),MarshalAs(UnmanagedType::ByValTStr,SizeConst=2)]
String ^ m_Reason; // Message Reason
};
I would like to see a cast from the array directly to the struct but I am not sure that is doable in managed code, simple in native cpp.
If there isn't a way to cast this, then what is the best way to proceed?

Related

Code repetition when working with structures

I am writing an app in C which takes responses from server, parses them and stores them in memory to do something later with that data. Server protocol is based on simple commands which looks like <Name of the command>#<arg1>#<arg2>...<argn>#%.
I decided to store those commands in my program the following way. I have a base structure message, which looks like this:
struct message
{
enum message_type type;
void *msg;
};
Enum looks like this:
enum message_type
{
MSG1,
MSG2,
...
MSGN
};
And the specific messages:
struct specific_message
{
int arg1;
char *arg2;
...
type argn;
};
To do some operations with these messages I have huge switch in functions, like:
struct message *create_message(enum message_type type)
{
struct message *res;
res = malloc(sizeof(*res));
res->type = type;
switch(type)
{
case MSG1: res->msg = create_msg1(); break;
...
case MSGN: res->msg = create_msgn(); break;
default: fprintf(stderr, "Unknown msg\n"); free(res); return NULL;
}
return res;
}
And specific ones look almost the same(for example if the field is a pointer it is initialized with NULL, if int with -1 etc...)
At first i done it myself by hand, but after writing several such functions i decided to write a script, which gets structs from header and generates such functions(initialization of fields in specific messages, destroying specific messages, copying, etc...).
It works fine, but the size of the implementation file becames very big(it is around 1000 lines now, and it is just the creation, copying and destruction). And I can't write those via a macro(well at least i don't know how), because macros don't know anything about the layout of the structure. So the whole file becomes repetitive.
I thought about different approaches, for example to have just the base message structure which would contain command type, number of arguments and array of char*, which would contain those arguments in string form. But then I need to add all this logic for retrieving the arguments I need from this array and casting it to the type I need. So I thought it wouldn't scale.
Is there another approach I could use?

Best way to extract all enums from C project

I'm writing my own enums-based backtrace. Basically each function call will append it's error to a pre-defined stack which will eventually include all errors across all function calls.
I want to create an efficient way to parse it. In the end I got int backtrace[] with enum values from all over my codebase, and I'd like to relate each enum value to it's name.
This should of course be done post-failure (not at runtime), so what I'm missing is a way, maybe as a part of the compilation step, to dump all my enums and there values to a file. Then i can write a script that parse the errors.
Is there an easy (cross-platform) static tool that does that?
Not sure how you log the errors, but how about:
typedef enum
{
E_SUCCESS = 0,
E_FAIL,
...
} error_t;
typedef struct
{
error_t code;
char * error_name;
} backtrace_entry_t;
backtrace_entry_t backtrace[..];
#define FUNC_EXIT(error_code__) \
do { \
backtrace[func_id].code = (error_code__); \
backtrace[func_id].error_name = #error_code__; \
} while (0)
Then, when you call FUNC_EXIT(E_SUCCESS);, you'll get for the backtrace for the function to be: {.code = 0, .error_name = "E_SUCCESS"}
The problem is, you can't get the right name if you call FUNC_EXIT(var);, where var is some local variable.
Another option, although still not an automagical one:
typedef enum
{
E_SUCCESS = 0,
E_FAIL,
...
NOF_ERROR_CODES
} error_t;
#define MAP_E_CODE_TO_NAME(error_code__) [error_code__] = #error_code__
const char * error_to_name_mapping[NOF_ERROR_CODES] = {
MAP_E_CODE_TO_NAME(E_SUCCESS),
MAP_E_CODE_TO_NAME(E_FAIL),
...
};
Will give a const char * array with {"E_SUCCESS", "E_FAIL", ...}, which you can use like this:
printf("The name of error code %d is '%s'", error, error_to_name_mapping[error]);
The problem here is that you have to have positive value to errors with increasing values.

Extracting data from an unknown encoding file

We use testing equipment (1995 year manufacturing) powered by MS DOS. Analog-digital converter records information in the file.
In [picture1] is shown the structure of that file.
In [picture2] is shown the oscillogram that constructed according to the data from the file (program for opening the file on MS DOS).
Below I placed link to this file (google drive).
This file contains the data that need for me - the massive of point of oscillogram. I want have opportunities to keep, analyze and print this chart on Windows or Linux (not MS DOS). So I need to extract data from the file.
But I can't make it. And no program (known to me) can't open this file. I analyzed a few first byte and they point to program 'TRAS v4.99'. This program is on MS DOS.
But I really hope, that it is really to get data without this program.
P.S. If anyone will say it is impossible - it is will well too because I haven't found point of view yet:)
Thank you for your time! Best regards!
LINK TO FILE ON GOOGLE DISK - 00014380.K00
STRUCTURE OF FILE
OPENING FILE VIA PROGRAM IN MS DOS
Here is an idea on how you can tackle this problem. Since the format is relatively well specified in the handbook you can use the Java programming language for example with something like java.io.RandomAccessFile to read arrays of bytes. These arrays of bytes can then be converted to Java primitive types OR to string according to the data type. After this conversion you can the print out the data in a human readable format.
Below you can find some sample code to give you an idea of what you could do with this approach (I have not tested the code, it is not complete, it is just to give you an idea of what you can do):
public static void readBinaryfile() throws IOException {
java.io.RandomAccessFile randomAccessFile = new RandomAccessFile("test.bin", "r");
byte[] addKenStrBytes = new byte[12];
randomAccessFile.read(addKenStrBytes);
String addKenStr = new String(addKenStrBytes, "UTF-8");
// TODO: Do something with addKenStr.
System.out.println(addKenStr);
byte[] kopfSizeBytes = new byte[2];
randomAccessFile.read(kopfSizeBytes);
// TODO: Do something with kopfSizeBytes
System.out.println(convertToInt(kopfSizeBytes));
byte[] addRufNrCounterBytes = new byte[6];
randomAccessFile.read(addRufNrCounterBytes);
long addRufNrCounter = convertToLong(addRufNrCounterBytes);
// TODO: Do something with addRufNrCounter
System.out.println(addRufNrCounter);
byte[] endAdrBytes = new byte[4];
randomAccessFile.read(endAdrBytes);
// TODO: Do something with endAdrBytes
System.out.println(convertToLong(endAdrBytes));
// Continue here and after you reached the end of the record repeat until you reached the end off the file
}
private static int convertToInt(byte[] bytes) {
if(bytes.length > 4) {
throw new IllegalArgumentException();
}
int buffer = 0;
for(byte b : bytes) {
buffer |= b;
buffer = buffer << 8;
}
return buffer;
}
private static long convertToLong(byte[] bytes) {
if(bytes.length > 8) {
throw new IllegalArgumentException();
}
long buffer = 0L;
for(byte b : bytes) {
buffer |= b;
buffer = buffer << 8;
}
return buffer;
}
Note that fields with more than 8 bytes need to be most probably converted to strings. This is not complete code, just an example to give you an idea on how you can tackle this problem.

Nanopb without callbacks

I'm using Nanopb to try and send protobuf messages from a VxWorks based National Instruments Compact RIO (9025). My cross compilation works great, and I can even send a complete message with data types that don't require extra encoding. What's getting me is the callbacks. My code is cross compiled and called from LabVIEW and the callback based structure of Nanopb seems to break (error out, crash, target reboots, whatever) on the target machine. If I run it without any callbacks it works great.
Here is the code in question:
bool encode_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
{
char *str = "Woo hoo!";
if (!pb_encode_tag_for_field(stream, field))
return false;
return pb_encode_string(stream, (uint8_t*)str, strlen(str));
}
extern "C" uint16_t getPacket(uint8_t* packet)
{
uint8_t buffer[256];
uint16_t packetSize;
ExampleMsg msg = {};
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
msg.name.funcs.encode = &encode_string;
msg.value = 17;
msg.number = 18;
pb_encode(&stream, ExampleMsg_fields, &msg);
packetSize = stream.bytes_written;
memcpy(packet, buffer, 256);
return packetSize;
}
And here's the proto file:
syntax = "proto2"
message ExampleMsg {
required int32 value = 1;
required int32 number = 2;
required string name = 3;
}
I have tried making the callback an extern "C" as well and it didn't change anything. I've also tried adding a nanopb options file with a max length and either didn't understand it correctly or it didn't work either.
If I remove the string from the proto message and remove the callback, it works great. It seems like the callback structure is not going to work in this LabVIEW -> C library environment. Is there another way I can encode the message without the callback structure? Or somehow embed the callback into the getPacket() function?
Updated code:
extern "C" uint16_t getPacket(uint8_t* packet)
{
uint8_t buffer[256];
for (unsigned int i = 0; i < 256; ++i)
buffer[i] = 0;
uint16_t packetSize;
ExampleMsg msg = {};
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
msg.name.funcs.encode = &encode_string;
msg.value = 17;
msg.number = 18;
char name[] = "Woo hoo!";
strncpy(msg.name, name, strlen(name));
pb_encode(&stream, ExampleMsg_fields, &msg);
packetSize = stream.bytes_written;
memcpy(packet, buffer, sizeof(buffer));
return packetSize;
}
Updated proto file:
syntax = "proto2"
import "nanopb.proto";
message ExampleMsg {
required int32 value = 1;
required int32 number = 2;
required string name = 3 [(nanopb).max_size = 40];
}
You can avoid callbacks by giving a maximum size for the string field using the option (nanopb).max_size = 123 in the .proto file. Then nanopb can generate a simple char array in the structure (relevant part of documentation).
Regarding why callbacks don't work: just a guess, but try adding extern "C" also to the callback function. I assume you are using C++ there, so perhaps on that platform the C and C++ calling conventions differ and that causes the crash.
Does the VxWorks serial console give any more information about the crash? I don't remember if it does that for functions called from LabView, so running some test code directly from the VxWorks shell may be worth a try also.
Perhaps the first hurdle is how the code handles strings.
LabVIEW's native string representation is not null-terminated like C, but you can configure LabVIEW to use a different representation or update your code to handle LabVIEW's native format.
LabVIEW stores a string in a special format in which the first four bytes of the array of characters form a 32-bit signed integer that stores how many characters appear in the string. Thus, a string with n characters requires n + 4 bytes to store in memory.
LabVIEW Help: Using Arrays and Strings in the Call Library Function Node
http://zone.ni.com/reference/en-XX/help/371361L-01/lvexcodeconcepts/array_and_string_options/

packing struct in golang in bytes to talk with C application

I have a golang based client that must to speak with a C daemon, using a binary protocol. I cannot change the server, to support json, xml or other protocol.
in the C code, i have to fill in the following struct and send it via network:
typedef struct pkt_struct{
int16_t pkt_version;
int16_t pkt_type;
u_int32_t crc32_v;
int16_t ret_code;
char buffer[1024];
}pkt;
to have an idea, how do I need the data, it should look like the output from:
$ irb
2.0.0-p353 :002 > [2, 1, 0, 0, 'version', 3].pack("nnNna1024n")
is gob the answer? reading through the documentation, looks that it is not. maybe ProtoBuf?
what I did until now is:
import "encoding/binary"
....
type NPacket struct {
packet_version int16
packet_type int16
crc32_value uint32
ret_code int16
buffer string
}
....
var pkt_send NPacket
pkt_send = NPacket{2,1,0,0,"version"}
buf := new(bytes.Buffer)
if err := binary.Write(buf, binary.BigEndian, &pkt_send); err != nil {
fmt.Println(err)
os.Exit(1)
}
error that I'm getting:
binary.Write: invalid type string
binary.Write only works on fixed sized objects. A string may be any size. Instead, you probably want to copy your C code and use a fixed size byte array:
type NPacket struct {
packet_version int16
packet_type int16
crc32_value uint32
ret_code int16
buffer [1024]byte
}
You can set the buffer with copy(pkt.buffer[:], "string").
Neither gob nor protobufs are the answer. They are both encoding formats and give you no control over how the data is marshalled.

Resources