假设现在我们要实现这样一个功能:我们需要让电机某轴(或几轴)循环往复运动,并每次循环获取电机运动结束位置,记录到csv文件中,每个循环结束后(即到达起点),我们要在textBox上写入循环次数。
我们有toPoint(axis,point,velocity),waitMotorEnd(axis,timeout);getMotorPos(axis),
killMotor(axis)
一. 第一种方法是添加一个Timer控件,设置Interval间隔,添加tick事件,在这个事件中编程和调用其他函数,用两个按钮和几个变量控制tick是否开启和关闭。
点评:这种方法简单易行,它可以访问任何控件,但是主线程已经有一个timer控件实时读取电机位置并显示出来,导致主界面在电机运动期间是卡死状态,包括最小化等界面图标均无法选中,因为这个timer控件本质上是主线程上的
二. 通过 system.Timers使用系统timer,这种方法不用在界面上拖拽控件,相应的需要在代码中设置属性。
首先我们先考虑主循环的事件处理,然后再考虑主线程控件调用问题,因为是线程安全的,这个线程是不能直接读取主线程中的控件,需要启用委托。
代码简单如下,仔细弄明白以便在自己的代码中进行移植。
using Systems.Timers;
using System.Threading;
namespace demo_np
{
public partial class MainForm : Form
{
private int count_cycle=0; //循环次数要填入textBox中,全局变量。
private string path = @"D:\MOTOR_CSV\info.csv";//写入csv的目录,文件在代码中建立。
public MainForm()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
InitTimer(); //这个是我们设置的timer属性初始化函数。
}
System.Timers.Timer timer;//实例化一个计时器
private void InitTimer()
{
//设置定时间隔,本例较大,因为太小的话代码执行太快,电机还没动起来导致程序总是认为你电机已经循环过。
int interval = 2000;
timer = new System.Timers.Timer(interval);
//设置执行一次(false)还是一直执行(true)
timer.AutoReset = true;
timer.Enabled = false;
//绑定Elapsed事件,让计时器能够不断执行这个TimerCycle事件
timer.Elapsed += new System.Timers.ElapsedEventHandler(TimerCycle);
}
private void TimerCycle(object sender, System.Timers.ElapsedEventArgs e)
{
try
{
timer.Enabled = false;//这里先关闭计时器,事件结尾再打开
int timeout = 1000000;
waitMotorEnd(0,timeout);代表0轴
write_csv();
toPoint(0,100.0f);
toPoint(0,0.0f);//代表一个往复循环
count_cycle = (count_cycle + 1) % 1000000;
//text_count.Text=count_cycle.ToString();非法调用主线程控件。
timer.Enabled = true;
}
catch(Exception ex)
{
MessageBox.Show(ex.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
System.Diagnostics.Debug.WriteLine(ex.Message);
}
//接下来是按钮控制开关及写入csv方法
private void btnCycleStart_Click(object sender, EventArgs e)
{
count_cycle=0;
timer.Enabled = true;
if (!File.Exists(path))
File.Create(path).Close();//创建csv文件。
}
private void button10_Click(object sender, EventArgs e)
{
killMotor(0);
timer.Enabled = false;
}
private void write_csv()
{
StreamWriter sw = new StreamWriter(path, true, Encoding.UTF8);
sw.Write("\r\n");
sw.Write(String.Format("{0:0.0000}", getMotorPos(0) + ",");
sw.Write(String.Format("{0:0.0000}", getMotorPos(0)) + ",");
sw.Flush();
sw.Close();
}
}
}
上述代码非法调用控件,接下来通过委托实现访问,注意和上面代码对照需要加到哪里?
delegate void text_countDelegate();
private void text_count()
{
this.text_count.Text = cycle_count.ToString();
}
//这里只是定义了这个委托,名字叫text_countDelegate,既没有运行也没有和咱们这个textcount事件联系起来,那怎么办呢?
textBox4.Invoke((txb4countDelegate)txb4count);
我们需要将这行代码加到TimerCycle非法调用那里,就可以成功访问啦
上面这种多线程访问控件的方法好处是可以选择加参数,但是这个例子中并没有参数传递,c#给了我们这样一种方法,MethodInvoker,表示一个委托,该委托可以执行托管代码中(声明为void且不接受任何参数的)任何方法!
this.Invoke((MethodInvoker)delegate
{
textCount.Text=cycle_count.ToString();
}
欢迎任何评论,有不当的地方欢迎提出来,共同交流共同进步!