The library i'm using is called Plugin.BLE. It doesn't have good documentation, and I'm just guessing.
First I scan for connected devices, then I connect to the bluetooth printer which is called "MTP-2" then I get it's service. From that service I get the characteristics, and I write data into it, but it doesn't actually do anything.
private async void Print(object sender, EventArgs e)
{
var adapter = CrossBluetoothLE.Current.Adapter;
var devices = adapter.GetSystemConnectedOrPairedDevices();
foreach (var device in devices)
{
var name = device.Name;
if (name == "MTP-2")
{
var cts = new CancellationToken();
var guid = device.Id;
await adapter.ConnectToDeviceAsync(device, new ConnectParameters(true, true), cts);
var services = await device.GetServicesAsync();
foreach (var _service in services)
{
var characteristics = await _service.GetCharacteristicsAsync();
foreach (var characteristic in characteristics)
{
var read = characteristic.CanRead;
var write = characteristic.CanWrite;
var update = characteristic.CanUpdate;
if (read && write && update)
{
await characteristic.StartUpdatesAsync();
var content = "Hi there.";
var data = System.Text.Encoding.ASCII.GetBytes(content);
await characteristic.WriteAsync(data);
await characteristic.StopUpdatesAsync();
return;
}
}
}
}
}
}
I think you have such options:
send a special character to initiate printing. I think the printer should have some documentation for this.
call a special method from the api.
I would like to download multiple download files recursively from a FTP Directory, to do this I'm using FluentFTP library and my code is this one:
private async Task downloadRecursively(string src, string dest, FtpClient ftp)
{
foreach(var item in ftp.GetListing(src))
{
if (item.Type == FtpFileSystemObjectType.Directory)
{
if (item.Size != 0)
{
System.IO.Directory.CreateDirectory(Path.Combine(dest, item.Name));
downloadRecursively(Path.Combine(src, item.Name), Path.Combine(dest, item.Name), ftp);
}
}
else if (item.Type == FtpFileSystemObjectType.File)
{
await ftp.DownloadFileAsync(Path.Combine(dest, item.Name), Path.Combine(src, item.Name));
}
}
}
I know you need one FtpClient per download you want, but how can I make to use a certain number of connections as maximum, I guess that the idea is to create, connect, download and close per every file I find but just having a X number of downloading files at the same time. Also I'm not sure if I should create Task with async, Threads and my biggest problem, how to implement all of this.
Answer from #Bradley here seems pretty good, but the question does read every file thas has to download from an external file and it doesn't have a maximum concurrent download value so I'm not sure how to apply these both requirements.
Use:
ConcurrentBag class to implement a connection pool;
Parallel class to parallelize the operation;
ParallelOptions.MaxDegreeOfParallelism to limit number of the concurrent threads.
var clients = new ConcurrentBag<FtpClient>();
var opts = new ParallelOptions { MaxDegreeOfParallelism = maxConnections };
Parallel.ForEach(files, opts, file =>
{
file = Path.GetFileName(file);
string thread = $"Thread {Thread.CurrentThread.ManagedThreadId}";
if (!clients.TryTake(out var client))
{
Console.WriteLine($"{thread} Opening connection...");
client = new FtpClient(host, user, pass);
client.Connect();
Console.WriteLine($"{thread} Opened connection {client.GetHashCode()}.");
}
string remotePath = sourcePath + "/" + file;
string localPath = Path.Combine(destPath, file);
string desc =
$"{thread}, Connection {client.GetHashCode()}, " +
$"File {remotePath} => {localPath}";
Console.WriteLine($"{desc} - Starting...");
client.DownloadFile(localPath, remotePath);
Console.WriteLine($"{desc} - Done.");
clients.Add(client);
});
Console.WriteLine($"Closing {clients.Count} connections");
foreach (var client in clients)
{
Console.WriteLine($"Closing connection {client.GetHashCode()}");
client.Dispose();
}
Another approach is to start a fixed number of threads with one connection for each and have them pick files from a queue.
For an example of an implementation, see my article for WinSCP .NET assembly:
Automating transfers in parallel connections over SFTP/FTP protocol
A similar question about SFTP:
Processing SFTP files using C# Parallel.ForEach loop not processing downloads
Here is a TPL Dataflow approach. A BufferBlock<FtpClient> is used as a pool of FtpClient objects. The recursive enumeration takes a parameter of type IEnumerable<string> that holds the segments of one filepath. These segments are combined differently when constructing the local and the remote filepath. As a side effect of invoking the recursive enumeration, the paths of the remote files are sent to an ActionBlock<IEnumerable<string>>. This block handles the parallel downloading of the files. Its Completion property contains eventually all the exceptions that may have occurred during the whole operation.
public static Task FtpDownloadDeep(string ftpHost, string ftpRoot,
string targetDirectory, string username = null, string password = null,
int maximumConnections = 1)
{
// Arguments validation omitted
if (!Directory.Exists(targetDirectory))
throw new DirectoryNotFoundException(targetDirectory);
var fsLocker = new object();
var ftpClientPool = new BufferBlock<FtpClient>();
async Task<TResult> UsingFtpAsync<TResult>(Func<FtpClient, Task<TResult>> action)
{
var client = await ftpClientPool.ReceiveAsync();
try { return await action(client); }
finally { ftpClientPool.Post(client); } // Return to the pool
}
var downloader = new ActionBlock<IEnumerable<string>>(async path =>
{
var remotePath = String.Join("/", path);
var localPath = Path.Combine(path.Prepend(targetDirectory).ToArray());
var localDir = Path.GetDirectoryName(localPath);
lock (fsLocker) Directory.CreateDirectory(localDir);
var status = await UsingFtpAsync(client =>
client.DownloadFileAsync(localPath, remotePath));
if (status == FtpStatus.Failed) throw new InvalidOperationException(
$"Download of '{remotePath}' failed.");
}, new ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism = maximumConnections,
BoundedCapacity = maximumConnections,
});
async Task Recurse(IEnumerable<string> path)
{
if (downloader.Completion.IsCompleted) return; // The downloader has failed
var listing = await UsingFtpAsync(client =>
client.GetListingAsync(String.Join("/", path)));
foreach (var item in listing)
{
if (item.Type == FtpFileSystemObjectType.Directory)
{
if (item.Size != 0) await Recurse(path.Append(item.Name));
}
else if (item.Type == FtpFileSystemObjectType.File)
{
var accepted = await downloader.SendAsync(path.Append(item.Name));
if (!accepted) break; // The downloader has failed
}
}
}
// Move on to the thread pool, to avoid ConfigureAwait(false) everywhere
return Task.Run(async () =>
{
// Fill the FtpClient pool
for (int i = 0; i < maximumConnections; i++)
{
var client = new FtpClient(ftpHost);
if (username != null && password != null)
client.Credentials = new NetworkCredential(username, password);
ftpClientPool.Post(client);
}
try
{
// Enumerate the files to download
await Recurse(new[] { ftpRoot });
downloader.Complete();
}
catch (Exception ex) { ((IDataflowBlock)downloader).Fault(ex); }
try
{
// Await the downloader to complete
await downloader.Completion;
}
catch (OperationCanceledException)
when (downloader.Completion.IsCanceled) { throw; }
catch { downloader.Completion.Wait(); } // Propagate AggregateException
finally
{
// Clean up
if (ftpClientPool.TryReceiveAll(out var clients))
foreach (var client in clients) client.Dispose();
}
});
}
Usage example:
await FtpDownloadDeep("ftp://ftp.test.com", "", #"C:\FtpTest",
"username", "password", maximumConnections: 10);
Note: The above implementation enumerates the remote directory lazily, following the tempo of the downloading process. If you prefer to enumerate it eagerly, gathering all info available about the remote listings ASAP, just remove the BoundedCapacity = maximumConnections configuration from the ActionBlock that downloads the files. Be aware that doing so could result in high memory consumption, in case the remote directory has a deep hierarchy of subfolders, containing cumulatively a huge number of small files.
I'd split this into three parts.
Recursively build a list of source and destination pairs.
Create the directories required.
Concurrently download the files.
It's the last part that is slow and should be done in parallel.
Here's the code:
private async Task DownloadRecursively(string src, string dest, FtpClient ftp)
{
/* 1 */
IEnumerable<(string source, string destination)> Recurse(string s, string d)
{
foreach (var item in ftp.GetListing(s))
{
if (item.Type == FtpFileSystemObjectType.Directory)
{
if (item.Size != 0)
{
foreach(var pair in Recurse(Path.Combine(s, item.Name), Path.Combine(d, item.Name)))
{
yield return pair;
}
}
}
else if (item.Type == FtpFileSystemObjectType.File)
{
yield return (Path.Combine(s, item.Name), Path.Combine(d, item.Name));
}
}
}
var pairs = Recurse(src, dest).ToArray();
/* 2 */
foreach (var d in pairs.Select(x => x.destination).Distinct())
{
System.IO.Directory.CreateDirectory(d);
}
/* 3 */
var downloads =
pairs
.AsParallel()
.Select(x => ftp.DownloadFileAsync(x.source, x.destination))
.ToArray();
await Task.WhenAll(downloads);
}
It should be clean, neat, and easy to reason about code.
We're experimenting with speech-to-text using Microsoft Cognitive Services. One of our requirements is to have word level timestamps. This works fine with short wav files, say, 2-3 minutes of audio, but with larger files we're getting an error:
"There was an error deserializing the object of type Microsoft.CognitiveServices.Speech.DetailedSpeechRecognitionResultCollection. The value '2152200000' cannot be parsed as the type 'Int32'."
Any and all hints as to how I can get around this would be greatly appreciated. Thanks in advance!
Code snippet:
config.OutputFormat = OutputFormat.Detailed;
config.RequestWordLevelTimestamps();
using (var audioInput = AudioConfig.FromWavFileInput(wavfile))
{
using var recognizer = new SpeechRecognizer(config, audioInput);
recognizer.Recognized += (s, e) =>
{
if (e.Result.Reason == ResultReason.RecognizedSpeech)
{
var framesStart = TimeSpan.FromTicks(e.Result.OffsetInTicks).TotalMilliseconds / 40;
var te = new TranscriptElement((long)framesStart, e.Result.Text, languageCode);
// Eventually fails on the following line:
var words = e.Result.Best().OrderByDescending(x => x.Confidence).First().Words;
foreach (var w in words.OrderBy(w => w.Offset))
{
var start = TimeSpan.FromTicks(w.Offset).TotalMilliseconds / 40;
var duration = TimeSpan.FromTicks(w.Duration).TotalMilliseconds / 40;
te.SingleWords.Add(new TranscriptSingleWord((long)start, (long)(start + duration), w.Word));
}
transcriptElements.Add(te);
}
else if (e.Result.Reason == ResultReason.NoMatch)
{
_logger.LogError($"NOMATCH: Speech could not be recognized.");
}
};
await recognizer.StartContinuousRecognitionAsync().ConfigureAwait(false);
Task.WaitAny(new[] { stopRecognition.Task });
await recognizer.StopContinuousRecognitionAsync().ConfigureAwait(false);
}
It's a bug in the data type the extension is using for the offset. An int can only track ~214s of audio.
You can access the raw JSON that the Best() method is using from the result's property collection through the SpeechServiceResponse_JsonResult property until a fix is available.
I manage to enumerate my USB device as below. How can I add the enumerateDevices into DeviceWatcher?
I have referred to the following examples but still not getting any progress.
The watcher did trigger once during the startup subsequently any changes in my usb devices doesn't show anything.
[How to get notifications if devices are added, removed, or changed (XAML)][1]
[Device Enumeration Samples][2] Any idea how to constantly monitoring the changes on my USB port to prompt if any of the USB device has been removed or connected?
private void StartWatcher()
{
usbStorageWatcher = DeviceInformation.CreateWatcher(DeviceClass.PortableStorageDevice);
audioCaptureWatcher = DeviceInformation.CreateWatcher(DeviceClass.AudioCapture);
audioRenderWatcher = DeviceInformation.CreateWatcher(DeviceClass.AudioRender);
usbStorageWatcherAdded = new TypedEventHandler<DeviceWatcher, DeviceInformation>(async (watcher, deviceInfo) =>
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Low, async () =>
{
var usbDevices = await DeviceInformation.FindAllAsync(DeviceClass.PortableStorageDevice);
if (usbDevices.Count > 0)
{
for (var i = 0; i < usbDevices.Count; i++)
{
usbDeviceList.Add(usbDevices[i]);
}
usbList.SelectedItem = usbDevices[0];
usbDeviceCount.Text = usbDevices.Count.ToString();
GeneralMessage.Text = String.Format("{0} USB Storage found.", usbDevices.Count);
}
});
});
usbStorageWatcher.Added += usbStorageWatcherAdded;
usbStorageWatcherRemoved = new TypedEventHandler<DeviceWatcher, DeviceInformationUpdate>(async (watcher, deviceInfoUpdate) =>
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Low, async () =>
{
var usbDevices = await DeviceInformation.FindAllAsync(DeviceClass.PortableStorageDevice);
if (usbDevices.Count > 0)
{
for (var i = 0; i < usbDevices.Count; i++)
{
usbDeviceList.Add(usbDevices[i]);
}
usbList.SelectedItem = usbDevices[0];
usbDeviceCount.Text = usbDevices.Count.ToString();
GeneralMessage.Text = String.Format("{0} USB Storage found.", usbDevices.Count);
}
});
});
usbStorageWatcher.Removed += usbStorageWatcherRemoved;
I'm trying to implement a function in my application that lists all the plugged in USB Mass Storage Devices in a computer.
My code works well when launching the application but my problem is that I want the box in my form to refresh automatically when a USB device is removed or attached.
Implementing DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE conditions should work but I get back a "DisconnectedContext was detected" exception.
I learned that I need to use a delegate and set an asyncronous call for this to work correctly.
Here's my code:
public void listUSB()
{
ManagementScope sc = new ManagementScope(wmiUsbList);
ObjectQuery query = new ObjectQuery("select * from Win32_DiskDrive " + "where InterfaceType='USB'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(sc, query);
ManagementObjectCollection result = searcher.Get();
foreach (ManagementObject obj in result)
{
if (obj["DeviceID"] != null)
{
usbListTextBox.AppendText(obj["Model"].ToString());
}
}
}
I'd really like to know how to apply a delegate to my method.
I also looked at this thread on MSDN which provides an excellent example, but as of that example I am not able to understand how to put the deviceList in a textbox.
I'm still Learning so if someone could be so kind to point me to the right direction on one or both of my questions, that would be greatly appreciated.
Thanks!
Try to use ManagementEventWatcher and assign an event handler to the EventArrived.
I don't know how to accomplish exactly this, but here is a watcher that listens to the print events:
string printQuery = "Select * From __InstanceCreationEvent Within 1 Where TargetInstance ISA 'Win32_PrintJob'";
string nspace = #"\\.\root\CIMV2";
var watcher = new ManagementEventWatcher(nspace, printQuery);
Hope it helps.
private usbListArrayDelegate mDeleg;
protected override void WndProc(ref Message m)
{
int devType;
base.WndProc(ref m);
switch (m.WParam.ToInt32())
{
case DBT_DEVICEARRIVAL:
devType = Marshal.ReadInt32(m.LParam, 4);
if (devType == DBT_DEVTYP_VOLUME)
{
// usb device inserted, call the query
mDeleg = new usbListArrayDelegate(usbListArray);
AsyncCallback callback = new AsyncCallback(usbListArrayCallback);
// invoke the thread that will handle getting the friendly names
mDeleg.BeginInvoke(callback, new object());
}
break;
case DBT_DEVICEREMOVECOMPLETE:
devType = Marshal.ReadInt32(m.LParam, 4);
if (devType == DBT_DEVTYP_VOLUME)
{
mDeleg = new usbListArrayDelegate(usbListArray);
AsyncCallback callback = new AsyncCallback(usbListArrayCallback);
// invoke the thread that will handle getting the friendly names
mDeleg.BeginInvoke(callback, new object());
}
break;
}
}
public ArrayList usbListArray()
{
ArrayList deviceList = new ArrayList();
manager = new UsbManager(); ==> about how to implement this please see http://www.codeproject.com/Articles/63878/Enumerate-and-Auto-Detect-USB-Drives
UsbDiskCollection disks = manager.GetAvailableDisks();
foreach (UsbDisk disk in disks)
{
deviceList.Add(disk);
}
return deviceList;
}
// delegate wrapper function for the getFriendlyNameList function
private delegate ArrayList usbListArrayDelegate();
// callback method when the thread returns
private void usbListArrayCallback(IAsyncResult ar)
{
string ArrayData = string.Empty;
// got the returned arrayList, now we can do whatever with it
ArrayList result = mDeleg.EndInvoke(ar);
int count = 0;
foreach (UsbDisk disk in result)
{
++count;
ArrayData += count + ") " + disk.ToString().
}