我需要 UWP 应用程序在应用程序启动时声明任何配对的蓝牙扫描仪设备,而且在蓝牙扫描仪电源循环后,或在超出范围后返回范围内。用户甚至不必考虑连接/声明。打开扫描仪设备并运行 UWP 应用程序即可。
我还没有找到任何可靠的方法或事件来自动检测蓝牙扫描仪何时在范围内并“准备好被认领”。(例如,在外出/触手可及或打开或重启蓝牙扫描仪设备之后。)
基本上,这导致了以下代码,其中我的代码不断尝试声明 DeviceWatcher 检测到的任何蓝牙扫描仪。它将这样做,直到它成功。对于 DeviceWatcher 中的任何无人认领的扫描仪,将在计时器上进行认领尝试。此外,我必须使用相当丑陋的 try/catch 构造来防止不成功的 ClaimScannerAsync()调用使应用程序崩溃,因为此方法似乎不会抛出它自己继承的 Exception 类。
这似乎确实有效,但我想知道是否有更好的方法。
有没有更好的方法来检查准备好的蓝牙扫描仪何时在范围内?
请考虑蓝牙条码扫描器的索赔流程。
- 首先在 Windows 设置中配对蓝牙,然后设备将可用并显示为已配对。即使它被关闭。
- 只有在应用程序中实际声明扫描仪后,设备才会显示为已连接。
(我正在粘贴我描述的代码以供参考和其他可能有相同要求的人)
private readonly List<BarcodeScanner> btScannerList = new List<BarcodeScanner>();
private readonly List<ClaimedBarcodeScanner> ClaimedScannerList = new List<ClaimedBarcodeScanner>();
/// <summary>
/// Function to claim scanner with retry timer in case of claim failure (used to reclaim scanner when scanner can go out/in of range or turned on/off)
/// In this case a device removed event is not fired by DeviceWatcher
/// This function is called for each bluetooth scanner detected by DeviceWatcher
/// </summary>
/// <param name="deviceId"></param>
private async void TryClaimBtScannerByDeviceId(string deviceId)
{
try
{
if (!await this.ClaimBtScannerByDeviceId(deviceId))
{
Log.Debug($"BarcodeService.TryClaimBtScannerByDeviceId Failed to reconnect, setting timer to reconnect.");
this.SetReclaimTimerForBtScanner(deviceId);
}
}
catch
{
Log.Debug($"BarcodeService.TryClaimBtScannerByDeviceId Exception while trying to reconnect (probably a timeout), setting timer to reconnect.");
this.SetReclaimTimerForBtScanner(deviceId);
}
}
private void SetReclaimTimerForBtScanner(string deviceId)
{
var timer = new System.Timers.Timer
{
Interval = 3000,
AutoReset = false
};
timer.Elapsed += delegate { this.TryClaimBtScannerByDeviceId(deviceId); };
timer.Start();
}
private async Task<bool> ClaimBtScannerByDeviceId(string deviceId)
{
var scanner = await BarcodeScanner.FromIdAsync(deviceId);
scanner.StatusUpdated += this.Scanner_StatusUpdated;
this.btScannerList.Add(scanner);
return await this.ClaimScannerAsync(scanner);
}
private void Scanner_StatusUpdated(BarcodeScanner sender, BarcodeScannerStatusUpdatedEventArgs args)
{
if (args.Status == BarcodeScannerStatus.OffOrOffline || args.Status == BarcodeScannerStatus.Offline || args.Status == BarcodeScannerStatus.Off)
{
Log.Information($"BarcodeService.DeviceWatcher StatusUpdated to off or offline, setting reclaim timer for deviceId: {sender.DeviceId} -> {args.Status}");
var deviceId = sender.DeviceId;
this.UnsetScannerByDeviceId(deviceId);
this.SetReclaimTimerForBtScanner(deviceId);
}
}
private async Task<bool> ClaimScannerAsync(BarcodeScanner scanner)
{
Log.Information($"BarcodeService.ClaimBarcodeScannerAsync() Trying to (re)claim scanner with DeviceId: {scanner.DeviceId} for exclusive use...");
// after successful creation, claim the scanner for exclusive use and enable it so that data reveived events are received.
var claimedScanner = await scanner.ClaimScannerAsync();
if (claimedScanner == null)
{
Log.Warning($"BarcodeService.ClaimBarcodeScannerAsync() Couldn't claim barcode scanner for exclusive use (deviceid {scanner.DeviceId})");
return false;
}
else
{
Log.Information($"BarcodeService.ClaimBarcodeScannerAsync() Claimed scanner for exclusive use. Setting up event handlers...");
// It is always a good idea to have a release device requested event handler. If this event is not handled, there are chances of another app can
// claim ownsership of the barcode scanner.
claimedScanner.ReleaseDeviceRequested += this.ClaimedScanner_ReleaseDeviceRequested;
// after successfully claiming, attach the datareceived event handler.
claimedScanner.DataReceived += this.ClaimedScanner_DataReceived;
// Ask the API to decode the data by default. By setting this, API will decode the raw data from the barcode scanner and
// send the ScanDataLabel and ScanDataType in the DataReceived event
claimedScanner.IsDecodeDataEnabled = true;
// enable the scanner.
// Note: If the scanner is not enabled (i.e. EnableAsync not called), attaching the event handler will not be any useful because the API will not fire the event
// if the claimedScanner has not beed Enabled
await claimedScanner.EnableAsync();
this.ClaimedScannerList.Add(claimedScanner);
Log.Information("BarcodeService.ClaimBarcodeScannerAsync() Ready to scan. Device ID: " + claimedScanner.DeviceId);
return true;
}
}
public void UnsetScannerByDeviceId(string deviceId)
{
try
{
foreach (var claimedScanner in this.ClaimedScannerList.Where(x => x.DeviceId.Equals(deviceId)).ToList())
{
this.DisposeClaimedScanner(claimedScanner);
}
foreach (var scanner in this.btScannerList.Where(x => x.DeviceId.Equals(deviceId)).ToList())
{
this.DisposeScanner(scanner);
}
}
catch
{
Log.Warning($"BarcodeService.UnsetScannerByDeviceId() Error while disposing scanner with scannerId: {deviceId}");
}
}
private void DisposeScanner(BarcodeScanner scanner)
{
scanner.StatusUpdated -= this.Scanner_StatusUpdated;
scanner.Dispose();
this.btScannerList.Remove(scanner);
scanner = null;
}
private void DisposeClaimedScanner(ClaimedBarcodeScanner claimedScanner)
{
// Detach the event handlers
claimedScanner.DataReceived -= this.ClaimedScanner_DataReceived;
claimedScanner.ReleaseDeviceRequested -= this.ClaimedScanner_ReleaseDeviceRequested;
// Release the Barcode Scanner and set to null
claimedScanner.Dispose();
this.ClaimedScannerList.Remove(claimedScanner);
claimedScanner = null;
}