2015-11-13 14 views
5

Tôi đang sửa đổi giao diện người dùng dựa trên C# có giao diện với một thiết bị kiểm tra vi điều khiển PIC nhỏ.C#: đọc cổng nối tiếp - BytesToRead

Giao diện người dùng bao gồm một vài nút khởi tạo kiểm tra bằng cách gửi "lệnh" tới vi điều khiển qua kết nối cổng nối tiếp. Mỗi 250 mili giây, giao diện người dùng thăm dò giao diện nối tiếp tìm kiếm một thông điệp ngắn gọn bao gồm các kết quả thử nghiệm từ PIC. Thông báo được hiển thị trong một hộp văn bản.

Mã Tôi được thừa kế như sau:

try 
{ 
    btr = serialPort1.BytesToRead; 
    if (btr > 0) 
     Thread.Sleep(300); 
    btr = serialPort1.BytesToRead; 
    if (btr > 0) 
    { 
     Thread.Sleep(300); 
     btr = serialPort1.BytesToRead; 

     numbytes = serialPort1.Read(stuffchar, 0, btr); 
     for (x = 0; x < (numbytes); x++) 
     { 
      cc = (stuffchar[x]); 
      stuff += Convert.ToString(Convert.ToChar((stuffchar[x]))); 
     } 

Điều gì sẽ là lý do căn bản cho một vài dòng đầu tiên gồm ba cuộc gọi đến BytesToRead và hai cuộc gọi 300 phần nghìn giây giấc ngủ cuối cùng trước khi đọc các cổng nối tiếp? Trừ khi tôi giải thích mã không chính xác, mọi đọc thành công từ cổng nối tiếp sẽ mất hơn 600 mili giây, điều này có vẻ đặc biệt đối với tôi.

Trả lời

0

Nếu mã đó ban đầu nằm trong vòng lặp, đó có thể là cách đợi PIC thu thập dữ liệu.

Nếu bạn có phần cứng thực để kiểm tra, tôi sẽ sugest bạn xóa cả hai chế độ ngủ.

4

Đó là một hack khủng khiếp xung quanh hành vi của SerialPort.Read(). Chỉ trả về số byte thực sự nhận được. Thông thường chỉ 1 hoặc 2, cổng nối tiếp là máy tính chậm và hiện đại rất nhanh. Vì vậy, bằng cách gọi Thread.Sleep(), mã đang trì hoãn chuỗi giao diện người dùng đủ lâu để có được lệnh gọi Đọc() trả lại nhiều hơn byte. Hy vọng rằng tất cả chúng, bất kể giao thức trông như thế nào. Thường hoạt động, không phải lúc nào. Và trong mã đã đăng nó không hoạt động và lập trình viên chỉ tự ý trì hoãn hai lần lâu. Ugh.

Sự đau khổ lớn của khóa học là chuỗi giao diện người dùng khá là hữu ích khi bị buộc phải ngủ. Khá đáng chú ý, nó sẽ rất chậm để vẽ và phản hồi đầu vào của người dùng.

Điều này cần phải được sửa chữa bằng cách đầu tiên chú ý đến giao thức. PIC cần phải hoặc là gửi một số cố định của byte trong phản ứng của nó, vì vậy bạn chỉ có thể đếm chúng tắt, hoặc cung cấp cho các máy tính một cách để phát hiện rằng sự đáp ứng đầy đủ được nhận. Thường được thực hiện bằng cách gửi một byte duy nhất làm byte cuối cùng của một phản hồi (SerialPort.NewLine) hoặc bằng cách bao gồm độ dài của phản hồi dưới dạng giá trị byte ở đầu thư. Lời khuyên cụ thể khó đưa ra, bạn không mô tả giao thức nào cả.

Bạn có thể giữ mã hacky và chuyển mã đó thành chuỗi công việc để nó không ảnh hưởng đến giao diện người dùng quá tệ. Bạn nhận được một miễn phí từ sự kiện SerialPort.DataReceived. Nhưng điều đó có xu hướng tạo ra hai vấn đề thay vì giải quyết vấn đề cốt lõi.

0

@TomWr bạn nói đúng, từ những gì tôi đọc là trường hợp này.
đoạn của bạn dưới đây với ý kiến ​​của tôi:

try 
{ 
    // Let's check how many bytes are available on the Serial Port 
    btr = serialPort1.BytesToRead; 

    // Something? Alright then, let's wait 300 ms. 
    if (btr > 0) 
     Thread.Sleep(300); 

    // Let's check again that there are some bytes available the Serial Port 
    btr = serialPort1.BytesToRead; 

    // ... and if so wait (maybe again) for 300 ms 
    // Please note that, at that point can be all cumulated about 600ms 
    // (if we actually already waited previously) 
    if (btr > 0) 
    { 
     Thread.Sleep(300); 
     btr = serialPort1.BytesToRead; 

     numbytes = serialPort1.Read(stuffchar, 0, btr); 
     for (x = 0; x < (numbytes); x++) 
     { 
      // Seems like a useless overhead could directly use 
      // an Encoding and ReadExisting method() of the SerialPort. 
      cc = (stuffchar[x]); 
      stuff += Convert.ToString(Convert.ToChar((stuffchar[x]))); 
     } 

Tôi đoán là như nhau nó như được đã đề cập ở trên bởi idstam, về cơ bản có lẽ là để kiểm tra xem được dữ liệu gửi qua đường điện thoại và lấy họ

Bạn có thể dễ dàng refactor mã này với các phương pháp SerialPort thích hợp gây ra có thực sự tốt hơn và ngắn gọn cách để làm kiểm tra xem có dữ liệu có sẵn hay không trên Cổng nối tiếp.

Thay vì "Tôi đang kiểm tra có bao nhiêu byte là trên cổng, nếu có cái gì đó sau đó tôi đợi 300 ms và sau đó điều tương tự một lần nữa."đó là kết thúc một cách khốn khổ với
" Vì vậy, yep 2 lần 300 ms = 600ms hoặc chỉ một lần (tùy thuộc vào việc có lần đầu tiên "hay không có gì cả (tùy thuộc vào thiết bị bạn đang giao tiếp thông qua giao diện người dùng này) điều này có thể thực sự không ổn định vì Thread.Sleep đang chặn giao diện người dùng ...). "

Trước tiên, hãy xem xét một thời gian mà bạn đang cố gắng giữ nhiều mã nguồn giống nhau, tại sao không chỉ đợi 600ms?

Hoặc tại sao không chỉ sử dụng thuộc tính ReadTimeout và bắt ngoại lệ thời gian chờ, không phải là sạch nhưng ít nhất là tốt hơn về khả năng đọc và cộng với bạn đang nhận chuỗi của bạn trực tiếp thay vì sử dụng một số cuộc gọi Convert.ToChar() ...

Tôi cảm thấy rằng mã đã được chuyển từ C hoặc C++ (hoặc ít nhất là lý do đằng sau) từ một người thay vì hầu như có nền phần mềm được nhúng.

Dù sao, quay lại số lượng kiểm tra byte khả dụng, có nghĩa là trừ khi dữ liệu Cổng nối tiếp bị xóa trong Trình xử lý Tác vụ/Trình xử lý nền/Trình xử lý khác, tôi không thấy lý do nào kiểm tra nó hai lần. được mã hóa.
Để làm cho nó nhanh hơn? Không thực sự, gây ra sự chậm trễ bổ sung nếu có dữ liệu thực sự trên Cổng nối tiếp. Nó không có ý nghĩa gì với tôi.

Một cách khác để làm cho đoạn mã của bạn tốt hơn một chút là thăm dò ý kiến ​​bằng cách sử dụng ReadExisting().

Nếu không, bạn cũng có thể xem xét các phương pháp không đồng bộ bằng cách sử dụng SerialPort BaseStream.

Tất cả trong tất cả, nó khá khó để nói mà không cần truy cập phần còn lại của codebase của bạn, hay còn gọi là ngữ cảnh.

Nếu bạn có thêm thông tin về các mục tiêu/giao thức, nó có thể đưa ra một số gợi ý về việc cần làm. Nếu không, tôi chỉ có thể nói điều này dường như được mã hóa kém, một lần nữa, trong bối cảnh.

Tôi tăng gấp đôi và thậm chí gấp ba lần những gì Hans đã đề cập về phản hồi giao diện người dùng theo ý nghĩa thực sự tôi hy vọng đoạn mã của bạn đang chạy trong chuỗi không phải là giao diện người dùng (mặc dù bạn đã đề cập trong bài đăng của mình vẫn hy vọng đoạn trích dành cho một công nhân khác).

Nếu đó thực sự là chuỗi giao diện người dùng thì nó sẽ bị chặn mỗi khi có cuộc gọi Thread.Sleep khiến giao diện người dùng không thực sự đáp ứng với tương tác của người dùng và có thể gây cảm giác thất vọng cho người dùng cuối của bạn.

Cũng đáng để đăng ký sự kiện DataReceived và thực hiện những gì bạn muốn/cần với trình xử lý (ví dụ: sử dụng bộ đệm và giá trị so sánh, v.v.). Xin lưu ý rằng mono vẫn không thực hiện kích hoạt của sự kiện này nhưng nếu bạn đang chạy chống lại một MS .NET thực hiện đồng bộ, điều này hoàn toàn tốt mà không có sự phức tạp của đa luồng.

Nói tóm lại:

  • Kiểm tra mà chủ đề (s) được (bị) chăm sóc đoạn mã của bạn và tâm về khả năng đáp ứng UI
    • Nếu đó là giao diện người dùng, sau đó sử dụng thread khác thông qua Chủ đề, BackgroundWorker (Threadpool) hoặc Task.
    • Suối Asynchronous phương pháp để tránh sự phức tạp của các giao diện người dùng đồng bộ hóa thread
  • Hãy thử để xem liệu các mục tiêu thực sự xứng đáng với một 300 ms đôi phương pháp Chủ đề Ngủ gọi
  • Bạn có thể trực tiếp lấy String thay của thu thập nếu kiểm tra sau này đang sử dụng để thực hiện các hoạt động thay vì thu thập Bytes của chính mình (nếu mã hóa được chọn có thể đáp ứng nhu cầu của bạn).
+0

Phương thức truyền không đồng bộ (ví dụ: 'port.BaseStream.ReadAsync') không sử dụng một chuỗi .... và đó là điều tốt. Bạn không phải lo lắng về việc đồng bộ hóa quyền truy cập vào các cấu trúc dữ liệu từ nhiều luồng, hoặc điều chỉnh việc sử dụng điều khiển giao diện người dùng giữa các luồng. Chỉ cần để lại cấu trúc dữ liệu trong trạng thái nhất quán mỗi khi một trình xử lý sự kiện hoặc trả về hoặc gọi 'await'. –

+0

@BenVoigt http://referencesource.microsoft.com/#mscorlib/system/io/stream.cs,e224b4bec8748849 async/await đang sử dụng Tác vụ, do đó Threadpool. Do đó nó có thể chạy trên một Thread khác, nhưng không nhất thiết phải mặc dù. Hãy giải thích thêm một chút nếu bạn nghĩ tôi sai. Đồng ý về việc đồng bộ hóa đặc biệt là khi nó liên quan đến giao diện người dùng. – Ehouarn

+1

Các đối tượng nhiệm vụ chạy trong ngữ cảnh hiện tại theo mặc định, trong ứng dụng giao diện người dùng, ngữ cảnh mặc định là trình điều phối tin nhắn UI. Không sử dụng thêm chủ đề nào. Bây giờ, bạn có thể chọn để buộc các tác vụ vào các luồng công nhân, nhưng điều đó sẽ là ngớ ngẩn cho I/O, đặc biệt là I/O nối tiếp. Chuỗi công việc chỉ thuận lợi cho các hoạt động liên kết CPU. –

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