c# charting - access from other thread exception - c#

Today I was trying to chart data from project where I recieve it by transmitting from my uC via UART.
The program is just to recieve data in interrupt, process it and display.
When program goes to my ChartData method I'm getting an exception:
An unhandled exception of type 'System.InvalidOperationException'
occurred in System.Windows.Forms.dll
Additional information: Cross-thread operation not valid: Control
'chart1' accessed from a thread other than the thread it was created
on.
It is strange for me - I think that I've got one thread in my program.
I have tried charting with pure project where I create data in Form_Load event - everything works fine.
My source code is here - any idea?
public partial class Form1 : Form
{
private List<float> freqList = new List<float>();
private List<float> magList = new List<float>();
private float[] fFrequency = new float[numberOfSamples];
private float[] fMagnitude = new float[numberOfSamples];
public static int counterOfRecBytes = 0;
public static int numberOfSamples = 512;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
GetAvailablePorts();
tb_Rx.Text = "Wprowadź parametry transmisji";
// Default transmission parameters for testing
cb_BaudRate.Text = "9600";
cb_Ports.Text = "COM5";
}
/// <summary>
/// Checks avaiable serialports - for linking my device to other computers
/// </summary>
void GetAvailablePorts()
{
string[] ports = SerialPort.GetPortNames();
cb_Ports.Items.AddRange(ports);
//cb_Ports.SelectedIndex = 0;
}
private void bt_connect_Click(object sender, EventArgs e)
{
if (bt_connect.Text == "Connect")
{
if (cb_Ports.Text == "" || cb_BaudRate.Text == "" || cb_Ports.Text == "")
tb_Rx.Text = "Nie wybrano jednego z parametrów";
else
{
bt_connect.Text = "Disconnect";
SerialP.PortName = cb_Ports.Text;
SerialP.BaudRate = Convert.ToInt32(cb_BaudRate.Text);
SerialP.StopBits = StopBits.One;
SerialP.DataBits = 8;
SerialP.Parity = Parity.None;
SerialP.Handshake = Handshake.None;
SerialP.ReadBufferSize = 8 * numberOfSamples + 4;
SerialP.Open();
pb_connect.Value = 100;
tb_Rx.Clear();
cb_Ports.Enabled = false;
cb_BaudRate.Enabled = false;
}
}
else
{
SerialP.Close();
bt_connect.Text = "Connect";
pb_connect.Value = 0;
bt_connect.Enabled = true;
cb_BaudRate.Enabled = true;
cb_Ports.Enabled = true;
freqList.Clear();
magList.Clear();
pb_connect.Value = 0;
tb_Rx.AppendText(Environment.NewLine + "----Zamknięto połączenie----");
}
}
private void SerialP_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
byte[] bArray = new byte[8 * numberOfSamples+4]; // frequencies (512 samples) and magnitudes (512 values) - each is a single precision float so 4 bytes
float[] fArray = new float[2*numberOfSamples];
//bool dataStart = false;
int previouse = counterOfRecBytes;
counterOfRecBytes += sp.BytesToRead;
sp.Read(bArray, previouse, (counterOfRecBytes - previouse));
if (counterOfRecBytes == 8*numberOfSamples+4)
{
for(uint i = 1; i < 2*numberOfSamples+1; i++)
{
// if (freqList.Count == numberOfSamples) freqList.Clear();
// if (magList.Count == numberOfSamples) magList.Clear();
fArray[i-1] = ByteToFloat(bArray, i);
if (i < numberOfSamples+1) freqList.Add(fArray[i]);
else magList.Add(fArray[i-1]);
}
//ListToArray();
ChartData();
// if (counterOfRecBytes >= 8 * numberOfSamples + 4) sp.DiscardInBuffer();
counterOfRecBytes = 0;
}
}
/// <summary>
/// Changes my recieved bArray to single precision floats
/// </summary>
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);
}
/// <summary>
/// Setting chart data
/// </summary>
private void ChartData()
{
for (int i = 0; i < numberOfSamples; i++)
{
chart1.Series["Widmo"].Points.AddXY(freqList[i], magList[i]);
}
}
}
Problem solved - what I had to do was putting code below in place of just calling ChartData method:
if (InvokeRequired)
{
Invoke(new MethodInvoker(ChartData));
}
else
{
ChartData();
}

Related

How to make RFID reader seek for card continously

For a while now, i have been trying to make my RFID reader to automatically read (scan) a card. Although the former code i had, makes the reader scan the card when a button is pressed. But i want to be able to scan cards automatically and continuously, once any card is placed in the field. Here is what i tried. but i was not able to display the scanned UID in a textbox. Please, your help will be appreciated.
class CardReader : IDisposable
{
IntPtr _pSnr = Marshal.AllocHGlobal(1024);
private Thread _t;
private Action<string> _callback;
private volatile bool _stop;
public void ReadCard()
{
short icdev = 0x0000;
int status;
byte type = (byte)'A';//mifare one type is A
byte mode = 0x26; // Request the card which is not halted.
ushort TagType = 0;
byte bcnt = 0x04;//mifare hold on 4
IntPtr pSnr;
byte len = 255;
sbyte size = 0;
pSnr = Marshal.AllocHGlobal(1024);
for (int i = 0; i < 2; i++)
{
status = rf_request(icdev, mode, ref TagType);//request card
if (status != 0)
continue;
status = rf_anticoll(icdev, bcnt, pSnr, ref len);//i did anticol--get the card sn
if (status != 0)
continue;
status = rf_select(icdev, pSnr, len, ref size);//select one card
if (status != 0)
continue;
byte[] szBytes = new byte[len];
for (int j = 0; j < len; j++)
{
szBytes[j] = Marshal.ReadByte(pSnr, j);
}
String m_cardNo = String.Empty;
for (int q = 0; q < len; q++)
{
m_cardNo += byteHEX(szBytes[q]);
}
_callback(m_cardNo);
// txtSearchPurse.Text = m_cardNo;
break;
}
}
public void Work()
{
while (!_stop)
{
ReadCard();
Thread.Sleep(1000);
}
}
public void Start(Action<string> cardRead)
{
if (_t != null)
return;
_stop = false;
_callback = cardRead;
_t = new Thread(Work);
_t.Start();
}
public void Stop()
{
if (_t != null)
{
_stop = true;
_t.Join();
_t = null;
}
}
public void Dispose()
{
Marshal.FreeHGlobal(_pSnr);
}
}
And i tried to use it like this:
private void btnRequest_Click(object sender, EventArgs e)
{
var reader = new CardReader();
reader.Start(CardReaded);
}
private void CardReaded(string cardnr){
//txtSearchPurse.Text = _callback;
}
Here is what i tried. but i was not able to display the scanned UID in a textbox.
So your callback successfully fired? What error, if any, did you get? I'm guessing you got a "cross thread exception". If so, fix it like this:
private void CardReaded(string cardnr)
{
this.Invoke((MethodInvoker)delegate
{
txtSearchPurse.Text = cardnr;
});
}

Noise artifacts during Naudio stream

I'm having some outputting my data as audio stream.
I've uploaded a sound snipped of a sine wave with the undesirable noise effect.
I think the issue is related that there is a small pause between data packages and somehow this produces this low, fairly regular pop sound
The AudioMonitor class gets its data via SetBuffer, each 250ms aprrox. ~ 6000 samples.
Where does the noise originate from exactly ?
What can be done to fix this ?
Edit *Seems to be an issue that the data isn't seamlessly coming in. After one package was played, there is a pause, the waveprovider idling and the clicking noise occurs.
Isn't it possible to just get a silence for the time there is no sample ready?*
Audacity view of the signal
using System;
using NAudio.Wave; // Version 1.7.0.13
namespace aws
{
public class AudioMonitor
{
private BufferedWaveProvider waveProvider;
private WaveOut wavePlayer;
int audioFilter = 0;
int sampleRate = 31250;
int resolution = 8;
private void CreateAudio()
{
if (wavePlayer != null)
{
StopAudio();
wavePlayer.Dispose();
wavePlayer = null;
waveProvider = null;
}
waveProvider = new BufferedWaveProvider(new WaveFormat(sampleRate, resolution, 1));
waveProvider.BufferLength = 31250; // standard value: 31250
wavePlayer = new WaveOut();
wavePlayer.DesiredLatency = 300; // standard value: 300
wavePlayer.NumberOfBuffers = 2; // standard value: 2
wavePlayer.Init(waveProvider);
}
public bool StartAudio()
{
CreateAudio();
if (wavePlayer != null)
{
wavePlayer.Play();
return true;
}
return false;
}
public void StopAudio()
{
if (wavePlayer != null)
{
wavePlayer.Stop();
}
}
public PlaybackState GetPlaybackState()
{
if (wavePlayer != null)
{
return wavePlayer.PlaybackState;
}
return PlaybackState.Stopped;
}
public void SetBuffer(byte[] b)
{
int indexWrite = 0, indexBuffer = 0, sizeBuffer;
byte[] nbuffer = b;
sizeBuffer = b.Length;
// valid data
for (indexWrite = indexBuffer; indexWrite < (indexBuffer + sizeBuffer); ++indexWrite)
{
nbuffer[indexWrite] = (Byte)((nbuffer[indexWrite]) + 128);
if ((nbuffer[indexWrite] < (128 + audioFilter)) && (nbuffer[indexWrite] > (128 - audioFilter)))
nbuffer[indexWrite] = 128;
}
try
{
waveProvider.AddSamples(nbuffer, indexBuffer, sizeBuffer);
}
catch (Exception e){
Console.WriteLine(e.StackTrace);
}
}
public void SetBuffer(double[] d, double fac)
{
byte[] tmpBuf = new byte[d.Length];
for(int i = 0; i < tmpBuf.Length; i++)
{
tmpBuf[i] = (byte)(d[i] / fac);
}
SetBuffer(tmpBuf);
}
public void SetAudioFilter(int f)
{
audioFilter = f*3; // min 0 max 60
}
public void Finalize()
{
if (wavePlayer != null)
{
wavePlayer.Stop();
wavePlayer.Dispose();
}
}
}
}

Ping Completed Event Handler not working [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
When I execute this code in command line, it's working fine:
class Program
{
private static List<Ping> pingers = new List<Ping>();
private static List<string> value = new List<string>();
private static int instances = 0;
private static object #lock = new object();
private static int result = 0;
private static int timeOut = 2500;
private static int ttl = 7;
public static void Main()
{
string baseIP = "192.168.1.";
Console.WriteLine("Pinging destinations of D-class in {0}*", baseIP);
CreatePingers(254);
PingOptions po = new PingOptions(ttl, true);
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
byte[] data = enc.GetBytes("");
SpinWait wait = new SpinWait();
int cnt =1;
Stopwatch watch = Stopwatch.StartNew();
foreach (Ping p in pingers)
{
lock (#lock)
{
instances += 1;
}
p.SendAsync(string.Concat(baseIP, cnt.ToString()), timeOut, data, po);
cnt += 1;
}
//while (instances > 0)
//{
// wait.SpinOnce();
//}
watch.Stop();
for (int i = 0; i < value.Count; i++)
{
Console.WriteLine(value[i]);
}
DestroyPingers();
Console.WriteLine("Finished in {0}. Found {1} active IP-addresses.", watch.Elapsed.ToString(), result);
Console.ReadKey();
}
public static void Ping_completed(object s, PingCompletedEventArgs e)
{
lock (#lock)
{
instances -= 1;
}
if (e.Reply.Status == IPStatus.Success)
{
string sa = string.Concat("Active IP: ", e.Reply.Address.ToString());
value.Add(sa);
//Console.WriteLine(sa);
String diachiip = e.Reply.Address.ToString();
result += 1;
}
else
{
//Console.WriteLine(String.Concat("Non-active IP: ", e.Reply.Address.ToString()))
}
}
private static void CreatePingers(int cnt)
{
for (int i = 1; i <= cnt; i++)
{
Ping p = new Ping();
p.PingCompleted += Ping_completed;
pingers.Add(p);
}
}
private static void DestroyPingers()
{
foreach (Ping p in pingers)
{
p.PingCompleted -= Ping_completed;
p.Dispose();
}
pingers.Clear();
}
}
But when I convert from it to window form, it doesn't work. I don't kwow why, I have tried many different ways...
Code is here:
public partial class Form1 : Form
{
public static List<Ping> pingers = new List<Ping>();
public static List<string> value = new List<string>();
public static int instances = 0;
public static object #lock = new object();
public static int result = 0;
public int timeout = 2500;
public static int ttl = 7;
public Form1()
{
InitializeComponent();
}
public void btnscan_Click(object sender, EventArgs e)
{
string baseIP = "192.168.1.";
//int kt = Int32.Parse(txtkt.Text);
//int start = Int32.Parse(txtstart.Text);
CreatePingers(254);
PingOptions po = new PingOptions(ttl, true);
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
byte[] data = enc.GetBytes("");
int cnt = 1;
Stopwatch watch = Stopwatch.StartNew();
foreach (Ping p in pingers)
{
lock (#lock)
{
instances += 1;
}
p.SendAsync(string.Concat(baseIP, cnt.ToString()), timeout, data, po);
cnt += 1;
}
watch.Stop();
//Result alway return 0
lst1.Items.Add(result.ToString());
lst1.Items.Add(value.Count.ToString());
for (int i = 0; i < value.Count; i++)
{
lst1.Items.Add(value[i]);
lst1.Items.Add("\n");
}
DestroyPingers();
string a = "Finished in " + watch.Elapsed.ToString() + ". Found " + result + " active IP-addresses.";
lst1.Items.Add(a);
}
public static void CreatePingers(int kt)
{
for (int start = 1; start <= kt; start++)
{
// class System.Net.NetworkInformation.Ping
Ping p = new Ping();
p.PingCompleted += Ping_completed();
pingers.Add(p);
}
}
public static PingCompletedEventHandler Ping_completed()
{
PingCompletedEventHandler a = new PingCompletedEventHandler(abc);
return a;
}
static void abc(object s, PingCompletedEventArgs e)
{
value.Add("abc");
lock (#lock)
{
instances -= 1;
}
if (e.Reply.Status == IPStatus.Success)
{
string abcd = string.Concat("Active IP: ", e.Reply.Address.ToString());
value.Add(abcd);
result += 1;
}
}
public static void DestroyPingers()
{
foreach (Ping p in pingers)
{
p.PingCompleted -= Ping_completed();
p.Dispose();
}
pingers.Clear();
}
}
What is wrong in this code?
Method SendAsync returns 0 because you are not waiting for it to complete. You are missing await and async (see msdn):
async void btnscan_Click(object sender, EventArgs e)
{
...
await p.SendAsync(string.Concat(baseIP, cnt.ToString()), timeout, data,
...
}
SpinWait was making code to work in console application. In winforms you should not use SpinWait (nor Sleep) in UI thread. You can create another thread (e.g. by using Task) and then you can copy/paste code from console application 1-to-1. But then you will need to use Invoke each time when you want to access UI controls.
async/await is really better.. if it will work (I concluded that from method name, I've no idea what method does, nor how to use it).
Perhaps I miss one thing, if SendAsync returns value, then you can get it by (the requirement to mark method where you use await with async still):
var result = await p.SendAsync(...);

Exception when trying to close serial port or exít application

I have an application that reads XBee frames through the serial port and displays the frame in a richtextbox. It also displays the 2 analog channels (temperature Readings) in a chart and updates it in real time.
I was having issues that the application was reading the serial port faster than the bytes were coming in so I added this line:
while (comPort.BytesToRead < (inLength + 4)) Thread.Sleep(10);
That solved that problem but now I am unable to Close the serial port or quit the application without getting the message: "An unhandled exception of type 'System.InvalidOperationException' occurred in System.dll Additional information: The port is closed."
I am suspicious that it is a multithreading issue, but how do I solve it? I saw on a similar post that using BeginInvoke instead of Invoke should resolve the issue, but I am already using that.
Here is the code:
namespace SerialTest
{
public partial class frmMain : Form
{
delegate void SetTextCallback(string text);
delegate void SetChartCallback(double a, double b);
string inRawFrame = String.Empty;
double temp1 = 0;
double temp2 = 0;
public frmMain()
{
InitializeComponent();
}
private void btnGetSerialPorts_Click(object sender, EventArgs e)
{
if(btnGetSerialPorts.Text == "Open")
{
btnGetSerialPorts.Text = "Close";
comPort.PortName = Convert.ToString(cboPorts.Text);
comPort.BaudRate = Convert.ToInt32(cboBaudRate.Text);
comPort.ReadTimeout = 4000;
comPort.WriteTimeout = 6000;
if (!comPort.IsOpen)
{
try
{
comPort.Open();
cboPorts.Enabled = false;
cboBaudRate.Enabled = false;
}
catch(UnauthorizedAccessException ex)
{
MessageBox.Show(ex.Message);
}
}
}
else if (btnGetSerialPorts.Text == "Close")
{
btnGetSerialPorts.Text = "Open";
comPort.Close();
cboPorts.Enabled = true;
cboBaudRate.Enabled = true;
}
}
private void Form1_Load(object sender, EventArgs e)
{
string[] arrayComPortsNames = null;
int index = 0;
string comPortName = null;
arrayComPortsNames = SerialPort.GetPortNames();
Array.Sort(arrayComPortsNames);
while (!((arrayComPortsNames[index] == comPortName) || (index == arrayComPortsNames.GetUpperBound(0))))
{
cboPorts.Items.Add(arrayComPortsNames[index]);
index++;
}
comPortName = arrayComPortsNames[0];
cboPorts.Text = comPortName;
cboBaudRate.Items.Add(9600);
cboBaudRate.Items.Add(14400);
cboBaudRate.Items.Add(19200);
cboBaudRate.Items.Add(38400);
cboBaudRate.Items.Add(57600);
cboBaudRate.Items.Add(115200);
cboBaudRate.Items.ToString();
cboBaudRate.Text = cboBaudRate.Items[5].ToString();
}
private void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
byte[] incByte = new byte[3];
int length = 0;
if(comPort.BytesToRead > 3)
{
comPort.Read(incByte, 0, 3);
if (incByte[0] == 0x7E)
{
length = (incByte[1] << 8) + incByte[2];
byte[] buffer = new byte[length+4];
buffer[0] = incByte[0];
buffer[1] = incByte[1];
buffer[2] = incByte[2];
ReadFrame(buffer, length, DateTime.Now);
temp1 = ReadTemp(buffer, 1);
temp2 = ReadTemp(buffer, 2);
DisplayFrame();
UpdateChart();
}
}
}
private void ReadFrame(byte[] inBuffer, int inLength, DateTime time)
{
while (comPort.BytesToRead < (inLength + 4)) Thread.Sleep(10);
comPort.Read(inBuffer, 3, (inBuffer.Length - 3));
inRawFrame = time + " " + BitConverter.ToString(inBuffer).Replace("-", " ");
}
private void DisplayFrame()
{
if (rtbIncomingData.InvokeRequired)
{
rtbIncomingData.BeginInvoke(new SetTextCallback(SetText), new object[] { inRawFrame });
}
else
{
SetText(inRawFrame);
}
}
private void SetText(string text)
{
this.rtbIncomingData.AppendText(text + Environment.NewLine);
}
private double ReadTemp(byte[] data, int channel)
{
if(data[3] == 0x92)
{
if(channel == 1)
{
return ((((data[19] << 8) + data[20]) * 1.2 / 1023) - 0.5) * 100.0;
}
else
{
return ((((data[21] << 8) + data[22]) * 1.2 / 1023) - 0.5) * 100.0;
}
}
else
return 100;
}
private void UpdateChart()
{
if (chart1.InvokeRequired)
chart1.BeginInvoke(new SetChartCallback(SetChart), new object[] { temp1, temp2 });
else
SetChart(temp1, temp2);
}
private void SetChart(double val1, double val2)
{
chart1.ChartAreas["ChartArea1"].AxisX.LabelStyle.Format = "HH:mm:ss";
chart1.ChartAreas["ChartArea1"].AxisX.MajorGrid.LineColor = Color.LightGray;
chart1.ChartAreas["ChartArea1"].AxisY.MajorGrid.LineColor = Color.LightGray;
chart1.ChartAreas["ChartArea1"].AxisX.LabelStyle.Font = new Font("Consolas", 8);
chart1.ChartAreas["ChartArea1"].AxisY.LabelStyle.Font = new Font("Consolas", 8);
chart1.ChartAreas["ChartArea1"].AxisY.Maximum = 30;
chart1.ChartAreas["ChartArea1"].AxisY.Minimum = 10;
chart1.ChartAreas["ChartArea1"].AxisY.Interval = 1;
chart1.Series[0].Name = "Temp 1";
chart1.Series[1].Name = "Temp 2";
chart1.Series[0].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
chart1.Series[0].MarkerStyle = System.Windows.Forms.DataVisualization.Charting.MarkerStyle.Diamond;
chart1.Series[0].XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.DateTime;
chart1.Series[1].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
chart1.Series[1].MarkerStyle = System.Windows.Forms.DataVisualization.Charting.MarkerStyle.Diamond;
chart1.Series[1].XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.DateTime;
chart1.Series[0].Points.AddXY(DateTime.Now, val1);
chart1.Series[1].Points.AddXY(DateTime.Now, val2);
}
}
}
If you close the serial port while ReadFrame() is still waiting for bytes, you'll end up checking BytesToRead on a closed serial port. Maybe try this version instead:
private void ReadFrame(byte[] inBuffer, int inLength, DateTime time)
{
while (comPort.IsOpen)
{
if (comPort.BytestoRead >= inLength + 4)
{
comPort.Read(inBuffer, 3, (inBuffer.Length - 3));
inRawFrame = time + " " + BitConverter.ToString(inBuffer).Replace("-", " ");
return;
}
Thread.Sleep(10);
}
}
It will exit if the serial port closes while waiting for a frame, and only checks BytesToRead if the port is still open.

Excel sheet renaming won't work

I want to rename the specified worksheet and put some processed data on it, but the following code (which looks almost the same as every example I could find on this and other websites) just won't to what I want:
try
{
xl.Worksheet = (ExcelRefer.Worksheet) xl.Workbook.Worksheets.get_Item("Sheet1");
xl.Worksheet.Name = "Smoothed result";
xl.Workbook.Save();
xl.Range = xl.Worksheet.UsedRange;
Debug.WriteLine("Saved");
}
catch(Exception exception)
{
MessageBox.Show(exception.Message);
}
The exception is never thrown, so the code doesn't contain any errors but the worksheet in the document I've opened still has the same name. Also, the
Debug.WriteLine(...)
method is called and the output is correctly displayed. It does even ask me if I want to overwrite the existing file when I tell the Workbook to save. Could anyone tell me what I'm doing wrong?
Edit:
xl is an object of a class that contains all necessary elements for working with excel.
using ExcelRefer = Microsoft.Office.Interop.Excel;
using Microsoft.Office.Tools.Excel;
using Microsoft.VisualBasic;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using ZedGraph;
class XLSXMulti
{
private static List<int[]> dataPositions = new List<int[]>();
private static PointPairList measureData = new PointPairList();
private static PointPairList[] dataComparison = new PointPairList[2];
private static bool smoothing = false;
private static bool measureDataFound = false;
private static bool fileFinished = false;
private static string[] excelFilePaths;
private static string[] temp;
private static int numberOfSamples;
private static BackgroundWorker[] bwg;
private static ProgressBar pbExcel;
public static void Init(string[] filePaths, ProgressBar pb, bool smooth)
{
excelFilePaths = filePaths;
pbExcel = pb;
smoothing = smooth;
measureData.Clear();
dataPositions.Clear();
temp = Interaction.InputBox("Number of consecutive values to be used " +
"for smoothing and the iteration step. These values" +
" will be used for all selected files.\n" +
"IMPORTANT: The two values have to be separated with a ','!",
"Select grade of smoothing",
"5,1", 500, 480).Split(',');
bwg = new BackgroundWorker[Environment.ProcessorCount];
for(int i = 0; i < bwg.Length; i ++)
{
bwg[i] = new BackgroundWorker();
}
foreach(BackgroundWorker bw in bwg)
{
bw.DoWork += bwg_doWork;
bw.ProgressChanged += bwg_ProgressChanged;
bw.RunWorkerCompleted += bwg_RunworkerCompleted;
bw.WorkerSupportsCancellation = true;
bw.WorkerReportsProgress = true;
}
List<string>[] filesForWorkers = new List<string>[bwg.Length];
for(int i = 0; i < filesForWorkers.Length; i ++)
{
filesForWorkers[i] = new List<string>();
}
int filesPerWorker = filePaths.Length / bwg.Length;
int workerindex = 0;
MessageBox.Show("filesPerWorker: " + filesPerWorker + "\n" +
"filePaths: " + filePaths.Length + "\n" +
"bwg: " + bwg.Length);
for(int i = 0; i < filePaths.Length; i ++)
{
filesForWorkers[workerindex].Add(filePaths[i]);
workerindex ++;
if(workerindex == bwg.Length)
{
workerindex = 0;
}
}
for(int i = 0; i < bwg.Length; i ++)
{
bwg[i].RunWorkerAsync(filesForWorkers[i]);
}
}
private static void bwg_doWork(object sender, DoWorkEventArgs e)
{
List<string> filelist = e.Argument as List<string>;
Excelattributes xl = new Excelattributes();
foreach(string s in filelist)
{
try
{
xl.App = new ExcelRefer.Application();
xl.Workbook = xl.App.Workbooks.Open(s, 0, true, 5, "", "", true,
Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
xl.Worksheet = (ExcelRefer.Worksheet)xl.Workbook.Worksheets.get_Item(1); // Zugriff auf eingegebenes Tabellenblatt
xl.Range = xl.Worksheet.UsedRange;
findMeasureData(xl);
if(xl.Workbook != null){xl.Workbook.Close(true, null, null);}
if(xl.App != null) {xl.App.Quit();}
}
catch(Exception exception)
{
MessageBox.Show(exception.Message, "Exception");
}
finally
{
if(xl.Workbook != null){xl.Workbook.Close(true, null, null);}
if(xl.App != null) {xl.App.Quit();}
}
}
}
private static void bwg_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// TODO: implement some sort of progress reporting
}
private static void bwg_RunworkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Statuslabel.label.Text = "Backgroundworker finished";
(sender as BackgroundWorker).Dispose();
}
private static void findMeasureData(Excelattributes xl)
{
// Erste 15 Zeilen durchgehen, um herauszufinden wo die Messwerte beginnen (9 + 6 Sicherheit)
// Nur bis inkl. Spalte AZ
try
{
for(int zeile = 1; zeile <= 15; zeile ++)
{
for(int spalte = 1; spalte <= 52; spalte ++)
{
// WICHTIG: (xl.Range...).Text verwenden, um Nullreferenceexception zu vermeiden [?]
Object zelleninhalt = (xl.Range.Cells[zeile, spalte] as ExcelRefer.Range).Text;
if(zelleninhalt.Equals("DATA START"))
{
dataPositions.Add(new int[2] {zeile +1, spalte +1});
measureDataFound = true;
}
else if(zelleninhalt.Equals("Number of Samples"))
{
numberOfSamples = Convert.ToInt32((xl.Range.Cells[zeile, spalte+1] as
ExcelRefer.Range).Text);
}
}
}
if(measureDataFound == true)
{
int[,] temp = new int[dataPositions.Count, 2];
for(int i = 0; i < dataPositions.Count; i ++)
{
temp[i, 0] = dataPositions.ElementAt(i).ToArray()[0];
temp[i, 1] = dataPositions.ElementAt(i).ToArray()[1];
}
readMeasureData(temp, xl);
}
}
catch(Exception exception)
{
MessageBox.Show(exception.Message, "Exception");
}
}
private static void readMeasureData(int[,] temp, Excelattributes xl)
{
int[,] zellen = temp;
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for(int i = zellen[0,0]; i < (zellen[0,0] + numberOfSamples); i ++)
{
try
{
double weg = Convert.ToDouble((xl.Range.Cells[i, zellen[0,1]] as ExcelRefer.Range).Value2);
double kraft = Convert.ToDouble((xl.Range.Cells[i, zellen[1,1]] as ExcelRefer.Range).Value2);
measureData.Add(new PointPair(weg, kraft));
}
catch(Exception exception)
{
MessageBox.Show(exception.Message, "Exception");
Application.Exit();
}
}
stopwatch.Stop();
MessageBox.Show(stopwatch.ElapsedMilliseconds / 1000 + " Sekunden");
dataComparison[0] = measureData;
if(smoothing == true)
{
smoothMeasureData(xl);
}
}
private static void smoothMeasureData(Excelattributes xl)
{
if(temp != null)
{
try
{
int[] smoothParameters = new int[]{Convert.ToInt32(temp[0]), Convert.ToInt32(temp[1])};
if(smoothParameters[1] == 0) {smoothParameters[1] = 1;}
PointPairList smoothedData = new PointPairList();
MessageBox.Show("Glätte...\n" + smoothParameters[0] + " " + measureData.Count);
for(int i = (smoothParameters[0] -1); i < measureData.Count; i += smoothParameters[1])
{
double durchschnittX = 0;
double durchschnittY = 0;
for(int x = i; x > (i - (smoothParameters[0])); x --)
{
durchschnittX += measureData.ElementAt(x).X;
durchschnittY += measureData.ElementAt(x).Y;
}
durchschnittX /= (double) smoothParameters[0];
durchschnittY /= (double) smoothParameters[0];
smoothedData.Add(new PointPair(durchschnittX, durchschnittY));
}
dataComparison[0] = measureData;
dataComparison[1] = smoothedData;
writeToXLSX(smoothedData, xl);
}
catch(Exception exception)
{
MessageBox.Show(exception.Message, "Exception");
}
}
}
private static void writeToXLSX(PointPairList smoothedData, Excelattributes xl)
{
try
{
xl.Worksheet = (ExcelRefer.Worksheet) xl.Workbook.Worksheets.get_Item("Sheet1");
xl.Worksheet.Name = "Smoothed result";
xl.Workbook.Save();
xl.Workbook.Close(true, null, null);
xl.Range = xl.Worksheet.UsedRange;
Debug.WriteLine("Saved");
}
catch(Exception exception)
{
MessageBox.Show(exception.Message);
}
}
}
Have you tried closing Excel when you've finished?
I can't quite work out where you are gettng xl from but the following code works for me:
Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
Workbook book = xlApp.Workbooks.Open(#"C:\test.xlsx");
Worksheet xl = book.Worksheets.get_Item("sheet1");
xl.Name = "Smoothed result";
book.Save();
book.Close();
Edit
After seeing the code you are using to open the workbook I think the issue is there. You are opening the file using:
xl.Workbook = xl.App.Workbooks.Open(s, 0, true, 5, "", "", true,
Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
The third parameter is a ReadOnly parameter which you have set to true. Try setting that to false.

Categories

Resources