PDA

View Full Version : Hỏi về cách lấy dữ liệu từ VDK trong c# đễ vẽ đồ thị.


trai_xq
13-03-2010, 02:51 PM
Mình đang gặp khó khăn về vấn đề này. Mình không biết cách nào để nhận dữ liệu động, truyền từ VDK qua USB, sau đó vẽ lên c#. Mình dùng một timer(trong c#) để tạo thời gian lấy mẩu trên trục x, còn trục y là dữ liệu mình đọc từ VDK lên. Các bạn nào đã làm qua cái này rồi hướng dẫn cho mình với. Mình có tham khảo cái này, http://zedgraph.org/wiki/index.php?title=Display_Dynamic_or_Real-Time_Data

Ý của mình là như thế này:
Trong hàm vẽ mình sẽ khai báo:
for (i = 0; i < 50; i++)
{
list1.Add(data_x[i], data_y0[i] );
list2.Add(data_x[i], data_y1[i] );
list3.Add(data_x[i], data_y2[i] );
}

trong đó data_x[i] là biến thời gian lấy mẩu nằm trong hàm này:
private void timer1_Tick( object sender, EventArgs e )
{
}
cái này thì chắc không khó, cái khó của mình là data_y phải là biến động lấy từ VDK, mà mình chưa biết phải làm như thế nào với nó. Hiện giờ thì mình đã truyền được nhiều byte lên máy tính qua USB rồi, nhưng mình cũng chưa suy nghĩ được phải khai báo như thế nào với cái data_y. Mong được giúp đỡ.

cskiller
13-03-2010, 03:19 PM
Bạn than khảo ứng dụng mẫu dùng ADC làm Oscilloscope trên PIC18F2550, ở đây dữ liệu nhận được từ VDK ko sử dụng timer mà dùng cơ chế event đọc bất đồng bộ từ USB. Cứ sau 1 thời gian nhất định thì sẽ có dữ liệu từ VDK gởi về PC, sau đó 1 event xảy ra và phần mềm sẽ vẽ lại, như vậy thời gian định thời là từ VDK:

http://www.picvietnam.com/forum/showthread.php?t=5071

Regards

trai_xq
13-03-2010, 03:47 PM
Bạn than khảo ứng dụng mẫu dùng ADC làm Oscilloscope trên PIC18F2550, ở đây dữ liệu nhận được từ VDK ko sử dụng timer mà dùng cơ chế event đọc bất đồng bộ từ USB. Cứ sau 1 thời gian nhất định thì sẽ có dữ liệu từ VDK gởi về PC, sau đó 1 event xảy ra và phần mềm sẽ vẽ lại, như vậy thời gian định thời là từ VDK:

http://www.picvietnam.com/forum/showthread.php?t=5071

Regards

Cảm ơn anh rất nhiều, em đang nghiên cứu. Có gì em sẻ trao đổi nha! Hiên gờ em cũng chưa biết phải làm theo cách nào

nakata87
13-03-2010, 05:08 PM
chao tat ca cac ban :
mình muốn hỏi tất cả mọi người về cái mach thu âm thanh với tiêu chí như sau :
thiết kế mạch thu âm thanh sử dụng micro ,rồi dùng pic điều khiển để thu tín hiệu âm thanh đó ghi vào ic nhớ (ROM)rồi phát trở lại ra loa.(trong mạch bắt buột phải dùng vi điều khiển ),ai biết hoặc đã từng làm thì vẽ cho mình với .Mình cảm ơn rất nhiều








ai cho ta kiến thức là còn quý hơn cho vàng
anhstanh

tbk_05
15-03-2010, 01:43 AM
Mình cũng đang làm về vấn đề này. Mình không biết khi con pic18 truyền lên được c#, thì làm thế nào lấy được dữ liệu đó để đưa vào hàm vẽ đồ thị, chỉ biết được là giá trị đó hiển thị lên trên texbox.
Ví dụ như đoạn chương trình sau là hiển thị giá trị ADC từ VDK lên texbox khi ấn buttom:

//Khi nhan nut "doc" thi`>>>>> truyen hai byte tu VDK len MT
private void btdoc_Click(object sender, EventArgs e)
{
TraceMsg("Receiver:\r\n");
SingleReceiver();
}
private void SingleReceiver()
{
usb_pipe_rev.SetContiguous(true);
DWORD dwBuffSize = 2;
byte[] buffer = new byte[2];
usb_pipe_rev.UsbPipeTransferAsync(true, 2, buffer,
0, 100000, new D_USER_TRANSFER_COMPLETION(Transfer_rev_Completion ));
}
private void Transfer_rev_Completion(pic18f4550_usb_Pipe pipe)
{
if (pipe.GetTransferStatus() == (DWORD)wdu_err.WD_STATUS_SUCCESS)
{
TraceMsg(DisplayHexBuffer(pipe.GetBuffer(), 2));
}
else
{
TraceMsg("receiver failure!\r\n");
}
}

Như vậy thì dữ liệu đó nằm đâu, làm thế nào để lấy ra và vẽ đồ thì. Mong các bạn chỉ dùm cho mình, mình cũng chưa hiểu rỏ lắm.

cskiller
15-03-2010, 09:46 AM
Mình cũng đang làm về vấn đề này. Mình không biết khi con pic18 truyền lên được c#, thì làm thế nào lấy được dữ liệu đó để đưa vào hàm vẽ đồ thị, chỉ biết được là giá trị đó hiển thị lên trên texbox.
Ví dụ như đoạn chương trình sau là hiển thị giá trị ADC từ VDK lên texbox khi ấn buttom:

//Khi nhan nut "doc" thi`>>>>> truyen hai byte tu VDK len MT
private void btdoc_Click(object sender, EventArgs e)
{
TraceMsg("Receiver:\r\n");
SingleReceiver();
}
private void SingleReceiver()
{
usb_pipe_rev.SetContiguous(true);
DWORD dwBuffSize = 2;
byte[] buffer = new byte[2];
usb_pipe_rev.UsbPipeTransferAsync(true, 2, buffer,
0, 100000, new D_USER_TRANSFER_COMPLETION(Transfer_rev_Completion ));
}
private void Transfer_rev_Completion(pic18f4550_usb_Pipe pipe)
{
if (pipe.GetTransferStatus() == (DWORD)wdu_err.WD_STATUS_SUCCESS)
{
TraceMsg(DisplayHexBuffer(pipe.GetBuffer(), 2));
}
else
{
TraceMsg("receiver failure!\r\n");
}
}

Như vậy thì dữ liệu đó nằm đâu, làm thế nào để lấy ra và vẽ đồ thì. Mong các bạn chỉ dùm cho mình, mình cũng chưa hiểu rỏ lắm.

Phương thức UsbPipeTransferAsync thực hiện là 1 yêu cầu đọc bất đồng bộ(Async) 2 byte trên PIPE của USB, và hàm callback Transfer_rev_Completion sế được gọi nếu có dữ liệu trả về từ VDK.
Do đó, sau khi nhấp button, sẽ có 1 request 'chờ' dữ liệu và khi dữ liệu này 'đến' nó được USB PIPE đọc tự động, đưa vào buffer và gọi hàm callback Transfer_rev_Completion xử lý buffer đó. Chính xác nó là 1 thread thực thi ngầm do Hệ điều hành quản lý để giám sát dữ liệu in trên PIPE.
Thực ra library mà bạn dùng này nó đã 'che' phần thao tác in/out trên USB PIPE nên bạn không thấy được luồng dữ liệu được đưa vào buffer như thế nào. Thông qua phương thức GetBuffer() bạn sẽ lấy được dữ liệu đã trên PIPE cho phần xử lý của mình mà thôi.
PS: Vì hàm callback Transfer_rev_Completion được gọi từ thread giám sát PIPE nên nếu bạn muốn thực hiện câu lệnh vẽ, hiện thị, thay đổi giá trị lên forms, components,... bên trong hàm này thì nhớ phải sử dụng cơ chế Invoke.

Regards

tbk_05
18-03-2010, 08:00 AM
mình đã vẽ được ba đồ thị cùng mọt lúc trong c#, cập nhập liên tục khi giá trị ADC ở VDK thay đôi.
Code như thế này:
//khai báo biến toàn cục
public ushort[] data_y0;// du lieu truc y
public ushort[] data_y1;
public ushort[] data_y2;
.....................
Khi ấn nút btlisten, thì sẽ thực hiện việc đọc liên tục sau mỗi 'polling time', máy tính sẽ quét và đọc dữ liệu từ usb driver.
void btlisten_Click(object sender, EventArgs e)
{
data_y0 = null;
data_y0 = null;
data_y1 = null;
SingleTransfer();
Listen_pipe();
}
private void Listen_pipe()
{
//--------------------
DWORD dwOptions = 0;//
usb_pipe_rev.SetContiguous(true); // khi ham nay kich hoat thi cu sau moi polling time thi computer se quet va doc du lieu tu usb device
TraceMsg(string.Format("began listening to {0} pipe number " +
"0x{1:X}", usb_device.DeviceDescription(),
usb_pipe_rev.GetPipeNum()));
usb_pipe_rev.UsbPipeTransferAsync(true, 0, TIME_OUT,
new D_USER_TRANSFER_COMPLETION(Transfer_rev_Completion ));
}
//----------------
sau đó hàm Transfer_rev_Completion mình sẽ lấy dữ liệu ra để vẻ:
private void Transfer_rev_Completion(DOAN_MINH_THUONG_Pipe pipe)
{

DWORD dwStatus = pipe.GetTransferStatus();
byte[] buff;
bool IsListenStopped = (dwStatus == (DWORD)wdu_err.WD_IRP_CANCELED)
&& !pipe.IsContiguous();
if (dwStatus != (DWORD)wdu_err.WD_STATUS_SUCCESS && !IsListenStopped)
{
ErrMsg(string.Format("Transfer Failed! Error {0}: {1} ",
dwStatus.ToString("X"), utils.Stat2Str(dwStatus)));
}
else
{
TraceMsg(DisplayHexBuffer(pipe.GetBuffer(), 3));
buff = pipe.GetBuffer();//hàm này sẽ lấy dữ liệu từ ống pipe
// TraceMsg(string.Format("{0}",
// DisplayHexBuffer(buff,pipe.GetBytesTransferred())) ); // ham pipe.getbuffer tra ve 1 mang chua cac gia tri nhan dc tu usb
// ham getbytes transferred tra ve so byte nhan duoc
time++;
//data_x[time] = time;
data_y0[time] = (ushort)(buff[0] );
data_y1[time] = (ushort)(buff[1]);
data_y2[time] = (ushort)(buff[2]);
}
//Hàm vẽ khi ấn nút btplot
private void btplot_Click(object sender, EventArgs e)
{
CreateGraph(zedGraphControl1 );
SetSize();
}
private void CreateGraph(ZedGraphControl zgc)
{
timer1.Interval = 50;
timer1.Enabled = true;
timer1.Start();
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// get a reference to the GraphPane
GraphPane myPane = zgc.GraphPane;
double v_desire = 120;
// Set the Titles
myPane.Title.Text = "Đọc gia trị ADC";
myPane.XAxis.Title.Text = "Time ( đơn vị : 10 ms )";
myPane.YAxis.Title.Text = "ADC ";
myPane.XAxis.MajorGrid.IsVisible = true;
myPane.YAxis.MajorGrid.IsVisible = true;
myPane.YAxis.Scale.MagAuto = false;
myPane.XAxis.Scale.MagAuto = false;
myPane.XAxis.Scale.Mag = 1;
// Make up some data arrays based on the Sine function
// Make up some data arrays based on the Sine function
PointPairList list1 = new PointPairList();
PointPairList list2 = new PointPairList();
PointPairList list3 = new PointPairList();

LineItem myCurve1 = myPane.AddCurve("ADC 1", list1, Color.Blue, SymbolType.None);
LineItem myCurve2 = myPane.AddCurve("ADC 2", list2, Color.Green, SymbolType.None);
LineItem myCurve3 = myPane.AddCurve("ADC 3", list3, Color.Tomato, SymbolType.None);
//LineItem myCurve2 = myPane.AddCurve("ADC 2", list3, Color.Green, SymbolType.None);
//LineItem myCurve3 = myPane.AddCurve("ADC 3", list8, Color.Red, SymbolType.None);
myCurve1.Line.IsSmooth = true;
myCurve1.Line.SmoothTension = 0.5F;
//---------
myCurve2.Line.IsSmooth = true;
myCurve2.Line.SmoothTension = 0.5F;
//--------
myCurve3.Line.IsSmooth = true;
myCurve3.Line.SmoothTension = 0.5F;
//myCurve3.Line.IsSmooth = true;
// myCurve3.Line.SmoothTension = 0.5F;
myCurve1.Line.Width = 2;
myCurve2.Line.Width = 2;
myCurve3.Line.Width = 2;
//myCurve3.Line.Width = 2;
//myCurve_desire.Line.Width = 2;
zgc.AxisChange();


//RollingPointPairList list2 = new RollingPointPairList(120);
// instead of discrete step-sized jumps
myPane.XAxis.Scale.Min = 0;
myPane.XAxis.Scale.Max = 30;
myPane.XAxis.Scale.MinorStep = 1;
myPane.XAxis.Scale.MajorStep = 5;

// Save the beginning time for reference
tickStart = Environment.TickCount;
}
private void timer1_Tick(object sender, EventArgs e)//để tạo thời gian và lấy mẩu
{
if (zedGraphControl1.GraphPane.CurveList.Count <= 0)
return;
LineItem curve1 = zedGraphControl1.GraphPane.CurveList[0] as LineItem;
if (curve1 == null)
return;
LineItem curve2 = zedGraphControl1.GraphPane.CurveList[1] as LineItem;
if (curve2 == null)
return;
LineItem curve3 = zedGraphControl1.GraphPane.CurveList[2] as LineItem;
if (curve3 == null)
return;
IPointListEdit list1 = curve1.Points as IPointListEdit;
IPointListEdit list2 = curve2.Points as IPointListEdit;
IPointListEdit list3 = curve3.Points as IPointListEdit;
if (list1 == null)
return;
if (list2 == null)
return;
if (list3 == null)
return;
double time1 = (Environment.TickCount - tickStart) / 1000.0;
// 3 seconds per cycle
list1.Add(time1, data_y0[time]);
list2.Add(time1, data_y1[time]);
list3.Add(time1, data_y2[time]);
Scale xScale = zedGraphControl1.GraphPane.XAxis.Scale;
if (time1 > xScale.Max - xScale.MajorStep)
{
xScale.Max = time1 + xScale.MajorStep;
xScale.Min = xScale.Max - 30.0;
}
zedGraphControl1.AxisChange();
zedGraphControl1.Invalidate();
}

Nhưng ở đây mình chỉ vẽ được 3 ADC 8bit(max=255), giờ mình muốn vẽ ADC 10bit thì làm thế nào. VDK chi gửi lên được 8bit một lần(gửi nhiều byte). Như vậy thì đối với chương trình CCS thì phải cắt ra để gửi, một byte chứa 8bit, con một byte chứa 2bit, thuật toán cắt sẻ như thế nào, các bạn nào biết chỉ mình với.
Giả sử như mình gửi được rồi thì trên C# mình cũng phải có thuật toán ghép chuổi lại, chứ không thể nào cộng hai byte đó lại được. Như thế thì thuật toán trong c# cũng là vấn đề. mong các bạn biết và chỉ dùm.

cskiller
18-03-2010, 09:56 AM
Rất đơn giản thôi bạn ah, trên C bạn dùng struct lưu 3 số integer 16 bit để chứa dữ liệu

typedef struct{
unsigned int16 ADC0;
unsigned int16 ADC1;
unsigned int16 ADC2;
} USBAdc;

Khi gởi qua cổng USB thì chuyển kiểu sang dạng mảng byte bình thường.

Còn trên C# bạn làm việc ngược lại, khai báo 1 struct hoặc class tương ứng nhưng nhớ dùng thêm chỉ thị alignment từng byte 1(để đảm bảo đúng thứ tự dữ liệu):

using System.Runtime.InteropServices;
...
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public class USBAdc
{
public UInt16 ADC0;
public UInt16 ADC1;
public UInt16 ADC2;
....
static public USBAdc FromByteArray(byte[] byte_Array)
{
USBAdc ret;
GCHandle handle = GCHandle.Alloc(byte_Array, GCHandleType.Pinned);
ret = (USBAdc)Marshal.PtrToStructure(handle.AddrOfPinned Object(), typeof(USBAdc));
handle.Free();
return ret;
}
}


Chú ý: FromByteArray là 1 phương thức static có chức năng chuyển 1 mảng Byte nhận được từ USB sang class USBAdc:

USBAdc adc= USBAdc.FromByteArray(usb_array_data);
...
adc.ADC0
adc.ADC1
adc.ADC2


Regards

tbk_05
18-03-2010, 12:26 PM
Cảm ơn anh, em sẻ nghiên cứu thuật toán của anh nói, có gì mới em sẻ hồi âm lại.

Chào anh!

pic18f4550_usb
19-03-2010, 10:23 AM
Anh cskiiler !Anh có thể nói rõ hơn về cách truyền ADC 10 bit lên máy tính thông qua USB được không?Em cảm ơn Anh.

trai_xq
19-03-2010, 10:46 AM
Theo mình nghỉ thì như thế này, mổi ADC 10bit chia thành 2 byte:
unsigned int16 value, value1,value2;
//-----------------
value=read_adc();
value1=value&255;//max cua vlue1 la 255
value2=value&768;//max cua value2 la 768, nhu vay thi > 255=1byte
delay_ms(20);
//value1+value2=1023
Sau đó truyền hai byte đó lên, nhưng không biết value2 có thể truyền được không bởi vì nó lớn hơn 255. Như vậy phải giải quyết như thế nào, mong anh cskiller có thể giúp đỡ được không a.?

tbk_05
19-03-2010, 11:00 AM
Theo mình nghỉ thì như thế này, mổi ADC 10bit chia thành 2 byte:
unsigned int16 value, value1,value2;
//-----------------
value=read_adc();
value1=value&255;//max cua vlue1 la 255
value2=value&768;//max cua value2 la 768, nhu vay thi > 255=1byte
delay_ms(20);
//value1+value2=1023
Sau đó truyền hai byte đó lên, nhưng không biết value2 có thể truyền được không bởi vì nó lớn hơn 255. Như vậy phải giải quyết như thế nào, mong anh cskiller có thể giúp đỡ được không a.?

Mình có cách giải quyết rồi, là như thế này:
unsigned int16 value, value1,value2;
//-----------------
value=read_adc();
value1=value&255;//max cua vlue1 la 255
value2=value&768;//max cua value2 la 768, nhu vay thi > 255=1byte
delay_ms(20);
//value1+value2=1023

Đoạn chương trình trên bạn sẻ gửi lên 2 byte là value1 và value2/256
Trên c# bạn sẻ nhận lai 2 byte đó như sau:
ADC10bit=value1+value*256;
OK!

tbk_05
19-03-2010, 11:00 AM
Theo mình nghỉ thì như thế này, mổi ADC 10bit chia thành 2 byte:
unsigned int16 value, value1,value2;
//-----------------
value=read_adc();
value1=value&255;//max cua vlue1 la 255
value2=value&768;//max cua value2 la 768, nhu vay thi > 255=1byte
delay_ms(20);
//value1+value2=1023
Sau đó truyền hai byte đó lên, nhưng không biết value2 có thể truyền được không bởi vì nó lớn hơn 255. Như vậy phải giải quyết như thế nào, mong anh cskiller có thể giúp đỡ được không a.?

Mình có cách giải quyết rồi, là như thế này:
unsigned int16 value, value1,value2;
//-----------------
value=read_adc();
value1=value&255;//max cua vlue1 la 255
value2=value&768;//max cua value2 la 768, nhu vay thi > 255=1byte
delay_ms(20);
//value1+value2=1023

Đoạn chương trình trên bạn sẻ gửi lên 2 byte là value1 và value2/256
Trên c# bạn sẻ nhận lai 2 byte đó như sau:
ADC10bit=value1+value2*256;
OK!

cskiller
19-03-2010, 11:55 AM
Như bạn tbk_05 đã giải quyết là cách cơ bản và dễ dàng nhất, cắt số integer 16bits chứa giá trị ADC 10bits sau đó ghép lại phía nhận.

Tuy nhiên cách này làm thủ công bằng tay nên khá mệt và dễ lộn xộn. Cách mình trình bày trên sẽ dễ dàng truyền nhận ko chỉ số integer 16 bits mà còn có thể áp dụng cho số 32 bits, các kiểu dữ liệu khác hoặc là 1 struct phức tạp.
Giả sử như với khai báo trên, phía VDK bạn đọc 3 kênh ADC sau đó gởi vào USB:

set_adc_channel(0);
adc.ADC0=read_adc(); // 100
set_adc_channel(1);
adc.ADC1=read_adc(); // 200
set_adc_channel(2);
adc.ADC2=read_adc(); // 300
...
usb_put_data((char*)&adc,sizeof(USBAdc)) // Giả sử gởi array là 6 bytes


Ở phía PC, giải sử việc đọc từ USB được 1 mảng byte là usb_byte_array(dĩ nhiên là 1 array 6 bytes như lúc gởi), sau đó dùng phương thức FromByteArray của lớp USBAdc chuyển thành 1 đối tượng USBAdc và sử dụng dữ liệu như bình thường:

USBAdc adc= USBAdc.FromByteArray(usb_byte_array);
...
TextBox0.Text = adc.ADC0; // Giá trị sẽ là 100
TextBox1.Text = adc.ADC1; // Giá trị sẽ là 200
TextBox2.Text = adc.ADC2; // Giá trị sẽ là 300
...

Như code trên, hoàn toàn ko cần cắt/ghép hay xử lý gì hết. Chỉ đơn giản là sử dụng khả năng chuyển kiểu của C và C# để thực hiện.
Bạn có thể mở rộng hơn 6 bytes hoặc thêm bớt các thành phần, biến, cấu trúc dữ liệu khác cho khai báo USBAdc, miễn sao đảm bảo đúng thứ tự và cấu trúc tương ứng 1 gói giữa C/VDK và C#/PC là ok.

Regards

letanminh
20-03-2010, 12:00 PM
Anh cskiier!Sau khi vẻ được đồ thị trên C# rồi.Làm sao để lưu những gì trị vẻ đó vào file .txt.Gồm 2 thành phần là thời gian và giá trị vi điều khiển đọc được.Nhờ Anh chỉ dùm.Em cảm ơn Anh!!!!

manucian1988
27-03-2011, 11:28 PM
Em là mem mới, các anh cho em hỏi, trong firmware mình muốn gửi một mảng int8 lên PC qua USB thì dùng hàm usb_puts như thế nào hay nếu dùng vòng for cho hàm usb_put_packet thì thời gian timeout thế nào? Và trong hàm UsbPipeTransferAsync này thì thời gian timeout có ý nghĩa gì?
Em đã dùng vòng for cho hàm usb_put_packet và vòng for cho hàm nhận trên C# thì thấy kết quả ra textbox lúc đúng lúc sai.
Đây là code C#:
private void button2_Click(object sender, EventArgs e)
{

for(i=0;i<9;i++)
{
SingleReceiver();
Application.DoEvents();
}
}
private void SingleReceiver()
{

DWORD dwBuffSize = 1;
byte[] buffer = new byte[1];
textBox1.Text = "";
usb_pipe_rev.UsbPipeTransferAsync(true, 0, buffer,dwBuffSize, TIME_OUT, new D_USER_TRANSFER_COMPLETION(Transfer_rev_Completion ));
}
private void Transfer_rev_Completion(MRMU_1WIRE_Pipe pipe)
{
byte[] buff;
if (pipe.GetTransferStatus() == (DWORD)wdu_err.WD_STATUS_SUCCESS)
{
TraceMsg(DisplayHexBuffer(pipe.GetBuffer(),1));
}
else
{
TraceMsg("Receiver failure!\n\r");
}
}


Thank!

buivanbinh12
23-12-2012, 06:04 PM
BÁC NÀO BIẾT ĐOẠN CODE VẼ ĐỒ THỊ NÀY CHÚ THÍCH CHO EM ÍT

#region Namespace Inclusions
using System;
using System.Linq;
using System.Data;
using System.Text;
using System.Drawing;
using System.IO.Ports;
using System.Windows.Forms;
using System.ComponentModel;
using System.Collections.Generic;
using DynamicData.Properties;
using System.Threading;
using System.IO;
using ZedGraph;
#endregion

namespace DynamicData
{
#region Public Enumerations
public enum DataMode { Text, Hex }
public enum LogMsgType { Incoming, Outgoing, Normal, Warning, Error };
#endregion

public partial class frmMain : Form
{
#region Local Variables
// The main control for communicating through the RS-232 port
private SerialPort comport = new SerialPort();
// Various colors for logging info
private Color[] LogMsgTypeColor = { Color.Blue, Color.Green, Color.Black, Color.Orange, Color.Red };
// Temp holder for whether a key was pressed
//private bool KeyHandled = false;
private Settings settings = Settings.Default;
#endregion

private GraphPane myPane;
private PointPairList m_pointsList;

int flagData = 0;
int flagPoint = 0;
int nIndex = 0;
int inputData1 = 0;
int inputData2 = 0;
// Starting time in milliseconds
int tickStart = 0;
public frmMain()
{
InitializeComponent();

// Restore the users settings
InitializeControlValues();

// Enable/disable controls based on the current state
EnableControls();

// When data is recieved through the port, call this method
comport.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);

button1.Enabled = false;
button2.Enabled = false;

CurrentDataMode = DataMode.Hex;

}

private void Form1_Load( object sender, EventArgs e )//khi
{
myPane = zedGraphControl1.GraphPane;
myPane.Title.Text = "Biểu đồ\n" +
"Ứng Suất/Độ biến dạng";
myPane.YAxis.Title.Text = "Ứng Suất, kN/Cm2";//truc x ghi ung suat
myPane.XAxis.Title.Text = "Độ biến dạng, mm/m";

// Scale the axes
zedGraphControl1.AxisChange();

// Save the beginning time for reference
tickStart = Environment.TickCount;

m_pointsList = new PointPairList();
}

private void CreateLineGraph()
{
myPane.CurveList.Clear();

/*
// Test
m_pointsList.Add(0, 0);
m_pointsList.Add(10, 2000);
m_pointsList.Add(50, 10000);
m_pointsList.Add(100, 20000);
*/
// Generate a blue curve with Star symbols
LineItem myCurve = myPane.AddCurve("Đường đặc tính", m_pointsList, Color.Blue, SymbolType.Star);

// Make sure the Y axis is rescaled to accommodate actual data
zedGraphControl1.AxisChange();
// Force a redraw
zedGraphControl1.Invalidate();
}

private void Form1_Resize( object sender, EventArgs e )
{
SetSize();
}

// Set the size and location of the ZedGraphControl
private void SetSize()
{
// Control is always 10 pixels inset from the client rectangle of the form
Rectangle formRect = this.ClientRectangle;
formRect.Inflate( -10, -10 );

if ( zedGraphControl1.Size != formRect.Size )
{
zedGraphControl1.Location = formRect.Location;
zedGraphControl1.Size = formRect.Size;
}
}

private void btnOpenPort_Click(object sender, EventArgs e)
{
bool error = false;

// If the port is open, close it.
if (comport.IsOpen) comport.Close();
else
{
// Set the port's settings
comport.BaudRate = int.Parse(cmbBaudRate.Text);
comport.DataBits = int.Parse(cmbDataBits.Text);
comport.StopBits = (StopBits)Enum.Parse(typeof(StopBits), cmbStopBits.Text);
comport.Parity = (Parity)Enum.Parse(typeof(Parity), cmbParity.Text);
comport.PortName = cmbPortName.Text;

try
{
// Open the port
comport.Open();
}
catch (UnauthorizedAccessException) { error = true; }
catch (IOException) { error = true; }
catch (ArgumentException) { error = true; }

if (error) MessageBox.Show(this, "Could not open the COM port. Most likely it is already in use, has been removed, or is unavailable.", "COM Port Unavalible", MessageBoxButtons.OK, MessageBoxIcon.Stop);
}

// Change the state of the form's controls
EnableControls();
}

/// <summary> Convert a string of hex digits (ex: E4 CA B2) to a byte array. </summary>
/// <param name="s"> The string containing the hex digits (with or without spaces). </param>
/// <returns> Returns an array of bytes. </returns>
private byte[] HexStringToByteArray(string s)
{
s = s.Replace(" ", "");
byte[] buffer = new byte[s.Length / 2];
for (int i = 0; i < s.Length; i += 2)
buffer[i / 2] = (byte)Convert.ToByte(s.Substring(i, 2), 16);
return buffer;
}

/// <summary> Converts an array of bytes into a formatted string of hex digits (ex: E4 CA B2)</summary>
/// <param name="data"> The array of bytes to be translated into a string of hex digits. </param>
/// <returns> Returns a well formatted string of hex digits with spacing. </returns>
private string ByteArrayToHexString(byte[] data)
{
StringBuilder sb = new StringBuilder(data.Length * 3);
foreach (byte b in data)
sb.Append(Convert.ToString(b, 16).PadLeft(2, '0').PadRight(3, ' '));
return sb.ToString().ToUpper();
}

/// <summary> Enable/disable controls based on the app's current state. </summary>
private void EnableControls()
{
// Enable/disable controls based on whether the port is open or not
gbPortSettings.Enabled = !comport.IsOpen;
//txtSendData.Enabled = btnSend.Enabled = comport.IsOpen;
//chkDTR.Enabled = chkRTS.Enabled = comport.IsOpen;

if (comport.IsOpen)
{
btnOpenPort.Text = "&Close Port";
button1.Enabled = true;
button2.Enabled = true;
}
else
{
btnOpenPort.Text = "&Open Port";
button1.Enabled = false;
button2.Enabled = false;
}
}

private string[] OrderedPortNames()
{
// Just a placeholder for a successful parsing of a string to an integer
int num;

// Order the serial port names in numberic order (if possible)
return SerialPort.GetPortNames().OrderBy(a => a.Length > 3 && int.TryParse(a.Substring(3), out num) ? num : 0).ToArray();
}

/// <summary> Populate the form's controls with default settings. </summary>
private void InitializeControlValues()
{
cmbParity.Items.Clear(); cmbParity.Items.AddRange(Enum.GetNames(typeof(Pari ty)));
cmbStopBits.Items.Clear(); cmbStopBits.Items.AddRange(Enum.GetNames(typeof(St opBits)));

cmbParity.Text = settings.Parity.ToString();
cmbStopBits.Text = settings.StopBits.ToString();
cmbDataBits.Text = settings.DataBits.ToString();
cmbParity.Text = settings.Parity.ToString();
cmbBaudRate.Text = settings.BaudRate.ToString();

RefreshComPortList();

// If it is still avalible, select the last com port used
if (cmbPortName.Items.Contains(settings.PortName)) cmbPortName.Text = settings.PortName;
else if (cmbPortName.Items.Count > 0) cmbPortName.SelectedIndex = cmbPortName.Items.Count - 1;
else
{
MessageBox.Show(this, "There are no COM Ports detected on this computer.\nPlease install a COM Port and restart this app.", "No COM Ports Installed", MessageBoxButtons.OK, MessageBoxIcon.Error);
this.Close();
}
}

private void tmrCheckComPorts_Tick(object sender, EventArgs e)
{
// checks to see if COM ports have been added or removed
// since it is quite common now with USB-to-Serial adapters
RefreshComPortList();
}

private void RefreshComPortList()
{
// Determain if the list of com port names has changed since last checked
string selected = RefreshComPortList(cmbPortName.Items.Cast<string>(), cmbPortName.SelectedItem as string, comport.IsOpen);

// If there was an update, then update the control showing the user the list of port names
if (!String.IsNullOrEmpty(selected))
{
cmbPortName.Items.Clear();
cmbPortName.Items.AddRange(OrderedPortNames());
cmbPortName.SelectedItem = selected;
}
}

private string RefreshComPortList(IEnumerable<string> PreviousPortNames, string CurrentSelection, bool PortOpen)
{
// Create a new return report to populate
string selected = null;

// Retrieve the list of ports currently mounted by the operating system (sorted by name)
string[] ports = SerialPort.GetPortNames();

// First determain if there was a change (any additions or removals)
bool updated = PreviousPortNames.Except(ports).Count() > 0 || ports.Except(PreviousPortNames).Count() > 0;

// If there was a change, then select an appropriate default port
if (updated)
{
// Use the correctly ordered set of port names
ports = OrderedPortNames();

// Find newest port if one or more were added
string newest = SerialPort.GetPortNames().Except(PreviousPortNames ).OrderBy(a => a).LastOrDefault();

// If the port was already open... (see logic notes and reasoning in Notes.txt)
if (PortOpen)
{
if (ports.Contains(CurrentSelection)) selected = CurrentSelection;
else if (!String.IsNullOrEmpty(newest)) selected = newest;
else selected = ports.LastOrDefault();
}
else
{
if (!String.IsNullOrEmpty(newest)) selected = newest;
else if (ports.Contains(CurrentSelection)) selected = CurrentSelection;
else selected = ports.LastOrDefault();
}
}

// If there was a change to the port list, return the recommended default selection
return selected;
}

private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// Read all the data waiting in the buffer
int data = comport.ReadByte();
// Check data
if (data == 0xAA)
{
flagData = 1;
nIndex++;
return;
}
if ((flagData == 1) & (nIndex == 1))
{
inputData1 = data;
nIndex++;
return;
}
if ((flagData == 1) & (nIndex == 2))
{
inputData2 = data;
flagPoint = 1;
nIndex++;
}
if ((data == 0x55) & (nIndex == 3))
{
nIndex = 0;
flagData = 0;
}
if (flagPoint == 1)
{
m_pointsList.Add((inputData1 * 256 + inputData2) / 10, (inputData1 * 256 + inputData2) * 0.2/10000);
CreateLineGraph();
flagPoint = 0;
}
}

private void cmbBaudRate_Validating(object sender, CancelEventArgs e)
{ int x; e.Cancel = !int.TryParse(cmbBaudRate.Text, out x); }

private void cmbDataBits_Validating(object sender, CancelEventArgs e)
{ int x; e.Cancel = !int.TryParse(cmbDataBits.Text, out x); }

#region Local Properties
private DataMode CurrentDataMode
{
get
{
if (rbHex.Checked) return DataMode.Hex;
else return DataMode.Text;
}
set
{
if (value == DataMode.Text) rbText.Checked = true;
else rbHex.Checked = true;
}
}
#endregion

private void button2_Click(object sender, EventArgs e)
{
CreateLineGraph();
}

private void button1_Click(object sender, EventArgs e)
{
myPane.CurveList.Clear();
m_pointsList.Clear();
// Force a redraw
zedGraphControl1.Invalidate();
}

private void btnClear_Click(object sender, EventArgs e)
{

}
}
}