diff --git a/Samples/DotNetSdk/SamplesPlannedSpecimenInstantiator/MainForm.cs b/Samples/DotNetSdk/SamplesPlannedSpecimenInstantiator/MainForm.cs
index 620dbce7..6e5e76e6 100644
--- a/Samples/DotNetSdk/SamplesPlannedSpecimenInstantiator/MainForm.cs
+++ b/Samples/DotNetSdk/SamplesPlannedSpecimenInstantiator/MainForm.cs
@@ -252,7 +252,7 @@ private void LoadTripsWithPlannedVisits()
.ToDictionary(
tripId => tripId,
tripId => plannedVisitsWithStartTimes
- .Where(v => tripId == v.FieldTrip.Id && IsCandidateVisit(v))
+ .Where(v => IsCandidateVisit(v) && tripId == v.FieldTrip.Id)
.ToList());
var items = tripsWithPlannedVisits
diff --git a/Samples/DotNetSdk/SamplesPlannedSpecimenInstantiator/Properties/AssemblyInfo.cs b/Samples/DotNetSdk/SamplesPlannedSpecimenInstantiator/Properties/AssemblyInfo.cs
index dc4a6995..da99e86e 100644
--- a/Samples/DotNetSdk/SamplesPlannedSpecimenInstantiator/Properties/AssemblyInfo.cs
+++ b/Samples/DotNetSdk/SamplesPlannedSpecimenInstantiator/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@
// 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")]
+[assembly: AssemblyVersion("1.0.405.0")]
+[assembly: AssemblyFileVersion("1.0.405.0")]
diff --git a/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor.rar b/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor.rar
new file mode 100644
index 00000000..b402acfb
Binary files /dev/null and b/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor.rar differ
diff --git a/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/App.config b/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/App.config
new file mode 100644
index 00000000..193aecc6
--- /dev/null
+++ b/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/EmbeddedResource.cs b/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/EmbeddedResource.cs
new file mode 100644
index 00000000..1f186cd3
--- /dev/null
+++ b/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/EmbeddedResource.cs
@@ -0,0 +1,25 @@
+using System.IO;
+using System.Reflection;
+
+namespace ExcelToCSVProcessor
+{
+ public class EmbeddedResource
+ {
+ public static byte[] LoadEmbeddedResource(string path)
+ {
+ // ReSharper disable once PossibleNullReferenceException
+ var resourceName = $"{MethodBase.GetCurrentMethod().DeclaringType.Namespace}.{path.Replace('\\', '.')}";
+
+ using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
+ {
+ if (stream == null)
+ throw new ExpectedException($"Can't load '{resourceName}' as embedded resource.");
+
+ using (var reader = new BinaryReader(stream))
+ {
+ return reader.ReadBytes((int)stream.Length);
+ }
+ }
+ }
+ }
+}
diff --git a/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/ExcelToCSVProcessor.csproj b/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/ExcelToCSVProcessor.csproj
new file mode 100644
index 00000000..019e29dc
--- /dev/null
+++ b/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/ExcelToCSVProcessor.csproj
@@ -0,0 +1,91 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {E921CFC6-AE8A-48C7-8B14-4DD3F3EA2306}
+ Exe
+ ExcelToCSVProcessor
+ ExcelToCSVProcessor
+ v4.8
+ 512
+ true
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\ExcelDataReader.3.6.0\lib\net45\ExcelDataReader.dll
+
+
+ ..\packages\ExcelDataReader.DataSet.3.6.0\lib\net35\ExcelDataReader.DataSet.dll
+
+
+ ..\packages\log4net.2.0.8\lib\net45-full\log4net.dll
+
+
+ ..\packages\ServiceStack.Interfaces.5.8.0\lib\net45\ServiceStack.Interfaces.dll
+
+
+ ..\packages\ServiceStack.Logging.Log4Net.5.8.0\lib\net45\ServiceStack.Logging.Log4Net.dll
+
+
+ ..\packages\ServiceStack.Text.5.8.0\lib\net45\ServiceStack.Text.dll
+
+
+
+ ..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll
+
+
+
+
+
+ ..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll
+
+
+
+ ..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll
+
+
+ ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/ExpectedException.cs b/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/ExpectedException.cs
new file mode 100644
index 00000000..d7bb7131
--- /dev/null
+++ b/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/ExpectedException.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace ExcelToCSVProcessor
+{
+ public class ExpectedException : Exception
+ {
+ public ExpectedException(string message)
+ : base(message)
+ {
+ }
+ }
+}
diff --git a/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/Program.cs b/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/Program.cs
new file mode 100644
index 00000000..6c2ccf12
--- /dev/null
+++ b/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/Program.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using ServiceStack.Logging.Log4Net;
+using log4net;
+using System.IO;
+using System.Xml;
+using System.Reflection;
+using ExcelDataReader;
+using ServiceStack;
+using System.Data;
+
+namespace ExcelToCSVProcessor
+{
+ internal class Program
+ {
+ private static ILog Log = null;
+
+ static void Main(string[] args)
+ {
+ bool debug = false;
+ Environment.ExitCode = 1;
+
+ try
+ {
+ debug = args.Length == 1 && string.Compare(args[0], "DEBUG", true) == 0;
+
+ ConfigureLogging();
+
+ using (Stream stdin = Console.OpenStandardInput())
+ {
+ using (Stream stdout = Console.OpenStandardOutput())
+ {
+ using (MemoryStream memoryStream = new MemoryStream())
+ {
+ stdin.CopyTo(memoryStream);
+ memoryStream.Seek(0, SeekOrigin.Begin);
+
+ using (var reader = ExcelReaderFactory.CreateReader(memoryStream))
+ {
+ var spreadsheet = reader.AsDataSet();
+ var table = spreadsheet.Tables[0];
+
+ var worksheetColumn = new DataColumn() { DefaultValue = table.TableName };
+ table.Columns.Add(worksheetColumn);
+ worksheetColumn.SetOrdinal(0);
+ table.Rows[0][worksheetColumn] = "#SheetName#";
+
+ var csv = table.ToCSV();
+ var bytes = Encoding.UTF8.GetBytes(csv);
+
+ if (debug)
+ foreach (var row in table.ToCSVArray())
+ Log?.Info(row);
+
+ stdout.Write(bytes, 0, bytes.Length);
+ if (debug)
+ Log?.Info($"Total row count(DEBUG): {table.Rows.Count}, Columns: {table.ColumnsToCSV()}");
+ else
+ Log?.Info($"Total row count: {table.Rows.Count}, Columns: {table.ColumnsToCSV()}");
+ }
+ }
+ }
+ }
+
+ Environment.ExitCode = 0;
+ }
+ catch (Exception ex)
+ {
+ Log?.Fatal(ex);
+ Console.WriteLine(ex.ToString());
+ }
+ finally
+ {
+ LogManager.Flush(5000);
+ }
+ }
+
+ private static void ConfigureLogging()
+ {
+ using (var stream = new MemoryStream(EmbeddedResource.LoadEmbeddedResource("log4net.config")))
+ using (var reader = new StreamReader(stream))
+ {
+ var xml = new XmlDocument();
+ xml.LoadXml(reader.ReadToEnd());
+
+ log4net.Config.XmlConfigurator.Configure(xml.DocumentElement);
+
+ Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ ServiceStack.Logging.LogManager.LogFactory = new Log4NetFactory();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/Properties/AssemblyInfo.cs b/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..160bd16c
--- /dev/null
+++ b/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/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("ExcelToCSVProcessor")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("ExcelToCSVProcessor")]
+[assembly: AssemblyCopyright("Copyright © 2024")]
+[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("e921cfc6-ae8a-48c7-8b14-4dd3f3ea2306")]
+
+// 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/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/TableExtension.cs b/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/TableExtension.cs
new file mode 100644
index 00000000..2cca92f7
--- /dev/null
+++ b/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/TableExtension.cs
@@ -0,0 +1,110 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ExcelToCSVProcessor
+{
+ public static class TableExtension
+ {
+ public static string ToCSV(this DataTable dtDataTable)
+ {
+ var builder = new StringBuilder();
+ foreach (DataRow dr in dtDataTable.Rows)
+ {
+ for (int i = 0; i < dtDataTable.Columns.Count; i++)
+ {
+ if (!Convert.IsDBNull(dr[i]))
+ {
+ string value = dr[i].ToString();
+ if (value.StartsWith("\""))
+ {
+ builder.Append(dr[i].ToString());
+ }
+ else
+ {
+ value = string.Format("\"{0}\"", value);
+ builder.Append(value);
+ }
+ }
+ if (i < dtDataTable.Columns.Count - 1)
+ {
+ builder.Append(",");
+ }
+ }
+ builder.Append(Environment.NewLine);
+ }
+
+ var csv = builder.ToString();
+ return csv;
+ }
+
+ public static string[] ToCSVArray(this DataTable dtDataTable)
+ {
+ var array = new List();
+
+ foreach (DataRow dr in dtDataTable.Rows)
+ {
+ var builder = new StringBuilder();
+ for (int i = 0; i < dtDataTable.Columns.Count; i++)
+ {
+ if (!Convert.IsDBNull(dr[i]))
+ {
+ string value = dr[i].ToString();
+ if (value.StartsWith("\""))
+ {
+ builder.Append(dr[i].ToString());
+ }
+ else
+ {
+ value = string.Format("\"{0}\"", value);
+ builder.Append(value);
+ }
+ }
+ if (i < dtDataTable.Columns.Count - 1)
+ {
+ builder.Append(",");
+ }
+ }
+
+ array.Add(builder.ToString());
+ }
+ return array.ToArray();
+ }
+
+ public static string ColumnsToCSV(this DataTable dtDataTable)
+ {
+ var builder = new StringBuilder();
+ foreach (DataRow dr in dtDataTable.Rows)
+ {
+ for (int i = 0; i < dtDataTable.Columns.Count; i++)
+ {
+ if (!Convert.IsDBNull(dr[i]))
+ {
+ string value = dr[i].ToString();
+ if (value.StartsWith("\""))
+ {
+ builder.Append(dr[i].ToString());
+ }
+ else
+ {
+ value = string.Format("\"{0}\"", value);
+ builder.Append(value);
+ }
+ }
+ if (i < dtDataTable.Columns.Count - 1)
+ {
+ builder.Append(",");
+ }
+ }
+
+ break;
+ }
+
+ var csv = builder.ToString();
+ return csv;
+ }
+ }
+}
diff --git a/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/log4net.config b/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/log4net.config
new file mode 100644
index 00000000..b03b5a61
--- /dev/null
+++ b/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/log4net.config
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/packages.config b/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/packages.config
new file mode 100644
index 00000000..13f7cb08
--- /dev/null
+++ b/TimeSeries/PublicApis/SdkExamples/ExcelToCSVProcessor/packages.config
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TimeSeries/PublicApis/SdkExamples/NEM12PreProcessor/log4net.config b/TimeSeries/PublicApis/SdkExamples/NEM12PreProcessor/log4net.config
index eb9eec8a..215a75c4 100644
--- a/TimeSeries/PublicApis/SdkExamples/NEM12PreProcessor/log4net.config
+++ b/TimeSeries/PublicApis/SdkExamples/NEM12PreProcessor/log4net.config
@@ -8,7 +8,7 @@
-
+
diff --git a/TimeSeries/PublicApis/SdkExamples/SdkExamples.sln b/TimeSeries/PublicApis/SdkExamples/SdkExamples.sln
index 55785b1b..d10bdcf8 100644
--- a/TimeSeries/PublicApis/SdkExamples/SdkExamples.sln
+++ b/TimeSeries/PublicApis/SdkExamples/SdkExamples.sln
@@ -35,6 +35,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NEM12PreProcessor", "NEM12P
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExcelCsvExtractor", "ExcelCsvExtractor\ExcelCsvExtractor.csproj", "{657DC627-59D4-4910-B723-C7BE8A70B43B}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExcelToCSVProcessor", "ExcelToCSVProcessor\ExcelToCSVProcessor.csproj", "{E921CFC6-AE8A-48C7-8B14-4DD3F3EA2306}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -93,6 +95,10 @@ Global
{657DC627-59D4-4910-B723-C7BE8A70B43B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{657DC627-59D4-4910-B723-C7BE8A70B43B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{657DC627-59D4-4910-B723-C7BE8A70B43B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E921CFC6-AE8A-48C7-8B14-4DD3F3EA2306}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E921CFC6-AE8A-48C7-8B14-4DD3F3EA2306}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E921CFC6-AE8A-48C7-8B14-4DD3F3EA2306}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E921CFC6-AE8A-48C7-8B14-4DD3F3EA2306}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE