public async Task StartReissuanceAsync(BobClient bobClient, IEnumerable <long> amounts, IEnumerable <long> vsizes, CancellationToken cancellationToken) { await Task.WhenAll(AmountCredentialToPresentTasks.Concat(VsizeCredentialToPresentTasks)).ConfigureAwait(false); IEnumerable <Credential> inputAmountCredentials = AmountCredentialToPresentTasks.Select(x => x.Result); IEnumerable <Credential> inputVsizeCredentials = VsizeCredentialToPresentTasks.Select(x => x.Result); var amountsToRequest = AddExtraCredentialRequests(amounts, inputAmountCredentials.Sum(x => x.Value)); var vsizesToRequest = AddExtraCredentialRequests(vsizes, inputVsizeCredentials.Sum(x => x.Value)); (IEnumerable <Credential> RealAmountCredentials, IEnumerable <Credential> RealVsizeCredentials)result = await bobClient.ReissueCredentialsAsync( amountsToRequest, vsizesToRequest, inputAmountCredentials, inputVsizeCredentials, cancellationToken).ConfigureAwait(false); // TODO keep the credentials that were not needed by the graph var(amountCredentials, _) = SeparateExtraCredentials(result.RealAmountCredentials, amounts); var(vsizeCredentials, _) = SeparateExtraCredentials(result.RealVsizeCredentials, vsizes); foreach (var(tcs, credential) in AmountCredentialTasks.Zip(amountCredentials)) { tcs.SetResult(credential); } foreach (var(tcs, credential) in VsizeCredentialTasks.Zip(vsizeCredentials)) { tcs.SetResult(credential); } }
public async Task StartReissuancesAsync(IEnumerable <AliceClient> aliceClients, BobClient bobClient, CancellationToken cancellationToken) { var aliceNodePairs = PairAliceClientAndRequestNodes(aliceClients, Graph); // Build tasks and link them together. List <SmartRequestNode> smartRequestNodes = new(); List <Task> allTasks = new(); // Temporary workaround because we don't yet have a mechanism to // propagate the final amounts to request amounts to AliceClient's // connection confirmation loop even though they are already known // after the final successful input registration, which may be well // before the connection confirmation phase actually starts. allTasks.Add(CompleteConnectionConfirmationAsync(aliceClients, bobClient, cancellationToken)); using CancellationTokenSource ctsOnError = new(); using CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, ctsOnError.Token); foreach (var node in Graph.Reissuances) { var inputAmountEdgeTasks = Graph.InEdges(node, CredentialType.Amount).Select(edge => DependencyTasks[edge].Task); var inputVsizeEdgeTasks = Graph.InEdges(node, CredentialType.Vsize).Select(edge => DependencyTasks[edge].Task); var outputAmountEdgeTaskCompSources = Graph.OutEdges(node, CredentialType.Amount).Select(edge => DependencyTasks[edge]); var outputVsizeEdgeTaskCompSources = Graph.OutEdges(node, CredentialType.Vsize).Select(edge => DependencyTasks[edge]); var requestedAmounts = Graph.OutEdges(node, CredentialType.Amount).Select(edge => edge.Value); var requestedVSizes = Graph.OutEdges(node, CredentialType.Vsize).Select(edge => edge.Value); SmartRequestNode smartRequestNode = new( inputAmountEdgeTasks, inputVsizeEdgeTasks, outputAmountEdgeTaskCompSources, outputVsizeEdgeTaskCompSources); var task = smartRequestNode .StartReissuanceAsync(bobClient, requestedAmounts, requestedVSizes, linkedCts.Token) .ContinueWith((t) => { if (t.IsFaulted && t.Exception is { } exception) { // If one task is failing, cancel all the tasks and throw. ctsOnError.Cancel(); throw exception; } }, linkedCts.Token); allTasks.Add(task); } await Task.WhenAll(allTasks).ConfigureAwait(false); var amountEdges = Graph.Outputs.SelectMany(node => Graph.InEdges(node, CredentialType.Amount)); var vsizeEdges = Graph.Outputs.SelectMany(node => Graph.InEdges(node, CredentialType.Vsize)); // Check if all tasks were finished, otherwise Task.Result will block. if (!amountEdges.Concat(vsizeEdges).All(edge => DependencyTasks[edge].Task.IsCompletedSuccessfully)) { throw new InvalidOperationException("Some Output nodes in-edges failed to complete"); } }
public async Task StartOutputRegistrationsAsync(IEnumerable <TxOut> txOuts, BobClient bobClient, CancellationToken cancellationToken) { List <Task> outputTasks = new(); using CancellationTokenSource ctsOnError = new(); using CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, ctsOnError.Token); foreach (var(node, txOut) in Enumerable.Zip(Graph.Outputs, txOuts)) { var amountCredsToPresentTasks = Graph.InEdges(node, CredentialType.Amount).Select(edge => DependencyTasks[edge].Task); var vsizeCredsToPresentTasks = Graph.InEdges(node, CredentialType.Vsize).Select(edge => DependencyTasks[edge].Task); SmartRequestNode smartRequestNode = new( amountCredsToPresentTasks, vsizeCredsToPresentTasks, Array.Empty <TaskCompletionSource <Credential> >(), Array.Empty <TaskCompletionSource <Credential> >()); var task = smartRequestNode .StartOutputRegistrationAsync(bobClient, txOut.ScriptPubKey, cancellationToken) .ContinueWith((t) => { if (t.IsFaulted && t.Exception is { } exception) { // If one task is failing, cancel all the tasks and throw. ctsOnError.Cancel(); throw exception; } }, linkedCts.Token); outputTasks.Add(task); } await Task.WhenAll(outputTasks).ConfigureAwait(false); }
public async Task DoesntSwitchImmaturelyAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 2, MinInputCountByRoundMultiplier = 0.5 }; var(key1, coin1, key2, coin2) = WabiSabiFactory.CreateCoinKeyPairs(); var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, mockRpc); var(round, arenaClient, alices) = await CreateRoundWithTwoConfirmedConnectionsAsync(arena, key1, coin1, key2, coin2); var(amountCredentials1, vsizeCredentials1) = (alices[0].IssuedAmountCredentials, alices[0].IssuedVsizeCredentials); var(amountCredentials2, vsizeCredentials2) = (alices[1].IssuedAmountCredentials, alices[1].IssuedVsizeCredentials); // Register outputs. var bobClient = new BobClient(round.Id, arenaClient); using var destKey = new Key(); await bobClient.RegisterOutputAsync( destKey.PubKey.WitHash.ScriptPubKey, amountCredentials1.Take(ProtocolConstants.CredentialNumber), vsizeCredentials1.Take(ProtocolConstants.CredentialNumber), CancellationToken.None); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.OutputRegistration, round.Phase); await arena.StopAsync(CancellationToken.None); }
public void Stop() { this.chatServer?.Dispose(); this.chatServer = null; this.bobClient?.Dispose(); this.bobClient = null; }
public async Task DiffTooSmallToBlameAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 2, MinInputCountByRoundMultiplier = 0.5, OutputRegistrationTimeout = TimeSpan.Zero, CoordinationFeeRate = CoordinationFeeRate.Zero }; var(keyChain, coin1, coin2) = WabiSabiFactory.CreateCoinKeyPairs(); var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin); using Arena arena = await ArenaBuilder.From(cfg).With(mockRpc).CreateAndStartAsync(); var(round, arenaClient, alices) = await CreateRoundWithTwoConfirmedConnectionsAsync(arena, keyChain, coin1, coin2); var(amountCredentials1, vsizeCredentials1) = (alices[0].IssuedAmountCredentials, alices[0].IssuedVsizeCredentials); var(amountCredentials2, vsizeCredentials2) = (alices[1].IssuedAmountCredentials, alices[1].IssuedVsizeCredentials); // Register outputs. var bobClient = new BobClient(round.Id, arenaClient); using var destKey1 = new Key(); using var destKey2 = new Key(); await bobClient.RegisterOutputAsync( destKey1.PubKey.WitHash.ScriptPubKey, amountCredentials1.Take(ProtocolConstants.CredentialNumber), vsizeCredentials1.Take(ProtocolConstants.CredentialNumber), CancellationToken.None); await bobClient.RegisterOutputAsync( destKey2.PubKey.WitHash.ScriptPubKey, amountCredentials2.Take(ProtocolConstants.CredentialNumber), vsizeCredentials2.Take(ProtocolConstants.CredentialNumber), CancellationToken.None); // Add another input. The input must be able to pay for itself, but // the remaining amount after deducting the fees needs to be less // than the minimum. var txParams = round.Assert <ConstructionState>().Parameters; var extraAlice = WabiSabiFactory.CreateAlice(txParams.FeeRate.GetFee(Constants.P2wpkhInputVirtualSize) + txParams.AllowedOutputAmounts.Min - new Money(1L), round); round.Alices.Add(extraAlice); round.CoinjoinState = round.Assert <ConstructionState>().AddInput(extraAlice.Coin); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.TransactionSigning, round.Phase); var tx = round.Assert <SigningState>().CreateTransaction(); Assert.Equal(3, tx.Inputs.Count); Assert.Equal(2, tx.Outputs.Count); Assert.DoesNotContain(round.CoordinatorScript, tx.Outputs.Select(x => x.ScriptPubKey)); await arena.StopAsync(CancellationToken.None); }
/// <summary> /// Invoked when application execution is being suspended. Application state is saved /// without knowing whether the application will be terminated or resumed with the contents /// of memory still intact. /// </summary> /// <param name="sender">The source of the suspend request.</param> /// <param name="e">Details about the suspend request.</param> private void OnSuspending(object sender, SuspendingEventArgs e) { var deferral = e.SuspendingOperation.GetDeferral(); if (this.sampleTimer != null) { this.sampleTimer.Dispose(); this.sampleTimer = null; } if (this.interoperabilityServer != null) { this.interoperabilityServer.Dispose(); this.interoperabilityServer = null; } if (this.chatServer != null) { this.chatServer.Dispose(); this.chatServer = null; } if (this.bobClient != null) { this.bobClient.Dispose(); this.bobClient = null; } if (this.sensorServer != null) { this.sensorServer.Dispose(); this.sensorServer = null; } if (this.provisioningClient != null) { this.provisioningClient.Dispose(); this.provisioningClient = null; } if (this.thingRegistryClient != null) { this.thingRegistryClient.Dispose(); this.thingRegistryClient = null; } if (this.xmppClient != null) { this.xmppClient.Dispose(); this.xmppClient = null; } Log.Terminate(); deferral.Complete(); }
public async Task AllBobsRegisteredAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 2, MinInputCountByRoundMultiplier = 0.5 }; using Key key1 = new(); using Key key2 = new(); var coin1 = WabiSabiFactory.CreateCoin(key1); var coin2 = WabiSabiFactory.CreateCoin(key2); var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient(coin1, coin2); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, mockRpc); var(round, arenaClient, alices) = await CreateRoundWithTwoConfirmedConnectionsAsync(arena, key1, coin1, key2, coin2); var(amountCredentials1, vsizeCredentials1) = (alices[0].IssuedAmountCredentials, alices[0].IssuedVsizeCredentials); var(amountCredentials2, vsizeCredentials2) = (alices[1].IssuedAmountCredentials, alices[1].IssuedVsizeCredentials); // Register outputs. var bobClient = new BobClient(round.Id, arenaClient); using var destKey1 = new Key(); await bobClient.RegisterOutputAsync( coin1.Amount - round.FeeRate.GetFee(coin1.ScriptPubKey.EstimateInputVsize()), destKey1.PubKey.WitHash.ScriptPubKey, amountCredentials1.Take(ProtocolConstants.CredentialNumber), vsizeCredentials1.Take(ProtocolConstants.CredentialNumber), CancellationToken.None); using var destKey2 = new Key(); await bobClient.RegisterOutputAsync( coin2.Amount - round.FeeRate.GetFee(coin2.ScriptPubKey.EstimateInputVsize()), destKey2.PubKey.WitHash.ScriptPubKey, amountCredentials2.Take(ProtocolConstants.CredentialNumber), vsizeCredentials2.Take(ProtocolConstants.CredentialNumber), CancellationToken.None); foreach (var alice in alices) { await alice.ReadyToSignAsync(CancellationToken.None); } await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.TransactionSigning, round.Phase); var tx = round.Assert <SigningState>().CreateTransaction(); Assert.Equal(2, tx.Inputs.Count); Assert.Equal(2, tx.Outputs.Count); await arena.StopAsync(CancellationToken.None); }
public override void ConnectClients() { base.ConnectClients(); Assert.AreEqual(XmppState.Connected, this.client1.State); Assert.AreEqual(XmppState.Connected, this.client2.State); this.bobClient1 = new BobClient(this.client1, "Bob1"); this.bobClient2 = new BobClient(this.client2, "Bob2"); }
private async Task RegisterOutputAsync(CcjClientRound ongoingRound) { using (var bobClient = new BobClient(CcjHostUri, TorSocks5EndPoint)) { await bobClient.PostOutputAsync(ongoingRound.RoundHash, ongoingRound.ActiveOutputAddress, ongoingRound.UnblindedSignature); ongoingRound.PostedOutput = true; Logger.LogInfo <AliceClient>($"Round ({ongoingRound.State.RoundId}) Bob Posted output."); } }
public Task Stop() { this.chatServer?.Dispose(); this.chatServer = null; this.bobClient?.Dispose(); this.bobClient = null; return(Task.CompletedTask); }
/// <summary> /// Invoked when application execution is being suspended. Application state is saved /// without knowing whether the application will be terminated or resumed with the contents /// of memory still intact. /// </summary> /// <param name="sender">The source of the suspend request.</param> /// <param name="e">Details about the suspend request.</param> private void OnSuspending(object sender, SuspendingEventArgs e) { var deferral = e.SuspendingOperation.GetDeferral(); if (instance == this) { instance = null; } this.chatServer?.Dispose(); this.chatServer = null; this.bobClient?.Dispose(); this.bobClient = null; this.sensorServer?.Dispose(); this.sensorServer = null; this.xmppClient?.Dispose(); this.xmppClient = null; this.minuteTimer?.Dispose(); this.minuteTimer = null; #if GPIO this.gpioPin.Dispose(); this.gpioPin = null; #else if (this.arduino != null) { this.arduino.digitalWrite(13, PinState.LOW); this.arduino.pinMode(13, PinMode.INPUT); // Onboard LED. this.arduino.pinMode(9, PinMode.INPUT); // Relay. this.arduino.Dispose(); this.arduino = null; } if (this.arduinoUsb != null) { this.arduinoUsb.end(); this.arduinoUsb.Dispose(); this.arduinoUsb = null; } #endif db?.Stop()?.Wait(); db?.Flush()?.Wait(); Log.Terminate(); deferral.Complete(); }
CreateRoundWithOutputsReadyToSignAsync(Arena arena, Key key1, Coin coin1, Key key2, Coin coin2) { // Create the round. await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var round = Assert.Single(arena.Rounds); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); // Register Alices. var aliceClient1 = new AliceClient(round.Id, arenaClient, coin1, round.FeeRate, key1.GetBitcoinSecret(round.Network)); var aliceClient2 = new AliceClient(round.Id, arenaClient, coin2, round.FeeRate, key2.GetBitcoinSecret(round.Network)); await aliceClient1.RegisterInputAsync(CancellationToken.None).ConfigureAwait(false); await aliceClient2.RegisterInputAsync(CancellationToken.None).ConfigureAwait(false); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.ConnectionConfirmation, round.Phase); // Confirm connections. await aliceClient1.ConfirmConnectionAsync(TimeSpan.FromMilliseconds(100), round.MaxVsizeAllocationPerAlice, CancellationToken.None).ConfigureAwait(false); await aliceClient2.ConfirmConnectionAsync(TimeSpan.FromMilliseconds(100), round.MaxVsizeAllocationPerAlice, CancellationToken.None).ConfigureAwait(false); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.OutputRegistration, round.Phase); // Register outputs. var bobClient = new BobClient(round.Id, arenaClient); using var destKey1 = new Key(); using var destKey2 = new Key(); await bobClient.RegisterOutputAsync( coin1.Amount - round.FeeRate.GetFee(coin1.ScriptPubKey.EstimateInputVsize()), destKey1.PubKey.WitHash.ScriptPubKey, aliceClient1.RealAmountCredentials, aliceClient1.RealVsizeCredentials, CancellationToken.None).ConfigureAwait(false); await bobClient.RegisterOutputAsync( coin2.Amount - round.FeeRate.GetFee(coin2.ScriptPubKey.EstimateInputVsize()), destKey1.PubKey.WitHash.ScriptPubKey, aliceClient2.RealAmountCredentials, aliceClient2.RealVsizeCredentials, CancellationToken.None).ConfigureAwait(false); return(round, aliceClient1, aliceClient2); }
CreateRoundWithOutputsReadyToSignAsync(Arena arena, Key key1, SmartCoin coin1, Key key2, SmartCoin coin2) { // Create the round. await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var round = Assert.Single(arena.Rounds); round.MaxVsizeAllocationPerAlice = 11 + 31 + MultipartyTransactionParameters.SharedOverhead; var arenaClient = WabiSabiFactory.CreateArenaClient(arena); // Register Alices. using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), arena); await roundStateUpdater.StartAsync(CancellationToken.None); using var identificationKey = new Key(); var task1 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin1, key1.GetBitcoinSecret(round.Network), identificationKey, roundStateUpdater, CancellationToken.None); var task2 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin2, key2.GetBitcoinSecret(round.Network), identificationKey, roundStateUpdater, CancellationToken.None); while (Phase.OutputRegistration != round.Phase) { await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); } await Task.WhenAll(task1, task2); var aliceClient1 = task1.Result; var aliceClient2 = task2.Result; // Register outputs. var bobClient = new BobClient(round.Id, arenaClient); using var destKey1 = new Key(); using var destKey2 = new Key(); await bobClient.RegisterOutputAsync( destKey1.PubKey.WitHash.ScriptPubKey, aliceClient1.IssuedAmountCredentials.Take(ProtocolConstants.CredentialNumber), aliceClient1.IssuedVsizeCredentials.Take(ProtocolConstants.CredentialNumber), CancellationToken.None).ConfigureAwait(false); await bobClient.RegisterOutputAsync( destKey1.PubKey.WitHash.ScriptPubKey, aliceClient2.IssuedAmountCredentials.Take(ProtocolConstants.CredentialNumber), aliceClient2.IssuedVsizeCredentials.Take(ProtocolConstants.CredentialNumber), CancellationToken.None).ConfigureAwait(false); await roundStateUpdater.StopAsync(CancellationToken.None); return(round, aliceClient1, aliceClient2); }
public CcjClient(Network network, BlindingRsaPubKey blindingPubKey, KeyManager keyManager, Uri ccjHostUri, IPEndPoint torSocks5EndPoint = null) { Network = Guard.NotNull(nameof(network), network); BlindingPubKey = Guard.NotNull(nameof(blindingPubKey), blindingPubKey); KeyManager = Guard.NotNull(nameof(keyManager), keyManager); AliceClient = new AliceClient(ccjHostUri, torSocks5EndPoint); BobClient = new BobClient(ccjHostUri, torSocks5EndPoint); SatoshiClient = new SatoshiClient(ccjHostUri, torSocks5EndPoint); Rounds = new List <CcjClientRound>(); _running = 0; Stop = new CancellationTokenSource(); _frequentStatusProcessingIfNotMixing = 0; CoinsWaitingForMix = new List <MixCoin>(); MixLock = new AsyncLock(); }
public override void DisposeClients() { if (this.bobClient1 != null) { this.bobClient1.Dispose(); this.bobClient1 = null; } if (this.bobClient2 != null) { this.bobClient2.Dispose(); this.bobClient2 = null; } base.DisposeClients(); }
public async Task StartOutputRegistrationAsync( BobClient bobClient, Script scriptPubKey, CancellationToken cancellationToken) { await Task.WhenAll(AmountCredentialToPresentTasks.Concat(VsizeCredentialToPresentTasks)).ConfigureAwait(false); IEnumerable <Credential> inputAmountCredentials = AmountCredentialToPresentTasks.Select(x => x.Result); IEnumerable <Credential> inputVsizeCredentials = VsizeCredentialToPresentTasks.Select(x => x.Result); await bobClient.RegisterOutputAsync( scriptPubKey, inputAmountCredentials, inputVsizeCredentials, cancellationToken).ConfigureAwait(false); }
public async Task SomeBobsRegisteredTimeoutAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 2, MinInputCountByRoundMultiplier = 0.5, OutputRegistrationTimeout = TimeSpan.Zero }; using Key key1 = new(); using Key key2 = new(); var coin1 = WabiSabiFactory.CreateCoin(key1); var coin2 = WabiSabiFactory.CreateCoin(key2); var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient(coin1, coin2); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, mockRpc).ConfigureAwait(false); var(round, arenaClient, alices) = await CreateRoundWithTwoConfirmedConnectionsAsync(arena, key1, coin1, key2, coin2).ConfigureAwait(false); var(realAmountCredentials1, realVsizeCredentials1) = alices[0]; var(realAmountCredentials2, realVsizeCredentials2) = alices[1]; // Register outputs. var bobClient = new BobClient(round.Id, arenaClient); using var destKey = new Key(); await bobClient.RegisterOutputAsync( coin1.Amount - round.FeeRate.GetFee(coin1.ScriptPubKey.EstimateInputVsize()), destKey.PubKey.WitHash.ScriptPubKey, realAmountCredentials1, realVsizeCredentials1, CancellationToken.None).ConfigureAwait(false); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.TransactionSigning, round.Phase); var tx = round.Assert <SigningState>().CreateTransaction(); Assert.Equal(2, tx.Inputs.Count); Assert.Equal(2, tx.Outputs.Count); Assert.Contains(cfg.BlameScript, tx.Outputs.Select(x => x.ScriptPubKey)); await arena.StopAsync(CancellationToken.None); }
public async Task SomeBobsRegisteredTimeoutAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 2, MinInputCountByRoundMultiplier = 0.5, OutputRegistrationTimeout = TimeSpan.Zero, CoordinationFeeRate = CoordinationFeeRate.Zero }; var(keyChain, coin1, coin2) = WabiSabiFactory.CreateCoinKeyPairs(); var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin); using Arena arena = await ArenaBuilder.From(cfg).With(mockRpc).CreateAndStartAsync(); var(round, arenaClient, alices) = await CreateRoundWithTwoConfirmedConnectionsAsync(arena, keyChain, coin1, coin2); var(amountCredentials1, vsizeCredentials1) = (alices[0].IssuedAmountCredentials, alices[0].IssuedVsizeCredentials); var(amountCredentials2, vsizeCredentials2) = (alices[1].IssuedAmountCredentials, alices[1].IssuedVsizeCredentials); // Register outputs. var bobClient = new BobClient(round.Id, arenaClient); using var destKey = new Key(); await bobClient.RegisterOutputAsync( destKey.PubKey.WitHash.ScriptPubKey, amountCredentials1.Take(ProtocolConstants.CredentialNumber), vsizeCredentials1.Take(ProtocolConstants.CredentialNumber), CancellationToken.None); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.TransactionSigning, round.Phase); var tx = round.Assert <SigningState>().CreateTransaction(); Assert.Equal(2, tx.Inputs.Count); Assert.Equal(2, tx.Outputs.Count); Assert.Contains(round.CoordinatorScript, tx.Outputs.Select(x => x.ScriptPubKey)); await arena.StopAsync(CancellationToken.None); }
public WaitHandle Start() { ConcentratorServer ConcentratorServer = null; ProvisioningClient ProvisioningClient = null; if (Types.TryGetModuleParameter("Concentrator", out object Obj)) { ConcentratorServer = Obj as ConcentratorServer; } if (Types.TryGetModuleParameter("Provisioning", out Obj)) { ProvisioningClient = Obj as ProvisioningClient; } this.bobClient = new BobClient(Gateway.XmppClient, Path.Combine(Gateway.AppDataFolder, "BoB")); this.chatServer = new ChatServer(Gateway.XmppClient, this.bobClient, ConcentratorServer, ProvisioningClient); return(null); }
public async Task StartOutputRegistrationsAsync(IEnumerable <TxOut> txOuts, BobClient bobClient, IKeyChain keyChain, ImmutableList <DateTimeOffset> outputRegistrationScheduledDates, CancellationToken cancellationToken) { using CancellationTokenSource ctsOnError = new(); using CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, ctsOnError.Token); var nodes = Graph.Outputs.Select(node => { var amountCredsToPresentTasks = Graph.InEdges(node, CredentialType.Amount).Select(edge => DependencyTasks[edge].Task); var vsizeCredsToPresentTasks = Graph.InEdges(node, CredentialType.Vsize).Select(edge => DependencyTasks[edge].Task); SmartRequestNode smartRequestNode = new( amountCredsToPresentTasks, vsizeCredsToPresentTasks, Array.Empty <TaskCompletionSource <Credential> >(), Array.Empty <TaskCompletionSource <Credential> >()); return(smartRequestNode); }); var tasks = txOuts.Zip(nodes, outputRegistrationScheduledDates, async(txOut, smartRequestNode, scheduledDate) => { try { var delay = scheduledDate - DateTimeOffset.UtcNow; if (delay > TimeSpan.Zero) { await Task.Delay(delay, cancellationToken).ConfigureAwait(false); } await smartRequestNode.StartOutputRegistrationAsync(bobClient, txOut.ScriptPubKey, cancellationToken).ConfigureAwait(false); } catch (WabiSabiProtocolException ex) when(ex.ErrorCode == WabiSabiProtocolErrorCode.AlreadyRegisteredScript) { Logger.LogDebug($"Output registration error, code:'{ex.ErrorCode}' message:'{ex.Message}'."); if (keyChain is KeyChain { KeyManager: var keyManager } && keyManager.TryGetKeyForScriptPubKey(txOut.ScriptPubKey, out var hdPubKey)) { hdPubKey.SetKeyState(KeyState.Used); } }
private async Task RegisterOutputAsync(CcjClientRound ongoingRound) { IEnumerable <TxoRef> registeredInputs = ongoingRound.Registration.CoinsRegistered.Select(x => x.GetTxoRef()); var shuffledOutputs = ongoingRound.Registration.ActiveOutputs.ToList(); shuffledOutputs.Shuffle(); foreach (var activeOutput in shuffledOutputs) { using (var bobClient = new BobClient(CcjHostUriAction, TorSocks5EndPoint)) { if (!await bobClient.PostOutputAsync(ongoingRound.RoundId, activeOutput)) { Logger.LogWarning <AliceClient>($"Round ({ongoingRound.State.RoundId}) Bobs did not have enough time to post outputs before timeout. If you see this message, contact nopara73, so he can optimize the phase timeout periods to the worst Internet/Tor connections, which may be yours.)"); break; } // Unblind our exposed links. foreach (TxoRef input in registeredInputs) { if (ExposedLinks.ContainsKey(input)) // Should never not contain, but oh well, let's not disrupt the round for this. { var found = ExposedLinks[input].FirstOrDefault(x => x.Key.GetP2wpkhAddress(Network) == activeOutput.Address); if (found != default) { found.IsBlinded = false; } else { // Should never happen, but oh well we can autocorrect it so why not. ExposedLinks[input] = ExposedLinks[input].Append(new HdPubKeyBlindedPair(KeyManager.GetKeyForScriptPubKey(activeOutput.Address.ScriptPubKey), false)); } } } } } ongoingRound.Registration.SetPhaseCompleted(CcjRoundPhase.OutputRegistration); Logger.LogInfo <AliceClient>($"Round ({ongoingRound.State.RoundId}) Bob Posted outputs: {ongoingRound.Registration.ActiveOutputs.Count()}."); }
/// <summary> /// Invoked when application execution is being suspended. Application state is saved /// without knowing whether the application will be terminated or resumed with the contents /// of memory still intact. /// </summary> /// <param name="sender">The source of the suspend request.</param> /// <param name="e">Details about the suspend request.</param> private void OnSuspending(object sender, SuspendingEventArgs e) { var deferral = e.SuspendingOperation.GetDeferral(); this.subscription?.Unsubscribe(); this.subscription = null; this.registryClient?.Dispose(); this.registryClient = null; this.chatServer?.Dispose(); this.chatServer = null; this.bobClient?.Dispose(); this.bobClient = null; this.sensorServer?.Dispose(); this.sensorServer = null; this.sensorClient?.Dispose(); this.sensorClient = null; this.controlClient?.Dispose(); this.controlClient = null; this.xmppClient?.Dispose(); this.xmppClient = null; this.secondTimer?.Dispose(); this.secondTimer = null; db?.Stop()?.Wait(); db?.Flush()?.Wait(); Log.Terminate(); deferral.Complete(); }
private async void StartActuator() { try { Log.Informational("Starting application."); SimpleXmppConfiguration xmppConfiguration = SimpleXmppConfiguration.GetConfigUsingSimpleConsoleDialog("xmpp.config", Guid.NewGuid().ToString().Replace("-", string.Empty), // Default user name. Guid.NewGuid().ToString().Replace("-", string.Empty), // Default password. FormSignatureKey, FormSignatureSecret, typeof(App).GetTypeInfo().Assembly); Log.Informational("Connecting to XMPP server."); xmppClient = xmppConfiguration.GetClient("en", typeof(App).GetTypeInfo().Assembly, false); xmppClient.AllowRegistration(FormSignatureKey, FormSignatureSecret); if (xmppConfiguration.Sniffer && MainPage.Sniffer != null) { xmppClient.Add(MainPage.Sniffer); } if (!string.IsNullOrEmpty(xmppConfiguration.Events)) { Log.Register(new XmppEventSink("XMPP Event Sink", xmppClient, xmppConfiguration.Events, false)); } if (!string.IsNullOrEmpty(xmppConfiguration.ThingRegistry)) { thingRegistryClient = new ThingRegistryClient(xmppClient, xmppConfiguration.ThingRegistry); thingRegistryClient.Claimed += (sender, e) => { ownerJid = e.JID; Log.Informational("Thing has been claimed.", ownerJid, new KeyValuePair <string, object>("Public", e.IsPublic)); this.RaiseOwnershipChanged(); }; thingRegistryClient.Disowned += (sender, e) => { Log.Informational("Thing has been disowned.", ownerJid); ownerJid = string.Empty; this.Register(); // Will call this.OwnershipChanged() after successful registration. }; thingRegistryClient.Removed += (sender, e) => { Log.Informational("Thing has been removed from the public registry.", ownerJid); }; } if (!string.IsNullOrEmpty(xmppConfiguration.Provisioning)) { provisioningClient = new ProvisioningClient(xmppClient, xmppConfiguration.Provisioning); } Timer ConnectionTimer = new Timer((P) => { if (xmppClient.State == XmppState.Offline || xmppClient.State == XmppState.Error || xmppClient.State == XmppState.Authenticating) { try { Log.Informational("Reconnecting."); xmppClient.Reconnect(); } catch (Exception ex) { Log.Critical(ex); } } }, null, 60000, 60000); xmppClient.OnStateChanged += (sender, NewState) => { Log.Informational(NewState.ToString()); switch (NewState) { case XmppState.Connected: connected = true; if (!registered && thingRegistryClient != null) { Register(); } break; case XmppState.Offline: immediateReconnect = connected; connected = false; if (immediateReconnect) { xmppClient.Reconnect(); } break; } }; xmppClient.OnPresenceSubscribe += (sender, e) => { Log.Informational("Subscription request received from " + e.From + "."); e.Accept(); // TODO: Provisioning RosterItem Item = xmppClient.GetRosterItem(e.FromBareJID); if (Item == null || Item.State == SubscriptionState.None || Item.State == SubscriptionState.From) { xmppClient.RequestPresenceSubscription(e.FromBareJID); } xmppClient.SetPresence(Availability.Chat); }; xmppClient.OnPresenceUnsubscribe += (sender, e) => { Log.Informational("Unsubscription request received from " + e.From + "."); e.Accept(); }; xmppClient.OnRosterItemUpdated += (sender, e) => { if (e.State == SubscriptionState.None && e.PendingSubscription != PendingSubscription.Subscribe) { xmppClient.RemoveRosterItem(e.BareJid); } }; bool SwitchOn = false; sensorServer = new SensorServer(xmppClient, provisioningClient, false); sensorServer.OnExecuteReadoutRequest += (Sender, Request) => { DateTime Now = DateTime.Now; Log.Informational("Readout requested", string.Empty, Request.Actor); Request.ReportFields(true, new BooleanField(ThingReference.Empty, Now, "Lamp", SwitchOn, FieldType.Momentary, FieldQoS.AutomaticReadout)); }; controlServer = new ControlServer(xmppClient, new BooleanControlParameter("Lamp", "Control", "Lamp switch on.", "If checked, lamp is turned on.", (Node) => SwitchOn, (Node, Value) => { SwitchOn = Value; Log.Informational("Lamp turned " + (SwitchOn ? "ON" : "OFF")); UpdateMainWindow(SwitchOn); })); this.bobClient = new BobClient(this.xmppClient, Path.Combine(Path.GetTempPath(), "BitsOfBinary")); this.chatServer = new ChatServer(xmppClient, this.bobClient, this.sensorServer, this.controlServer); interoperabilityServer = new InteroperabilityServer(xmppClient); interoperabilityServer.OnGetInterfaces += (sender, e) => { e.Add("XMPP.IoT.Actuator.Lamp"); }; xmppClient.Connect(); } catch (Exception ex) { Log.Emergency(ex); MessageDialog Dialog = new MessageDialog(ex.Message, "Error"); await Dialog.ShowAsync(); } }
private async void StartSensor() { try { Log.Informational("Starting application."); XmppCredentials Credentials = SimpleXmppConfiguration.GetConfigUsingSimpleConsoleDialog("xmpp.config", Guid.NewGuid().ToString().Replace("-", string.Empty), // Default user name. Guid.NewGuid().ToString().Replace("-", string.Empty), // Default password. typeof(App).GetTypeInfo().Assembly); Log.Informational("Connecting to XMPP server."); xmppClient = new XmppClient(Credentials, "en", typeof(App).GetTypeInfo().Assembly); if (Credentials.Sniffer && MainPage.Sniffer != null) { xmppClient.Add(MainPage.Sniffer); } if (!string.IsNullOrEmpty(Credentials.Events)) { Log.Register(new XmppEventSink("XMPP Event Sink", xmppClient, Credentials.Events, false)); } if (!string.IsNullOrEmpty(Credentials.ThingRegistry)) { thingRegistryClient = new ThingRegistryClient(xmppClient, Credentials.ThingRegistry); thingRegistryClient.Claimed += (sender, e) => { ownerJid = e.JID; Log.Informational("Thing has been claimed.", ownerJid, new KeyValuePair <string, object>("Public", e.IsPublic)); this.RaiseOwnershipChanged(); }; thingRegistryClient.Disowned += (sender, e) => { Log.Informational("Thing has been disowned.", ownerJid); ownerJid = string.Empty; this.Register(); // Will call this.OwnershipChanged() after successful registration. }; thingRegistryClient.Removed += (sender, e) => { Log.Informational("Thing has been removed from the public registry.", ownerJid); }; } if (!string.IsNullOrEmpty(Credentials.Provisioning)) { provisioningClient = new ProvisioningClient(xmppClient, Credentials.Provisioning); } Timer ConnectionTimer = new Timer((P) => { if (xmppClient.State == XmppState.Offline || xmppClient.State == XmppState.Error || xmppClient.State == XmppState.Authenticating) { try { Log.Informational("Reconnecting."); xmppClient.Reconnect(); } catch (Exception ex) { Log.Critical(ex); } } }, null, 60000, 60000); xmppClient.OnStateChanged += (sender, NewState) => { Log.Informational(NewState.ToString()); switch (NewState) { case XmppState.Connected: connected = true; if (!registered && thingRegistryClient != null) { this.Register(); } break; case XmppState.Offline: immediateReconnect = connected; connected = false; if (immediateReconnect) { xmppClient.Reconnect(); } break; } }; xmppClient.OnPresenceSubscribe += (sender, e) => { Log.Informational("Subscription request received from " + e.From + "."); e.Accept(); // TODO: Provisioning RosterItem Item = xmppClient.GetRosterItem(e.FromBareJID); if (Item is null || Item.State == SubscriptionState.None || Item.State == SubscriptionState.From) { xmppClient.RequestPresenceSubscription(e.FromBareJID); } xmppClient.SetPresence(Availability.Chat); }; xmppClient.OnPresenceUnsubscribe += (sender, e) => { Log.Informational("Unsubscription request received from " + e.From + "."); e.Accept(); }; xmppClient.OnRosterItemUpdated += (sender, e) => { if (e.State == SubscriptionState.None && e.PendingSubscription != PendingSubscription.Subscribe) { xmppClient.RemoveRosterItem(e.BareJid); } }; LinkedList <DayHistoryRecord> DayHistoricalValues = new LinkedList <DayHistoryRecord>(); LinkedList <MinuteHistoryRecord> MinuteHistoricalValues = new LinkedList <MinuteHistoryRecord>(); DateTime SampleTime = DateTime.Now; DateTime PeriodStart = SampleTime.Date; DateTime Now; DateTime MinTime = SampleTime; DateTime MaxTime = SampleTime; double CurrentTemperature = this.ReadTemp(); double MinTemp = CurrentTemperature; double MaxTemp = CurrentTemperature; double SumTemp = CurrentTemperature; int NrTemp = 1; int NrDayRecords = 0; int NrMinuteRecords = 0; object SampleSynch = new object(); this.sampleTimer = new Timer((P) => { lock (SampleSynch) { Now = DateTime.Now; if (Now.Date != PeriodStart.Date) { DayHistoryRecord Rec = new DayHistoryRecord(PeriodStart.Date, PeriodStart.Date.AddDays(1).AddMilliseconds(-1), MinTemp, MaxTemp, SumTemp / NrTemp); DayHistoricalValues.AddFirst(Rec); if (NrDayRecords < MaxRecordsPerPeriod) { NrDayRecords++; } else { DayHistoricalValues.RemoveLast(); } // TODO: Persistence PeriodStart = Now.Date; SumTemp = 0; NrTemp = 0; } CurrentTemperature = this.ReadTemp(); if (Now.Minute != SampleTime.Minute) { MinuteHistoryRecord Rec = new MinuteHistoryRecord(Now, CurrentTemperature); MinuteHistoricalValues.AddFirst(Rec); if (NrMinuteRecords < MaxRecordsPerPeriod) { NrMinuteRecords++; } else { MinuteHistoricalValues.RemoveLast(); } // TODO: Persistence } SampleTime = Now; if (CurrentTemperature < MinTemp) { MinTemp = CurrentTemperature; MinTime = SampleTime; } if (CurrentTemperature > MaxTemp) { MaxTemp = CurrentTemperature; MaxTime = SampleTime; } SumTemp += CurrentTemperature; NrTemp++; } if (this.sensorServer.HasSubscriptions(ThingReference.Empty)) { this.sensorServer.NewMomentaryValues(new QuantityField(ThingReference.Empty, SampleTime, "Temperature", CurrentTemperature, 1, "°C", FieldType.Momentary, FieldQoS.AutomaticReadout)); } this.UpdateMainWindow(CurrentTemperature, MinTemp, MaxTemp, SumTemp / NrTemp); }, null, 1000 - PeriodStart.Millisecond, 1000); this.sensorServer = new SensorServer(xmppClient, provisioningClient, true); this.sensorServer.OnExecuteReadoutRequest += (Sender, Request) => { Log.Informational("Readout requested by " + Request.From, string.Empty, Request.Actor); List <Field> Fields = new List <Field>(); bool IncludeTemp = Request.IsIncluded("Temperature"); bool IncludeTempMin = Request.IsIncluded("Temperature, Min"); bool IncludeTempMax = Request.IsIncluded("Temperature, Max"); bool IncludeTempAvg = Request.IsIncluded("Temperature, Average"); bool IncludePeak = Request.IsIncluded(FieldType.Peak); bool IncludeComputed = Request.IsIncluded(FieldType.Computed); lock (SampleSynch) { if (IncludeTemp && Request.IsIncluded(FieldType.Momentary)) { Fields.Add(new QuantityField(ThingReference.Empty, SampleTime, "Temperature", CurrentTemperature, 1, "°C", FieldType.Momentary, FieldQoS.AutomaticReadout)); } if (IncludePeak) { if (IncludeTempMin) { Fields.Add(new QuantityField(ThingReference.Empty, MinTime, "Temperature, Min", MinTemp, 1, "°C", FieldType.Peak, FieldQoS.AutomaticReadout)); } if (IncludeTempMax) { Fields.Add(new QuantityField(ThingReference.Empty, MaxTime, "Temperature, Max", MaxTemp, 1, "°C", FieldType.Peak, FieldQoS.AutomaticReadout)); } } if (IncludeTempAvg && IncludeComputed) { Fields.Add(new QuantityField(ThingReference.Empty, SampleTime, "Temperature, Average", SumTemp / NrTemp, 2, "°C", FieldType.Computed, FieldQoS.AutomaticReadout)); } if (Request.IsIncluded(FieldType.Historical)) { foreach (DayHistoryRecord Rec in DayHistoricalValues) { if (!Request.IsIncluded(Rec.PeriodStart)) { continue; } if (Fields.Count >= 100) { Request.ReportFields(false, Fields); Fields.Clear(); } if (IncludePeak) { if (IncludeTempMin) { Fields.Add(new QuantityField(ThingReference.Empty, Rec.PeriodStart, "Temperature, Min", Rec.MinTemperature, 1, "°C", FieldType.Peak | FieldType.Historical, FieldQoS.AutomaticReadout)); } if (IncludeTempMax) { Fields.Add(new QuantityField(ThingReference.Empty, Rec.PeriodStart, "Temperature, Max", Rec.MaxTemperature, 1, "°C", FieldType.Peak | FieldType.Historical, FieldQoS.AutomaticReadout)); } } if (IncludeTempAvg && IncludeComputed) { Fields.Add(new QuantityField(ThingReference.Empty, Rec.PeriodStart, "Temperature, Average", Rec.AverageTemperature, 1, "°C", FieldType.Computed | FieldType.Historical, FieldQoS.AutomaticReadout)); } } foreach (MinuteHistoryRecord Rec in MinuteHistoricalValues) { if (!Request.IsIncluded(Rec.Timestamp)) { continue; } if (IncludeTemp) { if (Fields.Count >= 100) { Request.ReportFields(false, Fields); Fields.Clear(); } Fields.Add(new QuantityField(ThingReference.Empty, Rec.Timestamp, "Temperature", Rec.Temperature, 1, "°C", FieldType.Historical, FieldQoS.AutomaticReadout)); } } } } Request.ReportFields(true, Fields); }; this.bobClient = new BobClient(this.xmppClient, Path.Combine(Path.GetTempPath(), "BitsOfBinary")); this.chatServer = new ChatServer(this.xmppClient, this.bobClient, this.sensorServer, this.provisioningClient); this.interoperabilityServer = new InteroperabilityServer(xmppClient); this.interoperabilityServer.OnGetInterfaces += (sender, e) => { e.Add("XMPP.IoT.Sensor.Temperature", "XMPP.IoT.Sensor.Temperature.History", "XMPP.IoT.Sensor.Temperature.Average", "XMPP.IoT.Sensor.Temperature.Average.History", "XMPP.IoT.Sensor.Temperature.Min", "XMPP.IoT.Sensor.Temperature.Min.History", "XMPP.IoT.Sensor.Temperature.Max", "XMPP.IoT.Sensor.Temperature.Max.History"); }; xmppClient.Connect(); } catch (Exception ex) { Log.Emergency(ex); MessageDialog Dialog = new MessageDialog(ex.Message, "Error"); await Dialog.ShowAsync(); } }
public static void Main(string[] _) { try { Console.ForegroundColor = ConsoleColor.White; Console.Out.WriteLine("Welcome to the PC Sensor application."); Console.Out.WriteLine(new string('-', 79)); Console.Out.WriteLine("This application will publish performace couters as sensor values."); Console.Out.WriteLine("Values will be published over XMPP using the interface defined in the IEEE XMPP IoT extensions."); Log.Register(new ConsoleEventSink(false)); Log.RegisterExceptionToUnnest(typeof(System.Runtime.InteropServices.ExternalException)); Log.RegisterExceptionToUnnest(typeof(System.Security.Authentication.AuthenticationException)); credentials = SimpleXmppConfiguration.GetConfigUsingSimpleConsoleDialog("xmpp.config", Environment.MachineName, // Default user name. Guid.NewGuid().ToString().Replace("-", string.Empty), // Default password. typeof(Program).Assembly); using (XmppClient Client = new XmppClient(credentials, "en", typeof(Program).Assembly)) { if (credentials.Sniffer) { Client.Add(new ConsoleOutSniffer(BinaryPresentationMethod.ByteCount, LineEnding.PadWithSpaces)); } if (!string.IsNullOrEmpty(credentials.Events)) { Log.Register(new XmppEventSink("XMPP Event Sink", Client, credentials.Events, false)); } if (!string.IsNullOrEmpty(credentials.ThingRegistry)) { thingRegistryClient = new ThingRegistryClient(Client, credentials.ThingRegistry); thingRegistryClient.Claimed += (sender, e) => { ownerJid = e.JID; Log.Informational("Thing has been claimed.", ownerJid, new KeyValuePair <string, object>("Public", e.IsPublic)); return(Task.CompletedTask); }; thingRegistryClient.Disowned += (sender, e) => { Log.Informational("Thing has been disowned.", ownerJid); ownerJid = string.Empty; Register(); return(Task.CompletedTask); }; thingRegistryClient.Removed += (sender, e) => { Log.Informational("Thing has been removed from the public registry.", ownerJid); return(Task.CompletedTask); }; } ProvisioningClient ProvisioningClient = null; if (!string.IsNullOrEmpty(credentials.Provisioning)) { ProvisioningClient = new ProvisioningClient(Client, credentials.Provisioning); } Timer ConnectionTimer = new Timer((P) => { if (Client.State == XmppState.Offline || Client.State == XmppState.Error || Client.State == XmppState.Authenticating) { try { Client.Reconnect(); } catch (Exception ex) { Log.Critical(ex); } } }, null, 60000, 60000); bool Connected = false; bool ImmediateReconnect; Client.OnStateChanged += (sender, NewState) => { switch (NewState) { case XmppState.Connected: Connected = true; if (!registered && thingRegistryClient != null) { Register(); } break; case XmppState.Offline: ImmediateReconnect = Connected; Connected = false; if (ImmediateReconnect) { Client.Reconnect(); } break; } return(Task.CompletedTask); }; Client.OnPresenceSubscribe += (sender, e) => { e.Accept(); // TODO: Provisioning RosterItem Item = Client.GetRosterItem(e.FromBareJID); if (Item is null || Item.State == SubscriptionState.None || Item.State == SubscriptionState.From) { Client.RequestPresenceSubscription(e.FromBareJID); } Client.SetPresence(Availability.Chat); return(Task.CompletedTask); }; Client.OnPresenceUnsubscribe += (sender, e) => { e.Accept(); return(Task.CompletedTask); }; Client.OnRosterItemUpdated += (sender, e) => { if (e.State == SubscriptionState.None && e.PendingSubscription != PendingSubscription.Subscribe) { Client.RemoveRosterItem(e.BareJid); } return(Task.CompletedTask); }; SortedDictionary <string, string[]> CategoryIncluded = new SortedDictionary <string, string[]>(); List <string> Instances = new List <string>(); XmlDocument Doc = new XmlDocument() { PreserveWhitespace = true }; Doc.Load("categories.xml"); XSL.Validate("categories.xml", Doc, "Categories", "http://waher.se/Schema/PerformanceCounterCategories.xsd", XSL.LoadSchema("Waher.Service.PcSensor.Schema.PerformanceCounterCategories.xsd")); foreach (XmlNode N in Doc.DocumentElement.ChildNodes) { if (N.LocalName == "Category") { XmlElement E = (XmlElement)N; string Name = XML.Attribute(E, "name"); bool Include = XML.Attribute(E, "include", false); if (Include) { Instances.Clear(); foreach (XmlNode N2 in N.ChildNodes) { if (N2.LocalName == "Instance") { E = (XmlElement)N2; Instances.Add(XML.Attribute(E, "name")); } } CategoryIncluded[Name] = Instances.ToArray(); } else { CategoryIncluded[Name] = null; } } } SensorServer SensorServer = new SensorServer(Client, ProvisioningClient, false); SensorServer.OnExecuteReadoutRequest += (Sender, Request) => { Log.Informational("Readout requested", string.Empty, Request.Actor); List <Field> Fields = new List <Field>(); DateTime Now = DateTime.Now; Fields.Add(new StringField(ThingReference.Empty, Now, "Machine Name", Environment.MachineName, FieldType.Identity, FieldQoS.AutomaticReadout)); Fields.Add(new StringField(ThingReference.Empty, Now, "OS Platform", Environment.OSVersion.Platform.ToString(), FieldType.Identity, FieldQoS.AutomaticReadout)); Fields.Add(new StringField(ThingReference.Empty, Now, "OS Service Pack", Environment.OSVersion.ServicePack, FieldType.Identity, FieldQoS.AutomaticReadout)); Fields.Add(new StringField(ThingReference.Empty, Now, "OS Version", Environment.OSVersion.VersionString, FieldType.Identity, FieldQoS.AutomaticReadout)); Fields.Add(new Int32Field(ThingReference.Empty, Now, "Processor Count", Environment.ProcessorCount, FieldType.Status, FieldQoS.AutomaticReadout)); string[] InstanceNames; string FieldName; string Unit; double Value; byte NrDec; bool Updated = false; foreach (PerformanceCounterCategory Category in PerformanceCounterCategory.GetCategories()) { FieldName = Category.CategoryName; lock (CategoryIncluded) { if (CategoryIncluded.TryGetValue(FieldName, out InstanceNames)) { if (InstanceNames is null) { continue; } } else { CategoryIncluded[FieldName] = null; Updated = true; continue; } } if (Category.CategoryType == PerformanceCounterCategoryType.MultiInstance) { foreach (string InstanceName in Category.GetInstanceNames()) { if (InstanceNames.Length > 0 && Array.IndexOf <string>(InstanceNames, InstanceName) < 0) { continue; } foreach (PerformanceCounter Counter in Category.GetCounters(InstanceName)) { FieldName = Category.CategoryName + ", " + InstanceName + ", " + Counter.CounterName; Value = Counter.NextValue(); GetUnitPrecision(ref FieldName, Value, out NrDec, out Unit); if (Fields.Count >= 100) { Request.ReportFields(false, Fields); Fields.Clear(); } Fields.Add(new QuantityField(ThingReference.Empty, Now, FieldName, Value, NrDec, Unit, FieldType.Momentary, FieldQoS.AutomaticReadout)); } } } else { foreach (PerformanceCounter Counter in Category.GetCounters()) { FieldName = Category.CategoryName + ", " + Counter.CounterName; Value = Counter.NextValue(); GetUnitPrecision(ref FieldName, Value, out NrDec, out Unit); if (Fields.Count >= 100) { Request.ReportFields(false, Fields); Fields.Clear(); } Fields.Add(new QuantityField(ThingReference.Empty, Now, FieldName, Value, NrDec, Unit, FieldType.Momentary, FieldQoS.AutomaticReadout)); } } } Request.ReportFields(true, Fields); if (Updated) { using (StreamWriter s = File.CreateText("categories.xml")) { using (XmlWriter w = XmlWriter.Create(s, XML.WriterSettings(true, false))) { w.WriteStartElement("Categories", "http://waher.se/Schema/PerformanceCounterCategories.xsd"); lock (CategoryIncluded) { foreach (KeyValuePair <string, string[]> P in CategoryIncluded) { w.WriteStartElement("Category"); w.WriteAttributeString("name", P.Key); w.WriteAttributeString("include", CommonTypes.Encode(P.Value != null)); if (P.Value != null) { foreach (string InstanceName in P.Value) { w.WriteStartElement("Instance"); w.WriteAttributeString("name", P.Key); w.WriteEndElement(); } } w.WriteEndElement(); } } w.WriteEndElement(); w.Flush(); } } } return(Task.CompletedTask); }; BobClient BobClient = new BobClient(Client, Path.Combine(Path.GetTempPath(), "BitsOfBinary")); ChatServer ChatServer = new ChatServer(Client, BobClient, SensorServer, ProvisioningClient); Client.Connect(); while (true) { Thread.Sleep(1000); } } } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Console.Out.WriteLine(ex.Message); } finally { Log.Terminate(); } }
private void AttachFeatures() { this.controlServer = new ControlServer(this.xmppClient, new BooleanControlParameter("Output", "Actuator", "Output:", "Digital output.", (Node) => this.output, async(Node, Value) => { try { await this.SetOutput(Value, "XMPP"); } catch (Exception ex) { Log.Critical(ex); } })); this.sensorServer = new SensorServer(this.xmppClient, true); this.sensorServer.OnExecuteReadoutRequest += (sender, e) => { try { Log.Informational("Performing readout.", this.xmppClient.BareJID, e.Actor); List <Field> Fields = new List <Field>(); DateTime Now = DateTime.Now; if (e.IsIncluded(FieldType.Identity)) { Fields.Add(new StringField(ThingReference.Empty, Now, "Device ID", this.deviceId, FieldType.Identity, FieldQoS.AutomaticReadout)); } if (this.output.HasValue) { Fields.Add(new BooleanField(ThingReference.Empty, Now, "Output", this.output.Value, FieldType.Momentary, FieldQoS.AutomaticReadout, true)); } e.ReportFields(true, Fields); } catch (Exception ex) { Log.Critical(ex); } }; this.xmppClient.OnError += (Sender, ex) => Log.Error(ex); this.xmppClient.OnPasswordChanged += (Sender, e) => Log.Informational("Password changed.", this.xmppClient.BareJID); this.xmppClient.OnPresenceSubscribe += (Sender, e) => { Log.Informational("Accepting friendship request.", this.xmppClient.BareJID, e.From); e.Accept(); }; this.xmppClient.OnPresenceUnsubscribe += (Sender, e) => { Log.Informational("Friendship removed.", this.xmppClient.BareJID, e.From); e.Accept(); }; this.xmppClient.OnPresenceSubscribed += (Sender, e) => Log.Informational("Friendship request accepted.", this.xmppClient.BareJID, e.From); this.xmppClient.OnPresenceUnsubscribed += (Sender, e) => Log.Informational("Friendship removal accepted.", this.xmppClient.BareJID, e.From); this.bobClient = new BobClient(this.xmppClient, Path.Combine(Path.GetTempPath(), "BitsOfBinary")); this.chatServer = new ChatServer(this.xmppClient, this.bobClient, this.sensorServer, this.controlServer); // XEP-0054: vcard-temp: http://xmpp.org/extensions/xep-0054.html this.xmppClient.RegisterIqGetHandler("vCard", "vcard-temp", this.QueryVCardHandler, true); }
private async Task CompleteConnectionConfirmationAsync(IEnumerable <AliceClient> aliceClients, BobClient bobClient, CancellationToken cancellationToken) { var aliceNodePairs = PairAliceClientAndRequestNodes(aliceClients, Graph); List <Task> connectionConfirmationTasks = new(); using CancellationTokenSource ctsOnError = new(); using CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, ctsOnError.Token); foreach ((var aliceClient, var node) in aliceNodePairs) { var amountEdgeTaskCompSources = Graph.OutEdges(node, CredentialType.Amount).Select(edge => DependencyTasks[edge]); var vsizeEdgeTaskCompSources = Graph.OutEdges(node, CredentialType.Vsize).Select(edge => DependencyTasks[edge]); SmartRequestNode smartRequestNode = new( aliceClient.IssuedAmountCredentials.Take(ProtocolConstants.CredentialNumber).Select(Task.FromResult), aliceClient.IssuedVsizeCredentials.Take(ProtocolConstants.CredentialNumber).Select(Task.FromResult), amountEdgeTaskCompSources, vsizeEdgeTaskCompSources); var amountsToRequest = Graph.OutEdges(node, CredentialType.Amount).Select(e => e.Value); var vsizesToRequest = Graph.OutEdges(node, CredentialType.Vsize).Select(e => e.Value); // Although connection confirmation requests support k // credential requests, for now we only know which amounts to // request after connection confirmation has finished and the // final decomposition can be computed, so as a workaround we // unconditionally request the full amount in one credential and // then do an equivalent reissuance request for every connection // confirmation. var task = smartRequestNode .StartReissuanceAsync(bobClient, amountsToRequest, vsizesToRequest, linkedCts.Token) .ContinueWith((t) => { if (t.IsFaulted && t.Exception is { } exception) { // If one task is failing, cancel all the tasks and throw. ctsOnError.Cancel(); throw exception; } }, linkedCts.Token); connectionConfirmationTasks.Add(task); } await Task.WhenAll(connectionConfirmationTasks).ConfigureAwait(false); var amountEdges = Graph.Inputs.SelectMany(node => Graph.OutEdges(node, CredentialType.Amount)); var vsizeEdges = Graph.Inputs.SelectMany(node => Graph.OutEdges(node, CredentialType.Vsize)); // Check if all tasks were finished, otherwise Task.Result will block. if (!amountEdges.Concat(vsizeEdges).All(edge => DependencyTasks[edge].Task.IsCompletedSuccessfully)) { throw new InvalidOperationException("Some Input nodes out-edges failed to complete."); } }
static void Main(string[] args) { try { Console.ForegroundColor = ConsoleColor.White; Console.Out.WriteLine("Welcome to the Mock Temperature sensor application."); Console.Out.WriteLine(new string('-', 79)); Console.Out.WriteLine("This application will simulate an outside temperature sensor."); Console.Out.WriteLine("Values will be published over XMPP using the interface defined in the IEEE XMPP IoT extensions."); Console.Out.WriteLine("You can also chat with the sensor."); Log.Register(new ConsoleEventSink()); Log.RegisterExceptionToUnnest(typeof(System.Runtime.InteropServices.ExternalException)); Log.RegisterExceptionToUnnest(typeof(System.Security.Authentication.AuthenticationException)); credentials = SimpleXmppConfiguration.GetConfigUsingSimpleConsoleDialog("xmpp.config", Guid.NewGuid().ToString().Replace("-", string.Empty), // Default user name. Guid.NewGuid().ToString().Replace("-", string.Empty), // Default password. typeof(Program).Assembly); using (XmppClient Client = new XmppClient(credentials, "en", typeof(Program).Assembly)) { if (credentials.Sniffer) { Client.Add(new ConsoleOutSniffer(BinaryPresentationMethod.ByteCount, LineEnding.PadWithSpaces)); } if (!string.IsNullOrEmpty(credentials.Events)) { Log.Register(new XmppEventSink("XMPP Event Sink", Client, credentials.Events, false)); } if (!string.IsNullOrEmpty(credentials.ThingRegistry)) { thingRegistryClient = new ThingRegistryClient(Client, credentials.ThingRegistry); thingRegistryClient.Claimed += (sender, e) => { ownerJid = e.JID; Log.Informational("Thing has been claimed.", ownerJid, new KeyValuePair <string, object>("Public", e.IsPublic)); }; thingRegistryClient.Disowned += (sender, e) => { Log.Informational("Thing has been disowned.", ownerJid); ownerJid = string.Empty; Register(); }; thingRegistryClient.Removed += (sender, e) => { Log.Informational("Thing has been removed from the public registry.", ownerJid); }; } ProvisioningClient ProvisioningClient = null; if (!string.IsNullOrEmpty(credentials.Provisioning)) { ProvisioningClient = new ProvisioningClient(Client, credentials.Provisioning); } Timer ConnectionTimer = new Timer((P) => { if (Client.State == XmppState.Offline || Client.State == XmppState.Error || Client.State == XmppState.Authenticating) { try { Client.Reconnect(); } catch (Exception ex) { Log.Critical(ex); } } }, null, 60000, 60000); bool Connected = false; bool ImmediateReconnect; Client.OnStateChanged += (sender, NewState) => { switch (NewState) { case XmppState.Connected: Connected = true; if (!registered && thingRegistryClient != null) { Register(); } break; case XmppState.Offline: ImmediateReconnect = Connected; Connected = false; if (ImmediateReconnect) { Client.Reconnect(); } break; } }; Client.OnPresenceSubscribe += (sender, e) => { e.Accept(); // TODO: Provisioning RosterItem Item = Client.GetRosterItem(e.FromBareJID); if (Item is null || Item.State == SubscriptionState.None || Item.State == SubscriptionState.From) { Client.RequestPresenceSubscription(e.FromBareJID); } Client.SetPresence(Availability.Chat); }; Client.OnPresenceUnsubscribe += (sender, e) => { e.Accept(); }; Client.OnRosterItemUpdated += (sender, e) => { if (e.State == SubscriptionState.None && e.PendingSubscription != PendingSubscription.Subscribe) { Client.RemoveRosterItem(e.BareJid); } }; bool SwitchOn = false; SensorServer SensorServer = new SensorServer(Client, ProvisioningClient, false); SensorServer.OnExecuteReadoutRequest += (Sender, Request) => { DateTime Now = DateTime.Now; Log.Informational("Readout requested", string.Empty, Request.Actor); Request.ReportFields(true, new BooleanField(ThingReference.Empty, Now, "Lamp", SwitchOn, FieldType.Momentary, FieldQoS.AutomaticReadout)); }; ControlServer ControlServer = new ControlServer(Client, new BooleanControlParameter("Lamp", "Control", "Lamp switch on.", "If checked, lamp is turned on.", (Node) => SwitchOn, (Node, Value) => { SwitchOn = Value; Log.Informational(Environment.NewLine + Environment.NewLine + "Lamp turned " + (SwitchOn ? "ON" : "OFF") + Environment.NewLine + Environment.NewLine); })); BobClient BobClient = new BobClient(Client, Path.Combine(Path.GetTempPath(), "BitsOfBinary")); ChatServer ChatServer = new ChatServer(Client, BobClient, SensorServer, ControlServer, ProvisioningClient); InteroperabilityServer InteroperabilityServer = new InteroperabilityServer(Client); InteroperabilityServer.OnGetInterfaces += (sender, e) => { e.Add("XMPP.IoT.Actuator.Lamp"); }; Client.Connect(); while (true) { Thread.Sleep(1000); } } } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Console.Out.WriteLine(ex.Message); } finally { Log.Terminate(); } }
public static void Main(string[] args) { try { Console.ForegroundColor = ConsoleColor.White; Console.Out.WriteLine("Welcome to the Mock Temperature sensor application."); Console.Out.WriteLine(new string('-', 79)); Console.Out.WriteLine("This application will simulate an outside temperature sensor."); Console.Out.WriteLine("Values will be published over XMPP using the interface defined in the IEEE XMPP IoT extensions."); Console.Out.WriteLine("You can also chat with the sensor."); Log.Register(new ConsoleEventSink()); Log.RegisterExceptionToUnnest(typeof(System.Runtime.InteropServices.ExternalException)); Log.RegisterExceptionToUnnest(typeof(System.Security.Authentication.AuthenticationException)); xmppConfiguration = SimpleXmppConfiguration.GetConfigUsingSimpleConsoleDialog("xmpp.config", Guid.NewGuid().ToString().Replace("-", string.Empty), // Default user name. Guid.NewGuid().ToString().Replace("-", string.Empty), // Default password. FormSignatureKey, FormSignatureSecret, typeof(Program).Assembly); using (XmppClient Client = xmppConfiguration.GetClient("en", typeof(Program).Assembly, false)) { Client.AllowRegistration(FormSignatureKey, FormSignatureSecret); if (xmppConfiguration.Sniffer) { Client.Add(new ConsoleOutSniffer(BinaryPresentationMethod.ByteCount, LineEnding.PadWithSpaces)); } if (!string.IsNullOrEmpty(xmppConfiguration.Events)) { Log.Register(new XmppEventSink("XMPP Event Sink", Client, xmppConfiguration.Events, false)); } if (!string.IsNullOrEmpty(xmppConfiguration.ThingRegistry)) { thingRegistryClient = new ThingRegistryClient(Client, xmppConfiguration.ThingRegistry); thingRegistryClient.Claimed += (sender, e) => { ownerJid = e.JID; Log.Informational("Thing has been claimed.", ownerJid, new KeyValuePair <string, object>("Public", e.IsPublic)); }; thingRegistryClient.Disowned += (sender, e) => { Log.Informational("Thing has been disowned.", ownerJid); ownerJid = string.Empty; Register(); }; thingRegistryClient.Removed += (sender, e) => { Log.Informational("Thing has been removed from the public registry.", ownerJid); }; } ProvisioningClient ProvisioningClient = null; if (!string.IsNullOrEmpty(xmppConfiguration.Provisioning)) { ProvisioningClient = new ProvisioningClient(Client, xmppConfiguration.Provisioning); } Timer ConnectionTimer = new Timer((P) => { if (Client.State == XmppState.Offline || Client.State == XmppState.Error || Client.State == XmppState.Authenticating) { try { Client.Reconnect(); } catch (Exception ex) { Log.Critical(ex); } } }, null, 60000, 60000); bool Connected = false; bool ImmediateReconnect; Client.OnStateChanged += (sender, NewState) => { switch (NewState) { case XmppState.Connected: Connected = true; if (!registered && thingRegistryClient != null) { Register(); } break; case XmppState.Offline: ImmediateReconnect = Connected; Connected = false; if (ImmediateReconnect) { Client.Reconnect(); } break; } }; Client.OnPresenceSubscribe += (sender, e) => { e.Accept(); // TODO: Provisioning RosterItem Item = Client.GetRosterItem(e.FromBareJID); if (Item == null || Item.State == SubscriptionState.None || Item.State == SubscriptionState.From) { Client.RequestPresenceSubscription(e.FromBareJID); } Client.SetPresence(Availability.Chat); }; Client.OnPresenceUnsubscribe += (sender, e) => { e.Accept(); }; Client.OnRosterItemUpdated += (sender, e) => { if (e.State == SubscriptionState.None && e.PendingSubscription != PendingSubscription.Subscribe) { Client.RemoveRosterItem(e.BareJid); } }; LinkedList <DayHistoryRecord> DayHistoricalValues = new LinkedList <DayHistoryRecord>(); LinkedList <MinuteHistoryRecord> MinuteHistoricalValues = new LinkedList <MinuteHistoryRecord>(); DateTime SampleTime = DateTime.Now; DateTime PeriodStart = SampleTime.Date; DateTime Now; DateTime MinTime = SampleTime; DateTime MaxTime = SampleTime; double CurrentTemperature = ReadTemp(); double MinTemp = CurrentTemperature; double MaxTemp = CurrentTemperature; double SumTemp = CurrentTemperature; int NrTemp = 1; int NrDayRecords = 0; int NrMinuteRecords = 0; object SampleSynch = new object(); SensorServer SensorServer = new SensorServer(Client, ProvisioningClient, true); SensorServer.OnExecuteReadoutRequest += (Sender, Request) => { Log.Informational("Readout requested", string.Empty, Request.Actor); List <Field> Fields = new List <Field>(); bool IncludeTemp = Request.IsIncluded("Temperature"); bool IncludeTempMin = Request.IsIncluded("Temperature, Min"); bool IncludeTempMax = Request.IsIncluded("Temperature, Max"); bool IncludeTempAvg = Request.IsIncluded("Temperature, Average"); bool IncludePeak = Request.IsIncluded(FieldType.Peak); bool IncludeComputed = Request.IsIncluded(FieldType.Computed); lock (SampleSynch) { if (IncludeTemp && Request.IsIncluded(FieldType.Momentary)) { Fields.Add(new QuantityField(ThingReference.Empty, SampleTime, "Temperature", CurrentTemperature, 1, "°C", FieldType.Momentary, FieldQoS.AutomaticReadout)); } if (IncludePeak) { if (IncludeTempMin) { Fields.Add(new QuantityField(ThingReference.Empty, MinTime, "Temperature, Min", MinTemp, 1, "°C", FieldType.Peak, FieldQoS.AutomaticReadout)); } if (IncludeTempMax) { Fields.Add(new QuantityField(ThingReference.Empty, MaxTime, "Temperature, Max", MaxTemp, 1, "°C", FieldType.Peak, FieldQoS.AutomaticReadout)); } } if (IncludeTempAvg && IncludeComputed) { Fields.Add(new QuantityField(ThingReference.Empty, SampleTime, "Temperature, Average", SumTemp / NrTemp, 2, "°C", FieldType.Computed, FieldQoS.AutomaticReadout)); } if (Request.IsIncluded(FieldType.Historical)) { foreach (DayHistoryRecord Rec in DayHistoricalValues) { if (!Request.IsIncluded(Rec.PeriodStart)) { continue; } if (Fields.Count >= 100) { Request.ReportFields(false, Fields); Fields.Clear(); } if (IncludePeak) { if (IncludeTempMin) { Fields.Add(new QuantityField(ThingReference.Empty, Rec.PeriodStart, "Temperature, Min", Rec.MinTemperature, 1, "°C", FieldType.Peak | FieldType.Historical, FieldQoS.AutomaticReadout)); } if (IncludeTempMax) { Fields.Add(new QuantityField(ThingReference.Empty, Rec.PeriodStart, "Temperature, Max", Rec.MaxTemperature, 1, "°C", FieldType.Peak | FieldType.Historical, FieldQoS.AutomaticReadout)); } } if (IncludeTempAvg && IncludeComputed) { Fields.Add(new QuantityField(ThingReference.Empty, Rec.PeriodStart, "Temperature, Average", Rec.AverageTemperature, 1, "°C", FieldType.Computed | FieldType.Historical, FieldQoS.AutomaticReadout)); } } foreach (MinuteHistoryRecord Rec in MinuteHistoricalValues) { if (!Request.IsIncluded(Rec.Timestamp)) { continue; } if (IncludeTemp) { if (Fields.Count >= 100) { Request.ReportFields(false, Fields); Fields.Clear(); } Fields.Add(new QuantityField(ThingReference.Empty, Rec.Timestamp, "Temperature", Rec.Temperature, 1, "°C", FieldType.Historical, FieldQoS.AutomaticReadout)); } } } } Request.ReportFields(true, Fields); }; Timer SampleTimer = new Timer((P) => { lock (SampleSynch) { Now = DateTime.Now; if (Now.Date != PeriodStart.Date) { DayHistoryRecord Rec = new DayHistoryRecord(PeriodStart.Date, PeriodStart.Date.AddDays(1).AddMilliseconds(-1), MinTemp, MaxTemp, SumTemp / NrTemp); DayHistoricalValues.AddFirst(Rec); if (NrDayRecords < MaxRecordsPerPeriod) { NrDayRecords++; } else { DayHistoricalValues.RemoveLast(); } // TODO: Persistence PeriodStart = Now.Date; SumTemp = 0; NrTemp = 0; } CurrentTemperature = ReadTemp(); if (Now.Minute != SampleTime.Minute) { MinuteHistoryRecord Rec = new MinuteHistoryRecord(Now, CurrentTemperature); MinuteHistoricalValues.AddFirst(Rec); if (NrMinuteRecords < MaxRecordsPerPeriod) { NrMinuteRecords++; } else { MinuteHistoricalValues.RemoveLast(); } // TODO: Persistence } SampleTime = Now; if (CurrentTemperature < MinTemp) { MinTemp = CurrentTemperature; MinTime = SampleTime; } if (CurrentTemperature > MaxTemp) { MaxTemp = CurrentTemperature; MaxTime = SampleTime; } SumTemp += CurrentTemperature; NrTemp++; } if (SensorServer.HasSubscriptions(ThingReference.Empty)) { SensorServer.NewMomentaryValues(new QuantityField(ThingReference.Empty, SampleTime, "Temperature", CurrentTemperature, 1, "°C", FieldType.Momentary, FieldQoS.AutomaticReadout)); } }, null, 1000 - PeriodStart.Millisecond, 1000); BobClient BobClient = new BobClient(Client, Path.Combine(Path.GetTempPath(), "BitsOfBinary")); ChatServer ChatServer = new ChatServer(Client, BobClient, SensorServer); InteroperabilityServer InteroperabilityServer = new InteroperabilityServer(Client); InteroperabilityServer.OnGetInterfaces += (sender, e) => { e.Add("XMPP.IoT.Sensor.Temperature", "XMPP.IoT.Sensor.Temperature.History", "XMPP.IoT.Sensor.Temperature.Average", "XMPP.IoT.Sensor.Temperature.Average.History", "XMPP.IoT.Sensor.Temperature.Min", "XMPP.IoT.Sensor.Temperature.Min.History", "XMPP.IoT.Sensor.Temperature.Max", "XMPP.IoT.Sensor.Temperature.Max.History"); }; Client.Connect(); while (true) { Thread.Sleep(1000); } } } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Console.Out.WriteLine(ex.Message); } finally { Log.Terminate(); } }