From 4705c9b552246391b07a3f90cc86b918e9813b47 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 16 Oct 2020 13:21:54 +0200 Subject: [PATCH] Add data processing sample (Sample25) --- Samples.sln | 7 + src/samples/README.md | 3 +- src/samples/Sample25/Activities/Absolute.cs | 28 + .../Activities/ArithmeticOperation.cs | 44 + src/samples/Sample25/Activities/Channel.cs | 37 + src/samples/Sample25/Activities/Subtract.cs | 12 + .../Sample25/DataProcessingWorkflow.cs | 57 + .../Extensions/PrintDataExtensions.cs | 1135 +++++++++++++++++ src/samples/Sample25/Program.cs | 61 + src/samples/Sample25/Sample25.csproj | 12 + 10 files changed, 1395 insertions(+), 1 deletion(-) create mode 100644 src/samples/Sample25/Activities/Absolute.cs create mode 100644 src/samples/Sample25/Activities/ArithmeticOperation.cs create mode 100644 src/samples/Sample25/Activities/Channel.cs create mode 100644 src/samples/Sample25/Activities/Subtract.cs create mode 100644 src/samples/Sample25/DataProcessingWorkflow.cs create mode 100644 src/samples/Sample25/Extensions/PrintDataExtensions.cs create mode 100644 src/samples/Sample25/Program.cs create mode 100644 src/samples/Sample25/Sample25.csproj diff --git a/Samples.sln b/Samples.sln index 0918908dce..b2e63dedd1 100644 --- a/Samples.sln +++ b/Samples.sln @@ -123,6 +123,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample23", "src\samples\Sam EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample24", "src\samples\Sample24\Sample24.csproj", "{C7B1C36D-7297-4AA2-8AFE-31FC5CB99AA0}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample25", "src\samples\Sample25\Sample25.csproj", "{ADDA6EB3-32F0-4802-B595-B12090DC4A1C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -313,6 +315,10 @@ Global {C7B1C36D-7297-4AA2-8AFE-31FC5CB99AA0}.Debug|Any CPU.Build.0 = Debug|Any CPU {C7B1C36D-7297-4AA2-8AFE-31FC5CB99AA0}.Release|Any CPU.ActiveCfg = Release|Any CPU {C7B1C36D-7297-4AA2-8AFE-31FC5CB99AA0}.Release|Any CPU.Build.0 = Release|Any CPU + {ADDA6EB3-32F0-4802-B595-B12090DC4A1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ADDA6EB3-32F0-4802-B595-B12090DC4A1C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ADDA6EB3-32F0-4802-B595-B12090DC4A1C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ADDA6EB3-32F0-4802-B595-B12090DC4A1C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -370,6 +376,7 @@ Global {BE634D4E-73A1-48CE-8682-F548CA0BA5B6} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A} {5F16E2EC-7162-47F8-B8F1-81CACB05A5D9} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A} {C7B1C36D-7297-4AA2-8AFE-31FC5CB99AA0} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A} + {ADDA6EB3-32F0-4802-B595-B12090DC4A1C} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8B0975FD-7050-48B0-88C5-48C33378E158} diff --git a/src/samples/README.md b/src/samples/README.md index 78e0838dd1..bab3f92a5c 100644 --- a/src/samples/README.md +++ b/src/samples/README.md @@ -25,4 +25,5 @@ This list of sample implementations showcase a variety of workflows using ELSA. 21. [Shopping Cart: Mass Transit & Quartz Scheduling](Sample21) - Example of a workflow that tracks a shopping cart. Uses Mass Transit and Quartz scheduling. 22. [Hellow World: SQL Server](Sample22) - A simple demonstration of using Entity Framework Core persistence providers with SQL Server. 23. [Hello World Custom Schema: Sqllite](Sample23) - A simple demonstration of using Entity Framework Core persistence providers with SqlLite and Custom Schema. -24. [Hello World: MySql with migration](Sample24) - A simple demonstration of using Entity Framework Core MySql persistence provider. A migration is also demonstrated. \ No newline at end of file +24. [Hello World: MySql with migration](Sample24) - A simple demonstration of using Entity Framework Core MySql persistence provider. A migration is also demonstrated. +24. [Data processing](Sample25) - A simple demonstration of flowing data through a pipeline of processing activities (https://github.com/elsa-workflows/elsa-core/issues/405). \ No newline at end of file diff --git a/src/samples/Sample25/Activities/Absolute.cs b/src/samples/Sample25/Activities/Absolute.cs new file mode 100644 index 0000000000..3d5fa6c252 --- /dev/null +++ b/src/samples/Sample25/Activities/Absolute.cs @@ -0,0 +1,28 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Elsa.Expressions; +using Elsa.Results; +using Elsa.Services; +using Elsa.Services.Models; + +namespace Sample25.Activities +{ + public class Absolute : Activity + { + public WorkflowExpression ValueExpression + { + get => GetState>(); + set => SetState(value); + } + + protected override async Task OnExecuteAsync(WorkflowExecutionContext context, CancellationToken cancellationToken) + { + var value = await context.EvaluateAsync(ValueExpression, cancellationToken); + var result = Math.Abs(value); + + Output.SetVariable("Result", result); + return Done(); + } + } +} \ No newline at end of file diff --git a/src/samples/Sample25/Activities/ArithmeticOperation.cs b/src/samples/Sample25/Activities/ArithmeticOperation.cs new file mode 100644 index 0000000000..d5df48e4d9 --- /dev/null +++ b/src/samples/Sample25/Activities/ArithmeticOperation.cs @@ -0,0 +1,44 @@ +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Elsa; +using Elsa.Activities.ControlFlow.Activities; +using Elsa.Expressions; +using Elsa.Results; +using Elsa.Services.Models; + +namespace Sample25.Activities +{ + public abstract class ArithmeticOperation : Join + { + public ArithmeticOperation() + { + Mode = JoinMode.WaitAll; + } + + public WorkflowExpression Values + { + get => GetState>(); + set => SetState(value); + } + + protected override async Task OnExecuteAsync(WorkflowExecutionContext context, CancellationToken cancellationToken) + { + var activityExecutionResult = await base.OnExecuteAsync(context, cancellationToken); + + if (IsCompleted(activityExecutionResult)) + { + var values = await context.EvaluateAsync(Values, cancellationToken); + var result = Calculate(values); + + Output.SetVariable("Result", result); + } + + return activityExecutionResult; + } + + protected abstract double Calculate(params double[] values); + + private bool IsCompleted(IActivityExecutionResult result) => result is OutcomeResult outcome && outcome.EndpointNames.Any(x => x == OutcomeNames.Done); + } +} \ No newline at end of file diff --git a/src/samples/Sample25/Activities/Channel.cs b/src/samples/Sample25/Activities/Channel.cs new file mode 100644 index 0000000000..4fe987377b --- /dev/null +++ b/src/samples/Sample25/Activities/Channel.cs @@ -0,0 +1,37 @@ +using Elsa.Results; +using Elsa.Services; +using Elsa.Services.Models; + +namespace Sample25.Activities +{ + /// + /// Produces a single value. + /// + public class Channel : Activity + { + /// + /// The ID of the sensor to observe. + /// + public string SensorId + { + get => GetState(); + set => SetState(value); + } + + // Execute only if we received data from the sensor being observed. + protected override bool OnCanExecute(WorkflowExecutionContext context) => context.Workflow.Input.ContainsKey(SensorId); + + // Halt workflow execution until sensor data is received. + protected override ActivityExecutionResult OnExecute(WorkflowExecutionContext context) => Halt(); + + protected override ActivityExecutionResult OnResume(WorkflowExecutionContext context) + { + // Read sensor output provided as workflow input. + var value = context.Workflow.Input.GetVariable(SensorId); + + // Set the value as an output of this activity. + Output.SetVariable("Value", value); + return Done(); + } + } +} \ No newline at end of file diff --git a/src/samples/Sample25/Activities/Subtract.cs b/src/samples/Sample25/Activities/Subtract.cs new file mode 100644 index 0000000000..0f2fab25c9 --- /dev/null +++ b/src/samples/Sample25/Activities/Subtract.cs @@ -0,0 +1,12 @@ +using System.Linq; + +namespace Sample25.Activities +{ + /// + /// Subtracts two incoming inputs + /// + public class Subtract : ArithmeticOperation + { + protected override double Calculate(params double[] values) => values.Aggregate((left, right) => left - right); + } +} \ No newline at end of file diff --git a/src/samples/Sample25/DataProcessingWorkflow.cs b/src/samples/Sample25/DataProcessingWorkflow.cs new file mode 100644 index 0000000000..9e54272456 --- /dev/null +++ b/src/samples/Sample25/DataProcessingWorkflow.cs @@ -0,0 +1,57 @@ +using Elsa; +using Elsa.Activities.Console.Activities; +using Elsa.Activities.ControlFlow.Activities; +using Elsa.Expressions; +using Elsa.Scripting.JavaScript; +using Elsa.Services; +using Elsa.Services.Models; +using Sample25.Activities; + +namespace Sample25 +{ + public class DataProcessingWorkflow : IWorkflow + { + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(x => x.TextExpression = new LiteralExpression("Waiting for sensor input.")) + + // Fork execution into two branches to wait for external stimuli from the two channels in parallel. + .Then( + fork => fork.Branches = new[] { "Channel 1", "Channel 2" }, + fork => + { + fork + .When("Channel 1") + .Then(x => x.SensorId = "Sensor1").WithName("Channel1") + .Then("Subtract"); // Connect to Subtract activity. + + fork + .When("Channel 2") + .Then(x => x.SensorId = "Sensor2").WithName("Channel2") + .Then("Subtract"); // Connect to Subtract activity. + }) + + // Subtract the specified values. + .Then(x => x.Values = new JavaScriptExpression("[Channel1.Value, Channel2.Value]")).WithName("Subtract") + + // Calculate the absolute value of the subtraction. + .Then(x => x.ValueExpression = new JavaScriptExpression("(Subtract.Result)")).WithName("Absolute") + + // Compare the absolute value against a constant threshold, and write the appropriate output. + .Then( + x => x.ConditionExpression = new JavaScriptExpression("(Absolute.Result) > 0.5"), + ifElse => + { + ifElse + .When(OutcomeNames.False) + .Then(x => x.TextExpression = new LiteralExpression("Data does not exceed threshold (FALSE)")); + + ifElse + .When(OutcomeNames.True) + .Then(x => x.TextExpression = new LiteralExpression("Data exceeds threshold (TRUE)")); + }) + .Then(x => x.TextExpression = new JavaScriptExpression("(`Finished data processing. Result: ${Absolute.Result}`)")); + } + } +} \ No newline at end of file diff --git a/src/samples/Sample25/Extensions/PrintDataExtensions.cs b/src/samples/Sample25/Extensions/PrintDataExtensions.cs new file mode 100644 index 0000000000..3a21ff6439 --- /dev/null +++ b/src/samples/Sample25/Extensions/PrintDataExtensions.cs @@ -0,0 +1,1135 @@ +// Taken from https://www.codeproject.com/Tips/1147879/Print-DataTable-to-Console-and-more + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + + // ReSharper disable once CheckNamespace + namespace System.Data +{ + public static class PrintDataExtensions + { + #region Print Columns + + public static void PrintColumns(this DataTable dataTable, params string[] columnNames) + { + PrintColumns(dataTable.TableName, GetColumns(dataTable, columnNames)); + } + + public static void PrintColumns(this DataView dataView, params string[] columnNames) + { + PrintColumns(dataView.Table.TableName, GetColumns(dataView.Table, columnNames)); + } + + public static void PrintColumns(this DataSet dataSet, params string[] columnNames) + { + foreach (DataTable dataTable in dataSet.Tables) + PrintColumns(dataTable, columnNames); + } + + private static void PrintColumns(string name, DataColumn[] columns) + { + string columnName = "Column Name"; + string dataType = "Data Type"; + string nullable = "Nullable"; + string dataMember = "Data Member"; + + int length0 = 0; + int length1 = columnName.Length; + int length2 = dataType.Length; + int length3 = nullable.Length; + int length4 = dataMember.Length; + + if (columns.Length > 0) + { + int maxLength = columns.Select(c => c.Ordinal).Max().ToString().Length; + if (length0 < maxLength) + length0 = maxLength; + + maxLength = columns.Select(c => c.ColumnName.Length).Max(); + if (length1 < maxLength) + length1 = maxLength; + + maxLength = columns.Select(c => c.DataType.ToString().Length).Max(); + if (length2 < maxLength) + length2 = maxLength; + + maxLength = columns.Select(c => GetDataMemberType(c).Length + 1 + c.ColumnName.Length).Max(); + if (length4 < maxLength) + length4 = maxLength; + } + + string horizontal0 = new String(Horizontal_Bar, length0 + 2); + string horizontal1 = new String(Horizontal_Bar, length1 + 2); + string horizontal2 = new String(Horizontal_Bar, length2 + 2); + string horizontal3 = new String(Horizontal_Bar, length3 + 2); + string horizontal4 = new String(Horizontal_Bar, length4 + 2); + + if (string.IsNullOrEmpty(name) == false) + WriteLine("{0}:", name); + + WriteLine("{5}{0}{6}{1}{6}{2}{6}{3}{6}{4}{7}", horizontal0, horizontal1, horizontal2, horizontal3, horizontal4, Top_Left, Top_Center, Top_Right); + + string format1 = string.Format("{{5}} {{0,-{0}}} {{5}} {{1,-{1}}} {{5}} {{2,-{2}}} {{5}} {{3,-{3}}} {{5}} {{4,-{4}}} {{5}}", length0, length1, length2, length3, length4); + WriteLine(format1, string.Empty, columnName, dataType, nullable, dataMember, Verticl_Bar); + + WriteLine("{5}{0}{6}{1}{6}{2}{6}{3}{6}{4}{7}", horizontal0, horizontal1, horizontal2, horizontal3, horizontal4, Middle_Left, Middle_Center, Middle_Right); + + foreach (DataColumn column in columns) + { + string dataMemberType = GetDataMemberType(column); + string format2 = string.Format("{{5}} {{0,{0}}} {{5}} {{1,-{1}}} {{5}} {{2,-{2}}} {{5}} {{3,-{3}}} {{5}} {{4}} {{1}} {{5,{4}}}", length0, length1, length2, length3, length4 - dataMemberType.Length - column.ColumnName.Length); + WriteLine(format2, column.Ordinal, column.ColumnName, column.DataType, column.AllowDBNull, dataMemberType, Verticl_Bar); + } + + WriteLine("{5}{0}{6}{1}{6}{2}{6}{3}{6}{4}{7}", horizontal0, horizontal1, horizontal2, horizontal3, horizontal4, Bottom_Left, Bottom_Center, Bottom_Right); + + WriteLine(); + } + + private static string GetDataMemberType(DataColumn column) + { + if (column.DataType == typeof(string)) + return "string"; + else if (column.DataType == typeof(int)) + return "int" + (column.AllowDBNull ? "?" : string.Empty); + else if (column.DataType == typeof(short)) + return "short" + (column.AllowDBNull ? "?" : string.Empty); + else if (column.DataType == typeof(long)) + return "long" + (column.AllowDBNull ? "?" : string.Empty); + else if (column.DataType == typeof(double)) + return "double" + (column.AllowDBNull ? "?" : string.Empty); + else if (column.DataType == typeof(decimal)) + return "decimal" + (column.AllowDBNull ? "?" : string.Empty); + else if (column.DataType == typeof(float)) + return "float" + (column.AllowDBNull ? "?" : string.Empty); + else if (column.DataType == typeof(char)) + return "char" + (column.AllowDBNull ? "?" : string.Empty); + else if (column.DataType == typeof(bool)) + return "bool" + (column.AllowDBNull ? "?" : string.Empty); + else if (column.DataType == typeof(uint)) + return "uint" + (column.AllowDBNull ? "?" : string.Empty); + else if (column.DataType == typeof(ushort)) + return "ushort" + (column.AllowDBNull ? "?" : string.Empty); + else if (column.DataType == typeof(ulong)) + return "ulong" + (column.AllowDBNull ? "?" : string.Empty); + else if (column.DataType == typeof(byte)) + return "byte" + (column.AllowDBNull ? "?" : string.Empty); + else if (column.DataType == typeof(sbyte)) + return "sbyte" + (column.AllowDBNull ? "?" : string.Empty); + else if (column.DataType == typeof(DateTime)) + return "DateTime" + (column.AllowDBNull ? "?" : string.Empty); + else if (column.DataType == typeof(TimeSpan)) + return "TimeSpan" + (column.AllowDBNull ? "?" : string.Empty); + else if (column.DataType == typeof(Type)) + return "Type"; + else if (column.DataType == typeof(byte[])) + return "byte[]"; + else + return column.DataType.ToString() + (column.AllowDBNull && column.DataType.IsClass == false ? "?" : string.Empty); + } + + #endregion + + #region Print DataTable + + public static void Print(this DataTable dataTable, params string[] columnNames) + { + Print(dataTable, false, 0, null, columnNames); + } + + public static void Print(this DataTable dataTable, bool rowOrdinals, params string[] columnNames) + { + Print(dataTable, rowOrdinals, 0, null, columnNames); + } + + public static void Print(this DataTable dataTable, int top, params string[] columnNames) + { + Print(dataTable, false, top, null, columnNames); + } + + public static void Print(this DataTable dataTable, bool rowOrdinals, int top, params string[] columnNames) + { + Print(dataTable, rowOrdinals, top, null, columnNames); + } + + public static void Print(this DataTable dataTable, ValueToStringHandler toString, params string[] columnNames) + { + Print(dataTable, false, 0, toString, columnNames); + } + + public static void Print(this DataTable dataTable, bool rowOrdinals, ValueToStringHandler toString, params string[] columnNames) + { + Print(dataTable, rowOrdinals, 0, toString, columnNames); + } + + public static void Print(this DataTable dataTable, int top, ValueToStringHandler toString, params string[] columnNames) + { + Print(dataTable, false, top, toString, columnNames); + } + + public static void Print(this DataTable dataTable, bool rowOrdinals = false, int top = 0, ValueToStringHandler toString = null, params string[] columnNames) + { + PrintRows(dataTable, dataTable.AsEnumerable(), rowOrdinals, top, toString, columnNames); + } + + #endregion + + #region Print DataView + + public static void Print(this DataView dataView, params string[] columnNames) + { + Print(dataView, false, 0, null, columnNames); + } + + public static void Print(this DataView dataView, bool rowOrdinals, params string[] columnNames) + { + Print(dataView, rowOrdinals, 0, null, columnNames); + } + + public static void Print(this DataView dataView, int top, params string[] columnNames) + { + Print(dataView, false, top, null, columnNames); + } + + public static void Print(this DataView dataView, bool rowOrdinals, int top, params string[] columnNames) + { + Print(dataView, rowOrdinals, top, null, columnNames); + } + + public static void Print(this DataView dataView, ValueToStringHandler toString, params string[] columnNames) + { + Print(dataView, false, 0, toString, columnNames); + } + + public static void Print(this DataView dataView, bool rowOrdinals, ValueToStringHandler toString, params string[] columnNames) + { + Print(dataView, rowOrdinals, 0, toString, columnNames); + } + + public static void Print(this DataView dataView, int top, ValueToStringHandler toString, params string[] columnNames) + { + Print(dataView, false, top, toString, columnNames); + } + + public static void Print(this DataView dataView, bool rowOrdinals = false, int top = 0, ValueToStringHandler toString = null, params string[] columnNames) + { + PrintRows(dataView, rowOrdinals, top, toString, columnNames); + } + + #endregion + + #region Print DataSet + + public static void Print(this DataSet dataSet, params string[] columnNames) + { + Print(dataSet, false, 0, null, columnNames); + } + + public static void Print(this DataSet dataSet, bool rowOrdinals, params string[] columnNames) + { + Print(dataSet, rowOrdinals, 0, null, columnNames); + } + + public static void Print(this DataSet dataSet, int top, params string[] columnNames) + { + Print(dataSet, false, top, null, columnNames); + } + + public static void Print(this DataSet dataSet, bool rowOrdinals, int top, params string[] columnNames) + { + Print(dataSet, rowOrdinals, top, null, columnNames); + } + + public static void Print(this DataSet dataSet, ValueToStringHandler toString, params string[] columnNames) + { + Print(dataSet, false, 0, toString, columnNames); + } + + public static void Print(this DataSet dataSet, bool rowOrdinals, ValueToStringHandler toString, params string[] columnNames) + { + Print(dataSet, rowOrdinals, 0, toString, columnNames); + } + + public static void Print(this DataSet dataSet, int top, ValueToStringHandler toString, params string[] columnNames) + { + Print(dataSet, false, top, toString, columnNames); + } + + public static void Print(this DataSet dataSet, bool rowOrdinals = false, int top = 0, ValueToStringHandler toString = null, params string[] columnNames) + { + foreach (DataTable dataTable in dataSet.Tables) + Print(dataTable, rowOrdinals, top, toString, columnNames); + } + + #endregion + + #region Print DataRow[] + + public static void Print(this DataRow[] dataRows, params string[] columnNames) + { + Print(dataRows, false, 0, null, columnNames); + } + + public static void Print(this DataRow[] dataRows, bool rowOrdinals, params string[] columnNames) + { + Print(dataRows, rowOrdinals, 0, null, columnNames); + } + + public static void Print(this DataRow[] dataRows, int top, params string[] columnNames) + { + Print(dataRows, false, top, null, columnNames); + } + + public static void Print(this DataRow[] dataRows, bool rowOrdinals, int top, params string[] columnNames) + { + Print(dataRows, rowOrdinals, top, null, columnNames); + } + + public static void Print(this DataRow[] dataRows, ValueToStringHandler toString, params string[] columnNames) + { + Print(dataRows, false, 0, toString, columnNames); + } + + public static void Print(this DataRow[] dataRows, bool rowOrdinals, ValueToStringHandler toString, params string[] columnNames) + { + Print(dataRows, rowOrdinals, 0, toString, columnNames); + } + + public static void Print(this DataRow[] dataRows, int top, ValueToStringHandler toString, params string[] columnNames) + { + Print(dataRows, false, top, toString, columnNames); + } + + public static void Print(this DataRow[] dataRows, bool rowOrdinals = false, int top = 0, ValueToStringHandler toString = null, params string[] columnNames) + { + PrintRows((dataRows.Length != 0 ? dataRows[0].Table : null), dataRows, rowOrdinals, top, toString, columnNames); + } + + #endregion + + #region Print Helper Methods + + public delegate string ValueToStringHandler(object obj, DataRow row, DataColumn column); + + private static void PrintRows(DataTable dataTable, IEnumerable dataRows, bool rowOrdinals, int top, ValueToStringHandler toString, string[] columnNames, string ordinalColumnName = null) + { + if (dataTable == null && dataRows.Count() == 0) + { + WriteLine("No rows were selected"); + WriteLine(); + return; + } + + if (dataTable != null && string.IsNullOrEmpty(dataTable.TableName) == false) + WriteLine("{0}:", dataTable.TableName); + + DataColumn[] columns = GetColumns(dataTable, columnNames, ordinalColumnName); + if (columns.Length == 0) + { + WriteLine("No columns were selected"); + WriteLine(); + return; + } + + if (top > 0) + dataRows = dataRows.Take(top); + + int[] lengths = columns.Select(c => c.ColumnName.Length).ToArray(); + foreach (DataRow row in dataRows) + CalculateLengths(row, columns, lengths, toString); + + int rowOrdinalsLength = 0; + if (rowOrdinals) + { + if (dataRows.Count() > 0) + { + int maxRowOrdinal = 0; + if (string.IsNullOrEmpty(ordinalColumnName)) + maxRowOrdinal = dataRows.Select(row => row.Table.Rows.IndexOf(row)).Max(); + else + maxRowOrdinal = dataRows.Select(row => (int)row[ordinalColumnName]).Max(); + + if (maxRowOrdinal > -1) + rowOrdinalsLength = maxRowOrdinal.ToString().Length; + } + } + + string header = Top_Left.ToString(); + string separator = Middle_Left.ToString(); + string footer = Bottom_Left.ToString(); + string formatHeaders = Verticl_Bar.ToString(); + string format = Verticl_Bar.ToString(); + + if (rowOrdinals) + { + string horizontal = new String(Horizontal_Bar, rowOrdinalsLength + 2); + header += horizontal + Top_Center; + separator += horizontal + Middle_Center; + footer += horizontal + Bottom_Center; + formatHeaders += string.Format(" {{0,-{0}}} {1}", rowOrdinalsLength, Verticl_Bar); + format += string.Format(" {{0,{0}}} {1}", rowOrdinalsLength, Verticl_Bar); + } + + int k = 0; + for (; k < columns.Length - 1; k++) + { + string horizontal = new String(Horizontal_Bar, lengths[k] + 2); + header += horizontal + Top_Center; + separator += horizontal + Middle_Center; + footer += horizontal + Bottom_Center; + + string cellFormat = string.Format(" {{{0},-{1}}} {2}", k + 1, lengths[k], Verticl_Bar); + formatHeaders += cellFormat; + format += cellFormat; + } + + k = columns.Length - 1; + if (k >= 0) + { + string horizontal = new String(Horizontal_Bar, lengths[k] + 2); + header += horizontal + Top_Right; + separator += horizontal + Middle_Right; + footer += horizontal + Bottom_Right; + + string cellFormat = string.Format(" {{{0},-{1}}} {2}", k + 1, lengths[k], Verticl_Bar); + formatHeaders += cellFormat; + format += cellFormat; + } + + object[] objects = new object[columns.Length + 1]; + + WriteLine(header); + + objects[0] = string.Empty; + for (int i = 0; i < columns.Length; i++) + objects[i + 1] = columns[i]; + WriteLine(formatHeaders, objects); + + WriteLine(separator); + + foreach (DataRow row in dataRows) + { + if (rowOrdinals) + { + int ordinal = 0; + if (string.IsNullOrEmpty(ordinalColumnName)) + ordinal = row.Table.Rows.IndexOf(row); + else + ordinal = (int)row[ordinalColumnName]; + objects[0] = (ordinal > -1 ? ordinal : (int?)null); + } + + for (int i = 0; i < columns.Length; i++) + { + object obj = row[columns[i]]; + + string str = null; + if (toString != null) + { + str = toString(obj, row, columns[i]); + if (str == null) + str = "null"; + } + else + { + str = string.Format("{0}", (obj == DBNull.Value || obj == null ? "null" : obj)); + } + + objects[i + 1] = str; + } + + WriteLine(format, objects); + } + + WriteLine(footer); + + WriteLine(); + } + + private static void PrintRows(DataView dataView, bool rowOrdinals, int top, ValueToStringHandler toString, string[] columnNames) + { + string ordinalColumnName = null; + DataTable dataTable = GetTableFromView(dataView, rowOrdinals, top, ref ordinalColumnName); + PrintRows(dataTable, dataTable.AsEnumerable(), rowOrdinals, top, toString, columnNames, ordinalColumnName); + } + + private static DataTable GetTableFromView(DataView dataView, bool rowOrdinals, int top, ref string ordinalColumnName) + { + DataTable dataTable = dataView.ToTable(); + + if (rowOrdinals) + { + ordinalColumnName = "_ordinal"; + while (dataTable.Columns.Contains(ordinalColumnName)) + ordinalColumnName = "_" + ordinalColumnName; + dataTable.Columns.Add(ordinalColumnName, typeof(int)); + + var it = dataView.GetEnumerator(); + int rowCounter = -1; + while (it.MoveNext()) + { + rowCounter++; + if (top > 0 && rowCounter >= top) + break; + DataRow dataRow = ((DataRowView)it.Current).Row; + dataTable.Rows[rowCounter][ordinalColumnName] = dataRow.Table.Rows.IndexOf(dataRow); + } + } + + return dataTable; + } + + private static DataColumn[] GetColumns(DataTable dataTable, string[] columnNames, string ordinalColumnName = null) + { + if (columnNames != null && columnNames.Length > 0) + return columnNames.Join(dataTable.Columns.Cast(), n => n, c => c.ColumnName, (n, c) => c, StringComparer.CurrentCultureIgnoreCase).Where(c => string.IsNullOrEmpty(ordinalColumnName) || c.ColumnName != ordinalColumnName).ToArray(); + else + return dataTable.Columns.Cast().Where(c => string.IsNullOrEmpty(ordinalColumnName) || c.ColumnName != ordinalColumnName).ToArray(); + } + + private static void CalculateLengths(DataRow row, DataColumn[] columns, int[] lengths, ValueToStringHandler toString) + { + for (int i = 0; i < columns.Length; i++) + { + object obj = row[columns[i]]; + + string str = null; + if (toString != null) + { + str = toString(obj, row, columns[i]); + if (str == null) + str = "null"; + } + else + { + str = string.Format("{0}", (obj == DBNull.Value || obj == null ? "null" : obj)); + } + + if (lengths[i] < str.Length) + lengths[i] = str.Length; + } + } + + #endregion + + #region PrintList DataTable + + public static void PrintList(this DataTable dataTable, params string[] columnNames) + { + PrintList(dataTable, false, 0, null, columnNames: columnNames); + } + + public static void PrintList(this DataTable dataTable, bool rowOrdinals, params string[] columnNames) + { + PrintList(dataTable, rowOrdinals, 0, null, columnNames: columnNames); + } + + public static void PrintList(this DataTable dataTable, int top, params string[] columnNames) + { + PrintList(dataTable, false, top, null, columnNames: columnNames); + } + + public static void PrintList(this DataTable dataTable, bool rowOrdinals, int top, params string[] columnNames) + { + PrintList(dataTable, rowOrdinals, top, null, columnNames: columnNames); + } + + public static void PrintList(this DataTable dataTable, ValueToStringHandler toString, params string[] columnNames) + { + PrintList(dataTable, false, 0, toString, columnNames: columnNames); + } + + public static void PrintList(this DataTable dataTable, bool rowOrdinals, ValueToStringHandler toString, params string[] columnNames) + { + PrintList(dataTable, rowOrdinals, 0, toString, columnNames: columnNames); + } + + public static void PrintList(this DataTable dataTable, int top, ValueToStringHandler toString, params string[] columnNames) + { + PrintList(dataTable, false, top, toString, columnNames: columnNames); + } + + public static void PrintList(this DataTable dataTable, int repeatColumns, RepeatDirection repeatDirection, params string[] columnNames) + { + PrintList(dataTable, false, 0, null, repeatColumns: repeatColumns, repeatDirection: repeatDirection, columnNames: columnNames); + } + + public static void PrintList(this DataTable dataTable, bool rowOrdinals = false, int top = 0, ValueToStringHandler toString = null, int repeatColumns = 2, RepeatDirection repeatDirection = RepeatDirection.Vertical, string delimiter = ": ", params string[] columnNames) + { + PrintListRows(dataTable, dataTable.AsEnumerable(), rowOrdinals, top, toString, repeatColumns, repeatDirection, delimiter, columnNames); + } + + #endregion + + #region PrintList DataView + + public static void PrintList(this DataView dataView, params string[] columnNames) + { + PrintList(dataView, false, 0, null, columnNames: columnNames); + } + + public static void PrintList(this DataView dataView, bool rowOrdinals, params string[] columnNames) + { + PrintList(dataView, rowOrdinals, 0, null, columnNames: columnNames); + } + + public static void PrintList(this DataView dataView, int top, params string[] columnNames) + { + PrintList(dataView, false, top, null, columnNames: columnNames); + } + + public static void PrintList(this DataView dataView, bool rowOrdinals, int top, params string[] columnNames) + { + PrintList(dataView, rowOrdinals, top, null, columnNames: columnNames); + } + + public static void PrintList(this DataView dataView, ValueToStringHandler toString, params string[] columnNames) + { + PrintList(dataView, false, 0, toString, columnNames: columnNames); + } + + public static void PrintList(this DataView dataView, bool rowOrdinals, ValueToStringHandler toString, params string[] columnNames) + { + PrintList(dataView, rowOrdinals, 0, toString, columnNames: columnNames); + } + + public static void PrintList(this DataView dataView, int top, ValueToStringHandler toString, params string[] columnNames) + { + PrintList(dataView, false, top, toString, columnNames: columnNames); + } + + public static void PrintList(this DataView dataView, int repeatColumns, RepeatDirection repeatDirection, params string[] columnNames) + { + PrintList(dataView, false, 0, null, repeatColumns: repeatColumns, repeatDirection: repeatDirection, columnNames: columnNames); + } + + public static void PrintList(this DataView dataView, bool rowOrdinals = false, int top = 0, ValueToStringHandler toString = null, int repeatColumns = 2, RepeatDirection repeatDirection = RepeatDirection.Vertical, string delimiter = ": ", params string[] columnNames) + { + PrintListRows(dataView, rowOrdinals, top, toString, repeatColumns, repeatDirection, delimiter, columnNames); + } + + #endregion + + #region PrintList DataSet + + public static void PrintList(this DataSet dataSet, params string[] columnNames) + { + PrintList(dataSet, false, 0, null, columnNames: columnNames); + } + + public static void PrintList(this DataSet dataSet, bool rowOrdinals, params string[] columnNames) + { + PrintList(dataSet, rowOrdinals, 0, null, columnNames: columnNames); + } + + public static void PrintList(this DataSet dataSet, int top, params string[] columnNames) + { + PrintList(dataSet, false, top, null, columnNames: columnNames); + } + + public static void PrintList(this DataSet dataSet, bool rowOrdinals, int top, params string[] columnNames) + { + PrintList(dataSet, rowOrdinals, top, null, columnNames: columnNames); + } + + public static void PrintList(this DataSet dataSet, ValueToStringHandler toString, params string[] columnNames) + { + PrintList(dataSet, false, 0, toString, columnNames: columnNames); + } + + public static void PrintList(this DataSet dataSet, bool rowOrdinals, ValueToStringHandler toString, params string[] columnNames) + { + PrintList(dataSet, rowOrdinals, 0, toString, columnNames: columnNames); + } + + public static void PrintList(this DataSet dataSet, int top, ValueToStringHandler toString, params string[] columnNames) + { + PrintList(dataSet, false, top, toString, columnNames: columnNames); + } + + public static void PrintList(this DataSet dataSet, int repeatColumns, RepeatDirection repeatDirection, params string[] columnNames) + { + PrintList(dataSet, false, 0, null, repeatColumns: repeatColumns, repeatDirection: repeatDirection, columnNames: columnNames); + } + + public static void PrintList(this DataSet dataSet, bool rowOrdinals = false, int top = 0, ValueToStringHandler toString = null, int repeatColumns = 2, RepeatDirection repeatDirection = RepeatDirection.Vertical, string delimiter = ": ", params string[] columnNames) + { + foreach (DataTable dataTable in dataSet.Tables) + PrintList(dataTable, rowOrdinals, top, toString, repeatColumns, repeatDirection, delimiter, columnNames); + } + + #endregion + + #region PrintList DataRow[] + + public static void PrintList(this DataRow[] dataRows, params string[] columnNames) + { + PrintList(dataRows, false, 0, null, columnNames: columnNames); + } + + public static void PrintList(this DataRow[] dataRows, bool rowOrdinals, params string[] columnNames) + { + PrintList(dataRows, rowOrdinals, 0, null, columnNames: columnNames); + } + + public static void PrintList(this DataRow[] dataRows, int top, params string[] columnNames) + { + PrintList(dataRows, false, top, null, columnNames: columnNames); + } + + public static void PrintList(this DataRow[] dataRows, bool rowOrdinals, int top, params string[] columnNames) + { + PrintList(dataRows, rowOrdinals, top, null, columnNames: columnNames); + } + + public static void PrintList(this DataRow[] dataRows, ValueToStringHandler toString, params string[] columnNames) + { + PrintList(dataRows, false, 0, toString, columnNames: columnNames); + } + + public static void PrintList(this DataRow[] dataRows, bool rowOrdinals, ValueToStringHandler toString, params string[] columnNames) + { + PrintList(dataRows, rowOrdinals, 0, toString, columnNames: columnNames); + } + + public static void PrintList(this DataRow[] dataRows, int top, ValueToStringHandler toString, params string[] columnNames) + { + PrintList(dataRows, false, top, toString, columnNames: columnNames); + } + + public static void PrintList(this DataRow[] dataRows, int repeatColumns, RepeatDirection repeatDirection, params string[] columnNames) + { + PrintList(dataRows, false, 0, null, repeatColumns: repeatColumns, repeatDirection: repeatDirection, columnNames: columnNames); + } + + public static void PrintList(this DataRow[] dataRows, bool rowOrdinals = false, int top = 0, ValueToStringHandler toString = null, int repeatColumns = 2, RepeatDirection repeatDirection = RepeatDirection.Vertical, string delimiter = ": ", params string[] columnNames) + { + PrintListRows((dataRows.Length != 0 ? dataRows[0].Table : null), dataRows, rowOrdinals, top, toString, repeatColumns, repeatDirection, delimiter, columnNames); + } + + #endregion + + #region PrintList Helper Methods + + public enum RepeatDirection + { + Horizontal = 0, + Vertical = 1, + } + + private static void PrintListRows(DataTable dataTable, IEnumerable dataRows, bool rowOrdinals, int top, ValueToStringHandler toString, int repeatColumns, RepeatDirection repeatDirection, string delimiter, string[] columnNames, string ordinalColumnName = null) + { + if (dataTable != null && string.IsNullOrEmpty(dataTable.TableName) == false) + WriteLine("{0}:", dataTable.TableName); + + if (dataRows.Count() == 0) + { + WriteLine("No rows were selected"); + WriteLine(); + return; + } + + DataColumn[] columns = GetColumns(dataTable, columnNames, ordinalColumnName); + if (columns.Length == 0) + { + WriteLine("No columns were selected"); + WriteLine(); + return; + } + + if (top > 0) + dataRows = dataRows.Take(top); + + int columnsLength = columns.Select(c => c.ColumnName.Length).Max(); + + int[] lengths = new int[columns.Length]; + foreach (DataRow row in dataRows) + CalculateLengths(row, columns, lengths, toString); + int rowsLength = lengths.Max(); + + if (rowOrdinals) + { + if (dataRows.Count() > 0) + { + if (columnsLength < 7) // "Ordinal".Length + columnsLength = 7; + + int maxRowOrdinal = 0; + if (string.IsNullOrEmpty(ordinalColumnName)) + maxRowOrdinal = dataRows.Select(row => row.Table.Rows.IndexOf(row)).Max(); + else + maxRowOrdinal = dataRows.Select(row => (int)row[ordinalColumnName]).Max(); + + if (maxRowOrdinal > -1) + { + int rowOrdinalsLength = maxRowOrdinal.ToString().Length; + if (rowsLength < rowOrdinalsLength) + rowsLength = rowOrdinalsLength; + } + } + } + + if (repeatColumns < 1) + repeatColumns = 1; + + if (repeatColumns > dataRows.Count()) + repeatColumns = dataRows.Count(); + + int lastRowFilledCellsCount = dataRows.Count() % repeatColumns; + if (lastRowFilledCellsCount == 0) + lastRowFilledCellsCount = repeatColumns; + int lastRowEmptyCellsCount = repeatColumns - lastRowFilledCellsCount; + int rowsCount = (dataRows.Count() / repeatColumns) + (dataRows.Count() % repeatColumns > 0 ? 1 : 0); + + if (delimiter == null) + delimiter = string.Empty; + + string cellFormat = string.Format(" {{{{0,-{0}}}}}{1}{{{{{{0}},-{2}}}}} {3}", columnsLength, delimiter, rowsLength, Verticl_Bar); + string horizontal = new String(Horizontal_Bar, columnsLength + delimiter.Length + rowsLength + 2); + + string header = Top_Left.ToString(); + string separator = Middle_Left.ToString(); + string footer = Bottom_Left.ToString(); + string format = Verticl_Bar.ToString(); + + int k = 0; + for (; k < repeatColumns - 1; k++) + { + header += horizontal + Top_Center; + separator += horizontal + Middle_Center; + footer += horizontal + Bottom_Center; + format += string.Format(cellFormat, k + 1); + } + + k = repeatColumns - 1; + if (k >= 0) + { + header += horizontal + Top_Right; + separator += horizontal + Middle_Right; + footer += horizontal + Bottom_Right; + format += string.Format(cellFormat, k + 1); + } + + string formatPartial = format; + if (lastRowEmptyCellsCount > 0) + { + formatPartial = Verticl_Bar.ToString(); + for (int i = 0; i < lastRowFilledCellsCount; i++) + formatPartial += string.Format(cellFormat, i + 1); + for (int i = 0; i < lastRowEmptyCellsCount; i++) + formatPartial += new String(' ', columnsLength + delimiter.Length + rowsLength + 2) + Verticl_Bar; + } + + object[] objects = new object[repeatColumns + 1]; + + if (repeatDirection == RepeatDirection.Horizontal) + { + for (int i = 0; i < rowsCount; i++) + { + WriteLine(i == 0 ? header : separator); + var rows = dataRows.Skip(i * repeatColumns).Take(repeatColumns); + PrintListRow(rows, columns, objects, (i < rowsCount - 1 ? format : formatPartial), rowOrdinals, toString, ordinalColumnName); + } + } + else if (repeatDirection == RepeatDirection.Vertical && lastRowEmptyCellsCount == 0) + { + for (int i = 0; i < rowsCount; i++) + { + WriteLine(i == 0 ? header : separator); + var rows = dataRows.Where((r, n) => n % rowsCount == i); + PrintListRow(rows, columns, objects, (i < rowsCount - 1 ? format : formatPartial), rowOrdinals, toString, ordinalColumnName); + } + } + else if (repeatDirection == RepeatDirection.Vertical && lastRowEmptyCellsCount > 0) + { + for (int i = 0; i < rowsCount; i++) + { + WriteLine(i == 0 ? header : separator); + var rows = dataRows.Where((r, n) => + (n < lastRowFilledCellsCount * rowsCount && n % rowsCount == i) || + (n >= lastRowFilledCellsCount * rowsCount && (n - (lastRowFilledCellsCount * rowsCount)) % (rowsCount - 1) == i) + ); + PrintListRow(rows, columns, objects, (i < rowsCount - 1 ? format : formatPartial), rowOrdinals, toString, ordinalColumnName); + } + } + + WriteLine(footer); + + WriteLine(); + } + + private static void PrintListRows(DataView dataView, bool rowOrdinals, int top, ValueToStringHandler toString, int repeatColumns, RepeatDirection repeatDirection, string delimiter, string[] columnNames) + { + string ordinalColumnName = null; + DataTable dataTable = GetTableFromView(dataView, rowOrdinals, top, ref ordinalColumnName); + PrintListRows(dataTable, dataTable.AsEnumerable(), rowOrdinals, top, toString, repeatColumns, repeatDirection, delimiter, columnNames, ordinalColumnName); + } + + private static void PrintListRow(IEnumerable rows, DataColumn[] columns, object[] objects, string format, bool rowOrdinals, ValueToStringHandler toString, string ordinalColumnName) + { + if (rowOrdinals) + { + IEnumerable ordinals = null; + if (string.IsNullOrEmpty(ordinalColumnName)) + ordinals = rows.Select(row => row.Table.Rows.IndexOf(row)); + else + ordinals = rows.Select(row => (int)row[ordinalColumnName]); + + objects[0] = "Ordinal"; + int k = 1; + foreach (int ordinal in ordinals) + objects[k++] = (ordinal > -1 ? ordinal : (int?)null); + WriteLine(format, objects); + } + + for (int i = 0; i < columns.Length; i++) + { + objects[0] = columns[i].ColumnName; + + int k = 1; + foreach (DataRow row in rows) + { + object obj = row[columns[i]]; + + string str = null; + if (toString != null) + { + str = toString(obj, row, columns[i]); + if (str == null) + str = "null"; + } + else + { + str = string.Format("{0}", (obj == DBNull.Value || obj == null ? "null" : obj)); + } + + objects[k++] = str; + } + + WriteLine(format, objects); + } + } + + #endregion + + #region Write, WriteLine + + public delegate void WriteHandler(string value = null, params object[] args); + public delegate void WriteLineHandler(string value = null, params object[] args); + + public static event WriteHandler Write = ConsoleWrite; + public static event WriteLineHandler WriteLine = ConsoleWriteLine; + + public static void SetOutput(WriteHandler writeHandler, WriteLineHandler writeLineHandler) + { + Write = null; + WriteLine = null; + Write += writeHandler; + WriteLine += writeLineHandler; + } + + public static void SetOutputConsole() + { + SetOutput(ConsoleWrite, ConsoleWriteLine); + } + + public static void SetOutputStringBuilder(StringBuilder builder) + { + SetOutput( + (value, args) => StringBuilderWrite(builder, value, args), + (value, args) => StringBuilderWriteLine(builder, value, args) + ); + } + + public static void SetOutputStream(Stream stream) + { + SetOutputStream(stream, Encoding.UTF8); + } + + public static void SetOutputStream(Stream stream, Encoding encoding) + { + SetOutput( + (value, args) => StreamWrite(stream, encoding ?? Encoding.UTF8, value, args), + (value, args) => StreamWriteLine(stream, encoding ?? Encoding.UTF8, value, args) + ); + } + + #region Console + + private static void ConsoleWrite(string value = null, params object[] args) + { + if (string.IsNullOrEmpty(value)) + return; + + if (args == null) + Console.Write(value); + else + Console.Write(value, args); + } + + private static void ConsoleWriteLine(string value = null, params object[] args) + { + if (string.IsNullOrEmpty(value)) + Console.WriteLine(); + else if (args == null) + Console.WriteLine(value); + else + Console.WriteLine(value, args); + } + + #endregion + + #region StringBuilder + + private static void StringBuilderWrite(StringBuilder builder, string value = null, params object[] args) + { + if (string.IsNullOrEmpty(value)) + return; + + if (args == null) + builder.Append(value); + else + builder.AppendFormat(value, args); + } + + private static void StringBuilderWriteLine(StringBuilder builder, string value = null, params object[] args) + { + if (string.IsNullOrEmpty(value)) + { + builder.AppendLine(); + } + else if (args == null) + { + builder.AppendLine(value); + } + else + { + builder.AppendFormat(value, args); + builder.AppendLine(); + } + } + + #endregion + + #region Stream + + private static void StreamWrite(Stream stream, Encoding encoding, string value = null, params object[] args) + { + if (string.IsNullOrEmpty(value)) + return; + + byte[] buffer = null; + + if (args == null) + buffer = encoding.GetBytes(value); + else + buffer = encoding.GetBytes(string.Format(value, args)); + + stream.Write(buffer, 0, buffer.Length); + } + + private static void StreamWriteLine(Stream stream, Encoding encoding, string value = null, params object[] args) + { + byte[] buffer = null; + + if (string.IsNullOrEmpty(value) == false) + { + if (args == null) + buffer = encoding.GetBytes(value); + else + buffer = encoding.GetBytes(string.Format(value, args)); + stream.Write(buffer, 0, buffer.Length); + } + + buffer = encoding.GetBytes(Environment.NewLine); + stream.Write(buffer, 0, buffer.Length); + } + + #endregion + + #endregion + + #region Border + + private const char ASCII_MINUS = '-'; + private const char ASCII_VERTICL_BAR = '|'; + private const char ASCII_PLUS = '+'; + private const char EXTENDED_ASCII_HORIZONTAL_BAR = '─'; + private const char EXTENDED_ASCII_VERTICL_BAR = '│'; + private const char EXTENDED_ASCII_TOP_LEFT = '┌'; + private const char EXTENDED_ASCII_TOP_CENTER = '┬'; + private const char EXTENDED_ASCII_TOP_RIGHT = '┐'; + private const char EXTENDED_ASCII_MIDDLE_LEFT = '├'; + private const char EXTENDED_ASCII_MIDDLE_CENTER = '┼'; + private const char EXTENDED_ASCII_MIDDLE_RIGHT = '┤'; + private const char EXTENDED_ASCII_BOTTOM_LEFT = '└'; + private const char EXTENDED_ASCII_BOTTOM_CENTER = '┴'; + private const char EXTENDED_ASCII_BOTTOM_RIGHT = '┘'; + + private static char Horizontal_Bar; + private static char Verticl_Bar; + private static char Top_Left; + private static char Top_Center; + private static char Top_Right; + private static char Middle_Left; + private static char Middle_Center; + private static char Middle_Right; + private static char Bottom_Left; + private static char Bottom_Center; + private static char Bottom_Right; + + public static void ClearBorder() + { + Horizontal_Bar = ' '; + Verticl_Bar = ' '; + Top_Left = ' '; + Top_Center = ' '; + Top_Right = ' '; + Middle_Left = ' '; + Middle_Center = ' '; + Middle_Right = ' '; + Bottom_Left = ' '; + Bottom_Center = ' '; + Bottom_Right = ' '; + } + + public static void ASCIIBorder() + { + Horizontal_Bar = ASCII_MINUS; + Verticl_Bar = ASCII_VERTICL_BAR; + Top_Left = ASCII_PLUS; + Top_Center = ASCII_PLUS; + Top_Right = ASCII_PLUS; + Middle_Left = ASCII_PLUS; + Middle_Center = ASCII_PLUS; + Middle_Right = ASCII_PLUS; + Bottom_Left = ASCII_PLUS; + Bottom_Center = ASCII_PLUS; + Bottom_Right = ASCII_PLUS; + } + + public static void ExtendedASCIIBorder() + { + Horizontal_Bar = EXTENDED_ASCII_HORIZONTAL_BAR; + Verticl_Bar = EXTENDED_ASCII_VERTICL_BAR; + Top_Left = EXTENDED_ASCII_TOP_LEFT; + Top_Center = EXTENDED_ASCII_TOP_CENTER; + Top_Right = EXTENDED_ASCII_TOP_RIGHT; + Middle_Left = EXTENDED_ASCII_MIDDLE_LEFT; + Middle_Center = EXTENDED_ASCII_MIDDLE_CENTER; + Middle_Right = EXTENDED_ASCII_MIDDLE_RIGHT; + Bottom_Left = EXTENDED_ASCII_BOTTOM_LEFT; + Bottom_Center = EXTENDED_ASCII_BOTTOM_CENTER; + Bottom_Right = EXTENDED_ASCII_BOTTOM_RIGHT; + } + + static PrintDataExtensions() + { + ExtendedASCIIBorder(); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/samples/Sample25/Program.cs b/src/samples/Sample25/Program.cs new file mode 100644 index 0000000000..058d0160c6 --- /dev/null +++ b/src/samples/Sample25/Program.cs @@ -0,0 +1,61 @@ +using System; +using System.Threading.Tasks; +using Elsa.Activities.Console.Extensions; +using Elsa.Models; +using Elsa.Persistence; +using Elsa.Services; +using Microsoft.Extensions.DependencyInjection; +using Sample25.Activities; + +namespace Sample25 +{ + /// + /// A strongly-typed, long-running workflows program demonstrating data processing of sensor input from two channels. + /// + internal static class Program + { + private static async Task Main() + { + // Setup a service collection. + var services = new ServiceCollection() + .AddElsa() + .AddConsoleActivities() + .AddActivity() + .AddActivity() + .AddActivity() + .AddWorkflow() + .BuildServiceProvider(); + + // Resolve services. + var workflowInstanceStore = services.GetRequiredService(); + var invoker = services.GetService(); + + // Create a new workflow instance that awaits sensor input. + // The workflow will be suspended automatically because of the two blocking Channel activities. + var workflowExecutionContext = await invoker.StartAsync(); + + // Get the workflow instance ID so we can resume it when we receive sensor data. + var workflowInstanceId = workflowExecutionContext.Workflow.Id; + + // Simulate receival of sensor 1 data. + Console.WriteLine("Simulate a value for Sensor 1:"); + var sensor1Value = double.Parse(Console.ReadLine()!); + var sensor1Input = new Variables { ["Sensor1"] = new Variable(sensor1Value) }; + + // Resume the workflow with received sensor data. + var workflowInstance1 = await workflowInstanceStore.GetByIdAsync(workflowInstanceId); + await invoker.ResumeAsync(workflowInstance1, sensor1Input); + + // Simulate receival of sensor 2 data. + Console.WriteLine("Simulate a value for Sensor 2:"); + var sensor2Value = double.Parse(Console.ReadLine()!); + var sensor2Input = new Variables { ["Sensor2"] = new Variable(sensor2Value) }; + + // Get the workflow instance to resume execution. + var workflowInstance2 = await workflowInstanceStore.GetByIdAsync(workflowInstanceId); + await invoker.ResumeAsync(workflowInstance2, sensor2Input); + + Console.ReadLine(); + } + } +} \ No newline at end of file diff --git a/src/samples/Sample25/Sample25.csproj b/src/samples/Sample25/Sample25.csproj new file mode 100644 index 0000000000..7ebf9b7b9c --- /dev/null +++ b/src/samples/Sample25/Sample25.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp3.1 + + + + + + +