How to properly write to a tape drive in linux with C/++? - c++

I'm currently trying to write to a LTO tape device via C++. My problem is, that subsequent write requests will result in "Invalid argument" errors, while the first call seemingly works just fine.
I have set up the tape drive to use a fixed block size of 64k (Which means, that it can only be written to in chunks of 64k or 64k multiples at a time).
When now trying to write via fwrite() to it, the first call will be successful (or at least return that the requested amount of entries where written), but subsequent calls (with the same parameters) will result in no data being written (fwrite returns 0) and an error of "Invalid argument".
Following is a small sample application thrown together to replicate the issue.
#include <iostream>
#include <unistd.h>
#define BLOCK_SIZE (64 * 1024)
#define DATA_BLOCKS 32
#define byte unsigned char
int main(int argc, char *argv[]) {
if (argc <= 1) {
std::cout << "Missing first parameter: Target file\n";
return 1;
}
// Create file handle to access tape device in RW mode
FILE* handle;
const char* targetFile = argv[1];
if (access(targetFile, R_OK | W_OK) == 0) {
handle = fopen(targetFile, "r+");
printf("Create handle OK: %s\n", targetFile);
} else {
printf("Could not access %s: Missing rad or write permission\n", targetFile);
return 1;
}
// Create an byte array with data for DATA_BLOCKS blocks
byte *data = new byte[BLOCK_SIZE * DATA_BLOCKS];
// Initialize with some data
for (int i = 0; i < BLOCK_SIZE * DATA_BLOCKS; i++) {
data[i] = '5';
}
// Write data in BLOCK_SIZE chunks, blocksToWrite times per fwrite() call, in numberOfWriteCalls fwrite() calls
size_t blocksToWrite = 4;
int numberOfWriteCalls = 5;
size_t written;
for (int i = 0; i < numberOfWriteCalls; i++) {
written = fwrite(data, BLOCK_SIZE, blocksToWrite, handle);
printf("Round %d: Wrote %d entries of expected %d entries\n", i+1, (int) written, (int) blocksToWrite);
// Check if there was an error and if so, print error info to stderr
if (ferror(handle)) {
printf("Error while writing:\n");
perror("");
clearerr(handle);
}
/* Start modification: Added flushing, as pointed out by user7860670 */
fflush(handle);
// Check if there was an error and if so, print error info to stderr
if (ferror(handle)) {
printf("Error while flushing:\n");
perror("");
clearerr(handle);
}
/* End modification: Added flushing, as pointed out by user7860670 */
}
delete[] data;
// Close file handle
fclose(handle);
return 0;
}
This code calls the fwrite function 5 times, each time trying to write 4 blocks of 64k data.
This works fine on normal files, but returns the following output on all of my tape devices:
Create handle OK: /dev/nst0
Round 1: Wrote 4 entries of expected 4 entries
Round 2: Wrote 0 entries of expected 4 entries
Invalid argument
Round 3: Wrote 0 entries of expected 4 entries
Invalid argument
Round 4: Wrote 0 entries of expected 4 entries
Invalid argument
Round 5: Wrote 0 entries of expected 4 entries
Invalid argument
As can be seen, the first call reacts as expected: fwrite return that 4 blocks of data have been written. But all subsequent calls, even while using the same parameters, will return 0 written blocks and an error of "Invalid argument".
Is there any "special sauce" when trying to write to tape files that I'm not aware of, or am I maybe using the fwrite functionality in a wrong way?

Related

Writing to uart serial port & receiving response, losing bytes when using nonblocking mode

I made a simple c++ program for armv7 architecture (compiled with linaro gnueabihf using raspi rootfs) that takes in arguments with baud rate, data, serial port etc and sends it to the selected serial port and receives the response. At least that's the goal of it.
I'm currently using it to send a command to disable/enable backlight on an industrial screen through an UART port. The screen takes a simple text command ended with crlf and returns a response. The specification of the screen says it uses 9600 baud, no parity, 8 data bits and 1 stop bit for communication, so pretty much standard.
While the sending works flawlessly - I cannot seem to find a way to properly receive the response. I tried configuring the termios port structure in multiple different ways (disabling hardware control, using cfmakeraw, configuring the VMIN and VTIME values) but without luck.
First thing is that, I'm receiving all the input byte by byte (so each read() call returns exactly 1 byte..), but that wouldn't be a problem.
When using nonblocking mode without select() I'm receiving all bytes, but I don't know when to stop receiving (and I want it to be universal, so I send a command, expect a simple response and if there is no more data then just exit). I made a time counter since the last message, so if nothing was received in last ~500ms then I assume nothing more will come. But this sometimes loses some bytes of the response and I don't know why.
When using blocking mode, I receive correct bytes (still byte by byte though), but I don't know when to stop and the last call to read() leaves the program hanging, because nothing else comes in the input.
When adding select() to the blocking call, to see if input is readable, I get very frequent data loss (sometimes just receiving a few bytes), and sometimes select returns 1, but read() blocks, and I'm left hanging.
When I just send data without doing any reading, and look at the input using cat -v < /dev/ttyS3 I can actually see correct input on the serial port all the time, however when I run both cat and my program as receivers, only one of them gets the data (or cat receives a few bytes and my program a few), this suggests me that something is "stealing" my bytes the same way when I try to read it, but what could it be, and why is it like that?
My current code (using the nonblocking read + 500ms timeout), that still loses some bytes from time to time:
#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
int open_port(char* portname)
{
int fd; // file description for the serial port
fd = open(portname, O_RDWR | O_NOCTTY | O_NDELAY);
if(fd == -1) // if open is unsucessful
{
printf("Error: open_port: Unable to open %s. \n", portname);
}
else
{
//fcntl(fd, F_SETFL, 0);
fcntl(fd, F_SETFL, FNDELAY);
}
return(fd);
}
int configure_port(int fd, int baud_rate)
{
struct termios port_settings;
tcgetattr(fd, &port_settings);
cfsetispeed(&port_settings, baud_rate); // set baud rates
cfsetospeed(&port_settings, baud_rate);
cfmakeraw(&port_settings);
port_settings.c_cflag &= ~PARENB; // no parity
port_settings.c_cflag &= ~CSTOPB; // 1 stop bit
port_settings.c_cflag &= ~CSIZE;
port_settings.c_cflag |= CS8; // 8 data bits
tcsetattr(fd, TCSANOW, &port_settings); // apply the settings to the port
return(fd);
}
/**
* Convert int baud rate to actual baud rate from termios
*/
int get_baud(int baud)
{
switch (baud) {
case 9600:
return B9600;
case 19200:
return B19200;
case 38400:
return B38400;
case 57600:
return B57600;
case 115200:
return B115200;
case 230400:
return B230400;
case 460800:
return B460800;
case 500000:
return B500000;
case 576000:
return B576000;
case 921600:
return B921600;
case 1000000:
return B1000000;
case 1152000:
return B1152000;
case 1500000:
return B1500000;
case 2000000:
return B2000000;
case 2500000:
return B2500000;
case 3000000:
return B3000000;
case 3500000:
return B3500000;
case 4000000:
return B4000000;
default:
return -1;
}
}
unsigned char* datahex(char* string) {
if(string == NULL)
return NULL;
size_t slength = strlen(string);
if((slength % 2) != 0) // must be even
return NULL;
size_t dlength = slength / 2;
unsigned char* data = (unsigned char*)malloc(dlength);
memset(data, 0, dlength);
size_t index = 0;
while (index < slength) {
char c = string[index];
int value = 0;
if(c >= '0' && c <= '9')
value = (c - '0');
else if (c >= 'A' && c <= 'F')
value = (10 + (c - 'A'));
else if (c >= 'a' && c <= 'f')
value = (10 + (c - 'a'));
else {
free(data);
return NULL;
}
data[(index/2)] += value << (((index + 1) % 2) * 4);
index++;
}
return data;
}
int main(int argc, char **argv) {
int baud_rate = B9600;
baud_rate = get_baud(atoi(argv[1]));
if(baud_rate == -1) {
printf("Error: Cannot convert baud rate %s, using 9600\n", argv[1]);
baud_rate = B9600;
}
bool convertHex = false;
char portName[24] = "/dev/ttyS0";
bool debug = false;
bool noreply = false;
for(int i = 3; i < argc; i++) {
if(!strcmp(argv[i], "hex"))
convertHex = true;
else if(strstr(argv[i], "/dev/") != NULL)
strncpy(portName, argv[i], sizeof(portName));
else if(!strcmp(argv[i], "debug"))
debug = true;
else if(!strcmp(argv[i], "no-reply"))
noreply = true;
}
unsigned char* data = nullptr;
size_t len = 0;
if(convertHex) {
data = datahex(argv[2]);
if((int)data == (int)NULL) {
convertHex = false;
printf("Error: Couldn't convert hex value! Needs to be even length (2 chars per byte)\n");
}
else
len = strlen(argv[2])/2;
}
if(!convertHex) {
data = (unsigned char*)argv[2];
len = strlen(argv[2]);
}
int fd = open_port(portName);
if(fd == -1) {
printf("Error: Couldn't open port %s\n", portName);
if(convertHex)
free(data);
return 0;
}
configure_port(fd, baud_rate);
if(debug) {
printf("Sending data (raw): ");
for(int i =0; i< len; i++) {
printf("%02X", data[i]);
}
printf("\n");
}
size_t writelen = write(fd, data, len);
if(debug)
printf("Sent %d/%d bytes\n", writelen, len);
if(writelen != len)
printf("Error: not all bytes were sent (%d/%d)\n", writelen, len);
else if(noreply)
printf("WRITE OK");
if(!noreply) {
unsigned char ibuff[512] = {0};
int curlen = 0; // full length
clock_t begin_time = clock();
while(( float(clock() - begin_time) / CLOCKS_PER_SEC) < 0.5 && curlen < sizeof(ibuff)) {
int ret = read(fd, ibuff+curlen, sizeof(ibuff)-curlen-1);
if(ret < 0) {
ret = 1;
continue;
}
if(ret > 0) {
curlen += ret;
begin_time = clock();
}
}
if(curlen > 0) {
ibuff[curlen] = 0; // null terminator
printf("RESPONSE: %s", ibuff);
}
}
if(fd)
close(fd);
if(convertHex)
free(data);
return 0;
}
I launch the program like ./rs232 9600 [hex string] hex debug
The scren should return a response like #BLIGHT_ON!OK, but sometimes I receive for example #BLI_ON!O
What can be the cause of this? I made some serial communcation earlier with QtSerial <-> STM32 controller and had no such issues that would cause data loss.
First thing is that, I'm receiving all the input byte by byte (so each
read() call returns exactly 1 byte..) [...]
That's not surprising. The response is coming back at 9600 baud, which is likely much slower per byte than one iteration of the loop requires. It would also arise directly from some configurations of the serial driver. It should be possible to tune this by manipulating VMIN and VTIME, but do note that that requires disabling canonical mode (which you probably want to do anyway; see below).
When using nonblocking mode without select() I'm receiving all bytes,
but I don't know when to stop receiving (and I want it to be
universal, so I send a command, expect a simple response and if there
is no more data then just exit). I made a time counter since the last
message, so if nothing was received in last ~500ms then I assume
nothing more will come. But this sometimes loses some bytes of the
response and I don't know why.
It's all in the details, which you have not presented for that case. We cannot therefore speak to your particular data losses.
Generally speaking, if you're working without flow control, then you have to be sure to read each byte before the next one arrives, on average, else pretty soon, new bytes will overwrite previously-received ones. VMIN and VTIME can help with that, or one can try other methods for tune read timing, but note well that a 9600 baud response will deliver bytes at a rate exceeding one per millisecond, so a 500 ms delay between read attempts is much too long. Supposing that the particular responses you are trying to read are relatively short, however, this will not explain the data losses.
When using blocking mode, I receive correct bytes (still byte by byte
though), but I don't know when to stop and the last call to read()
leaves the program hanging, because nothing else comes in the input.
So the command is required to be CRLF-terminated, but the response cannot be relied upon to be likewise terminated? What a rude device you're working with. If it terminated its responses the same way it required terminated commands, then you could probably work in canonical mode, and you could definitely watch for the terminator to recognize end-of-transmission.
When adding select() to the blocking call, to see if input is
readable, I get very frequent data loss (sometimes just receiving a
few bytes), and sometimes select returns 1, but read() blocks, and I'm
left hanging.
I cannot suggest what the problem may be in that case without any relevant code to analyze, but you really shouldn't need select() for this.
When I just send data without doing any reading, and look at the input
using cat -v < /dev/ttyS3 I can actually see correct input on the
serial port all the time,
That's a good test.
however when I run both cat and my program
as receivers, only one of them gets the data (or cat receives a few
bytes and my program a few),
That's exactly as I would expect. Once a program reads a byte from the port, it is no longer available for any other program to read. Thus, if multiple programs try to read from the same port at the same time then the data available will be partitioned among them in some unspecified and not necessarily consistent fashion.
this suggests me that something is
"stealing" my bytes the same way when I try to read it, but what could
it be, and why is it like that?
That seems unlikely, considering that cat is not affected the same way when you run it alone, nor (you report) are some versions of your own program.
In the first place, if the device supports flow control then I would enable it. Hardware flow control in preference to software flow control if both are viable. This is mainly a fail-safe, however -- I don't see any reason to think that flow control is likely to actually trigger if your program is well written.
Mainly, then, in addition to setting the serial line parameters (8/n/1), you should
Disable canonical mode. This is necessary because you (apparently) cannot rely on the response to be terminated by a line terminator, among other reasons.
Disable echo.
Avoid enabling non-blocking mode on the file.
(Optional) read the first response byte with VMIN == 1 and VTIME == 0; this allows for an arbitrary delay before the device starts sending the response. Alternatively, if you have a reliable upper bound on the time you're willing to wait for the device to start sending the response then you can probably skip this step by using a suitable VTIME in the next one. Or perhaps use a a larger VTIME for this first byte to accommodate a delay before start of transmission, yet not hang if the device fails to respond.
Do read the remaining response bytes with VTIME == 1 (or larger) and VMIN == 0. This probably gets you the whole remainder of the response in one call, but do repeat the read() until it returns 0 (or negative). The 0 return indicates that all available bytes have been transferred and no new ones were received for VTIME tenths of a second -- much longer than the inter-character time in a 9600-baud transmission even for VTIME == 1. Do note that the larger you make VTIME, the longer will be the delay between the device sending the last byte of its response and the program detecting end-of-transmission.
Do not implement any artificial delay between successive read attempts.
You should not need non-blocking mode at the fcntl level, and you should not need select(). There may be other termios settings you could apply to better tune your program for the particular device at the other end of the serial link, but the above should be enough for single-command / single-response pairs with ASCII-only data and no control characters other than carriage returns and newlines.

How to capture and store sensor data from Arduino into C++

I am writing a code to capture serial readings from the Arduino to C++
Is there a way to capture the readings line by line and then store it into an array? I have read another post similar to mine, but I am still unable to apply it.
Any help is greatly appreciated, thank you.
Environment setup:
Arduino UNO
ADXL 335 accelerometer
Ubuntu 16.04
C++
[Updated] applied solution from Bart
Cpp file
The reason why I added the "for-loop with print and break" is to analyze the array contents.
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <unistd.h>
using namespace std;
char serialPortFilename[] = "/dev/ttyACM0";
int main()
{
char readBuffer[1024];
FILE *serPort = fopen(serialPortFilename, "r");
if (serPort == NULL)
{
printf("ERROR");
return 0;
}
while(1)
{
usleep(1000); //sync up Linux and Arduino
memset(readBuffer, 0, 1024);
fread(readBuffer, sizeof(char),1024,serPort);
for(int i=0; i<1024; i++){
printf("%c",readBuffer[i]);
}
break;
}
return 0;
}
Ino file
Fetching data from the Accelerometer
#include <stdio.h>
const int xPin = A0;
const int yPin = A1;
const int zPin = A2;
void setup() {
Serial.begin(9600);
}
void loop() {
int x = 0, y = 0, z = 0;
x = analogRead(xPin);
y = analogRead(yPin);
z = analogRead(zPin);
char buffer[16];
int n;
n = sprintf(buffer,"<%d,%d,%d>",x,y,z);
Serial.write(buffer);
}
Results
Running the code for three times
Click Here
The ideal outputs should be
<a,b,c><a,b,c><a,b,c>...
but right now, some of the outputs has the values inside "corrupted" (please see the fourth line from the top).
Even if use the start and end markers to determine a correct dataset, the data within the set is still wrong. I suspect the issue lies with the char array from C++, due to it being unsynchronized with Arduino. Else I need to send by Bytes from Arduino (not really sure how)
When dealing with two programs running on different processors they will never start sending/receiving at the same time. What you likely see is not that the results are merged wrong it is more likely the reading program started and stopped half way through the data.
When sending data over a line it is best that you:
On the Arduino:
First frame the data.
Send the frame.
On Linux:
Read in data in a buffer.
Search the buffer for a complete frame and deframe.
1. Framing the data
With framing the data I mean that you need a structure which you can recognize and validate on the receiving side. For example you could add the characters STX and ETX as control characters around your data. When the length of your data varies it is also required to send this.
In the following example we take that the data array is never longer than 255 bytes. This means that you can store the length in a single byte. Below you see pseudo code of how a frame could look like:
STX LENGTH DATA_ARRAY ETX
The total length of the bytes which will be send are thus the length of the data plus three.
2. Sending
Next you do not use println but Serial.write(buf, len) instead.
3. Receiving
On the receiving side you have a buffer in which all data received will be appended.
4. Deframing
Next each time new data has been added search for an STX character, assume the next character is the length. Using the length +1 you should find a ETX. If so you have found a valid frame and you can use the data. Next remove it from the buffer.
for(uint32_t i = 0; i < (buffer.size() - 2); ++i)
{
if(STX == buffer[i])
{
uint8_t length = buffer[i+2];
if(buffer.size() > (i + length + 3) && (ETX == buffer[i + length + 2]))
{
// Do something with the data.
// Clear the buffer from every thing before i + length + 3
buffer.clear(0, i + length + 3);
// Break the loop as by clearing the data the current index becomes invalid.
break;
}
}
}
For an example also using a Cyclic Redundancy Check (CRC) see here

using write() function in C/C++ on Linux to write 70kB via UART Beaglebone Black

I'm trying to write a image via UART on Beaglebone Black. But when I use the write() function in library .
int write(int handle, void *buffer, int nbyte);
Regardless of the agurment nbyte has int type, I can not transfer 70kB at once. I displayed the number of byte which is transfered, and the result is the number of byte = 4111.
length = write(fd,body.c_str(),strlen(body.c_str())); //
cout<<length<<endl; // result length = 4111;
cout<<strlen(body.c_str())<<endl; // result strlen(body.c_str()) = 72255;
I hope to hear from you!
The write call does not assure that you can write the amount of data supplied, that's why it as an integer as its return, not a Boolean. The behavior you see is actually common among different operating systems, it may be due to the underline device might does not have sufficient buffer or storage for you to write 70kb. What you need is to write in a loop, each write will write the amount that is left unwritten:
int total = body.length(); // or strlen(body.c_str())
char *buffer = body.c_str();
int written = 0;
int ret;
while (written < total) {
ret = write(fd, buffer + written, total - written);
if (ret < 0) {
// error
break;
}
written += ret;
}

What should I do when write returns smaller size?

I am writing a wrapper around generic file operations and do not know how to handle the case when write returns a smaller size written then provided.
The man page for write says:
The number of bytes written may be less than count if, for example, there is insufficient space on the underlying physical medium, or the RLIMIT_FSIZE resource limit is encountered (see setrlimit(2)), or the call was interrupted by a signal handler after having written less than count bytes. (See also pipe(7).)
From my understanding of the above, it's a mixture of errors (medium full) and incitation to come back (interrupted call). If my file descriptors are all non-blocking, I should not get the interrupt case and then the only reason would be an error. Am I right ?
Code example:
int size_written = write(fd, str, count);
if (size_written == -1) {
if (errno == EAGAIN) {
// poll on fd and come back later
} else {
// throw an error
}
} else if (size_written < count) {
// ***************
// what should I do here ?
// throw an error ?
// ***************
}
You need to use the raw I/O functions in a loop:
ssize_t todo = count;
for (ssize_t n; todo > 0; )
{
n = write(fd, str, todo);
if (n == -1 && errno != EINTR)
{
// error
break;
}
str += n;
todo -= n;
}
if (todo != 0) { /* error */ }
The special condition concerning EINTR allows the write call to be interrupted by a signal without causing the entire operation to fail. Otherwise, we expect to be able to write all data eventually.
If you can't finish writing all data because your file descriptor is non-blocking and cannot accept any data at the moment, you have to store the remaining data and try again later when the file descriptor has signalled that it's ready for writing again.

What is happing to the parent process?

The task here is :This is basic C program which must contains two processes.First one writes in a file 60 random numbers.The seconds that needs to be written has to read the file and write only the even number into another new file.So I have this code and the parent process that needs to write the even numbers to a file its not working properly.Also my question is about the structure of the code.Is this the best way to describe the processes.
#include <stdio.h>
#include <fcntl.h>
int main() {
int pid, fd, buf, i, fd2, buf2;
pid = fork();
switch (pid) {
case 0: {
printf("\n Child process is writing numbers. \n");
fd = open("file.dat", O_RDWR | O_CREAT, 0644);
for (i = 0; i < 60; i++) {
buf = rand() % 20 - 10;
printf("\n Writing number %d %d ", i + 1, buf);
write(fd, &buf, sizeof(buf));
}
close(fd);
break;
}
case -1: {
printf("\n Fork error! \n");
}
default: {
wait(pid);
printf("\n Parent process is Copying numbers. \n");
fd = open("file.dat", O_RDONLY);
fd2 = open("file_output.dat", O_RDWR | O_CREAT, 0644);
while (read(fd, &buf, sizeof(buf)) == sizeof(buf)) {
if (buf & ~1)
(buf2 = buf);
write(fd2, &buf2, sizeof(buf2));
printf("Writing number in file_output.dat %d \n", buf2);
}
close(fd2);
close(fd);
}
}
return 0;
}
Your condition is wrong:
if (buf &~ 1) (buf2=buf);
write(fd2, &buf2, sizeof(buf2));
You write the number out regardless, but if it's odd (excepting that you need to fix the even/odd test) then you don't update buf2. If the first number is odd, buf2 is uninitialised.
You need this:
if (buf % 2 != 0) continue;
write(fd2, &buf, sizeof(buf));
printf("Writing number in file_output.dat %d \n", buf);
Note that this might not correctly test negative even numbers. You could test abs(buf) % 2 != 0. The only number that might not be handled would be INT_MIN.
Your test for even numbers is wrong. You exclude bit 1, so any number greater than one will be selected. You effectively test for
if (buf & 0xfffffffe) ...
but, you should test instead for
if (!(buf & 1)) ...
or even
if ((buf % 2) == 0) ...
well, there are a couple of bugs in the code.
it looks like you want to achieve this: the child writes the random numbers and the parent waits for the child to terminate, reads the random numbers and only writes the even ones out to the second file.
however, you have used wait(), which is the wrong function to use. instead use waitpid() like this:
waitpid(pid, NULL, 0);
the other bug is in your code that writes only even numbers. I would write it like this:
if (!(buf & 1))
write(fd2, &buf1, sizeof(buf1));
that way, you dont need buf2 at all. in fact, in your current code, if the number is not even, it will still write a number to the file! It will just write the last even number that it read again.
Why are you using non-portable open/read/write when you could be using portable fopen/fread/fwrite? That makes no sense to me.
Let us suppose your sizeof int == 4. Suppose your first process writes 4 bytes, which your second process reads as two chunks of 2 bytes for whatever reason, thus read returns 2 here in your loop: while (read(fd, &buf, sizeof(buf)) == sizeof(buf)) Is 2 == sizeof (buf)? No. Perhaps your loop is terminating on the wrong condition...
fread solves this problem with two parameters: One for the size of the item and one for the number of items you want. for (size_t x = fread(&buf, sizeof buf, 1, file_ptr); x > 0; x = fread(&buf, sizeof buf, 1, file_ptr)) { /* ... */ }... "Upon successful completion, fread() shall return the number of elements successfully read which is less than nitems only if a read error or end-of-file is encountered." This guarantee doesn't exist for read. You might need to use x to determine how many items to write, since that tells you how many items were read.