2009-05-04 31 views
1

これはやや長い質問ですので、私と一緒に裸にしてください。このデータ解析の問題をどのように解決しますか?

が同時に開発されているハードウェアのエミュレータを実装しています。考え方は、サードパーティの にクライアントソフトウェアをテストし、ハードウェアにファームウェアを実装するためのリファレンスポイント を与えるソフトウェアソリューションを与えることです。

ハードウェアのプロトコルを書いた人は、INCC_XDRと呼ばれるSUN XDRのカスタム バージョンを使用しました。それはシリアル化して メッセージを逆シリアル化するツールです。これはC言語で記述されており、 のネイティブコードを避けたいので、プロトコルデータを手動で解析しています。

プロトコルは、本質的にかなり複雑であり、データパケット は、多くの異なる構造を持つことができますが、それは常に同じグローバルな構造を有する:

[HEAD] [INTRO] [DATA] [TAIL]

[HEAD] = 
    byte sync 0x03 
    byte length X  [MSB]  X = length of [HEADER] + [INTRO] + [DATA] 
    byte length X  [LSB]  X = length of [HEADER] + [INTRO] + [DATA] 
    byte check X  [MSB]  X = crc of [INTRO] [DATA] 
    byte check X  [LSB]  X = crc of [INTRO] [DATA] 
    byte headercheck X    X = XOR over [SYNC] [LENGTH] [CHECK] 

[INTRO] 
    byte version 0x03 
    byte address X     X = 0 for point-to-point, 1-254 for specific controller, 255 = broadcast 
    byte sequence X     X = sequence number 
    byte group X  [MSB]  X = The category of the message 
    byte group X  [LSB]  X = The category of the message 
    byte type X   [MSB]  X = The id of the message 
    byte type X   [LSB]  X = The id of the message 

[DATA] = 
    The actuall data for the specified message, 
    this format really differs a lot. 

    It always starts with a DRCode which is one byte. 
    It more or less specifies the general structure of 
    the data, but even within the same structure the data 
    can mean many different things and have different lenghts. 
    (I think this is an artifact of the INCA_XDR tool) 

[TAIL] = 
    byte 0x0D 

あなたがそこにオーバーヘッドデータがたくさんあるが、 プロトコルはRS232(ポイント・ツー・マルチポイント)およびTCP/IP(P2P)の両方で動作する必要があるため、これは見ることができるように。

name  size value 
    drcode  1  1 
    name  8    contains a name that can be used as a file name (only alphanumeric characters allowed) 
    timestamp 14    yyyymmddhhmmss contains timestamp of bitmap library 
    size  4    size of bitmap library to be loaded 
    options  1    currently no options 

それとも、全く異なる構造を持っているかもしれません:

name  size value 
    drcode  1  2 
    lastblock 1  0 - 1 1 indicates last block. Firmware can be stored 
    blocknumber 2    Indicates block of firmware 
    blocksize 2  N  size of block to load 
    blockdata N    data of block of firmware 

は、時にはそれだけでDRCodeと追加データです。

グループとタイプフィールドに基づいて、エミュレータ は特定のアクションを実行する必要があります。だから最初に我々はこれらのフィールドを の2つのフィールドで調べ、それに基づいてデータ の期待値を知り、それを適切に解析しなければなりません。

さらに、 多くの異なるデータ構造を持つ応答データを生成する必要があります。メッセージによっては、単にACKメッセージまたはNACKメッセージを として生成するものもあれば、データで実際の応答を生成するものもあります。

私たちは物事を細かく分割することに決めました。

まず、IDataProcessorがあります。

生データを検証し、Messageクラスのインスタンスを生成するために、このインタフェースを実装するクラスは です。 彼らは通信の責任を負いません、単純にbyte []を渡します。

生データの検証とは、チェックサム、crc、および長さエラーのヘッダーをチェックすることです。

結果メッセージは、IMessageProcessorを実装するクラスに渡されます。 IDataProcessorに応答メッセージまたはその他の何らかの理由がないという理由で、生データが無効と見なされたとしても、生データは検証されます。エラーに関するIMessageProcessorを知らせるために

、いくつかの追加のプロパティは、Messageクラスに を追加されています

bool nakError = false; 
bool tailError = false; 
bool crcError = false; 
bool headerError = false; 
bool lengthError = false; 

これらはプロトコルに関連していないとだけIMessageProcessor

IMessageProcessorのために存在しています実際の作業が行われます。 パターンマッチング はネストされたif/elseおよびcasteステートメントをたくさん避けるため、F#を使用してIMessageProcessorインターフェイスを実装しました。 は

IMessageProcessorは、データを分析し、それがIHardwareControllerに を呼び出す必要がありますどのような方法を決定する(私はF#やLINQ以外にも関数型言語とSQLとの事前の経験を持っていません)。私は、IHardwareController、 を持っていると重複しているかもしれませんが、別の実装 と交換して、F#を使用することはできません。現在の実装は、WPFウィンドウの ですが、これはCocoa#ウィンドウまたは単純にコンソールなどです。

ユーザーインターフェイスを介してハードウェアパラメータとエラーを操作できる必要があるため、IHardwareControllerは状態の管理も担当します。

IMessageProcessorがIHardwareControllerで正しいメソッドを呼び出したら、 応答MEssageを生成する必要があります。これらの応答メッセージのデータは、多くの異なる構造を持つことができます。

最終的にIDataFactoryを使用して、メッセージを生のプロトコルデータ に変換し、通信を担当するクラスに送信する準備ができます。 (データの追加のカプセル化は、例えば必要になる場合があります)

これは、このコードを書くことについて「ハード」は何もありませんが、すべて異なる コマンドやデータ構造はコードの多くと多くを必要とし、いくつかの 物事は我々があります再利用できます。 (少なくとも私が今見ている限り、誰かが私を間違っていると思うことを望みます)

私はF#を使うのは初めてです。以下のコードは完成していません。 おそらく巨大な混乱のようです。それはプロトコル のすべてのメッセージのhandfullを実装し、私はそれらの多くがたくさんあることを伝えることができます。だからこのファイルは巨大になるだろう!

知ることが重要

:バイト順は(歴史的な理由)

module Arendee.Hardware.MessageProcessors 

open System; 
open System.Collections 
open Arendee.Hardware.Extenders 
open Arendee.Hardware.Interfaces 
open System.ComponentModel.Composition 
open System.Threading 
open System.Text 

let VPL_NOERROR = (uint16)0 
let VPL_CHECKSUM = (uint16)1 
let VPL_FRAMELENGTH = (uint16)2 
let VPL_OUTOFSEQUENCE = (uint16)3 
let VPL_GROUPNOTSUPPORTED = (uint16)4 
let VPL_REQUESTNOTSUPPORTED = (uint16)5 
let VPL_EXISTS = (uint16)6 
let VPL_INVALID = (uint16)7 
let VPL_TYPERROR = (uint16)8 
let VPL_NOTLOADING = (uint16)9 
let VPL_NOTFOUND = (uint16)10 
let VPL_OUTOFMEM = (uint16)11 
let VPL_INUSE = (uint16)12 
let VPL_SIZE = (uint16)13 
let VPL_BUSY = (uint16)14 
let SYNC_BYTE = (byte)0xE3 
let TAIL_BYTE = (byte)0x0D 
let MESSAGE_GROUP_VERSION = 3uy 
let MESSAGE_GROUP = 701us 


[<Export(typeof<IMessageProcessor>)>] 
type public StandardMessageProcessor() = class 
    let mutable controller : IHardwareController = null    

    interface IMessageProcessor with 
     member this.ProcessMessage m : Message = 
      printfn "%A" controller.Status 
      controller.Status <- ControllerStatusExtender.DisableBit(controller.Status,ControllerStatus.Nak) 

      match m with 
      | m when m.LengthError -> this.nakResponse(m,VPL_FRAMELENGTH) 
      | m when m.CrcError -> this.nakResponse(m,VPL_CHECKSUM) 
      | m when m.HeaderError -> this.nakResponse(m,VPL_CHECKSUM) 
      | m -> this.processValidMessage m 
      | _ -> null  

     member public x.HardwareController 
      with get() = controller 
      and set y = controller <- y     
    end 

    member private this.processValidMessage (m : Message) = 
     match m.Intro.MessageGroup with 
     | 701us -> this.processDefaultGroupMessage(m); 
     | _ -> this.nakResponse(m, VPL_GROUPNOTSUPPORTED); 

    member private this.processDefaultGroupMessage(m : Message) = 
     match m.Intro.MessageType with 
     | (1us) -> this.firmwareVersionListResponse(m)      //ListFirmwareVersions    0 
     | (2us) -> this.StartLoadingFirmwareVersion(m)      //StartLoadingFirmwareVersion  1 
     | (3us) -> this.LoadFirmwareVersionBlock(m)      //LoadFirmwareVersionBlock   2 
     | (4us) -> this.nakResponse(m, VPL_FRAMELENGTH)      //RemoveFirmwareVersion    3 
     | (5us) -> this.nakResponse(m, VPL_FRAMELENGTH)      //ActivateFirmwareVersion   3   
     | (12us) -> this.nakResponse(m,VPL_FRAMELENGTH)      //StartLoadingBitmapLibrary   2 
     | (13us) -> this.nakResponse(m,VPL_FRAMELENGTH)      //LoadBitmapLibraryBlock   2   
     | (21us) -> this.nakResponse(m, VPL_FRAMELENGTH)      //ListFonts       0 
     | (22us) -> this.nakResponse(m, VPL_FRAMELENGTH)      //LoadFont       4 
     | (23us) -> this.nakResponse(m, VPL_FRAMELENGTH)      //RemoveFont      3 
     | (24us) -> this.nakResponse(m, VPL_FRAMELENGTH)      //SetDefaultFont     3   
     | (31us) -> this.nakResponse(m, VPL_FRAMELENGTH)      //ListParameterSets     0 
     | (32us) -> this.nakResponse(m, VPL_FRAMELENGTH)      //LoadParameterSets     4 
     | (33us) -> this.nakResponse(m, VPL_FRAMELENGTH)      //RemoveParameterSet    3 
     | (34us) -> this.nakResponse(m, VPL_FRAMELENGTH)      //ActivateParameterSet    3 
     | (35us) -> this.nakResponse(m, VPL_FRAMELENGTH)      //GetParameterSet     3   
     | (41us) -> this.nakResponse(m, VPL_FRAMELENGTH)      //StartSelfTest      0 
     | (42us) -> this.returnStatus(m)          //GetStatus       0 
     | (43us) -> this.nakResponse(m, VPL_FRAMELENGTH)      //GetStatusDetail     0 
     | (44us) -> this.ResetStatus(m)      //ResetStatus      5 
     | (45us) -> this.nakResponse(m, VPL_FRAMELENGTH)      //SetDateTime      6 
     | (46us) -> this.nakResponse(m, VPL_FRAMELENGTH)      //GetDateTime      0 
     | _ -> this.nakResponse(m, VPL_REQUESTNOTSUPPORTED) 



    (* The various responses follow *) 

    //Generate a NAK response 
    member private this.nakResponse (message : Message , error) = 
     controller.Status <- controller.Status ||| ControllerStatus.Nak 
     let intro = new MessageIntro() 
     intro.MessageGroupVersion <- MESSAGE_GROUP_VERSION 
     intro.Address <- message.Intro.Address 
     intro.SequenceNumber <- this.setHigh(message.Intro.SequenceNumber) 
     intro.MessageGroup <- MESSAGE_GROUP 
     intro.MessageType <- 130us 
     let errorBytes = UShortExtender.ToIntelOrderedByteArray(error) 
     let data = Array.zero_create(5) 
     let x = this.getStatusBytes 
     let y = this.getStatusBytes 
     data.[0] <- 7uy 
     data.[1..2] <- this.getStatusBytes 
     data.[3..4] <- errorBytes  
     let header = this.buildHeader intro data 
     let message = new Message() 
     message.Header <- header 
     message.Intro <- intro 
     message.Tail <- TAIL_BYTE 
     message.Data <- data 
     message 

    //Generate an ACK response 
    member private this.ackResponse (message : Message) = 
     let intro = new MessageIntro() 
     intro.MessageGroupVersion <- MESSAGE_GROUP_VERSION 
     intro.Address <- message.Intro.Address 
     intro.SequenceNumber <- this.setHigh(message.Intro.SequenceNumber) 
     intro.MessageGroup <- MESSAGE_GROUP 
     intro.MessageType <- 129us 
     let data = Array.zero_create(3); 
     data.[0] <- 0x05uy 
     data.[1..2] <- this.getStatusBytes 
     let header = this.buildHeader intro data 
     message.Header <- header 
     message.Intro <- intro 
     message.Tail <- TAIL_BYTE 
     message.Data <- data 
     message   

    //Generate a ReturnFirmwareVersionList 
    member private this.firmwareVersionListResponse (message : Message) = 
     //Validation 
     if message.Data.[0] <> 0x00uy then 
      this.nakResponse(message,VPL_INVALID) 
     else 
      let intro = new MessageIntro() 
      intro.MessageGroupVersion <- MESSAGE_GROUP_VERSION 
      intro.Address <- message.Intro.Address 
      intro.SequenceNumber <- this.setHigh(message.Intro.SequenceNumber) 
      intro.MessageGroup <- MESSAGE_GROUP 
      intro.MessageType <- 132us  
      let firmwareVersions = controller.ReturnFirmwareVersionList(); 
      let firmwareVersionBytes = BitConverter.GetBytes((uint16)firmwareVersions.Count) |> Array.rev 

      //Create the data 
      let data = Array.zero_create(3 + (int)firmwareVersions.Count * 27) 
      data.[0] <- 0x09uy        //drcode 
      data.[1..2] <- firmwareVersionBytes    //Number of firmware versions 

      let mutable index = 0 
      let loops = firmwareVersions.Count - 1 
      for i = 0 to loops do 
       let nameBytes = ASCIIEncoding.ASCII.GetBytes(firmwareVersions.[i].Name) |> Array.rev 
       let timestampBytes = this.getTimeStampBytes firmwareVersions.[i].Timestamp |> Array.rev 
       let sizeBytes = BitConverter.GetBytes(firmwareVersions.[i].Size) |> Array.rev 

       data.[index + 3 .. index + 10] <- nameBytes 
       data.[index + 11 .. index + 24] <- timestampBytes 
       data.[index + 25 .. index + 28] <- sizeBytes 
       data.[index + 29] <- firmwareVersions.[i].Status 
       index <- index + 27    

      let header = this.buildHeader intro data 
      message.Header <- header 
      message.Intro <- intro 
      message.Data <- data 
      message.Tail <- TAIL_BYTE 
      message 

    //Generate ReturnStatus 
    member private this.returnStatus (message : Message) = 
     //Validation 
     if message.Data.[0] <> 0x00uy then 
      this.nakResponse(message,VPL_INVALID) 
     else 
      let intro = new MessageIntro() 
      intro.MessageGroupVersion <- MESSAGE_GROUP_VERSION 
      intro.Address <- message.Intro.Address 
      intro.SequenceNumber <- this.setHigh(message.Intro.SequenceNumber) 
      intro.MessageGroup <- MESSAGE_GROUP 
      intro.MessageType <- 131us 

      let statusDetails = controller.ReturnStatus(); 

      let sizeBytes = BitConverter.GetBytes((uint16)statusDetails.Length) |> Array.rev 

      let detailBytes = ASCIIEncoding.ASCII.GetBytes(statusDetails) |> Array.rev 

      let data = Array.zero_create(statusDetails.Length + 5) 
      data.[0] <- 0x08uy 
      data.[1..2] <- this.getStatusBytes 
      data.[3..4] <- sizeBytes //Details size 
      data.[5..5 + statusDetails.Length - 1] <- detailBytes 

      let header = this.buildHeader intro data 
      message.Header <- header 
      message.Intro <- intro 
      message.Data <- data 
      message.Tail <- TAIL_BYTE 
      message 

    //Reset some status bytes  
    member private this.ResetStatus (message : Message) = 
     if message.Data.[0] <> 0x05uy then 
      this.nakResponse(message, VPL_INVALID) 
     else   
      let flagBytes = message.Data.[1..2] |> Array.rev 
      let flags = Enum.ToObject(typeof<ControllerStatus>,BitConverter.ToInt16(flagBytes,0)) :?> ControllerStatus 
      let retVal = controller.ResetStatus flags 

      if retVal <> 0x00us then 
       this.nakResponse(message,retVal) 
      else 
       this.ackResponse(message) 

    //StartLoadingFirmwareVersion (Ack/Nak) 
    member private this.StartLoadingFirmwareVersion (message : Message) = 
     if (message.Data.[0] <> 0x01uy) then 
      this.nakResponse(message, VPL_INVALID) 
     else 
      //Analyze the data 
      let name = message.Data.[1..8] |> Array.rev |> ASCIIEncoding.ASCII.GetString 
      let text = message.Data.[9..22] |> Array.rev |> Seq.map(fun x -> ASCIIEncoding.ASCII.GetBytes(x.ToString()).[0]) |> Seq.to_array |> ASCIIEncoding.ASCII.GetString 
      let timestamp = DateTime.ParseExact(text,"yyyyMMddHHmmss",Thread.CurrentThread.CurrentCulture) 

      let size = BitConverter.ToUInt32(message.Data.[23..26] |> Array.rev,0) 
      let overwrite = 
       match message.Data.[27] with 
       | 0x00uy -> false 
       | _ -> true 

      //Create a FirmwareVersion instance 
      let firmware = new FirmwareVersion(); 
      firmware.Name <- name 
      firmware.Timestamp <- timestamp 
      firmware.Size <- size 

      let retVal = controller.StartLoadingFirmwareVersion(firmware,overwrite) 

      if retVal <> 0x00us then 
       this.nakResponse(message, retVal) //The controller denied the request 
      else 
       this.ackResponse(message); 

    //LoadFirmwareVersionBlock (ACK/NAK) 
    member private this.LoadFirmwareVersionBlock (message : Message) = 
     if message.Data.[0] <> 0x02uy then 
      this.nakResponse(message, VPL_INVALID) 
     else 
      //Analyze the data 
      let lastBlock = 
       match message.Data.[1] with 
       | 0x00uy -> false 
       | _true -> true 

      let blockNumber = BitConverter.ToUInt16(message.Data.[2..3] |> Array.rev,0)    
      let blockSize = BitConverter.ToUInt16(message.Data.[4..5] |> Array.rev,0) 
      let blockData = message.Data.[6..6 + (int)blockSize - 1] |> Array.rev 

      let retVal = controller.LoadFirmwareVersionBlock(lastBlock, blockNumber, blockSize, blockData) 

      if retVal <> 0x00us then 
       this.nakResponse(message, retVal) 
      else 
       this.ackResponse(message) 


    (* Helper methods *) 
    //We need to convert the DateTime instance to a byte[] understood by the device "yyyymmddhhmmss" 
    member private this.getTimeStampBytes (date : DateTime) = 
     let stringNumberToByte s = Byte.Parse(s.ToString()) //Casting to (byte) would give different results 

     let yearString = date.Year.ToString("0000") 
     let monthString = date.Month.ToString("00") 
     let dayString = date.Day.ToString("00") 
     let hourString = date.Hour.ToString("00") 
     let minuteString = date.Minute.ToString("00") 
     let secondsString = date.Second.ToString("00") 

     let y1 = stringNumberToByte yearString.[0] 
     let y2 = stringNumberToByte yearString.[1] 
     let y3 = stringNumberToByte yearString.[2] 
     let y4 = stringNumberToByte yearString.[3] 
     let m1 = stringNumberToByte monthString.[0] 
     let m2 = stringNumberToByte monthString.[1] 
     let d1 = stringNumberToByte dayString.[0] 
     let d2 = stringNumberToByte dayString.[1] 
     let h1 = stringNumberToByte hourString.[0] 
     let h2 = stringNumberToByte hourString.[1] 
     let min1 = stringNumberToByte minuteString.[0] 
     let min2 = stringNumberToByte minuteString.[1] 
     let s1 = stringNumberToByte secondsString.[0] 
     let s2 = stringNumberToByte secondsString.[1] 

     [| y1 ; y2 ; y3 ; y4 ; m1 ; m2 ; d1 ; d2 ; h1 ; h2 ; min1 ; min2 ; s1; s2 |] 

    //Sets the high bit of a byte to 1 
    member private this.setHigh (b : byte) : byte = 
     let array = new BitArray([| b |]) 
     array.[7] <- true 
     let mutable converted = [| 0 |] 
     array.CopyTo(converted, 0); 
     (byte)converted.[0] 

    //Build the header of a Message based on Intro + Data 
    member private this.buildHeader (intro : MessageIntro) (data : byte[]) = 
     let headerLength = 7; 
     let introLength = 7; 
     let length = (uint16)(headerLength + introLength + data.Length) 
     let crcData = ByteArrayExtender.Concat(intro.GetRawData(),data) 
     let crcValue = ByteArrayExtender.CalculateCRC16(crcData) 
     let lengthBytes = UShortExtender.ToIntelOrderedByteArray(length); 
     let crcValueBytes = UShortExtender.ToIntelOrderedByteArray(crcValue); 
     let headerChecksum = (byte)(SYNC_BYTE ^^^ lengthBytes.[0] ^^^ lengthBytes.[1] ^^^ crcValueBytes.[0] ^^^ crcValueBytes.[1]) 
     let header = new MessageHeader(); 
     header.Sync <- SYNC_BYTE 
     header.Length <- length 
     header.HeaderChecksum <- headerChecksum 
     header.DataChecksum <- crcValue 
     header 

    member private this.getStatusBytes = 
     let l = controller.Status 
     let status = (uint16)controller.Status 
     let statusBytes = BitConverter.GetBytes(status); 
     statusBytes |> Array.rev 

end 

(実際のソースでは、クラスは「ハードウェア」よりも具体的な別の名前を持っていることに注意してください)ワイヤ上で逆転され

私は、提案、コードを改善する方法、または問題を処理するさまざまな方法を期待しています。 たとえば、IronPythonなどの動的言語を使用すると、作業が簡単になります。 私は間違った方法ですべて一緒に行きます。このような問題のあなたの経験は何ですか? あなたは何を変えて、避けていますか?

更新:

ブライアンによって回答に基づいて、私は次のことを書き留め:

type DrCode9Item = {Name : string ; Timestamp : DateTime ; Size : uint32; Status : byte} 
type DrCode11Item = {Id : byte ; X : uint16 ; Y : uint16 ; SizeX : uint16 ; SizeY : uint16 
        Font : string ; Alignment : byte ; Scroll : byte ; Flash : byte} 
type DrCode12Item = {Id : byte ; X : uint16 ; Y : uint16 ; SizeX : uint16 ; SizeY : uint16} 
type DrCode14Item = {X : byte ; Y : byte} 

type DRType = 
| DrCode0 of byte 
| DrCode1 of byte * string * DateTime * uint32 * byte 
| DrCode2 of byte * byte * uint16 * uint16 * array<byte> 
| DrCode3 of byte * string 
| DrCode4 of byte * string * DateTime * byte * uint16 * array<byte> 
| DrCode5 of byte * uint16 
| DrCode6 of byte * DateTime 
| DrCode7 of byte * uint16 * uint16 
| DrCode8 of byte * uint16 * uint16 * uint16 * array<byte> 
| DrCode9 of byte * uint16 * array<DrCode9Item> 
| DrCode10 of byte * string * DateTime * uint32 * byte * array<byte> 
| DrCode11 of byte * array<DrCode11Item> 
| DrCode12 of byte * array<DrCode12Item> 
| DrCode13 of byte * uint16 * byte * uint16 * uint16 * string * byte * byte 
| DrCode14 of byte * array<DrCode14Item> 

私はすべてのDRタイプ(かなりの数)のためにこれをやって続けることができ、 けど私はそれが私を助ける方法をまだ理解していない。私は をWikibooksとF#の財団について読んだことがありますが、何かが私の頭の中でまだクリックされていません。

アップデート2

だから、私は次のことを行うことができます理解:それを

let execute dr = 
    match dr with 
    | DrCode0(drCode) -> printfn "Do something" 
    | DrCode1(drCode, name, timestamp, size, options) -> printfn "Show the size %A" size 
    | _ ->() 
let date = DateTime.Now 

let x = DrCode1(1uy,"blabla", date, 100ul, 0uy) 

しかし、メッセージはIMessageProcessorに出たとき、 一品がどのようなメッセージの右側がなされています であり、適切な関数が呼び出されます。上記はちょうど 追加コード、少なくともそれを理解する方法です 私は本当にここにポイントを見逃している必要があります...しかし、私はそれを見ません。

execute x 

答えて

1

F#は、区別された共用体を介してこのドメイン内のメッセージを表現するのに適していると思います。私は想像している。

type Message = 
    | Message1 of string * DateTime * int * byte //name,timestamp,size,options 
    | Message2 of bool * short * short * byte[] //last,blocknum,blocksize,data 
    ... 

とともに、バイト配列からメッセージを解析/非解析する方法もあります。あなたが言うように、この作品は単純で面倒です。

私はメッセージの処理についてはあまり明確ではありませんが、全体的にあなたの説明に基づいて、あなたはそのメッセージを扱うように思えます。

私はあなたの「ツールの柔軟性」を少し心配しています。あなたの制約は何ですか? (例えば、ネットは技術X、Y、Zを知っているプログラマーによって維持管理されなければならない...)

+0

制約について: エミュレータのコアをクロスプラットフォームにしたいと考えています。 ネイティブコード、特にCコードを使用することはありません。 私はCのハードウェア開発者が コードを書く方法を見てきました、それは恐ろしいです!どこでも略語 第三者は、エミュレータと通信したいものを(tcp/ipまたはrs232経由で)使用します。 – TimothyP

+0

ここで差別化された組合と何をするか説明してもらえますか?私はそれらを定義する方法を知っていますが、私は彼らが私をどのように助けてくれるのかわかりません... – TimothyP

+1

あなたは言った: "パターンマッチングはたくさんのネストされたif/elseを避ける良い方法のように思えたのでIMessageProcessorインターフェースを実装するためにF#そして、私は同意するが、具体的にはメッセージをDUにすることがここの鍵である。 MessageがDUの場合、メッセージのパターンマッチングは簡単になり、最大の勝利だと思います。 (他にどのように 'メッセージ'を表しますか?) – Brian

1

ここに私の2セントがあります。完全に文法があっても細かく指定された入力ファイル ファイルの内容をアクションにマップする必要があります。したがって、私はあなたがファイルを解析することをお勧めします。 F#は関数型言語であるため、Recursive Descent Parsingという構文解析手法に適合する可能性があります。 The book "Expert F#"には、再帰的な降下構文解析の議論が含まれています。

関連する問題