2013-04-04 36 views
6

Tôi có một bộ đệm có độ dài 256 nhận chuỗi byte từ bluetooth. Gói thực tế mà tôi cần trích xuất là bắt đầu và kết thúc với byte 126. Tôi muốn trích xuất các gói tin mới nhất trong bộ đệm bằng cách sử dụng LINQ.Trích xuất gói dữ liệu ra khỏi bộ đệm byte

Điều tôi đang làm bây giờ là kiểm tra chỉ mục cuối cùng của 126 và sau đó đếm ngược cho đến khi tôi đạt đến một số 126 khác. Có một số cạm bẫy là tốt, ví dụ, hai gói liền kề có thể dẫn đến hai byte của 126 cạnh nhau.

Đây là một mẫu của bộ đệm:

 
126 6 0 5 232 125 93 126 126 69 0 
0 1 0 2 2 34 6 0 5 232 125 
93 126 126 69 0 0 1 0 2 2 34 
6 0 5 232 125 93 126 126 69 0 0 
1 0 2 2 34 6 0 5 232 125 93 
126 126 69 0 0 

Vì vậy, thông tin mà tôi có là:

  • gói bắt đầu và kết thúc với giá trị byte của 126
  • byte tiếp theo sau khi chỉ số bắt đầu có giá trị 69
  • 3 byte cuối cùng bên phải cho byte kết thúc là 126 là CRC của toàn bộ gói mà tôi biết cách tính, vì vậy sau khi trích xuất một gói tôi có thể hãy xem CRC này để xem tôi có đúng gói

Vì vậy, ở cuối tôi muốn có một mảng hoặc danh sách chứa gói chính xác. ví dụ:

126 69 0 0 1 0 2 2 34 6 0 5 232 125 93 126 

Bạn có thể cho tôi một cách nhanh chóng giải nén gói này từ bộ đệm không?

Đây là những gì I'v cố gắng cho đến nay .... nó không thành công vì nó không thể thực sự trở lại với gói đúng Tôi đang tìm kiếm:

var data = ((byte[])msg.Obj).ToList(); //data is the buffer 

byte del = 126; //delimeter or start/end byte 
var lastIndex = data.LastIndexOf(del); 
var startIndex = 0; 
List<byte> tos = new List<byte>(); //a new list to store the result (packet)  

//try to figure out start index        
if(data[lastIndex - 1] != del) 
{ 
    for(int i = lastIndex; i > 0; i--) 
    { 
     if(data[i] == del) 
     { 
      startIndex = i; 
     } 
    } 

    //add the result in another list 
    for(int i = 0; i <= lastIndex - startIndex; i++) 
    { 
     tos.Add(data[i]); 
    } 

    string shit = string.Empty; 

    foreach (var b in tos) 
     shit += (int)b + ", "; 

    //print result in a textbox 
    AddTextToLogTextView(shit + "\r\n"); 
} 
+0

@KendallFrey tôi 'v đã thêm của riêng tôi cách tiếp cận, nó dường như không tìm thấy chỉ số bắt đầu và kết thúc thực sự của một gói chính xác: ( –

+0

Bạn có thể giải thích logic của mình không? – ehudt

+0

Có thể có kết quả một phần bộ đệm này không? Tôi có nghĩa là các byte bắt đầu được gửi nhưng cho đến khi kết thúc của bộ đệm không có điểm đánh dấu kết thúc bởi vì nó là một phần của một phản ứng lớn hơn? Trong trường hợp này, bất kỳ giải pháp nào được trình bày bên dưới đều sai. –

Trả lời

3

Solutions

Tôi đã chuẩn bị ba giải pháp khả thi lấy các gói tin cuối cùng từ đầu vào buffor:

Sử dụng LINQ

public static byte[] GetLastPacketUsingLINQ(byte[] input, byte delimiter) 
{ 
    var part = input.Reverse() 
        .SkipWhile(i => i != delimiter) 
        .SkipWhile(i => i == delimiter) 
        .TakeWhile(i => i != delimiter) 
        .Reverse(); 

    return (new byte[] { delimiter }).Concat(part).Concat(new byte[] { delimiter }).ToArray(); 
} 

Sử dụng string.Split

public static byte[] GetLastPacketUsingString(byte[] input, byte delimiter) 
{ 
    var encoding = System.Text.Encoding.GetEncoding("iso-8859-1"); 
    string inputString = encoding.GetString(input); 
    var parts = inputString.Split(new[] { (char)delimiter }, StringSplitOptions.RemoveEmptyEntries); 

    return encoding.GetBytes((char)delimiter + parts[parts.Length - 2] + (char)delimiter); 
} 

Sử dụng while loop và indexers

public static byte[] GetLastPacketUsingIndexers(byte[] input, byte delimiter) 
{ 
    int end = input.Length - 1; 
    while (input[end--] != delimiter) ; 

    int start = end - 1; 
    while (input[start--] != delimiter) ; 

    var result = new byte[end - start]; 
    Array.Copy(input, start + 1, result, 0, result.Length); 
    return result; 
} 

Performance

Tôi cũng đã thực hiện một số xét nghiệm thực hiện rất đơn giản. Dưới đây là kết quả:

LINQ version result: 
126 69 0 0 1 0 2 2 34 6 0 5 232 125 93 126 

String version result: 
126 69 0 0 1 0 2 2 34 6 0 5 232 125 93 126 

Indexers version result: 
126 69 0 0 1 0 2 2 34 6 0 5 232 125 93 126 

LINQ version time: 64ms (106111 ticks) 
String version time: 2ms (3422 ticks) 
Indexers version time: 1ms (2359 ticks) 

Kết luận

Như bạn có thể thấy, đơn giản nhất cũng là tốt nhất ở đây.

Bạn có thể nghĩ rằng LINQ là câu trả lời cho mọi vấn đề, nhưng một thời gian, bạn nên viết giải pháp đơn giản theo cách thủ công thay vì sử dụng các phương pháp LINQ.

+0

+0 Tất cả các mảng byte tùy ý không thể được chuyển đổi thành/từ chuỗi mà không mất một số byte. Vì vậy, phiên bản chuỗi của bạn sẽ không hoạt động. – I4V

+0

Phiên bản chỉ mục của bạn không thành công nếu bộ đệm kết thúc chính xác với phần cuối của gói tin cuối cùng. Hủy bỏ bốn byte cuối cùng của dữ liệu mẫu và bạn sẽ thấy những gì tôi có ý nghĩa. – pescolino

+0

Sau khi tất cả, cách tiếp cận lập chỉ mục của bạn với một số chỉnh sửa là câu trả lời tốt nhất mà tôi đã có cho đến nay. –

1

Thực tế, có nhiều cách khác nhau để giải quyết câu hỏi của bạn , ý tưởng đơn giản nhất là phát hiện đôi 126 (0x7e) và không quan trọng những thứ khác như CRC.

Các implemention cơ bản của khái niệm này sẽ là như thế này

  • Mã đơn giản

    var list=new List<byte[]>(); 
    int i=0, j=0; 
    for(; i<data.Length; ++i) 
        if(i>0&&0x7e==data[i]&&0x7e==data[i-1]) { 
         list.Add(data.Skip(j).Take(i-j).ToArray()); 
         j=i; 
        } 
    list.Add(data.Skip(j).Take(i-j).ToArray()); 
    

Căn cứ vào câu trả lời cũ của tôi về Konami Code in C#, và nó thậm chí còn sử dụng để giải quyết câu hỏi này : Double characters shown when typing special characters while logging keystrokes in c#.

  • Mã với một máy dò dãy

    public partial class TestClass { 
        public static void TestMethod() { 
         var data=(
          new[] { 
            126, 6, 0, 5, 232, 125, 93, 126, 
            126, 69, 0, 0, 1, 0, 2, 2, 34, 6, 0, 5, 232, 125, 93, 126, 
            126, 69, 0, 0, 1, 0, 2, 2, 34, 6, 0, 5, 232, 125, 93, 126, 
            126, 69, 0, 0, 1, 0, 2, 2, 34, 6, 0, 5, 232, 125, 93, 126, 
            126, 69, 0, 0 
           }).Select(x => (byte)x).ToArray(); 
    
         var list=new List<List<byte>>(); 
    
         foreach(var x in data) { 
          if(list.Count<1||SequenceCapturer.Captured((int)x)) 
           list.Add(new List<byte>()); 
    
          list.Last().Add(x); 
         } 
    
         foreach(var byteList in list) 
          Debug.Print("{0}", byteList.Select(x => x.ToString("x2")).Aggregate((a, b) => a+"\x20"+b)); 
        } 
    } 
    
    public class SequenceCapturer { 
        public int Count { 
         private set; 
         get; 
        } 
    
        public int[] Sequence { 
         set; 
         get; 
        } 
    
        public bool Captures(int value) { 
         for(var i=Sequence.Length; i-->0;) { 
          if(Sequence[i]!=value) { 
           if(0==i) 
            Count=0; 
    
           continue; 
          } 
    
          if(Count!=i) 
           continue; 
    
          ++Count; 
          break; 
         } 
    
         var x=Sequence.Length==Count; 
         Count=x?0:Count; 
         return x; 
        } 
    
        public SequenceCapturer(int[] newSequence) { 
         Sequence=newSequence; 
        } 
    
        public SequenceCapturer() 
         : this(new[] { 0x7e, 0x7e }) { 
        } 
    
        public static bool Captured(int value) { 
         return Instance.Captures(value); 
        } 
    
        public static SequenceCapturer Instance=new SequenceCapturer(); 
    } 
    

Hoặc nếu bạn muốn viết nó đầy đủ trong LINQ, bạn có thể muốn thử những điều sau đây. Bạn thậm chí không cần sử dụng List, packetArray cung cấp cho bạn một mảng các mảng byte trực tiếp.

Các let s được thiết kế để chia mã thành các dòng, nếu không nó sẽ là một tuyên bố dài cực dài trong một dòng. Nếu bạn xem xét một dòng là thì tốt nhất là, thì tôi sẽ làm như vậy.

  • Mã của packetArray

    var packetArray=(
        from sig in new[] { new byte[] { 0x7e, 0x7e } } 
        let find=new Func<byte[], int, IEnumerable<byte>>((x, i) => x.Skip(i).Take(sig.Length)) 
        let isMatch=new Func<IEnumerable<byte>, bool>(sig.SequenceEqual) 
        let filtered=data.Select((x, i) => 0==i||isMatch(find(data, i-1))?i:~0) 
        let indices=filtered.Where(i => ~0!=i).Concat(new[] { data.Length }).ToArray() 
        from index in Enumerable.Range(1, indices.Length-1) 
        let skipped=indices[index-1] 
        select data.Skip(skipped).Take(indices[index]-skipped).ToArray()).ToArray(); 
    
  • Mã cho đầu ra

    foreach(var byteArray in packetArray) 
        Debug.Print("{0}", byteArray.Select(x => x.ToString("x2")).Aggregate((a, b) => a+"\x20"+b)); 
    

Tuy nhiên, ngay cả trong cùng một khái niệm giải pháp, sẽ có nhiều cách khác nhau như tôi đã đề cập trước . Tôi thực sự khuyên bạn không nên liên quan đến các điều kiện bổ sung như điều gì đó về CRC, điều này có thể khiến mọi thứ trở nên phức tạp hơn.

3

Sử dụng LINQ này có thể được thực hiện trong một dòng mã nếu hai quy tắc sau đây có thể được áp dụng cho các bộ đệm:

  • Bộ đệm chứa ít nhất một gói hoàn chỉnh bao quanh bởi các delimiter nhất định.
  • Mỗi gói chứa ít nhất một byte dữ liệu.

Đây là mã: (. Ok, điều này còn hơn cả một dòng duy nhất bởi vì tôi tách nó thành nhiều dòng cho dễ đọc hơn)

var data = (byte[])msg.Obj; 
byte delimiter = 126; 

var packet = data.Reverse() 
       .SkipWhile(b => b != delimiter) 
       .SkipWhile(b => b == delimiter) 
       .TakeWhile(b => b != delimiter) 
       .Reverse(); 

EDIT: Removed sự gọi đến Take (1) vì điều đó sẽ luôn trả về một chuỗi rỗng. Kết quả tuy nhiên không chứa dấu phân tách theo cách này.


Và đây là cách hoạt động:

Kể từ khi chúng tôi muốn tìm các gói tin cuối cùng chúng ta có thể đảo ngược dữ liệu:

var reversed = data.Reverse(); 

Bộ đệm có thể kết thúc với một gói mà không phải là hoàn chỉnh chưa. Vì vậy, hãy bỏ qua rằng:

reversed = reversed.SkipWhile(b => b != delimiter); 

reversed tại là một trong hai sản phẩm nào hoặc nó bắt đầu với delimiter. Kể từ khi chúng tôi giả định rằng bộ đệm luôn chứa ít nhất một gói hoàn chỉnh, chúng tôi đã có thể lấy byte tiếp theo cho kết quả của chúng tôi bởi vì chúng tôi biết đó là dấu phân cách:

var packet = reversed.Take(1); 

Trong chuỗi bây giờ chúng ta có thể bỏ qua một byte.Nếu delimiter chúng tôi thấy thực sự là khởi đầu của một gói tin mới dãy còn lại sẽ bắt đầu với dấu phân cách khác để chúng ta phải bỏ qua mà cũng:

reversed = reversed.Skip(1); 
if (reversed.First() == delimiter) 
{ 
    reversed.Skip(1); 
} 

Kể từ khi chúng ta biết rằng một gói tin không thể để trống vì nó có chứa một 3 byte CRC chúng ta có thể viết:

reversed = reversed.SkipWhile(b => b == delimiter); 

Bây giờ dữ liệu thực tế sau:

packet = packet.Concat(reversed.TakeWhile(b => b != delimiter)); 
reversed = reversed.SkipWhile(b => b != delimiter); 

byte tiếp theo là dấu phân cách đánh dấu sự bắt đầu của gói tin:

packet = packet.Concat(reversed.Take(1)); 

Điều cuối cùng cần làm là để đảo ngược kết quả một lần nữa:

packet = packet.Reverse(); 

Có lẽ bạn muốn đưa điều này vào một phương pháp:

public IEnumerable<byte> GetPacket(byte[] data, byte delimiter) 
{ 
    yield return delimiter; 

    foreach (byte value in data.Reverse() 
           .SkipWhile(b => b != delimiter) 
           .SkipWhile(b => b == delimiter) 
           .TakeWhile(b => b != delimiter)) 
    { 
     yield return value; 
    } 

    yield return delimiter; 
} 

Bạn sẽ phải gọi Reverse trên giá trị trả về của phương thức này.


Nếu vấn đề hiệu suất bạn có thể sử dụng cùng một thuật toán trên mảng cơ bản. Bằng cách này, nó sẽ được khoảng 20 lần nhanh hơn:

int end = data.Length - 1; 
while (data[end] != delimiter) 
    end--; 

while (data[end] == delimiter) 
    end--; 

int start = end; 
while (data[start] != delimiter) 
    start--; 

byte[] result = new byte[end - start + 2]; // +2 to include delimiters 
Array.Copy(data, start, result, 0, result.Length); 
+0

Cảm ơn bạn đã trả lời. Và tôi đoán chúng ta có thể loại bỏ 'Đảo ngược' theo một cách nào đó. –

+1

@KenKin: Tôi đã thêm một phiên bản hoạt động trực tiếp trên mảng cơ bản thay vì sử dụng LINQ. Ngoài ra tôi đã sửa một sai lầm trong câu trả lời ban đầu: Gọi Take (1) tạo ra một chuỗi với một phần tử duy nhất mà sau đó bị bỏ qua. Điều này luôn dẫn đến một chuỗi rỗng. – pescolino

0

Vì bạn đang tìm gói tin cuối cùng, nên dễ dàng đảo ngược byte [] hơn và tìm gói đầu tiên. Hai dấu phân tách gói của bạn không chỉ là 126.Chúng là 126, 69 cho điểm bắt đầu và 126, 126 cho kết thúc trừ khi kết thúc gói là byte cuối cùng nhận được, làm cho dấu phân cách cuối 126.

Tôi sẽ đề xuất sử dụng phương pháp mô phỏng này:

public static byte[] GetMessage(byte[] msg) 
    { 
     //Set delimiters 
     byte delimit = 126; 
     byte startDelimit = 69; 

     //Reverse the msg so we can find the last packet 
     List<byte> buf = msg.Reverse().ToList(); 

     //set indices to impossible values to check for failures 
     int startIndex = -1; 
     int endIndex = -1; 
     //loop through the message 
     for (int i = 0; i < buf.Count - 1; i++) 
     { 
      //find either a double 126, or 126 as the last byte (message just ended) 
      if (buf[i] == delimit && (buf[i + 1] == delimit || i == 0)) 
      { 
       if (i == 0) 
       { 
        startIndex = i; 
        i++; 
       } 
       else 
       { 
        startIndex = i + 1; 
        i += 2; 
       } 
       continue; 
      } 
      //Only process if we've found the start index 
      if (startIndex != -1) 
      { 
       //check if the byte is 69 followed by 126 
       if (buf[i] == startDelimit && buf[i + 1] == delimit) 
       { 
        endIndex = i + 1; 
        break; 
       } 
      } 
     } 
     //make sure we've found a message 
     if (!(startIndex == -1 || endIndex==-1)) 
     { 
      //get the message and reverse it to be the original packet 
      byte[] revRet = new byte[endIndex - startIndex]; 
      Array.Copy(buf.ToArray(), startIndex, revRet, 0, endIndex - startIndex); 

      return revRet.Reverse().ToArray(); 
     } 
     return new byte[1]; 
    } 

Tôi không hoàn toàn chắc chắn liệu các chỉ mục của bản sao có hoàn toàn chính xác hay không, nhưng điều này có thể là nguyên tắc của nó.

0

vì bạn có thể nhận được dữ liệu không đầy đủ, bạn phải lưu bộ đệm không hoàn chỉnh cuối cùng.

này là trường hợp mẫu, First Nhận:

126, 6, 0, 5, 232, 125, 93, 126, 126, 69, 0, 
0, 1, 0, 2, 2, 34, 6 , 0 , 5 , 232, 125, 
93, 126, 126, 69, 0, 0, 1 , 0, 2, 2, 34, 
6, 0, 5, 232, 125, 93, 126, 126, 69, 0, 0 , 
1, 0, 2, 2, 34, 6, 0, 5, 232, 125, 93, 
126, 126, 69, 0, 0 

dòng thứ hai:

69, 0, 0 , 1, 0, 2, 2, 34, 6, 0, 126 

và mã:

List<byte> lastBuf = new List<byte>(); 

    List<byte[]> Extract(byte[] data, byte delim) 
    { 
     List<byte[]> result = new List<byte[]>(); 

     for (int i = 0; i < data.Length; i++) 
     { 
      if (lastBuf.Count > 0) 
      { 
       if(data[i] == delim) 
       { 
        result.Add(lastBuf.ToArray()); 
        lastBuf.Clear(); 
       } 
       else 
       { 
        lastBuf.Add(data[i]); 
       } 
      } 
      else 
      { 
       if(data[i] != 126) 
       { 
        lastBuf.Add(data[i]); 
       } 
      } 
     } 

     return result; 
    } 

kết quả: data result

Các vấn đề liên quan