课程设计要求
如下图:
我们选择采用C#进行开发,因为串口有距离限制不方便我们采集数据,所以我们决定用C#做一个TCP的服务器,进行数据观察以及数据采集。
将电脑作为TCP服务器
C#提供了丰富的网络开发API,根据socket通信基本流程图
总结通信的基本步骤:
- 创建一个用于监听连接的Socket对像;
- 用指定的端口号和服务器的ip建立一个EndPoint对像;
- 用socket对像的Bind()方法绑定EndPoint;
- 用socket对像的Listen()方法开始监听;
- 接收到客户端的连接,用socket对像的Accept()方法创建一个新的用于和客户端进行通信的socket对像;
- 第六步:通信结束后一定记得关闭socket;
//创建socket对象
Socket tcpServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
//绑定IP和申请端口
tcpServer.Bind(new IPEndPoint(IPAddress.Any, 8888));
}
catch (Exception ex)
{
MessageBox.Show("错误: ip地址设置失败,请正确连接热点并检查ip地址", "提示",MessageBoxButtons.OK);
t.Abort();//结束本线程
}
server_open_flag = true; //标志服务器开启成功
server_open_change = true; //标志服务器开启状态改变
tcpServer.Listen(100);//设置客户端最大连接数
clientSocket = tcpServer.Accept(); //会一直阻塞等待连接 若没有链接则不会进行
处理接收到的数据
处理接收到的数据就较为简单,我们每个数据包会发送14个字节的数据,也就是两位的校验位和6个int16的数据,分别代表三个轴上的加速度计和陀螺仪数据。具体步骤如下:
- 对收到的首位数据进行判断,若不为帧头,那么在缓冲区数组中移除改数据,并等待下一个数据,以保证数据的准确性。
- 将接收到的byte数据进行移位操作后得到正确的int16的数据。
while (true)
{
int buffer_num = data_buffer.Count; //提前存好,防止处理过程中Count变化
if (buffer_num >= 14) //至少14字节
{
if (data_buffer[0] == 0x5A) //帧头
{
if (data_buffer[1] == 0x01) //判断这一帧是否是心跳波形数据
{
Action act = delegate () {
cur_data.a_x = (Int16)(data_buffer[2] | data_buffer[3] << 8);
cur_data.a_y = (Int16)(data_buffer[4] | data_buffer[5] << 8);
cur_data.a_z = (Int16)(data_buffer[6] | data_buffer[7] << 8);
cur_data.g_x = (Int16)(data_buffer[8] | data_buffer[9] << 8);
cur_data.g_y = (Int16)(data_buffer[10]| data_buffer[11] << 8);
cur_data.g_z = (Int16)(data_buffer[12]| data_buffer[13] << 8);
ecg_add_data(cur_data.a_x, cur_data.a_y, cur_data.a_z, cur_data.g_x, cur_data.g_y, cur_data.g_z);
};
data_save.Add(new datatype(cur_data.a_x, cur_data.a_y, cur_data.a_z, cur_data.g_x, cur_data.g_y, cur_data.g_z));
this.Invoke(act);
data_buffer.RemoveRange(0, 13); //从缓存中删除前14个字节
}
}
else
{
//这里是很重要的,如果数据开始不是帧头,就删除一位
data_buffer.RemoveAt(0);
}
}
if (main_form_close_flag == true)
{
t1.Abort(); //结束本线程
}
}
曲线绘制
图表
我们采用自带的chart控件,在form中点击工具箱搜索即可寻找到该控件。
将chart移动到合适的位置后,我们可以在窗体加载函数中,将chart进行初始化配置,具体代码如下:
ChartArea chartArea = new ChartArea();
chartArea.Name = "FirstArea";
chartArea.CursorX.AutoScroll = true; //是否滚动
chartArea.AxisX.ScrollBar.Enabled = true; //开启滚动条
chartArea.CursorX.IsUserEnabled = true;
chartArea.CursorX.IsUserSelectionEnabled = true;
chartArea.AxisX.ScaleView.Zoomable = true;
chartArea.CursorX.SelectionColor = Color.SkyBlue;
chartArea.CursorY.IsUserEnabled = true;
chartArea.CursorY.AutoScroll = true;
chartArea.CursorY.IsUserSelectionEnabled = true;
chartArea.CursorY.SelectionColor = Color.SkyBlue;
chartArea.CursorX.IntervalType = DateTimeIntervalType.Auto;
chartArea.AxisX.ScrollBar.ButtonStyle = ScrollBarButtonStyles.All;//启用X轴滚动条按钮
chartArea.BackColor = Color.AliceBlue; //背景色
chartArea.BackSecondaryColor = Color.White; //渐变背景色
chartArea.BackGradientStyle = GradientStyle.TopBottom; //渐变方式
chartArea.BackHatchStyle = ChartHatchStyle.None; //背景阴影
chartArea.BorderDashStyle = ChartDashStyle.NotSet; //边框线样式
chartArea.BorderWidth = 1; //边框宽度
chartArea.BorderColor = Color.Black;
chartArea.AxisX.MajorGrid.Enabled = false;
chartArea.AxisY.MajorGrid.Enabled = false;
// Axis
chartArea.AxisY.Title = @"数值";
chartArea.AxisY.LabelAutoFitMinFontSize = 5;
chartArea.AxisY.LineWidth = 2;
chartArea.AxisY.LineColor = Color.Black;
chartArea.AxisY.Enabled = AxisEnabled.True;
chartArea.AxisX.Title = @"时间";
chartArea.AxisX.IsLabelAutoFit = true;
chartArea.AxisX.LabelAutoFitMinFontSize = 5;
chartArea.AxisX.LabelStyle.Angle = -15;
chartArea.AxisX.LabelStyle.IsEndLabelVisible = true; //show the last label
chartArea.AxisX.Interval = 10;
chartArea.AxisX.IntervalAutoMode = IntervalAutoMode.FixedCount;
chartArea.AxisX.IntervalType = DateTimeIntervalType.NotSet;
chartArea.AxisX.TextOrientation = TextOrientation.Auto;
chartArea.AxisX.LineWidth = 2;
chartArea.AxisX.LineColor = Color.Black;
chartArea.AxisX.Enabled = AxisEnabled.True;
chartArea.AxisX.ScaleView.MinSizeType = DateTimeIntervalType.Months;
chartArea.AxisX.Crossing = 0;
chartArea.Position.Height = 85;
chartArea.Position.Width = 85;
chartArea.Position.X = 5;
chartArea.Position.Y = 7;
chart_ecg.ChartAreas.Add(chartArea);
chart_ecg.BackGradientStyle = GradientStyle.TopBottom;
//图表的边框颜色、
chart_ecg.BorderlineColor = Color.FromArgb(26, 59, 105);
//图表的边框线条样式
chart_ecg.BorderlineDashStyle = ChartDashStyle.Solid;
//图表边框线条的宽度
chart_ecg.BorderlineWidth = 2;
//图表边框的皮肤
//chart_ecg.BorderSkin.SkinStyle = BorderSkinStyle.FrameThin3;
chart_ecg.ChartAreas[0].AxisX.Interval = 0.5; //设置横坐标的分辨率
chart_ecg.ChartAreas[0].AxisX.ScaleView.Size = 1000; //设置横坐标长度
曲线
除了图表,我们还需要绘制曲线,我们采用Series进行绘制,一共需要6条曲线,初始化操作相同,下面就给出一条曲线的初始化操作:
series1 = new Series();
series1.ChartArea = "FirstArea";
chart_ecg.Series.Add(series1);
//Series1 style
series1.ToolTip = "#VALX,#VALY"; //鼠标停留在数据点上,显示XY值
series1.Name = "a_x";
series1.ChartType = SeriesChartType.Line; // type:折线
series1.BorderWidth = 2;
series1.Color = Color.Black;
series1.XValueType = ChartValueType.Time;//x axis type
series1.YValueType = ChartValueType.Int32;//y axis type
//Marker
series1.MarkerStyle = MarkerStyle.Square;
series1.MarkerSize = 2;
series1.MarkerColor = Color.Black;
然后就是绘制曲线了,代码如下:
series1.Points.AddXY(osc_x, data1);
最后还有选择显示的曲线,直接用check控件即可,代码如下:
series1.Enabled = check1.Checked;
综上,我们的曲线绘制部分就完成了。
文件写入
为了方便数据的处理,需要将接受到的数据写入到文件中,具体实现步骤如下:
- 将接收到的数据写入缓冲区
- 将缓冲区的数据写入到文件
为了方便数据存储以及写入,我定义了一个类(偷懒了,写的不严谨):
class datatype
{
public Int16 a_x, a_y, a_z;
public Int16 g_x, g_y, g_z;
public datatype(Int16 ax = 0, Int16 ay = 0, Int16 az = 0, Int16 gx = 0, Int16 gy = 0, Int16 gz = 0)
{
this.a_x = ax;
this.a_y = ay;
this.a_z = az;
this.g_x = gx;
this.g_y = gy;
this.g_z = gz;
}
public string data_to_string()
{
return a_x + "," + a_y + "," + a_z + "," + g_x + "," + g_y + "," + g_z ;
}
}
定义一个缓冲区,用于存放数据:
private List<datatype> data_save = new List<datatype>();
写入缓冲区很简单,直接写入该list即可。
然后便是写入文件:
if (data_save.Count == 0)
{
MessageBox.Show("错误:缓存区为空", "提示", MessageBoxButtons.OK);
return;
}
//文件名为当前时间
string Child_Address = Save_File_Address + "\\" + DateTime.Now.ToString().Replace(@"/", ".").Replace(":", ".") + ".dyy";//创建文件
FileStream fs = new FileStream(Child_Address, FileMode.OpenOrCreate, FileAccess.ReadWrite);
StreamWriter sw = new StreamWriter(fs); // 创建写入baidu流
for (int i = 0; i < data_save.Count; i++)
{
sw.WriteLine(data_save[i].data_to_string());
}
//删除缓存区数据
data_save.RemoveRange(0, data_save.Count);
sw.Close(); //关闭文件
最后加个按键控制就完事了。
工程链接
工程链接:https://pan.baidu.com/s/11celASODrhJEwRoCH_7OVQ
提取码:dfds