public void PreferredMerge_must_prefer_selected_input_more_than_others() { const int numElements = 10000; var preferred = Source.From(Enumerable.Range(1, numElements).Select(_ => 1)) .MapMaterializedValue <Task <IEnumerable <int> > >(_ => null); var aux = Source.From(Enumerable.Range(1, numElements).Select(_ => 2)) .MapMaterializedValue <Task <IEnumerable <int> > >(_ => null); var result = RunnableGraph.FromGraph(GraphDsl.Create(Sink.First <IEnumerable <int> >(), (b, sink) => { var merge = b.Add(new MergePreferred <int>(3)); b.From(preferred).To(merge.Preferred); b.From(merge.Out).Via(Flow.Create <int>().Grouped(numElements * 2)).To(sink.Inlet); b.From(aux).To(merge.In(0)); b.From(aux).To(merge.In(1)); b.From(aux).To(merge.In(2)); return(ClosedShape.Instance); })).Run(Materializer); result.Wait(TimeSpan.FromSeconds(3)).Should().BeTrue(); result.Result.Count(x => x == 1).Should().Be(numElements); }
public void ZipWithN_must_work_with_3_inputs() { var probe = this.CreateManualSubscriberProbe <int>(); RunnableGraph.FromGraph(GraphDsl.Create(b => { var zip = b.Add(new ZipWithN <int, int>(ints => ints.Sum(), 3)); b.From(Source.Single(1)).To(zip.In(0)); b.From(Source.Single(2)).To(zip.In(1)); b.From(Source.Single(3)).To(zip.In(2)); b.From(zip.Out).To(Sink.FromSubscriber(probe)); return(ClosedShape.Instance); })).Run(Materializer); var subscription = probe.ExpectSubscription(); subscription.Request(5); probe.ExpectNext(6); probe.ExpectComplete(); }
public void A_Graph_with_materialized_value_must_expose_the_materialized_value_as_source(bool autoFusing) { var materializer = CreateMaterializer(autoFusing); var sub = this.CreateManualSubscriberProbe <int>(); var f = RunnableGraph.FromGraph(GraphDsl.Create(FoldSink, (b, fold) => { var source = Source.From(Enumerable.Range(1, 10)).MapMaterializedValue(_ => Task.FromResult(0)); b.From(source).To(fold); b.From(b.MaterializedValue) .Via(Flow.Create <Task <int> >().SelectAsync(4, x => x)) .To(Sink.FromSubscriber(sub).MapMaterializedValue(_ => Task.FromResult(0))); return(ClosedShape.Instance); })).Run(materializer); f.Wait(TimeSpan.FromSeconds(3)).Should().BeTrue(); var r1 = f.Result; sub.ExpectSubscription().Request(1); var r2 = sub.ExpectNext(); r1.Should().Be(r2); }
public void A_Graph_should_build_unzip_zip() { RunnableGraph.FromGraph(GraphDsl.Create(b => { var zip = b.Add(new Zip <int, string>()); var unzip = b.Add(new UnZip <int, string>()); var sink = Sink.AsPublisher <Tuple <int, string> >(false).MapMaterializedValue(_ => NotUsed.Instance); var source = Source.From(new[] { new KeyValuePair <int, string>(1, "a"), new KeyValuePair <int, string>(2, "b"), new KeyValuePair <int, string>(3, "c") }); b.From(source).To(unzip.In); b.From(unzip.Out0).Via(Flow.Create <int>().Select(x => x * 2)).To(zip.In0); b.From(unzip.Out1).To(zip.In1); b.From(zip.Out).To(sink); return(ClosedShape.Instance); })).Run(Materializer); }
public static IRunnableGraph <NotUsed> BuildGraph() { Sink <User, NotUsed> writeAuthors = WriteAuthors2; Sink <HashTagEntity, NotUsed> writeHashTags = WriteHashTags2; Source <Tweet, NotUsed> tweetSource = TweetSource; return(RunnableGraph.FromGraph(GraphDsl.Create(b => { var broadcast = b.Add(new Broadcast <Tweet>(2)); b.From(tweetSource).To(broadcast.In); b.From(broadcast.Out(0)) .Via(Flow.Create <Tweet>().Select(tweet => tweet.CreatedBy)) .To(writeAuthors); b.From(broadcast.Out(1)) .Via(Flow.Create <Tweet>().SelectMany(tweet => tweet.HashTags)) .To(writeHashTags); return ClosedShape.Instance; }))); }
public void A_BidiFlow_must_materialize_its_value() { var f = RunnableGraph.FromGraph(GraphDsl.Create(BidiMaterialized(), (b, bidi) => { var flow1 = b.Add(Flow.Create <string>().Select(int.Parse).MapMaterializedValue(_ => Task.FromResult(0))); var flow2 = b.Add( Flow.Create <long>() .Select(x => ByteString.FromString($"Hello {x}")) .MapMaterializedValue(_ => Task.FromResult(0))); b.AddEdge(flow1.Outlet, bidi.Inlet1); b.AddEdge(bidi.Outlet2, flow1.Inlet); b.AddEdge(bidi.Outlet1, flow2.Inlet); b.AddEdge(flow2.Outlet, bidi.Inlet2); return(ClosedShape.Instance); })).Run(Materializer); f.Wait(TimeSpan.FromSeconds(3)).Should().BeTrue(); f.Result.Should().Be(42); }
public void A_Flow_using_Join_must_allow_for_merge_cycle() { this.AssertAllStagesStopped(() => { var source = Source.Single("lonely traveler").MapMaterializedValue(_ => Task.FromResult("")); var flow1 = Flow.FromGraph(GraphDsl.Create(Sink.First <string>(), (b, sink) => { var merge = b.Add(new Merge <string>(2)); var broadcast = b.Add(new Broadcast <string>(2, true)); b.From(source).To(merge.In(0)); b.From(merge.Out).To(broadcast.In); b.From(broadcast.Out(0)).To(sink); return(new FlowShape <string, string>(merge.In(1), broadcast.Out(1))); })); var t = flow1.Join(Flow.Create <string>()).Run(Materializer); t.Wait(TimeSpan.FromSeconds(3)).Should().BeTrue(); t.Result.Should().Be("lonely traveler"); }, Materializer); }
public void A_Merge_must_work_in_the_happy_case() { this.AssertAllStagesStopped(() => { // Different input sizes(4 and 6) var source1 = Source.From(Enumerable.Range(0, 4)); var source2 = Source.From(Enumerable.Range(4, 6)); var source3 = Source.From(new List <int>()); var probe = this.CreateManualSubscriberProbe <int>(); RunnableGraph.FromGraph(GraphDsl.Create(b => { var m1 = b.Add(new Merge <int>(2)); var m2 = b.Add(new Merge <int>(2)); var sink = Sink.FromSubscriber(probe); b.From(source1).To(m1.In(0)); b.From(m1.Out).Via(Flow.Create <int>().Select(x => x * 2)).To(m2.In(0)); b.From(m2.Out).Via(Flow.Create <int>().Select(x => x / 2).Select(x => x + 1)).To(sink); b.From(source2).To(m1.In(1)); b.From(source3).To(m2.In(1)); return(ClosedShape.Instance); })).Run(Materializer); var subscription = probe.ExpectSubscription(); var collected = new List <int>(); for (var i = 1; i <= 10; i++) { subscription.Request(1); collected.Add(probe.ExpectNext()); } collected.ShouldAllBeEquivalentTo(Enumerable.Range(1, 10)); probe.ExpectComplete(); }, Materializer); }
public void ZipWith_must_work_with_up_to_9_inputs() { // the jvm version uses 19 inputs but we have only 9 this.AssertAllStagesStopped(() => { var probe = this.CreateManualSubscriberProbe <string>(); RunnableGraph.FromGraph(GraphDsl.Create(b => { Func <int, string, int, string, int, string, int, string, int, string> sum9 = (i1, s1, i2, s2, i3, s3, i4, s4, i5) => i1 + s1 + i2 + s2 + i3 + s3 + i4 + s4 + i5; // odd input ports will be Int, even input ports will be String var zipWith = b.Add(ZipWith.Apply(sum9)); b.From(Source.Single(1)).To(zipWith.In0); b.From(Source.Single("2")).To(zipWith.In1); b.From(Source.Single(3)).To(zipWith.In2); b.From(Source.Single("4")).To(zipWith.In3); b.From(Source.Single(5)).To(zipWith.In4); b.From(Source.Single("6")).To(zipWith.In5); b.From(Source.Single(7)).To(zipWith.In6); b.From(Source.Single("8")).To(zipWith.In7); b.From(Source.Single(9)).To(zipWith.In8); b.From(zipWith.Out).To(Sink.FromSubscriber(probe)); return(ClosedShape.Instance); })).Run(Materializer); var subscription = probe.ExpectSubscription(); subscription.Request(1); probe.ExpectNext(Enumerable.Range(1, 9).Aggregate("", (s, i) => s + i)); probe.ExpectComplete(); }, Materializer); }
public Property RoundRobinDistributerSpecs() { return(Prop.ForAll <PositiveInt>(value => { var probes = Enumerable.Range(0, value.Get).Select(_ => CreateTestProbe()).ToList(); var sinks = probes.Select(p => Sink.ActorRef <int>(p, "completed")).ToList(); using (var mat = Sys.Materializer()) { var graph = RunnableGraph.FromGraph(GraphDsl.Create(builder => { var source = Source.From(Enumerable.Range(0, value.Get)); var roundRobin = builder.Add(new RoundRobinFanOut <int>(value.Get)); builder.From(source).To(roundRobin.In); for (int i = 0; i < value.Get; i++) { builder.From(roundRobin.Out(i)).To(sinks[i]); } return ClosedShape.Instance; })); graph.Run(mat); for (int i = 0; i < value.Get; i++) { var msg = probes[i].ExpectMsg <int>(TimeSpan.FromSeconds(3)); msg.Should().Be(i); var completeMsg = probes[i].ExpectMsg <string>(TimeSpan.FromSeconds(3)); completeMsg.Should().Be("completed"); } } })); }
public void GraphDSLs_must_support_balance_merge_parallelization_layouts() { var elements = Enumerable.Range(0, 11).ToList(); var task = RunnableGraph.FromGraph(GraphDsl.Create(Sink.First <IEnumerable <int> >(), (b, sink) => { var balance = b.Add(new Balance <int>(5)); var merge = b.Add(new Merge <int>(5)); var source = Source.From(elements).MapMaterializedValue <Task <IEnumerable <int> > >(_ => null); b.From(source).To(balance.In); for (var i = 0; i < 5; i++) { b.From(balance.Out(i)).To(merge.In(i)); } b.From(merge.Out).Via(Flow.Create <int>().Grouped(elements.Count * 2)).To(sink.Inlet); return(ClosedShape.Instance); })).Run(Materializer); task.Wait(TimeSpan.FromSeconds(3)).Should().BeTrue(); task.Result.ShouldAllBeEquivalentTo(elements); }
CreateRunnableWethaerGraph <TMat>(Source <ITweet, TMat> tweetSource) { var formatUser = Flow.Create <IUser>() .Select(Utils.FormatUser); var formatCoordinates = Flow.Create <ICoordinates>() .Select(Utils.FormatCoordinates); var formatTemperature = Flow.Create <decimal>() .Select(Utils.FormatTemperature); var writeSink = Sink.ForEach <string>(Console.WriteLine); // 1- Throttle at the same rate line 72 (Throttle(10)) // 2- Throttle at different rate line 72 (Throttle(1)) // only 1 message because we have 1 stream source & broadcast = 2 channel with 1 request with 10 msg per second and 1 request with 1 msg per second... but we have only 1 stream source, so it cannot send messages to a different rate thus it sattisfy the lowest requirement. var graph = GraphDsl.Create(b => { var broadcast = b.Add(new Broadcast <ITweet>(2)); var merge = b.Add(new Merge <string>(2)); b.From(broadcast.Out(0)) .Via(Flow.Create <ITweet>().Select(tweet => tweet.CreatedBy) .Throttle(10, TimeSpan.FromSeconds(1), 1, ThrottleMode.Shaping)) .Via(formatUser) .To(merge.In(0)); b.From(broadcast.Out(1)) .Via(Flow.Create <ITweet>().Select(tweet => tweet.Coordinates) //.Buffer(10, OverflowStrategy.DropNew) .Throttle(1, TimeSpan.FromSeconds(1), 1, ThrottleMode.Shaping)) .Via(Flow.Create <ICoordinates>().SelectAsync(5, Utils.GetWeatherAsync)) .Via(formatTemperature) .To(merge.In(1)); return(new FlowShape <ITweet, string>(broadcast.In, merge.Out)); }); return(tweetSource.Where(x => x.Coordinates != null) .Via(graph).To(writeSink)); }
public void A_Balance_must_cancel_upstream_when_downstream_cancel() { this.AssertAllStagesStopped(() => { var p1 = this.CreateManualPublisherProbe <int>(); var c1 = this.CreateManualSubscriberProbe <int>(); var c2 = this.CreateManualSubscriberProbe <int>(); RunnableGraph.FromGraph(GraphDsl.Create(b => { var balance = b.Add(new Balance <int>(2)); var source = Source.FromPublisher(p1.Publisher); b.From(source).To(balance.In); b.From(balance.Out(0)).To(Sink.FromSubscriber(c1)); b.From(balance.Out(1)).To(Sink.FromSubscriber(c2)); return(ClosedShape.Instance); })).Run(Materializer); var bsub = p1.ExpectSubscription(); var sub1 = c1.ExpectSubscription(); var sub2 = c2.ExpectSubscription(); sub1.Request(1); p1.ExpectRequest(bsub, 16); bsub.SendNext(1); c1.ExpectNext(1); sub2.Request(1); bsub.SendNext(2); c2.ExpectNext(2); sub1.Cancel(); sub2.Cancel(); bsub.ExpectCancellation(); }, Materializer); }
public void A_Balance_must_work_with_5_way_balance() { this.AssertAllStagesStopped(() => { var sink = Sink.First <IEnumerable <int> >(); var t = RunnableGraph.FromGraph(GraphDsl.Create(sink, sink, sink, sink, sink, Tuple.Create, (b, s1, s2, s3, s4, s5) => { var balance = b.Add(new Balance <int>(5, true)); var source = Source.From(Enumerable.Range(0, 15)).MapMaterializedValue <Tuple <Task <IEnumerable <int> >, Task <IEnumerable <int> >, Task <IEnumerable <int> >, Task <IEnumerable <int> >, Task <IEnumerable <int> > > >(_ => null); b.From(source).To(balance.In); b.From(balance.Out(0)).Via(Flow.Create <int>().Grouped(15)).To(s1); b.From(balance.Out(1)).Via(Flow.Create <int>().Grouped(15)).To(s2); b.From(balance.Out(2)).Via(Flow.Create <int>().Grouped(15)).To(s3); b.From(balance.Out(3)).Via(Flow.Create <int>().Grouped(15)).To(s4); b.From(balance.Out(4)).Via(Flow.Create <int>().Grouped(15)).To(s5); return(ClosedShape.Instance); })).Run(Materializer); var task = Task.WhenAll(t.Item1, t.Item2, t.Item3, t.Item4, t.Item5); task.Wait(TimeSpan.FromSeconds(3)).Should().BeTrue(); task.Result.SelectMany(l => l).ShouldAllBeEquivalentTo(Enumerable.Range(0, 15)); }, Materializer); }
public Property UnzipEnumerableProps() { return(Prop.ForAll <PositiveInt>(value => { using (var mat = Sys.Materializer()) { var probes = Enumerable.Range(0, value.Get).Select(x => CreateTestProbe()).ToList(); var graph = RunnableGraph.FromGraph(GraphDsl.Create(builder => { var source = Source.From(Enumerable.Range(1, 1).Select(x => Enumerable.Range(0, value.Get).ToList())); var unzipper = builder.Add(new UnzipEnumerable <List <int>, int>(x => x.ToImmutableList(), value.Get)); var sinks = probes.Select(p => Sink.ActorRef <int>(p, "completed")).ToList(); for (int i = 0; i < value.Get; i++) { builder.From(unzipper.Out(i)).To(sinks[i]); } builder.From(source).To(unzipper.In); return ClosedShape.Instance; })); graph.Run(mat); for (int i = 0; i < value.Get; i++) { var msg = probes[i].ExpectMsg <int>(TimeSpan.FromSeconds(3)); msg.Should().Be(i); } } })); }
public void A_Flow_using_Join_must_allow_for_concat_cycle() { this.AssertAllStagesStopped(() => { var flow = Flow.FromGraph(GraphDsl.Create(TestSource.SourceProbe <string>(this), Sink.First <string>(), Keep.Both, (b, source, sink) => { var concat = b.Add(Concat.Create <string>(2)); var broadcast = b.Add(new Broadcast <string>(2, true)); b.From(source).To(concat.In(0)); b.From(concat.Out).To(broadcast.In); b.From(broadcast.Out(0)).To(sink); return(new FlowShape <string, string>(concat.In(1), broadcast.Out(1))); })); var tuple = flow.Join(Flow.Create <string>()).Run(Materializer); var probe = tuple.Item1; var t = tuple.Item2; probe.SendNext("lonely traveler"); t.Wait(TimeSpan.FromSeconds(3)).Should().BeTrue(); t.Result.Should().Be("lonely traveler"); probe.SendComplete(); }, Materializer); }
public void TheSameFlowIsMaterializedAsTwoConnections() { var topHeadSink = Sink.Last <int>(); var bottomHeadSink = Sink.First <int>(); var sharedDoubler = Flow.Create <int>().Select(x => x * 2); var g = RunnableGraph.FromGraph(GraphDsl.Create(topHeadSink, bottomHeadSink, Keep.Both, (builder, topHs, bottomHs) => { var broadcast = builder.Add(new Broadcast <int>(2)); var source = Source.From(Enumerable.Range(1, 10)).MapMaterializedValue <Tuple <Task <int>, Task <int> > >(_ => null); builder.From(source).To(broadcast.In); builder.From(broadcast.Out(0)).Via(sharedDoubler).To(topHs.Inlet); builder.From(broadcast.Out(1)).Via(sharedDoubler).To(bottomHs.Inlet); return(ClosedShape.Instance); })); var(a, b) = WithMaterializer(g.Run); Assert.That(a.Result, Is.EqualTo(20)); Assert.That(b.Result, Is.EqualTo(2)); }
public void GraphDSLs_Partial_must_be_able_to_build_and_reuse_simple_materializing_partial_graphs() { var doubler = GraphDsl.Create(Sink.First <IEnumerable <int> >(), (b, sink) => { var broadcast = b.Add(new Broadcast <int>(3)); var zip = b.Add(ZipWith.Apply((int i, int i1) => i + i1)); b.From(broadcast.Out(0)).To(zip.In0); b.From(broadcast.Out(1)).To(zip.In1); b.From(broadcast.Out(2)).Via(Flow.Create <int>().Grouped(100)).To(sink.Inlet); return(new FlowShape <int, int>(broadcast.In, zip.Out)); }); var t = RunnableGraph.FromGraph(GraphDsl.Create(doubler, doubler, Sink.First <IEnumerable <int> >(), Tuple.Create, (b, d1, d2, sink) => { var source = Source.From(Enumerable.Range(1, 3)) .MapMaterializedValue <Tuple <Task <IEnumerable <int> >, Task <IEnumerable <int> >, Task <IEnumerable <int> > > >(_ => null); b.From(source).To(d1.Inlet); b.From(d1.Outlet).To(d2.Inlet); b.From(d2.Outlet).Via(Flow.Create <int>().Grouped(100)).To(sink.Inlet); return(ClosedShape.Instance); })).Run(Materializer); var task = Task.WhenAll(t.Item1, t.Item2, t.Item3); task.Wait(TimeSpan.FromSeconds(3)).Should().BeTrue(); task.Result[0].ShouldAllBeEquivalentTo(new[] { 1, 2, 3 }); task.Result[1].ShouldAllBeEquivalentTo(new[] { 2, 4, 6 }); task.Result[2].ShouldAllBeEquivalentTo(new[] { 4, 8, 12 }); }
public void A_Graph_with_materialized_value_must_work_properly_with_nesting_and_reusing(bool autoFusing) { var materializer = CreateMaterializer(autoFusing); var compositeSource1 = Source.FromGraph(GraphDsl.Create(FoldFeedbackSource, FoldFeedbackSource, Keep.Both, (b, s1, s2) => { var zip = b.Add(new ZipWith <int, int, int>((i, i1) => i + i1)); b.From(s1.Outlet).Via(Flow.Create <Task <int> >().SelectAsync(4, x => x)).To(zip.In0); b.From(s2.Outlet).Via(Flow.Create <Task <int> >().SelectAsync(4, x => x).Select(x => x * 100)).To(zip.In1); return(new SourceShape <int>(zip.Out)); })); var compositeSource2 = Source.FromGraph(GraphDsl.Create(compositeSource1, compositeSource1, Keep.Both, (b, s1, s2) => { var zip = b.Add(new ZipWith <int, int, int>((i, i1) => i + i1)); b.From(s1.Outlet).To(zip.In0); b.From(s2.Outlet).Via(Flow.Create <int>().Select(x => x * 10000)).To(zip.In1); return(new SourceShape <int>(zip.Out)); })); var t = compositeSource2.ToMaterialized(Sink.First <int>(), Keep.Both).Run(materializer); var task = Task.WhenAll(t.Item1.Item1.Item1, t.Item1.Item1.Item2, t.Item1.Item2.Item1, t.Item1.Item2.Item2, t.Item2); task.Wait(TimeSpan.FromSeconds(3)).Should().BeTrue(); task.Result[0].Should().Be(55); task.Result[1].Should().Be(55); task.Result[2].Should().Be(55); task.Result[3].Should().Be(55); task.Result[4].Should().Be(55555555); }
public void FlowGraphs_when_used_together_should_allow_connecting_source_to_sink_directly() { var probe = TestSubscriber.CreateManualProbe <int>(this); var inSource = Source.AsSubscriber <int>(); var outSink = Sink.AsPublisher <int>(false); var source = Source.FromGraph(GraphDsl.Create(inSource, (b, src) => new SourceShape <int>(src.Outlet))); var sink = Sink.FromGraph(GraphDsl.Create(outSink, (b, s) => new SinkShape <int>(s.Inlet))); var t = RunnableGraph.FromGraph(GraphDsl.Create(source, sink, Keep.Both, (b, src, snk) => { b.From(src.Outlet).To(snk.Inlet); return(ClosedShape.Instance); })).Run(Materializer); var subscriber = t.Item1; var publisher = t.Item2; Source1.RunWith(Sink.AsPublisher <int>(false), Materializer).Subscribe(subscriber); publisher.Subscribe(probe); ValidateProbe(probe, 4, new[] { 0, 1, 2, 3 }); }
public void FlowGraphs_when_turned_into_sources_should_be_reusable_multiple_times() { var probe = TestSubscriber.CreateManualProbe <int>(this); var source = Source.FromGraph(GraphDsl.Create(Source.From(Enumerable.Range(1, 5)), (b, s) => { var o = b.From(s.Outlet).Via(Flow.Create <int>().Select(x => x * 2)); return(new SourceShape <int>(o.Out)); })); RunnableGraph.FromGraph(GraphDsl.Create(source, source, Keep.Both, (b, s1, s2) => { var merge = b.Add(new Merge <int>(2)); b.From(s1.Outlet).To(merge.In(0)); b.From(merge.Out) .To(Sink.FromSubscriber(probe).MapMaterializedValue(_ => Tuple.Create(NotUsed.Instance, NotUsed.Instance))); b.From(s2.Outlet).Via(Flow.Create <int>().Select(x => x * 10)).To(merge.In(1)); return(ClosedShape.Instance); })).Run(Materializer); ValidateProbe(probe, 10, new[] { 2, 4, 6, 8, 10, 20, 40, 60, 80, 100 }); }
public void Setup(BenchmarkContext context) { _system = ActorSystem.Create("Test"); var settings = ActorMaterializerSettings.Create(_system) .WithFuzzingMode(false) .WithSyncProcessingLimit(int.MaxValue) .WithAutoFusing(false); // We fuse manually in this test in the setup _materializer = _system.Materializer(settings); _testElements = Enumerable.Repeat(0, ElementCount).Select(i => new MutableElement(i)).ToArray(); var testSource = Source.FromGraph(new TestSource(_testElements)); var testSink = Sink.FromGraph(new CompletionLatch()); var identityStage = new IdentityStage(); _singleIdentity = Fuse(testSource.Via(identityStage).ToMaterialized(testSink, Keep.Right)); _chainOfIdentities = Fuse( testSource.Via(identityStage) .Via(identityStage) .Via(identityStage) .Via(identityStage) .Via(identityStage) .Via(identityStage) .Via(identityStage) .Via(identityStage) .Via(identityStage) .Via(identityStage) .ToMaterialized(testSink, Keep.Right)); _singleSelect = Fuse(testSource.Select(Add).ToMaterialized(testSink, Keep.Right)); _chainOfSelects = Fuse( testSource.Select(Add) .Select(Add) .Select(Add) .Select(Add) .Select(Add) .Select(Add) .Select(Add) .Select(Add) .Select(Add) .Select(Add) .ToMaterialized(testSink, Keep.Right)); _repeatTakeSelectAndAggregate = Fuse(Source.Repeat(new MutableElement(0)) .Take(ElementCount) .Select(Add) .Select(Add) .Aggregate(new MutableElement(0), (acc, x) => { acc.Value += x.Value; return(acc); }).ToMaterialized(testSink, Keep.Right)); _singleBuffer = Fuse(testSource.Buffer(10, OverflowStrategy.Backpressure).ToMaterialized(testSink, Keep.Right)); _chainOfBuffers = Fuse( testSource.Buffer(10, OverflowStrategy.Backpressure) .Buffer(10, OverflowStrategy.Backpressure) .Buffer(10, OverflowStrategy.Backpressure) .Buffer(10, OverflowStrategy.Backpressure) .Buffer(10, OverflowStrategy.Backpressure) .Buffer(10, OverflowStrategy.Backpressure) .Buffer(10, OverflowStrategy.Backpressure) .Buffer(10, OverflowStrategy.Backpressure) .Buffer(10, OverflowStrategy.Backpressure) .Buffer(10, OverflowStrategy.Backpressure) .ToMaterialized(testSink, Keep.Right)); var broadcastZipFLow = Flow.FromGraph(GraphDsl.Create(b => { var bcast = b.Add(new Broadcast <MutableElement>(2)); var zip = b.Add(new Zip <MutableElement, MutableElement>()); b.From(bcast).To(zip.In0); b.From(bcast).To(zip.In1); var outlet = b.From(zip.Out).Via(Flow.Create <(MutableElement, MutableElement)>().Select(t => t.Item1)); return(new FlowShape <MutableElement, MutableElement>(bcast.In, outlet.Out)); })); var balanceMergeFlow = Flow.FromGraph(GraphDsl.Create(b => { var balance = b.Add(new Balance <MutableElement>(2)); var merge = b.Add(new Merge <MutableElement>(2)); b.From(balance).To(merge); b.From(balance).To(merge); return(new FlowShape <MutableElement, MutableElement>(balance.In, merge.Out)); })); _broadcastZip = Fuse(testSource.Via(broadcastZipFLow).ToMaterialized(testSink, Keep.Right)); _balanceMerge = Fuse(testSource.Via(balanceMergeFlow).ToMaterialized(testSink, Keep.Right)); _broadcastZipBalanceMerge = Fuse(testSource.Via(broadcastZipFLow).Via(balanceMergeFlow).ToMaterialized(testSink, Keep.Right)); }
public async Task RunnableGraphMadeOfBackpressuredQueueAndActorRefWithAckWorksAsExpected() { const int MAX = 4; Source <int, ISourceQueueWithComplete <int> > source = Source.Queue <int>(MAX, OverflowStrategy.Backpressure); TestProbe probe = CreateTestProbe(); Sink <IEnumerable <int>, NotUsed> sink = Sink.ActorRefWithAck <IEnumerable <int> >(probe.Ref, "init", "ack", "complete"); RunnableGraph <ISourceQueueWithComplete <int> > rg = RunnableGraph.FromGraph(GraphDsl.Create(source, sink, Keep.Left, (builder, source_, sink_) => { UniformFanOutShape <int, int> broadcaster = builder.Add(new Broadcast <int>(2)); UniformFanInShape <IEnumerable <int>, IEnumerable <int> > merger = builder.Add(new Merge <IEnumerable <int> >(2)); var f1 = Flow.Create <int>().Aggregate(new List <int>(), (agg, curr) => { agg.Add(curr); return(agg); }).Select(list => list.AsEnumerable()); var f2 = Flow.Create <int>().Aggregate(new List <int>(), (agg, curr) => { agg.Add(curr); return(agg); }).Select(list => list.AsEnumerable()); builder.From(source_).To(broadcaster.In); builder.From(broadcaster.Out(0)).Via(f1).To(merger.In(0)); builder.From(broadcaster.Out(1)).Via(f2).To(merger.In(1)); builder.From(merger.Out).To(sink_); return(ClosedShape.Instance); })); ISourceQueueWithComplete <int> q = rg.Run(_materializer); probe.ExpectMsg <string>((msg, sender) => { if (msg != "init") { throw new InvalidOperationException($"Expected: init. Found: {msg}"); } sender.Tell("ack"); }); await q.OfferAsync(2); await q.OfferAsync(4); await q.OfferAsync(8); await q.OfferAsync(16); q.Complete(); await q.WatchCompletionAsync(); probe.ExpectMsg <IEnumerable <int> >((msg, sender) => { Assert.Equal(new[] { 2, 4, 8, 16 }.AsEnumerable(), msg); sender.Tell("ack"); }); probe.ExpectMsg <IEnumerable <int> >((msg, sender) => { Assert.Equal(new[] { 2, 4, 8, 16 }.AsEnumerable(), msg); sender.Tell("ack"); }); probe.ExpectMsg("complete"); probe.ExpectNoMsg(); }
private static RunnableGraph <ISourceQueueWithComplete <ChannelData <float> > > CreateGraph(IActorRef target, List <ChannelAdjusterConfig> configs, ChannelData <float> sample) { /* * Digital Merger is only necessary when there are additional digitals created and the same goes for the * Broadcast following the Analog Splitter. A broadcast is only required when the analog channel is produces * the additional digitals. Otherwise the analog is pushed straight to the merger +---------------+--------------+-----------------------------------------------------------------------+ | | | SyncData | | | | +-------------+----------------+-------------------------+ | | QueueSource | Channel Data | | FilterFlow | | Channel Data | | | Splitter | Analog | ================ | Analog | | | | | Splitter | Broadcast => Filter | Merger | | | | | | ----------------+----------------+ | | | | | \=> -FullScale | | | | | | | \=> +FullScale | Digital | | | | | | \=> FlatLining | Merger | | | | +-------------+-------------------------+ | | | | | Digitals | | | +---------------+--------------+-----------------------------------------------------------------------+ */ var indices = GetIndices(sample, configs); var number = indices.Count(); var temporalOffsets = configs.Select(x => - x.TemporalOffset).Append(0); var temp = temporalOffsets.Select(x => x - temporalOffsets.Min()).ToList(); var skipIndices = temp.Take(temp.Count - 1).ToList(); var zerothIndex = temp.Last(); var bufferSize = temp.Max() + 1; var skipFlowsNeeded = skipIndices.Any(x => x != 0); var graph = GraphDsl.Create(Source.Queue <ChannelData <float> >(10000, OverflowStrategy.Backpressure), (builder, source) => { //Split channel data into sync data, analogs and digitals var channelDataSplitter = new UnzipWith < ChannelData <float>, ISyncData, IReadOnlyList <DataChannel <float> >, IReadOnlyList <DataChannel <bool> > >(cd => Tuple.Create(cd as ISyncData, cd.Analogs, cd.Digitals)); var channelDataSplitterShape = builder.Add(channelDataSplitter); //Split, filter and reorder the analog channels into the required data channels var analogSplitter = new UnzipEnumerable < IReadOnlyList <DataChannel <float> >, DataChannel <float> >(list => indices.Select(i => list[i]).ToImmutableList(), number ); var analogSplitterShape = builder.Add(analogSplitter); //Re-combine the filtered analog channels var analogMerger = new ZipN <DataChannel <float> >(number); var analogMergerShape = builder.Add(analogMerger); //Digital additional flows var additionalDigitalFlows = new List <FlowShape <DataChannel <float>, DataChannel <bool> > >(); //Create the appropriate analog filtering flows. for (int i = 0; i < configs.Count(); i++) { var skipValue = skipIndices[i]; //Create new flows for the analogs switch (configs[i].Option) { // 1a) Each cfg generates one analog flow... case FilterOption.PassThrough: if (skipFlowsNeeded) { builder.From(analogSplitterShape.Out(i)) .Via( builder.Add( Flow.Create <DataChannel <float> >() .Buffer(bufferSize, OverflowStrategy.Backpressure) .Skip(skipValue) .Log("AnalogLog") ) ) .To(analogMergerShape.In(i)); } else { // Pass through channels can be connected straight from the splitter to the merger. builder.From(analogSplitterShape.Out(i)).To(analogMergerShape.In(i)); } break; case FilterOption.Filter: // Filtered channels create a single flow and connected from the splitter to the merger. var scale = configs[i].Scale; var offset = configs[i].Offset; var filterFlow = skipFlowsNeeded ? Flow.Create <DataChannel <float> >() .Buffer(bufferSize, OverflowStrategy.Backpressure) .Skip(skipValue) .Select(x => new DataChannel <float>(x.Name, x.Value * scale + offset, x.Units)) : Flow.Create <DataChannel <float> >() .Select(x => new DataChannel <float>(x.Name, x.Value * scale + offset, x.Units)); builder.From(analogSplitterShape.Out(i)).Via(builder.Add(filterFlow)).To(analogMergerShape.In(i)); break; // 1b) OR One analog flow and 3 additional digital flows. case FilterOption.CreateDigitals: // Filtered channels that create digitals creates a broadcaster for the analog channel first... var analogBroadcaster = new Broadcast <DataChannel <float> >(4); // ...then three flows for the digitals var d1Flow = builder.Add(Flow.Create <DataChannel <float> >().Select(x => new DataChannel <bool>($"{x.Name}_+FullScale", false))); var d2Flow = builder.Add(Flow.Create <DataChannel <float> >().Select(x => new DataChannel <bool>($"{x.Name}_-FullScale", false))); var d3Flow = builder.Add(Flow.Create <DataChannel <float> >().Select(x => new DataChannel <bool>($"{x.Name}_Flatlining", false))); // ...add the digital flow shapes to be connected later additionalDigitalFlows.Add(d1Flow); additionalDigitalFlows.Add(d2Flow); additionalDigitalFlows.Add(d3Flow); // ...create the broadcaster shape var analogBroadcasterShape = builder.Add(analogBroadcaster); // ...create the filter flow and connect the broadcaster to the merger via the filter var scaler = configs[i].Scale; var offsetter = configs[i].Offset; var filter = skipFlowsNeeded ? Flow.Create <DataChannel <float> >() .Buffer(bufferSize, OverflowStrategy.Backpressure) .Skip(skipValue) .Select(x => new DataChannel <float>(x.Name, x.Value * scaler + offsetter, x.Units)) : Flow.Create <DataChannel <float> >() .Select(x => new DataChannel <float>(x.Name, x.Value * scaler + offsetter, x.Units)); // ...link the analog splitter output to the broadcaster builder.From(analogSplitterShape.Out(i)) .Via(filter) .To(analogBroadcasterShape); builder.From(analogBroadcasterShape.Out(0)).To(analogMergerShape.In(i)); // ...link the broadcaster channels to the additional digital flows builder.From(analogBroadcasterShape.Out(1)).Via(d1Flow); builder.From(analogBroadcasterShape.Out(2)).Via(d2Flow); builder.From(analogBroadcasterShape.Out(3)).Via(d3Flow); break; case FilterOption.NotSet: throw new ArgumentException("Filter Option Not Set is not allowed."); } } //Merge everything back together var channelDataMerger = ZipWith.Apply < ISyncData, IImmutableList <DataChannel <float> >, IReadOnlyList <DataChannel <bool> >, ChannelData <float> >( (sync, analogs, digitals) => new ChannelData <float> ( analogs, digitals, sync.TimeStamp, sync.TachometerCount, sync.MasterSyncIncrement, sync.MasterSyncState, sync.SampleIndex ) ); var channelDataMergerShape = builder.Add(channelDataMerger); //Sink var sink = Sink.ActorRef <ChannelData <float> >(target, false); var sinkShape = builder.Add(sink); //_________Link stages_________ //=====Source===== //Source to the channel data splitter if (skipFlowsNeeded) { builder.From(source) .Via(builder.Add(Flow.Create <ChannelData <float> >().Buffer(bufferSize, OverflowStrategy.Backpressure))) .To(channelDataSplitterShape.In); //=====Splitter===== //Splitter sync data to merger. builder.From(channelDataSplitterShape.Out0) .Via(builder.Add(Flow.Create <ISyncData>().Buffer(bufferSize, OverflowStrategy.Backpressure).Skip(zerothIndex))) .To(channelDataMergerShape.In0); //Splitter analogs to analog splitter. builder.From(channelDataSplitterShape.Out1) .Via(builder.Add(Flow.Create <IReadOnlyList <DataChannel <float> > >().Buffer(bufferSize, OverflowStrategy.Backpressure))) .To(analogSplitterShape.In); //=====AdditionalDigitalFlows===== if (additionalDigitalFlows.Count > 0) { // Additonal Digital Merger var additionalDigitalMerger = new ZipWithN <DataChannel <bool>, IImmutableList <DataChannel <bool> > >(channel => channel, additionalDigitalFlows.Count); var additionalDigitalMergerShape = builder.Add(additionalDigitalMerger); //Combine the input digitals with the generated additional digitals var digitalMerger = ZipWith.Apply <List <DataChannel <bool> >, ImmutableList <DataChannel <bool> >, IReadOnlyList <DataChannel <bool> > >((channel1, channel2) => channel1.Concat(channel2).ToList()); var digitalMergerShape = builder.Add(digitalMerger); //Splitter digitals to digital merger. builder.From(channelDataSplitterShape.Out2) .Via(builder.Add(Flow.Create <IReadOnlyList <DataChannel <bool> > >().Buffer(bufferSize, OverflowStrategy.Backpressure))) .To(digitalMergerShape.In0); // Merge all additional flows together. for (int i = 0; i < additionalDigitalFlows.Count; i++) { builder.From(additionalDigitalFlows[i]).To(additionalDigitalMergerShape.In(i)); } //Additional digitals to digital merger builder.From(additionalDigitalMergerShape.Out).To(digitalMergerShape.In1); //=====DigitalMerger===== //Digital merger to channel data merger builder.From(digitalMergerShape.Out).To(channelDataMergerShape.In2); } else { // Splitter digitals to final merger. builder.From(channelDataSplitterShape.Out2) .Via(builder.Add(Flow.Create <IReadOnlyList <DataChannel <bool> > >().Buffer(bufferSize, OverflowStrategy.Backpressure))) .To(channelDataMergerShape.In2); } // Analog merger to final merger. builder.From(analogMergerShape.Out).To(channelDataMergerShape.In1); //=====Merger===== //Channel Data Merger to sink builder.From(channelDataMergerShape.Out).To(sinkShape); } else { builder.From(source).To(channelDataSplitterShape.In); //=====Splitter===== //Splitter sync data to merger. builder.From(channelDataSplitterShape.Out0).To(channelDataMergerShape.In0); //Splitter analogs to analog splitter. builder.From(channelDataSplitterShape.Out1).To(analogSplitterShape.In); //=====AdditionalDigitalFlows===== if (additionalDigitalFlows.Count > 0) { // Additonal Digital Merger var additionalDigitalMerger = new ZipWithN <DataChannel <bool>, IImmutableList <DataChannel <bool> > >(channel => channel, additionalDigitalFlows.Count); var additionalDigitalMergerShape = builder.Add(additionalDigitalMerger); //Combine the input digitals with the generated additional digitals var digitalMerger = ZipWith.Apply <List <DataChannel <bool> >, ImmutableList <DataChannel <bool> >, IReadOnlyList <DataChannel <bool> > >((channel1, channel2) => channel1.Concat(channel2).ToList()); var digitalMergerShape = builder.Add(digitalMerger); //Splitter digitals to digital merger. builder.From(channelDataSplitterShape.Out2).To(digitalMergerShape.In0); // Merge all additional flows together. for (int i = 0; i < additionalDigitalFlows.Count; i++) { builder.From(additionalDigitalFlows[i]).To(additionalDigitalMergerShape.In(i)); } //Additional digitals to digital merger builder.From(additionalDigitalMergerShape.Out).To(digitalMergerShape.In1); //=====DigitalMerger===== //Digital merger to channel data merger builder.From(digitalMergerShape.Out).To(channelDataMergerShape.In2); } else { // Splitter digitals to final merger. builder.From(channelDataSplitterShape.Out2).To(channelDataMergerShape.In2); } // Analog merger to final merger. builder.From(analogMergerShape.Out).To(channelDataMergerShape.In1); //=====Merger===== //Channel Data Merger to sink builder.From(channelDataMergerShape.Out).To(sinkShape); } return(ClosedShape.Instance); }); return(RunnableGraph.FromGraph(graph)); }