Menggunakan kelas Delphi XE2
dan TJvHidDevice
dari perpustakaan Jedi
, saya berhasil berkomunikasi dengan perangkat USB (papan pic32mx7
, dengan kode saya berjalan di atasnya). Cara biasa "kirim permintaan, tunggu satu respons" berfungsi.
Masalahnya adalah dengan perintah yang menghasilkan lebih banyak tanggapan berurutan. Jika tanggapan tersebut dikirim oleh perangkat secepat mungkin - atau bahkan jika saya menambahkan penundaan kecil di antara mereka seperti 5 ms - saya kehilangan paket (laporan? bingkai?). Acara OnDeviceData
sepertinya tidak cocok untuk mereka semua. Jika saya menambahkan penundaan yang lebih besar dalam kode perangkat, masalahnya akan hilang.
Saya menggunakan program USBPcap
untuk menangkap data USB dan membuangnya ke file yang, setelah saya buka di WireShark, berisi semua data yang dikirim oleh perangkat (saya mengirim 255 paket sebagai tes, dengan semua nol dan satu "1" menggeser tempatnya sebanyak 1 posisi di setiap paket). Jadi, saya pikir perangkat dan Windows melakukan tugasnya.
Untuk memastikan kode Delphi saya tidak salah, saya mencoba proyek contoh Jedi "DevReader" (ini adalah kode main.pas) yang membuang data ke layar dan juga paket yang hilang.
Saya merasa harus ada lebih banyak informasi di internet tentang kelas USB Jedi tetapi saya kesulitan menemukannya.
Saya mungkin dapat menghindari masalah ini dengan menggabungkan/menggabungkan respons perangkat, tetapi masih ingin tahu apa yang terjadi.
Sunting:
- Mencoba dari aplikasi konsol: paket tidak hilang lagi.
- Memodifikasi aplikasi demo Jedi untuk hanya menghitung paket yang diterima dan memperbarui label penghitung di layar (tidak ada pengecatan ulang jendela paksa) - tidak ada paket yang hilang.
- Menambahkan sleep(1) dalam acara OnData - tidak ada paket yang hilang.
- Menambahkan sleep(2) di acara OnData - kehilangan paket lagi.
Ini terlihat seperti utas Jedi yang membaca data tidak boleh ditunda oleh pemrosesan apa pun - bukankah seharusnya ada buffering data yang terjadi (oleh Windows?) yang memungkinkan jenis penundaan pemrosesan ini? Dilihat dari "pola" packet loss sepertinya ada buffering, tetapi itu tidak cukup karena saya dapat menerima mis. 30 paket kemudian kehilangan 5 kemudian menerima lagi 20 dst.
Saya akan memodifikasi kode saya untuk menyalin data dan keluar dari acara OnData secepat mungkin sehingga utas memiliki "waktu henti" minimum dan saya akan melaporkan hasilnya.
1 menjawab
Karena penyebab masalah tampaknya terkait dengan jumlah waktu utas membaca USB diblokir oleh Synchronise
, yaitu pemrosesan data yang dilakukan oleh utas utama, saya membuat perubahan pada kode utas, (kelas TJvHidDeviceReadThread , JvHidControllerClass.pas unit). Kode apa pun yang menggunakan unit ini dan kelas yang terkandung harus tetap berfungsi tanpa modifikasi apa pun, tidak ada publik yang diubah.
Perilaku baru: setiap kali data dibaca, data tersebut ditempatkan dalam daftar aman utas. Alih-alih Sinkronisasi, sekarang menggunakan Queue
, tetapi hanya jika belum diantrekan. Metode Antrian membaca dari daftar aman utas hingga kosong. Ini memicu peristiwa (peristiwa yang sama seperti dalam kode lama) untuk setiap laporan buffer dalam daftar. Setelah daftar kosong, bendera "Antri" direset dan pembacaan berikutnya akan menyebabkan Antrian lagi.
Dalam pengujian sejauh ini saya tidak menemukan paket yang hilang.
Kelas utas diperpanjang:
TJvHidDeviceReadThread = class(TJvCustomThread)
private
FErr: DWORD;
// start of additions
ReceivedReports : TThreadList;
Queued: boolean;
procedure PushReceivedReport(const bytes: array of byte; const NumBytesRead: cardinal);
function PopReceivedReport(var ReportID: byte; var ReportBytes: TBytes): boolean;
procedure FlushBuffer;
// end of additions
procedure DoData;
procedure DoDataError;
constructor CtlCreate(const Dev: TJvHidDevice);
protected
procedure Execute; override;
public
Device: TJvHidDevice;
NumBytesRead: Cardinal;
Report: array of Byte;
constructor Create(CreateSuspended: Boolean);
//added destructor:
destructor Destroy; override;
end;
Di bagian implementasi, berikut ini diubah:
constructor TJvHidDeviceReadThread.CtlCreate(const Dev: TJvHidDevice);
begin
inherited Create(False);
// start of changes
ReceivedReports := TThreadList.Create;
// end of changes
Device := Dev;
NumBytesRead := 0;
SetLength(Report, Dev.Caps.InputReportByteLength);
end;
procedure TJvHidDeviceReadThread.Execute;
...
...
...
//replaced: Synchronize(DoData); with:
PushReceivedReport (Report, NumBytesRead);
...
Dan berikut ini ditambahkan:
type
TReport = class
ID: byte;
Bytes: TBytes;
end;
destructor TJvHidDeviceReadThread.Destroy;
var
l: TList;
begin
RemoveQueuedEvents (self);
try
l := ReceivedReports.LockList;
while l.Count>0 do
begin
TReport(l[0]).Free;
l.Delete(0);
end;
finally
ReceivedReports.UnlockList;
FreeAndNil (ReceivedReports);
end;
inherited;
end;
procedure TJvHidDeviceReadThread.FlushBuffer;
var
ReportID: byte;
ReportBytes: TBytes;
begin
while PopReceivedReport (ReportID, ReportBytes) do
Device.OnData(Device, ReportID, ReportBytes, length(ReportBytes));
end;
function TJvHidDeviceReadThread.PopReceivedReport(var ReportID: byte; var ReportBytes: TBytes): boolean;
var
l: TList;
rep: TReport;
begin
l := ReceivedReports.LockList;
rep := nil;
try
result := l.Count>0;
if result
then
begin
rep := l[0];
l.Delete(0);
end
else Queued := false;
finally
ReceivedReports.UnlockList;
end;
if result then
begin
ReportID := rep.ID;
SetLength(ReportBytes, length(rep.Bytes));
System.move (rep.Bytes[0], ReportBytes[0], length(rep.Bytes));
rep.Free;
end;
end;
procedure TJvHidDeviceReadThread.PushReceivedReport(const bytes: array of byte; const NumBytesRead: cardinal);
var
rep: TReport;
begin
rep := TReport.Create;
setlength (rep.Bytes, NumBytesRead-1);
rep.ID := Bytes[0];
System.move (Bytes[1], rep.Bytes[0], NumBytesRead-1);
// explicitely lock the list just to provide a locking mechanism for the Queue flag as well
ReceivedReports.LockList;
try
if not Queued then
begin
Queued := true;
Queue (FlushBuffer);
end;
ReceivedReports.Add(rep);
finally
ReceivedReports.UnlockList;
end;
end;
bInterval
dari deskriptor endpoint HIDs IN Anda?