Actually, I am trying to write an open-source project called Telemetrium. It is a baby step but I got stuck with something: I will explain the situation in 2 parts
Without Telemetrium
With Telemetrium
Without Telemetrium:
Files :
Function1.cs
Startup.cs
TelemetryInitializer.cs
Everything is working good like below:
TelemetryInitializer.cs:
public class TelemetryInitializer : ITelemetryInitializer
{
private readonly IHttpContextAccessor _httpContextAccessor;
public TelemetryInitializer(IHttpContextAccessor httpContextAccessor)
{
if (httpContextAccessor == null) throw new System.ArgumentNullException(nameof(httpContextAccessor));
_httpContextAccessor = httpContextAccessor;
}
public async void Initialize(ITelemetry telemetry)
{
if (telemetry is DependencyTelemetry)
{
var dependencyTelemetry = telemetry as DependencyTelemetry;
if (dependencyTelemetry.Success == true)
dependencyTelemetry = dependencyTelemetry.ActivityToTelemetry(System.Diagnostics.Activity.Current) as DependencyTelemetry;
dependencyTelemetry.Success = true;
}
var requestTelemetry = telemetry as RequestTelemetry;
if (requestTelemetry == null) return;
var context = _httpContextAccessor.HttpContext;
if (context == null) return;
try
{
if (context.Request != null)
{
requestTelemetry = requestTelemetry.ActivityToTelemetry(System.Diagnostics.Activity.Current) as RequestTelemetry;
context.Request.EnableBuffering();
context.Request.Body.Position = 0;
requestTelemetry.Properties[$"Request"] = await new StreamReader(context.Request.Body).ReadToEndAsync();
foreach (var headerName in context.Request.Headers)
{
var header = context.Request.Headers[headerName.Key];
requestTelemetry.Properties[$"Request-{headerName.Key}"] = header;
}
}
if (context.Response != null && (context.Response.Body.CanRead && context.Response.Body.CanSeek))
{
requestTelemetry.Properties[$"Response"] = await new StreamReader(context.Response.Body).ReadToEndAsync();
foreach (var headerName in context.Response.Headers)
{
var header = context.Response.Headers[headerName.Key];
requestTelemetry.Properties[$"Response-{headerName.Key}"] = header;
}
}
}
catch (ObjectDisposedException ex)
{
Console.WriteLine($"obj accessed after dispose!: {ex.Message}");
Debug.WriteLine($"obj accessed after dispose!: {ex.Message}");
requestTelemetry.Success = false;
}
requestTelemetry.Success = true;
}
}
public static class Extensions
{
public static ITelemetry ActivityToTelemetry(this OperationTelemetry telemetry, Activity activity)
{
if (telemetry == null)
throw new ArgumentNullException(nameof(telemetry));
if (activity.Tags.Count() > 0)
foreach (var tag in activity.Tags)
telemetry.Properties[tag.Key] = tag.Value;
telemetry.Properties["Id"] = activity.Id;
telemetry.Properties["ParentId"] = activity.ParentId;
telemetry.Properties["RootId"] = activity.RootId;
return telemetry;
}
}
But I decided to make some changes for Telemetrium:
What I did:
I added Attributes
I developed reflection based Extension
This is Telemetrium.cs:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class TelemetriumAttribute : Attribute
{
private TelemetryEnum TelemetryType;
private string MethodName;
public TelemetriumAttribute(TelemetryEnum TelemetryType, string MethodName)
{
this.TelemetryType = TelemetryType;
this.MethodName = MethodName;
}
public void RUN()
{
if (TelemetryType == TelemetryEnum.RequestTelemtry)
{
var req = Telemetrium.telemetryClient.StartOperation<RequestTelemetry>(this.MethodName);
}
else if (TelemetryType == TelemetryEnum.DependencyTelemetry)
{
var dep = Telemetrium.telemetryClient.StartOperation<DependencyTelemetry>(this.MethodName);
}
}
}
public enum TelemetryEnum
{
RequestTelemtry, DependencyTelemetry
}
public static class Telemetrium
{
public static TelemetryClient telemetryClient { get; set; }
static public void StartTelemetrium(this Function1 This)
{
Type objType = typeof(Function1);
try
{
MethodInfo[] info = objType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance);
var methods = objType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Where(m => m.GetCustomAttributes(typeof(TelemetriumAttribute), false).Length > 0).ToArray();
var customAttributes = objType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Where(m => m.GetCustomAttributes(typeof(TelemetriumAttribute), false).Length > 0)
.Select(q => q.GetCustomAttributes(typeof(TelemetriumAttribute), false)).ToArray();
if (customAttributes.Length > 0 && customAttributes[0] != null)
{
foreach (var customAttribute in customAttributes)
{
foreach (var item in customAttribute)
{
TelemetriumAttribute _customAttribute = item as TelemetriumAttribute;
_customAttribute.RUN();
}
}
}
}
// catch ArgumentNullException here
catch (ArgumentNullException e)
{
Console.Write("name is null.");
Console.Write("Exception Thrown: ");
Console.Write("{0}", e.GetType(), e.Message);
}
}
}
By using Telemtrium I want to Convert method1 in Function1.cs from
private void method1()
{
using (var dep1 = Telemetrium.telemetryClient.StartOperation<DependencyTelemetry>("method1"))
{
int a = 2;
int b = 2;
int v = a * b;
dep2.Telemetry.Success = true;
}
}
TO
[Telemetrium(TelemetryEnum.DependencyTelemetry, "method1")]
private void method1()
{
//using (var dep1 = telemetryClient.StartOperation<DependencyTelemetry>("method1"))
//{
int a = 2;
int b = 2;
int v = a * b;
//dep1.Telemetry.Success = true;
//}
}
As a Result:
I don`t want to use like that
using (var getDetailsOperation = Telemetrium.telemetryClient.StartOperation("GetProductDetails"))
When any function in azure it should be automatically can in RUN method.
also when submethods run, using (var dep2 = Telemetrium.telemetryClient.StartOperation("method2")) should be created automatically. But How?
Related
I've written a Xamarin.Forms Application that connects to an ESP32 via Bluetooth. Now I'd like to get a value from a CustomControl.JoystickControl from the MainPage.xaml Page.
I've tried it like that:
MainPage.xaml.cs:
public partial class MainPage : ContentPage
{
public static Bluetooth.IBth bth = new Bluetooth.Bth();
public MainPage()
{
InitializeComponent();
Task.Run(async () => bth.Start("mecanumWheelRobot", 200, false));
}
public CustomControls.JoystickControl JoystickControlElement
{
get { return JoystickControl; }
}
public CustomControls.JoystickControl JoystickControlElement1
{
get { return JoystickControl1; }
}
}
Now I'd like to get the JoystickControlElement from the following Async Function:
var mainpage = new Views.MainPage();
string test = mainpage.test;
(these two lines are the problem)
class Bth : IBth
{
private CancellationTokenSource _ct { get; set; }
const int RequestResolveError = 1000;
public Bth()
{
}
public void Start(string name, int sleepTime = 200, bool readAsCharArray = false)
{
Task.Run(async () => loop(name, sleepTime, readAsCharArray));
}
private async Task loop(string name, int sleepTime, bool readAsCharArray)
{
BluetoothDevice device = null;
BluetoothAdapter adapter = BluetoothAdapter.DefaultAdapter;
BluetoothSocket BthSocket = null;
_ct = new CancellationTokenSource();
while (_ct.IsCancellationRequested == false)
{
try
{
Thread.Sleep(sleepTime);
adapter = BluetoothAdapter.DefaultAdapter;
if (adapter == null)
System.Diagnostics.Debug.WriteLine("No Bluetooth adapter found.");
else
System.Diagnostics.Debug.WriteLine("Adapter found!!");
if (!adapter.IsEnabled)
System.Diagnostics.Debug.WriteLine("Bluetooth adapter is not enabled.");
else
System.Diagnostics.Debug.WriteLine("Adapter enabled!");
System.Diagnostics.Debug.WriteLine("Try to connect to " + name);
foreach (var bd in adapter.BondedDevices)
{
System.Diagnostics.Debug.WriteLine("Paired devices found: " + bd.Name.ToUpper());
if (bd.Name.ToUpper().IndexOf(name.ToUpper()) >= 0)
{
System.Diagnostics.Debug.WriteLine("Found " + bd.Name + ". Try to connect with it!");
device = bd;
break;
}
}
if (device == null)
{
System.Diagnostics.Debug.WriteLine("Named device not found.");
MainThread.BeginInvokeOnMainThread(() =>
{
// Code to run on the main thread
});
}
else
{
UUID uuid = UUID.FromString("00001101-0000-1000-8000-00805f9b34fb");
if ((int)Android.OS.Build.VERSION.SdkInt >= 10) // Gingerbread 2.3.3 2.3.4
BthSocket = device.CreateInsecureRfcommSocketToServiceRecord(uuid);
else
BthSocket = device.CreateRfcommSocketToServiceRecord(uuid);
if (BthSocket != null)
{
//Task.Run ((Func<Task>)loop); /*) => {
await BthSocket.ConnectAsync();
if (BthSocket.IsConnected)
{
MainThread.BeginInvokeOnMainThread(() =>
{
service.toast.toastSuccess("Connected!");
// Code to run on the main thread
//pages.bluetoothBarcodeScan.connectedScanner();
});
System.Diagnostics.Debug.WriteLine("Connected!");
MainThread.BeginInvokeOnMainThread(() =>
{
service.toast.toastSuccess("Writing!");
// Code to run on the main thread
//
});
while (_ct.IsCancellationRequested == false)
{
var mainpage = new Views.MainPage();
string test = mainpage.test;
var bytes = Encoding.ASCII.GetBytes("{test}-");
BthSocket.OutputStream.Write(bytes, 0, bytes.Length);
Thread.Sleep(100);
}
System.Diagnostics.Debug.WriteLine("Exit the inner loop");
}
}
else
System.Diagnostics.Debug.WriteLine("BthSocket = null");
}
}
catch(Exception ex)
{
service.toast.toastSuccess(ex.Message);
}
finally
{
if (BthSocket != null)
BthSocket.Close();
device = null;
adapter = null;
}
}
System.Diagnostics.Debug.WriteLine("Exit the external loop");
}
public void Cancel()
{
if (_ct != null)
{
System.Diagnostics.Debug.WriteLine("Send a cancel to task!");
_ct.Cancel();
MainThread.BeginInvokeOnMainThread(() =>
{
service.toast.toastError("Disconnected");
// Code to run on the main thread
});
}
}
public async Task<string> GetData()
{
var mainpage = new Views.MainPage();
string test = mainpage.test;
return "1";
}
public ObservableCollection<string> PairedDevices()
{
BluetoothAdapter adapter = BluetoothAdapter.DefaultAdapter;
ObservableCollection<string> devices = new ObservableCollection<string>();
foreach (var bd in adapter.BondedDevices)
devices.Add(bd.Name);
return devices;
}
#endregion
}
The async function always starts new when it comes to this line.
How should I get the variable?
You can change your constructor method like:
public Bth(string txt)
{}
And then pass the mainpage.test like:
public static Bluetooth.IBth bth = new Bluetooth.Bth(txt);
I've written a Xamarin.Forms Application that connects to an ESP32 via Bluetooth. Now I'd like to get a value from a CustomControl.JoystickControl from the MainPage.xaml Page.
I've tried it like that:
MainPage.xaml.cs:
public partial class MainPage : ContentPage
{
public static Bluetooth.IBth bth = new Bluetooth.Bth();
public MainPage()
{
InitializeComponent();
Task.Run(async () => bth.Start("mecanumWheelRobot", 200, false));
}
public CustomControls.JoystickControl JoystickControlElement
{
get { return JoystickControl; }
}
public CustomControls.JoystickControl JoystickControlElement1
{
get { return JoystickControl1; }
}
}
Now I'd like to get the JoystickControlElement from the following Async Function:
var mainpage = new Views.MainPage();
string test = mainpage.test;
(these two lines are the problem)
class Bth : IBth
{
private CancellationTokenSource _ct { get; set; }
const int RequestResolveError = 1000;
public Bth()
{
}
public void Start(string name, int sleepTime = 200, bool readAsCharArray = false)
{
Task.Run(async () => loop(name, sleepTime, readAsCharArray));
}
private async Task loop(string name, int sleepTime, bool readAsCharArray)
{
BluetoothDevice device = null;
BluetoothAdapter adapter = BluetoothAdapter.DefaultAdapter;
BluetoothSocket BthSocket = null;
_ct = new CancellationTokenSource();
while (_ct.IsCancellationRequested == false)
{
try
{
Thread.Sleep(sleepTime);
adapter = BluetoothAdapter.DefaultAdapter;
if (adapter == null)
System.Diagnostics.Debug.WriteLine("No Bluetooth adapter found.");
else
System.Diagnostics.Debug.WriteLine("Adapter found!!");
if (!adapter.IsEnabled)
System.Diagnostics.Debug.WriteLine("Bluetooth adapter is not enabled.");
else
System.Diagnostics.Debug.WriteLine("Adapter enabled!");
System.Diagnostics.Debug.WriteLine("Try to connect to " + name);
foreach (var bd in adapter.BondedDevices)
{
System.Diagnostics.Debug.WriteLine("Paired devices found: " + bd.Name.ToUpper());
if (bd.Name.ToUpper().IndexOf(name.ToUpper()) >= 0)
{
System.Diagnostics.Debug.WriteLine("Found " + bd.Name + ". Try to connect with it!");
device = bd;
break;
}
}
if (device == null)
{
System.Diagnostics.Debug.WriteLine("Named device not found.");
MainThread.BeginInvokeOnMainThread(() =>
{
// Code to run on the main thread
});
}
else
{
UUID uuid = UUID.FromString("00001101-0000-1000-8000-00805f9b34fb");
if ((int)Android.OS.Build.VERSION.SdkInt >= 10) // Gingerbread 2.3.3 2.3.4
BthSocket = device.CreateInsecureRfcommSocketToServiceRecord(uuid);
else
BthSocket = device.CreateRfcommSocketToServiceRecord(uuid);
if (BthSocket != null)
{
//Task.Run ((Func<Task>)loop); /*) => {
await BthSocket.ConnectAsync();
if (BthSocket.IsConnected)
{
MainThread.BeginInvokeOnMainThread(() =>
{
service.toast.toastSuccess("Connected!");
// Code to run on the main thread
//pages.bluetoothBarcodeScan.connectedScanner();
});
System.Diagnostics.Debug.WriteLine("Connected!");
MainThread.BeginInvokeOnMainThread(() =>
{
service.toast.toastSuccess("Writing!");
// Code to run on the main thread
//
});
while (_ct.IsCancellationRequested == false)
{
var mainpage = new Views.MainPage();
string test = mainpage.test;
var bytes = Encoding.ASCII.GetBytes("{test}-");
BthSocket.OutputStream.Write(bytes, 0, bytes.Length);
Thread.Sleep(100);
}
System.Diagnostics.Debug.WriteLine("Exit the inner loop");
}
}
else
System.Diagnostics.Debug.WriteLine("BthSocket = null");
}
}
catch(Exception ex)
{
service.toast.toastSuccess(ex.Message);
}
finally
{
if (BthSocket != null)
BthSocket.Close();
device = null;
adapter = null;
}
}
System.Diagnostics.Debug.WriteLine("Exit the external loop");
}
public void Cancel()
{
if (_ct != null)
{
System.Diagnostics.Debug.WriteLine("Send a cancel to task!");
_ct.Cancel();
MainThread.BeginInvokeOnMainThread(() =>
{
service.toast.toastError("Disconnected");
// Code to run on the main thread
});
}
}
public async Task<string> GetData()
{
var mainpage = new Views.MainPage();
string test = mainpage.test;
return "1";
}
public ObservableCollection<string> PairedDevices()
{
BluetoothAdapter adapter = BluetoothAdapter.DefaultAdapter;
ObservableCollection<string> devices = new ObservableCollection<string>();
foreach (var bd in adapter.BondedDevices)
devices.Add(bd.Name);
return devices;
}
#endregion
}
The async function always starts new when it comes to this line.
How should I get the variable?
You can change your constructor method like:
public Bth(string txt)
{}
And then pass the mainpage.test like:
public static Bluetooth.IBth bth = new Bluetooth.Bth(txt);
I have asp.net core 3.1 application leveraging Azure Redis Cache with nuget package : StackExchange.Redis (Version: 2.2.62). Here I am trying to reset the Cache at every app startup event with the following code:
ResetCacheService.cs
public class ResetCacheService : IHostedService
{
private readonly IServiceProvider _serviceProvider;
public ResetCacheService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
// Create a new scope to retrieve scoped services
using (var scope = _serviceProvider.CreateScope())
{
// Get the CacheProvider instance
var redisCache = scope.ServiceProvider.GetRequiredService<ICacheProvider>();
//Do the cache reset asynchronously
await redisCache.ClearCacheAsync();
}
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
ICacheProvider.cs
public interface ICacheProvider
{
IList<string> GetKeys();
IList<string> GetKeys(string strGroup);
T Get<T>(string strKey, string strGroup = "");
bool Set(string strKey, object objData, string strGroup = "", int? intMinutes = null);
bool Remove(string strKey, string strGroup = "");
bool RemoveGroup(string strGroup);
bool ClearCache();
Task ClearCacheAsync();
}
CacheProvider.cs
public class CacheProvider : ICacheProvider
{
private static readonly int ExpiryMinutes = ConfigManager.Get(C.AppKeys.CacheExpiryMinutes, C.Defaults.CacheExpiryMinutes);
private static Lazy<ConnectionMultiplexer> _objCacheConn = CreateConnection();
private static Lazy<ConnectionMultiplexer> CreateConnection()
{
return new Lazy<ConnectionMultiplexer>(() =>
{
string strConn = ConfigManager.Get(C.VaultKeys.RedisConnString);
return ConnectionMultiplexer.Connect(strConn);
});
}
private ConnectionMultiplexer Connection
{
get
{
return _objCacheConn.Value;
}
}
private IDatabase GetDatabase()
{
return Connection.GetDatabase();
}
private EndPoint[] GetEndPoints()
{
return Connection.GetEndPoints();
}
private IServer GetServer()
{
var objEndpoint = GetEndPoints().First();
return Connection.GetServer(objEndpoint);
}
public IList<string> GetKeys()
{
return GetKeys("*");
}
public IList<string> GetKeys(string strGroup)
{
var lstKeys = new List<string>();
try
{
var objServer = GetServer();
if (objServer != null)
{
var strPattern = strGroup + ":*";
var objRedisKeys = objServer.Keys(pattern: strPattern);
var objLst = objRedisKeys.GetEnumerator();
while (objLst.MoveNext())
{
lstKeys.Add(objLst.Current.ToString());
}
}
}
catch (Exception)
{
lstKeys = new List<string>();
}
return lstKeys;
}
public T Get<T>(string strKey, string strGroup = "")
{
T objData = default(T);
try
{
var objCache = GetDatabase();
if (!strKey.IsEmpty() && objCache != null)
{
strKey = (strGroup.IsEmpty() ? C.CacheGroups.General : strGroup) + ":" + strKey;
var strData = objCache.StringGet(strKey).ToString();
if (!strData.IsEmpty())
{
objData = JsonConvert.DeserializeObject<T>(strData);
}
}
}
catch (Exception)
{
objData = default(T);
}
return objData;
}
public bool Set(string strKey, object objData, string strGroup = "", int? intMinutes = null)
{
bool blnSuccess = false;
try
{
var objCache = GetDatabase();
if (!strKey.IsEmpty() && objData != null && objCache != null)
{
intMinutes = intMinutes ?? ExpiryMinutes;
strKey = (strGroup.IsEmpty() ? C.CacheGroups.General : strGroup) + ":" + strKey;
var strData = JsonConvert.SerializeObject(objData);
var tsExpiry = new TimeSpan(0, intMinutes.Value, 0);
blnSuccess = objCache.StringSet(strKey, strData, tsExpiry);
}
}
catch (Exception)
{
blnSuccess = false;
}
return blnSuccess;
}
public bool Remove(string strKey, string strGroup = "")
{
bool blnSuccess = false;
try
{
var objCache = GetDatabase();
if (!strKey.IsEmpty() && objCache != null)
{
strKey = (strGroup.IsEmpty() ? C.CacheGroups.General : strGroup) + ":" + strKey;
blnSuccess = objCache.KeyDelete(strKey);
}
}
catch (Exception)
{
blnSuccess = false;
}
return blnSuccess;
}
public bool RemoveGroup(string strGroup)
{
bool blnSuccess = false;
try
{
var lstKeys = GetKeys(strGroup);
var objCache = GetDatabase();
if (lstKeys.Count > 0 && objCache != null)
{
foreach (var strKey in lstKeys)
{
objCache.KeyDelete(strKey);
}
blnSuccess = true;
}
}
catch (Exception)
{
blnSuccess = false;
}
return blnSuccess;
}
public bool ClearCache()
{
bool blnSuccess = false;
try
{
var objServer = GetServer();
if (objServer != null)
{
objServer.FlushAllDatabases();
blnSuccess = true;
}
}
catch (Exception)
{
blnSuccess = false;
}
return blnSuccess;
}
/// <summary>
/// Reset the Cache
/// </summary>
/// <returns></returns>
public async Task ClearCacheAsync()
{
var server = GetServer();
await server.FlushAllDatabasesAsync();
}
}
Startup.cs
public static IServiceCollection AddCacheResetHostedService(this IServiceCollection services) => services.AddHostedService<ResetCacheService>();
public virtual void ConfigureServices(IServiceCollection services) => services.AddConfigManager(this.configuration).AddCacheResetHostedService();
On executing the code I see the below error:
No connection is active/available to service this operation: FLUSHALL; It was not possible to connect to the redis server(s). ConnectTimeout, inst: 0, qu: 0, qs: 0, aw: False, rs: NotStarted, ws: Initializing, in: 0, serverEndpoint: daqmmredis.redis.cache.windows.net:6380, mc: 1/1/0, mgr: 10 of 10 available, clientName: USHYDSAPATRO7, IOCP: (Busy=0,Free=1000,Min=8,Max=1000), WORKER: (Busy=0,Free=32767,Min=8,Max=32767), v: 2.2.62.27853
Please check if the below given steps help to work around:
downgrading StackExchange.Redis to 2.1.58
Instead of creating a ConfigurationOptions parameter using the Endpoint and Password separately, use the "Primary connection string (StackExchange.Redis)" from Azure Access Keys as the parameter to ConnectionMultiplexer.Connect()
Set the ssl=True,sslprotocols=tls12 in your configuration to force it to the latest version if you're utilizing a secure TLS connection.
Refer here for more information.
How can I get all classes that implements a specific interface then call a function of that class if a string member of the specific class matches a given one?
Basically what I have is a ICommandHandler interface:
interface ICommandHandler
{
string Command { get; }
Task ExecuteAsync();
}
and a class that implements it:
public class StartCommand : ICommandHandler
{
public string Command { get => "start"; }
public async Task ExecuteAsync()
{
// do something
}
}
What I want to do is to get all the classes that implements the ICommandHandler interface, then verify if the class.Command equals a specific string and if it does then call the ExecuteAsync method.
I've tried using this answer here: https://stackoverflow.com/a/45382386/15306888 but the class.Command is always null
Edit: The answer I got bellow does what I wanted to do:
What I was looking for was a way to use ICommandHandler to allow me to easily gather all the classes inheriting from it and call the ExecuteAsync function instead of having to manually add the methods in the part of the code handling TdLib message events.
So now my project directory looks something like this:
TelegramClient.cs - The place where the classes inheriting from the ICommandHandler are loaded, and where the ExecuteAsync method is called if the Command member matches the Command parsed from the message returned by telegram.
Handlers/ - Base directory for my handlers
CommandHandlers/ - Folder with the ICommandHandler interface and the classes inheriting from it
MessageHandlers/ - Folder with another interface IMessageHandler and the classes inheriting from it
Anyway, in the meantime I've found another answer on a stackoverflow question (Had to scroll a few times) that made it way easier and faster to get multiple handlers without having to repeat the same code over and over again. I've ended by combining the answer linked above with this one: https://stackoverflow.com/a/41650057/15306888
So I ended with a simple method:
public static IEnumerable<T> GetAll<T>()
{
return Assembly.GetExecutingAssembly()
.GetTypes()
.Where(type => typeof(T).IsAssignableFrom(type))
.Where(type =>
!type.IsAbstract &&
!type.IsGenericType &&
type.GetConstructor(new Type[0]) != null)
.Select(type => (T)Activator.CreateInstance(type))
.ToList();
}
That can be easily used like:
private async Task ProcessMessage(TdApi.Update.UpdateNewMessage message)
{
var command = GetCommand(message.Message);
var textMessage = GetMessageText(message.Message);
if (!String.IsNullOrWhiteSpace(command))
{
var commandHandlers = GetAll<ICommandHandler>();
foreach (var handler in commandHandlers)
{
if (command == handler.Command)
await handler.ExecuteAsync(_client, message.Message);
}
}
else if (!String.IsNullOrWhiteSpace(textMessage))
{
var messageHandlers = GetAll<IMessageHandler>();
foreach (var handler in messageHandlers)
{
var outgoing = handler.Outgoing && message.Message.IsOutgoing;
var incoming = handler.Incoming && !message.Message.IsOutgoing;
if (outgoing || incoming)
{
if (!String.IsNullOrEmpty(handler.Pattern))
{
var match = Regex.Match(textMessage, handler.Pattern);
if (match.Success)
await handler.ExecuteAsync(_client, message.Message);
}
}
}
}
}
How the Interface is actually implemented:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using TdLib;
namespace YoutubeDl_Bot.Handlers.CommandHandlers
{
public class StartCommand : ICommandHandler
{
public string Command { get => "/start"; }
public async Task ExecuteAsync(TdClient client, TdApi.Message message)
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Hi! I'm a bot that downloads and sends video and audio files from youtube links and many other supported services");
stringBuilder.AppendLine(String.Empty);
stringBuilder.AppendLine("**Usage:**");
stringBuilder.AppendLine("• Send or forward a text message containing links and I will:");
stringBuilder.AppendLine("• Download the best audio quality available for the video in the speecified link");
stringBuilder.AppendLine("• Download the best video quality available for the video in the speecified link");
stringBuilder.AppendLine("• Send the direct download URL for every link specified in the message");
stringBuilder.AppendLine("• Supported links are available here: https://ytdl-org.github.io/youtube-dl/supportedsites.html");
var formattedText = await client.ExecuteAsync(new TdLib.TdApi.ParseTextEntities { Text = stringBuilder.ToString(), ParseMode = new TdLib.TdApi.TextParseMode.TextParseModeMarkdown() });
await client.ExecuteAsync(new TdLib.TdApi.SendMessage { ChatId = message.ChatId, InputMessageContent = new TdLib.TdApi.InputMessageContent.InputMessageText { Text = formattedText } });
}
}
}
Full TelegramClient class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using TdLib;
using YoutubeDl_Bot.Handlers.CallbackHandlers;
using YoutubeDl_Bot.Handlers.CommandHandlers;
using YoutubeDl_Bot.Handlers.MessageHandlers;
using YoutubeDl_Bot.Settings;
using YoutubeDl_Bot.Utils;
namespace YoutubeDl_Bot
{
class TelegramBotClient
{
private static TdClient _client;
private static TdLib.TdApi.User _me;
private static int _apiId;
private static string _apiHash;
private static string _token;
#if DEBUG
private static readonly int _verbosityLevel = 4;
#else
private static readonly int _verbosityLevel = 0;
#endif
public static bool AuthCompleted = false;
public TelegramBotClient(int apiId, string apiHash)
{
_apiId = apiId;
_apiHash = apiHash;
}
public async Task<TdClient> CreateClient()
{
_client = new TdClient();
await _client.ExecuteAsync(new TdApi.SetLogVerbosityLevel { NewVerbosityLevel = _verbosityLevel });
return _client;
}
public void StartListening(string botToken)
{
_token = botToken;
_client.UpdateReceived += _client_UpdateReceived;
}
private async void _client_UpdateReceived(object sender, TdApi.Update update)
{
switch (update)
{
case TdApi.Update.UpdateAuthorizationState updateAuthorizationState when updateAuthorizationState.AuthorizationState.GetType() == typeof(TdApi.AuthorizationState.AuthorizationStateWaitTdlibParameters):
await _client.ExecuteAsync(new TdApi.SetTdlibParameters
{
Parameters = new TdApi.TdlibParameters
{
ApiId = _apiId,
ApiHash = _apiHash,
ApplicationVersion = "0.0.1",
DeviceModel = "Bot",
SystemLanguageCode = "en",
SystemVersion = "Unknown"
}
});
break;
case TdApi.Update.UpdateAuthorizationState updateAuthorizationState when updateAuthorizationState.AuthorizationState.GetType() == typeof(TdLib.TdApi.AuthorizationState.AuthorizationStateWaitEncryptionKey):
await _client.ExecuteAsync(new TdLib.TdApi.CheckDatabaseEncryptionKey());
break;
case TdLib.TdApi.Update.UpdateAuthorizationState updateAuthorizationState when updateAuthorizationState.AuthorizationState.GetType() == typeof(TdLib.TdApi.AuthorizationState.AuthorizationStateWaitPhoneNumber):
await _client.ExecuteAsync(new TdLib.TdApi.CheckAuthenticationBotToken { Token = _token });
break;
case TdLib.TdApi.Update.UpdateConnectionState updateConnectionState when updateConnectionState.State.GetType() == typeof(TdLib.TdApi.ConnectionState.ConnectionStateReady):
// To Do Settings
var botSettings = new BotSettings(_apiId, _apiHash, _token);
_me = await _client.ExecuteAsync(new TdLib.TdApi.GetMe());
Helpers.Print($"Logged in as: {_me.FirstName}");
SettingsManager.Set<BotSettings>("BotSettings.data", botSettings);
break;
case TdLib.TdApi.Update.UpdateNewMessage message:
if (!message.Message.IsOutgoing)
await ProcessMessage(message);
break;
case TdApi.Update.UpdateNewCallbackQuery callbackQuery:
await ProcessCallbackQuery(callbackQuery);
break;
default:
break;
}
}
#region PROCESS_MESSAGE
private async Task ProcessMessage(TdApi.Update.UpdateNewMessage message)
{
var command = GetCommand(message.Message);
var textMessage = GetMessageText(message.Message);
#region COMMAND_HANDLERS
if (!String.IsNullOrWhiteSpace(command))
{
var commandHandlers = GetAll<ICommandHandler>();
foreach (var handler in commandHandlers)
{
if (command == handler.Command)
await handler.ExecuteAsync(_client, message.Message);
}
}
#endregion
#region MESSAGE_HANDLERS
else if (!String.IsNullOrWhiteSpace(textMessage))
{
var messageHandlers = GetAll<IMessageHandler>();
foreach (var handler in messageHandlers)
{
var outgoing = handler.Outgoing && message.Message.IsOutgoing;
var incoming = handler.Incoming && !message.Message.IsOutgoing;
if (outgoing || incoming)
{
if (!String.IsNullOrEmpty(handler.Pattern))
{
var match = Regex.Match(textMessage, handler.Pattern);
if (match.Success)
await handler.ExecuteAsync(_client, message.Message);
}
}
}
}
#endregion
}
#endregion
#region PROCESS_CALLACK
private async Task ProcessCallbackQuery(TdApi.Update.UpdateNewCallbackQuery callbackQuery)
{
if (callbackQuery.Payload.GetType() == typeof(TdApi.CallbackQueryPayload.CallbackQueryPayloadData))
{
var payload = callbackQuery.Payload as TdApi.CallbackQueryPayload.CallbackQueryPayloadData;
var callbackHandlers = GetAll<ICallbackHandler>();
foreach (var handler in callbackHandlers)
{
if (handler.DataIsRegex)
if (Regex.Match(System.Text.Encoding.UTF8.GetString(payload.Data), handler.Data).Success)
await handler.ExecuteAsync(_client, callbackQuery);
else if (handler.Data == System.Text.Encoding.UTF8.GetString(payload.Data))
await handler.ExecuteAsync(_client, callbackQuery);
}
}
}
#endregion
#region COMMAND_PARSER
public string GetCommand(TdApi.Message message)
{
string command = null;
TdLib.TdApi.FormattedText formattedText = new TdLib.TdApi.FormattedText();
if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessageText))
{
var messageText = message.Content as TdLib.TdApi.MessageContent.MessageText;
formattedText = messageText.Text;
}
else
{
if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessagePhoto))
{
var messagePhoto = message.Content as TdLib.TdApi.MessageContent.MessagePhoto;
formattedText = messagePhoto.Caption;
}
else if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessageDocument))
{
var messageDocument = message.Content as TdLib.TdApi.MessageContent.MessageDocument;
formattedText = messageDocument.Caption;
}
else if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessageVideo))
{
var messageVideo = message.Content as TdLib.TdApi.MessageContent.MessageVideo;
formattedText = messageVideo.Caption;
}
}
foreach (var entity in formattedText.Entities)
{
if (entity.Type.GetType() == typeof(TdLib.TdApi.TextEntityType.TextEntityTypeBotCommand) && String.IsNullOrWhiteSpace(command))
{
if (entity.Offset == 0)
{
var splitCommand = formattedText.Text.Split();
if (splitCommand[0].EndsWith($"#{_me.Username}"))
{
command = splitCommand[0].Split('#')[0];
}
else
{
command = splitCommand[0];
}
}
}
}
return command;
}
#endregion
#region MESSAGE_PARSER
public string GetMessageText(TdApi.Message message)
{
TdLib.TdApi.FormattedText formattedText = new TdLib.TdApi.FormattedText();
if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessageText))
{
var messageText = message.Content as TdLib.TdApi.MessageContent.MessageText;
formattedText = messageText.Text;
}
else
{
if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessagePhoto))
{
var messagePhoto = message.Content as TdLib.TdApi.MessageContent.MessagePhoto;
formattedText = messagePhoto.Caption;
}
else if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessageDocument))
{
var messageDocument = message.Content as TdLib.TdApi.MessageContent.MessageDocument;
formattedText = messageDocument.Caption;
}
else if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessageVideo))
{
var messageVideo = message.Content as TdLib.TdApi.MessageContent.MessageVideo;
formattedText = messageVideo.Caption;
}
}
return formattedText.Text;
}
#endregion
#region REFLECTION
// https://stackoverflow.com/a/41650057/15306888
public static IEnumerable<T> GetAll<T>()
{
return Assembly.GetExecutingAssembly()
.GetTypes()
.Where(type => typeof(T).IsAssignableFrom(type))
.Where(type =>
!type.IsAbstract &&
!type.IsGenericType &&
type.GetConstructor(new Type[0]) != null)
.Select(type => (T)Activator.CreateInstance(type))
.ToList();
}
#endregion
}
}
Since it's always null I think that the problem is that you're not creating an instance of your handler. I prepared a demo for you where I did that and it works.
public interface ICommandHandler
{
string Command { get; }
Task ExecuteAsync();
}
public class FirstCommandHandler : ICommandHandler
{
public string Command => "First";
public async Task ExecuteAsync()
{
Console.WriteLine("Hello from first.");
await Task.Delay(10);
}
}
public class SecondCommandHandler : ICommandHandler
{
public string Command => "Second";
public async Task ExecuteAsync()
{
Console.WriteLine("Hello from second.");
await Task.Delay(10);
}
}
public class Program
{
static async Task Main(string[] args)
{
var handlers = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => typeof(ICommandHandler).IsAssignableFrom(p) && p.IsClass);
foreach (var handler in handlers)
{
var handlerInstance = (ICommandHandler)Activator.CreateInstance(handler);
if (handlerInstance.Command == "First")
{
await handlerInstance.ExecuteAsync();
}
}
}
}
If it's not the case, could you show some more code? Are you trying to check Command value by reflection?
I am calling an async method that returns a Task < CurrentCartridgeStatus> from Background worker. Thing is until the CurrentCartridgeStatus changes its state I want to keep calling it. But I am not awaiting on this method call because making the DoWork method async is giving me an exception.
But in my case with my current call(Please see the StartCurrentRun method) the background worker initially reports 1% then it seems to be staying idle, and after the status changes then it reports the rest 99%. Please help how can I make the Background worker report progress in parallel with StartCurrentRun method.
private void StartCurrentRun(bool obj)
{
this.worker = new BackgroundWorker();
this.worker.WorkerReportsProgress = true;
this.worker.WorkerSupportsCancellation = true;
this.worker.DoWork += this.DoWork;
this.worker.ProgressChanged += this.ProgressChanged;
this.worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
IsLiveProgress = true;
StartTimer();
this.worker.RunWorkerAsync();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
CreateEventLogs.WriteToEventLog(string.Format("Run with Assay:{0} Volume{1} has been started", SelectedAssay, SelectedVolume));
if (worker.CancellationPending == true)
{
e.Cancel = true;
return;
}
int current = 1;
CurrentCartridgeStatus status = CurrentCartridgeStatus.NotProcessed;
var instance = ConnectToInstrument.InstrumentConnectionInstance;
do
{
var result = instance.StartCurrentRun(SelectedAssay.display_name, int.Parse(SelectedVolume.Remove(SelectedVolume.Length - 2, 2)));
//var result = Test(current);
status = result.Result;
CurrentStatus = status.ToString();
CreateEventLogs.WriteToEventLog("Run status - " + CurrentStatus);
//Thread.Sleep(2000);
worker.ReportProgress(Math.Min(current, 100));
current++;
}
while (status != CurrentCartridgeStatus.Processed);
ConnectToInstrument.IsAlreadyExecuted = false;
if (current < 100)
{
worker.ReportProgress(current + (100 - current));
}
}
Here is the StartCurrentRun method. It internally calls some firmware logic. Just to make this more clear here is the entire ConnectToInstrument Class.
public class ConnectToInstrument
{
private EventProxy _updater;
private IInstrument _instrument;
private QpCallBack _cb;
//private IQEvent #event;
private QpWrapper _qp;
private InstrumentTypes _type;
private CurrentInstrumentStatus InstrumentCurrentStatus = CurrentInstrumentStatus.NotReady;
private static ManualResetEvent manualResetEvent = new ManualResetEvent(false);
public static bool IsAlreadyExecuted = false;
public bool IsConnected { get; set; }
private static ConnectToInstrument _instrumentConnectionInstance;
public static ConnectToInstrument InstrumentConnectionInstance
{
get
{
if (_instrumentConnectionInstance == null)
{
_instrumentConnectionInstance = new ConnectToInstrument();
}
return _instrumentConnectionInstance;
}
}
private ConnectToInstrument()
{
try
{
_cb = new QpCallBack();
_qp = new QpWrapper();
//var config = new ConfigManager();
//var client = new Dna2Client();
_type = InstrumentTypes.Simulator;
var factory = new ApiFactory(_cb, _qp, _type);
_instrument = factory.GetInstrument();
InitializeQf();
_updater = new EventProxy();
_updater.Start(1);
StartUiProxy().Forget();
}
catch (Exception ex)
{
//Log to Error Log
}
}
private async Task<object> StartUiProxy()
{
while (true)
{
try
{
var #event = await _updater.GetEvent();
switch ((QpSignals)#event.QSignal)
{
case QpSignals.INSTRUMENT_STATUS:
{
var status = #event as UiEvent<InstrumentStatus>;
if (status.Value != InstrumentStatus.Ready)
{
InstrumentCurrentStatus = CurrentInstrumentStatus.NotConnected;
continue;
}
else
{
InstrumentCurrentStatus = CurrentInstrumentStatus.Ready;
return Task.FromResult<object>(InstrumentStatus.Ready);
}
}
case QpSignals.TRAY_STATUS:
{
var status = #event as UiEvent<TrayStatus>;
if (status.QSignal == 6 && status.Value == TrayStatus.Opened)
{
return Task.FromResult<object>(TraySignals.OPEN);
}
else if (status.QSignal == 6 && status.Value == TrayStatus.Opened)
{
return Task.FromResult<object>(TraySignals.CLOSE);
}
return null;
}
case QpSignals.PROCESS_CARTRIDGE:
{
var status = #event as UiEvent<CartridgeStatus>;
if (status.Value == CartridgeStatus.Processing)
{
return status;
}
else if (status.Value == CartridgeStatus.Processed)
{
return status;
}
return null;
}
}
}
catch (Exception ex)
{
//Log to Error Log
}
}
}
private void InitializeQf()
{
QF.Instance.Initialize((int)QpSignals.MAX_SIGNAL - 1);
}
public async Task<bool> Connect()
{
bool status = false;
string[] ports = new string[40];
if (_type == InstrumentTypes.Dgx)
{
ports = await ComPortInfo.GetAvailablePortNamesForDevice("USB Serial Port");
if (ports == null || ports.Length == 0)
{
Exception ex = new Exception("No Serial Devices found.");
//Log
return false;
}
}
try
{
string thermPort = _type == InstrumentTypes.Simulator ? string.Empty : ports[0]; //ports[0] should be changed to combobox selected value
status = _instrument.Connect(ports[0]);
}
catch (Exception ex)
{
Debug.Write(ex.Message);
}
return status;
}
public async Task<CurrentInstrumentStatus> GetCurrentInstrumentStatus()
{
return await Task.FromResult(InstrumentCurrentStatus);
//_cb.InstrumentStatusUpdates(InstrumentStatus.Ready);
//var value = await _updater.GetEvent();
//var status = value as UiEvent<InstrumentStatus>;
//if (status.QSignal == 5 && status.Value == InstrumentStatus.Ready)
//{
// return CurrentInstrumentStatus.Ready;
//}
//return CurrentInstrumentStatus.Error;
}
public async Task<CurrentCartridgeStatus> StartCurrentRun(string Assay, int volume)
{
object value;
UiEvent<CartridgeStatus> status;
do
{
//await Task.Delay(1000);
//_cb.ProcessCartridge(CartridgeStatus.Processing);
if (!IsAlreadyExecuted)
{
_instrument.ProcessCartridge();
}
IsAlreadyExecuted = true;
//value = await StartUiProxy();
value = await _updater.GetEvent();
status = value as UiEvent<CartridgeStatus>;
}
while (status == null);
try
{
//IQEvent value;
if (status.QSignal == 9 && status.Value == CartridgeStatus.Processing)
{
return CurrentCartridgeStatus.Processing;
}
if (status.QSignal == 9 && status.Value == CartridgeStatus.Processed)
{
return CurrentCartridgeStatus.Processed;
}
}
catch (Exception ex)
{
//Log it
}
return CurrentCartridgeStatus.Unidentified;
}
}
Here is the CurrentCartridgeStatus enum
public enum CurrentCartridgeStatus
{
Unidentified = 0,
NotProcessed = 1,
Processing = 2,
Processed = 3
}