public void AudioBuffer_Persist() { byte[] rawBytes = new byte[] { 1, 2, 3, 4, 5, 6 }; var wf = WaveFormatEx.Create(WaveFormatTag.WAVE_FORMAT_APTX, 16000, 16, 2, 0, 16000); AudioBuffer buffer = new AudioBuffer(rawBytes, wf); AudioBuffer bresult = default(AudioBuffer); var p1 = Pipeline.Create(); var store = PsiStore.Create(p1, "audio", null); Generators.Return(p1, buffer).Write("audio", store); p1.RunAsync(); var p2 = Pipeline.Create(); var store2 = PsiStore.Open(p2, "audio", null); store2.OpenStream <AudioBuffer>("audio").Do(b => bresult = b); p2.RunAsync(); System.Threading.Thread.Sleep(100); p1.Dispose(); p2.Dispose(); Assert.AreEqual(6, bresult.Length); Assert.AreEqual(6, bresult.Data.Length); Assert.AreEqual(wf, bresult.Format); CollectionAssert.AreEqual(rawBytes, bresult.Data); }
/// <summary> /// Exports data based on the specified export command. /// </summary> /// <param name="exportCommand">The export command.</param> /// <returns>An error code or 0 if success.</returns> public static int Run(Verbs.ExportCommand exportCommand) { const string videoStreamName = "VideoImageCameraView"; const string videoEncodedStreamName = "VideoEncodedImageCameraView"; const string audioStreamName = "Audio"; var pngEncoder = new ImageToPngStreamEncoder(); var decoder = new ImageFromStreamDecoder(); // Create a pipeline using var p = Pipeline.Create(deliveryPolicy: DeliveryPolicy.Unlimited); // Open the psi store for reading var store = PsiStore.Open(p, exportCommand.StoreName, exportCommand.StorePath); // Get references to the various streams. If a stream is not present in the store, // the reference will be null. var accelerometer = store.OpenStreamOrDefault <(Vector3D, DateTime)[]>("Accelerometer");
/// <summary> /// Transport messages from stream to the file system, message queue, etc. /// </summary> /// <param name="stream">Stream name.</param> /// <param name="store">Store name.</param> /// <param name="path">Store path.</param> /// <param name="transport">Transport component (FileWriter, NetMQWriter, etc.)</param> /// <returns>Success flag.</returns> private static int TransportStreamMessages(string stream, string store, string path, Func <Pipeline, IConsumer <dynamic> > transport) { using (var pipeline = Pipeline.Create()) { var data = PsiStore.Open(pipeline, store, Path.GetFullPath(path)); var messages = data.OpenDynamicStream(stream); messages.PipeTo(transport(pipeline)); messages.Count().Do(i => { if (i % 100 == 0) { Console.Write('.'); } }); pipeline.Run(); Console.WriteLine(); } return(0); }
public void CrossFrameworkDeserializeMembers() { if (!File.Exists(OtherTestExe)) { Assert.Inconclusive($"Unable to locate {OtherTestExe} to generate the test store for {nameof(this.CrossFrameworkDeserializeMembers)}."); } this.ExecuteTest(OtherTestExe, $"!{nameof(this.CrossFrameworkSerializeMembers)}"); TypeMembers result = null; using (var p = Pipeline.Create()) { var store = PsiStore.Open(p, "Store2", this.testPath); store.OpenStream <TypeMembers>("TypeMembers").Do(x => result = x.DeepClone()); p.Run(); } Assert.AreEqual(0x7777AAA, result.IntValue); Assert.AreEqual(0xBB, result.ByteValue); Assert.AreEqual(true, result.BoolValue); Assert.AreEqual(0x7CDD, result.ShortValue); Assert.AreEqual(0x77777777EEEEEEEE, result.LongValue); Assert.AreEqual('G', result.CharValue); Assert.AreEqual("This is a test.", result.StringValue); Assert.AreEqual(Math.PI, result.DoubleValue); Assert.AreEqual(-1.234f, result.FloatValue); CollectionAssert.AreEqual(new[] { 0.1f, 2.3f }, result.FloatArray); CollectionAssert.AreEqual(new[] { "one", "two" }, result.StringList); CollectionAssert.AreEqual(new[] { "bbb", "ccc" }, result.StringArraySegment.ToArray()); CollectionAssert.AreEqual(new[] { TimeSpan.Zero, TimeSpan.FromSeconds(1) }, result.Queue); Assert.IsTrue(result.IntComparer.Equals(1952, 1952)); Assert.AreEqual("This is a tuple.", result.Tuple.Item2); Assert.AreEqual(new DateTime(2020, 1, 2), result.ValueTuple.Item1); CollectionAssert.AreEqual(new[] { 782, 33 }, result.ValueTuple.Item2); CollectionAssert.AreEqual(new[] { "three", "four" }, result.StringArray); Assert.IsTrue(result.EnumComparer.Equals(DayOfWeek.Friday, DayOfWeek.Friday)); Assert.AreEqual(2, result.Dictionary.Count); Assert.AreEqual(1, result.Dictionary["one"]); Assert.AreEqual(2, result.Dictionary["two"]); }
private dynamic InstanceToDynamic <T>(T instance) { // Rube Goldberg machine to convert instance to dynamic by // writing to a store (typed) and reading back as dynamic using (var p = Pipeline.Create()) { var gen = Generators.Return(p, instance); var exporter = PsiStore.Create(p, "Test", this.path); exporter.Write(gen.Out, "Data", true); p.Run(); } using (var p = Pipeline.Create()) { var importer = PsiStore.Open(p, "Test", this.path); var data = importer.OpenDynamicStream("Data"); var result = data.ToEnumerable(); p.Run(); return(result.First()); } }
static void Main(string[] args) { using (Pipeline pipeline = Pipeline.Create()) { //var store = Store.Create(pipeline, "deepSpeechTest", ".\\recordings"); //var storeIn = Store.Open(pipeline, "deepSpeechTest", ".\\recordings\\deepSpeechTest.0000"); var storeIn = PsiStore.Open(pipeline, "mmr", "\\Users\\wwilson\\source\\repos\\MultimodalRecorder\\data\\DataStores"); var replay = true; IProducer <AudioBuffer> audioSource; if (replay) { audioSource = storeIn.OpenStream <AudioBuffer>("AudioIn"); //audioSource = null; } else { audioSource = new AudioCapture( pipeline, new AudioCaptureConfiguration() { Format = Microsoft.Psi.Audio.WaveFormat.Create16kHz1Channel16BitPcm(), DropOutOfOrderPackets = true } ); //audioSource.Write("Audio", store); } var dsr = new DeepSpeechRecognizer(pipeline); audioSource.PipeTo(dsr.AudioIn); //audioSource.Do(x => Console.Write('x')); dsr.TextOut.Do(x => Console.WriteLine(x)); Console.WriteLine("Starting..."); pipeline.Run(); Console.ReadLine(); } }
/// <summary> /// Display stream metadata info. /// </summary> /// <param name="stream">Stream name.</param> /// <param name="store">Store name.</param> /// <param name="path">Store path.</param> /// <returns>Success flag.</returns> internal static int DisplayStreamInfo(string stream, string store, string path) { Console.WriteLine($"Stream Metadata (stream={stream}, store={store}, path={path})"); using (var pipeline = Pipeline.Create()) { var data = PsiStore.Open(pipeline, store, Path.GetFullPath(path)); var meta = data.AvailableStreams.First(s => s.Name == stream) as PsiStreamMetadata; if (meta == null) { throw new NotSupportedException("Currently, only Psi Stores are supported."); } Console.WriteLine($"ID: {meta.Id}"); Console.WriteLine($"Name: {meta.Name}"); Console.WriteLine($"TypeName: {meta.TypeName}"); Console.WriteLine($"SupplementalMetadataTypeName: {meta.SupplementalMetadataTypeName}"); Console.WriteLine($"MessageCount: {meta.MessageCount}"); Console.WriteLine($"AverageFrequency: {meta.AverageFrequency}"); Console.WriteLine($"AverageLatency: {meta.AverageLatency}"); Console.WriteLine($"AverageMessageSize: {meta.AverageMessageSize}"); Console.WriteLine($"FirstMessageOriginatingTime: {meta.FirstMessageOriginatingTime}"); Console.WriteLine($"LastMessageOriginatingTime: {meta.LastMessageOriginatingTime}"); Console.WriteLine($"IsClosed: {meta.IsClosed}"); Console.WriteLine($"IsIndexed: {meta.IsIndexed}"); Console.WriteLine($"IsPersisted: {meta.IsPersisted}"); Console.WriteLine($"IsPolymorphic: {meta.IsPolymorphic}"); if (meta.IsPolymorphic) { Console.WriteLine("RuntimeTypes:"); foreach (var type in meta.RuntimeTypes.Values) { Console.WriteLine(type); } } } return(0); }
/// <summary> /// List streams within store. /// </summary> /// <param name="store">Store name.</param> /// <param name="path">Store path.</param> /// <param name="showSize">Indicates whether to show the stream size information.</param> /// <returns>Success flag.</returns> internal static int ListStreams(string store, string path, bool showSize = false) { var stringBuilder = new StringBuilder(); using (var pipeline = Pipeline.Create()) { var data = PsiStore.Open(pipeline, store, Path.GetFullPath(path)); stringBuilder.AppendLine($"{data.AvailableStreams.Count()} Available Streams (store={store}, path={path})"); if (showSize) { stringBuilder.AppendLine("[Avg. Message Size / Total Size]; * marks indexed streams"); foreach (var stream in data.AvailableStreams.OrderByDescending(s => (double)s.MessageCount * s.AverageMessageSize)) { var psiStream = stream as PsiStreamMetadata; if (psiStream == null) { throw new NotSupportedException("Currently, only Psi Stores are supported."); } var isIndexed = psiStream.IsIndexed ? "* " : " "; stringBuilder.AppendLine($"{isIndexed}[{(double)stream.AverageMessageSize / 1024:0.00}Kb / {(stream.AverageMessageSize * (double)stream.MessageCount) / (1024 * 1024):0.00}Mb] {stream.Name} ({stream.TypeName.Split(',')[0]})"); } } else { foreach (var stream in data.AvailableStreams) { stringBuilder.AppendLine($"{stream.Name} ({stream.TypeName.Split(',')[0]})"); } } } Console.WriteLine(stringBuilder.ToString()); return(0); }
/// <summary> /// Print (first n) messages from stream. /// </summary> /// <param name="stream">Stream name.</param> /// <param name="store">Store name.</param> /// <param name="path">Store path.</param> /// <param name="number">Number of messages to display.</param> /// <returns>Success flag.</returns> internal static int DisplayStreamMessages(string stream, string store, string path, int number) { Console.WriteLine($"Stream Messages (stream={stream}, store={store}, path={path}, number={number})"); using (var pipeline = Pipeline.Create()) { var count = 0; var data = PsiStore.Open(pipeline, store, Path.GetFullPath(path)); data.OpenDynamicStream(stream).Do((m, e) => { if (count++ < number) { PrintMessage(m, e); } }); pipeline.RunAsync(); while (count < number) { Thread.Sleep(100); } } return(0); }
public async Task SessionCreateDerivedPartition() { var dataset = new Dataset(); var session = dataset.CreateSession(); // generate a test store var originalStoreName = "OriginalStore"; GenerateTestStore(originalStoreName, StorePath); // add a partition var partition0 = session.AddPsiStorePartition(originalStoreName, StorePath, "Partition_0"); Assert.AreEqual(1, session.Partitions.Count); Assert.AreEqual("Partition_0", session.Partitions[0].Name); int multiplier = 7; // create a derived partition which contains the values from the original stream multiplied by a multiplier await session.CreateDerivedPsiPartitionAsync( (pipeline, importer, exporter, parameter) => { var inputStream = importer.OpenStream <int>("Root"); var derivedStream = inputStream.Select(x => x *parameter).Write("DerivedStream", exporter); }, multiplier, "Partition_1", false, "DerivedStore", StorePath); // should have created a new store partition Assert.AreEqual(2, session.Partitions.Count); Assert.IsTrue(PsiStore.Exists("OriginalStore", StorePath)); Assert.IsTrue(PsiStore.Exists("DerivedStore", StorePath)); // verify partition metadata var originalPartition = session.Partitions[0] as Partition <PsiStoreStreamReader>; Assert.AreEqual("Partition_0", originalPartition.Name); Assert.AreEqual("OriginalStore", originalPartition.StoreName); Assert.AreEqual($"{StorePath}{Path.DirectorySeparatorChar}OriginalStore.0000", originalPartition.StorePath); var derivedPartition = session.Partitions[1] as Partition <PsiStoreStreamReader>; Assert.AreEqual("Partition_1", derivedPartition.Name); Assert.AreEqual("DerivedStore", derivedPartition.StoreName); Assert.AreEqual(StorePath, derivedPartition.StorePath); // collections to capture stream values var originalValues = new List <int>(); var originalTimes = new List <DateTime>(); var derivedValues = new List <int>(); var derivedTimes = new List <DateTime>(); // read stream values from the partitions using (var pipeline = Pipeline.Create()) { var originalPartitionImporter = PsiStore.Open(pipeline, originalPartition.StoreName, originalPartition.StorePath); originalPartitionImporter.OpenStream <int>("Root").Do( (i, e) => { originalValues.Add(i); originalTimes.Add(e.OriginatingTime); }); var derivedPartitionImporter = PsiStore.Open(pipeline, derivedPartition.StoreName, derivedPartition.StorePath); derivedPartitionImporter.OpenStream <int>("DerivedStream").Do( (i, e) => { derivedValues.Add(i); derivedTimes.Add(e.OriginatingTime); }); pipeline.Run(); } // verify that we read the data Assert.AreEqual(10, originalValues.Count); Assert.AreEqual(10, originalTimes.Count); Assert.AreEqual(10, derivedValues.Count); Assert.AreEqual(10, derivedTimes.Count); // verify values from both streams are what we expect CollectionAssert.AreEqual(originalValues.Select(x => x * multiplier).ToList(), derivedValues); CollectionAssert.AreEqual(originalTimes, derivedTimes); }
/// <summary> /// Builds and runs a speech recognition pipeline using the Azure speech recognizer. Requires a valid Cognitive Services /// subscription key. See https://docs.microsoft.com/en-us/azure/cognitive-services/cognitive-services-apis-create-account. /// </summary> /// <remarks> /// If you are getting a <see cref="System.InvalidOperationException"/> with the message 'AzureSpeechRecognizer returned /// OnConversationError with error code: LoginFailed. Original error text: Transport error', this most likely is due to /// an invalid subscription key. Please check your Azure portal at https://portal.azure.com and ensure that you have /// added a subscription to the Azure Speech API on your account. /// </remarks> /// <param name="outputLogPath">The path under which to write log data.</param> /// <param name="inputLogPath">The path from which to read audio input data.</param> public static void RunAzureSpeech(string outputLogPath = null, string inputLogPath = null) { // Create the pipeline object. using (Pipeline pipeline = Pipeline.Create()) { // Use either live audio from the microphone or audio from a previously saved log IProducer <AudioBuffer> audioInput = null; if (inputLogPath != null) { // Open the MicrophoneAudio stream from the last saved log var store = PsiStore.Open(pipeline, Program.AppName, inputLogPath); audioInput = store.OpenStream <AudioBuffer>($"{Program.AppName}.MicrophoneAudio"); } else { // Create the AudioCapture component to capture audio from the default device in 16 kHz 1-channel // PCM format as required by both the voice activity detector and speech recognition components. audioInput = new AudioCapture(pipeline, WaveFormat.Create16kHz1Channel16BitPcm()); } // Perform voice activity detection using the voice activity detector component var vad = new SystemVoiceActivityDetector(pipeline); audioInput.PipeTo(vad); // Create Azure speech recognizer component var recognizer = new AzureSpeechRecognizer(pipeline, new AzureSpeechRecognizerConfiguration() { SubscriptionKey = Program.azureSubscriptionKey, Region = Program.azureRegion }); // The input audio to the Azure speech recognizer needs to be annotated with a voice activity flag. // This can be constructed by using the Psi Join() operator to combine the audio and VAD streams. var annotatedAudio = audioInput.Join(vad); // Subscribe the recognizer to the annotated audio annotatedAudio.PipeTo(recognizer); // Partial and final speech recognition results are posted on the same stream. Here // we use Psi's Where() operator to filter out only the final recognition results. var finalResults = recognizer.Out.Where(result => result.IsFinal); // Print the recognized text of the final recognition result to the console. finalResults.Do(result => Console.WriteLine(result.Text)); // Create a data store to log the data to if necessary. A data store is necessary // only if output logging is enabled. var dataStore = CreateDataStore(pipeline, outputLogPath); // For disk logging only if (dataStore != null) { // Log the microphone audio and recognition results audioInput.Write($"{Program.AppName}.MicrophoneAudio", dataStore); finalResults.Write($"{Program.AppName}.FinalRecognitionResults", dataStore); vad.Write($"{Program.AppName}.VoiceActivity", dataStore); } // Register an event handler to catch pipeline errors pipeline.PipelineExceptionNotHandled += Pipeline_PipelineException; // Register an event handler to be notified when the pipeline completes pipeline.PipelineCompleted += Pipeline_PipelineCompleted; // Run the pipeline pipeline.RunAsync(); // Azure speech transcribes speech to text Console.WriteLine("Say anything"); Console.WriteLine("Press any key to exit..."); Console.ReadKey(true); } }
/// <summary> /// Builds and runs a speech recognition pipeline using the .NET System.Speech recognizer and a set of fixed grammars. /// </summary> /// <param name="outputLogPath">The path under which to write log data.</param> /// <param name="inputLogPath">The path from which to read audio input data.</param> public static void RunSystemSpeech(string outputLogPath = null, string inputLogPath = null) { // Create the pipeline object. using (Pipeline pipeline = Pipeline.Create()) { // Use either live audio from the microphone or audio from a previously saved log IProducer <AudioBuffer> audioInput = null; if (inputLogPath != null) { // Open the MicrophoneAudio stream from the last saved log var store = PsiStore.Open(pipeline, Program.AppName, inputLogPath); audioInput = store.OpenStream <AudioBuffer>($"{Program.AppName}.MicrophoneAudio"); } else { // Create the AudioCapture component to capture audio from the default device in 16 kHz 1-channel // PCM format as required by both the voice activity detector and speech recognition components. audioInput = new AudioCapture(pipeline, WaveFormat.Create16kHz1Channel16BitPcm()); } // Create System.Speech recognizer component var recognizer = new SystemSpeechRecognizer( pipeline, new SystemSpeechRecognizerConfiguration() { Language = "en-US", Grammars = new GrammarInfo[] { new GrammarInfo() { Name = Program.AppName, FileName = "SampleGrammar.grxml" }, }, }); // Subscribe the recognizer to the input audio audioInput.PipeTo(recognizer); // Partial and final speech recognition results are posted on the same stream. Here // we use Psi's Where() operator to filter out only the final recognition results. var finalResults = recognizer.Out.Where(result => result.IsFinal); // Print the final recognition result to the console. finalResults.Do(result => { Console.WriteLine($"{result.Text} (confidence: {result.Confidence})"); }); // Create a data store to log the data to if necessary. A data store is necessary // only if output logging is enabled. var dataStore = CreateDataStore(pipeline, outputLogPath); // For disk logging only if (dataStore != null) { // Log the microphone audio and recognition results audioInput.Write($"{Program.AppName}.MicrophoneAudio", dataStore); finalResults.Write($"{Program.AppName}.FinalRecognitionResults", dataStore); } // Register an event handler to catch pipeline errors pipeline.PipelineExceptionNotHandled += Pipeline_PipelineException; // Register an event handler to be notified when the pipeline completes pipeline.PipelineCompleted += Pipeline_PipelineCompleted; // Run the pipeline pipeline.RunAsync(); // The file SampleGrammar.grxml defines a grammar to transcribe numbers Console.WriteLine("Say any number between 0 and 100"); Console.WriteLine("Press any key to exit..."); Console.ReadKey(true); } }
/// <summary> /// Initializes a new instance of the <see cref="RemoteImporter"/> class. /// </summary> /// <param name="pipeline">Pipeline to which to attach.</param> /// <param name="host">Remote host name.</param> /// <param name="port">TCP port on which to connect (default 11411).</param> /// <param name="allowSequenceRestart">Whether to allow sequence ID restarts upon connection loss/reacquire.</param> /// <remarks>In this case the start is a special behavior that is `DateTime.UtcNow` _at the sending `RemoteExporter`_.</remarks> public RemoteImporter(Pipeline pipeline, string host, int port = RemoteExporter.DefaultPort, bool allowSequenceRestart = true) : this(name => PsiStore.Open(pipeline, name, null), new TimeInterval(DateTime.MinValue, DateTime.MaxValue), true, host, port, $"RemoteImporter_{Guid.NewGuid().ToString()}", null, allowSequenceRestart) { }
/// <summary> /// Initializes a new instance of the <see cref="RemoteImporter"/> class. /// </summary> /// <param name="pipeline">Pipeline to which to attach.</param> /// <param name="replay">Time interval to be replayed from remote source.</param> /// <param name="host">Remote host name.</param> /// <param name="port">TCP port on which to connect (default 11411).</param> /// <param name="allowSequenceRestart">Whether to allow sequence ID restarts upon connection loss/reacquire.</param> public RemoteImporter(Pipeline pipeline, TimeInterval replay, string host, int port = RemoteExporter.DefaultPort, bool allowSequenceRestart = true) : this(name => PsiStore.Open(pipeline, name, null), replay, false, host, port, $"RemoteImporter_{Guid.NewGuid().ToString()}", null, allowSequenceRestart) { }
public void CrossFrameworkDeserialize() { if (!File.Exists(OtherTestExe)) { Assert.Inconclusive($"Unable to locate {OtherTestExe} to generate the test store for {nameof(this.CrossFrameworkDeserializeMembers)}."); } this.ExecuteTest(OtherTestExe, $"!{nameof(this.CrossFrameworkSerialize)}"); int intValue = 0; byte byteValue = 0; bool boolValue = false; short shortValue = 0; long longValue = 0; char charValue = '\0'; string stringValue = null; double doubleValue = 0; float floatValue = 0; float[] floatArray = null; List <string> stringList = null; ArraySegment <string> stringArraySegment = default; Queue <TimeSpan> queue = null; EqualityComparer <int> intComparer = null; Tuple <long, string> tuple = null; (DateTime, Stack <int>)valueTuple = default; Array intArray = null; ICollection stringArray = null; IEqualityComparer enumComparer = null; using (var p = Pipeline.Create()) { var store = PsiStore.Open(p, "Store1", this.testPath); store.Serializers.Register <System.Drawing.Point>(); store.OpenStream <int>("int").Do(x => intValue = x); store.OpenStream <byte>("byte").Do(x => byteValue = x); store.OpenStream <bool>("bool").Do(x => boolValue = x); store.OpenStream <short>("short").Do(x => shortValue = x); store.OpenStream <long>("long").Do(x => longValue = x); store.OpenStream <char>("char").Do(x => charValue = x); store.OpenStream <string>("string").Do(x => stringValue = x); store.OpenStream <double>("double").Do(x => doubleValue = x); store.OpenStream <float>("float").Do(x => floatValue = x); store.OpenStream <float[]>("floatArray").Do(x => floatArray = x.DeepClone()); store.OpenStream <List <string> >("stringList").Do(x => stringList = x.DeepClone()); store.OpenStream <ArraySegment <string> >("stringArraySegment").Do(x => stringArraySegment = x.DeepClone()); store.OpenStream <Queue <TimeSpan> >("queue").Do(x => queue = x.DeepClone()); store.OpenStream <EqualityComparer <int> >("intComparer").Do(x => intComparer = x.DeepClone()); store.OpenStream <Tuple <long, string> >("tuple").Do(x => tuple = x.DeepClone()); store.OpenStream <(DateTime, Stack <int>)>("dateStackTuple").Do(x => valueTuple = x.DeepClone()); store.OpenStream <Array>("intArray").Do(x => intArray = x.DeepClone()); store.OpenStream <ICollection>("stringArray").Do(x => stringArray = x.DeepClone()); store.OpenStream <IEqualityComparer>("enumComparer").Do(x => enumComparer = x.DeepClone()); p.Run(); } Assert.AreEqual(0x7777AAA, intValue); Assert.AreEqual(0xBB, byteValue); Assert.AreEqual(true, boolValue); Assert.AreEqual(0x7CDD, shortValue); Assert.AreEqual(0x77777777EEEEEEEE, longValue); Assert.AreEqual('G', charValue); Assert.AreEqual("This is a test.", stringValue); Assert.AreEqual(Math.PI, doubleValue); Assert.AreEqual(-1.234f, floatValue); CollectionAssert.AreEqual(new[] { 0.1f, 2.3f }, floatArray); CollectionAssert.AreEqual(new[] { "one", "two" }, stringList); CollectionAssert.AreEqual(new[] { "bbb", "ccc" }, stringArraySegment.ToArray()); CollectionAssert.AreEqual(new[] { TimeSpan.Zero, TimeSpan.FromSeconds(1) }, queue); Assert.IsTrue(intComparer.Equals(1952, 1952)); Assert.AreEqual(0x77777777EEEEEEEE, tuple.Item1); Assert.AreEqual("This is a tuple.", tuple.Item2); Assert.AreEqual(new DateTime(2020, 1, 2), valueTuple.Item1); CollectionAssert.AreEqual(new[] { 782, 33 }, valueTuple.Item2); CollectionAssert.AreEqual(new[] { 0, 3 }, intArray); CollectionAssert.AreEqual(new[] { "three", "four" }, stringArray); Assert.IsTrue(enumComparer.Equals(DayOfWeek.Friday, DayOfWeek.Friday)); }
/// <summary> /// Execute task against each message in a stream. /// </summary> /// <param name="stream">Stream name.</param> /// <param name="store">Store name.</param> /// <param name="path">Store path.</param> /// <param name="name">Task name.</param> /// <param name="assemblies">Optional assemblies containing task.</param> /// <param name="args">Additional configuration arguments.</param> /// <returns>Success flag.</returns> internal static int ExecuteTask(string stream, string store, string path, string name, IEnumerable <string> assemblies, IEnumerable <string> args) { Console.WriteLine($"Execute Task (stream={stream}, store={store}, path={path}, name={name}, assemblies={assemblies}, args={args})"); // find task var tasks = LoadTasks(assemblies).Where(t => t.Attribute.Name == name).ToArray(); if (tasks.Length == 0) { throw new Exception($"Could not find task named '{name}'."); } else if (tasks.Length > 1) { throw new Exception($"Ambiguous task name ({tasks.Count()} tasks found named '{name}')."); } var task = tasks[0]; // process task using (var pipeline = Pipeline.Create()) { var importer = store != null?PsiStore.Open(pipeline, store, Path.GetFullPath(path)) : null; // prepare parameters var streamMode = stream?.Length > 0; var messageIndex = -1; var envelopeIndex = -1; var argList = args.ToArray(); var argIndex = 0; var parameterInfo = task.Method.GetParameters(); var parameters = new object[parameterInfo.Length]; for (var i = 0; i < parameterInfo.Length; i++) { var p = parameterInfo[i]; if (p.ParameterType.IsAssignableFrom(typeof(Importer))) { if (importer == null) { throw new ArgumentException("Error: Task requires a store, but no store argument supplied (-s)."); } parameters[i] = importer; } else if (p.ParameterType.IsAssignableFrom(typeof(Pipeline))) { parameters[i] = pipeline; } else if (p.ParameterType.IsAssignableFrom(typeof(Envelope))) { envelopeIndex = i; } else if (streamMode && messageIndex == -1) { messageIndex = i; // assumed first arg } else { Action <string, Func <string, object> > processArgs = (friendlyName, parser) => { if (argIndex < args.Count()) { // take from command-line args try { parameters[i] = parser(argList[argIndex++]); } catch (Exception ex) { throw new ArgumentException($"Error: Parameter '{p.Name}' ({i}) expected {friendlyName}.", ex); } } else { // get value interactively do { try { Console.Write($"{p.Name} ({friendlyName})? "); parameters[i] = parser(Console.ReadLine()); } catch { Console.WriteLine($"Error: Expected {friendlyName}."); } }while (parameters[i] == null); } }; if (p.ParameterType.IsAssignableFrom(typeof(double))) { processArgs("double", v => double.Parse(v)); } else if (p.ParameterType.IsAssignableFrom(typeof(int))) { processArgs("integer", v => int.Parse(v)); } else if (p.ParameterType.IsAssignableFrom(typeof(bool))) { processArgs("boolean", v => bool.Parse(v)); } else if (p.ParameterType.IsAssignableFrom(typeof(DateTime))) { processArgs("datetime", v => DateTime.Parse(v)); } else if (p.ParameterType.IsAssignableFrom(typeof(TimeSpan))) { processArgs("timespan", v => TimeSpan.Parse(v)); } else if (p.ParameterType.IsAssignableFrom(typeof(string))) { processArgs("string", v => v); } else { throw new ArgumentException($"Unexpected parameter type ({p.ParameterType})."); } } } if (streamMode) { if (importer == null) { throw new ArgumentException("Error: Task requires a stream within a store, but no store argument supplied (-s)."); } importer.OpenDynamicStream(stream).Do((m, e) => { if (messageIndex != -1) { parameters[messageIndex] = m; } if (envelopeIndex != -1) { parameters[envelopeIndex] = e; } task.Method.Invoke(null, parameters); }); } else { task.Method.Invoke(null, parameters); } if (importer != null) { pipeline.ProgressReportInterval = TimeSpan.FromSeconds(1); pipeline.RunAsync(null, new Progress <double>(p => Console.WriteLine($"Progress: {p * 100.0:F2}%"))); pipeline.WaitAll(); } } return(0); }