2

I am trying to find a good method to write data from a NetworkStream (via C#) to a text file while "quasi-simultaneously" reading the newly written data from the text file into Matlab.

Basically, is there a good method or technique for coordinating write/read operations (from separate programs) such that a read operation does not block a write operation (and vice-versa) and the lag between successive write/reads is minimized?

Currently I am just writing (appending) data from the network stream to a text file via a WriteLine loop, and reading the data by looping Matlab's fscanf function which also marks the last element read and repositions the file-pointer to that spot.

Relevant portions of C# code:

(Note: The loop conditions I'm using are arbitrary, I'm just trying to see what works right now.)

NetworkStream network_stream = tcp_client.GetStream();

string path = @"C:\Matlab\serial_data.txt";
            FileInfo file_info = new FileInfo(path);
            using (StreamWriter writer = file_info.CreateText())
            {
                string foo = "";
                writer.WriteLine(foo);
            }

            using (StreamWriter writer = File.AppendText(path))
            {
                byte[] buffer = new byte[1];
                int maxlines = 100000;
                int lines = 0;
                while (lines <= maxlines)
                {
                    network_stream.Read(buffer, 0, buffer.Length);
                    byte byte2string = buffer[0];
                    writer.WriteLine(byte2string);
                    lines++;
                }
            }

Relevant Matlab Code:

i=0;

while i<100;
    a = fopen('serial_data.txt');
    b = fscanf(a, '%g', [1000 1]);
    fclose(a);
    i=i+1;
end

When I look at the data read into Matlab there are large stretches of zeros in between the actual data, and the most disconcerting part is that number of consecutive data-points read between these "false zero" stretches varies drastically.

I was thinking about trying to insert some delays (Thread.sleep and wait(timerObject)) into C# and Matlab, respectively, but even then, I don't feel confident that will guarantee I always obtain the data received over the network stream, which is imperative.

Any advice/suggestions would be greatly appreciated.

Fred
  • 23
  • 3

1 Answers1

1

Looks like there's an issue with how fscanf is being used in the reader on the Matlab side.

The reader code looks like it's going to reread the entire file each time through the loop, because it's re-opening it on each pass through the loop. Is this intentional? If you want to track the end of a file, you probably want to keep the file handle open, and just keep checking to see if you can read further data from it with repeated fscanf calls on the same open filehandle.

Also, that fscanf call looks like it might always return a zero-padded 1000-element array, regardless of how large the file it read was. Maybe that's where your "false zeros" are coming from. How many there are would vary with how much data is actually in the file and how often the Matlab code read it between writes. Grab the second argout of fscanf to see how many elements it actually read.

[b,nRead] = fscanf(a, '%g', [1000 1]);
fprintf('Read %d numbers\n', nRead);
b = b(1:nRead);

Check the doc page for fscanf. In the "Output Arguments" section: "If the input contains fewer than sizeA elements, MATLAB® pads A with zeros."

And then you may want to look at this question: How can I do an atomic write/append in C#, or how do I get files opened with the FILE_APPEND_DATA flag?. Keeping the writes shorter than the output stream's buffer (like they are now) will make them atomic, and flushing after each write will make them visible to the reader in a timely manner.

Community
  • 1
  • 1
Andrew Janke
  • 23,508
  • 5
  • 56
  • 85
  • There are no implicit conversion to the char type, but there is one from byte to int. Not tested but by that logic you'd get the WriteLine(Int32) call. – Voo Apr 22 '13 at 04:23
  • Okay, that means it's not a conversion issue like I thought in the first paragraph; maybe just an `fscanf` usage issue. – Andrew Janke Apr 22 '13 at 05:09
  • @Voo: removed the stuff about byte -> int32 vs char conversion. – Andrew Janke Apr 22 '13 at 05:12
  • @Andrew: Thanks for the input - It turned out that StreamWritter.WriteLine() implicitly converts the byte type to string. I was anticipating having to to do an explicit conversion, hence the misnomer byte2string variable (sorry for any confusion). As far as I can tell, the C# code writes the text file out with no problem. It's an ECG waveform and if I subsequently plot the text file data via Matlab I can tell there are gaps/improper conversions. – Fred Apr 22 '13 at 05:24
  • Okay. Yeah, that sounds like maybe `fscanf` zero-padding and re-reading the whole file each time. I edited my answer a bit to expand on that; have another look. – Andrew Janke Apr 22 '13 at 05:26
  • @Andrew: It was intentional, but I don't want to reread data - I was hoping there was some provision in fscanf such that it would remember it's "mark" if I closed it with each read iteration. The reason for closing it was because I assumed that if I left it open, the C# code would be unable to access the file to write new data in. Checking now to see whether it blocks write access/scans for new data if I only initialize fopen it once... – Fred Apr 22 '13 at 05:40
  • The POSIX-style `fopen` file I/O in Matlab will not block other file I/O on it. (In many cases this is a problem, because you must synchronize shared file access through other means.) The only question is whether it interferes with the C# code's win32 style atomic appends. Just leave it open and try it out; I think the C# code will error out if it can't get the access it needs. `fscanf` uses the file pointer; it loses its place across fcloses. If you need to reopen, use `ftell` before `fclose` and then `fseek` after `fopen` to get back to the same place in the file. – Andrew Janke Apr 22 '13 at 05:45
  • So it worked? I'm curious to know whether the close-and-reopen was necessary, or if sitting on the open read file handle played well with appending from C#. – Andrew Janke Apr 22 '13 at 20:28