C# BufferedStream - Is it possible to know advancement? [duplicate] - c#

This question already has answers here:
How to use WinForms progress bar?
(4 answers)
Closed 4 years ago.
I'm writing a simple checksum generator app using C# for large files. It's working quite fine, but users would like to see some sort of progress bar since the app freezes for a a few dozen seconds.
Here is a a sample of code I use (BufferedStream increased a lot the app performance):
private static string GetSHA5(string file)
{
using (var stream = new BufferedStream(File.OpenRead(file), 1200000))
{
var sha5 = new SHA512Managed();
byte[] checksum_sha5 = sha5.ComputeHash(stream);
return BitConverter.ToString(checksum_sha5).Replace("-", String.Empty);
}
}
My question is, is it possible to get the buffer "progress" ? Because I guess internally it operates some sort of division and looping.

I tried implementing jdweng solution but I had trouble accessing threads to update my progress bar with the position variable. In the end I rewrote my code using background_worker and a custom buffer. Here is a sample of a it.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
dynamic data = e.Argument;
string fPath = data["file"];
byte[] buffer;
int bytesRead;
long size;
long totalBytesRead = 0;
using (Stream file = File.OpenRead(fPath))
{
size = file.Length;
progressBar1.Visible = true;
HashAlgorithm hasher = MD5.Create();
do
{
buffer = new byte[4096];
bytesRead = file.Read(buffer, 0, buffer.Length);
totalBytesRead += bytesRead;
hasher.TransformBlock(buffer, 0, bytesRead, null, 0);
backgroundWorker1.ReportProgress((int)((double)totalBytesRead / size * 100));
}
while ( bytesRead != 0) ;
hasher.TransformFinalBlock(buffer, 0, 0);
e.Result = MakeHashString(hasher.Hash);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void md5HashBtn_Click(object sender, EventArgs e)
{
if (MD5TextBox.Text.Length > 0)
{
Dictionary<string, string> param = new Dictionary<string, string>();
param.Add("algo", "MD5");
param.Add("file", MD5TextBox.Text);
backgroundWorker1.RunWorkerAsync(param);
}
}

Related

Reading Real time data from serial port

I am trying to read real time data from an accelerometer. Sampling frequency of the accelerometer is 2650Hz. I am getting proper data from serial port, but unable to match with the sampling frequency. The sampling frequency is varying from 2100Hz to 2400Hz and it is not stable. I am using a stop watch for timing reference.
Here is my code for receiving serial data.
private void toolStripButton12_Click(object sender, EventArgs e)
{
serialPort1.PortName = comboBox1.Text;
serialPort1.BaudRate = Convert.ToInt32(115200);
if (!serialPort1.IsOpen)
{
serialPort1.Open();
}
sw.Start();
serialPort1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(SerialPort1_DataReceived);
}
}
private void SerialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
{
byteCount = serialPort1.BytesToRead;
if (byteCount > 4000)
byteCount = 4000;
if (e.EventType == SerialData.Eof)
return;
byte[] buffer = new byte[byteCount];
int readBytes = serialPort1.Read(buffer, 0, buffer.Length);
// FIFO Implementation
buffer.ToList().ForEach(b => newrecievedData1.Enqueue(b));
if (newrecievedData1.Count < 4000) return;
processdata3();
int i = 0;
{
while (i <= packet3.Length-4)
{
while (packet3[i++] != 69) ;
data = packet3[i++];
a = data << 8;
b = a + packet3[i++];
c = b << 8;
d = c + packet3[i++];
Port1data.Add(d);
countbyte[0]++;
tick = (double)countbyte[0];
}
}
t = (double)sw.Elapsed.TotalSeconds;
Sampling frequency = tick / t;
}
try
{
this.Invoke(new EventHandler(DisplayText));
}
catch
{
}
}
Int32[] packet3;
private Int32[] processdata3()
{
if (newrecievedData1.Count >= 4000)
{
packet3 = Enumerable.Range(0, 4000).Select(h => newrecievedData1.Dequeue()).ToArray();
}
return packet3;
}
I want to exactly get 2650 Hz sampling frequency all the time.Any help is highly appreciated.
That is 0.337 ms per sample. That is really pushing the upper limits of how much code can be done per sample.
Without some major optimization to your algorithms (possibly using custom collections designed specifically for your workload) I don't think your requirements are reachable using managed code.

c# SerialPort - not reading all data send

I have recently created an app that is recieving data send through UART from my STM device. The data I try to send is an array of floats - float[512].
The data is send as pure binary - not string. Generally idea is 4xbyte = float.
I am sure that data that I'm sending is good. Checked it with program called "Terminal" and then IEEE 754 Converter.
My program
In program I choose COMport from available and other parameters.
(by default they are my COM adapter parameters). I open connection and then when by STM finishes sending data I'm clicking on "Odebrane" button so it will display data in TextBox
The problem is that I'm not recieving all data.
And I have no idea what is the cause.
private List<float> lista = new List<float>();
private float[] fDane = new float[1024];
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
getAvailablePorts();
tb_Rx.Text = "Wprowadź parametry transmisji";
cb_BaudRate.Text = "9600";
cb_DataBits.Text = "8";
cb_Handshake.Text = "None";
cb_Parity.Text = "None";
cb_StopBits.Text = "One";
}
private void SerialP_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
int size = sp.BytesToRead;
byte[] bArray = new byte[size];
float[] fArray = new float[size / 4];
sp.Read(bArray, 0, size);
for (uint i = 0; i < size / 4; i++)
{
fArray[i] = ByteToFloat(bArray, i); // change 4 bytes to float
lista.Add(fArray[i]);
}
}
private float ByteToFloat(byte[] input, UInt32 i)
{
byte[] Array = new[] { input[4 * i], input[4 * i + 1], input[4 * i + 2], input[4 * i + 3] };
return BitConverter.ToSingle(Array, 0);
}
private void bt_transform_Click(object sender, EventArgs e)
{
float[] fDane = lista.ToArray();
for(UInt32 i = 0; i < fDane.Length; i++)
{
RxPisz(fDane[i]);
}
}
------------------------- SOLVED ---------------------
changed dataRecieved interrupt handler
private void SerialP_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
byte[] bArray = new byte[4*numberOfSamples];
float[] fArray = new float[numberOfSamples];
int previouse = counterOfRecBytes;
counterOfRecBytes += sp.BytesToRead;
sp.Read(bArray, previouse, (counterOfRecBytes - previouse));
if (counterOfRecBytes == 4*numberOfSamples)
{
for (uint i = 0; i < numberOfSamples; i++)
{
fArray[i] = ByteToFloat(bArray, i); // change 4 bytes to float
lista.Add(fArray[i]);
}
counterOfRecBytes = 0;
}
}
I think you are not reading data at the good speed, try to change BaudRate from 9600 to 19200.

How to get array data in correct sequence from serial port in C#

My purpose in below program is getting 16 bytes of data from microcontroller and processing data for appropriate instructions. There are a lot of related questions and answers here but I couldnt find anything about in below issue. I can get 16 bytes from MCU. Values of bytes are correct and I can see them in dataGridView but the sequence of bytes is changing . For example at first MCUData[0] = 0x01 , MCUData[1] = 0xFE , MCUData[2] = 0xCC then it changes to MCUData[0] = 0xFE , MCUData[1] = 0xCC , MCUData[2] = 0x01. İt is like some problem shifting my datas in byte array. I am sure my MCU is sending data correctly because I checked in one of serial terminal program. My code is in below
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;
namespace SerialCommunicationMCU
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
dataGridView1.Columns.Add("MCUData", "Byte Name");
dataGridView1.Columns.Add("MCUData", "Byte Value");
}
public System.IO.Ports.SerialPort SerialPc;
#region Variables
public string AvailablePort;
public string[] Ports = SerialPort.GetPortNames();
byte[] MCUData = new byte[16];
#endregion
private void Connect_Click(object sender, EventArgs e)
{
DataGreedByteNameShow();
SerialConnectandRead();
ConnectButton.Enabled = false;
DisconnectButton.Enabled = true;
}
private void Disconnect_Click(object sender, EventArgs e)
{
SerialPc.Close();
ConnectButton.Enabled = true;
DisconnectButton.Enabled = false;
}
public void SerialConnectandRead()
{
SerialPc = new SerialPort(AvailablePort, 115200, Parity.None, 8, StopBits.One);
try
{
SerialPc.Open();
SerialPc.DataReceived += new SerialDataReceivedEventHandler(SerialPc_DataReceived);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "Serial Port Error");
}
}
private void SerialPc_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPc.DiscardNull = false;
SerialPc.Read(MCUData, 0, 16);
SerialPc.ReceivedBytesThreshold = 16;
DataGreedByteValueShow();
}
private void Form1_Load(object sender, EventArgs e)
{
foreach (string port in Ports)
{
comboBox1.Items.Add(port);
}
DisconnectButton.Enabled = false;
}
public void DataGreedByteNameShow()
{
dataGridView1.Rows.Add("MCUData[0]");
dataGridView1.Rows.Add("MCUData[1]");
dataGridView1.Rows.Add("MCUData[2]");
dataGridView1.Rows.Add("MCUData[3]");
dataGridView1.Rows.Add("MCUData[4]");
dataGridView1.Rows.Add("MCUData[5]");
dataGridView1.Rows.Add("MCUData[6]");
dataGridView1.Rows.Add("MCUData[7]");
dataGridView1.Rows.Add("MCUData[8]");
dataGridView1.Rows.Add("MCUData[9]");
dataGridView1.Rows.Add("MCUData[10]");
dataGridView1.Rows.Add("MCUData[11]");
dataGridView1.Rows.Add("MCUData[12]");
dataGridView1.Rows.Add("MCUData[13]");
dataGridView1.Rows.Add("MCUData[14]");
dataGridView1.Rows.Add("MCUData[15]");
}
private void DataGreedByteValueShow()
{
dataGridView1.Rows[0].Cells[1].Value = MCUData[0];
dataGridView1.Rows[1].Cells[1].Value = MCUData[1];
dataGridView1.Rows[2].Cells[1].Value = MCUData[2];
dataGridView1.Rows[3].Cells[1].Value = MCUData[3];
dataGridView1.Rows[4].Cells[1].Value = MCUData[4];
dataGridView1.Rows[5].Cells[1].Value = MCUData[5];
dataGridView1.Rows[6].Cells[1].Value = MCUData[6];
dataGridView1.Rows[7].Cells[1].Value = MCUData[7];
dataGridView1.Rows[8].Cells[1].Value = MCUData[8];
dataGridView1.Rows[9].Cells[1].Value = MCUData[9];
dataGridView1.Rows[10].Cells[1].Value = MCUData[10];
dataGridView1.Rows[11].Cells[1].Value = MCUData[11];
dataGridView1.Rows[12].Cells[1].Value = MCUData[12];
dataGridView1.Rows[13].Cells[1].Value = MCUData[13];
dataGridView1.Rows[14].Cells[1].Value = MCUData[14];
dataGridView1.Rows[15].Cells[1].Value = MCUData[15];
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
AvailablePort = comboBox1.SelectedItem.ToString();
}
}
}
According to the MSDN documentation: http://msdn.microsoft.com/en-us/library/ms143549(v=vs.110).aspx
Reads a number of bytes from the SerialPort input buffer and writes
those bytes into a byte array at the specified offset.
public int Read(
byte[] buffer,
int offset,
int count
)
Here's how your current code uses the function:
SerialPc.Read(MCUData, 0, 16);
Your buffer is a global variable defined as:
byte[] MCUData = new byte[16];
This is one way that you can use to solve your problem:
List<byte> MCUDataOverTime = new List<byte>();
private void SerialPc_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPc.DiscardNull = false;
SerialPc.Read(MCUData, 0, 16);
MCUDataOverTime.AddRange(MCUData);
SerialPc.ReceivedBytesThreshold = 16;
DataGreedByteValueShow();
}
private void DataGreedByteValueShow()
{
if (MCUDataOverTime.Count >= 16)
{
dataGridView1.Rows[0].Cells[1].Value = MCUDataOverTime[0];
dataGridView1.Rows[1].Cells[1].Value = MCUDataOverTime[1];
dataGridView1.Rows[2].Cells[1].Value = MCUDataOverTime[2];
dataGridView1.Rows[3].Cells[1].Value = MCUDataOverTime[3];
dataGridView1.Rows[4].Cells[1].Value = MCUDataOverTime[4];
dataGridView1.Rows[5].Cells[1].Value = MCUDataOverTime[5];
dataGridView1.Rows[6].Cells[1].Value = MCUDataOverTime[6];
dataGridView1.Rows[7].Cells[1].Value = MCUDataOverTime[7];
dataGridView1.Rows[8].Cells[1].Value = MCUDataOverTime[8];
dataGridView1.Rows[9].Cells[1].Value = MCUDataOverTime[9];
dataGridView1.Rows[10].Cells[1].Value = MCUDataOverTime[10];
dataGridView1.Rows[11].Cells[1].Value = MCUDataOverTime[11];
dataGridView1.Rows[12].Cells[1].Value = MCUDataOverTime[12];
dataGridView1.Rows[13].Cells[1].Value = MCUDataOverTime[13];
dataGridView1.Rows[14].Cells[1].Value = MCUDataOverTime[14];
dataGridView1.Rows[15].Cells[1].Value = MCUDataOverTime[15];
}
}
The code above would solve the problem where your values change over time. It's happening in the first place because every time SerialPc_DataReceived() is called, you've set it so that every "Read" will store its result in your MCUData byte array. Consequently, it will end up overwriting your MCUData array since you've hardcoded the start-from offset to 0.
That's why it looks like your data is being shifted. You have to understand, fundamentally, these streams don't conveniently store data for you in the way where an index of 0 means the beginning of time (when the stream first opened and started). If you want it to work that way, you'll have to store it yourself.
Please note that the code above does not attempt to empty the serial input buffer. You're pulling 16 bytes every time you do a read when the DataReceived is called, but I wonder if the MCU is sending a lot more than that with every DataReceived message.
Edit:
List<byte> MCUDataOverTime = new List<byte>();
private void SerialPc_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPc.DiscardNull = false;
int readcount = 0;
byte [] temp;
do
{
readcount = SerialPc.Read(MCUData, 0, 16);
if (readcount > 0)
{
temp = new byte[readcount];
Array.Copy(MCUData, 0, temp, 0, readcount);
MCUDataOverTime.AddRange(temp);
SerialPc.ReceivedBytesThreshold = 16;
DataGreedByteValueShow();
}
} while (readcount > 0);
}
private void DataGreedByteValueShow()
{
if (MCUDataOverTime.Count >= 16)
{
dataGridView1.Rows[0].Cells[1].Value = MCUDataOverTime[0];
dataGridView1.Rows[1].Cells[1].Value = MCUDataOverTime[1];
dataGridView1.Rows[2].Cells[1].Value = MCUDataOverTime[2];
dataGridView1.Rows[3].Cells[1].Value = MCUDataOverTime[3];
dataGridView1.Rows[4].Cells[1].Value = MCUDataOverTime[4];
dataGridView1.Rows[5].Cells[1].Value = MCUDataOverTime[5];
dataGridView1.Rows[6].Cells[1].Value = MCUDataOverTime[6];
dataGridView1.Rows[7].Cells[1].Value = MCUDataOverTime[7];
dataGridView1.Rows[8].Cells[1].Value = MCUDataOverTime[8];
dataGridView1.Rows[9].Cells[1].Value = MCUDataOverTime[9];
dataGridView1.Rows[10].Cells[1].Value = MCUDataOverTime[10];
dataGridView1.Rows[11].Cells[1].Value = MCUDataOverTime[11];
dataGridView1.Rows[12].Cells[1].Value = MCUDataOverTime[12];
dataGridView1.Rows[13].Cells[1].Value = MCUDataOverTime[13];
dataGridView1.Rows[14].Cells[1].Value = MCUDataOverTime[14];
dataGridView1.Rows[15].Cells[1].Value = MCUDataOverTime[15];
}
}
I'd increase the size of MCUData to 1024 bytes or more since 16 bytes seems kind of small. The next thing that you'd need to do is keep track of the start and end points of each frame. I'm guessing that it'll always be 16 in the case of your microcontroller but in general you should have the microcontroller throwing out null values or a special string of symbols to indicate the end of a message.

C#, Calculating Download Speed, Where's The Problem?

I want to calculate download speed as kbps (kb per second). There's a problem in the code, it doesn't show actual speed. And I'm really tired of this work. Also, when using (TotalDownloadSize / ElapsedTime) formula it shows more realistic results but you know it will get average value and it will be stupidity.
It usually gives 4000 and it's basicly because of chunk 4096 when I set it to 128 that time I get 50/100/125 values.
DateTime dlElapsed;
private delegate void UpdateProgessCallback(Int64 BytesRead, Int64 TotalBytes, Int32 CurrentBytes);
private void UpdateProgress(Int64 BytesRead, Int64 TotalBytes, Int32 CurrentBytes)
{
DateTime Elapsed = DateTime.Now;
var progress = Convert.ToInt32((BytesRead * 100) / TotalBytes);
var elaps = (Elapsed - dlElapsed).TotalSeconds;
long kbps;
if (elaps > 0)
{
kbps = Convert.ToInt64((CurrentBytes / elaps) / 1024);
updateLabelText(String.Format("Downloading ({0} kbps)...", kbps));
}
// Make progress on the progress bar
if (progress < progressBar1.Maximum)
{
progressBar1.Value = progress;
}
else
{
progressBar1.Value = progressBar1.Maximum;
}
dlElapsed = DateTime.Now;
}
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
// Some stuff here...
int byteSize = 0;
byte[] downBuffer = new byte[4096];
FileStream strLocal= new FileStream(path, FileMode.Create, FileAccess.Write);
dlElapsed = DateTime.Now;
while ((byteSize = stream.Read(downBuffer, 0, downBuffer.Length)) > 0)
{
strLocal.Write(downBuffer, 0, byteSize);
this.Invoke(new UpdateProgessCallback(this.UpdateProgress),
new object[] { strLocal.Length, totalbyte, byteSize});
}
updateLabelText("Download complete!");
strLocal.Close();
}
}
So where's the problem?
So where's the problem?
You're coarsely sampling something that varies a lot.
Consider buffering measurements and averaging the last 5 or so. Look for implementations of a "running average".
Well, my first comment would be that your code is not thread safe, so when you set dlElapsed = DateTime.Now;, it's not the same dlElapsed value that UpdateProgress is going to be checking.

Creating a Huge Dummy File in a Matter of Seconds in C#

I want to create a huge dummy file say 1~2 GBs in matter of seconds.
here is what I've written in C#:
file.writeallbytes("filename",new byte[a huge number]);
and another way with indicating the status, was like following:
long FSS = din.TotalFreeSpace;
long segments = FSS / 10000;
long last_seg = FSS % 10000;
BinaryWriter br = new BinaryWriter(fs);
for (long i = 0; i < segments; i++)
{
br.Write(new byte[10000]);
this.label2.Text = "segments write :" + i.ToString() + "\r\n" + "segments remain :" + ((segments-i)+1).ToString();
Application.DoEvents();
}
br.Write(new byte[last_seg]);
this.label2.Text += "\r\nDone!";
br.Close();
where din is Disk Information object
well with these two approach it takes something like 2 or more minutes to write such a big but dummy file. Is there any other faster way for doing so?
Simply create the file, seek to a suitably large offset, and write a single byte:
FileStream fs = new FileStream(#"c:\tmp\huge_dummy_file", FileMode.CreateNew);
fs.Seek(2048L * 1024 * 1024, SeekOrigin.Begin);
fs.WriteByte(0);
fs.Close();
This will yield a 2GB file with basically unpredictable contents, which should be fine for your purposes.
If you don't care about the contents, then by far the fastest way I know of is this - it is practically instant:
private void CreateDummyFile(string fileName, long length)
{
using (var fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
{
fileStream.SetLength(length);
}
}
If you just need a FileStream, you could use FileStream.SetLength. That will get you a stream which is 2 GB long. Then you can write the final byte at an arbitrary position of your choice. But the contents will be undefined.
If you're trying to actually create a file on the disk, yes, you'll need to actually write its contents. And yes, hard disks are going to be slow; something like a 1 GB/min write speed isn't totally ridiculous. Sorry -- that's physics!
Why did you not use the BackgroundWorker class to achieve this, as you can pass anything into the method ReportProgress to indicate the status report. See the example below:
private BackgroundWorker bgWorker;
public Form1()
{
InitializeComponent();
bgWorker = new BackgroundWorker();
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.ProgressChanged += new ProgressChangedEventHandler(bgWorker_ProgressChanged);
bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
bgWorker.RunWorkerAsync();
}
void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.label2.Text = "Done";
}
void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
MyStatus myProgressStatus = (MyStatus)e.UserState;
this.label2.Text = string.Format("segments write : {0}" + Environment.Newline + "Segments Remain: {1}", myProgressStatus.iWritten, myProgressStatus.iRemaining);
}
void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
long FSS = din.TotalFreeSpace;
long segments = FSS / 10000;
long last_seg = FSS % 10000;
BinaryWriter br = new BinaryWriter(fs);
for (long i = 0; i < segments; i++)
{
br.Write(new byte[10000]);
bgWorker.ReportProgress(i.ToString(), new MyStatus(i, ((segments-i) + 1)));
}
br.Write(new byte[last_seg]);
br.Close();
}
public class MyStatus{
public int iWritten;
public int iRemaining;
public MyStatus(int iWrit, int iRem){
this.iWritten = iWrit;
this.iRemaining = iRem;
}
}
}
This is a rough draft...
I could be wrong but you will probably find that it's impossible to create a file that large that quickly as there will be a bottleneck in the I/O writing process.
However in your code above the Applciation.DoEvents will be slowing things down. Also any repainting of the screenthis.label2.Text = will cause a slight slow down.

Categories