Waiting for event to finish - c#

Im trying to convert the response from the webclient to Json, but it's trying to create the JSON object before it is done downloaing it from the server.
Is there a "nice" way to for me to wait for WebOpenReadCompleted to be executed?
Have to mention that this is a WP7 app, so everything is Async
public class Client
{
public String _url;
private String _response;
private WebClient _web;
private JObject jsonsobject;
private Boolean blockingCall;
private Client(String url)
{
_web = new WebClient();
_url = url;
}
public JObject Login(String username, String password)
{
String uriUsername = HttpUtility.UrlEncode(username);
String uriPassword = HttpUtility.UrlEncode(password);
Connect(_url + "/data.php?req=Login&username=" + uriUsername + "&password=" + uriPassword + "");
jsonsobject = new JObject(_response);
return jsonsobject;
}
public JObject GetUserInfo()
{
Connect(_url + "/data.php?req=GetUserInfo");
jsonsobject = new JObject(_response);
return jsonsobject;
}
public JObject Logout()
{
Connect(_url + "/data.php?req=Logout");
jsonsobject = new JObject(_response);
return jsonsobject;
}
private void Connect(String url)
{
_web.Headers["Accept"] = "application/json";
_web.OpenReadCompleted += new OpenReadCompletedEventHandler(WebOpenReadCompleted);
_web.OpenReadAsync(new Uri(url));
}
private void WebOpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error != null || e.Cancelled)
{
MessageBox.Show("Error:" + e.Error.Message);
_response = "";
}
else
{
using (var reader = new StreamReader(e.Result))
{
_response = reader.ReadToEnd();
}
}
}
}

You can use an EventWaitHandle to nicely block until the async read is complete. I had a similar requirement for downloading files with WebClient. My solution was to subclass WebClient. Full source is below. Specifically, DownloadFileWithEvents blocks nicely until the async download completes.
It should be pretty straightforward to modify the class for your purpose.
public class MyWebClient : WebClient, IDisposable
{
public int Timeout { get; set; }
public int TimeUntilFirstByte { get; set; }
public int TimeBetweenProgressChanges { get; set; }
public long PreviousBytesReceived { get; private set; }
public long BytesNotNotified { get; private set; }
public string Error { get; private set; }
public bool HasError { get { return Error != null; } }
private bool firstByteReceived = false;
private bool success = true;
private bool cancelDueToError = false;
private EventWaitHandle asyncWait = new ManualResetEvent(false);
private Timer abortTimer = null;
const long ONE_MB = 1024 * 1024;
public delegate void PerMbHandler(long totalMb);
public event PerMbHandler NotifyMegabyteIncrement;
public MyWebClient(int timeout = 60000, int timeUntilFirstByte = 30000, int timeBetweenProgressChanges = 15000)
{
this.Timeout = timeout;
this.TimeUntilFirstByte = timeUntilFirstByte;
this.TimeBetweenProgressChanges = timeBetweenProgressChanges;
this.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(MyWebClient_DownloadFileCompleted);
this.DownloadProgressChanged += new DownloadProgressChangedEventHandler(MyWebClient_DownloadProgressChanged);
abortTimer = new Timer(AbortDownload, null, TimeUntilFirstByte, System.Threading.Timeout.Infinite);
}
protected void OnNotifyMegabyteIncrement(long totalMb)
{
if (NotifyMegabyteIncrement != null) NotifyMegabyteIncrement(totalMb);
}
void AbortDownload(object state)
{
cancelDueToError = true;
this.CancelAsync();
success = false;
Error = firstByteReceived ? "Download aborted due to >" + TimeBetweenProgressChanges + "ms between progress change updates." : "No data was received in " + TimeUntilFirstByte + "ms";
asyncWait.Set();
}
void MyWebClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
if (cancelDueToError) return;
long additionalBytesReceived = e.BytesReceived - PreviousBytesReceived;
PreviousBytesReceived = e.BytesReceived;
BytesNotNotified += additionalBytesReceived;
if (BytesNotNotified > ONE_MB)
{
OnNotifyMegabyteIncrement(e.BytesReceived);
BytesNotNotified = 0;
}
firstByteReceived = true;
abortTimer.Change(TimeBetweenProgressChanges, System.Threading.Timeout.Infinite);
}
public bool DownloadFileWithEvents(string url, string outputPath)
{
asyncWait.Reset();
Uri uri = new Uri(url);
this.DownloadFileAsync(uri, outputPath);
asyncWait.WaitOne();
return success;
}
void MyWebClient_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
if (cancelDueToError) return;
asyncWait.Set();
}
protected override WebRequest GetWebRequest(Uri address)
{
var result = base.GetWebRequest(address);
result.Timeout = this.Timeout;
return result;
}
void IDisposable.Dispose()
{
if (asyncWait != null) asyncWait.Dispose();
if (abortTimer != null) abortTimer.Dispose();
base.Dispose();
}
}

I see you are using OpenReadAsync(). This is an asynchronous method, meaning that the calling thread is not suspended while the handler is executing.
This means your assignment operation setting jsonsobject happens while WebOpenReadCompleted() is still executing.
I'd say your best bet is to replace OpenReadAsync(new Uri(url)) with OpenRead(new Uri(url)) in your Connect(string url) method.
OpenRead() is a synchronous operation, so the calling method will wait until the WebOpenReadCompleted() method is complete before your assignment occurs in the Connect() method.

Related

Disable a button, when a Task is completed

I have a method, that checks if a button can be pressed or not, if my file path or the language is null, I don't enable the button, and of course, when my file path is selected, I raise the event.
So right now I am doing some work on Azure, and I want to disable the button when I start my work and enable it when I finished my work.
I tried to raise the event, before the call o the method and after the call, but it doesn't enable the button
public string? FilePath { get; set; }
public bool IsWorking { get; set; }
public Dictionary<int, Languages>? LanguagesDictionary { get; set; }
public Visibility CanShow { get; set; }
public DialogHelper DialogHelper { get; }
public FolderHelper FolderHelper { get; }
public AudioHelper AudioHelper { get; }
public AzureTranscriptionService AzureTranscription { get; }
public Command PickFileCommad { get; set; }
public Command StartCommand { get; set; }
private string? _SelectedItem;
public string SelectedItem {
get => _SelectedItem!;
set {
if (_SelectedItem != value) {
_SelectedItem = value;
StartCommand.RaiseCanExecuteChanged();
}
}
}
public AudioPageViewModel() {
InitListLanguages();
AzureTranscription = new AzureTranscriptionService();
DialogHelper = new DialogHelper();
FolderHelper = new FolderHelper();
AudioHelper = new AudioHelper();
CanShow = Visibility.Hidden;
PickFileCommad = new Command(PickFileAction);
StartCommand = new Command(StartAction, CanStartAction);
}
private bool CanStartAction(object arg) {
if (string.IsNullOrEmpty(SelectedItem) ||
string.IsNullOrEmpty(FilePath) ||
IsWorking == true) {
return false;
}
return true;
}
private async void StartAction(object obj) {
var FileWithoutExtension = Path.GetFileNameWithoutExtension
(FilePath);
var AudioPath = FolderHelper.CreateFolder(ConstantsHelpers.AUDIO);
var DocumentPath = FolderHelper.CreateFolder();
var AudioFileNamePath = Path.Combine(AudioPath, $"{FileWithoutExtension}{ConstantsHelpers.WAV}");
var ConvertedAudioPath = AudioHelper.Converter(FilePath!, AudioFileNamePath);
var DocumentName = Path.Combine(DocumentPath, $"{FileWithoutExtension}{ConstantsHelpers.DOCX}");
IsWorking = true;
StartCommand.RaiseCanExecuteChanged();
await AzureTranscription.ConvertToTextAsync(ConvertedAudioPath,
SelectedItem, DocumentName);
IsWorking = false;
StartCommand.RaiseCanExecuteChanged();
}
private void PickFileAction() {
var FullPath = DialogHelper.GetFilePath(ConstantsHelpers.AUDIO);
FilePath = FullPath;
StartCommand?.RaiseCanExecuteChanged();
}
public async Task ConvertToTextAsync(
string FilePath,
string Language,
string WordDocName) {
// Configure speech service
var config = SpeechConfig.FromSubscription(ConstantsHelpers.AZURE_KEY, ConstantsHelpers.AZURE_REGION);
config.SpeechRecognitionLanguage = Language;
// Configure speech recognition
var taskCompleteionSource = new TaskCompletionSource<int>();
using var audioConfig = AudioConfig.FromWavFileInput(FilePath);
using var speechRecognizer = new SpeechRecognizer(config, audioConfig);
speechRecognizer.Recognizing += SpeechRecognizer_Recognizing;
speechRecognizer.Recognized += SpeechRecognizer_Recognized;
speechRecognizer.SessionStarted += SpeechRecognizer_SessionStarted;
speechRecognizer.SessionStopped += SpeechRecognizer_SessionStopped;
await speechRecognizer.StartContinuousRecognitionAsync().ConfigureAwait(false);
Task.WaitAny(new[] { taskCompleteionSource.Task });
await speechRecognizer.StopContinuousRecognitionAsync().ConfigureAwait(false);
}
private void SpeechRecognizer_SessionStopped(object? sender, SessionEventArgs e) {
Debug.WriteLine("Stepped");
var sb = new StringBuilder();
foreach (var item in Letters) {
sb.Append(item);
}
}
private void SpeechRecognizer_SessionStarted(object? sender, SessionEventArgs e) {
Debug.WriteLine("Started");
}
private void SpeechRecognizer_Recognized(object? sender, SpeechRecognitionEventArgs e) {
if (e.Result.Reason == ResultReason.RecognizedSpeech) {
foreach (var item in e.Result.Text) {
Letters.Add(item);
}
}
}
private void SpeechRecognizer_Recognizing(object? sender, SpeechRecognitionEventArgs e) {
Debug.WriteLine(e.Result.Text);
}
}
When I start working, I execute this code
Many thanks
If the below refactorings don't help, debug your program:
check if CanStartAction is actually called and returns the expected result.
probably the asynchronous method returns too fast for your eye to see the button being disabled. Modify your code as follows to test this:
await AzureTranscription.ConvertToTextAsync(ConvertedAudioPath,
SelectedItem, DocumentName);
await Task.Delay(TimeSpan.FromSeconds(5));
In general, move the RaiseCanExecuteChanged request to the relevant property setters to keep your code clean.
private bool isBusy;
private bool IsBusy
{
get => this.isBusy;
set
{
this.isBusy = value;
this.StartCommand.RaiseCanExecuteChanged();
}
}
private bool CanStartAction(object arg)
{
return !string.IsNullOrEmpty(SelectedItem) &&
!string.IsNullOrEmpty(FilePath) &&
!this.IsBusy
}
private async void StartAction(object obj)
{
this.IsBusy = true;
var fileWithoutExtension = Path.GetFileNameWithoutExtension
(FilePath);
var audioPath = FolderHelper.CreateFolder(ConstantsHelpers.AUDIO);
var documentPath = FolderHelper.CreateFolder();
var audioFileNamePath = Path.Combine(audioPath, $"{FileWithoutExtension}{ConstantsHelpers.WAV}");
var convertedAudioPath = AudioHelper.Converter(FilePath!, audioFileNamePath);
var documentName = Path.Combine(documentPath, $"{fileWithoutExtension}{ConstantsHelpers.DOCX}");
await AzureTranscription.ConvertToTextAsync(convertedAudioPath,
this.SelectedItem, documentName);
this.IsBusy = false;
}
public async Task ConvertToTextAsync(string FilePath,
string Language,
string WordDocName)
{
...
// Using Task.Wait, Task.WaitAny and Task.WaitAll will execute a Task synchronously and introduces a potential deadlock.
// To avoid this, always await a Task and use Task.WhenAny and Task.WhenAll instead
await Task.WhenAny(new[] { taskCompleteionSource.Task });
// Or because you only have to wait for a single Task write
await taskCompleteionSource.Task;
...
}
See C# Naming guidelines

System.NotSupportedException when Init BitmapImage

I receive an array of messages with attachments from the API and create on their basis Models for UserControls. In model I call Initial method for download image in local storage.But if I open dialog(recieve array from the API) as soon as i run the app, I get an error message System.NotSupportedException in PresentationCore.dll
Additional Information: No pixel format information found.
Here is code of Model:
Most interesting i have catch on it but still get
public class MediaMessageAttachment : ViewModelBase
{
public string FullImageURI { get; set; }
public string VideoURI { get; set; }
public string Title { get; set; }
public bool HasVideo => VideoURI != null;
public bool HasImage => FullImageURI != null;
public string Duration { get; set; }
public bool IsLoading
{
get { return isLoading; }
set
{
isLoading = value;
RaisePropertyChanged("IsLoading");
}
}
private BitmapImage image;
private string previewImageURI;
private bool isLoading;
public string PreviewImageURI { get { return previewImageURI; } set { previewImageURI = value; RaisePropertyChanged("PreviewImageURI"); Initial(); } }
public BitmapImage Image
{
get { return image; }
set
{
image = value;
RaisePropertyChanged("Image");
}
}
public RelayCommand OpenImageCommand
{
get;
private set;
}
public MediaMessageAttachment()
{
OpenImageCommand = new RelayCommand(() => OpenImage());
}
private void OpenImage()
{
if (HasImage)
PopupVM.ShowPopup(FullImageURI);
else if (HasVideo)
PopupVM.ShowPopup(VideoURI);
}
public async Task Initial()
{
IsLoading = true;
if (!Directory.Exists(Environment.CurrentDirectory + "\\temp"))
{
Directory.CreateDirectory(Environment.CurrentDirectory + "\\temp");
}
using (WebClient client = new WebClient())
{
var path = Environment.CurrentDirectory + "\\temp\\" + PreviewImageURI.Substring(PreviewImageURI.LastIndexOf('/') + 1);
if (File.Exists(path))
DownloadFileCompleted(null, null);
else
{
client.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadFileCompleted);
await client.DownloadFileTaskAsync(new Uri(PreviewImageURI), path);
}
}
}
private void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
try
{
var path = Environment.CurrentDirectory + "\\temp\\" + PreviewImageURI.Substring(PreviewImageURI.LastIndexOf('/') + 1);
Image = new BitmapImage();
Image.BeginInit();
Image.UriSource = new Uri(path);
Image.CacheOption = BitmapCacheOption.OnLoad;
Image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
Image.EndInit();
Image.Freeze();
IsLoading = false;
}
catch (System.NotSupportedException)
{
Initial();
}
}
}

Asynchronous socket + waitone()

Let's start with some code:
class InternetConnector
{
private struct ConnectionData
{
public Action<Socket> SuccessHandler { get; set; }
public ClientStateObject clientObj { get; set; }
public Action<Exception> ErrorHandler { get; set; }
public Socket Socket { get; set; }
}
public static ManualResetEvent processingDone = new ManualResetEvent( false );
public static ConcurrentQueue<string> messages = new ConcurrentQueue<string>();
public bool ReceiveMessage(Action<Socket> successHandler, Action<Exception> errorHandler)
{
ClientStateObject obj = new ClientStateObject();
obj.server = client;
var connectionData = new ConnectionData
{
ErrorHandler = errorHandler,
SuccessHandler = successHandler,
Socket = client,
clientObj = obj
};
if (Connected)
{
client.BeginReceive(connectionData.clientObj.buffer, 0, ClientStateObject.bufSize, 0, new AsyncCallback(ReceiveCallback), connectionData);
receive = true;
receiveDone.WaitOne();
}
return receive;
}
private static void ReceiveCallback(IAsyncResult ar)
{
ConnectionData connectionData = new ConnectionData();
bool complete = false;
try
{
connectionData = (ConnectionData)ar.AsyncState;
Socket client = connectionData.Socket;
int num = client.EndReceive(ar);
{
connectionData.clientObj.stringBuffer.Append(Encoding.ASCII.GetString(connectionData.clientObj.buffer, 0, num));
string response = connectionData.clientObj.stringBuffer.ToString();
string[] msgs = response.Split('&');
for (int i = 0; i < msgs.Count(); i++)
{
string sts = msgs[i];
messages.Enqueue(sts + "&" );
}
receiveDone.Set();
if (connectionData.SuccessHandler != null)
{
connectionData.SuccessHandler(client);
processingDone.WaitOne();
client.BeginReceive(connectionData.clientObj.buffer, 0, ClientStateObject.bufSize, 0, new AsyncCallback(ReceiveCallback), connectionData);
}
}
}
catch (Exception e)
{
if (connectionData.ErrorHandler != null)
connectionData.ErrorHandler(e);
}
}
public partial class Form1 : Form
{
private InternetConnector client = new InternetConnector();
private bool isRunning = false;
private void AsyncSuccessHandler(Socket socket)
{
if (InvokeRequired)
{
BeginInvoke(new Action( () => AsyncSuccessHandler( socket ) ));
return;
}
if (InternetConnector.messages.Count() == 0)
{
status.Text = "Signals Receiver: Connected";
status.ForeColor = Color.Green;
isRunning = true;
client.ReceiveMessage(AsyncSuccessHandler, AsyncErrorHandler);
}
else
{
GUIChangeOnConnection();
InternetConnector.processingDone.Set();
}
}
private void GUIChangeOnConnection()
{
for( int i = 0; i < InternetConnector.messages.Count; i++ )
{
string message;
InternetConnector.messages.TryDequeue( out message );
// process the message
}
}
}
Now the problem.
Everything works fine. Reading from the socket is happening. However the call processingDone.WaitOne(); which should block the callback indefinitely until the call to processingDone.Set(); returns too early.
I verified it by setting the breakpoint at the end of the GUIChangeOnConnection(); - function closing bracket line. It hits the breakpoint and looking at the InternetConnector.messages I see that the queue is not empty, which means that the for loop did not finish.
And the second time this breakpoint is hit the number of messages in the queue is sky-rocketing.
What am I doing wrong? Or maybe my design is incorrect?
Thank you.

Show downloading progress in c#

I have created this program which sends a html request to twitter and downloads tweets into a database. This program is in console and is coded using c#. I am not sure on the displaying the downloading progress of the user information.
Example: I am downloading screenname, location, etc for user alice and am writing it into database
Question: How can I show the user the progress as in 10% completed, something like that.
My code for the web request (small example) as of follows :
WebClient wc = new WebClient();
string url = "https://api.twitter.com/1/users/lookup.json?screen_name=" +username;
It just so happens that I wrote a custom subclass of WebClient that can show update progress. It raises an event every 1MB (because that was my need, see NotifyMegabyteIncrement) but could easily be modified.
public class MyWebClient : WebClient, IDisposable
{
public int Timeout { get; set; }
public int TimeUntilFirstByte { get; set; }
public int TimeBetweenProgressChanges { get; set; }
public long PreviousBytesReceived { get; private set; }
public long BytesNotNotified { get; private set; }
public string Error { get; private set; }
public bool HasError { get { return Error != null; } }
private bool firstByteReceived = false;
private bool success = true;
private bool cancelDueToError = false;
private EventWaitHandle asyncWait = new ManualResetEvent(false);
private Timer abortTimer = null;
const long ONE_MB = 1024 * 1024;
public delegate void PerMbHandler(long totalMb);
public event PerMbHandler NotifyMegabyteIncrement;
public MyWebClient(int timeout = 60000, int timeUntilFirstByte = 30000, int timeBetweenProgressChanges = 15000)
{
this.Timeout = timeout;
this.TimeUntilFirstByte = timeUntilFirstByte;
this.TimeBetweenProgressChanges = timeBetweenProgressChanges;
this.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(MyWebClient_DownloadFileCompleted);
this.DownloadProgressChanged += new DownloadProgressChangedEventHandler(MyWebClient_DownloadProgressChanged);
abortTimer = new Timer(AbortDownload, null, TimeUntilFirstByte, System.Threading.Timeout.Infinite);
}
protected void OnNotifyMegabyteIncrement(long totalMb)
{
if (NotifyMegabyteIncrement != null) NotifyMegabyteIncrement(totalMb);
}
void AbortDownload(object state)
{
cancelDueToError = true;
this.CancelAsync();
success = false;
Error = firstByteReceived ? "Download aborted due to >" + TimeBetweenProgressChanges + "ms between progress change updates." : "No data was received in " + TimeUntilFirstByte + "ms";
asyncWait.Set();
}
void MyWebClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
if (cancelDueToError) return;
long additionalBytesReceived = e.BytesReceived - PreviousBytesReceived;
PreviousBytesReceived = e.BytesReceived;
BytesNotNotified += additionalBytesReceived;
if (BytesNotNotified > ONE_MB)
{
OnNotifyMegabyteIncrement(e.BytesReceived);
BytesNotNotified = 0;
}
firstByteReceived = true;
abortTimer.Change(TimeBetweenProgressChanges, System.Threading.Timeout.Infinite);
}
public bool DownloadFileWithEvents(string url, string outputPath)
{
asyncWait.Reset();
Uri uri = new Uri(url);
this.DownloadFileAsync(uri, outputPath);
asyncWait.WaitOne();
return success;
}
void MyWebClient_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
if (cancelDueToError) return;
asyncWait.Set();
}
protected override WebRequest GetWebRequest(Uri address)
{
var result = base.GetWebRequest(address);
result.Timeout = this.Timeout;
return result;
}
void IDisposable.Dispose()
{
if (asyncWait != null) asyncWait.Dispose();
if (abortTimer != null) abortTimer.Dispose();
base.Dispose();
}
}
private WebClient mWebClient = new WebClient();
//...
mWebClient.DownloadProgressChanged += (sender, e) => progressPercentageChanged(e.ProgressPercentage); //your method to display the percentage
mWebClient.DownloadFileCompleted += (sender, e) => yourMethodToProcessTheFile();
mWebClient.DownloadFileAsync(uri, fileNameOnDisk);
//you need to keep the instance of webclient, so it does not get garbage collected

C# multi-thread ping

I'm working on a network monitoring application, that pings a (not known) number of hosts. So far I have the code below. I've made a class PingHost with a function zping and I called it with the help of a timer once every 2 seconds to let the 2 pings to finish, even if one of them gets TimedOut. But I think a better solution is to generate a new thread for every ping, so that the ping of every host would be independent.
Can anyone give me a hint how to do this?
namespace pinguin
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
PingHost caca = new PingHost();
PingHost caca1 = new PingHost();
this.label1.Text = caca.zping("89.115.14.160");
this.label2.Text = caca1.zping("89.115.14.129");
}
}
public class PingHost
{
public string zping(string dest)
{
Application.DoEvents();
Ping sender = new Ping();
PingOptions options = new PingOptions();
options.DontFragment = true;
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
int timeout = 50;
int failed = 0;
int pingAmount = 5;
string stat = "";
PingReply reply = sender.Send(dest, timeout, buffer, options);
if (reply.Status == IPStatus.Success)
{
stat = "ok";
}
else
{
stat = "not ok!";
}
return stat;
}
}
}
If you use .NET 4 you can use Parallel.Invoke.
You could handle the Ping.PingCompleted event:
ping.PingCompleted += new PingCompletedEventHandler(ping_PingCompleted);
then use:
ping.SendAsync()
side note: Choose more suitable names for your classes and routines. PingHost is more suitable as a routine name
Once I wrote such a solution (it constantly pings about 300 machines):
public class ManyAdressPing {
private readonly bool bAutoStarted;
private readonly CancellationTokenSource cancel = new CancellationTokenSource();
public ConcurrentDictionary<IPAddress, OneAddressPing> pingi = new ConcurrentDictionary<IPAddress, OneAddressPing>();
public ManyAdressPing(bool AutoStarted = true) {
bAutoStarted = AutoStarted;
}
public int CountPings => pingi.Count;
public void AddPingAddress(IPAddress addr, int msTimeOut = 3000, int BetweenPing = 3000) {
var oap = new OneAddressPing(addr, cancel.Token, msTimeOut, BetweenPing);
if (bAutoStarted) oap.Start();
pingi.TryAdd(oap.ipAddress, oap);
}
public void RemovePingAddress(IPAddress addr) {
if (pingi.TryRemove(addr, out var p)) p.Stop();
}
public void Stop() {
cancel.Cancel();
foreach (var pair in pingi) pair.Value.Stop();
}
public PingReply GetReply(IPAddress addr) {
if (pingi.ContainsKey(addr)) return pingi[addr].GetReply();
return null;
}
public Tuple<long, long> GetSuccessOperation(IPAddress addr) {
if (pingi.ContainsKey(addr)) return pingi[addr].GetSuccessOperation();
return null;
}
public PingReply[] GetReply() {
PingReply[] ret = pingi.Values.Select(x=>x.GetReply()).ToArray();
return ret;
}
public PingInfo GetPingInfo(IPAddress addr) {
if (pingi.ContainsKey(addr)) {
var ret = new PingInfo();
var p = pingi[addr];
ret.reply = p.GetReply();
ret.SuccessPing = p._SuccessReply;
ret.FailPing = p._FailReply;
ret.LastSuccessPing = p.LastSuccessfullPing;
return ret;
}
return null;
}
public bool IsPinged(IPAddress addr) {
if (pingi.ContainsKey(addr)) return true;
return false;
}
public IPAddress[] GetAddressesPing() {
return pingi.Keys.ToArray();
}
}
public class PingInfo {
public PingReply reply;
public long SuccessPing = 0;
public long FailPing = 0;
public DateTime LastSuccessPing;
public override string ToString() {
return $"Sping: {SuccessPing} last={LastSuccessPing}, Fping:{FailPing}, reply:{reply}";
}
}
public class OneAddressPing {
public static byte[] bu = {
0
};
public long _FailReply;
public long _SuccessReply;
private bool bStop = false;
private readonly CancellationToken cancellationToken;
public DateTime LastSuccessfullPing = DateTime.MinValue;
public int mSecBetweenPing = 3000;
public Ping ping;
public PingOptions popt;
private Task pTask;
// Here is a self-written LIFO stack
public LightQueue<PingReply> replys = new LightQueue<PingReply>(10);
private readonly AutoResetEvent reset = new AutoResetEvent(false);
private Logger log = null;
private Task pinging = null;
public OneAddressPing(IPAddress addr, CancellationToken ct, int timeOut = 3000, int BetweenPing = 3000, Logger _log =null) {
ipAddress = addr;
popt = new PingOptions();
popt.DontFragment = false;
cancellationToken = ct;
mSecTimeOut = timeOut;
mSecBetweenPing = BetweenPing;
log = _log;
}
public int mSecTimeOut { get; set; } = 3000;
public IPAddress ipAddress { get; set; }
public int CountPings => replys.Length;
private void SetReply(PingReply rep) {
if (rep == null) return;
replys.Put(rep);
if (rep.Status == IPStatus.Success) {
Interlocked.Increment(ref _SuccessReply);
LastSuccessfullPing = DateTime.Now;
} else {
Interlocked.Increment(ref _FailReply);
}
}
public async Task Start() {
if (pTask == null || pTask.Status != TaskStatus.Running) {
ping = new Ping();
Task.Factory.StartNew(PingCircle, TaskCreationOptions.RunContinuationsAsynchronously | TaskCreationOptions.LongRunning); pTask = Task.Run(PingCircle, cancellationToken);
}
}
public void Stop() {
if (pTask.Status == TaskStatus.Running) {
bStop = true;
try {
pTask.Wait(mSecTimeOut, cancellationToken);
} catch (Exception ex) {
log.ErrorSource($"Error ping stop: {ex.Message}");
}
}
}
private async Task PingCircle() {
while (cancellationToken.IsCancellationRequested == false && !bStop) {
try {
try {
PingReply rep = await ping.SendPingAsync(ipAddress, mSecTimeOut, bu,popt);
if (rep != null) SetReply(rep);
} catch (PingException p) {
// ignore ping error
Debug.WriteLine($"error: {p}");
} catch (Exception ee) {
log?.ErrorSource(ee);
Debug.WriteLine($"error: {ee}");
}
await Task.Delay(mSecBetweenPing, cancellationToken);
} catch (Exception ee) {
log?.ErrorSource(ee);
}
}
}
public PingReply GetReply() {
if (replys.IsEmpty) return null;
return replys.PeekLast(0);
}
public Tuple<long, long> GetSuccessOperation() {
return new Tuple<long, long>(_SuccessReply, _FailReply);
}
public bool LongPingSuccess() {
int ret = 0;
for (int i = 0; i < 5; i++) {
var r = replys.PeekLast(i);
if (r.Status == IPStatus.Success) ret++;
}
if (ret > 2) return true;
return false;
}
}

Categories

Resources