Returning string from a remote server using rpcgen - rpc

I am going through RPC tutorial and learn few techniques in rpcgen. I have the idea of adding, multiplying different data types using rpcgen.
But I have not found any clue that how could I declare a function in .x file which will return a string. Actually I am trying to build a procedure which will return a random string(rand string array is in server).
Can any one advise me how to proceed in this issue? It will be helpful if you advise me any tutorial regarding this returning string/pointer issue.
Thank you in advance.

Ok, answering to the original question (more than 2 years old), the first answer is correct but a little tricky.
In your .x file, you define your structure with the string inside, having defined previously the size of the string:
typedef string str_t<255>;
struct my_result {
str_t data;
};
...
Then you invoke rpcgen on your .x file to generate client and server stubs and .xdr file:
$rpcgen -N *file.x*
Now you can compile client and server in addition to any program where you pretend to use the remote functions. To do so, I followed the "repcgen Tutorial" in ORACLE's web page:
https://docs.oracle.com/cd/E19683-01/816-1435/rpcgenpguide-21470/index.html
The tricky part is, although you defined a string of size m (array of m characters) what rpcgen and .xdr file create is a pointer to allocated memmory. Something like this:
.h file
typedef char *str_t;
struct my_result {
int res;
str_t data;
};
typedef struct my_result my_result;
.xdr file
bool_t xdr_str_t (XDR *xdrs, str_t *objp)
{
register int32_t *buf;
if (!xdr_string (xdrs, objp, 255))
return FALSE;
return TRUE;
}
So just take into account when using this structure in your server side that it is not a string of size m, but a char pointer for which you'll have to reserve memory before using it or you'll be prompted the same error than me on execution:
Segmentation fault!
To use it on the server you can write:
static my_result response;
static char text[255];
memset(&response, '\0', sizeof(my_result));
memset(text, '\0', sizeof(text));
response.data = text;
And from there you are ready to use it wisely! :)

According to the XDR protocol specification you can define a string type where m is the length of the string in bytes:
The standard defines a string of n (numbered 0 to n -1) bytes to be the number n encoded as an unsigned integer (as described above), and followed by the n bytes of the string. Each byte must be regarded by the implementation as being 8-bit transparent data. This allows use of arbitrary character set encodings. Byte m of the string always precedes byte m +1 of the string, and byte 0 of the string always follows the string's length. If n is not a multiple of four, then the n bytes are followed by enough (0 to 3) residual zero bytes, r, to make the total byte count a multiple of four.
string object<m>;
You can then define a struct with the string type str_t as one of the variables:
typedef string str_t<255>;
struct my_result {
str_t data;
};
Then in your .x file you can define an RPC in your program which returns a struct of type my_result. Since rpcgen will give you a pointer to this struct (which I have called res) you can print the message with prinf("%s\n", res->data);.
program HELLO_PROG {
version HELLO_VERSION {
my_result abc() = 1;
} = 1;
} = 1000;

Related

Convert an Arduino C++ String to an ANSI C string (char* array) using the String method .c_str() [duplicate]

This question already has answers here:
How to convert a std::string to const char* or char*
(11 answers)
Closed 1 year ago.
because many posts about this problem are misleading or ambiguous, this is how it works, just for the record (tested):
How to convert an Arduino C++ String to an ANSI C string (char* array) using the String method .c_str()
String myString = "WLAN_123456789"; // Arduino String
char cbuffer[128]=""; // ANSI C string (char* array); adjust size appropriately
strcpy( cbuffer, myString.c_str() );
example (tested):
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println();
String myString = "WLAN_123456789";
char cbuffer[128]="";
Serial.println(myString); // debug
strcpy( cbuffer, myString.c_str() );
Serial.println(cbuffer); // debug
}
void loop() {
}
PS:
the example is for variable Strings and for variable char arrays which are not constant, to be able to be assigned to repeatedly and arbitrarily
(also for constant strings optionally, too).
I know your intension is trying to share "best practise" of using String.c_str(), but I couldn't help to say that your "answer" is exactly the one that cause confusion, misleading or ambiguous that you said. If anyone find String.c_str() usage is misleading or ambiguous, it is because one probably does not read the c_str() on Arduino Reference, or fully understand the pointer well.
Converts the contents of a String as a C-style, null-terminated string. Note that this gives direct access to the internal String buffer and should be used with care. In particular, you should never modify the string through the pointer returned. When you modify the String object, or when it is destroyed, any pointer previously returned by c_str() becomes invalid and should not be used any longer.
String.c_str() return a pointer to char array (i.e. a array of char terminated with a '\0'), in another word const char* ptr = String.c_str() represented exactly what String.c_str() is about. Noticed that it is directly access to the buffer in the String object and you are not supposed to change it. So in most case, you use it like Serial.print(ptr). You only need to get a copy of it (as show in your strcpy) if you want to modified the content of your 'copy'.
To put it simply, if the char array (i.e. String.c_str()) is the banana you want to get from a jungle (i.e. the String object) which consists of the forest, the monkey and the whole nine yards, the String.c_str() point you directly to the banana, nothing more, nothing less.

How to send an int over uint8_t data?

I'm using the RadioHead Packet Radio library from airspayce.com. In the example (nrf24_reliable_datagram_client & server) they let two nodes communicate with each other by sending strings back and forth. Now I want to send an int instead of a string there, and do something with this data. This is what they do in the example:
Define the buf byte.
uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN];
This function receives the data:
manager.recvfromAckTimeout(buf, &len, 500, &from)
Print the buf variable.
Serial.print((char*)buf);
So far so good.Now I want to do something like:
int value = (char*)buf;
Or:
char value[10] = { (char*)buf };
But then I get:
invalid conversion from 'char*' to 'int' (or to 'char'...)
Next to that, on the other side where I'm sending the data, I have:
uint8_t data[] = { analogRead(A0) };
When I'm printing this data on the receiver side, using the code from the first question, I get weird characters. So I thought, let's try:
Serial.print((char*)buf, DEC); // or BYTE
But then I get:
call of overloaded 'print(char*, int)' is ambiguous
What am I doing wrong? Thanks in advance!
You can't just assign an array to an integer and hope that it merges the elements together for you - for example, how does it know how to merge them?
For converting a uint16_t to a uint8_t[2] array you would want to do something like this:
uint16_t analog = analogRead(A0); //read in as int.
uint8_t data[2] = {analog, (analog >> 8)}; // extract as {lower byte, upper byte)
Serial.write(data,2); //write the two bytes to the serial port, lower byte first.
You could do it in other ways like using a union of a uint16_t with an array of two uint8_t's, but the above way is more portable. You could also do it by type casting the pointer to an int, however if one end uses big endian and the other uses little endian, that won't work unless you flip the data around in the array as you are receiving it.
For the receiver end, you would have:
uint8_t data[2];
...
... //whatever you do to receive the bytes that were sent over serial.
...
//Now assuming that data[] contains the received bytes where:
//data[0] was the first in (lower byte) and data[1] was the second in (upper byte)
uint16_t merged = (data[1] << 8) | data[0]; //merge them back together
Hopefully that helps.
Also, the 'overloaded prototype' is saying that no function exists which takes that particular set of input variables. From the print class header you will find there is however this prototype:
write(const uint8_t *buffer, size_t size);
which does what you want - print a specified number of uint8_t's from an array.

Converting an int or String to a char array on Arduino

I am getting an int value from one of the analog pins on my Arduino. How do I concatenate this to a String and then convert the String to a char[]?
It was suggested that I try char msg[] = myString.getChars();, but I am receiving a message that getChars does not exist.
To convert and append an integer, use operator += (or member function concat):
String stringOne = "A long integer: ";
stringOne += 123456789;
To get the string as type char[], use toCharArray():
char charBuf[50];
stringOne.toCharArray(charBuf, 50)
In the example, there is only space for 49 characters (presuming it is terminated by null). You may want to make the size dynamic.
Overhead
The cost of bringing in String (it is not included if not used anywhere in the sketch), is approximately 1212 bytes of program memory (flash) and 48 bytes RAM.
This was measured using Arduino IDE version 1.8.10 (2019-09-13) for an Arduino Leonardo sketch.
Risk
There must be sufficient free RAM available. Otherwise, the result may be lockup/freeze of the application or other strange behaviour (UB).
Just as a reference, below is an example of how to convert between String and char[] with a dynamic length -
// Define
String str = "This is my string";
// Length (with one extra character for the null terminator)
int str_len = str.length() + 1;
// Prepare the character array (the buffer)
char char_array[str_len];
// Copy it over
str.toCharArray(char_array, str_len);
Yes, this is painfully obtuse for something as simple as a type conversion, but somehow it's the easiest way.
You can convert it to char* if you don't need a modifiable string by using:
(char*) yourString.c_str();
This would be very useful when you want to publish a String variable via MQTT in arduino.
None of that stuff worked. Here's a much simpler way .. the label str is the pointer to what IS an array...
String str = String(yourNumber, DEC); // Obviously .. get your int or byte into the string
str = str + '\r' + '\n'; // Add the required carriage return, optional line feed
byte str_len = str.length();
// Get the length of the whole lot .. C will kindly
// place a null at the end of the string which makes
// it by default an array[].
// The [0] element is the highest digit... so we
// have a separate place counter for the array...
byte arrayPointer = 0;
while (str_len)
{
// I was outputting the digits to the TX buffer
if ((UCSR0A & (1<<UDRE0))) // Is the TX buffer empty?
{
UDR0 = str[arrayPointer];
--str_len;
++arrayPointer;
}
}
With all the answers here, I'm surprised no one has brought up using itoa already built in.
It inserts the string representation of the integer into the given pointer.
int a = 4625;
char cStr[5]; // number of digits + 1 for null terminator
itoa(a, cStr, 10); // int value, pointer to string, base number
Or if you're unsure of the length of the string:
int b = 80085;
int len = String(b).length();
char cStr[len + 1]; // String.length() does not include the null terminator
itoa(b, cStr, 10); // or you could use String(b).toCharArray(cStr, len);

Convert hex to int

I've seen lots of answers to this, but I cannot seem to get any to work. I think I'm getting confused between variable types. I have an input from NetworkStream that is put a hex code into a String^. I need to take part of this string, convert it to a number (presumably int) so I can add some arithemetic, then output the reult on the form. The code I have so far:
String^ msg; // gets filled later, e.g. with "A55A6B0550000000FFFBDE0030C8"
String^ test;
//I have selected the relevant part of the string, e.g. 5A
test = msg->Substring(2, 2);
//I have tried many different routes to extract the numverical value of the
//substring. Below are some of them:
std::stringstream ss;
hexInt = 0;
//Works if test is string, not String^ but then I can't output it later.
ss << sscanf(test.c_str(), "%x", &hexInt);
//--------
sprintf(&hexInt, "%d", test);
//--------
//And a few others that I've deleted after they don't work at all.
//Output:
this->textBox1->AppendText("Display numerical value after a bit of math");
Any help with this would be greatly appreciated.
Chris
Does this help?
String^ hex = L"5A";
int converted = System::Convert::ToInt32(hex, 16);
The documentation for the Convert static method used is on the MSDN.
You need to stop thinking about using the standard C++ library with managed types. The .Net BCL is really very good...
Hope this helps:
/*
the method demonstrates converting hexadecimal values,
which are broken into low and high bytes.
*/
int main(){
//character buffer
char buf[1];
buf[0]= 0x06; //buffer initialized to some hex value
buf[1]= 0xAE; //buffer initialized to some hex value
int number=0;
//number generated by binary shift of high byte and its OR with low byte
number = 0xFFFF&((buf[1]<<8)|buf[0]);
printf("%x",number); //this prints AE06
printf(ā€œ%dā€,number); //this prints the integer equivalent
getch();
}

Variant type storage and alignment issues

I've made a variant type to use instead of boost::variant. Mine works storing an index of the current type on a list of the possible types, and storing data in a byte array with enough space to store the biggest type.
unsigned char data[my_types::max_size];
int type;
Now, when I write a value to this variant type comes the trouble. I use the following:
template<typename T>
void set(T a) {
int t = type_index(T);
if (t != -1) {
type = t;
puts("writing atom data");
*((T *) data) = a; //THIS PART CRASHES!!!!
puts("did it!");
} else {
throw atom_bad_assignment;
}
}
The line that crashes is the one that stores data to the internal buffer. As you can see, I just cast the byte array directly to a pointer of the desired type. This gives me bad address signals and bus errors when trying to write some values.
I'm using GCC on a 64-bit system. How do I set the alignment for the byte array to make sure the address of the array is 64-bit aligned? (or properly aligned for any architecture I might port this project to).
EDIT: Thank you all, but the mistake was somewhere else. Apparently, Intel doesn't really care about alignment. Aligned stuff is faster but not mandatory, and the program works fine this way. My problem was I didn't clear the data buffer before writing stuff and this caused trouble with the constructors of some types. I will not, however, mark the question as answered, so more people can give me tips on alignment ;)
See http://gcc.gnu.org/onlinedocs/gcc-4.0.4/gcc/Variable-Attributes.html
unsigned char data[my_types::max_size] __attribute__ ((aligned));
int type;
I believe
#pragma pack(64)
will work on all modern compilers; it definitely works on GCC.
A more correct solution (that doesn't mess with packing globally) would be:
#pragma pack(push, 64)
// define union here
#pragma pack(pop)

Resources