aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Software/DB/PPC/Tango.mdfbin75497472 -> 75497472 bytes
-rw-r--r--Software/DB/PPC/Tango_log.ldfbin53673984 -> 53673984 bytes
-rw-r--r--Software/DB/Tango.mdfbin75497472 -> 75497472 bytes
-rw-r--r--Software/DB/Tango_log.ldfbin22675456 -> 22675456 bytes
-rw-r--r--Software/Experiments/HangFireTest/HangFireTest.sln25
-rw-r--r--Software/Experiments/HangFireTest/HangFireTest/App.config19
-rw-r--r--Software/Experiments/HangFireTest/HangFireTest/CustomLogger.cs37
-rw-r--r--Software/Experiments/HangFireTest/HangFireTest/HangFireModel.Context.cs30
-rw-r--r--Software/Experiments/HangFireTest/HangFireTest/HangFireModel.Context.tt636
-rw-r--r--Software/Experiments/HangFireTest/HangFireTest/HangFireModel.Designer.cs10
-rw-r--r--Software/Experiments/HangFireTest/HangFireTest/HangFireModel.cs9
-rw-r--r--Software/Experiments/HangFireTest/HangFireTest/HangFireModel.edmx85
-rw-r--r--Software/Experiments/HangFireTest/HangFireTest/HangFireModel.edmx.diagram12
-rw-r--r--Software/Experiments/HangFireTest/HangFireTest/HangFireModel.tt733
-rw-r--r--Software/Experiments/HangFireTest/HangFireTest/HangFireTest.csproj116
-rw-r--r--Software/Experiments/HangFireTest/HangFireTest/Job.cs25
-rw-r--r--Software/Experiments/HangFireTest/HangFireTest/Program.cs107
-rw-r--r--Software/Experiments/HangFireTest/HangFireTest/Properties/AssemblyInfo.cs36
-rw-r--r--Software/Experiments/HangFireTest/HangFireTest/packages.config8
-rw-r--r--Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/EventLogging/DefaultEventLogger.cs1
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/EventLogging/DefaultEventLogger.cs1
-rw-r--r--Software/Visual_Studio/Tango.Emulations/Emulators/MachineEmulator.cs51
-rw-r--r--Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeReceiver.cs20
-rw-r--r--Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeSignalRClient.cs6
-rw-r--r--Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeTcpClient.cs34
-rw-r--r--Software/Visual_Studio/Tango.Integration/Operation/IMachineOperator.cs20
-rw-r--r--Software/Visual_Studio/Tango.Integration/Operation/MachineOperator.cs740
-rw-r--r--Software/Visual_Studio/Tango.Integration/Storage/StorageManager.cs45
-rw-r--r--Software/Visual_Studio/Tango.Integration/Tango.Integration.csproj3
-rw-r--r--Software/Visual_Studio/Tango.Stubs/StubManager.cs4
-rw-r--r--Software/Visual_Studio/Tango.Transport/ContinuousResponseAbortedException.cs10
-rw-r--r--Software/Visual_Studio/Tango.Transport/Discovery/UsbCommunicationScanner.cs2
-rw-r--r--Software/Visual_Studio/Tango.Transport/ITransporter.cs83
-rw-r--r--Software/Visual_Studio/Tango.Transport/RequestFailedEventArgs.cs (renamed from Software/Visual_Studio/Tango.Integration/Operation/RequestFailedEventArgs.cs)2
-rw-r--r--Software/Visual_Studio/Tango.Transport/Tango.Transport.csproj6
-rw-r--r--Software/Visual_Studio/Tango.Transport/TransportContinuousRequestConfig.cs13
-rw-r--r--Software/Visual_Studio/Tango.Transport/TransportMessage.cs52
-rw-r--r--Software/Visual_Studio/Tango.Transport/TransportMessageBase.cs13
-rw-r--r--Software/Visual_Studio/Tango.Transport/TransportRequestConfig.cs14
-rw-r--r--Software/Visual_Studio/Tango.Transport/TransportResponseConfig.cs16
-rw-r--r--Software/Visual_Studio/Tango.Transport/TransporterBase.cs495
41 files changed, 2714 insertions, 805 deletions
diff --git a/Software/DB/PPC/Tango.mdf b/Software/DB/PPC/Tango.mdf
index d269178b8..bdc7134c7 100644
--- a/Software/DB/PPC/Tango.mdf
+++ b/Software/DB/PPC/Tango.mdf
Binary files differ
diff --git a/Software/DB/PPC/Tango_log.ldf b/Software/DB/PPC/Tango_log.ldf
index e9f5137d0..3d480cd4e 100644
--- a/Software/DB/PPC/Tango_log.ldf
+++ b/Software/DB/PPC/Tango_log.ldf
Binary files differ
diff --git a/Software/DB/Tango.mdf b/Software/DB/Tango.mdf
index d270bb895..d765d6513 100644
--- a/Software/DB/Tango.mdf
+++ b/Software/DB/Tango.mdf
Binary files differ
diff --git a/Software/DB/Tango_log.ldf b/Software/DB/Tango_log.ldf
index ebd97b83a..99cf5050f 100644
--- a/Software/DB/Tango_log.ldf
+++ b/Software/DB/Tango_log.ldf
Binary files differ
diff --git a/Software/Experiments/HangFireTest/HangFireTest.sln b/Software/Experiments/HangFireTest/HangFireTest.sln
new file mode 100644
index 000000000..43789224b
--- /dev/null
+++ b/Software/Experiments/HangFireTest/HangFireTest.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.28307.645
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HangFireTest", "HangFireTest\HangFireTest.csproj", "{0A3D27ED-178E-40B7-BB6C-94BD552D4971}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {0A3D27ED-178E-40B7-BB6C-94BD552D4971}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0A3D27ED-178E-40B7-BB6C-94BD552D4971}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0A3D27ED-178E-40B7-BB6C-94BD552D4971}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0A3D27ED-178E-40B7-BB6C-94BD552D4971}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {F0D5C4A9-5629-4934-999D-E2438120DFC4}
+ EndGlobalSection
+EndGlobal
diff --git a/Software/Experiments/HangFireTest/HangFireTest/App.config b/Software/Experiments/HangFireTest/HangFireTest/App.config
new file mode 100644
index 000000000..3a79005cd
--- /dev/null
+++ b/Software/Experiments/HangFireTest/HangFireTest/App.config
@@ -0,0 +1,19 @@
+<?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=&quot;data source=localhost\SQLEXPRESS;initial catalog=HangFire;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;" 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