7 Commits
12 changed files with 517 additions and 430 deletions
+38 -51
View File
@@ -1,71 +1,58 @@
using System; using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Loader; using System.Runtime.Loader;
using System.Threading; using System.Threading;
namespace BlubbFish.Utils.IoT.Bots { namespace BlubbFish.Utils.IoT.Bots {
public abstract class ABot { public abstract class ABot {
#if !NETCOREAPP
private Thread sig_thread;
#endif
private Boolean RunningProcess = true; private Boolean RunningProcess = true;
private readonly ProgramLogger logger = null;
protected ProgramLogger logger = new ProgramLogger(); public Boolean DebugLogging {
get;
} = false;
private void SetupShutdown(Object sender, ConsoleCancelEventArgs e) { public ABot(String[] _, Boolean fileLogging, String configSearchPath) {
e.Cancel = true; InIReader.SetSearchPath(new List<String>() { "/etc/"+ configSearchPath, Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\"+ configSearchPath });
Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.SetupShutdown: Signalhandler Windows INT recieved."); if(fileLogging && InIReader.ConfigExist("settings") && Boolean.TryParse(InIReader.GetInstance("settings").GetValue("logging", "enabled", "false"), out Boolean _)) {
this.RunningProcess = false; this.logger = new ProgramLogger(InIReader.GetInstance("settings").GetValue("logging", "path", Assembly.GetEntryAssembly().GetName().Name + ".log"));
}
if(InIReader.ConfigExist("settings") && Boolean.TryParse(InIReader.GetInstance("settings").GetValue("logging", "debug", "true"), out Boolean debuglog)) {
this.DebugLogging = debuglog;
}
}
private void ConsoleCancelEvent(Object sender, ConsoleCancelEventArgs e) {
e.Cancel = true;
Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.ConsoleCancelEvent()");
this.RunningProcess = false;
} }
#if NETCOREAPP #if NETCOREAPP
private void Default_Unloading(AssemblyLoadContext obj) { private void Unloading(AssemblyLoadContext obj) => this.RunningProcess = false;
Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.SetupShutdown: Signalhandler Windows NETCORE recieved.");
this.RunningProcess = false; private void ProcessExit(Object sender, EventArgs e) => this.RunningProcess = false;
Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.WaitForShutdown: Shutdown.");
this.Dispose();
}
#endif #endif
protected void WaitForShutdown() { protected void WaitForShutdown() {
if(Type.GetType("Mono.Runtime") != null) { #if NETCOREAPP
#if !NETCOREAPP AssemblyLoadContext.Default.Unloading += this.Unloading;
this.sig_thread = new Thread(delegate () { AppDomain.CurrentDomain.ProcessExit += this.ProcessExit;
Mono.Unix.UnixSignal[] signals = new Mono.Unix.UnixSignal[] { Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.WaitForShutdown: Attach Unloading and ProcessExit.");
new Mono.Unix.UnixSignal(Mono.Unix.Native.Signum.SIGTERM), #endif
new Mono.Unix.UnixSignal(Mono.Unix.Native.Signum.SIGINT) Console.CancelKeyPress += new ConsoleCancelEventHandler(this.ConsoleCancelEvent);
}; Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.WaitForShutdown: Attach ConsoleCancelEvent.");
Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.WaitForShutdown: Signalhandler Mono attached."); while(this.RunningProcess) {
while(true) { Thread.Sleep(100);
Int32 i = Mono.Unix.UnixSignal.WaitAny(signals, -1); }
Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.WaitForShutdown: Signalhandler Mono INT recieved " + i + "."); Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.WaitForShutdown: Shutdown.");
this.RunningProcess = false;
break;
}
});
this.sig_thread.Start();
#endif
} else {
#if NETCOREAPP
AssemblyLoadContext.Default.Unloading += this.Default_Unloading;
Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.WaitForShutdown: Signalhandler Netcore attached.");
#endif
Console.CancelKeyPress += new ConsoleCancelEventHandler(this.SetupShutdown);
Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.WaitForShutdown: Signalhandler Windows attached.");
}
while(this.RunningProcess) {
Thread.Sleep(100);
}
Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.WaitForShutdown: Shutdown.");
} }
public virtual void Dispose() { public virtual void Dispose() {
#if !NETCOREAPP Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.Dispose: Shutdown.");
if(this.sig_thread != null && this.sig_thread.IsAlive) { this.RunningProcess = false;
this.sig_thread.Abort(); this.logger?.Dispose();
}
#endif
} }
} }
} }
@@ -5,26 +5,17 @@ using System.Net;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Web; using System.Web;
using BlubbFish.Utils.IoT.Connector;
using BlubbFish.Utils.IoT.Events;
using LitJson;
namespace BlubbFish.Utils.IoT.Bots { namespace BlubbFish.Utils.IoT.Bots {
public abstract class Webserver : ABot public abstract class AWebserver : ABot {
{
protected Dictionary<String, String> config; protected Dictionary<String, String> config;
protected static InIReader requests;
protected HttpListener httplistener; protected HttpListener httplistener;
protected ABackend databackend;
public Webserver(ABackend backend, Dictionary<String, String> settings, InIReader requestslookup) { public AWebserver(String[] args, Boolean fileLogging, String configSearchPath, Dictionary<String, String> settings) : base(args, fileLogging, configSearchPath) =>
this.config = settings; this.config = this.config is null ? InIReader.GetInstance("settings").GetSection("webserver") : settings;
requests = requestslookup;
this.databackend = backend;
}
protected void StartListen() { protected void StartListen() {
this.databackend.MessageIncomming += this.Backend_MessageIncomming;
this.httplistener = new HttpListener(); this.httplistener = new HttpListener();
this.httplistener.Prefixes.Add(this.config["prefix"]); this.httplistener.Prefixes.Add(this.config["prefix"]);
this.httplistener.Start(); this.httplistener.Start();
@@ -45,7 +36,57 @@ namespace BlubbFish.Utils.IoT.Bots {
}); });
} }
public static Boolean SendFileResponse(HttpListenerContext cont, String folder = "resources", Boolean printOutput = true) { public override void Dispose() {
if(this.httplistener.IsListening) {
this.httplistener.Stop();
}
this.httplistener.Close();
base.Dispose();
}
protected abstract Boolean SendWebserverResponse(HttpListenerContext cont);
}
#region HttpListener* Extensions
public static class HttpListenerHelper {
private static InIReader requests;
public static Dictionary<String, String> GetPostParams(this HttpListenerRequest request) {
if(request.HttpMethod == "POST") {
if(request.HasEntityBody) {
StreamReader reader = new StreamReader(request.InputStream, request.ContentEncoding);
String rawData = reader.ReadToEnd();
request.InputStream.Close();
reader.Close();
Dictionary<String, String> ret = new Dictionary<String, String>();
foreach(String param in rawData.Split('&')) {
String[] kvPair = param.Split('=');
if(!ret.ContainsKey(kvPair[0])) {
ret.Add(kvPair[0], HttpUtility.UrlDecode(kvPair[1]));
}
}
return ret;
}
}
return new Dictionary<String, String>();
}
public static Boolean SendStringResponse(this HttpListenerContext cont, String obj) => cont.SendBinaryResponse(Encoding.UTF8.GetBytes(obj));
public static Boolean SendBinaryResponse(this HttpListenerContext cont, Byte[] buf) {
try {
cont.Response.ContentLength64 = buf.Length;
cont.Response.OutputStream.Write(buf, 0, buf.Length);
Console.WriteLine("200 - " + cont.Request.Url.PathAndQuery);
return true;
} catch(Exception e) {
Helper.WriteError("500 - " + e.Message + "\n\n" + e.StackTrace);
cont.Response.StatusCode = 500;
}
return false;
}
public static Boolean SendFileResponse(this HttpListenerContext cont, String folder = "resources", Boolean printOutput = true) {
String restr = cont.Request.Url.PathAndQuery; String restr = cont.Request.Url.PathAndQuery;
if(restr.StartsWith("/")) { if(restr.StartsWith("/")) {
restr = restr.IndexOf("?") != -1 ? restr[1..restr.IndexOf("?")] : restr[1..]; restr = restr.IndexOf("?") != -1 ? restr[1..restr.IndexOf("?")] : restr[1..];
@@ -81,7 +122,7 @@ namespace BlubbFish.Utils.IoT.Bots {
file = file.Replace("\"{%" + item.Key.ToUpper() + "%}\"", item.Value); file = file.Replace("\"{%" + item.Key.ToUpper() + "%}\"", item.Value);
} }
} }
file = file.Replace("{%REQUEST_URL_HOST%}", cont.Request.Url.Host+":"+cont.Request.Url.Port); file = file.Replace("{%REQUEST_URL_HOST%}", cont.Request.Url.Host + ":" + cont.Request.Url.Port);
Byte[] buf = Encoding.UTF8.GetBytes(file); Byte[] buf = Encoding.UTF8.GetBytes(file);
cont.Response.ContentLength64 = buf.Length; cont.Response.ContentLength64 = buf.Length;
switch(end) { switch(end) {
@@ -109,53 +150,7 @@ namespace BlubbFish.Utils.IoT.Bots {
return false; return false;
} }
public static Boolean SendJsonResponse(Object data, HttpListenerContext cont) { public static void SetRequestsOverride(InIReader requestslookup) => requests = requestslookup;
try {
Byte[] buf = Encoding.UTF8.GetBytes(JsonMapper.ToJson(data));
cont.Response.ContentLength64 = buf.Length;
cont.Response.OutputStream.Write(buf, 0, buf.Length);
Console.WriteLine("200 - " + cont.Request.Url.PathAndQuery);
return true;
} catch(Exception e) {
Helper.WriteError("500 - " + e.Message + "\n\n" + e.StackTrace);
cont.Response.StatusCode = 500;
}
return false;
}
public static Dictionary<String, String> GetPostParams(HttpListenerRequest req) {
if(req.HttpMethod == "POST") {
if(req.HasEntityBody) {
StreamReader reader = new StreamReader(req.InputStream, req.ContentEncoding);
String rawData = reader.ReadToEnd();
req.InputStream.Close();
reader.Close();
Dictionary<String, String> ret = new Dictionary<String, String>();
foreach(String param in rawData.Split('&')) {
String[] kvPair = param.Split('=');
if(!ret.ContainsKey(kvPair[0])) {
ret.Add(kvPair[0], HttpUtility.UrlDecode(kvPair[1]));
}
}
return ret;
}
}
return new Dictionary<String, String>();
}
public override void Dispose() {
if(this.httplistener.IsListening) {
this.httplistener.Stop();
}
this.httplistener.Close();
if(this.databackend != null) {
this.databackend.Dispose();
}
base.Dispose();
}
protected abstract void Backend_MessageIncomming(Object sender, BackendEvent e);
protected abstract Boolean SendWebserverResponse(HttpListenerContext cont);
} }
#endregion
} }
+24
View File
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using BlubbFish.Utils.IoT.Connector;
using BlubbFish.Utils.IoT.Events;
namespace BlubbFish.Utils.IoT.Bots {
public abstract class AWebserverDataBackend : AWebserver {
protected ABackend databackend;
protected AWebserverDataBackend(String[] args, Boolean fileLogging, String configSearchPath, ABackend backend, Dictionary<String, String> settings) : base(args, fileLogging, configSearchPath, settings) =>
this.databackend = backend is null ? ABackend.GetInstance(InIReader.GetInstance("settings").GetSection("backend")) : backend;
protected void StartDataBackend() => this.databackend.MessageIncomming += this.Backend_MessageIncomming;
protected abstract void Backend_MessageIncomming(Object sender, BackendEvent e);
public override void Dispose() {
if(this.databackend != null) {
this.databackend.Dispose();
}
base.Dispose();
}
}
}
+26 -20
View File
@@ -1,34 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net10.0</TargetFramework>
<RootNamespace>BlubbFish.Utils.IoT.Bots</RootNamespace> <RootNamespace>BlubbFish.Utils.IoT.Bots</RootNamespace>
<AssemblyName>Bot-Utils</AssemblyName> <AssemblyName>Bot-Utils</AssemblyName>
<PackageId>Bots.IoT.Utils.BlubbFish</PackageId> <PackageId>Bots.IoT.Utils.BlubbFish</PackageId>
<Version>1.2.1</Version> <Version>1.2.6</Version>
<AssemblyVersion>1.2.1</AssemblyVersion>
<FileVersion>1.2.1</FileVersion>
<NeutralLanguage>de-DE</NeutralLanguage> <NeutralLanguage>de-DE</NeutralLanguage>
<Description>Bot-Utils are helpers for programming a bot</Description> <Description>Bot-Utils are helpers for programming a bot</Description>
<Authors>BlubbFish</Authors> <Authors>BlubbFish</Authors>
<Company>BlubbFish</Company> <Company>BlubbFish</Company>
<Copyright>Copyright © BlubbFish 2018 - 30.08.2019</Copyright> <Copyright>Copyright © BlubbFish 2018 - 25.01.2022</Copyright>
<PackageLicenseFile>LICENSE</PackageLicenseFile> <PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageProjectUrl>http://git.blubbfish.net/vs_utils/Bot-Utils</PackageProjectUrl> <PackageProjectUrl>http://git.blubbfish.net/vs_utils/Bot-Utils</PackageProjectUrl>
<RepositoryUrl>http://git.blubbfish.net/vs_utils/Bot-Utils.git</RepositoryUrl> <RepositoryUrl>http://git.blubbfish.net/vs_utils/Bot-Utils.git</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PackageReleaseNotes>1.2.1 When using Dispose, kill also mqtt connection and other tiny fixes <PackageReleaseNotes>
1.2.0 Refactor Bot to ABot and refere MultiSourceBot, Webserver and Bot to it. Add MultiSourceBot. Rewrite Mqtt module so that it not need to watch the connection. 1.2.6 - 2022-01-25 - Makeing Logging easyier
1.1.9 Modify Output of SendFileResponse 1.2.5 - 2022-01-20 - Better linux handling
1.1.8 Add logger to Webserver Class 1.2.4 - 2022-01-18 - Config enabled module loading
1.1.7 Restrucutre loading, so that all is init and after the listener is started, REQUEST_URL_HOST gives now host and port 1.2.3 - 2022-01-09 - Tiny Refactoring
1.1.6 rename functions and make SendFileResponse with a parameter for the folder (default resources), also put returntype boolean, add function that parse post params, if path is a dictionary try to load index.html 1.2.2 - 2021-08-22 - Going to netcore
1.1.5 add a function to send an object as json directly 1.2.1 - 2019-08-30 - When using Dispose, kill also mqtt connection and other tiny fixes
1.1.4 add Woff as Binary type 1.2.0 - 2019-05-27 - Refactor Bot to ABot and refere MultiSourceBot
1.1.3 Variables parsing now as a String 1.1.9 - 2019-04-21 - Modify Output of SendFileResponse
1.1.2 Fixing bug for Contenttype 1.1.8 - 2019-04-15 - Add logger to Webserver Class
1.1.1 Update to local librarys 1.1.7 - 2019-04-14 - REQUEST_URL_HOST
1.1.0 Remove Helper from Bot-Utils</PackageReleaseNotes> 1.1.6 - 2019-04-03 - Refactoring and bugfixing
1.1.5 - 2019-03-27 - add a function to send an object as json directly
1.1.4 - 2019-03-13 - add Woff as Binary type
1.1.3 - 2019-03-10 - Variables parsing now as a String
1.1.2 - 2019-03-08 - Fixing bug for Contenttype
1.1.1 - 2019-02-17 - Update to local librarys
1.1.0 - 2019-02-14 - Remove Helper from Bot-Utils
</PackageReleaseNotes>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -39,10 +44,11 @@
<ItemGroup> <ItemGroup>
<Content Include="../CHANGELOG.md" /> <Content Include="../CHANGELOG.md" />
<Content Include="../CONTRIBUTING.md" /> <Content Include="../CONTRIBUTING.md" />
<Content Include="../LICENSE" /> <Content Include="../LICENSE" />
<Content Include="../README.md" /> <Content Include="../README.md" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\LICENSE"> <None Include="..\LICENSE">
<Pack>True</Pack> <Pack>True</Pack>
+70 -58
View File
@@ -1,60 +1,72 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using BlubbFish.Utils.IoT.Bots.Events; using BlubbFish.Utils.IoT.Bots.Events;
using BlubbFish.Utils.IoT.Bots.Interfaces; using BlubbFish.Utils.IoT.Bots.Interfaces;
using BlubbFish.Utils.IoT.Bots.Moduls; using BlubbFish.Utils.IoT.Bots.Moduls;
namespace BlubbFish.Utils.IoT.Bots { namespace BlubbFish.Utils.IoT.Bots {
public abstract class Bot<T> : ABot { public abstract class Bot<T> : ABot {
protected readonly Dictionary<String, AModul<T>> moduls = new Dictionary<String, AModul<T>>(); protected readonly Dictionary<String, AModul<T>> moduls = new Dictionary<String, AModul<T>>();
protected void ModulDispose() { public Bot(String[] args, Boolean fileLogging, String configSearchPath) : base(args, fileLogging, configSearchPath) { }
foreach (KeyValuePair<String, AModul<T>> item in this.moduls) {
item.Value.Dispose(); protected void ModulDispose() {
Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.ModulDispose: Modul entladen: " + item.Key); foreach (KeyValuePair<String, AModul<T>> item in this.moduls) {
} Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.ModulDispose: Entlade Modul: " + item.Key);
this.Dispose(); item.Value.Dispose();
} Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.ModulDispose: Modul entladen: " + item.Key);
}
protected void ModulLoader(String @namespace, Object library) { this.Dispose();
Assembly asm = Assembly.GetEntryAssembly(); }
foreach (Type item in asm.GetTypes()) {
if (item.Namespace == @namespace) { protected void ModulLoader(String @namespace, Object library) {
Type t = item; Assembly asm = Assembly.GetEntryAssembly();
String name = t.Name; foreach (Type item in asm.GetTypes()) {
try { if (item.Namespace == @namespace) {
if (InIReader.ConfigExist(name.ToLower())) { Type t = item;
Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.ModulLoader: Load Modul " + name); String name = t.Name;
this.moduls.Add(name, (AModul<T>)t.GetConstructor(new Type[] { typeof(T), typeof(InIReader) }).Invoke(new Object[] { library, InIReader.GetInstance(name.ToLower()) })); try {
Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.ModulLoader: Loaded Modul " + name); if (InIReader.ConfigExist(name.ToLower())) {
} else if (t.HasInterface(typeof(IForceLoad))) { Dictionary<String, String> modulconfig = InIReader.GetInstance(name.ToLower()).GetSection("modul");
Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.ModulLoader: Load Modul Forced " + name); if(!(modulconfig.ContainsKey("enabled") && modulconfig["enabled"].ToLower() == "false")) {
this.moduls.Add(name, (AModul<T>)t.GetConstructor(new Type[] { typeof(T), typeof(InIReader) }).Invoke(new Object[] { library, null })); Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.ModulLoader: Load Modul " + name);
Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.ModulLoader: Loaded Modul Forced " + name); this.moduls.Add(name, (AModul<T>)t.GetConstructor(new Type[] { typeof(T), typeof(InIReader) }).Invoke(new Object[] { library, InIReader.GetInstance(name.ToLower()) }));
} Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.ModulLoader: Loaded Modul " + name);
} catch(Exception e) { continue;
Helper.WriteError(e.InnerException.Message); }
} }
} if (t.HasInterface(typeof(IForceLoad))) {
} Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.ModulLoader: Forced Load Modul " + name);
} this.moduls.Add(name, (AModul<T>)t.GetConstructor(new Type[] { typeof(T), typeof(InIReader) }).Invoke(new Object[] { library, null }));
Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.ModulLoader: Forced Loaded Modul " + name);
protected void ModulInterconnect() { }
foreach (KeyValuePair<String, AModul<T>> item in this.moduls) { } catch(Exception e) {
item.Value.Interconnect(this.moduls); Helper.WriteError(e.InnerException.Message);
Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.ModulInterconnect: Interconnect Module " + item.Key); }
} }
} }
}
protected void ModulEvents() {
foreach (KeyValuePair<String, AModul<T>> item in this.moduls) { protected void ModulInterconnect() {
item.Value.EventLibSetter(); foreach (KeyValuePair<String, AModul<T>> item in this.moduls) {
item.Value.Update += this.ModulUpdate; item.Value.Interconnect(this.moduls);
Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.ModulEvents: Attach Event " + item.Key); Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.ModulInterconnect: Interconnect Module " + item.Key);
} }
} }
protected void ModulUpdate(Object sender, ModulEventArgs e) => Console.WriteLine(e.ToString()); protected void ModulEvents() {
} foreach (KeyValuePair<String, AModul<T>> item in this.moduls) {
} item.Value.EventLibSetter();
item.Value.Update += this.ModulUpdate;
Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.ModulEvents: Attach Event " + item.Key);
}
}
protected void ModulUpdate(Object sender, ModulEventArgs e) {
if(this.DebugLogging) {
Console.WriteLine(e.ToString());
}
}
}
}
+162 -170
View File
@@ -1,170 +1,162 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using BlubbFish.Utils.IoT.Bots.Interfaces;
using BlubbFish.Utils.IoT.Bots.Interfaces;
namespace BlubbFish.Utils.IoT.Bots.Moduls {
public abstract class CronJob<T> : AModul<T>, IDisposable, IForceLoad { namespace BlubbFish.Utils.IoT.Bots.Moduls {
protected readonly List<Tuple<String, Action<Object>, Object>> internalCron = new List<Tuple<String, Action<Object>, Object>>(); public abstract class CronJob<T> : AModul<T>, IForceLoad {
protected Thread thread; protected readonly List<Tuple<String, Action<Object>, Object>> internalCron = new List<Tuple<String, Action<Object>, Object>>();
protected DateTime crontime; protected Thread thread;
protected Boolean threadRunning = false;
protected readonly Dictionary<String, String> cron_named = new Dictionary<String, String> { protected DateTime crontime;
{ "@yearly", "0 0 1 1 *" },
{ "@annually", "0 0 1 1 *" }, protected readonly Dictionary<String, String> cron_named = new Dictionary<String, String> {
{ "@monthly", "0 0 1 * *" }, { "@yearly", "0 0 1 1 *" },
{ "@weekly", "0 0 * * 0" }, { "@annually", "0 0 1 1 *" },
{ "@daily", "0 0 * * *" }, { "@monthly", "0 0 1 * *" },
{ "@hourly", "0 * * * *" } { "@weekly", "0 0 * * 0" },
}; { "@daily", "0 0 * * *" },
{ "@hourly", "0 * * * *" }
#region Constructor };
public CronJob(T lib, InIReader settings) : base(lib, settings) {
this.crontime = DateTime.Now; #region Constructor
this.thread = new Thread(this.Runner); public CronJob(T lib, InIReader settings) : base(lib, settings) {
this.thread.Start(); this.crontime = DateTime.Now;
} this.thread = new Thread(this.Runner);
#endregion this.threadRunning = true;
this.thread.Start();
#region Cronjobrunner }
protected void Runner() { #endregion
Thread.Sleep(DateTime.Now.AddMinutes(1).AddSeconds(DateTime.Now.Second * -1).AddMilliseconds(DateTime.Now.Millisecond * -1) - DateTime.Now);
while (true) { #region Cronjobrunner
if (this.crontime.Minute != DateTime.Now.Minute) { protected void Runner() {
this.crontime = DateTime.Now; DateTime nextminute = DateTime.Now.AddMinutes(1).AddSeconds(DateTime.Now.Second * -1).AddMilliseconds(DateTime.Now.Millisecond * -1);
if (this.config.Count != 0) { while(nextminute > DateTime.Now && this.threadRunning) {
foreach (KeyValuePair<String, Dictionary<String, String>> item in this.config) { Thread.Sleep(100);
if (item.Value.ContainsKey("cron") && item.Value.ContainsKey("set") && this.ParseCronString(item.Value["cron"])) { }
this.SetValues(item.Value["set"]); while (this.threadRunning) {
} if (this.crontime.Minute != DateTime.Now.Minute) {
} this.crontime = DateTime.Now;
} if (this.config.Count != 0) {
foreach (Tuple<String, Action<Object>, Object> item in this.internalCron) { foreach (KeyValuePair<String, Dictionary<String, String>> item in this.config) {
if (this.ParseCronString(item.Item1)) { if (item.Value.ContainsKey("cron") && item.Value.ContainsKey("set") && this.ParseCronString(item.Value["cron"])) {
item.Item2?.Invoke(item.Item3); this.SetValues(item.Value["set"]);
} }
} }
} }
Thread.Sleep(100); foreach (Tuple<String, Action<Object>, Object> item in this.internalCron) {
} if (this.ParseCronString(item.Item1)) {
} item.Item2?.Invoke(item.Item3);
}
protected abstract void SetValues(String value); }
#endregion }
Thread.Sleep(100);
#region CronFunctions }
protected Boolean ParseCronString(String cronstring) { }
cronstring = cronstring.Trim();
if (this.cron_named.ContainsKey(cronstring)) { protected abstract void SetValues(String value);
cronstring = this.cron_named[cronstring]; #endregion
}
String[] value = cronstring.Split(' '); #region CronFunctions
if (value.Length != 5) { protected Boolean ParseCronString(String cronstring) {
return false; cronstring = cronstring.Trim();
} if (this.cron_named.ContainsKey(cronstring)) {
if (!this.CheckDateStr(this.crontime.ToString("mm"), value[0], "0-59")) { cronstring = this.cron_named[cronstring];
return false; }
} String[] value = cronstring.Split(' ');
if (!this.CheckDateStr(this.crontime.ToString("HH"), value[1], "0-23")) { if (value.Length != 5) {
return false; return false;
} }
if (!this.CheckDateStr(this.crontime.ToString("MM"), value[3], "1-12")) { if (!this.CheckDateStr(this.crontime.ToString("mm"), value[0], "0-59")) {
return false; return false;
} }
if (value[2] != "*" && value[4] != "*") { if (!this.CheckDateStr(this.crontime.ToString("HH"), value[1], "0-23")) {
if (!this.CheckDateStr(this.crontime.ToString("dd"), value[2], "1-31") && !this.CheckDateStr(((Int32)this.crontime.DayOfWeek).ToString(), value[4], "0-7")) { return false;
return false; }
} if (!this.CheckDateStr(this.crontime.ToString("MM"), value[3], "1-12")) {
} else { return false;
if (!this.CheckDateStr(this.crontime.ToString("dd"), value[2], "1-31")) { }
return false; if (value[2] != "*" && value[4] != "*") {
} if (!this.CheckDateStr(this.crontime.ToString("dd"), value[2], "1-31") && !this.CheckDateStr(((Int32)this.crontime.DayOfWeek).ToString(), value[4], "0-7")) {
if (!this.CheckDateStr(((Int32)this.crontime.DayOfWeek).ToString(), value[4], "0-7")) { return false;
return false; }
} } else {
} if (!this.CheckDateStr(this.crontime.ToString("dd"), value[2], "1-31")) {
return true; return false;
} }
protected Boolean CheckDateStr(String date, String cron, String limit) { if (!this.CheckDateStr(((Int32)this.crontime.DayOfWeek).ToString(), value[4], "0-7")) {
cron = cron.ToLower(); return false;
for (Int32 i = 0; i <= 6; i++) { }
cron = cron.Replace(DateTime.Parse("2015-01-" + (4 + i) + "T00:00:00").ToString("ddd", CultureInfo.CreateSpecificCulture("en-US")), i.ToString()); }
cron = cron.Replace(DateTime.Parse("2015-01-" + (4 + i) + "T00:00:00").ToString("dddd", CultureInfo.CreateSpecificCulture("en-US")), i.ToString()); return true;
} }
for (Int32 i = 1; i <= 12; i++) { protected Boolean CheckDateStr(String date, String cron, String limit) {
cron = cron.Replace(DateTime.Parse("2015-" + i + "-01T00:00:00").ToString("MMM", CultureInfo.CreateSpecificCulture("en-US")), i.ToString()); cron = cron.ToLower();
cron = cron.Replace(DateTime.Parse("2015-" + i + "-01T00:00:00").ToString("MMMM", CultureInfo.CreateSpecificCulture("en-US")), i.ToString()); for (Int32 i = 0; i <= 6; i++) {
} cron = cron.Replace(DateTime.Parse("2015-01-" + (4 + i) + "T00:00:00").ToString("ddd", CultureInfo.CreateSpecificCulture("en-US")), i.ToString());
if (cron.Contains("*")) { cron = cron.Replace(DateTime.Parse("2015-01-" + (4 + i) + "T00:00:00").ToString("dddd", CultureInfo.CreateSpecificCulture("en-US")), i.ToString());
cron = cron.Replace("*", limit); }
} for (Int32 i = 1; i <= 12; i++) {
if (cron.Contains("-")) { cron = cron.Replace(DateTime.Parse("2015-" + i + "-01T00:00:00").ToString("MMM", CultureInfo.CreateSpecificCulture("en-US")), i.ToString());
MatchCollection m = new Regex("(\\d+)-(\\d+)").Matches(cron); cron = cron.Replace(DateTime.Parse("2015-" + i + "-01T00:00:00").ToString("MMMM", CultureInfo.CreateSpecificCulture("en-US")), i.ToString());
foreach (Match p in m) { }
List<String> s = new List<String>(); if (cron.Contains("*")) {
for (Int32 i = Math.Min(Int32.Parse(p.Groups[1].Value), Int32.Parse(p.Groups[2].Value)); i <= Math.Max(Int32.Parse(p.Groups[1].Value), Int32.Parse(p.Groups[2].Value)); i++) { cron = cron.Replace("*", limit);
s.Add(i.ToString()); }
} if (cron.Contains("-")) {
cron = cron.Replace(p.Groups[0].Value, String.Join(",", s)); MatchCollection m = new Regex("(\\d+)-(\\d+)").Matches(cron);
} foreach (Match p in m) {
} List<String> s = new List<String>();
Int32 match = 0; for (Int32 i = Math.Min(Int32.Parse(p.Groups[1].Value), Int32.Parse(p.Groups[2].Value)); i <= Math.Max(Int32.Parse(p.Groups[1].Value), Int32.Parse(p.Groups[2].Value)); i++) {
if (cron.Contains("/")) { s.Add(i.ToString());
Match m = new Regex("/(\\d+)").Match(cron); }
cron = cron.Replace(m.Groups[0].Value, ""); cron = cron.Replace(p.Groups[0].Value, String.Join(",", s));
match = Int32.Parse(m.Groups[1].Value); }
} }
Dictionary<Int32, String> ret = new Dictionary<Int32, String>(); Int32 match = 0;
if (!cron.Contains(",")) { if (cron.Contains("/")) {
ret.Add(Int32.Parse(cron), ""); Match m = new Regex("/(\\d+)").Match(cron);
} else { cron = cron.Replace(m.Groups[0].Value, "");
foreach (String item in cron.Split(',')) { match = Int32.Parse(m.Groups[1].Value);
if (!ret.ContainsKey(Int32.Parse(item))) { }
ret.Add(Int32.Parse(item), ""); Dictionary<Int32, String> ret = new Dictionary<Int32, String>();
} if (!cron.Contains(",")) {
} ret.Add(Int32.Parse(cron), "");
} } else {
if (match != 0) { foreach (String item in cron.Split(',')) {
Dictionary<Int32, String> r = new Dictionary<Int32, String>(); if (!ret.ContainsKey(Int32.Parse(item))) {
foreach (KeyValuePair<Int32, String> item in ret) { ret.Add(Int32.Parse(item), "");
if (item.Key % match == 0) { }
r.Add(item.Key, ""); }
} }
} if (match != 0) {
ret = r; Dictionary<Int32, String> r = new Dictionary<Int32, String>();
} foreach (KeyValuePair<Int32, String> item in ret) {
return ret.ContainsKey(Int32.Parse(date)); if (item.Key % match == 0) {
} r.Add(item.Key, "");
#endregion }
}
#region AModul ret = r;
public override void SetInterconnection(String cron, Action<Object> hook, Object data) => this.internalCron.Add(new Tuple<String, Action<Object>, Object>(cron, hook, data)); }
return ret.ContainsKey(Int32.Parse(date));
protected override void UpdateConfig() { } }
#endregion #endregion
#region IDisposable Support #region AModul
private Boolean disposedValue = false; public override void SetInterconnection(String cron, Action<Object> hook, Object data) => this.internalCron.Add(new Tuple<String, Action<Object>, Object>(cron, hook, data));
protected virtual void Dispose(Boolean disposing) { protected override void UpdateConfig() { }
if (!this.disposedValue) {
if (disposing) { public override void Dispose() {
if (this.thread != null) { this.threadRunning = false;
this.thread.Abort(); while(this.thread != null && this.thread.IsAlive) {
while (this.thread.ThreadState == ThreadState.Running) { Thread.Sleep(100); } Thread.Sleep(10);
} }
} this.thread = null;
this.thread = null; }
this.disposedValue = true; #endregion
} }
} }
public override void Dispose() {
this.Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
}
+4 -26
View File
@@ -7,7 +7,7 @@ using BlubbFish.Utils.IoT.Events;
using LitJson; using LitJson;
namespace BlubbFish.Utils.IoT.Bots.Moduls { namespace BlubbFish.Utils.IoT.Bots.Moduls {
public abstract class Mqtt<T> : AModul<T>, IDisposable { public abstract class Mqtt<T> : AModul<T> {
protected ABackend mqtt; protected ABackend mqtt;
protected Dictionary<String, AModul<T>> modules; protected Dictionary<String, AModul<T>> modules;
@@ -25,13 +25,7 @@ namespace BlubbFish.Utils.IoT.Bots.Moduls {
} }
} }
protected void Connect() { protected void Connect() => this.mqtt = !this.config.ContainsKey("settings") ? throw new ArgumentException("Setting section [settings] is missing!") : ABackend.GetInstance(this.config["settings"], ABackend.BackendType.Data);
if(!this.config.ContainsKey("settings")) {
throw new ArgumentException("Setting section [settings] is missing!");
} else {
this.mqtt = ABackend.GetInstance(this.config["settings"], ABackend.BackendType.Data);
}
}
protected void Disconnect() => this.mqtt.Dispose(); protected void Disconnect() => this.mqtt.Dispose();
#endregion #endregion
@@ -40,6 +34,8 @@ namespace BlubbFish.Utils.IoT.Bots.Moduls {
public override void Interconnect(Dictionary<String, AModul<T>> moduls) => this.modules = moduls; public override void Interconnect(Dictionary<String, AModul<T>> moduls) => this.modules = moduls;
protected override void UpdateConfig() => this.Reconnect(); protected override void UpdateConfig() => this.Reconnect();
public override void Dispose() => this.Disconnect();
#endregion #endregion
protected Tuple<Boolean, MqttEvent> ChangeConfig(BackendEvent e, String topic) { protected Tuple<Boolean, MqttEvent> ChangeConfig(BackendEvent e, String topic) {
@@ -80,23 +76,5 @@ namespace BlubbFish.Utils.IoT.Bots.Moduls {
} }
return new Tuple<Boolean, MqttEvent>(false, null); return new Tuple<Boolean, MqttEvent>(false, null);
} }
#region IDisposable Support
private Boolean disposedValue = false;
protected void Dispose(Boolean disposing) {
if (!this.disposedValue) {
if (disposing) {
this.Disconnect();
}
this.disposedValue = true;
}
}
public override void Dispose() {
this.Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
} }
} }
+4 -19
View File
@@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace BlubbFish.Utils.IoT.Bots.Moduls { namespace BlubbFish.Utils.IoT.Bots.Moduls {
public abstract class Overtaker<T> : AModul<T>, IDisposable { public abstract class Overtaker<T> : AModul<T> {
protected readonly Dictionary<String, Dictionary<String, String>> events = new Dictionary<String, Dictionary<String, String>>(); protected readonly Dictionary<String, Dictionary<String, String>> events = new Dictionary<String, Dictionary<String, String>>();
#region Constructor #region Constructor
@@ -22,7 +22,7 @@ namespace BlubbFish.Utils.IoT.Bots.Moduls {
} }
} }
protected void SetValues(Object sender, String name, Dictionary<String, String> dictionary) { protected void SetValues(Object sender, String _, Dictionary<String, String> dictionary) {
String from = dictionary["from"]; String from = dictionary["from"];
String[] source = from.Split(':'); String[] source = from.Split(':');
if (source.Length != 2) { if (source.Length != 2) {
@@ -62,23 +62,8 @@ namespace BlubbFish.Utils.IoT.Bots.Moduls {
#region AModul #region AModul
public override void Interconnect(Dictionary<String, AModul<T>> moduls) { } public override void Interconnect(Dictionary<String, AModul<T>> moduls) { }
protected override void UpdateConfig() => this.ParseIni(); protected override void UpdateConfig() => this.ParseIni();
#endregion public override void Dispose() {
#region IDisposable Support
private Boolean disposedValue = false;
protected virtual void Dispose(Boolean disposing) {
if (!this.disposedValue) {
if (disposing) {
}
this.disposedValue = true;
}
} }
#endregion
public override void Dispose() {
this.Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
} }
} }
+2 -17
View File
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using BlubbFish.Utils.IoT.Bots.Interfaces; using BlubbFish.Utils.IoT.Bots.Interfaces;
namespace BlubbFish.Utils.IoT.Bots.Moduls { namespace BlubbFish.Utils.IoT.Bots.Moduls {
public abstract class Statuspolling<T> : AModul<T>, IDisposable, IForceLoad { public abstract class Statuspolling<T> : AModul<T>, IForceLoad {
#region Constructor #region Constructor
public Statuspolling(T lib, InIReader settings) : base(lib, settings) { } public Statuspolling(T lib, InIReader settings) : base(lib, settings) { }
@@ -31,22 +31,7 @@ namespace BlubbFish.Utils.IoT.Bots.Moduls {
} }
} }
protected override void UpdateConfig() { } protected override void UpdateConfig() { }
#endregion public override void Dispose() {
#region IDisposable Support
private Boolean disposedValue = false;
protected virtual void Dispose(Boolean disposing) {
if (!this.disposedValue) {
if (disposing) {
}
this.disposedValue = true;
}
}
public override void Dispose() {
this.Dispose(true);
GC.SuppressFinalize(this);
} }
#endregion #endregion
} }
+2 -4
View File
@@ -1,8 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BlubbFish.Utils.IoT.Connector; using BlubbFish.Utils.IoT.Connector;
namespace BlubbFish.Utils.IoT.Bots { namespace BlubbFish.Utils.IoT.Bots {
@@ -10,7 +8,7 @@ namespace BlubbFish.Utils.IoT.Bots {
protected Dictionary<String, ABackend> sources; protected Dictionary<String, ABackend> sources;
protected Dictionary<String, String> settings; protected Dictionary<String, String> settings;
protected MultiSourceBot(Dictionary<String, ABackend> sources, Dictionary<String, String> settings) { protected MultiSourceBot(String[] args, Boolean fileLogging, String configSearchPath, Dictionary<String, ABackend> sources, Dictionary<String, String> settings) : base(args, fileLogging, configSearchPath) {
this.sources = sources; this.sources = sources;
this.settings = settings; this.settings = settings;
} }
+124
View File
@@ -0,0 +1,124 @@
# Changelog
## 1.2.6 - 2022-01-25 - Makeing Logging easyier
### New Features
* Construct the Programm logger only if enabled
* Set Searchpath by default for a Bot
* Add an option that allows to debug logging (by default on)
### Bugfixes
### Changes
* Deconstruct Programmlogger
* Change all classes that extend ABot
* Module messages also uses debug logging flag
* Codingstyles
## 1.2.5 - 2022-01-20 - Better linux handling
### New Features
* Add ProcessExit Handler in ABot
* Add Output in ModulDispose
### Bugfixes
* Eleminate Hangs in Cronjob, when in startup phase it not blocks shutdown
### Changes
* Reweite ABot, remove Mono Code
* Codingstyle
## 1.2.4 - 2022-01-18 - Config enabled module loading
### New Features
* Modules can have an enabled=true|false in config, so that also enables or disables moduleloading.
### Bugfixes
### Changes
## 1.2.3 - 2022-01-09 - Tiny Refactoring
### New Features
### Bugfixes
### Changes
* Cleanup Changelog
* Tiny Refactorings
## 1.2.2 - 2021-08-22 - Going to netcore
### New Features
* Split Wbserver to AWebserver and AWebserverDataBackend
* Add mp4 as binary content
* make a mirror of this repo on github
### Bugfixes
### Changes
* change to c+ newer coding style
* mograde to c# netcore 3.1
## 1.2.1 - 2019-08-30 - When using Dispose, kill also mqtt connection and other tiny fixes
### New Features
* Add LICENSE, CONTRIBUTING.md and README.md
### Bugfixes
* When using Dispose, kill also mqtt connection
### Changes
* A bit more debugging
## 1.2.0 - 2019-05-27 - Refactor Bot to ABot and refere MultiSourceBot
### New Features
* Add MultiSourceBot*
* Refere MultiSourceBot, Webserver and Bot to it.
### Bugfixes
### Changes
* Refactor Bot to ABot
* Rewrite Mqtt module so that it not need to watch the connection.
## 1.1.9 - 2019-04-21 - Modify Output of SendFileResponse
### New Features
* Modify Output of SendFileResponse
### Bugfixes
### Changes
## 1.1.8 - 2019-04-15 - Add logger to Webserver Class
### New Features
* Add logger to Webserver Class
### Bugfixes
### Changes
## 1.1.7 - 2019-04-14 - REQUEST_URL_HOST
### New Features
### Bugfixes
### Changes
* Restrucutre loading, so that all is init and after the listener is started, REQUEST_URL_HOST gives now host and port
## 1.1.6 - 2019-04-03 - Refactoring and bugfixing
### New Features
* SendFileResponse with a parameter for the folder
* add function that parse post params
### Bugfixes
### Changes
## 1.1.5 - 2019-03-27 - add a function to send an object as json directly
### New Features
* add a function to send an object as json directly
### Bugfixes
### Changes
## 1.1.4 - 2019-03-13 - add Woff as Binary type
### New Features
* add Woff as Binary type
### Bugfixes
### Changes
## 1.1.3 - 2019-03-10 - Variables parsing now as a String
### New Features
### Bugfixes
### Changes
* Variables parsing now as a String
## 1.1.2 - 2019-03-08 - Fixing bug for Contenttype
### New Features
### Bugfixes
* Fixing bug for Contenttype
### Changes
## 1.1.1 - 2019-02-17 - Update to local librarys
### New Features
### Bugfixes
### Changes
* Update to local librarys
## 1.1.0 - 2019-02-14 - Remove Helper from Bot-Utils
### New Features
* Remove Helper from Bot-Utils
### Bugfixes
### Changes
+3 -2
View File
@@ -1,11 +1,12 @@
# BlubbFish.Utils.IoT.Bots (Bot-Utils) # BlubbFish.Utils.IoT.Bots (Bot-Utils)
Library that makes it easier to create bots. Library that makes it easier to create bots.
Maybe you find this Repo on Github. This is a mirror from [here](https://git.blubbfish.net/vs_utils/Bot-Utils).
## Linking to ## Linking to
### Internal ### Internal
* BlubbFish.Utils ([Utils](http://git.blubbfish.net/vs_utils/Utils)) * BlubbFish.Utils ([Utils](http://git.blubbfish.net/vs_utils/Utils))
* BlubbFish.Utils.IoT ([Utils-IoT](http://git.blubbfish.net/vs_utils/Utils-IoT)) * BlubbFish.Utils.IoT ([Utils-IoT](http://git.blubbfish.net/vs_utils/Utils-IoT))
### External ### External
* litjson * litjson ([Used Fork](https://github.com/blubbfish/litjson), [Original](https://github.com/LitJSON/litjson))
* Mono.Posix