public void GraphDSLs_Partial_must_be_able_to_build_and_reuse_simple_partial_graphs() { var doubler = GraphDsl.Create(b => { var broadcast = b.Add(new Broadcast <int>(2)); 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); return(new FlowShape <int, int>(broadcast.In, zip.Out)); }); var task = 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 <NotUsed, NotUsed, 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).Item3; task.Wait(TimeSpan.FromSeconds(3)).Should().BeTrue(); task.Result.ShouldAllBeEquivalentTo(new[] { 4, 8, 12 }); }
public void GraphDSLs_Partial_must_be_able_to_build_and_reuse_simple_partial_graphs() { var doubler = GraphDsl.Create(b => { var broadcast = b.Add(new Broadcast <int>(2)); 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); return(new FlowShape <int, int>(broadcast.In, zip.Out)); }); var task = RunnableGraph.FromGraph(GraphDsl.Create(doubler, doubler, Sink.First <IEnumerable <int> >(), ValueTuple.Create, (b, d1, d2, sink) => { var source = Source.From(Enumerable.Range(1, 3)) .MapMaterializedValue(_ => default((NotUsed, NotUsed, Task <IEnumerable <int> >))); 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).Item3;
public void PartialGraph() { var pickMaxOfThree = GraphDsl.Create(b => { var zip1 = b.Add(ZipWith.Apply <int, int, int>(Math.Max)); var zip2 = b.Add(ZipWith.Apply <int, int, int>(Math.Max)); b.From(zip1.Out).To(zip2.In0); return(new UniformFanInShape <int, int>(zip2.Out, zip1.In0, zip1.In1, zip2.In1)); }); var resultSink = Sink.First <int>(); var g = RunnableGraph.FromGraph(GraphDsl.Create(resultSink, (b, sink) => { var pm3 = b.Add(pickMaxOfThree); var s1 = Source.Single(1).MapMaterializedValue <Task <int> >(_ => null); var s2 = Source.Single(2).MapMaterializedValue <Task <int> >(_ => null); var s3 = Source.Single(3).MapMaterializedValue <Task <int> >(_ => null); b.From(s1).To(pm3.In(0)); b.From(s2).To(pm3.In(1)); b.From(s3).To(pm3.In(2)); b.From(pm3.Out).To(sink); return(ClosedShape.Instance); })); var max = WithMaterializer(g.Run); Assert.That(max.Result, Is.EqualTo(3)); }
public void GraphDSLs_Partial_must_be_able_to_build_and_reuse_complex_materializing_partial_graphs() { var summer = Sink.Aggregate <int, int>(0, (i, i1) => i + i1); var doubler = GraphDsl.Create(summer, summer, Tuple.Create, (b, s1, s2) => { var broadcast = b.Add(new Broadcast <int>(3)); var broadcast2 = b.Add(new Broadcast <int>(2)); 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)).To(s1.Inlet); b.From(zip.Out).To(broadcast2.In); b.From(broadcast2.Out(0)).To(s2.Inlet); return(new FlowShape <int, int>(broadcast.In, broadcast2.Out(1))); }); 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 <Tuple <Task <int>, Task <int> >, Tuple <Task <int>, Task <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.Item1, t.Item1.Item2, t.Item2.Item1, t.Item2.Item2); task.Wait(TimeSpan.FromSeconds(3)).Should().BeTrue(); task.Result.ShouldAllBeEquivalentTo(new[] { 6, 12, 12, 24 }); t.Item3.Wait(TimeSpan.FromSeconds(3)).Should().BeTrue(); t.Item3.Result.ShouldAllBeEquivalentTo(new [] { 4, 8, 12 }); }
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); }
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)); }