将接收到的串行数据事件处理程序添加到f#串行端口读取器

时间:2018-11-13 13:49:01

标签: f#

我在f#中有简单的读写指令以在串行端口之间进行通信:

        async {
            do! port.AsyncWriteLineAsByte messange            
            let! response = port.AsyncReadLineAsByte() 

            return response
        }

其中响应​​是byte [],方法很简单:

    member this.AsyncReadLineAsByte() : Async<byte[]> =
        async { 

            let buffer_ref = ref (Array.zeroCreate<byte> this.ReadBufferSize)
            let buffer = !buffer_ref

            let! read_bytes = this.BaseStream.AsyncRead(buffer, 0, this.ReadBufferSize)

            return buffer
        }

它运行良好,可以发送和接收消息,但所有这些都只有一个。 阅读响应应该是某种事件。我是F#的新手,但是我尝试过类似的操作:

    async {

        let data_recived_event = port.AsyncReadLineAsByte() 

        do! port.AsyncWriteLineAsByte messange  

        port.DataReceived.AddHandler(data_recived_event) //  it says SerialDataReciveHandler is what he expects

        let! response = ??? 

        return response
    }

但是幸运的是,文档却是,对于f#,它仅指定原型和方法构造,而并非实际用途。我需要一个事件和一种返回该值的方法,有办法吗?

编辑:

我能够添加事件,因为串行端口名称空间具有DataReceived.AddHandler事件订阅。

现在看起来:

        async {

        let data_recived() =
            async{
                let! buffer = port.AsyncReadLineAsByte()
                printfn "Response from event %A" buffer
                // return buffer
            } |> fun response -> Async.RunSynchronously(response)

        port.DataReceived.AddHandler(fun _ _ -> data_recived())

        do! port.AsyncWriteLineAsByte messange  
        let! response = port.AsyncReadLineAsByte()            

        return response
    }

而且有效,问题仍然是如何从事件中返回这样的值,如果我执行以下操作:

            let data_recived() =
                async{
                    let! buffer = port.AsyncReadLineAsByte()

                    printfn "Response from event %A" buffer
                    return buffer
                } |> fun response -> Async.RunSynchronously(response)

            port.DataReceived.AddHandler(fun _ _ -> response = data_recived())

是说它期望uint并得到bool

1 个答案:

答案 0 :(得分:2)

我不是串行端口如何工作的专家,但是您可以使用async操作在Async.AwaitEvent工作流程中等待事件。因此,您可以编写如下内容:

let writeAndRead () = async {
  let port = new SerialPort("COM1")
  port.Write("TEST")
  let mutable finished = false
  while not finished do
    let! e = port.DataReceived |> Async.AwaitEvent
    let data = port.ReadExisting()
    printfn "GOT: %A" data
    finished <- data.Contains("EOF") }

这里唯一需要注意的是,DataReceived事件可能在您正在循环主体中处理接收到的数据时并发触发-然后您将错过该事件。我不确定串行端口如何工作以及这种情况是否会真正发生,但这可能会导致问题。

要解决此问题,您可以使用F#Async Extras中的BlockingQueueAgent type。这将使您实现通知队列-因此DataReceived处理程序会将通知添加到队列,然后在循环中从队列中读取它们。我还没有实际测试过,但是我认为这样应该可以工作:

let writeAndRead () = async {
  let queue = BlockingQueueAgent<_>(Int32.MaxValue)
  let port = new SerialPort("COM1")
  port.DataReceived.Add(fun e -> queue.Add(e))
  port.Write("TEST")
  let mutable finished = false
  while not finished do
    let! e = queue.AsyncGet()
    let data = port.ReadExisting()
    finished <- data.Contains("EOF") }

编辑:在将任何数据写入端口之前,移动了queue事件处理程序设置。