From 621230afe9d9040536b43241e63117c9bb34beaa Mon Sep 17 00:00:00 2001 From: Roy Ben Shabat Date: Wed, 11 Dec 2019 20:57:30 +0200 Subject: Implemented Jobs, JobRuns & Machine Events Synchronization. Added synchronizations to Updates view on Machine Designer. Removed request response events logging from machine studio. Fixed issue with exception throwing from machine service. Implemented "New jobs synchronized" notification item to PPC. Added synchronization to PPC settings. Added Synchronization view to technician module. Implemented PPC Schema synchronizer utility. Added custom query support to EntityCollectionBuilder. Added synchronization status to TangoUpdate. Removed FK from Jobs and Job runs. --- .../Tango.MachineStudio.Common/EventLogging/DefaultEventLogger.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/EventLogging/DefaultEventLogger.cs') diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/EventLogging/DefaultEventLogger.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/EventLogging/DefaultEventLogger.cs index 11d156292..9992d4e2f 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/EventLogging/DefaultEventLogger.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/EventLogging/DefaultEventLogger.cs @@ -137,7 +137,7 @@ namespace Tango.MachineStudio.Common.EventLogging /// The message. private void Machine_RequestSent(object sender, IMessage message) { - Log(EventTypes.REQUEST_SENT, String.Format("Sending request '{0}'...{1}{2}", message.GetType().Name, Environment.NewLine, message.ToJsonString())); + //Log(EventTypes.REQUEST_SENT, String.Format("Sending request '{0}'...{1}{2}", message.GetType().Name, Environment.NewLine, message.ToJsonString())); } /// @@ -147,7 +147,7 @@ namespace Tango.MachineStudio.Common.EventLogging /// The instance containing the event data. private void Machine_RequestFailed(object sender, RequestFailedEventArgs e) { - Log(EventTypes.REQUEST_FAILED, String.Format("Request failed '{0}'...{1}{2}{1}{3}", e.Message.GetType().Name, Environment.NewLine, e.Message.ToJsonString(), e.Exception.ToString())); + //Log(EventTypes.REQUEST_FAILED, String.Format("Request failed '{0}'...{1}{2}{1}{3}", e.Message.GetType().Name, Environment.NewLine, e.Message.ToJsonString(), e.Exception.ToString())); } /// @@ -157,7 +157,7 @@ namespace Tango.MachineStudio.Common.EventLogging /// The message. private void Machine_ResponseReceived(object sender, IMessage message) { - Log(EventTypes.RESPONSE_RECEIVED, String.Format("Response received '{0}'...{1}{2}", message.GetType().Name, Environment.NewLine, message.ToJsonString())); + //Log(EventTypes.RESPONSE_RECEIVED, String.Format("Response received '{0}'...{1}{2}", message.GetType().Name, Environment.NewLine, message.ToJsonString())); } /// -- cgit v1.3.1 From 2367c43d732aea2ea169f6fa5844fc877d96632d Mon Sep 17 00:00:00 2001 From: Roy Ben Shabat Date: Sat, 25 Jan 2020 21:23:35 +0200 Subject: Implemented Hangfire experiment. Refactored Transporter to accept a configuration instead of multiple parameters on all request/response methods. Refactored Transporter to automatically log request/response messaged based on configuration. Fixed issue with ContinuousResponseAborted and external bridge. --- Software/DB/PPC/Tango.mdf | Bin 75497472 -> 75497472 bytes Software/DB/PPC/Tango_log.ldf | Bin 53673984 -> 53673984 bytes Software/DB/Tango.mdf | Bin 75497472 -> 75497472 bytes Software/DB/Tango_log.ldf | Bin 22675456 -> 22675456 bytes Software/Experiments/HangFireTest/HangFireTest.sln | 25 + .../HangFireTest/HangFireTest/App.config | 19 + .../HangFireTest/HangFireTest/CustomLogger.cs | 37 + .../HangFireTest/HangFireModel.Context.cs | 30 + .../HangFireTest/HangFireModel.Context.tt | 636 +++++++++++++++++ .../HangFireTest/HangFireModel.Designer.cs | 10 + .../HangFireTest/HangFireTest/HangFireModel.cs | 9 + .../HangFireTest/HangFireTest/HangFireModel.edmx | 85 +++ .../HangFireTest/HangFireModel.edmx.diagram | 12 + .../HangFireTest/HangFireTest/HangFireModel.tt | 733 +++++++++++++++++++ .../HangFireTest/HangFireTest/HangFireTest.csproj | 116 ++++ .../Experiments/HangFireTest/HangFireTest/Job.cs | 25 + .../HangFireTest/HangFireTest/Program.cs | 107 +++ .../HangFireTest/Properties/AssemblyInfo.cs | 36 + .../HangFireTest/HangFireTest/packages.config | 8 + .../EventLogging/DefaultEventLogger.cs | 1 + .../EventLogging/DefaultEventLogger.cs | 1 + .../Tango.Emulations/Emulators/MachineEmulator.cs | 51 +- .../ExternalBridge/ExternalBridgeReceiver.cs | 20 +- .../ExternalBridge/ExternalBridgeSignalRClient.cs | 6 +- .../ExternalBridge/ExternalBridgeTcpClient.cs | 34 +- .../Operation/IMachineOperator.cs | 20 - .../Tango.Integration/Operation/MachineOperator.cs | 772 ++++++++------------- .../Operation/RequestFailedEventArgs.cs | 22 - .../Tango.Integration/Storage/StorageManager.cs | 45 +- .../Tango.Integration/Tango.Integration.csproj | 3 +- Software/Visual_Studio/Tango.Stubs/StubManager.cs | 4 +- .../ContinuousResponseAbortedException.cs | 10 +- .../Discovery/UsbCommunicationScanner.cs | 2 +- .../Visual_Studio/Tango.Transport/ITransporter.cs | 83 ++- .../Tango.Transport/RequestFailedEventArgs.cs | 22 + .../Tango.Transport/Tango.Transport.csproj | 6 +- .../TransportContinuousRequestConfig.cs | 13 + .../Tango.Transport/TransportMessage.cs | 52 +- .../Tango.Transport/TransportMessageBase.cs | 13 + .../Tango.Transport/TransportRequestConfig.cs | 14 + .../Tango.Transport/TransportResponseConfig.cs | 16 + .../Tango.Transport/TransporterBase.cs | 495 ++++++++----- 42 files changed, 2751 insertions(+), 842 deletions(-) create mode 100644 Software/Experiments/HangFireTest/HangFireTest.sln create mode 100644 Software/Experiments/HangFireTest/HangFireTest/App.config create mode 100644 Software/Experiments/HangFireTest/HangFireTest/CustomLogger.cs create mode 100644 Software/Experiments/HangFireTest/HangFireTest/HangFireModel.Context.cs create mode 100644 Software/Experiments/HangFireTest/HangFireTest/HangFireModel.Context.tt create mode 100644 Software/Experiments/HangFireTest/HangFireTest/HangFireModel.Designer.cs create mode 100644 Software/Experiments/HangFireTest/HangFireTest/HangFireModel.cs create mode 100644 Software/Experiments/HangFireTest/HangFireTest/HangFireModel.edmx create mode 100644 Software/Experiments/HangFireTest/HangFireTest/HangFireModel.edmx.diagram create mode 100644 Software/Experiments/HangFireTest/HangFireTest/HangFireModel.tt create mode 100644 Software/Experiments/HangFireTest/HangFireTest/HangFireTest.csproj create mode 100644 Software/Experiments/HangFireTest/HangFireTest/Job.cs create mode 100644 Software/Experiments/HangFireTest/HangFireTest/Program.cs create mode 100644 Software/Experiments/HangFireTest/HangFireTest/Properties/AssemblyInfo.cs create mode 100644 Software/Experiments/HangFireTest/HangFireTest/packages.config delete mode 100644 Software/Visual_Studio/Tango.Integration/Operation/RequestFailedEventArgs.cs create mode 100644 Software/Visual_Studio/Tango.Transport/RequestFailedEventArgs.cs create mode 100644 Software/Visual_Studio/Tango.Transport/TransportContinuousRequestConfig.cs create mode 100644 Software/Visual_Studio/Tango.Transport/TransportRequestConfig.cs create mode 100644 Software/Visual_Studio/Tango.Transport/TransportResponseConfig.cs (limited to 'Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/EventLogging/DefaultEventLogger.cs') diff --git a/Software/DB/PPC/Tango.mdf b/Software/DB/PPC/Tango.mdf index d269178b8..bdc7134c7 100644 Binary files a/Software/DB/PPC/Tango.mdf and b/Software/DB/PPC/Tango.mdf differ diff --git a/Software/DB/PPC/Tango_log.ldf b/Software/DB/PPC/Tango_log.ldf index e9f5137d0..3d480cd4e 100644 Binary files a/Software/DB/PPC/Tango_log.ldf and b/Software/DB/PPC/Tango_log.ldf differ diff --git a/Software/DB/Tango.mdf b/Software/DB/Tango.mdf index d270bb895..d765d6513 100644 Binary files a/Software/DB/Tango.mdf and b/Software/DB/Tango.mdf differ diff --git a/Software/DB/Tango_log.ldf b/Software/DB/Tango_log.ldf index ebd97b83a..99cf5050f 100644 Binary files a/Software/DB/Tango_log.ldf and b/Software/DB/Tango_log.ldf differ diff --git a/Software/Experiments/HangFireTest/HangFireTest.sln b/Software/Experiments/HangFireTest/HangFireTest.sln new file mode 100644 index 000000000..43789224b --- /dev/null +++ b/Software/Experiments/HangFireTest/HangFireTest.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.645 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HangFireTest", "HangFireTest\HangFireTest.csproj", "{0A3D27ED-178E-40B7-BB6C-94BD552D4971}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0A3D27ED-178E-40B7-BB6C-94BD552D4971}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0A3D27ED-178E-40B7-BB6C-94BD552D4971}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0A3D27ED-178E-40B7-BB6C-94BD552D4971}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0A3D27ED-178E-40B7-BB6C-94BD552D4971}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F0D5C4A9-5629-4934-999D-E2438120DFC4} + EndGlobalSection +EndGlobal diff --git a/Software/Experiments/HangFireTest/HangFireTest/App.config b/Software/Experiments/HangFireTest/HangFireTest/App.config new file mode 100644 index 000000000..3a79005cd --- /dev/null +++ b/Software/Experiments/HangFireTest/HangFireTest/App.config @@ -0,0 +1,19 @@ + + + + +
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Software/Experiments/HangFireTest/HangFireTest/CustomLogger.cs b/Software/Experiments/HangFireTest/HangFireTest/CustomLogger.cs new file mode 100644 index 000000000..e0b5a888b --- /dev/null +++ b/Software/Experiments/HangFireTest/HangFireTest/CustomLogger.cs @@ -0,0 +1,37 @@ +using Hangfire.Logging; +using System; + +public class CustomLogger : ILog +{ + public string Name { get; set; } + + public bool Log(LogLevel logLevel, Func messageFunc, Exception exception = null) + { + if (messageFunc == null) + { + // Before calling a method with an actual message, LogLib first probes + // whether the corresponding log level is enabled by passing a `null` + // messageFunc instance. + return logLevel > LogLevel.Info; + } + + // Writing a message somewhere, make sure you also include the exception parameter, + // because it usually contain valuable information, but it can be `null` for regular + // messages. + Console.WriteLine(String.Format("{0}: {1} {2} {3}", logLevel, Name, messageFunc(), exception)); + + // Telling LibLog the message was successfully logged. + return true; + } +} + +public class CustomLogProvider : ILogProvider +{ + public ILog GetLogger(string name) + { + // Logger name usually contains the full name of a type that uses it, + // e.g. "Hangfire.Server.RecurringJobScheduler". It's used to know the + // context of this or that message and for filtering purposes. + return new CustomLogger { Name = name }; + } +} \ No newline at end of file diff --git a/Software/Experiments/HangFireTest/HangFireTest/HangFireModel.Context.cs b/Software/Experiments/HangFireTest/HangFireTest/HangFireModel.Context.cs new file mode 100644 index 000000000..057b730a0 --- /dev/null +++ b/Software/Experiments/HangFireTest/HangFireTest/HangFireModel.Context.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace HangFireTest +{ + using System; + using System.Data.Entity; + using System.Data.Entity.Infrastructure; + + public partial class HangFireEntities : DbContext + { + public HangFireEntities() + : base("name=HangFireEntities") + { + } + + protected override void OnModelCreating(DbModelBuilder modelBuilder) + { + throw new UnintentionalCodeFirstException(); + } + + public virtual DbSet Job { get; set; } + } +} diff --git a/Software/Experiments/HangFireTest/HangFireTest/HangFireModel.Context.tt b/Software/Experiments/HangFireTest/HangFireTest/HangFireModel.Context.tt new file mode 100644 index 000000000..75bdd2bbc --- /dev/null +++ b/Software/Experiments/HangFireTest/HangFireTest/HangFireModel.Context.tt @@ -0,0 +1,636 @@ +<#@ template language="C#" debug="false" hostspecific="true"#> +<#@ include file="EF6.Utility.CS.ttinclude"#><#@ + output extension=".cs"#><# + +const string inputFile = @"HangFireModel.edmx"; +var textTransform = DynamicTextTransformation.Create(this); +var code = new CodeGenerationTools(this); +var ef = new MetadataTools(this); +var typeMapper = new TypeMapper(code, ef, textTransform.Errors); +var loader = new EdmMetadataLoader(textTransform.Host, textTransform.Errors); +var itemCollection = loader.CreateEdmItemCollection(inputFile); +var modelNamespace = loader.GetModelNamespace(inputFile); +var codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef); + +var container = itemCollection.OfType().FirstOrDefault(); +if (container == null) +{ + return string.Empty; +} +#> +//------------------------------------------------------------------------------ +// +// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine1")#> +// +// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine2")#> +// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine3")#> +// +//------------------------------------------------------------------------------ + +<# + +var codeNamespace = code.VsNamespaceSuggestion(); +if (!String.IsNullOrEmpty(codeNamespace)) +{ +#> +namespace <#=code.EscapeNamespace(codeNamespace)#> +{ +<# + PushIndent(" "); +} + +#> +using System; +using System.Data.Entity; +using System.Data.Entity.Infrastructure; +<# +if (container.FunctionImports.Any()) +{ +#> +using System.Data.Entity.Core.Objects; +using System.Linq; +<# +} +#> + +<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext +{ + public <#=code.Escape(container)#>() + : base("name=<#=container.Name#>") + { +<# +if (!loader.IsLazyLoadingEnabled(container)) +{ +#> + this.Configuration.LazyLoadingEnabled = false; +<# +} + +foreach (var entitySet in container.BaseEntitySets.OfType()) +{ + // Note: the DbSet members are defined below such that the getter and + // setter always have the same accessibility as the DbSet definition + if (Accessibility.ForReadOnlyProperty(entitySet) != "public") + { +#> + <#=codeStringGenerator.DbSetInitializer(entitySet)#> +<# + } +} +#> + } + + protected override void OnModelCreating(DbModelBuilder modelBuilder) + { + throw new UnintentionalCodeFirstException(); + } + +<# + foreach (var entitySet in container.BaseEntitySets.OfType()) + { +#> + <#=codeStringGenerator.DbSet(entitySet)#> +<# + } + + foreach (var edmFunction in container.FunctionImports) + { + WriteFunctionImport(typeMapper, codeStringGenerator, edmFunction, modelNamespace, includeMergeOption: false); + } +#> +} +<# + +if (!String.IsNullOrEmpty(codeNamespace)) +{ + PopIndent(); +#> +} +<# +} +#> +<#+ + +private void WriteFunctionImport(TypeMapper typeMapper, CodeStringGenerator codeStringGenerator, EdmFunction edmFunction, string modelNamespace, bool includeMergeOption) +{ + if (typeMapper.IsComposable(edmFunction)) + { +#> + + [DbFunction("<#=edmFunction.NamespaceName#>", "<#=edmFunction.Name#>")] + <#=codeStringGenerator.ComposableFunctionMethod(edmFunction, modelNamespace)#> + { +<#+ + codeStringGenerator.WriteFunctionParameters(edmFunction, WriteFunctionParameter); +#> + <#=codeStringGenerator.ComposableCreateQuery(edmFunction, modelNamespace)#> + } +<#+ + } + else + { +#> + + <#=codeStringGenerator.FunctionMethod(edmFunction, modelNamespace, includeMergeOption)#> + { +<#+ + codeStringGenerator.WriteFunctionParameters(edmFunction, WriteFunctionParameter); +#> + <#=codeStringGenerator.ExecuteFunction(edmFunction, modelNamespace, includeMergeOption)#> + } +<#+ + if (typeMapper.GenerateMergeOptionFunction(edmFunction, includeMergeOption)) + { + WriteFunctionImport(typeMapper, codeStringGenerator, edmFunction, modelNamespace, includeMergeOption: true); + } + } +} + +public void WriteFunctionParameter(string name, string isNotNull, string notNullInit, string nullInit) +{ +#> + var <#=name#> = <#=isNotNull#> ? + <#=notNullInit#> : + <#=nullInit#>; + +<#+ +} + +public const string TemplateId = "CSharp_DbContext_Context_EF6"; + +public class CodeStringGenerator +{ + private readonly CodeGenerationTools _code; + private readonly TypeMapper _typeMapper; + private readonly MetadataTools _ef; + + public CodeStringGenerator(CodeGenerationTools code, TypeMapper typeMapper, MetadataTools ef) + { + ArgumentNotNull(code, "code"); + ArgumentNotNull(typeMapper, "typeMapper"); + ArgumentNotNull(ef, "ef"); + + _code = code; + _typeMapper = typeMapper; + _ef = ef; + } + + public string Property(EdmProperty edmProperty) + { + return string.Format( + CultureInfo.InvariantCulture, + "{0} {1} {2} {{ {3}get; {4}set; }}", + Accessibility.ForProperty(edmProperty), + _typeMapper.GetTypeName(edmProperty.TypeUsage), + _code.Escape(edmProperty), + _code.SpaceAfter(Accessibility.ForGetter(edmProperty)), + _code.SpaceAfter(Accessibility.ForSetter(edmProperty))); + } + + public string NavigationProperty(NavigationProperty navProp) + { + var endType = _typeMapper.GetTypeName(navProp.ToEndMember.GetEntityType()); + return string.Format( + CultureInfo.InvariantCulture, + "{0} {1} {2} {{ {3}get; {4}set; }}", + AccessibilityAndVirtual(Accessibility.ForNavigationProperty(navProp)), + navProp.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType, + _code.Escape(navProp), + _code.SpaceAfter(Accessibility.ForGetter(navProp)), + _code.SpaceAfter(Accessibility.ForSetter(navProp))); + } + + public string AccessibilityAndVirtual(string accessibility) + { + return accessibility + (accessibility != "private" ? " virtual" : ""); + } + + public string EntityClassOpening(EntityType entity) + { + return string.Format( + CultureInfo.InvariantCulture, + "{0} {1}partial class {2}{3}", + Accessibility.ForType(entity), + _code.SpaceAfter(_code.AbstractOption(entity)), + _code.Escape(entity), + _code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType))); + } + + public string EnumOpening(SimpleType enumType) + { + return string.Format( + CultureInfo.InvariantCulture, + "{0} enum {1} : {2}", + Accessibility.ForType(enumType), + _code.Escape(enumType), + _code.Escape(_typeMapper.UnderlyingClrType(enumType))); + } + + public void WriteFunctionParameters(EdmFunction edmFunction, Action writeParameter) + { + var parameters = FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef); + foreach (var parameter in parameters.Where(p => p.NeedsLocalVariable)) + { + var isNotNull = parameter.IsNullableOfT ? parameter.FunctionParameterName + ".HasValue" : parameter.FunctionParameterName + " != null"; + var notNullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", " + parameter.FunctionParameterName + ")"; + var nullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", typeof(" + TypeMapper.FixNamespaces(parameter.RawClrTypeName) + "))"; + writeParameter(parameter.LocalVariableName, isNotNull, notNullInit, nullInit); + } + } + + public string ComposableFunctionMethod(EdmFunction edmFunction, string modelNamespace) + { + var parameters = _typeMapper.GetParameters(edmFunction); + + return string.Format( + CultureInfo.InvariantCulture, + "{0} IQueryable<{1}> {2}({3})", + AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)), + _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace), + _code.Escape(edmFunction), + string.Join(", ", parameters.Select(p => TypeMapper.FixNamespaces(p.FunctionParameterType) + " " + p.FunctionParameterName).ToArray())); + } + + public string ComposableCreateQuery(EdmFunction edmFunction, string modelNamespace) + { + var parameters = _typeMapper.GetParameters(edmFunction); + + return string.Format( + CultureInfo.InvariantCulture, + "return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<{0}>(\"[{1}].[{2}]({3})\"{4});", + _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace), + edmFunction.NamespaceName, + edmFunction.Name, + string.Join(", ", parameters.Select(p => "@" + p.EsqlParameterName).ToArray()), + _code.StringBefore(", ", string.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray()))); + } + + public string FunctionMethod(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption) + { + var parameters = _typeMapper.GetParameters(edmFunction); + var returnType = _typeMapper.GetReturnType(edmFunction); + + var paramList = String.Join(", ", parameters.Select(p => TypeMapper.FixNamespaces(p.FunctionParameterType) + " " + p.FunctionParameterName).ToArray()); + if (includeMergeOption) + { + paramList = _code.StringAfter(paramList, ", ") + "MergeOption mergeOption"; + } + + return string.Format( + CultureInfo.InvariantCulture, + "{0} {1} {2}({3})", + AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)), + returnType == null ? "int" : "ObjectResult<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">", + _code.Escape(edmFunction), + paramList); + } + + public string ExecuteFunction(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption) + { + var parameters = _typeMapper.GetParameters(edmFunction); + var returnType = _typeMapper.GetReturnType(edmFunction); + + var callParams = _code.StringBefore(", ", String.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray())); + if (includeMergeOption) + { + callParams = ", mergeOption" + callParams; + } + + return string.Format( + CultureInfo.InvariantCulture, + "return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction{0}(\"{1}\"{2});", + returnType == null ? "" : "<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">", + edmFunction.Name, + callParams); + } + + public string DbSet(EntitySet entitySet) + { + return string.Format( + CultureInfo.InvariantCulture, + "{0} virtual DbSet<{1}> {2} {{ get; set; }}", + Accessibility.ForReadOnlyProperty(entitySet), + _typeMapper.GetTypeName(entitySet.ElementType), + _code.Escape(entitySet)); + } + + public string DbSetInitializer(EntitySet entitySet) + { + return string.Format( + CultureInfo.InvariantCulture, + "{0} = Set<{1}>();", + _code.Escape(entitySet), + _typeMapper.GetTypeName(entitySet.ElementType)); + } + + public string UsingDirectives(bool inHeader, bool includeCollections = true) + { + return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion()) + ? string.Format( + CultureInfo.InvariantCulture, + "{0}using System;{1}" + + "{2}", + inHeader ? Environment.NewLine : "", + includeCollections ? (Environment.NewLine + "using System.Collections.Generic;") : "", + inHeader ? "" : Environment.NewLine) + : ""; + } +} + +public class TypeMapper +{ + private const string ExternalTypeNameAttributeName = @"http://schemas.microsoft.com/ado/2006/04/codegeneration:ExternalTypeName"; + + private readonly System.Collections.IList _errors; + private readonly CodeGenerationTools _code; + private readonly MetadataTools _ef; + + public static string FixNamespaces(string typeName) + { + return typeName.Replace("System.Data.Spatial.", "System.Data.Entity.Spatial."); + } + + public TypeMapper(CodeGenerationTools code, MetadataTools ef, System.Collections.IList errors) + { + ArgumentNotNull(code, "code"); + ArgumentNotNull(ef, "ef"); + ArgumentNotNull(errors, "errors"); + + _code = code; + _ef = ef; + _errors = errors; + } + + public string GetTypeName(TypeUsage typeUsage) + { + return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace: null); + } + + public string GetTypeName(EdmType edmType) + { + return GetTypeName(edmType, isNullable: null, modelNamespace: null); + } + + public string GetTypeName(TypeUsage typeUsage, string modelNamespace) + { + return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace); + } + + public string GetTypeName(EdmType edmType, string modelNamespace) + { + return GetTypeName(edmType, isNullable: null, modelNamespace: modelNamespace); + } + + public string GetTypeName(EdmType edmType, bool? isNullable, string modelNamespace) + { + if (edmType == null) + { + return null; + } + + var collectionType = edmType as CollectionType; + if (collectionType != null) + { + return String.Format(CultureInfo.InvariantCulture, "ICollection<{0}>", GetTypeName(collectionType.TypeUsage, modelNamespace)); + } + + var typeName = _code.Escape(edmType.MetadataProperties + .Where(p => p.Name == ExternalTypeNameAttributeName) + .Select(p => (string)p.Value) + .FirstOrDefault()) + ?? (modelNamespace != null && edmType.NamespaceName != modelNamespace ? + _code.CreateFullName(_code.EscapeNamespace(edmType.NamespaceName), _code.Escape(edmType)) : + _code.Escape(edmType)); + + if (edmType is StructuralType) + { + return typeName; + } + + if (edmType is SimpleType) + { + var clrType = UnderlyingClrType(edmType); + if (!IsEnumType(edmType)) + { + typeName = _code.Escape(clrType); + } + + typeName = FixNamespaces(typeName); + + return clrType.IsValueType && isNullable == true ? + String.Format(CultureInfo.InvariantCulture, "Nullable<{0}>", typeName) : + typeName; + } + + throw new ArgumentException("edmType"); + } + + public Type UnderlyingClrType(EdmType edmType) + { + ArgumentNotNull(edmType, "edmType"); + + var primitiveType = edmType as PrimitiveType; + if (primitiveType != null) + { + return primitiveType.ClrEquivalentType; + } + + if (IsEnumType(edmType)) + { + return GetEnumUnderlyingType(edmType).ClrEquivalentType; + } + + return typeof(object); + } + + public object GetEnumMemberValue(MetadataItem enumMember) + { + ArgumentNotNull(enumMember, "enumMember"); + + var valueProperty = enumMember.GetType().GetProperty("Value"); + return valueProperty == null ? null : valueProperty.GetValue(enumMember, null); + } + + public string GetEnumMemberName(MetadataItem enumMember) + { + ArgumentNotNull(enumMember, "enumMember"); + + var nameProperty = enumMember.GetType().GetProperty("Name"); + return nameProperty == null ? null : (string)nameProperty.GetValue(enumMember, null); + } + + public System.Collections.IEnumerable GetEnumMembers(EdmType enumType) + { + ArgumentNotNull(enumType, "enumType"); + + var membersProperty = enumType.GetType().GetProperty("Members"); + return membersProperty != null + ? (System.Collections.IEnumerable)membersProperty.GetValue(enumType, null) + : Enumerable.Empty(); + } + + public bool EnumIsFlags(EdmType enumType) + { + ArgumentNotNull(enumType, "enumType"); + + var isFlagsProperty = enumType.GetType().GetProperty("IsFlags"); + return isFlagsProperty != null && (bool)isFlagsProperty.GetValue(enumType, null); + } + + public bool IsEnumType(GlobalItem edmType) + { + ArgumentNotNull(edmType, "edmType"); + + return edmType.GetType().Name == "EnumType"; + } + + public PrimitiveType GetEnumUnderlyingType(EdmType enumType) + { + ArgumentNotNull(enumType, "enumType"); + + return (PrimitiveType)enumType.GetType().GetProperty("UnderlyingType").GetValue(enumType, null); + } + + public string CreateLiteral(object value) + { + if (value == null || value.GetType() != typeof(TimeSpan)) + { + return _code.CreateLiteral(value); + } + + return string.Format(CultureInfo.InvariantCulture, "new TimeSpan({0})", ((TimeSpan)value).Ticks); + } + + public bool VerifyCaseInsensitiveTypeUniqueness(IEnumerable types, string sourceFile) + { + ArgumentNotNull(types, "types"); + ArgumentNotNull(sourceFile, "sourceFile"); + + var hash = new HashSet(StringComparer.InvariantCultureIgnoreCase); + if (types.Any(item => !hash.Add(item))) + { + _errors.Add( + new CompilerError(sourceFile, -1, -1, "6023", + String.Format(CultureInfo.CurrentCulture, CodeGenerationTools.GetResourceString("Template_CaseInsensitiveTypeConflict")))); + return false; + } + return true; + } + + public IEnumerable GetEnumItemsToGenerate(IEnumerable itemCollection) + { + return GetItemsToGenerate(itemCollection) + .Where(e => IsEnumType(e)); + } + + public IEnumerable GetItemsToGenerate(IEnumerable itemCollection) where T: EdmType + { + return itemCollection + .OfType() + .Where(i => !i.MetadataProperties.Any(p => p.Name == ExternalTypeNameAttributeName)) + .OrderBy(i => i.Name); + } + + public IEnumerable GetAllGlobalItems(IEnumerable itemCollection) + { + return itemCollection + .Where(i => i is EntityType || i is ComplexType || i is EntityContainer || IsEnumType(i)) + .Select(g => GetGlobalItemName(g)); + } + + public string GetGlobalItemName(GlobalItem item) + { + if (item is EdmType) + { + return ((EdmType)item).Name; + } + else + { + return ((EntityContainer)item).Name; + } + } + + public IEnumerable GetSimpleProperties(EntityType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); + } + + public IEnumerable GetSimpleProperties(ComplexType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); + } + + public IEnumerable GetComplexProperties(EntityType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); + } + + public IEnumerable GetComplexProperties(ComplexType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); + } + + public IEnumerable GetPropertiesWithDefaultValues(EntityType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null); + } + + public IEnumerable GetPropertiesWithDefaultValues(ComplexType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null); + } + + public IEnumerable GetNavigationProperties(EntityType type) + { + return type.NavigationProperties.Where(np => np.DeclaringType == type); + } + + public IEnumerable GetCollectionNavigationProperties(EntityType type) + { + return type.NavigationProperties.Where(np => np.DeclaringType == type && np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many); + } + + public FunctionParameter GetReturnParameter(EdmFunction edmFunction) + { + ArgumentNotNull(edmFunction, "edmFunction"); + + var returnParamsProperty = edmFunction.GetType().GetProperty("ReturnParameters"); + return returnParamsProperty == null + ? edmFunction.ReturnParameter + : ((IEnumerable)returnParamsProperty.GetValue(edmFunction, null)).FirstOrDefault(); + } + + public bool IsComposable(EdmFunction edmFunction) + { + ArgumentNotNull(edmFunction, "edmFunction"); + + var isComposableProperty = edmFunction.GetType().GetProperty("IsComposableAttribute"); + return isComposableProperty != null && (bool)isComposableProperty.GetValue(edmFunction, null); + } + + public IEnumerable GetParameters(EdmFunction edmFunction) + { + return FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef); + } + + public TypeUsage GetReturnType(EdmFunction edmFunction) + { + var returnParam = GetReturnParameter(edmFunction); + return returnParam == null ? null : _ef.GetElementType(returnParam.TypeUsage); + } + + public bool GenerateMergeOptionFunction(EdmFunction edmFunction, bool includeMergeOption) + { + var returnType = GetReturnType(edmFunction); + return !includeMergeOption && returnType != null && returnType.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType; + } +} + +public static void ArgumentNotNull(T arg, string name) where T : class +{ + if (arg == null) + { + throw new ArgumentNullException(name); + } +} +#> \ No newline at end of file diff --git a/Software/Experiments/HangFireTest/HangFireTest/HangFireModel.Designer.cs b/Software/Experiments/HangFireTest/HangFireTest/HangFireModel.Designer.cs new file mode 100644 index 000000000..962368d70 --- /dev/null +++ b/Software/Experiments/HangFireTest/HangFireTest/HangFireModel.Designer.cs @@ -0,0 +1,10 @@ +// T4 code generation is enabled for model 'C:\Users\Roy\source\repos\HangFireTest\HangFireTest\HangFireModel.edmx'. +// To enable legacy code generation, change the value of the 'Code Generation Strategy' designer +// property to 'Legacy ObjectContext'. This property is available in the Properties Window when the model +// is open in the designer. + +// If no context and entity classes have been generated, it may be because you created an empty model but +// have not yet chosen which version of Entity Framework to use. To generate a context class and entity +// classes for your model, open the model in the designer, right-click on the designer surface, and +// select 'Update Model from Database...', 'Generate Database from Model...', or 'Add Code Generation +// Item...'. \ No newline at end of file diff --git a/Software/Experiments/HangFireTest/HangFireTest/HangFireModel.cs b/Software/Experiments/HangFireTest/HangFireTest/HangFireModel.cs new file mode 100644 index 000000000..7cc066228 --- /dev/null +++ b/Software/Experiments/HangFireTest/HangFireTest/HangFireModel.cs @@ -0,0 +1,9 @@ +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + diff --git a/Software/Experiments/HangFireTest/HangFireTest/HangFireModel.edmx b/Software/Experiments/HangFireTest/HangFireTest/HangFireModel.edmx new file mode 100644 index 000000000..71b84d72a --- /dev/null +++ b/Software/Experiments/HangFireTest/HangFireTest/HangFireModel.edmx @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Software/Experiments/HangFireTest/HangFireTest/HangFireModel.edmx.diagram b/Software/Experiments/HangFireTest/HangFireTest/HangFireModel.edmx.diagram new file mode 100644 index 000000000..90c4a7307 --- /dev/null +++ b/Software/Experiments/HangFireTest/HangFireTest/HangFireModel.edmx.diagram @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Software/Experiments/HangFireTest/HangFireTest/HangFireModel.tt b/Software/Experiments/HangFireTest/HangFireTest/HangFireModel.tt new file mode 100644 index 000000000..8d258341d --- /dev/null +++ b/Software/Experiments/HangFireTest/HangFireTest/HangFireModel.tt @@ -0,0 +1,733 @@ +<#@ template language="C#" debug="false" hostspecific="true"#> +<#@ include file="EF6.Utility.CS.ttinclude"#><#@ + output extension=".cs"#><# + +const string inputFile = @"HangFireModel.edmx"; +var textTransform = DynamicTextTransformation.Create(this); +var code = new CodeGenerationTools(this); +var ef = new MetadataTools(this); +var typeMapper = new TypeMapper(code, ef, textTransform.Errors); +var fileManager = EntityFrameworkTemplateFileManager.Create(this); +var itemCollection = new EdmMetadataLoader(textTransform.Host, textTransform.Errors).CreateEdmItemCollection(inputFile); +var codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef); + +if (!typeMapper.VerifyCaseInsensitiveTypeUniqueness(typeMapper.GetAllGlobalItems(itemCollection), inputFile)) +{ + return string.Empty; +} + +WriteHeader(codeStringGenerator, fileManager); + +foreach (var entity in typeMapper.GetItemsToGenerate(itemCollection)) +{ + fileManager.StartNewFile(entity.Name + ".cs"); + BeginNamespace(code); +#> +<#=codeStringGenerator.UsingDirectives(inHeader: false)#> +<#=codeStringGenerator.EntityClassOpening(entity)#> +{ +<# + var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(entity); + var collectionNavigationProperties = typeMapper.GetCollectionNavigationProperties(entity); + var complexProperties = typeMapper.GetComplexProperties(entity); + + if (propertiesWithDefaultValues.Any() || collectionNavigationProperties.Any() || complexProperties.Any()) + { +#> + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + public <#=code.Escape(entity)#>() + { +<# + foreach (var edmProperty in propertiesWithDefaultValues) + { +#> + this.<#=code.Escape(edmProperty)#> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>; +<# + } + + foreach (var navigationProperty in collectionNavigationProperties) + { +#> + this.<#=code.Escape(navigationProperty)#> = new HashSet<<#=typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType())#>>(); +<# + } + + foreach (var complexProperty in complexProperties) + { +#> + this.<#=code.Escape(complexProperty)#> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>(); +<# + } +#> + } + +<# + } + + var simpleProperties = typeMapper.GetSimpleProperties(entity); + if (simpleProperties.Any()) + { + foreach (var edmProperty in simpleProperties) + { +#> + <#=codeStringGenerator.Property(edmProperty)#> +<# + } + } + + if (complexProperties.Any()) + { +#> + +<# + foreach(var complexProperty in complexProperties) + { +#> + <#=codeStringGenerator.Property(complexProperty)#> +<# + } + } + + var navigationProperties = typeMapper.GetNavigationProperties(entity); + if (navigationProperties.Any()) + { +#> + +<# + foreach (var navigationProperty in navigationProperties) + { + if (navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many) + { +#> + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] +<# + } +#> + <#=codeStringGenerator.NavigationProperty(navigationProperty)#> +<# + } + } +#> +} +<# + EndNamespace(code); +} + +foreach (var complex in typeMapper.GetItemsToGenerate(itemCollection)) +{ + fileManager.StartNewFile(complex.Name + ".cs"); + BeginNamespace(code); +#> +<#=codeStringGenerator.UsingDirectives(inHeader: false, includeCollections: false)#> +<#=Accessibility.ForType(complex)#> partial class <#=code.Escape(complex)#> +{ +<# + var complexProperties = typeMapper.GetComplexProperties(complex); + var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(complex); + + if (propertiesWithDefaultValues.Any() || complexProperties.Any()) + { +#> + public <#=code.Escape(complex)#>() + { +<# + foreach (var edmProperty in propertiesWithDefaultValues) + { +#> + this.<#=code.Escape(edmProperty)#> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>; +<# + } + + foreach (var complexProperty in complexProperties) + { +#> + this.<#=code.Escape(complexProperty)#> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>(); +<# + } +#> + } + +<# + } + + var simpleProperties = typeMapper.GetSimpleProperties(complex); + if (simpleProperties.Any()) + { + foreach(var edmProperty in simpleProperties) + { +#> + <#=codeStringGenerator.Property(edmProperty)#> +<# + } + } + + if (complexProperties.Any()) + { +#> + +<# + foreach(var edmProperty in complexProperties) + { +#> + <#=codeStringGenerator.Property(edmProperty)#> +<# + } + } +#> +} +<# + EndNamespace(code); +} + +foreach (var enumType in typeMapper.GetEnumItemsToGenerate(itemCollection)) +{ + fileManager.StartNewFile(enumType.Name + ".cs"); + BeginNamespace(code); +#> +<#=codeStringGenerator.UsingDirectives(inHeader: false, includeCollections: false)#> +<# + if (typeMapper.EnumIsFlags(enumType)) + { +#> +[Flags] +<# + } +#> +<#=codeStringGenerator.EnumOpening(enumType)#> +{ +<# + var foundOne = false; + + foreach (MetadataItem member in typeMapper.GetEnumMembers(enumType)) + { + foundOne = true; +#> + <#=code.Escape(typeMapper.GetEnumMemberName(member))#> = <#=typeMapper.GetEnumMemberValue(member)#>, +<# + } + + if (foundOne) + { + this.GenerationEnvironment.Remove(this.GenerationEnvironment.Length - 3, 1); + } +#> +} +<# + EndNamespace(code); +} + +fileManager.Process(); + +#> +<#+ + +public void WriteHeader(CodeStringGenerator codeStringGenerator, EntityFrameworkTemplateFileManager fileManager) +{ + fileManager.StartHeader(); +#> +//------------------------------------------------------------------------------ +// +// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine1")#> +// +// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine2")#> +// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine3")#> +// +//------------------------------------------------------------------------------ +<#=codeStringGenerator.UsingDirectives(inHeader: true)#> +<#+ + fileManager.EndBlock(); +} + +public void BeginNamespace(CodeGenerationTools code) +{ + var codeNamespace = code.VsNamespaceSuggestion(); + if (!String.IsNullOrEmpty(codeNamespace)) + { +#> +namespace <#=code.EscapeNamespace(codeNamespace)#> +{ +<#+ + PushIndent(" "); + } +} + +public void EndNamespace(CodeGenerationTools code) +{ + if (!String.IsNullOrEmpty(code.VsNamespaceSuggestion())) + { + PopIndent(); +#> +} +<#+ + } +} + +public const string TemplateId = "CSharp_DbContext_Types_EF6"; + +public class CodeStringGenerator +{ + private readonly CodeGenerationTools _code; + private readonly TypeMapper _typeMapper; + private readonly MetadataTools _ef; + + public CodeStringGenerator(CodeGenerationTools code, TypeMapper typeMapper, MetadataTools ef) + { + ArgumentNotNull(code, "code"); + ArgumentNotNull(typeMapper, "typeMapper"); + ArgumentNotNull(ef, "ef"); + + _code = code; + _typeMapper = typeMapper; + _ef = ef; + } + + public string Property(EdmProperty edmProperty) + { + return string.Format( + CultureInfo.InvariantCulture, + "{0} {1} {2} {{ {3}get; {4}set; }}", + Accessibility.ForProperty(edmProperty), + _typeMapper.GetTypeName(edmProperty.TypeUsage), + _code.Escape(edmProperty), + _code.SpaceAfter(Accessibility.ForGetter(edmProperty)), + _code.SpaceAfter(Accessibility.ForSetter(edmProperty))); + } + + public string NavigationProperty(NavigationProperty navProp) + { + var endType = _typeMapper.GetTypeName(navProp.ToEndMember.GetEntityType()); + return string.Format( + CultureInfo.InvariantCulture, + "{0} {1} {2} {{ {3}get; {4}set; }}", + AccessibilityAndVirtual(Accessibility.ForNavigationProperty(navProp)), + navProp.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType, + _code.Escape(navProp), + _code.SpaceAfter(Accessibility.ForGetter(navProp)), + _code.SpaceAfter(Accessibility.ForSetter(navProp))); + } + + public string AccessibilityAndVirtual(string accessibility) + { + return accessibility + (accessibility != "private" ? " virtual" : ""); + } + + public string EntityClassOpening(EntityType entity) + { + return string.Format( + CultureInfo.InvariantCulture, + "{0} {1}partial class {2}{3}", + Accessibility.ForType(entity), + _code.SpaceAfter(_code.AbstractOption(entity)), + _code.Escape(entity), + _code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType))); + } + + public string EnumOpening(SimpleType enumType) + { + return string.Format( + CultureInfo.InvariantCulture, + "{0} enum {1} : {2}", + Accessibility.ForType(enumType), + _code.Escape(enumType), + _code.Escape(_typeMapper.UnderlyingClrType(enumType))); + } + + public void WriteFunctionParameters(EdmFunction edmFunction, Action writeParameter) + { + var parameters = FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef); + foreach (var parameter in parameters.Where(p => p.NeedsLocalVariable)) + { + var isNotNull = parameter.IsNullableOfT ? parameter.FunctionParameterName + ".HasValue" : parameter.FunctionParameterName + " != null"; + var notNullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", " + parameter.FunctionParameterName + ")"; + var nullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", typeof(" + TypeMapper.FixNamespaces(parameter.RawClrTypeName) + "))"; + writeParameter(parameter.LocalVariableName, isNotNull, notNullInit, nullInit); + } + } + + public string ComposableFunctionMethod(EdmFunction edmFunction, string modelNamespace) + { + var parameters = _typeMapper.GetParameters(edmFunction); + + return string.Format( + CultureInfo.InvariantCulture, + "{0} IQueryable<{1}> {2}({3})", + AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)), + _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace), + _code.Escape(edmFunction), + string.Join(", ", parameters.Select(p => TypeMapper.FixNamespaces(p.FunctionParameterType) + " " + p.FunctionParameterName).ToArray())); + } + + public string ComposableCreateQuery(EdmFunction edmFunction, string modelNamespace) + { + var parameters = _typeMapper.GetParameters(edmFunction); + + return string.Format( + CultureInfo.InvariantCulture, + "return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<{0}>(\"[{1}].[{2}]({3})\"{4});", + _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace), + edmFunction.NamespaceName, + edmFunction.Name, + string.Join(", ", parameters.Select(p => "@" + p.EsqlParameterName).ToArray()), + _code.StringBefore(", ", string.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray()))); + } + + public string FunctionMethod(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption) + { + var parameters = _typeMapper.GetParameters(edmFunction); + var returnType = _typeMapper.GetReturnType(edmFunction); + + var paramList = String.Join(", ", parameters.Select(p => TypeMapper.FixNamespaces(p.FunctionParameterType) + " " + p.FunctionParameterName).ToArray()); + if (includeMergeOption) + { + paramList = _code.StringAfter(paramList, ", ") + "MergeOption mergeOption"; + } + + return string.Format( + CultureInfo.InvariantCulture, + "{0} {1} {2}({3})", + AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)), + returnType == null ? "int" : "ObjectResult<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">", + _code.Escape(edmFunction), + paramList); + } + + public string ExecuteFunction(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption) + { + var parameters = _typeMapper.GetParameters(edmFunction); + var returnType = _typeMapper.GetReturnType(edmFunction); + + var callParams = _code.StringBefore(", ", String.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray())); + if (includeMergeOption) + { + callParams = ", mergeOption" + callParams; + } + + return string.Format( + CultureInfo.InvariantCulture, + "return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction{0}(\"{1}\"{2});", + returnType == null ? "" : "<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">", + edmFunction.Name, + callParams); + } + + public string DbSet(EntitySet entitySet) + { + return string.Format( + CultureInfo.InvariantCulture, + "{0} virtual DbSet<{1}> {2} {{ get; set; }}", + Accessibility.ForReadOnlyProperty(entitySet), + _typeMapper.GetTypeName(entitySet.ElementType), + _code.Escape(entitySet)); + } + + public string UsingDirectives(bool inHeader, bool includeCollections = true) + { + return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion()) + ? string.Format( + CultureInfo.InvariantCulture, + "{0}using System;{1}" + + "{2}", + inHeader ? Environment.NewLine : "", + includeCollections ? (Environment.NewLine + "using System.Collections.Generic;") : "", + inHeader ? "" : Environment.NewLine) + : ""; + } +} + +public class TypeMapper +{ + private const string ExternalTypeNameAttributeName = @"http://schemas.microsoft.com/ado/2006/04/codegeneration:ExternalTypeName"; + + private readonly System.Collections.IList _errors; + private readonly CodeGenerationTools _code; + private readonly MetadataTools _ef; + + public TypeMapper(CodeGenerationTools code, MetadataTools ef, System.Collections.IList errors) + { + ArgumentNotNull(code, "code"); + ArgumentNotNull(ef, "ef"); + ArgumentNotNull(errors, "errors"); + + _code = code; + _ef = ef; + _errors = errors; + } + + public static string FixNamespaces(string typeName) + { + return typeName.Replace("System.Data.Spatial.", "System.Data.Entity.Spatial."); + } + + public string GetTypeName(TypeUsage typeUsage) + { + return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace: null); + } + + public string GetTypeName(EdmType edmType) + { + return GetTypeName(edmType, isNullable: null, modelNamespace: null); + } + + public string GetTypeName(TypeUsage typeUsage, string modelNamespace) + { + return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace); + } + + public string GetTypeName(EdmType edmType, string modelNamespace) + { + return GetTypeName(edmType, isNullable: null, modelNamespace: modelNamespace); + } + + public string GetTypeName(EdmType edmType, bool? isNullable, string modelNamespace) + { + if (edmType == null) + { + return null; + } + + var collectionType = edmType as CollectionType; + if (collectionType != null) + { + return String.Format(CultureInfo.InvariantCulture, "ICollection<{0}>", GetTypeName(collectionType.TypeUsage, modelNamespace)); + } + + var typeName = _code.Escape(edmType.MetadataProperties + .Where(p => p.Name == ExternalTypeNameAttributeName) + .Select(p => (string)p.Value) + .FirstOrDefault()) + ?? (modelNamespace != null && edmType.NamespaceName != modelNamespace ? + _code.CreateFullName(_code.EscapeNamespace(edmType.NamespaceName), _code.Escape(edmType)) : + _code.Escape(edmType)); + + if (edmType is StructuralType) + { + return typeName; + } + + if (edmType is SimpleType) + { + var clrType = UnderlyingClrType(edmType); + if (!IsEnumType(edmType)) + { + typeName = _code.Escape(clrType); + } + + typeName = FixNamespaces(typeName); + + return clrType.IsValueType && isNullable == true ? + String.Format(CultureInfo.InvariantCulture, "Nullable<{0}>", typeName) : + typeName; + } + + throw new ArgumentException("edmType"); + } + + public Type UnderlyingClrType(EdmType edmType) + { + ArgumentNotNull(edmType, "edmType"); + + var primitiveType = edmType as PrimitiveType; + if (primitiveType != null) + { + return primitiveType.ClrEquivalentType; + } + + if (IsEnumType(edmType)) + { + return GetEnumUnderlyingType(edmType).ClrEquivalentType; + } + + return typeof(object); + } + + public object GetEnumMemberValue(MetadataItem enumMember) + { + ArgumentNotNull(enumMember, "enumMember"); + + var valueProperty = enumMember.GetType().GetProperty("Value"); + return valueProperty == null ? null : valueProperty.GetValue(enumMember, null); + } + + public string GetEnumMemberName(MetadataItem enumMember) + { + ArgumentNotNull(enumMember, "enumMember"); + + var nameProperty = enumMember.GetType().GetProperty("Name"); + return nameProperty == null ? null : (string)nameProperty.GetValue(enumMember, null); + } + + public System.Collections.IEnumerable GetEnumMembers(EdmType enumType) + { + ArgumentNotNull(enumType, "enumType"); + + var membersProperty = enumType.GetType().GetProperty("Members"); + return membersProperty != null + ? (System.Collections.IEnumerable)membersProperty.GetValue(enumType, null) + : Enumerable.Empty(); + } + + public bool EnumIsFlags(EdmType enumType) + { + ArgumentNotNull(enumType, "enumType"); + + var isFlagsProperty = enumType.GetType().GetProperty("IsFlags"); + return isFlagsProperty != null && (bool)isFlagsProperty.GetValue(enumType, null); + } + + public bool IsEnumType(GlobalItem edmType) + { + ArgumentNotNull(edmType, "edmType"); + + return edmType.GetType().Name == "EnumType"; + } + + public PrimitiveType GetEnumUnderlyingType(EdmType enumType) + { + ArgumentNotNull(enumType, "enumType"); + + return (PrimitiveType)enumType.GetType().GetProperty("UnderlyingType").GetValue(enumType, null); + } + + public string CreateLiteral(object value) + { + if (value == null || value.GetType() != typeof(TimeSpan)) + { + return _code.CreateLiteral(value); + } + + return string.Format(CultureInfo.InvariantCulture, "new TimeSpan({0})", ((TimeSpan)value).Ticks); + } + + public bool VerifyCaseInsensitiveTypeUniqueness(IEnumerable types, string sourceFile) + { + ArgumentNotNull(types, "types"); + ArgumentNotNull(sourceFile, "sourceFile"); + + var hash = new HashSet(StringComparer.InvariantCultureIgnoreCase); + if (types.Any(item => !hash.Add(item))) + { + _errors.Add( + new CompilerError(sourceFile, -1, -1, "6023", + String.Format(CultureInfo.CurrentCulture, CodeGenerationTools.GetResourceString("Template_CaseInsensitiveTypeConflict")))); + return false; + } + return true; + } + + public IEnumerable GetEnumItemsToGenerate(IEnumerable itemCollection) + { + return GetItemsToGenerate(itemCollection) + .Where(e => IsEnumType(e)); + } + + public IEnumerable GetItemsToGenerate(IEnumerable itemCollection) where T: EdmType + { + return itemCollection + .OfType() + .Where(i => !i.MetadataProperties.Any(p => p.Name == ExternalTypeNameAttributeName)) + .OrderBy(i => i.Name); + } + + public IEnumerable GetAllGlobalItems(IEnumerable itemCollection) + { + return itemCollection + .Where(i => i is EntityType || i is ComplexType || i is EntityContainer || IsEnumType(i)) + .Select(g => GetGlobalItemName(g)); + } + + public string GetGlobalItemName(GlobalItem item) + { + if (item is EdmType) + { + return ((EdmType)item).Name; + } + else + { + return ((EntityContainer)item).Name; + } + } + + public IEnumerable GetSimpleProperties(EntityType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); + } + + public IEnumerable GetSimpleProperties(ComplexType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); + } + + public IEnumerable GetComplexProperties(EntityType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); + } + + public IEnumerable GetComplexProperties(ComplexType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); + } + + public IEnumerable GetPropertiesWithDefaultValues(EntityType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null); + } + + public IEnumerable GetPropertiesWithDefaultValues(ComplexType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null); + } + + public IEnumerable GetNavigationProperties(EntityType type) + { + return type.NavigationProperties.Where(np => np.DeclaringType == type); + } + + public IEnumerable GetCollectionNavigationProperties(EntityType type) + { + return type.NavigationProperties.Where(np => np.DeclaringType == type && np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many); + } + + public FunctionParameter GetReturnParameter(EdmFunction edmFunction) + { + ArgumentNotNull(edmFunction, "edmFunction"); + + var returnParamsProperty = edmFunction.GetType().GetProperty("ReturnParameters"); + return returnParamsProperty == null + ? edmFunction.ReturnParameter + : ((IEnumerable)returnParamsProperty.GetValue(edmFunction, null)).FirstOrDefault(); + } + + public bool IsComposable(EdmFunction edmFunction) + { + ArgumentNotNull(edmFunction, "edmFunction"); + + var isComposableProperty = edmFunction.GetType().GetProperty("IsComposableAttribute"); + return isComposableProperty != null && (bool)isComposableProperty.GetValue(edmFunction, null); + } + + public IEnumerable GetParameters(EdmFunction edmFunction) + { + return FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef); + } + + public TypeUsage GetReturnType(EdmFunction edmFunction) + { + var returnParam = GetReturnParameter(edmFunction); + return returnParam == null ? null : _ef.GetElementType(returnParam.TypeUsage); + } + + public bool GenerateMergeOptionFunction(EdmFunction edmFunction, bool includeMergeOption) + { + var returnType = GetReturnType(edmFunction); + return !includeMergeOption && returnType != null && returnType.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType; + } +} + +public static void ArgumentNotNull(T arg, string name) where T : class +{ + if (arg == null) + { + throw new ArgumentNullException(name); + } +} +#> \ No newline at end of file diff --git a/Software/Experiments/HangFireTest/HangFireTest/HangFireTest.csproj b/Software/Experiments/HangFireTest/HangFireTest/HangFireTest.csproj new file mode 100644 index 000000000..6a80ed325 --- /dev/null +++ b/Software/Experiments/HangFireTest/HangFireTest/HangFireTest.csproj @@ -0,0 +1,116 @@ + + + + + Debug + AnyCPU + {0A3D27ED-178E-40B7-BB6C-94BD552D4971} + Exe + HangFireTest + HangFireTest + v4.6.1 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll + + + ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll + + + ..\packages\Hangfire.Core.1.7.9\lib\net46\Hangfire.Core.dll + + + ..\packages\Hangfire.SqlServer.1.7.9\lib\net45\Hangfire.SqlServer.dll + + + ..\packages\Newtonsoft.Json.5.0.1\lib\net45\Newtonsoft.Json.dll + + + ..\packages\Owin.1.0\lib\net40\Owin.dll + + + + + + + + + + + + + + + + + True + True + HangFireModel.Context.tt + + + True + True + HangFireModel.tt + + + True + True + HangFireModel.edmx + + + HangFireModel.tt + + + + + + + + EntityModelCodeGenerator + HangFireModel.Designer.cs + + + HangFireModel.edmx + + + + + + TextTemplatingFileGenerator + HangFireModel.Context.cs + HangFireModel.edmx + + + TextTemplatingFileGenerator + HangFireModel.edmx + HangFireModel.cs + + + + + + + \ No newline at end of file diff --git a/Software/Experiments/HangFireTest/HangFireTest/Job.cs b/Software/Experiments/HangFireTest/HangFireTest/Job.cs new file mode 100644 index 000000000..61ce278b7 --- /dev/null +++ b/Software/Experiments/HangFireTest/HangFireTest/Job.cs @@ -0,0 +1,25 @@ +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace HangFireTest +{ + using System; + using System.Collections.Generic; + + public partial class Job + { + public long Id { get; set; } + public Nullable StateId { get; set; } + public string StateName { get; set; } + public string InvocationData { get; set; } + public string Arguments { get; set; } + public System.DateTime CreatedAt { get; set; } + public Nullable ExpireAt { get; set; } + } +} diff --git a/Software/Experiments/HangFireTest/HangFireTest/Program.cs b/Software/Experiments/HangFireTest/HangFireTest/Program.cs new file mode 100644 index 000000000..0a6d8ffcb --- /dev/null +++ b/Software/Experiments/HangFireTest/HangFireTest/Program.cs @@ -0,0 +1,107 @@ +using Hangfire; +using Hangfire.Storage; +using Hangfire.Storage.Monitoring; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Data.Entity; + +namespace HangFireTest +{ + class Program + { + private static BackgroundJobServer _server; + private static Random rnd; + + static void Main(string[] args) + { + rnd = new Random(); + + //Configure the storage of jobs. (requires database to be existing and empty) + GlobalConfiguration.Configuration.UseSqlServerStorage(@"server=localhost\SQLEXPRESS; Database=HangFire; Integrated Security=SSPI;", new Hangfire.SqlServer.SqlServerStorageOptions() + { + CommandBatchMaxTimeout = TimeSpan.FromMinutes(5), + SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5), + QueuePollInterval = TimeSpan.FromSeconds(1), + UseRecommendedIsolationLevel = true, + UsePageLocksOnDequeue = true, + DisableGlobalLocks = true, + JobExpirationCheckInterval = TimeSpan.FromMinutes(60), //Default + }); + + //Global filter + //GlobalConfiguration.Configuration.UseFilter(new AutomaticRetryAttribute() { Attempts = int.MaxValue }); + + //Custom logging of job exceptions. + //GlobalConfiguration.Configuration.UseLogProvider(new CustomLogProvider()); + + String jobId = null; + + //A trick to get the last job id before the application was closed. + using (HangFireEntities db = new HangFireEntities()) + { + var job = db.Job.Where(x => (x.StateName == "Awaiting" || x.StateName == "Scheduled" || x.StateName == "Enqueued") && x.Arguments.Contains("myqueue")).OrderByDescending(x => x.Id).FirstOrDefault(); + + if (job != null) + { + jobId = job.Id.ToString(); + } + } + + //Start the server + _server = new BackgroundJobServer(new BackgroundJobServerOptions() + { + WorkerCount = 1, //To prevent parallel job execution. + Queues = new string[] { "myqueue" } + }); + + //Set the timout to delete successful jobs from db. + JobStorage.Current.JobExpirationTimeout = TimeSpan.FromMinutes(60); + + Console.WriteLine("Started..."); + Console.ReadLine(); + + //Insert first node in tree or insert a new child node. + for (int i = 0; i < 10; i++) + { + if (jobId == null) + { + jobId = BackgroundJob.Enqueue(() => WriteLine1($"I'm a job {i}", "myqueue")); + } + else + { + jobId = BackgroundJob.ContinueJobWith(jobId, () => WriteLine2($"I'm a job {i}", "myqueue"), JobContinuationOptions.OnlyOnSucceededState); + } + } + + Console.ReadLine(); + } + + [Queue("myqueue")] + [AutomaticRetry(Attempts = 0)] + public static void WriteLine1(String text, String queue) + { + Console.WriteLine(text); + } + + [Queue("myqueue")] + [AutomaticRetry(Attempts = int.MaxValue, DelaysInSeconds = new int[] { 2 })] + public static void WriteLine2(String text, String queue) + { + + Console.Write(text + ", "); + + int i = rnd.Next(0, 100); + + if (i < 40) + { + Console.Write("Failed"); + throw new InvalidOperationException(); + } + + Console.WriteLine(", Completed"); + } + } +} diff --git a/Software/Experiments/HangFireTest/HangFireTest/Properties/AssemblyInfo.cs b/Software/Experiments/HangFireTest/HangFireTest/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..de64dbdea --- /dev/null +++ b/Software/Experiments/HangFireTest/HangFireTest/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("HangFireTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("HangFireTest")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("0a3d27ed-178e-40b7-bb6c-94bd552d4971")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Software/Experiments/HangFireTest/HangFireTest/packages.config b/Software/Experiments/HangFireTest/HangFireTest/packages.config new file mode 100644 index 000000000..b76cd5325 --- /dev/null +++ b/Software/Experiments/HangFireTest/HangFireTest/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/EventLogging/DefaultEventLogger.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/EventLogging/DefaultEventLogger.cs index 9992d4e2f..b92c9f285 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/EventLogging/DefaultEventLogger.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/EventLogging/DefaultEventLogger.cs @@ -18,6 +18,7 @@ using Tango.MachineStudio.Common.StudioApplication; using Tango.PMR.Diagnostics; using Tango.Integration.Operation; using Tango.Core.ExtensionMethods; +using Tango.Transport; namespace Tango.MachineStudio.Common.EventLogging { diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/EventLogging/DefaultEventLogger.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/EventLogging/DefaultEventLogger.cs index f9674e409..0afd2e31d 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/EventLogging/DefaultEventLogger.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/EventLogging/DefaultEventLogger.cs @@ -16,6 +16,7 @@ using Tango.Integration.Operation; using Tango.PPC.Common.Application; using Tango.PPC.Common.Authentication; using Tango.PPC.Common.Connection; +using Tango.Transport; namespace Tango.PPC.Common.EventLogging { diff --git a/Software/Visual_Studio/Tango.Emulations/Emulators/MachineEmulator.cs b/Software/Visual_Studio/Tango.Emulations/Emulators/MachineEmulator.cs index 5e6fdbc63..b901bd224 100644 --- a/Software/Visual_Studio/Tango.Emulations/Emulators/MachineEmulator.cs +++ b/Software/Visual_Studio/Tango.Emulations/Emulators/MachineEmulator.cs @@ -787,6 +787,11 @@ namespace Tango.Emulations.Emulators lastProgress = progress; Thread.Sleep(100); + + if (_cancelJob) + { + break; + } } if (_cancelJob) @@ -795,7 +800,7 @@ namespace Tango.Emulations.Emulators } } - foreach( var packLevel in MachineStatus.IDSPacksLevels) + foreach (var packLevel in MachineStatus.IDSPacksLevels) { Debug.WriteLine($"packLevel.DispenserLevel = {packLevel.DispenserLevel}"); Debug.WriteLine($"quantity = {130000000 - packLevel.DispenserLevel}"); @@ -810,14 +815,14 @@ namespace Tango.Emulations.Emulators Transporter.SendResponse(new JobResponse() { - }, request.Container.Token, false, ErrorCode.ContinuousResponseAborted); + }, request.Container.Token, new TransportResponseConfig() { ErrorCode = ErrorCode.ContinuousResponseAborted }); } else { Transporter.SendResponse(new ResumeCurrentJobResponse() { - }, _current_job_resume_token, false, ErrorCode.ContinuousResponseAborted); + }, _current_job_resume_token, new TransportResponseConfig() { ErrorCode = ErrorCode.ContinuousResponseAborted }); } } else @@ -831,7 +836,7 @@ namespace Tango.Emulations.Emulators Progress = unit_length + job.ProcessParameters.DryerBufferLength * ProcessParametersTable.DRYER_METERS_PER_CYCLE + ProcessParametersTable.DRYER_TO_SPOOL_LENGTH_METERS, } - }, request.Container.Token, !_cancelJob); + }, request.Container.Token, new TransportResponseConfig() { Completed = !_cancelJob }); } else { @@ -842,7 +847,7 @@ namespace Tango.Emulations.Emulators Progress = unit_length + job.ProcessParameters.DryerBufferLength * ProcessParametersTable.DRYER_METERS_PER_CYCLE + ProcessParametersTable.DRYER_TO_SPOOL_LENGTH_METERS, } - }, _current_job_resume_token, !_cancelJob); + }, _current_job_resume_token, new TransportResponseConfig() { Completed = !_cancelJob }); } } @@ -854,8 +859,8 @@ namespace Tango.Emulations.Emulators { //if (_jobAbortCounter == 1) //{ - _cancelJob = true; - Transporter.SendResponse(new AbortJobResponse(), request.Container.Token); + _cancelJob = true; + Transporter.SendResponse(new AbortJobResponse(), request.Container.Token); // _jobAbortCounter = 0; //} //else @@ -930,7 +935,7 @@ namespace Tango.Emulations.Emulators Thread.Sleep(30); } - Transporter.SendResponse(new MotorHomingResponse() { MaxProgress = 100, Progress = 100 }, request.Container.Token, true); + Transporter.SendResponse(new MotorHomingResponse() { MaxProgress = 100, Progress = 100 }, request.Container.Token, new TransportResponseConfig() { Completed = true }); _motorHomingRequestCodes.Remove(homeRequest.MotorType); ResetGraphFactors(); }); @@ -1001,7 +1006,7 @@ namespace Tango.Emulations.Emulators Thread.Sleep(30); } - Transporter.SendResponse(new DispenserHomingResponse() { MaxProgress = 100, Progress = 100 }, request.Container.Token, true); + Transporter.SendResponse(new DispenserHomingResponse() { MaxProgress = 100, Progress = 100 }, request.Container.Token, new TransportResponseConfig() { Completed = true }); _dispenserHomingRequestCodes.Remove(homeRequest.Index); ResetGraphFactors(); }); @@ -1024,7 +1029,7 @@ namespace Tango.Emulations.Emulators } else { - Transporter.SendResponse(new SetDigitalOutResponse(), request.Container.Token, null, ErrorCode.InvalidDigitalPinNumber); + Transporter.SendResponse(new SetDigitalOutResponse(), request.Container.Token, new TransportResponseConfig() { ErrorCode = ErrorCode.InvalidDigitalPinNumber }); } } @@ -1124,7 +1129,7 @@ namespace Tango.Emulations.Emulators DiagnosticsInterval = (uint)DIAGNOSTICS_INTERVAL, }, IsAfterReset = _isAfterReset, - }, request.Container.Token, null, request.Message.Password == "1234" ? ErrorCode.None : ErrorCode.UnauthorizedConnection); + }, request.Container.Token, new TransportResponseConfig() { ErrorCode = request.Message.Password == "1234" ? ErrorCode.None : ErrorCode.UnauthorizedConnection }); _connectionTime = DateTime.Now; @@ -1234,7 +1239,7 @@ namespace Tango.Emulations.Emulators } else { - Transporter.SendResponse(new ResumeCurrentJobResponse(), request.Container.Token, true, ErrorCode.NoJobInProgress); + Transporter.SendResponse(new ResumeCurrentJobResponse(), request.Container.Token, new TransportResponseConfig() { Completed = true, ErrorCode = ErrorCode.NoJobInProgress }); } } @@ -1300,7 +1305,7 @@ namespace Tango.Emulations.Emulators msg = ex.Message; } - Transporter.SendResponse(response, request.Container.Token, false, failed ? ErrorCode.GeneralError : ErrorCode.None, msg); + Transporter.SendResponse(response, request.Container.Token, new TransportResponseConfig() { ErrorCode = failed ? ErrorCode.GeneralError : ErrorCode.None, ErrorMessage = msg }); } private void HandleFileDownloadRequest(TangoMessage request) @@ -1311,7 +1316,7 @@ namespace Tango.Emulations.Emulators { DownloadID = Guid.NewGuid().ToString(), MaxChunkLength = Math.Min(MAX_CHUNK_LENGTH, length), - }, request.Container.Token, null, File.Exists(request.Message.FileName) ? ErrorCode.None : ErrorCode.FileNotFound); + }, request.Container.Token, new TransportResponseConfig() { ErrorCode = File.Exists(request.Message.FileName) ? ErrorCode.None : ErrorCode.FileNotFound }); } private void HandleFileChunkDownloadRequest(TangoMessage request) @@ -1336,7 +1341,7 @@ namespace Tango.Emulations.Emulators Transporter.SendResponse(new FileChunkDownloadResponse() { - }, request.Container.Token, null, ErrorCode.FileRequestDenied, ex.Message); + }, request.Container.Token, new TransportResponseConfig() { ErrorCode = ErrorCode.FileRequestDenied, ErrorMessage = ex.Message }); } } @@ -1349,7 +1354,7 @@ namespace Tango.Emulations.Emulators } catch (Exception ex) { - Transporter.SendResponse(new CreateResponse(), request.Container.Token, null, ErrorCode.FileRequestInvalidParameter, ex.Message); + Transporter.SendResponse(new CreateResponse(), request.Container.Token, new TransportResponseConfig() { ErrorCode = ErrorCode.FileRequestInvalidParameter, ErrorMessage = ex.Message }); } } @@ -1370,7 +1375,7 @@ namespace Tango.Emulations.Emulators } catch (Exception ex) { - Transporter.SendResponse(new DeleteResponse(), request.Container.Token, null, ErrorCode.FileRequestInvalidParameter, ex.Message); + Transporter.SendResponse(new DeleteResponse(), request.Container.Token, new TransportResponseConfig() { ErrorCode = ErrorCode.FileRequestInvalidParameter, ErrorMessage = ex.Message }); } } @@ -1403,7 +1408,7 @@ namespace Tango.Emulations.Emulators } catch (Exception ex) { - Transporter.SendResponse(new FileChunkUploadResponse(), request.Container.Token, null, ErrorCode.GeneralError, ex.Message); + Transporter.SendResponse(new FileChunkUploadResponse(), request.Container.Token, new TransportResponseConfig() { ErrorCode = ErrorCode.GeneralError, ErrorMessage = ex.Message }); } } @@ -1424,7 +1429,7 @@ namespace Tango.Emulations.Emulators } catch (Exception ex) { - Transporter.SendResponse(new ValidateVersionResponse(), request.Container.Token, null, ErrorCode.GeneralError, ex.Message); + Transporter.SendResponse(new ValidateVersionResponse(), request.Container.Token, new TransportResponseConfig() { ErrorCode = ErrorCode.GeneralError, ErrorMessage = ex.Message }); } }); } @@ -1446,7 +1451,7 @@ namespace Tango.Emulations.Emulators } catch (Exception ex) { - Transporter.SendResponse(new ActivateVersionResponse(), request.Container.Token, null, ErrorCode.GeneralError, ex.Message); + Transporter.SendResponse(new ActivateVersionResponse(), request.Container.Token, new TransportResponseConfig() { ErrorCode = ErrorCode.GeneralError, ErrorMessage = ex.Message }); } }); } @@ -1478,7 +1483,7 @@ namespace Tango.Emulations.Emulators ProgressPercentage = 100, Message = "Machine is turned off", State = PowerDownState.PowerOffCompleted - }, request.Container.Token, true); + }, request.Container.Token, new TransportResponseConfig() { Completed = true }); }); } @@ -1517,7 +1522,7 @@ namespace Tango.Emulations.Emulators } else { - await Transporter.SendResponse(new ContinueThreadLoadingResponse(), request.Container.Token, null, ErrorCode.GeneralError, "StartThreadLoadingRequest was never sent."); + await Transporter.SendResponse(new ContinueThreadLoadingResponse(), request.Container.Token, new TransportResponseConfig() { ErrorCode = ErrorCode.GeneralError, ErrorMessage = "StartThreadLoadingRequest was never sent." }); } } @@ -1531,7 +1536,7 @@ namespace Tango.Emulations.Emulators { Action = CartridgeAction.Inserted, - }, TimeSpan.FromSeconds(10)); + }, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(10) }); return response.Message.Index; } diff --git a/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeReceiver.cs b/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeReceiver.cs index fc00bd18e..2ca1b08f2 100644 --- a/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeReceiver.cs +++ b/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeReceiver.cs @@ -202,7 +202,7 @@ namespace Tango.Integration.ExternalBridge { if (IsLoggedIn) { - await SendRequest(new ExternalBridgeLogoutRequest(), TimeSpan.FromSeconds(3)); + await SendRequest(new ExternalBridgeLogoutRequest(), new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(3), ShouldLog = true }); } } catch (Exception ex) @@ -265,7 +265,7 @@ namespace Tango.Integration.ExternalBridge (reason) => { //Decline - SendResponse(new ExternalBridgeLoginResponse(), container.Token, false, ErrorCode.GeneralError, reason); + SendResponse(new ExternalBridgeLoginResponse(), container.Token, new TransportResponseConfig() { ErrorCode = ErrorCode.GeneralError, ErrorMessage = reason }); Disconnect().GetAwaiter().GetResult(); }); @@ -325,6 +325,10 @@ namespace Tango.Integration.ExternalBridge { SendResponse((ex as ResponseErrorException).Container); } + else if (ex is ContinuousResponseAbortedException) + { + SendResponse((ex as ContinuousResponseAbortedException).Container); + } } }); } @@ -372,7 +376,7 @@ namespace Tango.Integration.ExternalBridge if (_diagnosticsToken != null) { RequiresDiagnostics = false; - SendResponse(new StartDiagnosticsResponse(), _diagnosticsToken, true); + SendResponse(new StartDiagnosticsResponse(), _diagnosticsToken, new TransportResponseConfig() { Completed = true }); _diagnosticsToken = null; SendResponse(new StopDiagnosticsResponse(), container.Token); } @@ -390,7 +394,7 @@ namespace Tango.Integration.ExternalBridge if (_debugLogsToken != null) { RequiresDebugLogs = false; - SendResponse(new StartDebugLogResponse(), _debugLogsToken, true); + SendResponse(new StartDebugLogResponse(), _debugLogsToken, new TransportResponseConfig() { Completed = true }); _debugLogsToken = null; SendResponse(new StopDebugLogResponse(), container.Token); } @@ -408,7 +412,7 @@ namespace Tango.Integration.ExternalBridge if (_eventsToken != null) { RequiresEventsNotification = false; - SendResponse(new StartEventsNotificationResponse(), _eventsToken, true); + SendResponse(new StartEventsNotificationResponse(), _eventsToken, new TransportResponseConfig() { Completed = true }); _eventsToken = null; SendResponse(new StopEventsNotificationResponse(), container.Token); } @@ -426,7 +430,7 @@ namespace Tango.Integration.ExternalBridge if (_machineStatusToken != null) { RequiresMachineStatusUpdate = false; - SendResponse(new StartMachineStatusUpdateResponse(), _machineStatusToken, true); + SendResponse(new StartMachineStatusUpdateResponse(), _machineStatusToken, new TransportResponseConfig() { Completed = true }); _machineStatusToken = null; SendResponse(new StopMachineStatusUpdateResponse(), container.Token); } @@ -444,7 +448,7 @@ namespace Tango.Integration.ExternalBridge if (_applicationLogsToken != null) { RequiresApplicationLogs = false; - SendResponse(new StartApplicationLogsResponse(), _applicationLogsToken, true); + SendResponse(new StartApplicationLogsResponse(), _applicationLogsToken, new TransportResponseConfig() { Completed = true }); _applicationLogsToken = null; SendResponse(new StopApplicationLogsResponse(), container.Token); } @@ -468,7 +472,7 @@ namespace Tango.Integration.ExternalBridge protected virtual void OnStartPowerDownRequest(MessageContainer container) { - SendResponse(new StartPowerDownResponse() { }, container.Token, false, ErrorCode.ContinuousResponseAborted, "Power down request is not supported via external bridge."); + SendResponse(new StartPowerDownResponse() { }, container.Token, new TransportResponseConfig() { ErrorCode = ErrorCode.ContinuousResponseAborted, ErrorMessage = "Power down request is not supported via external bridge." }); } #endregion diff --git a/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeSignalRClient.cs b/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeSignalRClient.cs index e2aae9eb8..dc1bd1203 100644 --- a/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeSignalRClient.cs +++ b/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeSignalRClient.cs @@ -43,10 +43,7 @@ namespace Tango.Integration.ExternalBridge LogManager.Log("External Bridge SignalR Client Connected..."); - - LogRequestSent(login); - - var response = await SendRequest(login); + var response = await SendRequest(login, new TransportRequestConfig() { ShouldLog = true }); SessionLogger.CreateSession(); @@ -59,7 +56,6 @@ namespace Tango.Integration.ExternalBridge } catch (Exception ex) { - LogRequestFailed(login, ex); try { await Adapter.Disconnect(); diff --git a/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeTcpClient.cs b/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeTcpClient.cs index f050708d7..df6a4cc01 100644 --- a/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeTcpClient.cs +++ b/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeTcpClient.cs @@ -104,10 +104,7 @@ namespace Tango.Integration.ExternalBridge LogManager.Log("External Bridge TCP Client Connected..."); - - LogRequestSent(login); - - var response = await SendRequest(login); + var response = await SendRequest(login, new TransportRequestConfig() { ShouldLog = true }); SessionLogger.CreateSession(); @@ -120,7 +117,6 @@ namespace Tango.Integration.ExternalBridge } catch (Exception ex) { - LogRequestFailed(login, ex); try { await Adapter.Disconnect(); @@ -149,14 +145,13 @@ namespace Tango.Integration.ExternalBridge bool responseLogged = false; _logs_sent = true; - SendContinuousRequest(request).ObserveOn(new NewThreadScheduler()) + SendContinuousRequest(request, new TransportContinuousRequestConfig() { ShouldLog = true }).ObserveOn(new NewThreadScheduler()) .Subscribe ( (response) => { if (!responseLogged) { - LogResponseReceived(response.Message); responseLogged = true; } @@ -165,18 +160,11 @@ namespace Tango.Integration.ExternalBridge (ex) => { _logs_sent = false; - - if (!(ex is ContinuousResponseAbortedException)) - { - LogRequestFailed(request, ex); - } }, () => { _logs_sent = false; }); - - LogRequestSent(request); } else if (_logs_sent) { @@ -188,14 +176,9 @@ namespace Tango.Integration.ExternalBridge try { - LogRequestSent(req); - var res = await SendRequest(req); - LogResponseReceived(res.Message); - } - catch (Exception ex) - { - LogRequestFailed(req, ex); + var res = await SendRequest(req, new TransportRequestConfig() { ShouldLog = true }); } + catch { } } } } @@ -222,19 +205,14 @@ namespace Tango.Integration.ExternalBridge if (State == TransportComponentState.Connected) { ExternalBridgeLogoutRequest request = new ExternalBridgeLogoutRequest(); - LogRequestSent(request); try { - var response = await SendRequest(request); - LogResponseReceived(response.Message); + var response = await SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); Status = MachineStatuses.Standby; } - catch (Exception ex) - { - LogRequestFailed(request, ex); - } + catch { } } State = TransportComponentState.Disconnected; diff --git a/Software/Visual_Studio/Tango.Integration/Operation/IMachineOperator.cs b/Software/Visual_Studio/Tango.Integration/Operation/IMachineOperator.cs index 8adb61750..44128a2ae 100644 --- a/Software/Visual_Studio/Tango.Integration/Operation/IMachineOperator.cs +++ b/Software/Visual_Studio/Tango.Integration/Operation/IMachineOperator.cs @@ -154,26 +154,6 @@ namespace Tango.Integration.Operation ///
event EventHandler ResumingJob; - /// - /// Occurs when a request has been sent. - /// - event EventHandler RequestSent; - - /// - /// Occurs when a request response has been received. - /// - event EventHandler ResponseReceived; - - /// - /// Occurs when a response has been sent. - /// - event EventHandler ResponseSent; - - /// - /// Occurs when a request has failed. - /// - event EventHandler RequestFailed; - /// /// Occurs when there is new diagnostics data available. /// diff --git a/Software/Visual_Studio/Tango.Integration/Operation/MachineOperator.cs b/Software/Visual_Studio/Tango.Integration/Operation/MachineOperator.cs index cd320e023..5f8361256 100644 --- a/Software/Visual_Studio/Tango.Integration/Operation/MachineOperator.cs +++ b/Software/Visual_Studio/Tango.Integration/Operation/MachineOperator.cs @@ -173,26 +173,6 @@ namespace Tango.Integration.Operation ///
public event EventHandler CartridgeValidationRequestReceived; - /// - /// Occurs when a request has been sent. - /// - public event EventHandler RequestSent; - - /// - /// Occurs when a response has been sent. - /// - public event EventHandler ResponseSent; - - /// - /// Occurs when a request has timed out. - /// - public event EventHandler RequestFailed; - - /// - /// Occurs when a request response has been received. - /// - public event EventHandler ResponseReceived; - /// /// Reports about the job printing preparation progress. /// @@ -582,13 +562,12 @@ namespace Tango.Integration.Operation bool responseLogged = false; _diagnosticsSent = true; - SendContinuousRequest(request).ObserveOn(new NewThreadScheduler()).Subscribe( + SendContinuousRequest(request, new TransportContinuousRequestConfig() { ShouldLog = true }).ObserveOn(new NewThreadScheduler()).Subscribe( (response) => { if (!responseLogged) { _diagnosticsTime = DateTime.Now; - LogResponseReceived(response.Message); responseLogged = true; } else @@ -603,19 +582,12 @@ namespace Tango.Integration.Operation (ex) => { _diagnosticsSent = false; - - if (!(ex is ContinuousResponseAbortedException) && !(ex is TransporterDisconnectedException)) - { - LogRequestFailed(request, ex); - } }, () => { _diagnosticsSent = false; LogManager.Log("Diagnostics response completed!?", LogCategory.Warning); }); - - LogRequestSent(request); } else if (_diagnosticsSent) { @@ -627,14 +599,9 @@ namespace Tango.Integration.Operation try { - LogRequestSent(req); - var res = await SendRequest(req); - LogResponseReceived(res.Message); - } - catch (Exception ex) - { - LogRequestFailed(req, ex); + var res = await SendRequest(req, new TransportRequestConfig() { ShouldLog = true }); } + catch { } } } } @@ -652,33 +619,25 @@ namespace Tango.Integration.Operation bool responseLogged = false; _eventsSent = true; - SendContinuousRequest(request).ObserveOn(new NewThreadScheduler()).Subscribe( + SendContinuousRequest(request, new TransportContinuousRequestConfig() { ShouldLog = true }).ObserveOn(new NewThreadScheduler()).Subscribe( (response) => { OnEventsNotification(response); if (!responseLogged) { - LogResponseReceived(response.Message); responseLogged = true; } }, (ex) => { _eventsSent = false; - - if (!(ex is ContinuousResponseAbortedException) && !(ex is TransporterDisconnectedException)) - { - LogRequestFailed(request, ex); - } }, () => { _eventsSent = false; LogManager.Log("Events Notification response completed!?", LogCategory.Warning); }); - - LogRequestSent(request); } else if (_eventsSent) { @@ -690,14 +649,9 @@ namespace Tango.Integration.Operation try { - LogRequestSent(req); - var res = await SendRequest(req); - LogResponseReceived(res.Message); - } - catch (Exception ex) - { - LogRequestFailed(req, ex); + var res = await SendRequest(req, new TransportRequestConfig() { ShouldLog = true }); } + catch { } } } } @@ -715,14 +669,13 @@ namespace Tango.Integration.Operation bool responseLogged = false; _debugSent = true; - SendContinuousRequest(request).ObserveOn(new NewThreadScheduler()) + SendContinuousRequest(request, new TransportContinuousRequestConfig() { ShouldLog = true }).ObserveOn(new NewThreadScheduler()) .Subscribe ( (response) => { if (!responseLogged) { - LogResponseReceived(response.Message); responseLogged = true; } @@ -731,18 +684,11 @@ namespace Tango.Integration.Operation (ex) => { _debugSent = false; - - if (!(ex is ContinuousResponseAbortedException) && !(ex is TransporterDisconnectedException)) - { - LogRequestFailed(request, ex); - } }, () => { _debugSent = false; }); - - LogRequestSent(request); } else if (_debugSent) { @@ -754,14 +700,9 @@ namespace Tango.Integration.Operation try { - LogRequestSent(req); - var res = await SendRequest(req); - LogResponseReceived(res.Message); - } - catch (Exception ex) - { - LogRequestFailed(req, ex); + var res = await SendRequest(req, new TransportRequestConfig() { ShouldLog = true }); } + catch { } } } } @@ -779,33 +720,25 @@ namespace Tango.Integration.Operation bool responseLogged = false; _machineStatusSent = true; - SendContinuousRequest(request).ObserveOn(new NewThreadScheduler()).Subscribe( + SendContinuousRequest(request, new TransportContinuousRequestConfig() { ShouldLog = true }).ObserveOn(new NewThreadScheduler()).Subscribe( (response) => { OnMachineStatusChanged(response); if (!responseLogged) { - LogResponseReceived(response.Message); responseLogged = true; } }, (ex) => { _machineStatusSent = false; - - if (!(ex is ContinuousResponseAbortedException) && !(ex is TransporterDisconnectedException)) - { - LogRequestFailed(request, ex); - } }, () => { _machineStatusSent = false; LogManager.Log("Machine status update response completed!?", LogCategory.Warning); }); - - LogRequestSent(request); } else if (_machineStatusSent) { @@ -817,14 +750,9 @@ namespace Tango.Integration.Operation try { - LogRequestSent(req); - var res = await SendRequest(req); - LogResponseReceived(res.Message); - } - catch (Exception ex) - { - LogRequestFailed(req, ex); + var res = await SendRequest(req, new TransportRequestConfig() { ShouldLog = true }); } + catch { } } } } @@ -842,33 +770,25 @@ namespace Tango.Integration.Operation bool responseLogged = false; _threadLoadingSent = true; - SendContinuousRequest(request).ObserveOn(new NewThreadScheduler()).Subscribe( + SendContinuousRequest(request, new TransportContinuousRequestConfig() { ShouldLog = true }).ObserveOn(new NewThreadScheduler()).Subscribe( (response) => { OnThreadLoadingStatusChanged(response); if (!responseLogged) { - LogResponseReceived(response.Message); responseLogged = true; } }, (ex) => { _threadLoadingSent = false; - - if (!(ex is ContinuousResponseAbortedException) && !(ex is TransporterDisconnectedException)) - { - LogRequestFailed(request, ex); - } }, () => { _threadLoadingSent = false; LogManager.Log("Thread loading response completed!?", LogCategory.Warning); }); - - LogRequestSent(request); } else if (_threadLoadingSent) { @@ -880,14 +800,9 @@ namespace Tango.Integration.Operation try { - LogRequestSent(req); - var res = await SendRequest(req); - LogResponseReceived(res.Message); - } - catch (Exception ex) - { - LogRequestFailed(req, ex); + var res = await SendRequest(req, new TransportRequestConfig() { ShouldLog = true }); } + catch { } } } } @@ -1019,7 +934,7 @@ namespace Tango.Integration.Operation var r = SendRequest(new ContinueThreadLoadingRequest() { ProcessParameters = process, - }).Result; + }, new TransportRequestConfig() { ShouldLog = true }).Result; } catch (Exception ex) { @@ -1040,24 +955,6 @@ namespace Tango.Integration.Operation } } - /// - /// Called when the request has been sent - /// - /// The request. - protected virtual void OnRequestSent(IMessage request) - { - RequestSent?.Invoke(this, request); - } - - /// - /// Called when the response has been received - /// - /// The response. - protected virtual void OnResponseReceived(IMessage response) - { - ResponseReceived?.Invoke(this, response); - } - /// /// Called when a new request has been received. /// @@ -1076,24 +973,6 @@ namespace Tango.Integration.Operation } } - /// - /// Called when the response has been sent - /// - /// The response. - protected virtual void OnResponseSent(IMessage response) - { - ResponseSent?.Invoke(this, response); - } - - /// - /// Called when the request has been failed - /// - /// The request. - protected virtual void OnRequestFailed(IMessage request, Exception exception) - { - RequestFailed?.Invoke(this, new RequestFailedEventArgs(request, exception)); - } - /// /// Called when the machine status has been changed /// @@ -1279,19 +1158,14 @@ namespace Tango.Integration.Operation if (State == TransportComponentState.Connected) { DisconnectRequest request = new DisconnectRequest(); - LogRequestSent(request); try { - var response = await SendRequest(request); - LogResponseReceived(response.Message); + var response = await SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); Status = MachineStatuses.Disconnected; } - catch (Exception ex) - { - LogRequestFailed(request, ex); - } + catch { } } if (MachineEventsStateProvider != null) @@ -1323,16 +1197,13 @@ namespace Tango.Integration.Operation Password = "1234", UnixTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds() }; - LogRequestSent(request); try { - var response = await SendRequest(request); + var response = await SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); SessionLogger.CreateSession(); - LogResponseReceived(response.Message); - _isPowerDownRequestInProgress = false; if (Status != MachineStatuses.Upgrading) @@ -1365,7 +1236,6 @@ namespace Tango.Integration.Operation } catch (Exception ex) { - LogRequestFailed(request, ex); await base.Disconnect(); throw ex; } @@ -1386,7 +1256,7 @@ namespace Tango.Integration.Operation try { - var res = await SendRequest(new CurrentJobRequest()); + var res = await SendRequest(new CurrentJobRequest(), new TransportRequestConfig() { ShouldLog = true }); if (res.Message.IsJobInProgress) { @@ -1418,7 +1288,7 @@ namespace Tango.Integration.Operation try { handler.CanCancel = false; - var result = await SendRequest(new AbortJobRequest()); + var result = await SendRequest(new AbortJobRequest(), new TransportRequestConfig() { ShouldLog = true }); handler.IsCanceled = true; OnPrintingAborted(handler, originalJob); handler.RaiseCanceled(); @@ -1435,91 +1305,88 @@ namespace Tango.Integration.Operation RunningJobStatus = s; }; - LogRequestSent(request); bool responseLogged = false; Thread.Sleep(500); //Just wait maybe Shlomo is getting this message to fast after restart ? bool completed = false; - SendContinuousRequest(request, null, ContinuousRequestTimeout).Subscribe((response) => - { - if (!completed) - { - if (!responseLogged) - { - if (_last_job_status != null) - { - _last_job_status.IsCanceled = false; - _last_job_status.IsCompleted = false; - _last_job_status.IsFailed = false; - handler.Status = _last_job_status; - } - } - - handler.RaiseStatusReceived(response.Message.Status); - - if (!responseLogged) - { - Status = MachineStatuses.GettingReady; - responseLogged = true; - RunningJob = originalJob; - OnPrintingStarted(handler, originalJob); - LogResponseReceived(response.Message); - } - - if (JobHandlingMode == JobHandlerModes.SettingUp) - { - if (response.Message.Status.Progress > processParameters.DryerBufferLengthMeters) - { - if (!completed) - { - Status = MachineStatuses.Printing; - } - } - } - else - { - if (response.Message.Status.Progress > 0) - { - if (!completed) - { - Status = MachineStatuses.Printing; - } - } - } - } - }, (ex) => - { - if (!completed) - { - completed = true; - if (!(ex is ContinuousResponseAbortedException)) - { - Status = MachineStatuses.ReadyToDye; - - if (!handler.IsCanceled) - { - OnPrintingFailed(handler, originalJob, ex); - handler.RaiseFailed(ex); - LogRequestFailed(request, ex); - } - } - else - { - Status = MachineStatuses.ReadyToDye; - } - } - }, () => - { - if (!completed) - { - completed = true; - Status = MachineStatuses.ReadyToDye; - OnPrintingCompleted(handler, originalJob); - handler.RaiseCompleted(); - } - }); + SendContinuousRequest(request, new TransportContinuousRequestConfig() { ContinuousTimeout = ContinuousRequestTimeout, ShouldLog = true }).Subscribe((response) => + { + if (!completed) + { + if (!responseLogged) + { + if (_last_job_status != null) + { + _last_job_status.IsCanceled = false; + _last_job_status.IsCompleted = false; + _last_job_status.IsFailed = false; + handler.Status = _last_job_status; + } + } + + handler.RaiseStatusReceived(response.Message.Status); + + if (!responseLogged) + { + Status = MachineStatuses.GettingReady; + responseLogged = true; + RunningJob = originalJob; + OnPrintingStarted(handler, originalJob); + } + + if (JobHandlingMode == JobHandlerModes.SettingUp) + { + if (response.Message.Status.Progress > processParameters.DryerBufferLengthMeters) + { + if (!completed) + { + Status = MachineStatuses.Printing; + } + } + } + else + { + if (response.Message.Status.Progress > 0) + { + if (!completed) + { + Status = MachineStatuses.Printing; + } + } + } + } + }, (ex) => + { + if (!completed) + { + completed = true; + if (!(ex is ContinuousResponseAbortedException)) + { + Status = MachineStatuses.ReadyToDye; + + if (!handler.IsCanceled) + { + OnPrintingFailed(handler, originalJob, ex); + handler.RaiseFailed(ex); + } + } + else + { + Status = MachineStatuses.ReadyToDye; + } + } + }, () => + { + if (!completed) + { + completed = true; + Status = MachineStatuses.ReadyToDye; + OnPrintingCompleted(handler, originalJob); + handler.RaiseCompleted(); + } + }); return handler; }); @@ -1538,37 +1405,37 @@ namespace Tango.Integration.Operation /// Logs the request sent. ///
/// The message. - protected void LogRequestSent(IMessage message) - { - if (!(message is FileChunkUploadRequest) && !(message is FileDownloadRequest)) - { - LogManager.Log($"{GetExtendedComponentName()}: Sending request '{message.GetType().Name}'...\n{message.ToJsonString()}"); - OnRequestSent(message); - } - } + //protected void LogRequestSent(IMessage message) + //{ + // if (!(message is FileChunkUploadRequest) && !(message is FileDownloadRequest)) + // { + // //LogManager.Log($"{GetExtendedComponentName()}: Sending request '{message.GetType().Name}'...\n{message.ToJsonString()}"); + // OnRequestSent(message); + // } + //} /// /// Logs the request failed. /// /// The message. - protected void LogRequestFailed(IMessage message, Exception ex) - { - LogManager.Log($"{GetExtendedComponentName()}: Request failed '{message.GetType().Name}'...\n{message.ToJsonString()}\n{ex.ToString()}", LogCategory.Error); - OnRequestFailed(message, ex); - } + //protected void LogRequestFailed(IMessage message, Exception ex) + //{ + // //LogManager.Log($"{GetExtendedComponentName()}: Request failed '{message.GetType().Name}'...\n{message.ToJsonString()}\n{ex.ToString()}", LogCategory.Error); + // OnRequestFailed(message, ex); + //} /// /// Logs the response received. /// /// The message. - protected void LogResponseReceived(IMessage message) - { - if (!(message is FileChunkUploadResponse) && !(message is FileDownloadResponse)) - { - LogManager.Log($"{GetExtendedComponentName()}: Response received '{message.GetType().Name}'...\n{message.ToJsonString()}"); - OnResponseReceived(message); - } - } + //protected void LogResponseReceived(IMessage message) + //{ + // if (!(message is FileChunkUploadResponse) && !(message is FileDownloadResponse)) + // { + // //LogManager.Log($"{GetExtendedComponentName()}: Response received '{message.GetType().Name}'...\n{message.ToJsonString()}"); + // OnResponseReceived(message); + // } + //} /// /// Creates a PMR job segment. @@ -1668,12 +1535,11 @@ namespace Tango.Integration.Operation request.JobTicket = ticket; - LogRequestSent(request); bool responseLogged = false; var previous_segments_length = job.Segments.Where(x => x.SegmentIndex < segment.SegmentIndex).Sum(x => x.Length); - SendContinuousRequest(request, null, ContinuousRequestTimeout).Subscribe((response) => + SendContinuousRequest(request, new TransportContinuousRequestConfig() { ContinuousTimeout = ContinuousRequestTimeout, ShouldLog = true }).Subscribe((response) => { response.Message.Status.Progress += previous_segments_length; @@ -1685,7 +1551,6 @@ namespace Tango.Integration.Operation Status = MachineStatuses.Printing; RunningJob = handler.Job; OnPrintingStarted(handler, handler.Job); - LogResponseReceived(response.Message); } }, (ex) => @@ -1698,7 +1563,6 @@ namespace Tango.Integration.Operation { OnPrintingFailed(handler, handler.Job, ex); handler.RaiseFailed(ex); - LogRequestFailed(request, ex); } } else @@ -2413,12 +2277,13 @@ namespace Tango.Integration.Operation if (handler.CanCancel) { handler.CanCancel = false; - + LogManager.Log("Aborting current job..."); LogManager.Log($"Aborting current gradient generation..."); GradientGenerationConfiguration.AbortCurrentGeneration(); if (fileUploadHandler != null) { + LogManager.Log("Job is currently uploading. Aborting file upload..."); await fileUploadHandler.Cancel(); fileUploadHandler = null; LogManager.Log("Job upload canceled."); @@ -2433,7 +2298,7 @@ namespace Tango.Integration.Operation { if (requestSent) { - var result = await SendRequest(new AbortJobRequest()); + var result = await SendRequest(new AbortJobRequest(), new TransportRequestConfig() { ShouldLog = true }); handler.IsCanceled = true; } @@ -2646,7 +2511,6 @@ namespace Tango.Integration.Operation Status = MachineStatuses.ReadyToDye; OnPrintingFailed(handler, clonedJob, ex); handler.RaiseFailed(ex); - LogRequestFailed(request, ex); return; } } @@ -2664,106 +2528,103 @@ namespace Tango.Integration.Operation _machineStatusBeforeJobStart = MachineStatus.Clone(); - LogRequestSent(request); bool responseLogged = false; bool completed = false; //Use this in case Shlomo is sending progress after completion. _jobHeatingStartDate = DateTime.UtcNow; - SendContinuousRequest(request, null, ContinuousRequestTimeout.Add(TimeSpan.FromSeconds(3))).Subscribe((response) => - { - if (!completed) - { - handler.RaiseStatusReceived(response.Message.Status); - _last_job_status = handler.Status; - - if (response.Message.Status.Progress > 0) - { - if (oldKeepAlive != UseKeepAlive) - { - UseKeepAlive = oldKeepAlive; - } - - if (_jobActualStartDate == null) - { - _jobActualStartDate = DateTime.UtcNow; - } - } - - if (!responseLogged) - { - requestSent = true; - responseLogged = true; - LogResponseReceived(response.Message); - } - - if (JobHandlingMode == JobHandlerModes.SettingUp) - { - if (response.Message.Status.Progress > processParameters.DryerBufferLengthMeters) - { - if (!completed) - { - Status = MachineStatuses.Printing; - } - } - } - else - { - if (response.Message.Status.Progress > 0) - { - if (!completed) - { - Status = MachineStatuses.Printing; - } - } - } - } - - }, (ex) => - { - if (!completed) - { - completed = true; - - UseKeepAlive = oldKeepAlive; - - if (!(ex is ContinuousResponseAbortedException)) - { - if (Status != MachineStatuses.Disconnected) - { - Status = MachineStatuses.ReadyToDye; - } - - if (!handler.IsCanceled) - { - SaveLastJobLiquidQuantities(originalJob, originalJob.Machine.Configuration, processParameters, handler); - OnPrintingFailed(handler, originalJob, ex); - handler.RaiseFailed(ex); - LogRequestFailed(request, ex); - } - } - else - { - if (Status != MachineStatuses.Disconnected) - { - Status = MachineStatuses.ReadyToDye; - } - } - } - }, () => - { - if (!completed) - { - completed = true; - - UseKeepAlive = oldKeepAlive; - - Status = MachineStatuses.ReadyToDye; - SaveLastJobLiquidQuantities(clonedJob, originalJob.Machine.Configuration, processParameters, handler); - OnPrintingCompleted(handler, clonedJob); - handler.RaiseCompleted(); - } - }); + SendContinuousRequest(request, new TransportContinuousRequestConfig() { ContinuousTimeout = ContinuousRequestTimeout.Add(TimeSpan.FromSeconds(3)), ShouldLog = true }).Subscribe((response) => + { + if (!completed) + { + handler.RaiseStatusReceived(response.Message.Status); + _last_job_status = handler.Status; + + if (response.Message.Status.Progress > 0) + { + if (oldKeepAlive != UseKeepAlive) + { + UseKeepAlive = oldKeepAlive; + } + + if (_jobActualStartDate == null) + { + _jobActualStartDate = DateTime.UtcNow; + } + } + + if (!responseLogged) + { + requestSent = true; + responseLogged = true; + } + + if (JobHandlingMode == JobHandlerModes.SettingUp) + { + if (response.Message.Status.Progress > processParameters.DryerBufferLengthMeters) + { + if (!completed) + { + Status = MachineStatuses.Printing; + } + } + } + else + { + if (response.Message.Status.Progress > 0) + { + if (!completed) + { + Status = MachineStatuses.Printing; + } + } + } + } + + }, (ex) => + { + if (!completed) + { + completed = true; + + UseKeepAlive = oldKeepAlive; + + if (!(ex is ContinuousResponseAbortedException)) + { + if (Status != MachineStatuses.Disconnected) + { + Status = MachineStatuses.ReadyToDye; + } + + if (!handler.IsCanceled) + { + SaveLastJobLiquidQuantities(originalJob, originalJob.Machine.Configuration, processParameters, handler); + OnPrintingFailed(handler, originalJob, ex); + handler.RaiseFailed(ex); + } + } + else + { + if (Status != MachineStatuses.Disconnected) + { + Status = MachineStatuses.ReadyToDye; + } + } + } + }, () => + { + if (!completed) + { + completed = true; + + UseKeepAlive = oldKeepAlive; + + Status = MachineStatuses.ReadyToDye; + SaveLastJobLiquidQuantities(clonedJob, originalJob.Machine.Configuration, processParameters, handler); + OnPrintingCompleted(handler, clonedJob); + handler.RaiseCompleted(); + } + }); }); return handler; @@ -2935,7 +2796,7 @@ namespace Tango.Integration.Operation { try { - var result = await SendRequest(new StubAbortJobRequest()); + var result = await SendRequest(new StubAbortJobRequest(), new TransportRequestConfig() { ShouldLog = true }); OnPrintingAborted(handler, originalJob); handler.RaiseCanceled(); } @@ -2950,44 +2811,41 @@ namespace Tango.Integration.Operation RunningJobStatus = s; }; - LogRequestSent(request); bool responseLogged = false; - SendContinuousRequest(request, null, ContinuousRequestTimeout).Subscribe((response) => - { - handler.RaiseStatusReceived(response.Message.Status); - - if (!responseLogged) - { - responseLogged = true; - Status = MachineStatuses.Printing; - RunningJob = originalJob; - OnPrintingStarted(handler, originalJob); - LogResponseReceived(response.Message); - } - }, (ex) => - { - if (!(ex is ContinuousResponseAbortedException)) - { - Status = MachineStatuses.ReadyToDye; + SendContinuousRequest(request, new TransportContinuousRequestConfig() { ContinuousTimeout = ContinuousRequestTimeout, ShouldLog = true }).Subscribe((response) => + { + handler.RaiseStatusReceived(response.Message.Status); - if (!handler.IsCanceled) - { - OnPrintingFailed(handler, originalJob, ex); - handler.RaiseFailed(ex); - LogRequestFailed(request, ex); - } - } - else - { - Status = MachineStatuses.ReadyToDye; - } - }, () => - { - Status = MachineStatuses.ReadyToDye; - OnPrintingCompleted(handler, originalJob); - handler.RaiseCompleted(); - }); + if (!responseLogged) + { + responseLogged = true; + Status = MachineStatuses.Printing; + RunningJob = originalJob; + OnPrintingStarted(handler, originalJob); + } + }, (ex) => + { + if (!(ex is ContinuousResponseAbortedException)) + { + Status = MachineStatuses.ReadyToDye; + + if (!handler.IsCanceled) + { + OnPrintingFailed(handler, originalJob, ex); + handler.RaiseFailed(ex); + } + } + else + { + Status = MachineStatuses.ReadyToDye; + } + }, () => + { + Status = MachineStatuses.ReadyToDye; + OnPrintingCompleted(handler, originalJob); + handler.RaiseCompleted(); + }); return handler; @@ -3010,13 +2868,10 @@ namespace Tango.Integration.Operation try { CurrentProcessParameters = processParameters; - LogRequestSent(request); - response = await SendRequest(request); - LogResponseReceived(response); + response = await SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); } catch (Exception ex) { - LogRequestFailed(request, ex); throw ex; } @@ -3116,13 +2971,10 @@ namespace Tango.Integration.Operation try { CurrentHardwareConfiguration = hardwareConfiguration; - LogRequestSent(request); - response = await SendRequest(request); - LogResponseReceived(response); + response = await SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); } catch (Exception ex) { - LogRequestFailed(request, ex); throw ex; } @@ -3140,13 +2992,10 @@ namespace Tango.Integration.Operation try { - LogRequestSent(request); - response = await SendRequest(request); - LogResponseReceived(response); + response = await SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); } catch (Exception ex) { - LogRequestFailed(request, ex); throw ex; } @@ -3160,8 +3009,7 @@ namespace Tango.Integration.Operation /// public async Task StopMotorJogging(MotorAbortJoggingRequest request) { - LogRequestSent(request); - return await SendRequest(request); + return await SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); } /// @@ -3171,8 +3019,7 @@ namespace Tango.Integration.Operation /// public IObservable StartMotorHoming(MotorHomingRequest request) { - LogRequestSent(request); - return SendContinuousRequest(request, null, ContinuousRequestTimeout).Select(x => x.Message); + return SendContinuousRequest(request, new TransportContinuousRequestConfig() { ContinuousTimeout = ContinuousRequestTimeout, ShouldLog = true }).Select(x => x.Message); } /// @@ -3182,8 +3029,7 @@ namespace Tango.Integration.Operation /// public async Task StopMotorHoming(MotorAbortHomingRequest request) { - LogRequestSent(request); - return await SendRequest(request); + return await SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); } /// @@ -3193,8 +3039,7 @@ namespace Tango.Integration.Operation /// public async Task StartDispenserJogging(DispenserJoggingRequest request) { - LogRequestSent(request); - return await SendRequest(request); + return await SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); } /// @@ -3204,8 +3049,7 @@ namespace Tango.Integration.Operation /// public async Task StopDispenserJogging(DispenserAbortJoggingRequest request) { - LogRequestSent(request); - return await SendRequest(request); + return await SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); } /// @@ -3215,8 +3059,7 @@ namespace Tango.Integration.Operation /// public IObservable StartDispenserHoming(DispenserHomingRequest request) { - LogRequestSent(request); - return SendContinuousRequest(request, null, ContinuousRequestTimeout).Select(x => x.Message); + return SendContinuousRequest(request, new TransportContinuousRequestConfig() { ContinuousTimeout = ContinuousRequestTimeout, ShouldLog = true }).Select(x => x.Message); } /// @@ -3226,8 +3069,7 @@ namespace Tango.Integration.Operation /// public async Task StopDispenserHoming(DispenserAbortHomingRequest request) { - LogRequestSent(request); - return await SendRequest(request); + return await SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); } /// @@ -3237,8 +3079,7 @@ namespace Tango.Integration.Operation /// public async Task SetDigitalOut(SetDigitalOutRequest request) { - LogRequestSent(request); - return await SendRequest(request); + return await SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); } /// @@ -3248,8 +3089,7 @@ namespace Tango.Integration.Operation /// public async Task StartThreadJogging(ThreadJoggingRequest request) { - LogRequestSent(request); - return await SendRequest(request); + return await SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); } /// @@ -3259,8 +3099,7 @@ namespace Tango.Integration.Operation /// public async Task StopThreadJogging(ThreadAbortJoggingRequest request) { - LogRequestSent(request); - return await SendRequest(request); + return await SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); } /// @@ -3270,8 +3109,7 @@ namespace Tango.Integration.Operation /// public async Task SetComponentValue(SetComponentValueRequest request) { - LogRequestSent(request); - return await SendRequest(request); + return await SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); } /// @@ -3292,13 +3130,10 @@ namespace Tango.Integration.Operation try { - LogRequestSent(request); - response = await SendRequest(request); - LogResponseReceived(response); + response = await SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); } catch (Exception ex) { - LogRequestFailed(request, ex); throw ex; } @@ -3324,13 +3159,10 @@ namespace Tango.Integration.Operation try { - LogRequestSent(request); - response = await SendRequest(request); - LogResponseReceived(response); + response = await SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); } catch (Exception ex) { - LogRequestFailed(request, ex); throw ex; } @@ -3354,13 +3186,10 @@ namespace Tango.Integration.Operation try { - LogRequestSent(request); - response = await SendRequest(request); - LogResponseReceived(response); + response = await SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); } catch (Exception ex) { - LogRequestFailed(request, ex); throw ex; } @@ -3375,8 +3204,7 @@ namespace Tango.Integration.Operation public async Task ResolveEvent(PMR.Diagnostics.EventType eventType) { ResolveEventRequest request = new ResolveEventRequest() { Type = eventType }; - LogRequestSent(request); - return await SendRequest(request); + return await SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); } /// @@ -3396,13 +3224,10 @@ namespace Tango.Integration.Operation Value = 0x0 }; - LogRequestSent(request); - response = await SendRequest(request); - LogResponseReceived(response); + response = await SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); } catch (Exception ex) { - LogRequestFailed(request, ex); throw ex; } @@ -3416,13 +3241,10 @@ namespace Tango.Integration.Operation Value = 0x1 }; - LogRequestSent(request); - response = await SendRequest(request); - LogResponseReceived(response); + response = await SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); } catch (Exception ex) { - LogRequestFailed(request, ex); throw ex; } @@ -3707,7 +3529,7 @@ namespace Tango.Integration.Operation upgradeHandler.RaiseProgress(upgradeHandler.Total, FirmwareUpgradeStatus.Validating, "Validating version..."); var validateRequest = new ValidateVersionRequest(); validateRequest.Path = package_folder; - var validateResponse = SendRequest(validateRequest, TimeSpan.FromSeconds(10)).Result; + var validateResponse = SendRequest(validateRequest, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(10), ShouldLog = true }).Result; activate(); } catch (Exception ex) @@ -3724,7 +3546,7 @@ namespace Tango.Integration.Operation upgradeHandler.RaiseProgress(upgradeHandler.Total, FirmwareUpgradeStatus.Activating, "Activating version..."); var activateRequest = new ActivateVersionRequest(); activateRequest.Path = package_folder; - var activateResponse = SendRequest(activateRequest, TimeSpan.FromSeconds(10)).Result; + var activateResponse = SendRequest(activateRequest, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(10), ShouldLog = true }).Result; postActivation(); } catch (Exception ex) @@ -3788,7 +3610,7 @@ namespace Tango.Integration.Operation { var validateRequest = new ValidateVersionRequest(); validateRequest.Path = path; - await SendRequest(validateRequest, TimeSpan.FromSeconds(10)); + await SendRequest(validateRequest, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(10), ShouldLog = true }); } /// @@ -3799,7 +3621,7 @@ namespace Tango.Integration.Operation { var activateRequest = new ActivateVersionRequest(); activateRequest.Path = path; - await SendRequest(activateRequest, TimeSpan.FromSeconds(10)); + await SendRequest(activateRequest, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(10), ShouldLog = true }); } /// @@ -3819,7 +3641,7 @@ namespace Tango.Integration.Operation { _isPowerDownRequestInProgress = false; Thread.Sleep(2000); - var r = SendRequest(new AbortPowerDownRequest()).Result; + var r = SendRequest(new AbortPowerDownRequest(), new TransportRequestConfig() { ShouldLog = true }).Result; })); Task.Factory.StartNew(() => @@ -3828,31 +3650,31 @@ namespace Tango.Integration.Operation bool firstResponse = true; - SendContinuousRequest(new StartPowerDownRequest(), RequestTimeout, TimeSpan.FromSeconds(5)).ObserveOn(new NewThreadScheduler()).Subscribe((response) => - { - if (firstResponse) - { - firstResponse = false; - Status = MachineStatuses.ShuttingDown; - } - - handler.RaiseStatusChanged(response); - }, (ex) => - { - if (_isPowerDownRequestInProgress) - { - _isPowerDownRequestInProgress = false; - LogManager.Log(ex, "Power down error."); - handler.RaiseFailed(ex); - } - }, () => - { - if (_isPowerDownRequestInProgress) - { - _isPowerDownRequestInProgress = false; - handler.RaiseCompleted(); - } - }); + SendContinuousRequest(new StartPowerDownRequest(), new TransportContinuousRequestConfig() { ContinuousTimeout = TimeSpan.FromSeconds(2), ShouldLog = true }).ObserveOn(new NewThreadScheduler()).Subscribe((response) => + { + if (firstResponse) + { + firstResponse = false; + Status = MachineStatuses.ShuttingDown; + } + + handler.RaiseStatusChanged(response); + }, (ex) => + { + if (_isPowerDownRequestInProgress) + { + _isPowerDownRequestInProgress = false; + LogManager.Log(ex, "Power down error."); + handler.RaiseFailed(ex); + } + }, () => + { + if (_isPowerDownRequestInProgress) + { + _isPowerDownRequestInProgress = false; + handler.RaiseCompleted(); + } + }); }); PowerDownStarted?.Invoke(this, new PowerDownStartedEventArgs() diff --git a/Software/Visual_Studio/Tango.Integration/Operation/RequestFailedEventArgs.cs b/Software/Visual_Studio/Tango.Integration/Operation/RequestFailedEventArgs.cs deleted file mode 100644 index e946dd0c1..000000000 --- a/Software/Visual_Studio/Tango.Integration/Operation/RequestFailedEventArgs.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Google.Protobuf; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Tango.Integration.Operation -{ - public class RequestFailedEventArgs : EventArgs - { - public IMessage Message { get; set; } - - public Exception Exception { get; set; } - - public RequestFailedEventArgs(IMessage message, Exception exception) - { - Message = message; - Exception = exception; - } - } -} diff --git a/Software/Visual_Studio/Tango.Integration/Storage/StorageManager.cs b/Software/Visual_Studio/Tango.Integration/Storage/StorageManager.cs index 2bf2b7364..2cc95e4a6 100644 --- a/Software/Visual_Studio/Tango.Integration/Storage/StorageManager.cs +++ b/Software/Visual_Studio/Tango.Integration/Storage/StorageManager.cs @@ -88,37 +88,6 @@ namespace Tango.Integration.Storage #endregion - #region Protected Methods - - /// - /// Logs the request sent. - /// - /// The message. - protected void LogRequestSent(IMessage message) - { - LogManager.Log($"{_transporter.ComponentName}: Sending request '{message.GetType().Name}'...\n{message.ToJsonString()}", LogCategory.Debug); - } - - /// - /// Logs the request failed. - /// - /// The message. - protected void LogRequestFailed(IMessage message, Exception ex) - { - LogManager.Log($"{_transporter.ComponentName}: Request failed '{message.GetType().Name}'...\n{message.ToJsonString()}\n{ex.ToString()}", LogCategory.Error); - } - - /// - /// Logs the response received. - /// - /// The message. - protected void LogResponseReceived(IMessage message) - { - LogManager.Log($"{_transporter.ComponentName}: Response received '{message.GetType().Name}'...\n{message.ToJsonString()}", LogCategory.Debug); - } - - #endregion - #region Public Methods /// @@ -134,11 +103,10 @@ namespace Tango.Integration.Storage try { - response = await _transporter.SendRequest(request); + response = await _transporter.SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); } catch (Exception ex) { - LogRequestFailed(request, ex); throw ex; } @@ -189,11 +157,10 @@ namespace Tango.Integration.Storage try { - response = await _transporter.SendRequest(request); + response = await _transporter.SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); } catch (Exception ex) { - LogRequestFailed(request, ex); throw ex; } @@ -246,7 +213,7 @@ namespace Tango.Integration.Storage request.Path = path; request.Length = stream.Length; - var fileUploadResponse = await _transporter.SendRequest(request); + var fileUploadResponse = await _transporter.SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); String uploadId = fileUploadResponse.Message.UploadID; long max_length = fileUploadResponse.Message.MaxChunkLength; @@ -362,7 +329,7 @@ namespace Tango.Integration.Storage FileDownloadRequest request = new FileDownloadRequest(); request.FileName = file.Path; - var fileDownloadResponse = await _transporter.SendRequest(request); + var fileDownloadResponse = await _transporter.SendRequest(request, new TransportRequestConfig() { ShouldLog = true }); String download_id = fileDownloadResponse.Message.DownloadID; long max_length = fileDownloadResponse.Message.MaxChunkLength; @@ -436,7 +403,7 @@ namespace Tango.Integration.Storage { Path = item.Path, Attribute = item.Attribute, - }); + }, new TransportRequestConfig() { ShouldLog = true }); } /// @@ -450,7 +417,7 @@ namespace Tango.Integration.Storage { Path = path, Attribute = FileAttribute.Directory, - }); + }, new TransportRequestConfig() { ShouldLog = true }); } #endregion diff --git a/Software/Visual_Studio/Tango.Integration/Tango.Integration.csproj b/Software/Visual_Studio/Tango.Integration/Tango.Integration.csproj index 2530baf7b..e2c799cf6 100644 --- a/Software/Visual_Studio/Tango.Integration/Tango.Integration.csproj +++ b/Software/Visual_Studio/Tango.Integration/Tango.Integration.csproj @@ -135,7 +135,6 @@ - @@ -209,7 +208,7 @@ - + \ No newline at end of file diff --git a/Software/Visual_Studio/Tango.Stubs/StubManager.cs b/Software/Visual_Studio/Tango.Stubs/StubManager.cs index d8058ee07..0bab8830f 100644 --- a/Software/Visual_Studio/Tango.Stubs/StubManager.cs +++ b/Software/Visual_Studio/Tango.Stubs/StubManager.cs @@ -166,7 +166,7 @@ namespace Tango.Stubs { try { - response = _machineOperator.SendRequest(stub, TimeSpan.FromSeconds(RequestTimeout)).Result; + response = _machineOperator.SendRequest(stub, new Transport.TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(RequestTimeout) }).Result; OnCompleted(JsonConvert.SerializeObject(response, Formatting.Indented)); done = true; } @@ -224,7 +224,7 @@ namespace Tango.Stubs try { - _machineOperator.SendContinuousRequest(stub, TimeSpan.FromSeconds(RequestTimeout)).Subscribe((msg) => + _machineOperator.SendContinuousRequest(stub, new Transport.TransportContinuousRequestConfig() { Timeout = TimeSpan.FromSeconds(RequestTimeout) }).Subscribe((msg) => { callback?.Invoke(msg as T); diff --git a/Software/Visual_Studio/Tango.Transport/ContinuousResponseAbortedException.cs b/Software/Visual_Studio/Tango.Transport/ContinuousResponseAbortedException.cs index 2a34ba248..1f1245d5d 100644 --- a/Software/Visual_Studio/Tango.Transport/ContinuousResponseAbortedException.cs +++ b/Software/Visual_Studio/Tango.Transport/ContinuousResponseAbortedException.cs @@ -3,19 +3,17 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Tango.PMR.Common; namespace Tango.Transport { public class ContinuousResponseAbortedException : Exception { - public ContinuousResponseAbortedException() - { - - } + public MessageContainer Container { get; set; } - public ContinuousResponseAbortedException(String message) : base(message) + public ContinuousResponseAbortedException(MessageContainer container, String message) : base(message) { - + Container = container; } } } diff --git a/Software/Visual_Studio/Tango.Transport/Discovery/UsbCommunicationScanner.cs b/Software/Visual_Studio/Tango.Transport/Discovery/UsbCommunicationScanner.cs index 10628da27..7606de4fc 100644 --- a/Software/Visual_Studio/Tango.Transport/Discovery/UsbCommunicationScanner.cs +++ b/Software/Visual_Studio/Tango.Transport/Discovery/UsbCommunicationScanner.cs @@ -144,7 +144,7 @@ namespace Tango.Transport.Discovery logManager.Log("Connecting transporter..."); await transporter.Connect(); logManager.Log("Sending scanning request..."); - var response = await transporter.SendRequest(request, TimeSpan.FromSeconds(2)); + var response = await transporter.SendRequest(request, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(2) }); if (response is TResponse) { diff --git a/Software/Visual_Studio/Tango.Transport/ITransporter.cs b/Software/Visual_Studio/Tango.Transport/ITransporter.cs index 1f7039df7..5576de0b2 100644 --- a/Software/Visual_Studio/Tango.Transport/ITransporter.cs +++ b/Software/Visual_Studio/Tango.Transport/ITransporter.cs @@ -38,67 +38,65 @@ namespace Tango.Transport /// Sends a request. /// /// The request. - /// Optional timeout. If not specified will use the . + /// Request configuration. /// - Task SendRequest(IMessage request, TimeSpan? timeout = null); + Task SendRequest(IMessage request, TransportRequestConfig config = null); /// /// Sends the request. /// - /// The container. + /// Request configuration. /// - Task SendRequest(MessageContainer container); + Task SendRequest(MessageContainer container, TransportRequestConfig config = null); /// - /// Sends a continuous request. + /// Sends a request. /// - /// The container. + /// The type of the request. + /// The type of the response. + /// The request. + /// Request configuration. /// - IObservable SendContinuousRequest(MessageContainer container); + Task> SendRequest(TangoMessage request, TransportRequestConfig config = null) where Request : IMessage where Response : IMessage; /// - /// Sends the response. + /// Sends a continuous request. /// /// The container. + /// Request configuration. /// - Task SendResponse(MessageContainer container); + IObservable SendContinuousRequest(MessageContainer container, TransportContinuousRequestConfig config = null); /// - /// Sends the response. + /// Sends a request and expecting multiple response messages. /// - /// The response. - /// The token. - /// The completed. - /// The error code. - /// The error message. + /// Request configuration. /// - Task SendResponse(IMessage response, String token, bool? completed = null, ErrorCode? errorCode = null, String errorMessage = null); + IObservable SendContinuousRequest(IMessage request, TransportContinuousRequestConfig config = null); /// /// Sends a request and expecting multiple response messages. /// - /// The request. + /// The type of the request. + /// The type of the response. + /// Request configuration. /// - IObservable SendContinuousRequest(IMessage request, TimeSpan? timeout = null); + IObservable> SendContinuousRequest(TangoMessage request, TransportContinuousRequestConfig config = null) where Request : IMessage where Response : IMessage; /// - /// Sends a request. + /// Sends the response. /// - /// The type of the request. - /// The type of the response. - /// The request. - /// Optional timeout. If not specified will use the . + /// The container. /// - Task> SendRequest(TangoMessage request, TimeSpan? timeout = null) where Request : IMessage where Response : IMessage; + Task SendResponse(MessageContainer container); /// - /// Sends a request and expecting multiple response messages. + /// Sends the response. /// - /// The type of the request. - /// The type of the response. - /// The request. + /// Request token. + /// Response configuration. /// - IObservable> SendContinuousRequest(TangoMessage request, TimeSpan? firstTimeout = null, TimeSpan? continousTimeout = null) where Request : IMessage where Response : IMessage; + Task SendResponse(IMessage response, String token, TransportResponseConfig config = null); /// /// Sends a response. @@ -109,22 +107,20 @@ namespace Tango.Transport Task SendResponse(TangoMessage response) where Response : IMessage; /// - /// Sends a response for the specified token. + /// Sends a response. /// /// The type of the response. /// The response. - /// The token. - /// The completed. - /// The error code. - /// The error message. + /// Request token. + /// Response configuration. /// - Task SendResponse(TangoMessage response, String token, bool? completed = null, ErrorCode? errorCode = null, String errorMessage = null) where Response : IMessage; + Task SendResponse(TangoMessage response, String token, TransportResponseConfig config = null) where Response : IMessage; /// /// Sends a general error response agnostic to the type of request. /// /// The exception. - /// The token. + /// Request token. /// Task SendErrorResponse(Exception exception, String token); @@ -143,6 +139,21 @@ namespace Tango.Transport /// event EventHandler PendingResponseReceived; + /// + /// Occurs when a request has been sent. + /// + event EventHandler RequestSent; + + /// + /// Occurs when a request response has been received. + /// + event EventHandler ResponseReceived; + + /// + /// Occurs when a request has failed. + /// + event EventHandler RequestFailed; + /// /// Gets or sets the default request timeout. /// diff --git a/Software/Visual_Studio/Tango.Transport/RequestFailedEventArgs.cs b/Software/Visual_Studio/Tango.Transport/RequestFailedEventArgs.cs new file mode 100644 index 000000000..dfbb24a6f --- /dev/null +++ b/Software/Visual_Studio/Tango.Transport/RequestFailedEventArgs.cs @@ -0,0 +1,22 @@ +using Google.Protobuf; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.Transport +{ + public class RequestFailedEventArgs : EventArgs + { + public IMessage Message { get; set; } + + public Exception Exception { get; set; } + + public RequestFailedEventArgs(IMessage message, Exception exception) + { + Message = message; + Exception = exception; + } + } +} diff --git a/Software/Visual_Studio/Tango.Transport/Tango.Transport.csproj b/Software/Visual_Studio/Tango.Transport/Tango.Transport.csproj index 9ade854c5..b38058b98 100644 --- a/Software/Visual_Studio/Tango.Transport/Tango.Transport.csproj +++ b/Software/Visual_Studio/Tango.Transport/Tango.Transport.csproj @@ -120,19 +120,23 @@ + + + + @@ -176,7 +180,7 @@ - + \ No newline at end of file diff --git a/Software/Visual_Studio/Tango.Transport/TransportContinuousRequestConfig.cs b/Software/Visual_Studio/Tango.Transport/TransportContinuousRequestConfig.cs new file mode 100644 index 000000000..999522c9a --- /dev/null +++ b/Software/Visual_Studio/Tango.Transport/TransportContinuousRequestConfig.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.Transport +{ + public class TransportContinuousRequestConfig : TransportRequestConfig + { + public TimeSpan? ContinuousTimeout { get; set; } + } +} diff --git a/Software/Visual_Studio/Tango.Transport/TransportMessage.cs b/Software/Visual_Studio/Tango.Transport/TransportMessage.cs index 4a1d44892..8810b8db4 100644 --- a/Software/Visual_Studio/Tango.Transport/TransportMessage.cs +++ b/Software/Visual_Studio/Tango.Transport/TransportMessage.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Reactive.Subjects; using System.Text; using System.Threading.Tasks; +using Tango.Core.ExtensionMethods; using Tango.Logging; using Tango.PMR; using Tango.PMR.Common; @@ -24,8 +25,6 @@ namespace Tango.Transport public Subject ContinuesResponseSubject { get; set; } - public bool AtLeastOneResponseReceived { get; set; } - public DateTime LastResponseTime { get; set; } public bool Completed { get; set; } @@ -132,15 +131,64 @@ namespace Tango.Transport { if (!_completionSource.Task.IsCompleted) { + if (!(ex is ContinuousResponseAbortedException) && !(ex is TransporterDisconnectedException)) + { + LogManager.Log($"{TransportComponentName}: Request failed '{GetActualMessageTypeName()}'...\n{ex.FlattenException()}", LogCategory.Error); + } _completionSource.SetException(ex); } } else { + if (!(ex is ContinuousResponseAbortedException) && !(ex is TransporterDisconnectedException)) + { + LogManager.Log($"{TransportComponentName}: Request failed '{GetActualMessageTypeName()}'...\n{ex.FlattenException()}", LogCategory.Error); + } + AtLeastOneResponseReceived = true; ContinuesResponseSubject.OnError(ex); } }); } + + public override string GetActualMessageTypeName() + { + String name = String.Empty; + + if (Message is ITangoMessage) + { + name = Message.GetType().GetProperty("Message").GetValue(Message).GetType().Name; + } + else if (Message is MessageContainer) + { + name = (Message as MessageContainer).Type.ToString(); + } + else + { + name = Message.GetType().Name; + } + + return name; + } + + public override object GetActualMessage() + { + object obj = null; + + if (Message is ITangoMessage) + { + obj = Message.GetType().GetProperty("Message").GetValue(Message); + } + else if (Message is MessageContainer) + { + obj = Message; + } + else + { + obj = Message; + } + + return obj; + } } } diff --git a/Software/Visual_Studio/Tango.Transport/TransportMessageBase.cs b/Software/Visual_Studio/Tango.Transport/TransportMessageBase.cs index 7736a2752..849317b9b 100644 --- a/Software/Visual_Studio/Tango.Transport/TransportMessageBase.cs +++ b/Software/Visual_Studio/Tango.Transport/TransportMessageBase.cs @@ -13,6 +13,10 @@ namespace Tango.Transport /// internal abstract class TransportMessageBase : ExtendedObject { + public bool AtLeastOneResponseReceived { get; set; } + + public String TransportComponentName { get; set; } + /// /// Gets or sets a value indicating whether this instance is multi response. /// @@ -43,6 +47,11 @@ namespace Tango.Transport /// public Object Message { get; set; } + /// + /// Gets or sets a value indicating whether log the message before sending. + /// + public bool ShouldLog { get; set; } + /// /// Notifies the message observer of the new result. /// @@ -55,6 +64,10 @@ namespace Tango.Transport /// The ex. public abstract void SetException(Exception ex); + public abstract String GetActualMessageTypeName(); + + public abstract Object GetActualMessage(); + /// /// Initializes a new instance of the class. /// diff --git a/Software/Visual_Studio/Tango.Transport/TransportRequestConfig.cs b/Software/Visual_Studio/Tango.Transport/TransportRequestConfig.cs new file mode 100644 index 000000000..6250cd881 --- /dev/null +++ b/Software/Visual_Studio/Tango.Transport/TransportRequestConfig.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.Transport +{ + public class TransportRequestConfig + { + public TimeSpan? Timeout { get; set; } + public bool ShouldLog { get; set; } + } +} diff --git a/Software/Visual_Studio/Tango.Transport/TransportResponseConfig.cs b/Software/Visual_Studio/Tango.Transport/TransportResponseConfig.cs new file mode 100644 index 000000000..449482e8a --- /dev/null +++ b/Software/Visual_Studio/Tango.Transport/TransportResponseConfig.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.PMR.Common; + +namespace Tango.Transport +{ + public class TransportResponseConfig + { + public bool Completed { get; set; } + public ErrorCode? ErrorCode { get; set; } + public String ErrorMessage { get; set; } + } +} diff --git a/Software/Visual_Studio/Tango.Transport/TransporterBase.cs b/Software/Visual_Studio/Tango.Transport/TransporterBase.cs index 263e78933..ba220797e 100644 --- a/Software/Visual_Studio/Tango.Transport/TransporterBase.cs +++ b/Software/Visual_Studio/Tango.Transport/TransporterBase.cs @@ -18,6 +18,7 @@ using Tango.Transport.Encoders; using Tango.PMR.Connection; using Tango.Core.Threading; using System.IO; +using Tango.Core.ExtensionMethods; namespace Tango.Transport { @@ -57,6 +58,21 @@ namespace Tango.Transport /// public event EventHandler StateChanged; + /// + /// Occurs when a request has been sent. + /// + public event EventHandler RequestSent; + + /// + /// Occurs when a request response has been received. + /// + public event EventHandler ResponseReceived; + + /// + /// Occurs when a request has failed. + /// + public event EventHandler RequestFailed; + #endregion #region Properties @@ -283,6 +299,7 @@ namespace Tango.Transport try { LogManager.Log($"Notifying continuous request '{(request.Message as ITangoMessage).Type}'..."); + OnRequestFailed(request, new TransporterDisconnectedException("Transporter disconnected.")); request.SetException(new TransporterDisconnectedException("Transporter disconnected.")); } catch (Exception e) @@ -292,6 +309,49 @@ namespace Tango.Transport } } + /// + /// Called when the request has been sent + /// + /// The request. + private void OnRequestSent(TransportMessageBase request) + { + if (request.ShouldLog) + { + IMessage message = request.GetActualMessage() as IMessage; + + if (message != null) + { + RequestSent?.Invoke(this, message); + } + } + } + + /// + /// Called when the response has been received + /// + /// The response. + private void OnResponseReceived(IMessage response) + { + if (response != null) + { + ResponseReceived?.Invoke(this, response); + } + } + + /// + /// Called when the request has been failed + /// + /// The request. + private void OnRequestFailed(TransportMessageBase request, Exception exception) + { + IMessage message = request.GetActualMessage() as IMessage; + + if (message != null) + { + RequestFailed?.Invoke(this, new RequestFailedEventArgs(message, exception)); + } + } + #endregion #region Constructors @@ -381,21 +441,28 @@ namespace Tango.Transport NotifyContinuousRequestMessagesDisconnection(); } + #endregion + + #region Public Request Methods + /// /// Sends a request. /// /// The request. - /// Optional timeout. If not specified will use the . + /// Request configuration. /// - public Task SendRequest(IMessage request, TimeSpan? timeout = null) + /// + public Task SendRequest(IMessage request, TransportRequestConfig config = null) { + config = config ?? new TransportRequestConfig(); + String requestName = request.GetType().Name; String responseName = requestName.Replace("Request", "Response"); MessageContainer container = new MessageContainer(); container.Token = Guid.NewGuid().ToString(); container.Data = request.ToByteString(); - container.Timeout = timeout.HasValue ? (UInt32)timeout.Value.TotalMilliseconds : (UInt32)RequestTimeout.TotalMilliseconds; + container.Timeout = config.Timeout.HasValue ? (UInt32)config.Timeout.Value.TotalMilliseconds : (UInt32)RequestTimeout.TotalMilliseconds; container.Type = MessageFactory.ParseMessageType(requestName); LogManager.Log($"{GetExtendedComponentName()}: Queuing request message: " + requestName + " Token: " + container.Token, LogCategory.Debug); @@ -408,6 +475,8 @@ namespace Tango.Transport TaskCompletionSource source = new TaskCompletionSource(); TransportMessage message = new TransportMessage(container.Token, request, TransportMessageDirection.Request, () => container.ToByteArray(), source); + message.ShouldLog = config.ShouldLog; + message.TransportComponentName = GetExtendedComponentName(); message.ActivateTimeout = () => { @@ -416,13 +485,13 @@ namespace Tango.Transport if (!source.Task.IsCompleted) { - TimeoutException ex = new TimeoutException("Request message: " + requestName + " had timed out after " + (timeout != null ? timeout.Value.TotalSeconds : RequestTimeout.TotalSeconds) + " seconds."); - LogManager.Log(ex); + TimeoutException ex = new TimeoutException("Request message: " + requestName + " had timed out after " + (config.Timeout != null ? config.Timeout.Value.TotalSeconds : RequestTimeout.TotalSeconds) + " seconds."); LogManager.Log($"{GetExtendedComponentName()}: Setting request task exception...", LogCategory.Debug); - source.SetException(ex); + OnRequestFailed(message, ex); + message.SetException(ex); } - }, timeout != null ? timeout.Value : RequestTimeout); + }, config.Timeout != null ? config.Timeout.Value : RequestTimeout); }; EnqueueMessageOut(message); @@ -430,50 +499,17 @@ namespace Tango.Transport return source.Task; } - /// - /// Sends the response. - /// - /// The response. - /// The token. - /// The completed. - /// The error code. - /// The error message. - /// - /// Matching request token was not found! - public Task SendResponse(IMessage response, string token, bool? completed = default(bool?), ErrorCode? errorCode = default(ErrorCode?), string errorMessage = null) - { - String responseName = response.GetType().Name; - - MessageContainer container = new MessageContainer(); - container.Token = token; - container.Data = response.ToByteString(); - container.Type = MessageFactory.ParseMessageType(responseName); - - if (errorCode.HasValue) - { - container.Error = errorCode.Value; - } - - if (errorMessage != null) - { - container.ErrorMessage = errorMessage; - } - - if (completed.HasValue) - { - container.Completed = completed.Value; - } - - return SendResponse(container); - } - /// /// Sends the request. /// - /// The container. + /// + /// Request configuration. /// - public Task SendRequest(MessageContainer container) + /// + public Task SendRequest(MessageContainer container, TransportRequestConfig config = null) { + config = config ?? new TransportRequestConfig(); + String responseName = container.Type.ToString().Replace("Request", "Response"); TimeSpan? timeout = GetContainerTimeoutOrDefault(container); @@ -487,6 +523,8 @@ namespace Tango.Transport TaskCompletionSource source = new TaskCompletionSource(); TransportMessage message = new TransportMessage(container.Token, container, TransportMessageDirection.Request, () => container.ToByteArray(), source); + message.ShouldLog = config.ShouldLog; + message.TransportComponentName = GetExtendedComponentName(); message.ActivateTimeout = () => { @@ -496,9 +534,9 @@ namespace Tango.Transport if (!source.Task.IsCompleted) { TimeoutException ex = new TimeoutException("Request message: " + container.Type + " had timed out after " + (timeout != null ? timeout.Value.TotalSeconds : RequestTimeout.TotalSeconds) + " seconds."); - LogManager.Log(ex); LogManager.Log($"{GetExtendedComponentName()}: Setting request task exception...", LogCategory.Debug); - source.SetException(ex); + OnRequestFailed(message, ex); + message.SetException(ex); } }, timeout != null ? timeout.Value : RequestTimeout); @@ -511,61 +549,65 @@ namespace Tango.Transport } /// - /// Sends the response. + /// Sends a request. /// - /// The container. + /// The type of the request. + /// The type of the response. + /// The request. + /// Request configuration. /// - /// Matching request token was not found! - public Task SendResponse(MessageContainer container) + /// + public Task> SendRequest(TangoMessage request, TransportRequestConfig config = null) where Request : IMessage where Response : IMessage { - String token = container.Token; - - LogManager.Log($"{GetExtendedComponentName()}: Queuing response message: " + container.Type, LogCategory.Debug); + config = config ?? new TransportRequestConfig(); - PendingResponse pendingResponse = null; + LogManager.Log($"{GetExtendedComponentName()}: Queuing request message: " + typeof(Request).Name + " Token: " + request.Container.Token, LogCategory.Debug); + LogManager.Log($"{GetExtendedComponentName()}: Expected response: " + typeof(Response).Name, LogCategory.Debug); if (State != TransportComponentState.Connected) { - throw LogManager.Log(new InvalidOperationException($"{GetExtendedComponentName()}: Could not send the response while transporter state is {State}.")); + throw LogManager.Log(new InvalidOperationException($"{GetExtendedComponentName()}: Could not send the request while transporter state is {State}.")); } - LogManager.Log($"{GetExtendedComponentName()}: Searching for matching request token: " + token, LogCategory.Debug); + request.Container.Timeout = config.Timeout.HasValue ? (UInt32)config.Timeout.Value.TotalMilliseconds : (UInt32)RequestTimeout.TotalMilliseconds; - if (_pendingResponses.TryGetValue(token, out pendingResponse)) - { - LogManager.Log($"{GetExtendedComponentName()}: Found matching request token: " + token, LogCategory.Debug); + TaskCompletionSource> source = new TaskCompletionSource>(); + TransportMessage> message = new TransportMessage>(request.Container.Token, request, TransportMessageDirection.Request, () => Encoder.Encode(request), source); + message.ShouldLog = config.ShouldLog; + message.TransportComponentName = GetExtendedComponentName(); - if (!pendingResponse.IsContinuous) - { - LogManager.Log($"{GetExtendedComponentName()}: Removing matching request token.", LogCategory.Debug); - _pendingResponses.Remove(token); - } - else if (container.Completed) - { - LogManager.Log($"{GetExtendedComponentName()}: Response completed. Removing matching request token.", LogCategory.Debug); - _pendingResponses.Remove(token); - } - } - else + message.ActivateTimeout = () => { - //This should never happen. - throw LogManager.Log(new InvalidOperationException($"{GetExtendedComponentName()}: Matching request token was not found!"), LogCategory.Critical); - } + TimeoutTask.StartNew(() => + { + + if (!source.Task.IsCompleted) + { + TimeoutException ex = new TimeoutException("Request message: " + typeof(Request).Name + " had timed out after " + (config.Timeout != null ? config.Timeout.Value.TotalSeconds : RequestTimeout.TotalSeconds) + " seconds."); + LogManager.Log($"{GetExtendedComponentName()}: Setting request task exception...", LogCategory.Debug); + OnRequestFailed(message, ex); + message.SetException(ex); + } + + }, config.Timeout != null ? config.Timeout.Value : RequestTimeout); + }; - TaskCompletionSource source = new TaskCompletionSource(); - TransportMessage message = new TransportMessage(token, container, TransportMessageDirection.Response, () => container.ToByteArray(), source); EnqueueMessageOut(message); + return source.Task; } /// /// Sends a request and expecting multiple response messages. /// - /// The request. - /// + /// + /// Request configuration. /// - public IObservable SendContinuousRequest(IMessage request, TimeSpan? timeout = default(TimeSpan?)) + /// + public IObservable SendContinuousRequest(IMessage request, TransportContinuousRequestConfig config = null) { + config = config ?? new TransportContinuousRequestConfig(); + String requestName = request.GetType().Name; String responseName = requestName.Replace("Request", "Response"); @@ -573,7 +615,7 @@ namespace Tango.Transport container.Token = Guid.NewGuid().ToString(); container.Data = request.ToByteString(); container.Type = MessageFactory.ParseMessageType(requestName); - container.Timeout = timeout.HasValue ? (UInt32)timeout.Value.TotalMilliseconds : (UInt32)RequestTimeout.TotalMilliseconds; + container.Timeout = config.Timeout.HasValue ? (UInt32)config.Timeout.Value.TotalMilliseconds : (UInt32)RequestTimeout.TotalMilliseconds; container.Continuous = true; LogManager.Log($"{GetExtendedComponentName()}: Queuing continuous request message: " + requestName + " Token: " + container.Token, LogCategory.Debug); @@ -591,6 +633,8 @@ namespace Tango.Transport { IsContinuous = true, ContinuesResponseSubject = subject, + ShouldLog = config.ShouldLog, + TransportComponentName = GetExtendedComponentName(), }; message.ActivateTimeout = () => @@ -601,13 +645,13 @@ namespace Tango.Transport if (!message.AtLeastOneResponseReceived) { - TimeoutException ex = new TimeoutException("Request message: " + requestName + " had timed out after " + (timeout != null ? timeout.Value.TotalSeconds : RequestTimeout.TotalSeconds) + " seconds."); - LogManager.Log(ex); + TimeoutException ex = new TimeoutException("Request message: " + requestName + " had timed out after " + (config.Timeout != null ? config.Timeout.Value.TotalSeconds : RequestTimeout.TotalSeconds) + " seconds."); LogManager.Log($"{GetExtendedComponentName()}: Setting request exception...", LogCategory.Debug); + OnRequestFailed(message, ex); message.SetException(ex); } - }, timeout != null ? timeout.Value : RequestTimeout); + }, config.Timeout != null ? config.Timeout.Value : RequestTimeout); }; @@ -616,60 +660,19 @@ namespace Tango.Transport return subject.AsObservable(); } - /// - /// Sends a request. - /// - /// The type of the request. - /// The type of the response. - /// The request. - /// Optional timeout. If not specified will use the . - /// - public Task> SendRequest(TangoMessage request, TimeSpan? timeout = null) where Request : IMessage where Response : IMessage - { - LogManager.Log($"{GetExtendedComponentName()}: Queuing request message: " + typeof(Request).Name + " Token: " + request.Container.Token, LogCategory.Debug); - LogManager.Log($"{GetExtendedComponentName()}: Expected response: " + typeof(Response).Name, LogCategory.Debug); - - if (State != TransportComponentState.Connected) - { - throw LogManager.Log(new InvalidOperationException($"{GetExtendedComponentName()}: Could not send the request while transporter state is {State}.")); - } - - request.Container.Timeout = timeout.HasValue ? (UInt32)timeout.Value.TotalMilliseconds : (UInt32)RequestTimeout.TotalMilliseconds; - - TaskCompletionSource> source = new TaskCompletionSource>(); - TransportMessage> message = new TransportMessage>(request.Container.Token, request, TransportMessageDirection.Request, () => Encoder.Encode(request), source); - - message.ActivateTimeout = () => - { - TimeoutTask.StartNew(() => - { - - if (!source.Task.IsCompleted) - { - TimeoutException ex = new TimeoutException("Request message: " + typeof(Request).Name + " had timed out after " + (timeout != null ? timeout.Value.TotalSeconds : RequestTimeout.TotalSeconds) + " seconds."); - LogManager.Log(ex); - LogManager.Log($"{GetExtendedComponentName()}: Setting request task exception...", LogCategory.Debug); - source.SetException(ex); - } - - }, timeout != null ? timeout.Value : RequestTimeout); - }; - - EnqueueMessageOut(message); - - return source.Task; - } - /// /// Sends a request and expecting multiple response messages. /// /// The type of the request. /// The type of the response. - /// The request. - /// + /// + /// Request configuration. /// - public IObservable> SendContinuousRequest(TangoMessage request, TimeSpan? firstTimeout = null, TimeSpan? continousTimeout = null) where Request : IMessage where Response : IMessage + /// + public IObservable> SendContinuousRequest(TangoMessage request, TransportContinuousRequestConfig config = null) where Request : IMessage where Response : IMessage { + config = config ?? new TransportContinuousRequestConfig(); + LogManager.Log($"{GetExtendedComponentName()}: Queuing continuous request message: " + typeof(Request).Name + " Token: " + request.Container.Token, LogCategory.Debug); Subject> subject = new Subject>(); @@ -684,13 +687,15 @@ namespace Tango.Transport request.Container.Continuous = true; request.Container.Completed = false; - request.Container.Timeout = firstTimeout.HasValue ? (UInt32)firstTimeout.Value.TotalMilliseconds : (UInt32)RequestTimeout.TotalMilliseconds; - request.Container.ContinuousTimeout = continousTimeout.HasValue ? (UInt32)continousTimeout.Value.TotalMilliseconds : 0; + request.Container.Timeout = config.Timeout.HasValue ? (UInt32)config.Timeout.Value.TotalMilliseconds : (UInt32)RequestTimeout.TotalMilliseconds; + request.Container.ContinuousTimeout = config.ContinuousTimeout.HasValue ? (UInt32)config.ContinuousTimeout.Value.TotalMilliseconds : 0; TransportMessage> message = new TransportMessage>(request.Container.Token, request, TransportMessageDirection.Request, () => Encoder.Encode(request), null) { IsContinuous = true, ContinuesResponseSubject = subject, + ShouldLog = config.ShouldLog, + TransportComponentName = GetExtendedComponentName(), }; message.ActivateTimeout = () => @@ -700,27 +705,27 @@ namespace Tango.Transport if (!message.AtLeastOneResponseReceived) { - TimeoutException ex = new TimeoutException("Request message: " + typeof(Request).Name + " had timed out after " + (firstTimeout != null ? firstTimeout.Value.TotalSeconds : RequestTimeout.TotalSeconds) + " seconds."); - LogManager.Log(ex); + TimeoutException ex = new TimeoutException("Request message: " + typeof(Request).Name + " had timed out after " + (config.Timeout != null ? config.Timeout.Value.TotalSeconds : RequestTimeout.TotalSeconds) + " seconds."); LogManager.Log($"{GetExtendedComponentName()}: Setting request exception...", LogCategory.Debug); + OnRequestFailed(message, ex); message.SetException(ex); } - if (continousTimeout != null) + if (config.ContinuousTimeout != null) { Task.Factory.StartNew(async () => { while (!message.Completed) { - await Task.Delay(continousTimeout.Value).ContinueWith((y) => + await Task.Delay(config.ContinuousTimeout.Value).ContinueWith((y) => { if (!message.Completed) { - if (DateTime.Now - message.LastResponseTime > continousTimeout.Value) + if (DateTime.Now - message.LastResponseTime > config.ContinuousTimeout.Value) { - TimeoutException ex = new TimeoutException("Continuous request message: " + typeof(Request).Name + " had failed to provide a response for a period of " + (continousTimeout.Value.TotalSeconds) + " seconds and has timed out."); - LogManager.Log(ex); + TimeoutException ex = new TimeoutException("Continuous request message: " + typeof(Request).Name + " had failed to provide a response for a period of " + (config.ContinuousTimeout.Value.TotalSeconds) + " seconds and has timed out."); LogManager.Log($"{GetExtendedComponentName()}: Setting request exception...", LogCategory.Debug); + OnRequestFailed(message, ex); message.SetException(ex); return; } @@ -730,7 +735,7 @@ namespace Tango.Transport }); } - }, firstTimeout != null ? firstTimeout.Value : RequestTimeout); + }, config.Timeout != null ? config.Timeout.Value : RequestTimeout); }; EnqueueMessageOut(message); @@ -742,9 +747,13 @@ namespace Tango.Transport /// Sends a continuous request. /// /// The container. + /// Request configuration. /// - public IObservable SendContinuousRequest(MessageContainer container) + /// + public IObservable SendContinuousRequest(MessageContainer container, TransportContinuousRequestConfig config = null) { + config = config ?? new TransportContinuousRequestConfig(); + TimeSpan? timeout = GetContainerTimeoutOrDefault(container); TimeSpan? continuousTimeout = container.ContinuousTimeout > 0 ? TimeSpan.FromMilliseconds(container.ContinuousTimeout) : default(TimeSpan?); @@ -766,6 +775,8 @@ namespace Tango.Transport { IsContinuous = true, ContinuesResponseSubject = subject, + ShouldLog = config.ShouldLog, + TransportComponentName = GetExtendedComponentName(), }; message.ActivateTimeout = () => @@ -776,8 +787,8 @@ namespace Tango.Transport if (!message.AtLeastOneResponseReceived) { TimeoutException ex = new TimeoutException("Request message: " + requestName + " had timed out after " + (timeout != null ? timeout.Value.TotalSeconds : RequestTimeout.TotalSeconds) + " seconds."); - LogManager.Log(ex); LogManager.Log($"{GetExtendedComponentName()}: Setting request exception...", LogCategory.Debug); + OnRequestFailed(message, ex); message.SetException(ex); } @@ -794,8 +805,8 @@ namespace Tango.Transport if (DateTime.Now - message.LastResponseTime > continuousTimeout.Value) { TimeoutException ex = new TimeoutException("Continuous request message: " + requestName + " had failed to provide a response for a period of " + (continuousTimeout.Value.TotalSeconds) + " seconds and has timed out."); - LogManager.Log(ex); LogManager.Log($"{GetExtendedComponentName()}: Setting request exception...", LogCategory.Debug); + OnRequestFailed(message, ex); message.SetException(ex); return; } @@ -813,6 +824,10 @@ namespace Tango.Transport return subject.AsObservable(); } + #endregion + + #region Public Response Methods + /// /// Sends a response. /// @@ -825,18 +840,102 @@ namespace Tango.Transport } /// - /// Sends a response for the specified token. + /// Sends the response. + /// + /// + /// Request token. + /// Response configuration. + /// + public Task SendResponse(IMessage response, String token, TransportResponseConfig config = null) + { + config = config ?? new TransportResponseConfig(); + + String responseName = response.GetType().Name; + + MessageContainer container = new MessageContainer(); + container.Token = token; + container.Data = response.ToByteString(); + container.Type = MessageFactory.ParseMessageType(responseName); + + if (config.ErrorCode.HasValue) + { + container.Error = config.ErrorCode.Value; + } + + if (config.ErrorMessage != null) + { + container.ErrorMessage = config.ErrorMessage; + } + + container.Completed = config.Completed; + + return SendResponse(container); + } + + /// + /// Sends the response. + /// + /// The container. + /// + /// + /// + public Task SendResponse(MessageContainer container) + { + String token = container.Token; + + LogManager.Log($"{GetExtendedComponentName()}: Queuing response message: " + container.Type, LogCategory.Debug); + + PendingResponse pendingResponse = null; + + if (State != TransportComponentState.Connected) + { + throw LogManager.Log(new InvalidOperationException($"{GetExtendedComponentName()}: Could not send the response while transporter state is {State}.")); + } + + LogManager.Log($"{GetExtendedComponentName()}: Searching for matching request token: " + token, LogCategory.Debug); + + if (_pendingResponses.TryGetValue(token, out pendingResponse)) + { + LogManager.Log($"{GetExtendedComponentName()}: Found matching request token: " + token, LogCategory.Debug); + + if (!pendingResponse.IsContinuous) + { + LogManager.Log($"{GetExtendedComponentName()}: Removing matching request token.", LogCategory.Debug); + _pendingResponses.Remove(token); + } + else if (container.Completed) + { + LogManager.Log($"{GetExtendedComponentName()}: Response completed. Removing matching request token.", LogCategory.Debug); + _pendingResponses.Remove(token); + } + } + else + { + //This should never happen. + throw LogManager.Log(new InvalidOperationException($"{GetExtendedComponentName()}: Matching request token was not found!"), LogCategory.Critical); + } + + TaskCompletionSource source = new TaskCompletionSource(); + TransportMessage message = new TransportMessage(token, container, TransportMessageDirection.Response, () => container.ToByteArray(), source); + EnqueueMessageOut(message); + return source.Task; + } + + /// + /// Sends a response. /// /// The type of the response. /// The response. - /// The token. - /// The completed. - /// The error code. - /// The error message. + /// Request token. + /// Response configuration. /// - /// Matching request token was not found! - public Task SendResponse(TangoMessage response, String token, bool? completed = null, ErrorCode? errorCode = null, String errorMessage = null) where Response : IMessage + /// + /// Transporter push thread is not in a running state. + /// + public Task SendResponse(TangoMessage response, String token, TransportResponseConfig config = null) where Response : IMessage { + config = config ?? new TransportResponseConfig(); + if (_pushThread == null || _pushThread.ThreadState == ThreadState.Aborted) { throw new InvalidOperationException("Transporter push thread is not in a running state."); @@ -844,19 +943,16 @@ namespace Tango.Transport response.Container.Token = token; - if (completed.HasValue) - { - response.Container.Completed = completed.Value; - } + response.Container.Completed = config.Completed; - if (errorCode.HasValue) + if (config.ErrorCode.HasValue) { - response.Container.Error = errorCode.Value; + response.Container.Error = config.ErrorCode.Value; } - if (!String.IsNullOrEmpty(errorMessage)) + if (!String.IsNullOrEmpty(config.ErrorMessage)) { - response.Container.ErrorMessage = errorMessage; + response.Container.ErrorMessage = config.ErrorMessage; } LogManager.Log($"{GetExtendedComponentName()}: Queuing response message: " + typeof(Response).Name, LogCategory.Debug); @@ -901,11 +997,16 @@ namespace Tango.Transport /// Sends a general error response agnostic to the type of request. /// /// The exception. - /// The token. + /// Response configuration. /// - public Task SendErrorResponse(Exception exception, string token) + public Task SendErrorResponse(Exception exception, String token) { - return SendResponse(new ErrorResponse() { }, token, true, ErrorCode.GeneralError, exception.Message); + return SendResponse(new ErrorResponse() { }, token, new TransportResponseConfig() + { + ErrorCode = ErrorCode.GeneralError, + Completed = true, + ErrorMessage = exception.Message + }); } #endregion @@ -989,31 +1090,46 @@ namespace Tango.Transport { if (message.Token.Length != MESSAGE_TOKEN_LENGTH) { - message.SetException(LogManager.Log(new InvalidOperationException($"{GetExtendedComponentName()}: Invalid message token length: " + message.Token))); + var ex = LogManager.Log(new InvalidOperationException($"{GetExtendedComponentName()}: Invalid message token length: " + message.Token)); + OnRequestFailed(message, ex); + message.SetException(ex); continue; } - LogManager.Log($"{GetExtendedComponentName()}: Sending message on adapter: " + Adapter.Address + "...", LogCategory.Debug, message.Message); - if (message.Direction == TransportMessageDirection.Request) { + if (message.ShouldLog) + { + LogManager.Log($"{GetExtendedComponentName()}: Sending request '{message.GetActualMessageTypeName()}'...\n{message.GetActualMessage().ToJsonString()}", LogCategory.Info); + OnRequestSent(message); + } + lock (_pendingRequests) { _pendingRequests.Add(message); } } + else + { + if (message.ShouldLog) + { + LogManager.Log($"{GetExtendedComponentName()}: Sending response '{message.GetActualMessageTypeName()}'...\n{message.GetActualMessage().ToJsonString()}", LogCategory.Info); + } + } Adapter.Write(message.Serialize()); message.ActivateTimeout?.Invoke(); - LogManager.Log($"{GetExtendedComponentName()}: Message sent on adapter: " + Adapter.Address + "...", LogCategory.Debug, message.Message); + LogManager.Log($"{GetExtendedComponentName()}: Message sent...", LogCategory.Debug, message.Message); } else { if (message.Direction == TransportMessageDirection.Request) { - message.SetException(LogManager.Log(new InvalidOperationException($"{GetExtendedComponentName()}: Could not send message " + message.Message.GetType().Name + ". Adapter is disconnected."))); + var ex = new InvalidOperationException($"{GetExtendedComponentName()}: Could not send message " + message.Message.GetType().Name + ". Adapter is disconnected."); + OnRequestFailed(message, ex); + message.SetException(ex); } } @@ -1024,6 +1140,7 @@ namespace Tango.Transport } catch (Exception ex) { + OnRequestFailed(message, ex); message.SetException(ex); } } @@ -1053,7 +1170,7 @@ namespace Tango.Transport { byte[] data = _arrivedResponses.BlockDequeue(); - LogManager.Log($"{GetExtendedComponentName()}: Message received on adapter: " + Adapter.Address, LogCategory.Debug); + LogManager.Log($"{GetExtendedComponentName()}: Message received...", LogCategory.Debug); LogManager.Log($"{GetExtendedComponentName()}: Parsing message container...", LogCategory.Debug); MessageContainer container = Encoder.DecodeContainer(data); @@ -1110,17 +1227,35 @@ namespace Tango.Transport if (container.Error == ErrorCode.None) { var message = Encoder.Decode(data); + + if (request.ShouldLog) + { + try + { + var messageContent = message.GetType().GetProperty("Message").GetValue(message); + LogManager.Log($"{GetExtendedComponentName()}: Response received '{message.Type}'...\n{messageContent.ToJsonString()}", LogCategory.Info); + OnResponseReceived(messageContent as IMessage); + } + catch + { + LogManager.Log("Error logging response received.", LogCategory.Warning); + } + } + LogManager.Log($"{GetExtendedComponentName()}: Parsing inner response message and setting pending request task result...", LogCategory.Debug, message); request.SetResult(message, true); LogManager.Log($"{GetExtendedComponentName()}: Message enquirer released...", LogCategory.Debug); } else { - request.SetException(LogManager.Log(new ResponseErrorException(container), LogCategory.Warning)); + var ex = LogManager.Log(new ResponseErrorException(container)); + OnRequestFailed(request, ex); + request.SetException(ex); } } catch (Exception ex) { + OnRequestFailed(request, ex); request.SetException(LogManager.Log(ex, $"{GetExtendedComponentName()}: Error parsing response message.")); } } @@ -1134,6 +1269,20 @@ namespace Tango.Transport { var message = Encoder.Decode(data); + if (request.ShouldLog && !request.AtLeastOneResponseReceived) + { + try + { + var messageContent = message.GetType().GetProperty("Message").GetValue(message); + LogManager.Log($"{GetExtendedComponentName()}: Response received '{message.Type}'...\n{messageContent.ToJsonString()}", LogCategory.Info); + OnResponseReceived(messageContent as IMessage); + } + catch + { + LogManager.Log("Error logging response received.", LogCategory.Warning); + } + } + LogManager.Log($"{GetExtendedComponentName()}: Parsing inner response message and invoking continuous response callback...", LogCategory.Debug, message); if (container.Completed) @@ -1148,12 +1297,14 @@ namespace Tango.Transport String m = $"{GetExtendedComponentName()}: Continuous response " + container.Type + " has been aborted: " + container.Error.ToString(); LogManager.Log(m, LogCategory.Info); _pendingRequests.Remove(request); - request.SetException(new ContinuousResponseAbortedException(m)); + OnRequestFailed(request, new ContinuousResponseAbortedException(container, m)); + request.SetException(new ContinuousResponseAbortedException(container, m)); } else { LogManager.Log($"{GetExtendedComponentName()}: Continuous response has returned with error: " + container.Error.ToString(), LogCategory.Warning); _pendingRequests.Remove(request); + OnRequestFailed(request, new ResponseErrorException(container)); request.SetException(new ResponseErrorException(container)); } } @@ -1251,7 +1402,7 @@ namespace Tango.Transport if (_arrivedResponses.Count == 0) { retryCounter--; - var response = SendRequest(new KeepAliveRequest(), KeepAliveTimeout).Result; + var response = SendRequest(new KeepAliveRequest(), new TransportRequestConfig() { Timeout = KeepAliveTimeout }).Result; retryCounter = KeepAliveRetries; } else -- cgit v1.3.1