110,534
社区成员
发帖
与我相关
我的任务
分享
public static IObservable<byte[]> ReceivedMsg(this SerialPort serialPort)
{
Subject<byte[]> subject = new Subject<byte[]>();
var ReceivSource = Observable.FromEventPattern<SerialDataReceivedEventArgs>(serialPort, "DataReceived");
ReceivSource.Subscribe(p =>
{
SerialPort _serialPort =(SerialPort) p.Sender;
var buf = new byte[_serialPort.BytesToRead];
_serialPort.Read(buf, 0, buf.Length);
subject.OnNext(buf);
});
return subject;
}
SerialPort serialPort1=new SerialPort();
serialPort1.PortName = "com1";
serialPort1.BaudRate = 9600;
serialPort1.Open();
byte[] checkdata=new byte[]{0xa5,0x11};
serialPort1.ReceivedMsg()
//因你原来有wait,猜测你不想做粘包判定,想直接先缓存一秒,故这里我也先缓存1秒
.Buffer(TimeSpan.FromMinutes(1000))
// 因缓存了1秒,所以他是list<byte[]>代表可能不是一个包
//SelectMany重新产生一个替代数据,把list<byte[]>重整为首尾相连的一个byte[]
.SelectMany(p => p.SelectMany(c => c).ToObservable())
//因为你要判定连续2个byte,所以这里使用一个平滑移动窗口,重新产生一个窗口数据
//本来可以用传统循环,不管既然展示rx的功能,我干脆全用rx做了
//平滑移动窗口的其实比较有用,前面有人问了,如何计算股票这类的macd,kdj,这个就是
//像移动加权平均,像音频信号处理,包括图像的卷积运算,移动窗口都是常规手段
.Window(2, 1)
//因为这是rx,所以窗口出的数据观察数据,所以再次重整为2个byte的byte[]
.SelectMany(p => p.ToList())
//这里就没啥好说的了,就是byte[]序列相等比较
.Where(p => p.SequenceEqual(checkdata))
//超时设定3秒
.Timeout(TimeSpan.FromSeconds(3))
//如果没有超时,并且收到数据,他会进入下面的订阅,你可以用TaskCompletionSource通知外面有结果了
.Subscribe(p => { MessageBox.Show("3秒内收到了"); });
/// <summary>
/// 带参数的异步任务, Task 参数直接在lamda表达式内部直接使用
/// </summary>
/// <param name="strCombaud">串口的波特率</param>
/// <returns></returns>
private Task<string> TaskScan(string strCombaud)
{
return Task.Run( () =>
{
string ret = "";
foreach(var port in SerialPort.GetPortNames() )
{
try
{
serialPort1.PortName = port;
serialPort1.BaudRate = Convert.ToInt32(strCombaud);
serialPort1.Open();
Task.Delay(1000).Wait();
Console.WriteLine("{0} open suc", port);
if (serialPort1.BytesToRead > 0)
{
byte[] data = new byte[serialPort1.BytesToRead];
serialPort1.Read(data, 0, data.Length);
byte lastByte = data[0];
for(int i = 1; i< data.Length; i++)
{
if(lastByte == 0xa5 && data[i] ==0x11 )
{
ret = port; break;
}
lastByte = data[i];
}
}
serialPort1.Close();
}
catch
{
Console.WriteLine("{0} open failed", port);
}
}
return ret;
});
}
private async void btnComScan_Click(object sender, EventArgs e)
{
string v = await TaskScan( comboBoxComBaud.Text );
if( v != string.Empty )
{
comboBoxComPort.Text = v;
MessageBox.Show("search right serial port , " + v);
}
}
你提到的 RX.NET 这些都是很美妙的编程思想, 也是我一直寻找的, 我再继续理解理解
private Task<string> TaskScan()
{
return Task.Run( () =>
{
foreach(var port in SerialPort.GetPortNames() )
{
try
{
serialPort1.PortName = port;
serialPort1.BaudRate = Convert.ToInt32("460800");
serialPort1.Open();
Task.Delay(1000).Wait();
serialPort1.Close();
Console.WriteLine("{0} open suc", port);
}
catch
{
Console.WriteLine("{0} open failed", port);
}
}
return "";
});
}
private async void btnComScan_Click(object sender, EventArgs e)
{
string v = await TaskScan();
}
搞了个串口扫描的, 数据异步接收部分就不知道怎么整了
public static IObservable<byte[]> ReceivedMsg(this SerialPort serialPort)
{
Subject<byte[]> subject = new Subject<byte[]>();
var ReceivSource = Observable.FromEventPattern<SerialDataReceivedEventArgs>(serialPort, "DataReceived");
ReceivSource.Subscribe(p =>
{
SerialPort _serialPort =(SerialPort) p.Sender;
var buf = new byte[_serialPort.BytesToRead];
_serialPort.Read(buf, 0, buf.Length);
subject.OnNext(buf);
});
return subject;
}
那个扩展方法我简单示意一下,我这里只是接收数据,不做解析。也不做其他异常处理 async Task<bool> test(string com)
{
//这是一个异步消息封装器,用来返回异步消息
TaskCompletionSource<bool> tcs=new TaskCompletionSource<bool>();
SerialPort port = new SerialPort(com);
try
{
//这里还会有些波特率一类的附加设置,你自己完成
port.Open(); //端口占用会直接异常
port.ReceivedMsg //ReceivedMsg 这个我通常写成扩展方法,接收解析消息,并返回rx的Subject<object>
.where(此处可以放一些订阅条件) //where是rx自己的扩展方法,用来过滤条件
.Timeout(此处放超时时间,超时后会触发下面的异常) //timeout也是rx自己的扩展方法,用来设定超时,超时会直接异常
.订阅(p =>
{
//当符后where条件,并且没有超时的情况下,收到消息,告诉外面他成功了
tcs.SetResult(true);
});
port.Write("发送指令");
}
catch (Exception e)
{
//打开占用异常,超时未收到消息异常
tcs.SetResult(false);
}
finally
{
if(port.IsOpen)
port.Close();
}
return await tcs.Task;
}
数据观察源.next(解析好的byte[])
串口发送(协议字节) 数据观察源.订阅条件().订阅处理(p=>{我收到了解析成功的数据,这个过程因为是异步我们通常用 TaskCompletionSource去通知外面});
TaskCompletionSource,CancelToken超时,rx.net 订阅和超时处理
这个就只能先规定一个握手协议 或者叫 发现协议 然后SerialPort.GetPortNames 取得所有串口,然后挨个遍历,发送握手协议,等待对方返回符合期望的数据(带超时处理) 有设备应答退出循环,没有一台设备应答,休眠个几秒,重新遍历,直到找到(或手动放弃)为止 ps:通常情况下设备发现都是这个过程(etcd,consoul这类主动注册另算),比如upnp,ssdp都是如此,不过ssdp是个udp的,情况类似ssdp主机发一个udp广播(查询指定服务名的udp),谁收到udp广播了认为我就是这个服务,就对ssdp返回一个udp响应(当然ssdp协议服务本身也会没事对外发出一个udp广播,告诉你们我存在啊,我是xx服务)