2

我是 F# 新手,需要帮助来解决这个问题。

我有一个字节流,它来自串行端口并在 F# 中表示为序列。流由帧组成,每个帧以 0xFE 开始,以 0xFF 结束。连续传输的字节和同步我必须跳过一些字节直到 0xFE。但是流可能已损坏并且可能缺少 0xFE 或 0xFF。

我有一个通过串口输入迭代的函数。函数代码为:

let getFrame s =
    let r = s |> Seq.skipWhile (fun x->x<>0xFEuy) 
              |> Seq.takeWhile (fun x->x<>0xFFuy)
    if Seq.isEmpty r then r else Seq.skip 1 r

如何重写此代码以跳过字节直到 0xFE 或跳过一定数量的字节,如果没有发生 0xFE 以功能方式返回错误?

对于直到 0xFF 的帧字节也是如此。

4

2 回答 2

2

您可以将Option<'T>类型用作函数的结果,Some x这意味着从输入序列中捕获了某些帧并且None未找到帧数据:

let getFrame s =
    let r = s |> Seq.skipWhile ((<>) 0xFEuy) 
              |> Seq.takeWhile ((<>) 0xFFuy)
    if Seq.isEmpty r then None
    else Some (Seq.skip 1 r)

但是,正确的故障处理实现取决于getFrame函数的语义。例如,如果0xFE存在但不存在0xFF怎么办?这是否意味着这不是数据框?这是否意味着数据帧被拆分为多个序列?等等。

于 2013-08-16T13:06:22.890 回答
1

我认为以下功能可以满足您的需求。输入流由垃圾和帧组成。Gargabe 可以先出现,也可以在帧之间出现。如果垃圾太多或帧太长,则会报告错误。否则返回下一帧。

let [<Literal>] FrameStart = 0xFE
let [<Literal>] FrameEnd = 0xFF

let [<Literal>] MaxGarbageLength = 5
let [<Literal>] MaxFrameLength = 5

type State<'T> =
| Garbage of int
| Frame of int * 'T list

let getFrame stream =
    let getNextRest stream =
        match Seq.isEmpty stream with
        | true -> None
        | false -> Some(Seq.head stream, Seq.skip 1 stream)

    let rec parse state stream =
        match getNextRest stream with
        | None -> None
        | Some(next, rest) ->
            match state with
            | Garbage n when n >= MaxGarbageLength -> None
            | Garbage n ->
                match next with
                | FrameStart -> parse (Frame(0, [])) rest
                | _ -> parse (Garbage(n+1)) rest
            | Frame(n, _) when n >= MaxFrameLength -> None
            | Frame(n, content) ->
                match next with
                | FrameEnd -> Some(content, rest)
                | _ -> parse (Frame(n+1, content @ [next])) rest

    parse (Garbage 0) stream

从流中获取两个帧:

[<Test>]
let ``can parse two frames with garbage in between``() =
    let stream = Seq.ofList [1;2;3;FrameStart;4;5;6;FrameEnd;7;8;FrameStart;9;0;FrameEnd]

    let (frame1, rest) = (getFrame stream).Value
    frame1 |> should equal [4;5;6]
    rest |> should equal [7;8;FrameStart;9;0;FrameEnd]

    let (frame2, rest) = (getFrame rest).Value
    frame2 |> should equal [9;0]
    rest |> should equal []

通过返回 None 来正确检测错误(注意 MaxGarbageLength 为 5,所以下面报错):

[<Test>]
let ``none is returned when there is too much garbage``() =
    let stream = [1;2;3;4;5;6;FrameStart;7;8;9;FrameEnd]
    (getFrame stream).IsNone |> should equal true

这似乎可行,并且应该易于扩展/修改。但对我来说,它看起来像是相当多的代码。欢迎改进。

于 2013-08-18T21:54:40.430 回答