diff options
41 files changed, 2714 insertions, 805 deletions
diff --git a/Software/DB/PPC/Tango.mdf b/Software/DB/PPC/Tango.mdf Binary files differindex d269178b8..bdc7134c7 100644 --- a/Software/DB/PPC/Tango.mdf +++ b/Software/DB/PPC/Tango.mdf diff --git a/Software/DB/PPC/Tango_log.ldf b/Software/DB/PPC/Tango_log.ldf Binary files differindex e9f5137d0..3d480cd4e 100644 --- a/Software/DB/PPC/Tango_log.ldf +++ b/Software/DB/PPC/Tango_log.ldf diff --git a/Software/DB/Tango.mdf b/Software/DB/Tango.mdf Binary files differindex d270bb895..d765d6513 100644 --- a/Software/DB/Tango.mdf +++ b/Software/DB/Tango.mdf diff --git a/Software/DB/Tango_log.ldf b/Software/DB/Tango_log.ldf Binary files differindex ebd97b83a..99cf5050f 100644 --- a/Software/DB/Tango_log.ldf +++ b/Software/DB/Tango_log.ldf 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<configuration> + <configSections> + <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --> + <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> + </configSections> + <startup> + <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /> + </startup> + <connectionStrings> + <add name="HangFireEntities" connectionString="metadata=res://*/HangFireModel.csdl|res://*/HangFireModel.ssdl|res://*/HangFireModel.msl;provider=System.Data.SqlClient;provider connection string="data source=localhost\SQLEXPRESS;initial catalog=HangFire;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient" /> + </connectionStrings> + <entityFramework> + <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" /> + <providers> + <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> + </providers> + </entityFramework> +</configuration>
\ 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<string> 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 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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. +// </auto-generated> +//------------------------------------------------------------------------------ + +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> 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<EntityContainer>().FirstOrDefault(); +if (container == null) +{ + return string.Empty; +} +#> +//------------------------------------------------------------------------------ +// <auto-generated> +// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine1")#> +// +// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine2")#> +// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine3")#> +// </auto-generated> +//------------------------------------------------------------------------------ + +<# + +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<EntitySet>()) +{ + // 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<EntitySet>()) + { +#> + <#=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<string, string, string, string> 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<MetadataItem>(); + } + + 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<string> types, string sourceFile) + { + ArgumentNotNull(types, "types"); + ArgumentNotNull(sourceFile, "sourceFile"); + + var hash = new HashSet<string>(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<SimpleType> GetEnumItemsToGenerate(IEnumerable<GlobalItem> itemCollection) + { + return GetItemsToGenerate<SimpleType>(itemCollection) + .Where(e => IsEnumType(e)); + } + + public IEnumerable<T> GetItemsToGenerate<T>(IEnumerable<GlobalItem> itemCollection) where T: EdmType + { + return itemCollection + .OfType<T>() + .Where(i => !i.MetadataProperties.Any(p => p.Name == ExternalTypeNameAttributeName)) + .OrderBy(i => i.Name); + } + + public IEnumerable<string> GetAllGlobalItems(IEnumerable<GlobalItem> 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<EdmProperty> GetSimpleProperties(EntityType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); + } + + public IEnumerable<EdmProperty> GetSimpleProperties(ComplexType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); + } + + public IEnumerable<EdmProperty> GetComplexProperties(EntityType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); + } + + public IEnumerable<EdmProperty> GetComplexProperties(ComplexType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); + } + + public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(EntityType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null); + } + + public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(ComplexType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null); + } + + public IEnumerable<NavigationProperty> GetNavigationProperties(EntityType type) + { + return type.NavigationProperties.Where(np => np.DeclaringType == type); + } + + public IEnumerable<NavigationProperty> 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<FunctionParameter>)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<FunctionImportParameter> 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>(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 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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. +// </auto-generated> +//------------------------------------------------------------------------------ + 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<edmx:Edmx Version="3.0" xmlns:edmx="http://schemas.microsoft.com/ado/2009/11/edmx"> + <!-- EF Runtime content --> + <edmx:Runtime> + <!-- SSDL content --> + <edmx:StorageModels> + <Schema Namespace="HangFireModel.Store" Provider="System.Data.SqlClient" ProviderManifestToken="2012" Alias="Self" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns:customannotation="http://schemas.microsoft.com/ado/2013/11/edm/customannotation" xmlns="http://schemas.microsoft.com/ado/2009/11/edm/ssdl"> + <EntityType Name="Job"> + <Key> + <PropertyRef Name="Id" /> + </Key> + <Property Name="Id" Type="bigint" StoreGeneratedPattern="Identity" Nullable="false" /> + <Property Name="StateId" Type="bigint" /> + <Property Name="StateName" Type="nvarchar" MaxLength="20" /> + <Property Name="InvocationData" Type="nvarchar(max)" Nullable="false" /> + <Property Name="Arguments" Type="nvarchar(max)" Nullable="false" /> + <Property Name="CreatedAt" Type="datetime" Nullable="false" /> + <Property Name="ExpireAt" Type="datetime" /> + </EntityType> + <EntityContainer Name="HangFireModelStoreContainer"> + <EntitySet Name="Job" EntityType="Self.Job" Schema="HangFire" store:Type="Tables" /> + </EntityContainer> + </Schema> + </edmx:StorageModels> + <!-- CSDL content --> + <edmx:ConceptualModels> + <Schema Namespace="HangFireModel" Alias="Self" annotation:UseStrongSpatialTypes="false" xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation" xmlns:customannotation="http://schemas.microsoft.com/ado/2013/11/edm/customannotation" xmlns="http://schemas.microsoft.com/ado/2009/11/edm"> + <EntityType Name="Job"> + <Key> + <PropertyRef Name="Id" /> + </Key> + <Property Name="Id" Type="Int64" Nullable="false" annotation:StoreGeneratedPattern="Identity" /> + <Property Name="StateId" Type="Int64" /> + <Property Name="StateName" Type="String" MaxLength="20" FixedLength="false" Unicode="true" /> + <Property Name="InvocationData" Type="String" MaxLength="Max" FixedLength="false" Unicode="true" Nullable="false" /> + <Property Name="Arguments" Type="String" MaxLength="Max" FixedLength="false" Unicode="true" Nullable="false" /> + <Property Name="CreatedAt" Type="DateTime" Nullable="false" Precision="3" /> + <Property Name="ExpireAt" Type="DateTime" Precision="3" /> + </EntityType> + <EntityContainer Name="HangFireEntities" annotation:LazyLoadingEnabled="true"> + <EntitySet Name="Job" EntityType="Self.Job" /> + </EntityContainer> + </Schema> + </edmx:ConceptualModels> + <!-- C-S mapping content --> + <edmx:Mappings> + <Mapping Space="C-S" xmlns="http://schemas.microsoft.com/ado/2009/11/mapping/cs"> + <EntityContainerMapping StorageEntityContainer="HangFireModelStoreContainer" CdmEntityContainer="HangFireEntities"> + <EntitySetMapping Name="Job"> + <EntityTypeMapping TypeName="HangFireModel.Job"> + <MappingFragment StoreEntitySet="Job"> + <ScalarProperty Name="Id" ColumnName="Id" /> + <ScalarProperty Name="StateId" ColumnName="StateId" /> + <ScalarProperty Name="StateName" ColumnName="StateName" /> + <ScalarProperty Name="InvocationData" ColumnName="InvocationData" /> + <ScalarProperty Name="Arguments" ColumnName="Arguments" /> + <ScalarProperty Name="CreatedAt" ColumnName="CreatedAt" /> + <ScalarProperty Name="ExpireAt" ColumnName="ExpireAt" /> + </MappingFragment> + </EntityTypeMapping> + </EntitySetMapping> + </EntityContainerMapping> + </Mapping> + </edmx:Mappings> + </edmx:Runtime> + <!-- EF Designer content (DO NOT EDIT MANUALLY BELOW HERE) --> + <Designer xmlns="http://schemas.microsoft.com/ado/2009/11/edmx"> + <Connection> + <DesignerInfoPropertySet> + <DesignerProperty Name="MetadataArtifactProcessing" Value="EmbedInOutputAssembly" /> + </DesignerInfoPropertySet> + </Connection> + <Options> + <DesignerInfoPropertySet> + <DesignerProperty Name="ValidateOnBuild" Value="true" /> + <DesignerProperty Name="EnablePluralization" Value="false" /> + <DesignerProperty Name="IncludeForeignKeysInModel" Value="false" /> + <DesignerProperty Name="UseLegacyProvider" Value="false" /> + <DesignerProperty Name="CodeGenerationStrategy" Value="None" /> + </DesignerInfoPropertySet> + </Options> + <!-- Diagram content (shape and connector positions) --> + <Diagrams></Diagrams> + </Designer> +</edmx:Edmx>
\ 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<edmx:Edmx Version="3.0" xmlns:edmx="http://schemas.microsoft.com/ado/2009/11/edmx"> + <!-- EF Designer content (DO NOT EDIT MANUALLY BELOW HERE) --> + <edmx:Designer xmlns="http://schemas.microsoft.com/ado/2009/11/edmx"> + <!-- Diagram content (shape and connector positions) --> + <edmx:Diagrams> + <Diagram DiagramId="5e201b921dc74691b29e78300046fa52" Name="Diagram1"> + <EntityTypeShape EntityType="HangFireModel.Job" Width="1.5" PointX="0.75" PointY="0.75" IsExpanded="true" /> + </Diagram> + </edmx:Diagrams> + </edmx:Designer> +</edmx:Edmx>
\ 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<EntityType>(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<ComplexType>(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(); +#> +//------------------------------------------------------------------------------ +// <auto-generated> +// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine1")#> +// +// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine2")#> +// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine3")#> +// </auto-generated> +//------------------------------------------------------------------------------ +<#=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<string, string, string, string> 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<MetadataItem>(); + } + + 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<string> types, string sourceFile) + { + ArgumentNotNull(types, "types"); + ArgumentNotNull(sourceFile, "sourceFile"); + + var hash = new HashSet<string>(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<SimpleType> GetEnumItemsToGenerate(IEnumerable<GlobalItem> itemCollection) + { + return GetItemsToGenerate<SimpleType>(itemCollection) + .Where(e => IsEnumType(e)); + } + + public IEnumerable<T> GetItemsToGenerate<T>(IEnumerable<GlobalItem> itemCollection) where T: EdmType + { + return itemCollection + .OfType<T>() + .Where(i => !i.MetadataProperties.Any(p => p.Name == ExternalTypeNameAttributeName)) + .OrderBy(i => i.Name); + } + + public IEnumerable<string> GetAllGlobalItems(IEnumerable<GlobalItem> 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<EdmProperty> GetSimpleProperties(EntityType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); + } + + public IEnumerable<EdmProperty> GetSimpleProperties(ComplexType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); + } + + public IEnumerable<EdmProperty> GetComplexProperties(EntityType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); + } + + public IEnumerable<EdmProperty> GetComplexProperties(ComplexType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); + } + + public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(EntityType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null); + } + + public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(ComplexType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null); + } + + public IEnumerable<NavigationProperty> GetNavigationProperties(EntityType type) + { + return type.NavigationProperties.Where(np => np.DeclaringType == type); + } + + public IEnumerable<NavigationProperty> 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<FunctionParameter>)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<FunctionImportParameter> 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>(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 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{0A3D27ED-178E-40B7-BB6C-94BD552D4971}</ProjectGuid> + <OutputType>Exe</OutputType> + <RootNamespace>HangFireTest</RootNamespace> + <AssemblyName>HangFireTest</AssemblyName> + <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> + <Deterministic>true</Deterministic> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <PlatformTarget>AnyCPU</PlatformTarget> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <PlatformTarget>AnyCPU</PlatformTarget> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"> + <HintPath>..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll</HintPath> + </Reference> + <Reference Include="EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"> + <HintPath>..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll</HintPath> + </Reference> + <Reference Include="Hangfire.Core, Version=1.7.9.0, Culture=neutral, processorArchitecture=MSIL"> + <HintPath>..\packages\Hangfire.Core.1.7.9\lib\net46\Hangfire.Core.dll</HintPath> + </Reference> + <Reference Include="Hangfire.SqlServer, Version=1.7.9.0, Culture=neutral, processorArchitecture=MSIL"> + <HintPath>..\packages\Hangfire.SqlServer.1.7.9\lib\net45\Hangfire.SqlServer.dll</HintPath> + </Reference> + <Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> + <HintPath>..\packages\Newtonsoft.Json.5.0.1\lib\net45\Newtonsoft.Json.dll</HintPath> + </Reference> + <Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL"> + <HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath> + </Reference> + <Reference Include="System" /> + <Reference Include="System.ComponentModel.DataAnnotations" /> + <Reference Include="System.Core" /> + <Reference Include="System.Runtime.Serialization" /> + <Reference Include="System.Security" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="Microsoft.CSharp" /> + <Reference Include="System.Data" /> + <Reference Include="System.Net.Http" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="CustomLogger.cs" /> + <Compile Include="HangFireModel.Context.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>HangFireModel.Context.tt</DependentUpon> + </Compile> + <Compile Include="HangFireModel.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>HangFireModel.tt</DependentUpon> + </Compile> + <Compile Include="HangFireModel.Designer.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>HangFireModel.edmx</DependentUpon> + </Compile> + <Compile Include="Job.cs"> + <DependentUpon>HangFireModel.tt</DependentUpon> + </Compile> + <Compile Include="Program.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <ItemGroup> + <None Include="App.config" /> + <EntityDeploy Include="HangFireModel.edmx"> + <Generator>EntityModelCodeGenerator</Generator> + <LastGenOutput>HangFireModel.Designer.cs</LastGenOutput> + </EntityDeploy> + <None Include="HangFireModel.edmx.diagram"> + <DependentUpon>HangFireModel.edmx</DependentUpon> + </None> + <None Include="packages.config" /> + </ItemGroup> + <ItemGroup> + <Content Include="HangFireModel.Context.tt"> + <Generator>TextTemplatingFileGenerator</Generator> + <LastGenOutput>HangFireModel.Context.cs</LastGenOutput> + <DependentUpon>HangFireModel.edmx</DependentUpon> + </Content> + <Content Include="HangFireModel.tt"> + <Generator>TextTemplatingFileGenerator</Generator> + <DependentUpon>HangFireModel.edmx</DependentUpon> + <LastGenOutput>HangFireModel.cs</LastGenOutput> + </Content> + </ItemGroup> + <ItemGroup> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> +</Project>
\ 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 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace HangFireTest +{ + using System; + using System.Collections.Generic; + + public partial class Job + { + public long Id { get; set; } + public Nullable<long> 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<System.DateTime> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="EntityFramework" version="6.2.0" targetFramework="net461" /> + <package id="Hangfire.Core" version="1.7.9" targetFramework="net461" /> + <package id="Hangfire.SqlServer" version="1.7.9" targetFramework="net461" /> + <package id="Newtonsoft.Json" version="5.0.1" targetFramework="net461" /> + <package id="Owin" version="1.0" targetFramework="net461" /> +</packages>
\ 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<JobResponse>(new JobResponse() { - }, request.Container.Token, false, ErrorCode.ContinuousResponseAborted); + }, request.Container.Token, new TransportResponseConfig() { ErrorCode = ErrorCode.ContinuousResponseAborted }); } else { Transporter.SendResponse<ResumeCurrentJobResponse>(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<AbortJobResponse>(new AbortJobResponse(), request.Container.Token); + _cancelJob = true; + Transporter.SendResponse<AbortJobResponse>(new AbortJobResponse(), request.Container.Token); // _jobAbortCounter = 0; //} //else @@ -930,7 +935,7 @@ namespace Tango.Emulations.Emulators Thread.Sleep(30); } - Transporter.SendResponse<MotorHomingResponse>(new MotorHomingResponse() { MaxProgress = 100, Progress = 100 }, request.Container.Token, true); + Transporter.SendResponse<MotorHomingResponse>(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<DispenserHomingResponse>(new DispenserHomingResponse() { MaxProgress = 100, Progress = 100 }, request.Container.Token, true); + Transporter.SendResponse<DispenserHomingResponse>(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<SetDigitalOutResponse>(new SetDigitalOutResponse(), request.Container.Token, null, ErrorCode.InvalidDigitalPinNumber); + Transporter.SendResponse<SetDigitalOutResponse>(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<ResumeCurrentJobResponse>(new ResumeCurrentJobResponse(), request.Container.Token, true, ErrorCode.NoJobInProgress); + Transporter.SendResponse<ResumeCurrentJobResponse>(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<GetFilesResponse>(response, request.Container.Token, false, failed ? ErrorCode.GeneralError : ErrorCode.None, msg); + Transporter.SendResponse<GetFilesResponse>(response, request.Container.Token, new TransportResponseConfig() { ErrorCode = failed ? ErrorCode.GeneralError : ErrorCode.None, ErrorMessage = msg }); } private void HandleFileDownloadRequest(TangoMessage<FileDownloadRequest> 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<FileChunkDownloadRequest> request) @@ -1336,7 +1341,7 @@ namespace Tango.Emulations.Emulators Transporter.SendResponse<FileChunkDownloadResponse>(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<CreateResponse>(new CreateResponse(), request.Container.Token, null, ErrorCode.FileRequestInvalidParameter, ex.Message); + Transporter.SendResponse<CreateResponse>(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<DeleteResponse>(new DeleteResponse(), request.Container.Token, null, ErrorCode.FileRequestInvalidParameter, ex.Message); + Transporter.SendResponse<DeleteResponse>(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<FileChunkUploadResponse>(new FileChunkUploadResponse(), request.Container.Token, null, ErrorCode.GeneralError, ex.Message); + Transporter.SendResponse<FileChunkUploadResponse>(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<ValidateVersionResponse>(new ValidateVersionResponse(), request.Container.Token, null, ErrorCode.GeneralError, ex.Message); + Transporter.SendResponse<ValidateVersionResponse>(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<ActivateVersionResponse>(new ActivateVersionResponse(), request.Container.Token, null, ErrorCode.GeneralError, ex.Message); + Transporter.SendResponse<ActivateVersionResponse>(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<ContinueThreadLoadingResponse>(new ContinueThreadLoadingResponse(), request.Container.Token, null, ErrorCode.GeneralError, "StartThreadLoadingRequest was never sent."); + await Transporter.SendResponse<ContinueThreadLoadingResponse>(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<ExternalBridgeLogoutRequest, ExternalBridgeLogoutResponse>(new ExternalBridgeLogoutRequest(), TimeSpan.FromSeconds(3)); + await SendRequest<ExternalBridgeLogoutRequest, ExternalBridgeLogoutResponse>(new ExternalBridgeLogoutRequest(), new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(3), ShouldLog = true }); } } catch (Exception ex) @@ -265,7 +265,7 @@ namespace Tango.Integration.ExternalBridge (reason) => { //Decline - SendResponse<ExternalBridgeLoginResponse>(new ExternalBridgeLoginResponse(), container.Token, false, ErrorCode.GeneralError, reason); + SendResponse<ExternalBridgeLoginResponse>(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<StartDiagnosticsResponse>(new StartDiagnosticsResponse(), _diagnosticsToken, true); + SendResponse<StartDiagnosticsResponse>(new StartDiagnosticsResponse(), _diagnosticsToken, new TransportResponseConfig() { Completed = true }); _diagnosticsToken = null; SendResponse<StopDiagnosticsResponse>(new StopDiagnosticsResponse(), container.Token); } @@ -390,7 +394,7 @@ namespace Tango.Integration.ExternalBridge if (_debugLogsToken != null) { RequiresDebugLogs = false; - SendResponse<StartDebugLogResponse>(new StartDebugLogResponse(), _debugLogsToken, true); + SendResponse<StartDebugLogResponse>(new StartDebugLogResponse(), _debugLogsToken, new TransportResponseConfig() { Completed = true }); _debugLogsToken = null; SendResponse<StopDebugLogResponse>(new StopDebugLogResponse(), container.Token); } @@ -408,7 +412,7 @@ namespace Tango.Integration.ExternalBridge if (_eventsToken != null) { RequiresEventsNotification = false; - SendResponse<StartEventsNotificationResponse>(new StartEventsNotificationResponse(), _eventsToken, true); + SendResponse<StartEventsNotificationResponse>(new StartEventsNotificationResponse(), _eventsToken, new TransportResponseConfig() { Completed = true }); _eventsToken = null; SendResponse<StopEventsNotificationResponse>(new StopEventsNotificationResponse(), container.Token); } @@ -426,7 +430,7 @@ namespace Tango.Integration.ExternalBridge if (_machineStatusToken != null) { RequiresMachineStatusUpdate = false; - SendResponse<StartMachineStatusUpdateResponse>(new StartMachineStatusUpdateResponse(), _machineStatusToken, true); + SendResponse<StartMachineStatusUpdateResponse>(new StartMachineStatusUpdateResponse(), _machineStatusToken, new TransportResponseConfig() { Completed = true }); _machineStatusToken = null; SendResponse<StopMachineStatusUpdateResponse>(new StopMachineStatusUpdateResponse(), container.Token); } @@ -444,7 +448,7 @@ namespace Tango.Integration.ExternalBridge if (_applicationLogsToken != null) { RequiresApplicationLogs = false; - SendResponse<StartApplicationLogsResponse>(new StartApplicationLogsResponse(), _applicationLogsToken, true); + SendResponse<StartApplicationLogsResponse>(new StartApplicationLogsResponse(), _applicationLogsToken, new TransportResponseConfig() { Completed = true }); _applicationLogsToken = null; SendResponse<StopApplicationLogsResponse>(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<ExternalBridgeLoginRequest, ExternalBridgeLoginResponse>(login); + var response = await SendRequest<ExternalBridgeLoginRequest, ExternalBridgeLoginResponse>(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<ExternalBridgeLoginRequest, ExternalBridgeLoginResponse>(login); + var response = await SendRequest<ExternalBridgeLoginRequest, ExternalBridgeLoginResponse>(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<StartApplicationLogsRequest, StartApplicationLogsResponse>(request).ObserveOn(new NewThreadScheduler()) + SendContinuousRequest<StartApplicationLogsRequest, StartApplicationLogsResponse>(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<StopApplicationLogsRequest, StopApplicationLogsResponse>(req); - LogResponseReceived(res.Message); - } - catch (Exception ex) - { - LogRequestFailed(req, ex); + var res = await SendRequest<StopApplicationLogsRequest, StopApplicationLogsResponse>(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<ExternalBridgeLogoutRequest, ExternalBridgeLogoutResponse>(request); - LogResponseReceived(response.Message); + var response = await SendRequest<ExternalBridgeLogoutRequest, ExternalBridgeLogoutResponse>(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 @@ -155,26 +155,6 @@ namespace Tango.Integration.Operation event EventHandler<ResumingJobEventArgs> ResumingJob; /// <summary> - /// Occurs when a request has been sent. - /// </summary> - event EventHandler<IMessage> RequestSent; - - /// <summary> - /// Occurs when a request response has been received. - /// </summary> - event EventHandler<IMessage> ResponseReceived; - - /// <summary> - /// Occurs when a response has been sent. - /// </summary> - event EventHandler<IMessage> ResponseSent; - - /// <summary> - /// Occurs when a request has failed. - /// </summary> - event EventHandler<RequestFailedEventArgs> RequestFailed; - - /// <summary> /// Occurs when there is new diagnostics data available. /// </summary> event EventHandler<StartDiagnosticsResponse> DiagnosticsDataAvailable; 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 @@ -174,26 +174,6 @@ namespace Tango.Integration.Operation public event EventHandler<CartridgeValidationEventArgs> CartridgeValidationRequestReceived; /// <summary> - /// Occurs when a request has been sent. - /// </summary> - public event EventHandler<IMessage> RequestSent; - - /// <summary> - /// Occurs when a response has been sent. - /// </summary> - public event EventHandler<IMessage> ResponseSent; - - /// <summary> - /// Occurs when a request has timed out. - /// </summary> - public event EventHandler<RequestFailedEventArgs> RequestFailed; - - /// <summary> - /// Occurs when a request response has been received. - /// </summary> - public event EventHandler<IMessage> ResponseReceived; - - /// <summary> /// Reports about the job printing preparation progress. /// </summary> public event EventHandler<PreparingJobProgressEventArgs> PreparingJobProgress; @@ -582,13 +562,12 @@ namespace Tango.Integration.Operation bool responseLogged = false; _diagnosticsSent = true; - SendContinuousRequest<StartDiagnosticsRequest, StartDiagnosticsResponse>(request).ObserveOn(new NewThreadScheduler()).Subscribe( + SendContinuousRequest<StartDiagnosticsRequest, StartDiagnosticsResponse>(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<StopDiagnosticsRequest, StopDiagnosticsResponse>(req); - LogResponseReceived(res.Message); - } - catch (Exception ex) - { - LogRequestFailed(req, ex); + var res = await SendRequest<StopDiagnosticsRequest, StopDiagnosticsResponse>(req, new TransportRequestConfig() { ShouldLog = true }); } + catch { } } } } @@ -652,33 +619,25 @@ namespace Tango.Integration.Operation bool responseLogged = false; _eventsSent = true; - SendContinuousRequest<StartEventsNotificationRequest, StartEventsNotificationResponse>(request).ObserveOn(new NewThreadScheduler()).Subscribe( + SendContinuousRequest<StartEventsNotificationRequest, StartEventsNotificationResponse>(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<StopEventsNotificationRequest, StopEventsNotificationResponse>(req); - LogResponseReceived(res.Message); - } - catch (Exception ex) - { - LogRequestFailed(req, ex); + var res = await SendRequest<StopEventsNotificationRequest, StopEventsNotificationResponse>(req, new TransportRequestConfig() { ShouldLog = true }); } + catch { } } } } @@ -715,14 +669,13 @@ namespace Tango.Integration.Operation bool responseLogged = false; _debugSent = true; - SendContinuousRequest<StartDebugLogRequest, StartDebugLogResponse>(request).ObserveOn(new NewThreadScheduler()) + SendContinuousRequest<StartDebugLogRequest, StartDebugLogResponse>(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<StopDebugLogRequest, StopDebugLogResponse>(req); - LogResponseReceived(res.Message); - } - catch (Exception ex) - { - LogRequestFailed(req, ex); + var res = await SendRequest<StopDebugLogRequest, StopDebugLogResponse>(req, new TransportRequestConfig() { ShouldLog = true }); } + catch { } } } } @@ -779,33 +720,25 @@ namespace Tango.Integration.Operation bool responseLogged = false; _machineStatusSent = true; - SendContinuousRequest<StartMachineStatusUpdateRequest, StartMachineStatusUpdateResponse>(request).ObserveOn(new NewThreadScheduler()).Subscribe( + SendContinuousRequest<StartMachineStatusUpdateRequest, StartMachineStatusUpdateResponse>(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<StopMachineStatusUpdateRequest, StopMachineStatusUpdateResponse>(req); - LogResponseReceived(res.Message); - } - catch (Exception ex) - { - LogRequestFailed(req, ex); + var res = await SendRequest<StopMachineStatusUpdateRequest, StopMachineStatusUpdateResponse>(req, new TransportRequestConfig() { ShouldLog = true }); } + catch { } } } } @@ -842,33 +770,25 @@ namespace Tango.Integration.Operation bool responseLogged = false; _threadLoadingSent = true; - SendContinuousRequest<StartThreadLoadingRequest, StartThreadLoadingResponse>(request).ObserveOn(new NewThreadScheduler()).Subscribe( + SendContinuousRequest<StartThreadLoadingRequest, StartThreadLoadingResponse>(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<StopThreadLoadingRequest, StopThreadLoadingResponse>(req); - LogResponseReceived(res.Message); - } - catch (Exception ex) - { - LogRequestFailed(req, ex); + var res = await SendRequest<StopThreadLoadingRequest, StopThreadLoadingResponse>(req, new TransportRequestConfig() { ShouldLog = true }); } + catch { } } } } @@ -1019,7 +934,7 @@ namespace Tango.Integration.Operation var r = SendRequest<ContinueThreadLoadingRequest, ContinueThreadLoadingResponse>(new ContinueThreadLoadingRequest() { ProcessParameters = process, - }).Result; + }, new TransportRequestConfig() { ShouldLog = true }).Result; } catch (Exception ex) { @@ -1041,24 +956,6 @@ namespace Tango.Integration.Operation } /// <summary> - /// Called when the request has been sent - /// </summary> - /// <param name="response">The request.</param> - protected virtual void OnRequestSent(IMessage request) - { - RequestSent?.Invoke(this, request); - } - - /// <summary> - /// Called when the response has been received - /// </summary> - /// <param name="response">The response.</param> - protected virtual void OnResponseReceived(IMessage response) - { - ResponseReceived?.Invoke(this, response); - } - - /// <summary> /// Called when a new request has been received. /// </summary> /// <param name="container">The request.</param> @@ -1077,24 +974,6 @@ namespace Tango.Integration.Operation } /// <summary> - /// Called when the response has been sent - /// </summary> - /// <param name="response">The response.</param> - protected virtual void OnResponseSent(IMessage response) - { - ResponseSent?.Invoke(this, response); - } - - /// <summary> - /// Called when the request has been failed - /// </summary> - /// <param name="request">The request.</param> - protected virtual void OnRequestFailed(IMessage request, Exception exception) - { - RequestFailed?.Invoke(this, new RequestFailedEventArgs(request, exception)); - } - - /// <summary> /// Called when the machine status has been changed /// </summary> /// <param name="status">The status.</param> @@ -1279,19 +1158,14 @@ namespace Tango.Integration.Operation if (State == TransportComponentState.Connected) { DisconnectRequest request = new DisconnectRequest(); - LogRequestSent(request); try { - var response = await SendRequest<DisconnectRequest, DisconnectResponse>(request); - LogResponseReceived(response.Message); + var response = await SendRequest<DisconnectRequest, DisconnectResponse>(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<ConnectRequest, ConnectResponse>(request); + var response = await SendRequest<ConnectRequest, ConnectResponse>(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<CurrentJobRequest, CurrentJobResponse>(new CurrentJobRequest()); + var res = await SendRequest<CurrentJobRequest, CurrentJobResponse>(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<AbortJobRequest, AbortJobResponse>(new AbortJobRequest()); + var result = await SendRequest<AbortJobRequest, AbortJobResponse>(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<ResumeCurrentJobRequest, ResumeCurrentJobResponse>(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; - } - } + SendContinuousRequest<ResumeCurrentJobRequest, ResumeCurrentJobResponse>(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); + handler.RaiseStatusReceived(response.Message.Status); - if (!responseLogged) - { - Status = MachineStatuses.GettingReady; - responseLogged = true; - RunningJob = originalJob; - OnPrintingStarted(handler, originalJob); - LogResponseReceived(response.Message); - } + 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 (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(); - } - }); + 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. /// </summary> /// <param name="message">The message.</param> - 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); + // } + //} /// <summary> /// Logs the request failed. /// </summary> /// <param name="message">The message.</param> - 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); + //} /// <summary> /// Logs the response received. /// </summary> /// <param name="message">The message.</param> - 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); + // } + //} /// <summary> /// 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<JobRequest, JobResponse>(request, null, ContinuousRequestTimeout).Subscribe((response) => + SendContinuousRequest<JobRequest, JobResponse>(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<AbortJobRequest, AbortJobResponse>(new AbortJobRequest()); + var result = await SendRequest<AbortJobRequest, AbortJobResponse>(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<JobRequest, JobResponse>(request, null, ContinuousRequestTimeout.Add(TimeSpan.FromSeconds(3))).Subscribe((response) => - { - if (!completed) - { - handler.RaiseStatusReceived(response.Message.Status); - _last_job_status = handler.Status; + SendContinuousRequest<JobRequest, JobResponse>(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 (response.Message.Status.Progress > 0) + { + if (oldKeepAlive != UseKeepAlive) + { + UseKeepAlive = oldKeepAlive; + } - if (_jobActualStartDate == null) - { - _jobActualStartDate = DateTime.UtcNow; - } - } + if (_jobActualStartDate == null) + { + _jobActualStartDate = DateTime.UtcNow; + } + } - if (!responseLogged) - { - requestSent = true; - responseLogged = true; - LogResponseReceived(response.Message); - } + 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; - } - } - } - } + 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; + }, (ex) => + { + if (!completed) + { + completed = true; - UseKeepAlive = oldKeepAlive; + UseKeepAlive = oldKeepAlive; - if (!(ex is ContinuousResponseAbortedException)) - { - if (Status != MachineStatuses.Disconnected) - { - Status = MachineStatuses.ReadyToDye; - } + 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; + 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; + UseKeepAlive = oldKeepAlive; - Status = MachineStatuses.ReadyToDye; - SaveLastJobLiquidQuantities(clonedJob, originalJob.Machine.Configuration, processParameters, handler); - OnPrintingCompleted(handler, clonedJob); - handler.RaiseCompleted(); - } - }); + 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<StubAbortJobRequest, StubAbortJobResponse>(new StubAbortJobRequest()); + var result = await SendRequest<StubAbortJobRequest, StubAbortJobResponse>(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<StubJobRequest, StubJobResponse>(request, null, ContinuousRequestTimeout).Subscribe((response) => - { - handler.RaiseStatusReceived(response.Message.Status); + SendContinuousRequest<StubJobRequest, StubJobResponse>(request, new TransportContinuousRequestConfig() { ContinuousTimeout = ContinuousRequestTimeout, ShouldLog = true }).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; + 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); - LogRequestFailed(request, ex); - } - } - else - { - Status = MachineStatuses.ReadyToDye; - } - }, () => - { - Status = MachineStatuses.ReadyToDye; - OnPrintingCompleted(handler, originalJob); - handler.RaiseCompleted(); - }); + 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<UploadProcessParametersRequest, UploadProcessParametersResponse>(request); - LogResponseReceived(response); + response = await SendRequest<UploadProcessParametersRequest, UploadProcessParametersResponse>(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<UploadHardwareConfigurationRequest, UploadHardwareConfigurationResponse>(request); - LogResponseReceived(response); + response = await SendRequest<UploadHardwareConfigurationRequest, UploadHardwareConfigurationResponse>(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<MotorJoggingRequest, MotorJoggingResponse>(request); - LogResponseReceived(response); + response = await SendRequest<MotorJoggingRequest, MotorJoggingResponse>(request, new TransportRequestConfig() { ShouldLog = true }); } catch (Exception ex) { - LogRequestFailed(request, ex); throw ex; } @@ -3160,8 +3009,7 @@ namespace Tango.Integration.Operation /// <returns></returns> public async Task<MotorAbortJoggingResponse> StopMotorJogging(MotorAbortJoggingRequest request) { - LogRequestSent(request); - return await SendRequest<MotorAbortJoggingRequest, MotorAbortJoggingResponse>(request); + return await SendRequest<MotorAbortJoggingRequest, MotorAbortJoggingResponse>(request, new TransportRequestConfig() { ShouldLog = true }); } /// <summary> @@ -3171,8 +3019,7 @@ namespace Tango.Integration.Operation /// <returns></returns> public IObservable<MotorHomingResponse> StartMotorHoming(MotorHomingRequest request) { - LogRequestSent(request); - return SendContinuousRequest<MotorHomingRequest, MotorHomingResponse>(request, null, ContinuousRequestTimeout).Select(x => x.Message); + return SendContinuousRequest<MotorHomingRequest, MotorHomingResponse>(request, new TransportContinuousRequestConfig() { ContinuousTimeout = ContinuousRequestTimeout, ShouldLog = true }).Select(x => x.Message); } /// <summary> @@ -3182,8 +3029,7 @@ namespace Tango.Integration.Operation /// <returns></returns> public async Task<MotorAbortHomingResponse> StopMotorHoming(MotorAbortHomingRequest request) { - LogRequestSent(request); - return await SendRequest<MotorAbortHomingRequest, MotorAbortHomingResponse>(request); + return await SendRequest<MotorAbortHomingRequest, MotorAbortHomingResponse>(request, new TransportRequestConfig() { ShouldLog = true }); } /// <summary> @@ -3193,8 +3039,7 @@ namespace Tango.Integration.Operation /// <returns></returns> public async Task<DispenserJoggingResponse> StartDispenserJogging(DispenserJoggingRequest request) { - LogRequestSent(request); - return await SendRequest<DispenserJoggingRequest, DispenserJoggingResponse>(request); + return await SendRequest<DispenserJoggingRequest, DispenserJoggingResponse>(request, new TransportRequestConfig() { ShouldLog = true }); } /// <summary> @@ -3204,8 +3049,7 @@ namespace Tango.Integration.Operation /// <returns></returns> public async Task<DispenserAbortJoggingResponse> StopDispenserJogging(DispenserAbortJoggingRequest request) { - LogRequestSent(request); - return await SendRequest<DispenserAbortJoggingRequest, DispenserAbortJoggingResponse>(request); + return await SendRequest<DispenserAbortJoggingRequest, DispenserAbortJoggingResponse>(request, new TransportRequestConfig() { ShouldLog = true }); } /// <summary> @@ -3215,8 +3059,7 @@ namespace Tango.Integration.Operation /// <returns></returns> public IObservable<DispenserHomingResponse> StartDispenserHoming(DispenserHomingRequest request) { - LogRequestSent(request); - return SendContinuousRequest<DispenserHomingRequest, DispenserHomingResponse>(request, null, ContinuousRequestTimeout).Select(x => x.Message); + return SendContinuousRequest<DispenserHomingRequest, DispenserHomingResponse>(request, new TransportContinuousRequestConfig() { ContinuousTimeout = ContinuousRequestTimeout, ShouldLog = true }).Select(x => x.Message); } /// <summary> @@ -3226,8 +3069,7 @@ namespace Tango.Integration.Operation /// <returns></returns> public async Task<DispenserAbortHomingResponse> StopDispenserHoming(DispenserAbortHomingRequest request) { - LogRequestSent(request); - return await SendRequest<DispenserAbortHomingRequest, DispenserAbortHomingResponse>(request); + return await SendRequest<DispenserAbortHomingRequest, DispenserAbortHomingResponse>(request, new TransportRequestConfig() { ShouldLog = true }); } /// <summary> @@ -3237,8 +3079,7 @@ namespace Tango.Integration.Operation /// <returns></returns> public async Task<SetDigitalOutResponse> SetDigitalOut(SetDigitalOutRequest request) { - LogRequestSent(request); - return await SendRequest<SetDigitalOutRequest, SetDigitalOutResponse>(request); + return await SendRequest<SetDigitalOutRequest, SetDigitalOutResponse>(request, new TransportRequestConfig() { ShouldLog = true }); } /// <summary> @@ -3248,8 +3089,7 @@ namespace Tango.Integration.Operation /// <returns></returns> public async Task<ThreadJoggingResponse> StartThreadJogging(ThreadJoggingRequest request) { - LogRequestSent(request); - return await SendRequest<ThreadJoggingRequest, ThreadJoggingResponse>(request); + return await SendRequest<ThreadJoggingRequest, ThreadJoggingResponse>(request, new TransportRequestConfig() { ShouldLog = true }); } /// <summary> @@ -3259,8 +3099,7 @@ namespace Tango.Integration.Operation /// <returns></returns> public async Task<ThreadAbortJoggingResponse> StopThreadJogging(ThreadAbortJoggingRequest request) { - LogRequestSent(request); - return await SendRequest<ThreadAbortJoggingRequest, ThreadAbortJoggingResponse>(request); + return await SendRequest<ThreadAbortJoggingRequest, ThreadAbortJoggingResponse>(request, new TransportRequestConfig() { ShouldLog = true }); } /// <summary> @@ -3270,8 +3109,7 @@ namespace Tango.Integration.Operation /// <returns></returns> public async Task<SetComponentValueResponse> SetComponentValue(SetComponentValueRequest request) { - LogRequestSent(request); - return await SendRequest<SetComponentValueRequest, SetComponentValueResponse>(request); + return await SendRequest<SetComponentValueRequest, SetComponentValueResponse>(request, new TransportRequestConfig() { ShouldLog = true }); } /// <summary> @@ -3292,13 +3130,10 @@ namespace Tango.Integration.Operation try { - LogRequestSent(request); - response = await SendRequest<SetHeaterStateRequest, SetHeaterStateResponse>(request); - LogResponseReceived(response); + response = await SendRequest<SetHeaterStateRequest, SetHeaterStateResponse>(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<SetBlowerStateRequest, SetBlowerStateResponse>(request); - LogResponseReceived(response); + response = await SendRequest<SetBlowerStateRequest, SetBlowerStateResponse>(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<SetValveStateRequest, SetValveStateResponse>(request); - LogResponseReceived(response); + response = await SendRequest<SetValveStateRequest, SetValveStateResponse>(request, new TransportRequestConfig() { ShouldLog = true }); } catch (Exception ex) { - LogRequestFailed(request, ex); throw ex; } @@ -3375,8 +3204,7 @@ namespace Tango.Integration.Operation public async Task<ResolveEventResponse> ResolveEvent(PMR.Diagnostics.EventType eventType) { ResolveEventRequest request = new ResolveEventRequest() { Type = eventType }; - LogRequestSent(request); - return await SendRequest<ResolveEventRequest, ResolveEventResponse>(request); + return await SendRequest<ResolveEventRequest, ResolveEventResponse>(request, new TransportRequestConfig() { ShouldLog = true }); } /// <summary> @@ -3396,13 +3224,10 @@ namespace Tango.Integration.Operation Value = 0x0 }; - LogRequestSent(request); - response = await SendRequest<StubFpgaWriteRegRequest, StubFpgaWriteRegResponse>(request); - LogResponseReceived(response); + response = await SendRequest<StubFpgaWriteRegRequest, StubFpgaWriteRegResponse>(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<StubFpgaWriteRegRequest, StubFpgaWriteRegResponse>(request); - LogResponseReceived(response); + response = await SendRequest<StubFpgaWriteRegRequest, StubFpgaWriteRegResponse>(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<ValidateVersionRequest, ValidateVersionResponse>(validateRequest, TimeSpan.FromSeconds(10)).Result; + var validateResponse = SendRequest<ValidateVersionRequest, ValidateVersionResponse>(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<ActivateVersionRequest, ActivateVersionResponse>(activateRequest, TimeSpan.FromSeconds(10)).Result; + var activateResponse = SendRequest<ActivateVersionRequest, ActivateVersionResponse>(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<ValidateVersionRequest, ValidateVersionResponse>(validateRequest, TimeSpan.FromSeconds(10)); + await SendRequest<ValidateVersionRequest, ValidateVersionResponse>(validateRequest, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(10), ShouldLog = true }); } /// <summary> @@ -3799,7 +3621,7 @@ namespace Tango.Integration.Operation { var activateRequest = new ActivateVersionRequest(); activateRequest.Path = path; - await SendRequest<ActivateVersionRequest, ActivateVersionResponse>(activateRequest, TimeSpan.FromSeconds(10)); + await SendRequest<ActivateVersionRequest, ActivateVersionResponse>(activateRequest, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(10), ShouldLog = true }); } /// <summary> @@ -3819,7 +3641,7 @@ namespace Tango.Integration.Operation { _isPowerDownRequestInProgress = false; Thread.Sleep(2000); - var r = SendRequest<AbortPowerDownRequest, AbortPowerDownResponse>(new AbortPowerDownRequest()).Result; + var r = SendRequest<AbortPowerDownRequest, AbortPowerDownResponse>(new AbortPowerDownRequest(), new TransportRequestConfig() { ShouldLog = true }).Result; })); Task.Factory.StartNew(() => @@ -3828,31 +3650,31 @@ namespace Tango.Integration.Operation bool firstResponse = true; - SendContinuousRequest<StartPowerDownRequest, StartPowerDownResponse>(new StartPowerDownRequest(), RequestTimeout, TimeSpan.FromSeconds(5)).ObserveOn(new NewThreadScheduler()).Subscribe((response) => - { - if (firstResponse) - { - firstResponse = false; - Status = MachineStatuses.ShuttingDown; - } + SendContinuousRequest<StartPowerDownRequest, StartPowerDownResponse>(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(); - } - }); + 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/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 - - /// <summary> - /// Logs the request sent. - /// </summary> - /// <param name="message">The message.</param> - protected void LogRequestSent(IMessage message) - { - LogManager.Log($"{_transporter.ComponentName}: Sending request '{message.GetType().Name}'...\n{message.ToJsonString()}", LogCategory.Debug); - } - - /// <summary> - /// Logs the request failed. - /// </summary> - /// <param name="message">The message.</param> - protected void LogRequestFailed(IMessage message, Exception ex) - { - LogManager.Log($"{_transporter.ComponentName}: Request failed '{message.GetType().Name}'...\n{message.ToJsonString()}\n{ex.ToString()}", LogCategory.Error); - } - - /// <summary> - /// Logs the response received. - /// </summary> - /// <param name="message">The message.</param> - protected void LogResponseReceived(IMessage message) - { - LogManager.Log($"{_transporter.ComponentName}: Response received '{message.GetType().Name}'...\n{message.ToJsonString()}", LogCategory.Debug); - } - - #endregion - #region Public Methods /// <summary> @@ -134,11 +103,10 @@ namespace Tango.Integration.Storage try { - response = await _transporter.SendRequest<GetStorageInfoRequest, GetStorageInfoResponse>(request); + response = await _transporter.SendRequest<GetStorageInfoRequest, GetStorageInfoResponse>(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<GetFilesRequest, GetFilesResponse>(request); + response = await _transporter.SendRequest<GetFilesRequest, GetFilesResponse>(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<FileUploadRequest, FileUploadResponse>(request); + var fileUploadResponse = await _transporter.SendRequest<FileUploadRequest, FileUploadResponse>(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<FileDownloadRequest, FileDownloadResponse>(request); + var fileDownloadResponse = await _transporter.SendRequest<FileDownloadRequest, FileDownloadResponse>(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 }); } /// <summary> @@ -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 @@ <Compile Include="Operation\JobHandler.cs" /> <Compile Include="Operation\MachineStatuses.cs" /> <Compile Include="Operation\PrintingEventArgs.cs" /> - <Compile Include="Operation\RequestFailedEventArgs.cs" /> <Compile Include="Operation\ResumingJobEventArgs.cs" /> <Compile Include="Operation\RunningJobStatus.cs" /> <Compile Include="ExternalBridge\ExternalBridgeScanner.cs" /> @@ -209,7 +208,7 @@ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <ProjectExtensions> <VisualStudio> - <UserProperties BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UseGlobalSettings="False" BuildVersion_StartDate="2000/1/1" /> + <UserProperties BuildVersion_StartDate="2000/1/1" BuildVersion_UseGlobalSettings="False" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" /> </VisualStudio> </ProjectExtensions> </Project>
\ 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. /// </summary> /// <param name="request">The request.</param> - /// <param name="timeout">Optional timeout. If not specified will use the <see cref="RequestTimeout"/>.</param> + /// <param name="config">Request configuration.</param> /// <returns></returns> - Task<IMessage> SendRequest(IMessage request, TimeSpan? timeout = null); + Task<IMessage> SendRequest(IMessage request, TransportRequestConfig config = null); /// <summary> /// Sends the request. /// </summary> - /// <param name="container">The container.</param> + /// <param name="config">Request configuration.</param> /// <returns></returns> - Task<MessageContainer> SendRequest(MessageContainer container); + Task<MessageContainer> SendRequest(MessageContainer container, TransportRequestConfig config = null); /// <summary> - /// Sends a continuous request. + /// Sends a request. /// </summary> - /// <param name="container">The container.</param> + /// <typeparam name="Request">The type of the request.</typeparam> + /// <typeparam name="Response">The type of the response.</typeparam> + /// <param name="request">The request.</param> + /// <param name="config">Request configuration.</param> /// <returns></returns> - IObservable<MessageContainer> SendContinuousRequest(MessageContainer container); + Task<TangoMessage<Response>> SendRequest<Request, Response>(TangoMessage<Request> request, TransportRequestConfig config = null) where Request : IMessage<Request> where Response : IMessage<Response>; /// <summary> - /// Sends the response. + /// Sends a continuous request. /// </summary> /// <param name="container">The container.</param> + /// <param name="config">Request configuration.</param> /// <returns></returns> - Task SendResponse(MessageContainer container); + IObservable<MessageContainer> SendContinuousRequest(MessageContainer container, TransportContinuousRequestConfig config = null); /// <summary> - /// Sends the response. + /// Sends a request and expecting multiple response messages. /// </summary> - /// <param name="response">The response.</param> - /// <param name="token">The token.</param> - /// <param name="completed">The completed.</param> - /// <param name="errorCode">The error code.</param> - /// <param name="errorMessage">The error message.</param> + /// <param name="config">Request configuration.</param> /// <returns></returns> - Task SendResponse(IMessage response, String token, bool? completed = null, ErrorCode? errorCode = null, String errorMessage = null); + IObservable<IMessage> SendContinuousRequest(IMessage request, TransportContinuousRequestConfig config = null); /// <summary> /// Sends a request and expecting multiple response messages. /// </summary> - /// <param name="request">The request.</param> + /// <typeparam name="Request">The type of the request.</typeparam> + /// <typeparam name="Response">The type of the response.</typeparam> + /// <param name="config">Request configuration.</param> /// <returns></returns> - IObservable<IMessage> SendContinuousRequest(IMessage request, TimeSpan? timeout = null); + IObservable<TangoMessage<Response>> SendContinuousRequest<Request, Response>(TangoMessage<Request> request, TransportContinuousRequestConfig config = null) where Request : IMessage<Request> where Response : IMessage<Response>; /// <summary> - /// Sends a request. + /// Sends the response. /// </summary> - /// <typeparam name="Request">The type of the request.</typeparam> - /// <typeparam name="Response">The type of the response.</typeparam> - /// <param name="request">The request.</param> - /// <param name="timeout">Optional timeout. If not specified will use the <see cref="RequestTimeout"/>.</param> + /// <param name="container">The container.</param> /// <returns></returns> - Task<TangoMessage<Response>> SendRequest<Request, Response>(TangoMessage<Request> request, TimeSpan? timeout = null) where Request : IMessage<Request> where Response : IMessage<Response>; + Task SendResponse(MessageContainer container); /// <summary> - /// Sends a request and expecting multiple response messages. + /// Sends the response. /// </summary> - /// <typeparam name="Request">The type of the request.</typeparam> - /// <typeparam name="Response">The type of the response.</typeparam> - /// <param name="request">The request.</param> + /// <param name="response">Request token.</param> + /// <param name="config">Response configuration.</param> /// <returns></returns> - IObservable<TangoMessage<Response>> SendContinuousRequest<Request, Response>(TangoMessage<Request> request, TimeSpan? firstTimeout = null, TimeSpan? continousTimeout = null) where Request : IMessage<Request> where Response : IMessage<Response>; + Task SendResponse(IMessage response, String token, TransportResponseConfig config = null); /// <summary> /// Sends a response. @@ -109,22 +107,20 @@ namespace Tango.Transport Task SendResponse<Response>(TangoMessage<Response> response) where Response : IMessage<Response>; /// <summary> - /// Sends a response for the specified token. + /// Sends a response. /// </summary> /// <typeparam name="Response">The type of the response.</typeparam> /// <param name="response">The response.</param> - /// <param name="token">The token.</param> - /// <param name="completed">The completed.</param> - /// <param name="errorCode">The error code.</param> - /// <param name="errorMessage">The error message.</param> + /// <param name="token">Request token.</param> + /// <param name="config">Response configuration.</param> /// <returns></returns> - Task SendResponse<Response>(TangoMessage<Response> response, String token, bool? completed = null, ErrorCode? errorCode = null, String errorMessage = null) where Response : IMessage<Response>; + Task SendResponse<Response>(TangoMessage<Response> response, String token, TransportResponseConfig config = null) where Response : IMessage<Response>; /// <summary> /// Sends a general error response agnostic to the type of request. /// </summary> /// <param name="exception">The exception.</param> - /// <param name="token">The token.</param> + /// <param name="token">Request token.</param> /// <returns></returns> Task SendErrorResponse(Exception exception, String token); @@ -144,6 +140,21 @@ namespace Tango.Transport event EventHandler<MessageContainer> PendingResponseReceived; /// <summary> + /// Occurs when a request has been sent. + /// </summary> + event EventHandler<IMessage> RequestSent; + + /// <summary> + /// Occurs when a request response has been received. + /// </summary> + event EventHandler<IMessage> ResponseReceived; + + /// <summary> + /// Occurs when a request has failed. + /// </summary> + event EventHandler<RequestFailedEventArgs> RequestFailed; + + /// <summary> /// Gets or sets the default request timeout. /// </summary> TimeSpan RequestTimeout { get; set; } diff --git a/Software/Visual_Studio/Tango.Integration/Operation/RequestFailedEventArgs.cs b/Software/Visual_Studio/Tango.Transport/RequestFailedEventArgs.cs index e946dd0c1..dfbb24a6f 100644 --- a/Software/Visual_Studio/Tango.Integration/Operation/RequestFailedEventArgs.cs +++ b/Software/Visual_Studio/Tango.Transport/RequestFailedEventArgs.cs @@ -5,7 +5,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Tango.Integration.Operation +namespace Tango.Transport { public class RequestFailedEventArgs : EventArgs { 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 @@ <Compile Include="PendingResponse.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="ITransporter.cs" /> + <Compile Include="RequestFailedEventArgs.cs" /> <Compile Include="ResponseErrorException.cs" /> <Compile Include="Routing\SimpleTransportRouter.cs" /> <Compile Include="Servers\ClientConnectedEventArgs.cs" /> <Compile Include="Servers\TcpServer.cs" /> <Compile Include="TransportAdapterBase.cs" /> <Compile Include="TransportComponentState.cs" /> + <Compile Include="TransportContinuousRequestConfig.cs" /> <Compile Include="TransporterBase.cs" /> <Compile Include="TransporterDisconnectedException.cs" /> <Compile Include="Transporters\BasicTransporter.cs" /> <Compile Include="TransportMessage.cs" /> <Compile Include="TransportMessageBase.cs" /> + <Compile Include="TransportRequestConfig.cs" /> <Compile Include="TransportMessageDirection.cs" /> <Compile Include="Routing\TransportRoutingChannel.cs" /> + <Compile Include="TransportResponseConfig.cs" /> <Compile Include="Web\AutoFileDownloader.cs" /> <Compile Include="Web\InvalidTokenException.cs" /> <Compile Include="Web\IWebFileDownloader.cs" /> @@ -176,7 +180,7 @@ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <ProjectExtensions> <VisualStudio> - <UserProperties BuildVersion_StartDate="2000/1/1" BuildVersion_UseGlobalSettings="False" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" /> + <UserProperties BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UseGlobalSettings="False" BuildVersion_StartDate="2000/1/1" /> </VisualStudio> </ProjectExtensions> </Project>
\ 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<T> 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 /// </summary> internal abstract class TransportMessageBase : ExtendedObject { + public bool AtLeastOneResponseReceived { get; set; } + + public String TransportComponentName { get; set; } + /// <summary> /// Gets or sets a value indicating whether this instance is multi response. /// </summary> @@ -44,6 +48,11 @@ namespace Tango.Transport public Object Message { get; set; } /// <summary> + /// Gets or sets a value indicating whether log the message before sending. + /// </summary> + public bool ShouldLog { get; set; } + + /// <summary> /// Notifies the message observer of the new result. /// </summary> /// <param name="result">The result.</param> @@ -55,6 +64,10 @@ namespace Tango.Transport /// <param name="ex">The ex.</param> public abstract void SetException(Exception ex); + public abstract String GetActualMessageTypeName(); + + public abstract Object GetActualMessage(); + /// <summary> /// Initializes a new instance of the <see cref="TransportMessageBase"/> class. /// </summary> 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 /// </summary> public event EventHandler<TransportComponentState> StateChanged; + /// <summary> + /// Occurs when a request has been sent. + /// </summary> + public event EventHandler<IMessage> RequestSent; + + /// <summary> + /// Occurs when a request response has been received. + /// </summary> + public event EventHandler<IMessage> ResponseReceived; + + /// <summary> + /// Occurs when a request has failed. + /// </summary> + public event EventHandler<RequestFailedEventArgs> 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 } } + /// <summary> + /// Called when the request has been sent + /// </summary> + /// <param name="response">The request.</param> + private void OnRequestSent(TransportMessageBase request) + { + if (request.ShouldLog) + { + IMessage message = request.GetActualMessage() as IMessage; + + if (message != null) + { + RequestSent?.Invoke(this, message); + } + } + } + + /// <summary> + /// Called when the response has been received + /// </summary> + /// <param name="response">The response.</param> + private void OnResponseReceived(IMessage response) + { + if (response != null) + { + ResponseReceived?.Invoke(this, response); + } + } + + /// <summary> + /// Called when the request has been failed + /// </summary> + /// <param name="request">The request.</param> + 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 + /// <summary> /// Sends a request. /// </summary> /// <param name="request">The request.</param> - /// <param name="timeout">Optional timeout. If not specified will use the <see cref="RequestTimeout" />.</param> + /// <param name="config">Request configuration.</param> /// <returns></returns> - public Task<IMessage> SendRequest(IMessage request, TimeSpan? timeout = null) + /// <exception cref="InvalidOperationException"></exception> + public Task<IMessage> 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<IMessage> source = new TaskCompletionSource<IMessage>(); TransportMessage<IMessage> message = new TransportMessage<IMessage>(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); @@ -431,49 +500,16 @@ namespace Tango.Transport } /// <summary> - /// Sends the response. - /// </summary> - /// <param name="response">The response.</param> - /// <param name="token">The token.</param> - /// <param name="completed">The completed.</param> - /// <param name="errorCode">The error code.</param> - /// <param name="errorMessage">The error message.</param> - /// <returns></returns> - /// <exception cref="System.InvalidOperationException">Matching request token was not found!</exception> - 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); - } - - /// <summary> /// Sends the request. /// </summary> - /// <param name="container">The container.</param> + /// <param name="container"></param> + /// <param name="config">Request configuration.</param> /// <returns></returns> - public Task<MessageContainer> SendRequest(MessageContainer container) + /// <exception cref="InvalidOperationException"></exception> + public Task<MessageContainer> 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<MessageContainer> source = new TaskCompletionSource<MessageContainer>(); TransportMessage<MessageContainer> message = new TransportMessage<MessageContainer>(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 } /// <summary> - /// Sends the response. + /// Sends a request. /// </summary> - /// <param name="container">The container.</param> + /// <typeparam name="Request">The type of the request.</typeparam> + /// <typeparam name="Response">The type of the response.</typeparam> + /// <param name="request">The request.</param> + /// <param name="config">Request configuration.</param> /// <returns></returns> - /// <exception cref="System.InvalidOperationException">Matching request token was not found!</exception> - public Task SendResponse(MessageContainer container) + /// <exception cref="InvalidOperationException"></exception> + public Task<TangoMessage<Response>> SendRequest<Request, Response>(TangoMessage<Request> request, TransportRequestConfig config = null) where Request : IMessage<Request> where Response : IMessage<Response> { - 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<TangoMessage<Response>> source = new TaskCompletionSource<TangoMessage<Response>>(); + TransportMessage<TangoMessage<Response>> message = new TransportMessage<TangoMessage<Response>>(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<object> source = new TaskCompletionSource<object>(); - TransportMessage<object> message = new TransportMessage<object>(token, container, TransportMessageDirection.Response, () => container.ToByteArray(), source); EnqueueMessageOut(message); + return source.Task; } /// <summary> /// Sends a request and expecting multiple response messages. /// </summary> - /// <param name="request">The request.</param> - /// <param name="timeout"></param> + /// <param name="request"></param> + /// <param name="config">Request configuration.</param> /// <returns></returns> - public IObservable<IMessage> SendContinuousRequest(IMessage request, TimeSpan? timeout = default(TimeSpan?)) + /// <exception cref="InvalidOperationException"></exception> + public IObservable<IMessage> 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); }; @@ -617,59 +661,18 @@ namespace Tango.Transport } /// <summary> - /// Sends a request. - /// </summary> - /// <typeparam name="Request">The type of the request.</typeparam> - /// <typeparam name="Response">The type of the response.</typeparam> - /// <param name="request">The request.</param> - /// <param name="timeout">Optional timeout. If not specified will use the <see cref="RequestTimeout" />.</param> - /// <returns></returns> - public Task<TangoMessage<Response>> SendRequest<Request, Response>(TangoMessage<Request> request, TimeSpan? timeout = null) where Request : IMessage<Request> where Response : IMessage<Response> - { - 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<TangoMessage<Response>> source = new TaskCompletionSource<TangoMessage<Response>>(); - TransportMessage<TangoMessage<Response>> message = new TransportMessage<TangoMessage<Response>>(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; - } - - /// <summary> /// Sends a request and expecting multiple response messages. /// </summary> /// <typeparam name="Request">The type of the request.</typeparam> /// <typeparam name="Response">The type of the response.</typeparam> - /// <param name="request">The request.</param> - /// <param name="timeout"></param> + /// <param name="request"></param> + /// <param name="config">Request configuration.</param> /// <returns></returns> - public IObservable<TangoMessage<Response>> SendContinuousRequest<Request, Response>(TangoMessage<Request> request, TimeSpan? firstTimeout = null, TimeSpan? continousTimeout = null) where Request : IMessage<Request> where Response : IMessage<Response> + /// <exception cref="InvalidOperationException"></exception> + public IObservable<TangoMessage<Response>> SendContinuousRequest<Request, Response>(TangoMessage<Request> request, TransportContinuousRequestConfig config = null) where Request : IMessage<Request> where Response : IMessage<Response> { + config = config ?? new TransportContinuousRequestConfig(); + LogManager.Log($"{GetExtendedComponentName()}: Queuing continuous request message: " + typeof(Request).Name + " Token: " + request.Container.Token, LogCategory.Debug); Subject<TangoMessage<Response>> subject = new Subject<TangoMessage<Response>>(); @@ -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<TangoMessage<Response>> message = new TransportMessage<TangoMessage<Response>>(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. /// </summary> /// <param name="container">The container.</param> + /// <param name="config">Request configuration.</param> /// <returns></returns> - public IObservable<MessageContainer> SendContinuousRequest(MessageContainer container) + /// <exception cref="InvalidOperationException"></exception> + public IObservable<MessageContainer> 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 + /// <summary> /// Sends a response. /// </summary> @@ -825,18 +840,102 @@ namespace Tango.Transport } /// <summary> - /// Sends a response for the specified token. + /// Sends the response. + /// </summary> + /// <param name="response"></param> + /// <param name="token">Request token.</param> + /// <param name="config">Response configuration.</param> + /// <returns></returns> + 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); + } + + /// <summary> + /// Sends the response. + /// </summary> + /// <param name="container">The container.</param> + /// <returns></returns> + /// <exception cref="InvalidOperationException"> + /// </exception> + 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<object> source = new TaskCompletionSource<object>(); + TransportMessage<object> message = new TransportMessage<object>(token, container, TransportMessageDirection.Response, () => container.ToByteArray(), source); + EnqueueMessageOut(message); + return source.Task; + } + + /// <summary> + /// Sends a response. /// </summary> /// <typeparam name="Response">The type of the response.</typeparam> /// <param name="response">The response.</param> - /// <param name="token">The token.</param> - /// <param name="completed">The completed.</param> - /// <param name="errorCode">The error code.</param> - /// <param name="errorMessage">The error message.</param> + /// <param name="token">Request token.</param> + /// <param name="config">Response configuration.</param> /// <returns></returns> - /// <exception cref="InvalidOperationException">Matching request token was not found!</exception> - public Task SendResponse<Response>(TangoMessage<Response> response, String token, bool? completed = null, ErrorCode? errorCode = null, String errorMessage = null) where Response : IMessage<Response> + /// <exception cref="InvalidOperationException"> + /// Transporter push thread is not in a running state. + /// </exception> + public Task SendResponse<Response>(TangoMessage<Response> response, String token, TransportResponseConfig config = null) where Response : IMessage<Response> { + 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. /// </summary> /// <param name="exception">The exception.</param> - /// <param name="token">The token.</param> + /// <param name="config">Response configuration.</param> /// <returns></returns> - public Task SendErrorResponse(Exception exception, string token) + public Task SendErrorResponse(Exception exception, String token) { - return SendResponse<ErrorResponse>(new ErrorResponse() { }, token, true, ErrorCode.GeneralError, exception.Message); + return SendResponse<ErrorResponse>(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<KeepAliveRequest, KeepAliveResponse>(new KeepAliveRequest(), KeepAliveTimeout).Result; + var response = SendRequest<KeepAliveRequest, KeepAliveResponse>(new KeepAliveRequest(), new TransportRequestConfig() { Timeout = KeepAliveTimeout }).Result; retryCounter = KeepAliveRetries; } else |
