public void TestLoopCell() { DiscreteCellSink <int> ca = DiscreteCell.CreateSink(22); ValueTuple <DiscreteCellLoop <int>, DiscreteCell <int>, DiscreteCell <int> > c = Transaction.Run(() => { DiscreteCellLoop <int> cbLocal = DiscreteCell.CreateLoop <int>(); DiscreteCell <int> ccLocal = ca.Map(x => x % 10).Lift(cbLocal, (x, y) => x * y); DiscreteCell <int> cbOut = ca.Map(x => x / 10); cbLocal.Loop(cbOut); return(ValueTuple.Create(cbLocal, cbOut, ccLocal)); }); DiscreteCellLoop <int> cb = c.Item1; DiscreteCell <int> cb2 = c.Item2; DiscreteCell <int> cc = c.Item3; List <int> @out = new List <int>(); List <int> out2 = new List <int>(); List <int> out3 = new List <int>(); IListener l = cb.Listen(@out.Add); IListener l2 = cb2.Listen(out2.Add); IListener l3 = cc.Listen(out3.Add); ca.Send(2); ca.Send(52); l3.Unlisten(); l2.Unlisten(); l.Unlisten(); CollectionAssert.AreEqual(new[] { 2, 0, 5 }, @out.ToArray()); CollectionAssert.AreEqual(new[] { 2, 0, 5 }, out2.ToArray()); CollectionAssert.AreEqual(new[] { 4, 0, 10 }, out3.ToArray()); }
public void TestLoop() { Tuple <DiscreteCell <int>, DiscreteCellStreamSink <int> > result = Transaction.Run(() => { DiscreteCellLoop <int> loop = DiscreteCell.CreateLoop <int>(); DiscreteCell <int> cLocal = loop.Map(v => v * 5); DiscreteCellStreamSink <int> sLocal = new DiscreteCellStreamSink <int>(); loop.Loop(sLocal.Hold(3)); return(Tuple.Create(cLocal, sLocal)); }); DiscreteCell <int> c = result.Item1; DiscreteCellStreamSink <int> s = result.Item2; List <int> output1 = new List <int>(); List <int> output2 = new List <int>(); IListener l = c.Listen(output1.Add); IListener l2 = c.Updates.Listen(output2.Add); s.Send(5); s.Send(7); l2.Unlisten(); l.Unlisten(); CollectionAssert.AreEqual(new[] { 15, 25, 35 }, output1); CollectionAssert.AreEqual(new[] { 25, 35 }, output2); }
public void TestDiscreteCellLoopThrowsException() { //TODO: adjust the types so that loops can only be created safely through the type system Exception actual = null; try { StreamSink <int> s = new StreamSink <int>(); DiscreteCell <int> cell = Transaction.Run(() => { DiscreteCellLoop <int> cellLoop = new DiscreteCellLoop <int>(); DiscreteCell <int> cellLocal = cellLoop.Updates.Filter(v => v % 2 == 0).Map(v => v + 1).Merge(s, (_, r) => r).Hold(1); cellLoop.Loop(cellLocal); return(cellLocal); }); List <int> @out = new List <int>(); IListener l = cell.Listen(@out.Add); s.Send(3); s.Send(4); s.Send(7); s.Send(8); l.Unlisten(); } catch (Exception e) { actual = e; } Assert.IsNotNull(actual); Assert.AreEqual("A dependency cycle was detected.", actual.Message); }
public void TestSwitchSValuesLoop() { DiscreteCellStreamSink <IReadOnlyList <TestObject> > streamSink = new DiscreteCellStreamSink <IReadOnlyList <TestObject> >(); DiscreteCell <IReadOnlyList <TestObject> > cell = Transaction.Run(() => { DiscreteCellLoop <IReadOnlyList <TestObject> > cellLoop = new DiscreteCellLoop <IReadOnlyList <TestObject> >(); DiscreteCell <IReadOnlyList <TestObject> > cellLocal = streamSink.Map <Func <IReadOnlyList <TestObject>, IReadOnlyList <TestObject> > >(v => _ => v) .Merge(cellLoop.Map(oo => oo.Select(o => o.Output).Lift(vv => vv.Sum()).Values).SwitchS().Filter(sum => sum < 50).MapTo <Func <IReadOnlyList <TestObject>, IReadOnlyList <TestObject> > >(v => v.Concat(new[] { new TestObject() }).ToArray()), (f, g) => v => g(f(v))) .Snapshot(cellLoop, (f, v) => f(v)) .Hold(Enumerable.Range(1, 10).Select(_ => new TestObject()).ToArray()); cellLoop.Loop(cellLocal); return(cellLocal); }); List <int> objectCounts = new List <int>(); objectCounts.Add(-1); cell.Listen(vv => objectCounts.Add(vv.Count)); objectCounts.Add(-1); cell.Cell.Sample()[2].Input1.Send(1); objectCounts.Add(-1); cell.Cell.Sample()[1].Input1.Send(-20); objectCounts.Add(-1); streamSink.Send(new TestObject[0]); objectCounts.Add(-1); // Ideal result, likely not achievable. //CollectionAssert.AreEquivalent(new[] { -1, 10, -1, 11, -1, 15, -1, 10, -1 }, objectCounts); // Glitchy result, also not returned by this method. //CollectionAssert.AreEquivalent(new[] { -1, 10, -1, 11, -1, 12, 13, 14, 15, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1 }, objectCounts); // Incorrect result we will see. CollectionAssert.AreEquivalent(new[] { -1, 10, -1, 11, -1, 12, -1, 0, -1 }, objectCounts); }
public void TestSwitchC() { StreamSink <Sc> ssc = Stream.CreateSink <Sc>(); // Split each field out of SB so we can update multiple behaviors in a // single transaction. DiscreteCell <char> ca = ssc.Map(s => s.A).FilterMaybe().Hold('A'); DiscreteCell <char> cb = ssc.Map(s => s.B).FilterMaybe().Hold('a'); DiscreteCell <DiscreteCell <char> > csw = ssc.Map(s => s.Sw).FilterMaybe().Hold(ca); DiscreteCell <char> co = csw.SwitchC(); List <char> @out = new List <char>(); IListener l = co.Listen(@out.Add); ssc.Send(new Sc(Maybe.Just('B'), Maybe.Just('b'), Maybe.Nothing <DiscreteCell <char> >())); ssc.Send(new Sc(Maybe.Just('C'), Maybe.Just('c'), Maybe.Just(cb))); ssc.Send(new Sc(Maybe.Just('D'), Maybe.Just('d'), Maybe.Nothing <DiscreteCell <char> >())); ssc.Send(new Sc(Maybe.Just('E'), Maybe.Just('e'), Maybe.Just(ca))); ssc.Send(new Sc(Maybe.Just('F'), Maybe.Just('f'), Maybe.Nothing <DiscreteCell <char> >())); ssc.Send(new Sc(Maybe.Nothing <char>(), Maybe.Nothing <char>(), Maybe.Just(cb))); ssc.Send(new Sc(Maybe.Nothing <char>(), Maybe.Nothing <char>(), Maybe.Just(ca))); ssc.Send(new Sc(Maybe.Just('G'), Maybe.Just('g'), Maybe.Just(cb))); ssc.Send(new Sc(Maybe.Just('H'), Maybe.Just('h'), Maybe.Just(ca))); ssc.Send(new Sc(Maybe.Just('I'), Maybe.Just('i'), Maybe.Just(ca))); l.Unlisten(); CollectionAssert.AreEqual(new[] { 'A', 'B', 'c', 'd', 'E', 'F', 'f', 'F', 'g', 'H', 'I' }, @out); }
public void TestSwitchCDeferredLoopWithBetterApi() { DiscreteCellStreamSink <IReadOnlyList <TestObject> > streamSink = new DiscreteCellStreamSink <IReadOnlyList <TestObject> >(); DiscreteCell <IReadOnlyList <TestObject> > cell = Transaction.Run(() => { DiscreteCellLoop <IReadOnlyList <TestObject> > cellLoop = new DiscreteCellLoop <IReadOnlyList <TestObject> >(); DiscreteCell <IReadOnlyList <TestObject> > cellLocal = streamSink .OrElse(Operational.Defer(cellLoop.Map(oo => oo.Select(o => o.Output).Lift(vv => vv.Sum())).SwitchCWithDeferredValues()).Filter(sum => sum < 50).Snapshot(cellLoop, (_, items) => (IReadOnlyList <TestObject>)items.Concat(new[] { new TestObject() }).ToArray())) .Hold(Enumerable.Range(1, 10).Select(_ => new TestObject()).ToArray()); cellLoop.Loop(cellLocal); return(cellLocal); }); List <int> objectCounts = new List <int>(); objectCounts.Add(-1); cell.Listen(vv => objectCounts.Add(vv.Count)); objectCounts.Add(-1); cell.Cell.Sample()[2].Input1.Send(1); objectCounts.Add(-1); cell.Cell.Sample()[1].Input1.Send(-20); objectCounts.Add(-1); streamSink.Send(new TestObject[0]); objectCounts.Add(-1); // Ideal result, likely not achievable. //CollectionAssert.AreEquivalent(new[] { -1, 10, -1, 11, -1, 15, -1, 10, -1 }, objectCounts); // Glitchy result, but correct otherwise. CollectionAssert.AreEquivalent(new[] { -1, 10, -1, 11, -1, 12, 13, 14, 15, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1 }, objectCounts); }
public void TestSwitchCSimultaneous() { Sc2 sc1 = new Sc2(0); DiscreteCellSink <Sc2> csc = DiscreteCell.CreateSink(sc1); DiscreteCell <int> co = csc.Map <DiscreteCell <int> >(b => b.C).SwitchC(); List <int> @out = new List <int>(); IListener l = co.Listen(@out.Add); Sc2 sc2 = new Sc2(3); Sc2 sc3 = new Sc2(4); Sc2 sc4 = new Sc2(7); sc1.C.Send(1); sc1.C.Send(2); csc.Send(sc2); sc1.C.Send(3); sc2.C.Send(4); sc3.C.Send(5); csc.Send(sc3); sc3.C.Send(6); sc3.C.Send(7); Transaction.RunVoid(() => { sc3.C.Send(2); csc.Send(sc4); sc4.C.Send(8); }); sc4.C.Send(9); l.Unlisten(); CollectionAssert.AreEqual(new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, @out); }
/// <summary> /// A timer that fires at the specified time. /// </summary> /// <param name="t">The time to fire at.</param> /// <returns>A stream which fires at the specified time.</returns> public Stream <T> At(DiscreteCell <Maybe <T> > t) { StreamSink <T> alarm = new StreamSink <T>(); Maybe <ITimer> currentTimer = Maybe.None; IListener l = t.Listen( m => { currentTimer.MatchSome(timer => timer.Cancel()); currentTimer = m.Match( time => Maybe.Some( this.implementation.SetTimer( time, () => { lock (this.eventQueue) { this.eventQueue.Enqueue(new Event(time, alarm)); } // Open and close a transaction to trigger queued // events to run. Transaction.RunVoid(() => { }); })), () => Maybe.None); }); return(alarm.AttachListener(l)); }
public void TestHold() { StreamSink <int> s = Stream.CreateSink <int>(); DiscreteCell <int> c = s.Hold(0); List <int> @out = new List <int>(); IListener l = c.Listen(@out.Add); s.Send(2); s.Send(9); l.Unlisten(); CollectionAssert.AreEqual(new[] { 0, 2, 9 }, @out); }
public void TestHold() { StreamSink <char> s = Stream.CreateSink <char>(); DiscreteCell <char> c = s.Hold(' '); List <char> @out = new List <char>(); IListener l = c.Listen(@out.Add); s.Send('C'); s.Send('B'); s.Send('A'); l.Unlisten(); CollectionAssert.AreEqual(new[] { ' ', 'C', 'B', 'A' }, @out); }
public void TestMapLateListen() { DiscreteCellSink <int> c = DiscreteCell.CreateSink(6); List <string> @out = new List <string>(); DiscreteCell <string> cm = c.Map(x => x.ToString()); c.Send(2); IListener l = cm.Listen(@out.Add); c.Send(8); l.Unlisten(); CollectionAssert.AreEqual(new[] { "2", "8" }, @out); }
public void TestLiftGlitch() { DiscreteCellSink <int> c1 = DiscreteCell.CreateSink(1); DiscreteCell <int> c3 = c1.Map(x => x * 3); DiscreteCell <int> c5 = c1.Map(x => x * 5); DiscreteCell <string> c = c3.Lift(c5, (x, y) => x + " " + y); List <string> @out = new List <string>(); IListener l = c.Listen(@out.Add); c1.Send(2); l.Unlisten(); CollectionAssert.AreEqual(new[] { "3 5", "6 10" }, @out); }
public void TestAccum() { StreamSink <int> sa = Stream.CreateSink <int>(); List <int> @out = new List <int>(); DiscreteCell <int> sum = sa.Accum(100, (a, s) => a + s); IListener l = sum.Listen(@out.Add); sa.Send(5); sa.Send(7); sa.Send(1); sa.Send(2); sa.Send(3); l.Unlisten(); CollectionAssert.AreEqual(new[] { 100, 105, 112, 113, 115, 118 }, @out); }
public void TestLiftListLarge() { IReadOnlyList <DiscreteCellSink <int> > cellSinks = Enumerable.Range(0, 500).Select(_ => DiscreteCell.CreateSink(1)).ToArray(); DiscreteCell <int> sum = cellSinks.Lift().Map(v => v.Sum()); List <int> @out = new List <int>(); IListener l = sum.Listen(@out.Add); cellSinks[4].Send(5); cellSinks[5].Send(5); Transaction.RunVoid(() => { cellSinks[9].Send(5); cellSinks[17].Send(5); cellSinks[41].Send(5); cellSinks[48].Send(5); }); l.Unlisten(); CollectionAssert.AreEqual(new[] { 500, 504, 508, 524 }, @out); }
public void TestLiftInSwitchC() { IReadOnlyList <Test> list1 = new[] { new Test(0), new Test(1), new Test(2), new Test(3), new Test(4) }; IReadOnlyList <Test> list2 = new[] { new Test(5), new Test(6), new Test(7), new Test(8), new Test(9) }; DiscreteCellSink <IReadOnlyList <Test> > v = DiscreteCell.CreateSink(list1); DiscreteCell <IReadOnlyList <int> > c = v.Map(oo => oo.Select(o => o.Value).Lift()).SwitchC(); List <IReadOnlyList <int> > streamOutput = new List <IReadOnlyList <int> >(); IListener l = c.Updates.Listen(streamOutput.Add); List <IReadOnlyList <int> > cellOutput = new List <IReadOnlyList <int> >(); IListener l2 = c.Listen(cellOutput.Add); list1[2].Value.Send(12); list2[1].Value.Send(16); list1[4].Value.Send(14); Transaction.RunVoid(() => { list2[2].Value.Send(17); list1[0].Value.Send(10); v.Send(list2); }); list1[3].Value.Send(13); list2[3].Value.Send(18); l2.Unlisten(); l.Unlisten(); Assert.AreEqual(4, streamOutput.Count); Assert.AreEqual(5, cellOutput.Count); CollectionAssert.AreEqual(new[] { 0, 1, 2, 3, 4 }, cellOutput[0]); CollectionAssert.AreEqual(new[] { 0, 1, 12, 3, 4 }, streamOutput[0]); CollectionAssert.AreEqual(new[] { 0, 1, 12, 3, 4 }, cellOutput[1]); CollectionAssert.AreEqual(new[] { 0, 1, 12, 3, 14 }, streamOutput[1]); CollectionAssert.AreEqual(new[] { 0, 1, 12, 3, 14 }, cellOutput[2]); CollectionAssert.AreEqual(new[] { 5, 16, 17, 8, 9 }, streamOutput[2]); CollectionAssert.AreEqual(new[] { 5, 16, 17, 8, 9 }, cellOutput[3]); CollectionAssert.AreEqual(new[] { 5, 16, 17, 18, 9 }, streamOutput[3]); CollectionAssert.AreEqual(new[] { 5, 16, 17, 18, 9 }, cellOutput[4]); }
public void TestSwitchCLoop() { Exception actual = null; try { DiscreteCellStreamSink <IReadOnlyList <TestObject> > streamSink = new DiscreteCellStreamSink <IReadOnlyList <TestObject> >(); DiscreteCell <IReadOnlyList <TestObject> > cell = Transaction.Run(() => { DiscreteCellLoop <IReadOnlyList <TestObject> > cellLoop = new DiscreteCellLoop <IReadOnlyList <TestObject> >(); DiscreteCell <IReadOnlyList <TestObject> > cellLocal = streamSink.Map <Func <IReadOnlyList <TestObject>, IReadOnlyList <TestObject> > >(v => _ => v) .Merge(cellLoop.Map(oo => oo.Select(o => o.Output).Lift(vv => vv.Sum())).SwitchC().Updates.Filter(sum => sum < 50).MapTo <Func <IReadOnlyList <TestObject>, IReadOnlyList <TestObject> > >(v => v.Concat(new[] { new TestObject() }).ToArray()), (f, g) => v => g(f(v))) .Snapshot(cellLoop, (f, v) => f(v)) .Hold(Enumerable.Range(1, 10).Select(_ => new TestObject()).ToArray()); cellLoop.Loop(cellLocal); return(cellLocal); }); List <int> objectCounts = new List <int>(); objectCounts.Add(-1); cell.Listen(vv => objectCounts.Add(vv.Count)); objectCounts.Add(-1); cell.Cell.Sample()[2].Input1.Send(1); objectCounts.Add(-1); cell.Cell.Sample()[1].Input1.Send(-20); objectCounts.Add(-1); streamSink.Send(new TestObject[0]); objectCounts.Add(-1); } catch (AggregateException e) { actual = e.InnerExceptions.FirstOrDefault(ex => ex.Message == "A dependency cycle was detected."); } catch (Exception e) { actual = e; } Assert.IsNotNull(actual); Assert.AreEqual("A dependency cycle was detected.", actual.Message); }
public async Task TestListenAsync() { DiscreteCellSink <int> a = DiscreteCell.CreateSink(1); DiscreteCell <int> a1 = a.Map(x => x + 1); DiscreteCell <int> a2 = a.Map(x => x * 2); ValueTuple <List <int>, DiscreteCellLoop <int>, IListener> resultsAndCalled = Transaction.Run(() => { DiscreteCell <int> result = a1.Lift(a2, (x, y) => x + y); Stream <Unit> incrementStream = Operational.Value(result.Cell).MapTo(Unit.Value); StreamSink <Unit> decrementStream = Stream.CreateSink <Unit>(); DiscreteCellLoop <int> calledLoop = DiscreteCell.CreateLoop <int>(); calledLoop.Loop(incrementStream.MapTo(1).Merge(decrementStream.MapTo(-1), (x, y) => x + y).Snapshot(calledLoop.Cell, (u, c) => c + u).Hold(0)); List <int> r = new List <int>(); IListener l = result.Listen(v => { Task.Run(async() => { await Task.Delay(900); r.Add(v); decrementStream.Send(Unit.Value); }); }); return(ValueTuple.Create(r, calledLoop, l)); }); // ReSharper disable once UnusedVariable List <int> results = resultsAndCalled.Item1; DiscreteCell <int> called = resultsAndCalled.Item2; List <int> calledResults = new List <int>(); IListener l2 = called.Listen(calledResults.Add); await Task.Delay(500); a.Send(2); await Task.Delay(500); a.Send(3); await Task.Delay(2500); l2.Unlisten(); resultsAndCalled.Item3.Unlisten(); }
public void TestDiscreteCellLoop() { StreamSink <int> s = new StreamSink <int>(); DiscreteCell <int> cell = Transaction.Run(() => { DiscreteCellLoop <int> cellLoop = new DiscreteCellLoop <int>(); DiscreteCell <int> cellLocal = s.Snapshot(cellLoop, (x, y) => x + y).Hold(1); cellLoop.Loop(cellLocal); return(cellLocal); }); List <int> @out = new List <int>(); IListener l = cell.Listen(@out.Add); s.Send(3); s.Send(4); s.Send(7); s.Send(8); l.Unlisten(); CollectionAssert.AreEqual(new[] { 1, 4, 8, 15, 23 }, @out); }
public void SwitchCCatchFirst() { List <int> output = new List <int>(); ValueTuple <DiscreteCell <int>, DiscreteCellSink <int>, DiscreteCellSink <int>, DiscreteCellSink <DiscreteCell <int> >, IListener> t = Transaction.Run(() => { DiscreteCellSink <int> c1 = DiscreteCell.CreateSink(1); DiscreteCellSink <int> c2 = DiscreteCell.CreateSink(11); DiscreteCellSink <DiscreteCell <int> > s = DiscreteCell.CreateSink(c1.AsDiscreteCell()); DiscreteCell <int> c = s.SwitchC(); c1.Send(2); c2.Send(12); s.Send(c2); IListener l = c.Listen(output.Add); return(ValueTuple.Create(c, c1, c2, s, l)); }); t.Item2.Send(3); t.Item3.Send(13); Transaction.RunVoid(() => { t.Item2.Send(4); t.Item3.Send(14); t.Item4.Send(t.Item2); }); t.Item2.Send(5); t.Item3.Send(15); t.Item5.Unlisten(); CollectionAssert.AreEqual(new[] { 12, 13, 4, 5 }, output); }
public void TestLiftListLargeManyUpdates() { IReadOnlyList <DiscreteCellSink <int> > cellSinks = Enumerable.Range(0, 500).Select(_ => DiscreteCell.CreateSink(1)).ToArray(); DiscreteCell <int> sum = cellSinks.Lift().Map(v => v.Sum()); List <int> @out = new List <int>(); IListener l = sum.Listen(@out.Add); for (int i = 0; i < 100; i++) { int n = i; cellSinks[n * 5].Send(5); cellSinks[n * 5 + 1].Send(5); Transaction.RunVoid(() => { cellSinks[n * 5 + 2].Send(5); cellSinks[n * 5 + 3].Send(5); cellSinks[n * 5 + 4].Send(5); }); } l.Unlisten(); IReadOnlyList <int> expected = new[] { 500 }.Concat(Enumerable.Range(0, 100).SelectMany(n => new[] { 500 + 20 * n + 4, 500 + 20 * n + 8, 500 + 20 * n + 20 })).ToArray(); CollectionAssert.AreEqual(expected, @out); }
public Implementation(PetrolPumpWindow petrolPump) { SComboBox <IPump> logic = new SComboBox <IPump>( new IPump[] { new LifeCyclePump(), new AccumulatePulsesPump(), new ShowDollarsPump(), new ClearSalePump(), new KeypadPump(), new PresetAmountPump() }, p => p.GetType().FullName); petrolPump.LogicComboBoxPlaceholder.Children.Add(logic); STextField textPrice1 = new STextField("2.149") { Width = 100 }; petrolPump.Price1Placeholder.Children.Add(textPrice1); STextField textPrice2 = new STextField("2.341") { Width = 100 }; petrolPump.Price2Placeholder.Children.Add(textPrice2); STextField textPrice3 = new STextField("1.499") { Width = 100 }; petrolPump.Price3Placeholder.Children.Add(textPrice3); Func <string, double> parseDoubleSafe = s => { double n; if (double.TryParse(s, out n)) { return(n); } return(0.0); }; StreamSink <Key> sKey = new StreamSink <Key>(); Dictionary <Key, FrameworkElement> containersByKey = new Dictionary <Key, FrameworkElement> { { Key.One, petrolPump.Keypad1Button }, { Key.Two, petrolPump.Keypad2Button }, { Key.Three, petrolPump.Keypad3Button }, { Key.Four, petrolPump.Keypad4Button }, { Key.Five, petrolPump.Keypad5Button }, { Key.Six, petrolPump.Keypad6Button }, { Key.Seven, petrolPump.Keypad7Button }, { Key.Eight, petrolPump.Keypad8Button }, { Key.Nine, petrolPump.Keypad9Button }, { Key.Zero, petrolPump.Keypad0Button }, { Key.Clear, petrolPump.KeypadClearButton } }; foreach (KeyValuePair <Key, FrameworkElement> containerAndKey in containersByKey) { containerAndKey.Value.MouseDown += async(sender, args) => { if (args.LeftButton == MouseButtonState.Pressed) { await Task.Run(() => sKey.Send(containerAndKey.Key)); } }; } DiscreteCellLoop <UpDown> nozzle1 = new DiscreteCellLoop <UpDown>(); DiscreteCellLoop <UpDown> nozzle2 = new DiscreteCellLoop <UpDown>(); DiscreteCellLoop <UpDown> nozzle3 = new DiscreteCellLoop <UpDown>(); DiscreteCell <double> calibration = DiscreteCell.Constant(0.001); DiscreteCell <double> price1 = textPrice1.Text.Map(parseDoubleSafe); DiscreteCell <double> price2 = textPrice2.Text.Map(parseDoubleSafe); DiscreteCell <double> price3 = textPrice3.Text.Map(parseDoubleSafe); DiscreteCellSink <Stream <Unit> > csClearSale = new DiscreteCellSink <Stream <Unit> >(Sodium.Stream.Never <Unit>()); Stream <Unit> sClearSale = csClearSale.SwitchS(); StreamSink <int> sFuelPulses = new StreamSink <int>(); Cell <Outputs> outputs = logic.SelectedItem.Map( pump => pump.Create(new Inputs( nozzle1.Updates, nozzle2.Updates, nozzle3.Updates, sKey, sFuelPulses, calibration, price1, price2, price3, sClearSale))); DiscreteCell <Delivery> delivery = outputs.Map(o => o.Delivery).SwitchC(); DiscreteCell <string> presetLcd = outputs.Map(o => o.PresetLcd).SwitchC(); DiscreteCell <string> saleCostLcd = outputs.Map(o => o.SaleCostLcd).SwitchC(); DiscreteCell <string> saleQuantityLcd = outputs.Map(o => o.SaleQuantityLcd).SwitchC(); DiscreteCell <string> priceLcd1 = outputs.Map(o => o.PriceLcd1).SwitchC(); DiscreteCell <string> priceLcd2 = outputs.Map(o => o.PriceLcd2).SwitchC(); DiscreteCell <string> priceLcd3 = outputs.Map(o => o.PriceLcd3).SwitchC(); Stream <Unit> sBeep = outputs.Map(o => o.SBeep).SwitchS(); Stream <Sale> sSaleComplete = outputs.Map(o => o.SSaleComplete).SwitchS(); SoundPlayer beepPlayer = new SoundPlayer(GetResourceStream(@"sounds\beep.wav")); this.listeners.Add(sBeep.Listen(_ => new Thread(() => beepPlayer.PlaySync()) { IsBackground = true }.Start())); SoundPlayer fastRumblePlayer = new SoundPlayer(GetResourceStream(@"sounds\fast.wav")); Action stopFast = () => { }; void PlayFast() { ManualResetEvent mre = new ManualResetEvent(false); new Thread(() => { fastRumblePlayer.PlayLooping(); mre.WaitOne(); fastRumblePlayer.Stop(); }) { IsBackground = true }.Start(); stopFast = () => { mre.Set(); stopFast = () => { }; }; } SoundPlayer slowRumblePlayer = new SoundPlayer(GetResourceStream(@"sounds\slow.wav")); Action stopSlow = () => { }; void PlaySlow() { ManualResetEvent mre = new ManualResetEvent(false); new Thread(() => { slowRumblePlayer.PlayLooping(); mre.WaitOne(); slowRumblePlayer.Stop(); }) { IsBackground = true }.Start(); stopSlow = () => { mre.Set(); stopSlow = () => { }; }; } this.listeners.Add(delivery.Changes().Listen(d => { petrolPump.Dispatcher.InvokeIfNecessary(() => { if (d == Delivery.Fast1 || d == Delivery.Fast2 || d == Delivery.Fast3) { PlayFast(); } else { stopFast(); } if (d == Delivery.Slow1 || d == Delivery.Slow2 || d == Delivery.Slow3) { PlaySlow(); } else { stopSlow(); } }); })); StackPanel presetLcdStackPanel = new StackPanel { Orientation = Orientation.Horizontal }; petrolPump.PresetPlaceholder.Children.Add(presetLcdStackPanel); this.listeners.Add(presetLcd.Listen(t => petrolPump.Dispatcher.InvokeIfNecessary(() => petrolPump.SetLcdDigits(presetLcdStackPanel, t, 5, true)))); StackPanel saleCostStackPanel = new StackPanel { Orientation = Orientation.Horizontal }; petrolPump.DollarsPlaceholder.Children.Add(saleCostStackPanel); this.listeners.Add(saleCostLcd.Listen(t => petrolPump.Dispatcher.InvokeIfNecessary(() => petrolPump.SetLcdDigits(saleCostStackPanel, t, 5, true)))); StackPanel saleQuantityLcdStackPanel = new StackPanel { Orientation = Orientation.Horizontal }; petrolPump.LitersPlaceholder.Children.Add(saleQuantityLcdStackPanel); this.listeners.Add(saleQuantityLcd.Listen(t => petrolPump.Dispatcher.InvokeIfNecessary(() => petrolPump.SetLcdDigits(saleQuantityLcdStackPanel, t, 5, true)))); StackPanel priceLcd1StackPanel = new StackPanel { Orientation = Orientation.Horizontal }; petrolPump.Fuel1Placeholder.Children.Add(priceLcd1StackPanel); this.listeners.Add(priceLcd1.Listen(t => petrolPump.Dispatcher.InvokeIfNecessary(() => petrolPump.SetLcdDigits(priceLcd1StackPanel, t, 5, false)))); StackPanel priceLcd2StackPanel = new StackPanel { Orientation = Orientation.Horizontal }; petrolPump.Fuel2Placeholder.Children.Add(priceLcd2StackPanel); this.listeners.Add(priceLcd2.Listen(t => petrolPump.Dispatcher.InvokeIfNecessary(() => petrolPump.SetLcdDigits(priceLcd2StackPanel, t, 5, false)))); StackPanel priceLcd3StackPanel = new StackPanel { Orientation = Orientation.Horizontal }; petrolPump.Fuel3Placeholder.Children.Add(priceLcd3StackPanel); this.listeners.Add(priceLcd3.Listen(t => petrolPump.Dispatcher.InvokeIfNecessary(() => petrolPump.SetLcdDigits(priceLcd3StackPanel, t, 5, false)))); Dictionary <DiscreteCellLoop <UpDown>, Image> nozzles = new Dictionary <DiscreteCellLoop <UpDown>, Image> { { nozzle1, petrolPump.Nozzle1Image }, { nozzle2, petrolPump.Nozzle2Image }, { nozzle3, petrolPump.Nozzle3Image } }; this.listeners.AddRange(nozzles.Select(nozzle => nozzle.Key.Listen(p => petrolPump.Dispatcher.InvokeIfNecessary(() => nozzle.Value.Margin = p == UpDown.Up ? new Thickness(0, 0, 0, 0) : new Thickness(0, 30, 0, 0))))); foreach (KeyValuePair <DiscreteCellLoop <UpDown>, Image> nozzle in nozzles) { StreamSink <Unit> nozzleClicks = new StreamSink <Unit>(); nozzle.Value.MouseDown += async(sender, args) => { if (args.LeftButton == MouseButtonState.Pressed) { await Task.Run(() => nozzleClicks.Send(Unit.Value)); } }; nozzle.Key.Loop(nozzleClicks.Snapshot(nozzle.Key, (_, n) => n == UpDown.Down ? UpDown.Up : UpDown.Down).Hold(UpDown.Down)); } this.listeners.Add(sSaleComplete.Listen(sale => { Task.Run(() => { petrolPump.Dispatcher.InvokeIfNecessary(() => { SaleCompleteDialog dialog = new SaleCompleteDialog( sale.Fuel.ToString(), Formatters.FormatPrice(sale.Price, null), Formatters.FormatSaleCost(sale.Cost), Formatters.FormatSaleQuantity(sale.Quantity)); dialog.Owner = petrolPump; csClearSale.Send(dialog.SOkClicked); dialog.Show(); IListener l = null; // ReSharper disable once RedundantAssignment l = dialog.SOkClicked.Listen(_ => { petrolPump.Dispatcher.InvokeIfNecessary(() => dialog.Close()); // ReSharper disable once AccessToModifiedClosure l?.Unlisten(); }); }); }); })); Task.Run(async() => { while (true) { Transaction.RunVoid(() => { switch (delivery.Cell.Sample()) { case Delivery.Fast1: case Delivery.Fast2: case Delivery.Fast3: sFuelPulses.Send(40); break; case Delivery.Slow1: case Delivery.Slow2: case Delivery.Slow3: sFuelPulses.Send(2); break; } }); await Task.Delay(200).ConfigureAwait(false); } // ReSharper disable once FunctionNeverReturns }); }