From 0d68665ab220a028abd46eefd30fa82c6909ec41 Mon Sep 17 00:00:00 2001 From: "SmallChi(Koike)" <564952747@qq.com> Date: Sun, 1 Sep 2019 22:10:32 +0800 Subject: [PATCH] =?UTF-8?q?1.=E8=BF=98=E6=98=AF=E9=80=9A=E8=BF=87=E7=BD=91?= =?UTF-8?q?=E5=85=B3=E9=AA=8C=E8=AF=81=E6=A3=80=E9=AA=8C=E5=92=8C=202.?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0808=E6=B6=88=E6=81=AF=E7=9A=84=E7=94=9F?= =?UTF-8?q?=E4=BA=A7=E6=B6=88=E8=B4=B9=E5=8F=8A=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../JT808.DotNetty.Kafka.csproj | 11 ++- .../JT808ConsumerConfig.cs | 15 ++++ src/JT808.DotNetty.Kafka/JT808MsgConsumer.cs | 83 +++++++++++++++++++ src/JT808.DotNetty.Kafka/JT808MsgProducer.cs | 38 +++++++++ .../JT808MsgReplyConsumer.cs | 83 +++++++++++++++++++ .../JT808MsgReplyProducer.cs | 38 +++++++++ .../JT808ProducerConfig.cs | 15 ++++ .../JT808.DotNetty.RabbitMQ.csproj | 11 +++ .../JT808MsgProducer.cs | 35 ++++++++ .../JT808ProducerConfig.cs | 14 ++++ .../Handlers/JT808TcpServerHandler.cs | 1 - .../JT808.DotNetty.Kafka.Test.csproj | 21 +++++ .../JT808MsgProducerTest.cs | 20 +++++ .../JT808.DotNetty.RabbitMQ.Test.csproj | 15 ++++ .../JT808.DotNetty.RabbitMQ.Test/UnitTest1.cs | 14 ++++ .../Handlers/JT808UdpServerHandler.cs | 1 - src/JT808.DotNetty.sln | 34 +++++--- 17 files changed, 435 insertions(+), 14 deletions(-) create mode 100644 src/JT808.DotNetty.Kafka/JT808ConsumerConfig.cs create mode 100644 src/JT808.DotNetty.Kafka/JT808MsgConsumer.cs create mode 100644 src/JT808.DotNetty.Kafka/JT808MsgProducer.cs create mode 100644 src/JT808.DotNetty.Kafka/JT808MsgReplyConsumer.cs create mode 100644 src/JT808.DotNetty.Kafka/JT808MsgReplyProducer.cs create mode 100644 src/JT808.DotNetty.Kafka/JT808ProducerConfig.cs create mode 100644 src/JT808.DotNetty.RabbitMQ/JT808MsgProducer.cs create mode 100644 src/JT808.DotNetty.RabbitMQ/JT808ProducerConfig.cs create mode 100644 src/JT808.DotNetty.Tests/JT808.DotNetty.Kafka.Test/JT808.DotNetty.Kafka.Test.csproj create mode 100644 src/JT808.DotNetty.Tests/JT808.DotNetty.Kafka.Test/JT808MsgProducerTest.cs create mode 100644 src/JT808.DotNetty.Tests/JT808.DotNetty.RabbitMQ.Test/JT808.DotNetty.RabbitMQ.Test.csproj create mode 100644 src/JT808.DotNetty.Tests/JT808.DotNetty.RabbitMQ.Test/UnitTest1.cs diff --git a/src/JT808.DotNetty.Kafka/JT808.DotNetty.Kafka.csproj b/src/JT808.DotNetty.Kafka/JT808.DotNetty.Kafka.csproj index 73dd61a..3eb35c4 100644 --- a/src/JT808.DotNetty.Kafka/JT808.DotNetty.Kafka.csproj +++ b/src/JT808.DotNetty.Kafka/JT808.DotNetty.Kafka.csproj @@ -1,4 +1,4 @@ - + JT808.DotNetty.Kafka @@ -6,5 +6,12 @@ 基于Kafka的JT808消息发布与订阅 基于Kafka的JT808消息发布与订阅 - + + + + + + + + diff --git a/src/JT808.DotNetty.Kafka/JT808ConsumerConfig.cs b/src/JT808.DotNetty.Kafka/JT808ConsumerConfig.cs new file mode 100644 index 0000000..5ff137a --- /dev/null +++ b/src/JT808.DotNetty.Kafka/JT808ConsumerConfig.cs @@ -0,0 +1,15 @@ +using Confluent.Kafka; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Kafka +{ + public class JT808ConsumerConfig: ConsumerConfig, IOptions + { + public string TopicName { get; set; } + + public JT808ConsumerConfig Value => this; + } +} diff --git a/src/JT808.DotNetty.Kafka/JT808MsgConsumer.cs b/src/JT808.DotNetty.Kafka/JT808MsgConsumer.cs new file mode 100644 index 0000000..76d84c2 --- /dev/null +++ b/src/JT808.DotNetty.Kafka/JT808MsgConsumer.cs @@ -0,0 +1,83 @@ +using Confluent.Kafka; +using JT808.DotNetty.Abstractions; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Kafka +{ + public class JT808MsgConsumer : IJT808MsgConsumer + { + public CancellationTokenSource Cts => new CancellationTokenSource(); + + private readonly IConsumer consumer; + + private readonly ILogger logger; + + public string TopicName { get; } + + public JT808MsgConsumer( + IOptions consumerConfigAccessor, + ILoggerFactory loggerFactory) + { + consumer = new ConsumerBuilder(consumerConfigAccessor.Value).Build(); + logger = loggerFactory.CreateLogger("JT808MsgConsumer"); + } + + public void OnMessage(Action<(string TerminalNo, byte[] Data)> callback) + { + Task.Run(() => + { + while (!Cts.IsCancellationRequested) + { + try + { + //如果不指定分区,根据kafka的机制会从多个分区中拉取数据 + //如果指定分区,根据kafka的机制会从相应的分区中拉取数据 + var data = consumer.Consume(Cts.Token); + if (logger.IsEnabled(LogLevel.Debug)) + { + logger.LogDebug($"Topic: {data.Topic} Key: {data.Key} Partition: {data.Partition} Offset: {data.Offset} TopicPartitionOffset:{data.TopicPartitionOffset}"); + } + callback((data.Key, data.Value)); + } + catch (ConsumeException ex) + { + logger.LogError(ex, TopicName); + Thread.Sleep(1000); + } + catch (OperationCanceledException ex) + { + logger.LogError(ex, TopicName); + Thread.Sleep(1000); + } + catch (Exception ex) + { + logger.LogError(ex, TopicName); + Thread.Sleep(1000); + } + } + }, Cts.Token); + } + + public void Subscribe() + { + consumer.Subscribe(TopicName); + } + + public void Unsubscribe() + { + consumer.Unsubscribe(); + } + + public void Dispose() + { + consumer.Close(); + consumer.Dispose(); + } + } +} diff --git a/src/JT808.DotNetty.Kafka/JT808MsgProducer.cs b/src/JT808.DotNetty.Kafka/JT808MsgProducer.cs new file mode 100644 index 0000000..cae0138 --- /dev/null +++ b/src/JT808.DotNetty.Kafka/JT808MsgProducer.cs @@ -0,0 +1,38 @@ +using Confluent.Kafka; +using JT808.DotNetty.Abstractions; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Kafka +{ + public class JT808MsgProducer : IJT808MsgProducer + { + public string TopicName { get; } + + private readonly IProducer producer; + public JT808MsgProducer( + IOptions producerConfigAccessor) + { + producer = new ProducerBuilder(producerConfigAccessor.Value).Build(); + TopicName = producerConfigAccessor.Value.TopicName; + } + + public void Dispose() + { + producer.Dispose(); + } + + public Task ProduceAsync(string terminalNo, byte[] data) + { + producer.ProduceAsync(TopicName, new Message + { + Key = terminalNo, + Value = data + }); + return Task.CompletedTask; + } + } +} diff --git a/src/JT808.DotNetty.Kafka/JT808MsgReplyConsumer.cs b/src/JT808.DotNetty.Kafka/JT808MsgReplyConsumer.cs new file mode 100644 index 0000000..3903af7 --- /dev/null +++ b/src/JT808.DotNetty.Kafka/JT808MsgReplyConsumer.cs @@ -0,0 +1,83 @@ +using Confluent.Kafka; +using JT808.DotNetty.Abstractions; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Kafka +{ + public class JT808MsgReplyConsumer : IJT808MsgConsumer + { + public CancellationTokenSource Cts => new CancellationTokenSource(); + + private readonly IConsumer consumer; + + private readonly ILogger logger; + + public string TopicName { get; } + + public JT808MsgReplyConsumer( + IOptions consumerConfigAccessor, + ILoggerFactory loggerFactory) + { + consumer = new ConsumerBuilder(consumerConfigAccessor.Value).Build(); + logger = loggerFactory.CreateLogger("JT808MsgReplyConsumer"); + } + + public void OnMessage(Action<(string TerminalNo, byte[] Data)> callback) + { + Task.Run(() => + { + while (!Cts.IsCancellationRequested) + { + try + { + //如果不指定分区,根据kafka的机制会从多个分区中拉取数据 + //如果指定分区,根据kafka的机制会从相应的分区中拉取数据 + var data = consumer.Consume(Cts.Token); + if (logger.IsEnabled(LogLevel.Debug)) + { + logger.LogDebug($"Topic: {data.Topic} Key: {data.Key} Partition: {data.Partition} Offset: {data.Offset} TopicPartitionOffset:{data.TopicPartitionOffset}"); + } + callback((data.Key, data.Value)); + } + catch (ConsumeException ex) + { + logger.LogError(ex, TopicName); + Thread.Sleep(1000); + } + catch (OperationCanceledException ex) + { + logger.LogError(ex, TopicName); + Thread.Sleep(1000); + } + catch (Exception ex) + { + logger.LogError(ex, TopicName); + Thread.Sleep(1000); + } + } + }, Cts.Token); + } + + public void Subscribe() + { + consumer.Subscribe(TopicName); + } + + public void Unsubscribe() + { + consumer.Unsubscribe(); + } + + public void Dispose() + { + consumer.Close(); + consumer.Dispose(); + } + } +} diff --git a/src/JT808.DotNetty.Kafka/JT808MsgReplyProducer.cs b/src/JT808.DotNetty.Kafka/JT808MsgReplyProducer.cs new file mode 100644 index 0000000..bc49e93 --- /dev/null +++ b/src/JT808.DotNetty.Kafka/JT808MsgReplyProducer.cs @@ -0,0 +1,38 @@ +using Confluent.Kafka; +using JT808.DotNetty.Abstractions; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Kafka +{ + public class JT808MsgReplyProducer : IJT808MsgProducer + { + public string TopicName { get;} + + private IProducer producer; + public JT808MsgReplyProducer( + IOptions producerConfigAccessor) + { + producer = new ProducerBuilder(producerConfigAccessor.Value).Build(); + TopicName = producerConfigAccessor.Value.TopicName; + } + + public void Dispose() + { + producer.Dispose(); + } + + public Task ProduceAsync(string terminalNo, byte[] data) + { + producer.ProduceAsync(TopicName, new Message + { + Key = terminalNo, + Value = data + }); + return Task.CompletedTask; + } + } +} diff --git a/src/JT808.DotNetty.Kafka/JT808ProducerConfig.cs b/src/JT808.DotNetty.Kafka/JT808ProducerConfig.cs new file mode 100644 index 0000000..1a3c9ad --- /dev/null +++ b/src/JT808.DotNetty.Kafka/JT808ProducerConfig.cs @@ -0,0 +1,15 @@ +using Confluent.Kafka; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Kafka +{ + public class JT808ProducerConfig : ProducerConfig,IOptions + { + public string TopicName { get; set; } + + public JT808ProducerConfig Value => this; + } +} diff --git a/src/JT808.DotNetty.RabbitMQ/JT808.DotNetty.RabbitMQ.csproj b/src/JT808.DotNetty.RabbitMQ/JT808.DotNetty.RabbitMQ.csproj index 8e980a9..0f3fc58 100644 --- a/src/JT808.DotNetty.RabbitMQ/JT808.DotNetty.RabbitMQ.csproj +++ b/src/JT808.DotNetty.RabbitMQ/JT808.DotNetty.RabbitMQ.csproj @@ -7,5 +7,16 @@ 基于RabbitMQ的JT808消息发布与订阅 基于RabbitMQ的JT808消息发布与订阅 + + + + + + + + + + + diff --git a/src/JT808.DotNetty.RabbitMQ/JT808MsgProducer.cs b/src/JT808.DotNetty.RabbitMQ/JT808MsgProducer.cs new file mode 100644 index 0000000..4f0b149 --- /dev/null +++ b/src/JT808.DotNetty.RabbitMQ/JT808MsgProducer.cs @@ -0,0 +1,35 @@ +using EasyNetQ; +using EasyNetQ.Topology; +using JT808.DotNetty.Abstractions; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace JT808.DotNetty.RabbitMQ +{ + public class JT808MsgProducer : IJT808MsgProducer + { + public string TopicName { get; } + + private readonly IBus bus; + public JT808MsgProducer(IOptions producerConfigAccessor) + { + bus = RabbitHutch.CreateBus(producerConfigAccessor.Value.ConnectionString); + TopicName = producerConfigAccessor.Value.TopicName; + } + + public void Dispose() + { + bus.Dispose(); + } + + public Task ProduceAsync(string terminalNo, byte[] data) + { + var exchange = bus.Advanced.ExchangeDeclare(TopicName, ExchangeType.Fanout); + bus.Advanced.Publish(exchange, "", false, new Message(data)); + return Task.CompletedTask; + } + } +} diff --git a/src/JT808.DotNetty.RabbitMQ/JT808ProducerConfig.cs b/src/JT808.DotNetty.RabbitMQ/JT808ProducerConfig.cs new file mode 100644 index 0000000..cf43fa1 --- /dev/null +++ b/src/JT808.DotNetty.RabbitMQ/JT808ProducerConfig.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.RabbitMQ +{ + public class JT808ProducerConfig : IOptions + { + public string TopicName { get; set; } + public string ConnectionString { get; set; } + public JT808ProducerConfig Value => this; + } +} diff --git a/src/JT808.DotNetty.Tcp/Handlers/JT808TcpServerHandler.cs b/src/JT808.DotNetty.Tcp/Handlers/JT808TcpServerHandler.cs index 1666b43..c65a5cf 100644 --- a/src/JT808.DotNetty.Tcp/Handlers/JT808TcpServerHandler.cs +++ b/src/JT808.DotNetty.Tcp/Handlers/JT808TcpServerHandler.cs @@ -37,7 +37,6 @@ namespace JT808.DotNetty.Tcp.Handlers this.JT808MsgProducer = jT808MsgProducer; this.jT808AtomicCounterService = jT808AtomicCounterServiceFactory.Create(JT808TransportProtocolType.tcp); this.JT808Serializer = jT808Config.GetSerializer(); - jT808Config.SkipCRCCode = true; logger = loggerFactory.CreateLogger(); } diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Kafka.Test/JT808.DotNetty.Kafka.Test.csproj b/src/JT808.DotNetty.Tests/JT808.DotNetty.Kafka.Test/JT808.DotNetty.Kafka.Test.csproj new file mode 100644 index 0000000..db1c6cf --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Kafka.Test/JT808.DotNetty.Kafka.Test.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp2.2 + + false + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Kafka.Test/JT808MsgProducerTest.cs b/src/JT808.DotNetty.Tests/JT808.DotNetty.Kafka.Test/JT808MsgProducerTest.cs new file mode 100644 index 0000000..bb8d49f --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Kafka.Test/JT808MsgProducerTest.cs @@ -0,0 +1,20 @@ +using JT808.DotNetty.Abstractions; +using System; +using System.Collections.Generic; +using System.Text; +using Xunit; + +namespace JT808.DotNetty.Kafka.Test +{ + public class JT808MsgProducerTest + { + [Fact] + public void Test1() + { + using (IJT808MsgProducer jT808MsgProducer = new JT808MsgProducer(new JT808ProducerConfig { BootstrapServers = "192.168.3.11:9092" })) + { + jT808MsgProducer.ProduceAsync("123456", new byte[] { 0x7E, 0, 0x7E }); + } + } + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.RabbitMQ.Test/JT808.DotNetty.RabbitMQ.Test.csproj b/src/JT808.DotNetty.Tests/JT808.DotNetty.RabbitMQ.Test/JT808.DotNetty.RabbitMQ.Test.csproj new file mode 100644 index 0000000..d45c579 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.RabbitMQ.Test/JT808.DotNetty.RabbitMQ.Test.csproj @@ -0,0 +1,15 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.RabbitMQ.Test/UnitTest1.cs b/src/JT808.DotNetty.Tests/JT808.DotNetty.RabbitMQ.Test/UnitTest1.cs new file mode 100644 index 0000000..fc2ac69 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.RabbitMQ.Test/UnitTest1.cs @@ -0,0 +1,14 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace JT808.DotNetty.RabbitMQ.Test +{ + [TestClass] + public class UnitTest1 + { + //https://www.rabbitmq.com/getstarted.html + [TestMethod] + public void TestMethod1() + { + } + } +} diff --git a/src/JT808.DotNetty.Udp/Handlers/JT808UdpServerHandler.cs b/src/JT808.DotNetty.Udp/Handlers/JT808UdpServerHandler.cs index 1a0a023..e3c6471 100644 --- a/src/JT808.DotNetty.Udp/Handlers/JT808UdpServerHandler.cs +++ b/src/JT808.DotNetty.Udp/Handlers/JT808UdpServerHandler.cs @@ -38,7 +38,6 @@ namespace JT808.DotNetty.Udp.Handlers this.jT808UdpSessionManager = jT808UdpSessionManager; logger = loggerFactory.CreateLogger(); JT808Serializer = jT808Config.GetSerializer(); - jT808Config.SkipCRCCode = true; } protected override void ChannelRead0(IChannelHandlerContext ctx, JT808UdpPackage msg) diff --git a/src/JT808.DotNetty.sln b/src/JT808.DotNetty.sln index 51842ff..7d3576f 100644 --- a/src/JT808.DotNetty.sln +++ b/src/JT808.DotNetty.sln @@ -37,9 +37,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.SimpleServer EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.CleintBenchmark", "JT808.DotNetty.CleintBenchmark\JT808.DotNetty.CleintBenchmark.csproj", "{C2B1A0F4-2C49-45DA-9F48-7A016FC6E9E1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JT808.DotNetty.Kafka", "JT808.DotNetty.Kafka\JT808.DotNetty.Kafka.csproj", "{7050DC16-4CD8-406C-9F3B-F085407E94EB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Kafka", "JT808.DotNetty.Kafka\JT808.DotNetty.Kafka.csproj", "{576A8394-AA60-4DAE-864B-D4BBB67B8E75}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JT808.DotNetty.RabbitMQ", "JT808.DotNetty.RabbitMQ\JT808.DotNetty.RabbitMQ.csproj", "{48C53550-610E-4CE5-AFE4-E285280A8365}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Kafka.Test", "JT808.DotNetty.Tests\JT808.DotNetty.Kafka.Test\JT808.DotNetty.Kafka.Test.csproj", "{50A94BD5-5CDF-4777-AE4C-80BA769AEDAB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JT808.DotNetty.RabbitMQ", "JT808.DotNetty.RabbitMQ\JT808.DotNetty.RabbitMQ.csproj", "{BE8F9C65-2F03-4E8A-88D2-0F846D871473}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JT808.DotNetty.RabbitMQ.Test", "JT808.DotNetty.Tests\JT808.DotNetty.RabbitMQ.Test\JT808.DotNetty.RabbitMQ.Test.csproj", "{D3CA0D73-1CCF-41BA-88D8-5BE50515CA64}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -107,14 +111,22 @@ Global {C2B1A0F4-2C49-45DA-9F48-7A016FC6E9E1}.Debug|Any CPU.Build.0 = Debug|Any CPU {C2B1A0F4-2C49-45DA-9F48-7A016FC6E9E1}.Release|Any CPU.ActiveCfg = Release|Any CPU {C2B1A0F4-2C49-45DA-9F48-7A016FC6E9E1}.Release|Any CPU.Build.0 = Release|Any CPU - {7050DC16-4CD8-406C-9F3B-F085407E94EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7050DC16-4CD8-406C-9F3B-F085407E94EB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7050DC16-4CD8-406C-9F3B-F085407E94EB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7050DC16-4CD8-406C-9F3B-F085407E94EB}.Release|Any CPU.Build.0 = Release|Any CPU - {48C53550-610E-4CE5-AFE4-E285280A8365}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {48C53550-610E-4CE5-AFE4-E285280A8365}.Debug|Any CPU.Build.0 = Debug|Any CPU - {48C53550-610E-4CE5-AFE4-E285280A8365}.Release|Any CPU.ActiveCfg = Release|Any CPU - {48C53550-610E-4CE5-AFE4-E285280A8365}.Release|Any CPU.Build.0 = Release|Any CPU + {576A8394-AA60-4DAE-864B-D4BBB67B8E75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {576A8394-AA60-4DAE-864B-D4BBB67B8E75}.Debug|Any CPU.Build.0 = Debug|Any CPU + {576A8394-AA60-4DAE-864B-D4BBB67B8E75}.Release|Any CPU.ActiveCfg = Release|Any CPU + {576A8394-AA60-4DAE-864B-D4BBB67B8E75}.Release|Any CPU.Build.0 = Release|Any CPU + {50A94BD5-5CDF-4777-AE4C-80BA769AEDAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50A94BD5-5CDF-4777-AE4C-80BA769AEDAB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50A94BD5-5CDF-4777-AE4C-80BA769AEDAB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50A94BD5-5CDF-4777-AE4C-80BA769AEDAB}.Release|Any CPU.Build.0 = Release|Any CPU + {BE8F9C65-2F03-4E8A-88D2-0F846D871473}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BE8F9C65-2F03-4E8A-88D2-0F846D871473}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BE8F9C65-2F03-4E8A-88D2-0F846D871473}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BE8F9C65-2F03-4E8A-88D2-0F846D871473}.Release|Any CPU.Build.0 = Release|Any CPU + {D3CA0D73-1CCF-41BA-88D8-5BE50515CA64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D3CA0D73-1CCF-41BA-88D8-5BE50515CA64}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D3CA0D73-1CCF-41BA-88D8-5BE50515CA64}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D3CA0D73-1CCF-41BA-88D8-5BE50515CA64}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -127,6 +139,8 @@ Global {A0F2F006-5AEB-454E-83C5-ABFB58DE17A9} = {3BD7FF02-8516-4A77-A385-9FDCDD792E22} {E6F61CE8-BFB4-4946-A0D3-AECCE77824E5} = {2459FB59-8A33-49A4-ADBC-A0B12C5886A6} {CCE6AEFB-1AB0-4BD9-8EA2-8B4CDD097E88} = {2459FB59-8A33-49A4-ADBC-A0B12C5886A6} + {50A94BD5-5CDF-4777-AE4C-80BA769AEDAB} = {3BD7FF02-8516-4A77-A385-9FDCDD792E22} + {D3CA0D73-1CCF-41BA-88D8-5BE50515CA64} = {3BD7FF02-8516-4A77-A385-9FDCDD792E22} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {FC0FFCEA-E1EF-4C97-A1C5-F89418B6834B}