Переглянути джерело

1.元旦快乐,有事停写4天

2.去除无用项目
3.调整UDP的会话管理、解码器
4.调整TCP/UDP的统一下发
tags/v1.0.0
SmallChi 6 роки тому
джерело
коміт
4bfada2fb7
17 змінених файлів з 152 додано та 97 видалено
  1. +1
    -0
      src/JT808.DotNetty.Core/Codecs/JT808UdpDecoder.cs
  2. +12
    -3
      src/JT808.DotNetty.Core/JT808SimpleTcpClient.cs
  3. +13
    -3
      src/JT808.DotNetty.Core/JT808SimpleUdpClient.cs
  4. +4
    -13
      src/JT808.DotNetty.Core/Services/JT808UnificationTcpSendService.cs
  5. +3
    -12
      src/JT808.DotNetty.Core/Services/JT808UnificationUdpSendService.cs
  6. +9
    -6
      src/JT808.DotNetty.Core/Session/JT808TcpSessionManager.cs
  7. +49
    -13
      src/JT808.DotNetty.Core/Session/JT808UdpSessionManager.cs
  8. +3
    -1
      src/JT808.DotNetty.Hosting/JT808.DotNetty.Hosting.csproj
  9. +0
    -22
      src/JT808.DotNetty.Hosting/JT808MsgIdCustomHandler.cs
  10. +11
    -6
      src/JT808.DotNetty.Hosting/Program.cs
  11. +3
    -1
      src/JT808.DotNetty.Hosting/appsettings.json
  12. +13
    -0
      src/JT808.DotNetty.Tests/JT808.DotNetty.Tcp.Test/JT808SessionServiceTest.cs
  13. +17
    -0
      src/JT808.DotNetty.Tests/JT808.DotNetty.Udp.Test/JT808SessionServiceTest.cs
  14. +4
    -0
      src/JT808.DotNetty.Udp/Handlers/JT808UdpServerHandler.cs
  15. +1
    -1
      src/JT808.DotNetty.Udp/JT808UdpDotnettyExtensions.cs
  16. +1
    -1
      src/JT808.DotNetty.Udp/JT808UdpServerHost.cs
  17. +8
    -15
      src/JT808.DotNetty.sln

+ 1
- 0
src/JT808.DotNetty.Core/Codecs/JT808UdpDecoder.cs Переглянути файл

@@ -11,6 +11,7 @@ namespace JT808.DotNetty.Core.Codecs
{
protected override void Decode(IChannelHandlerContext context, DatagramPacket message, List<object> output)
{
if (!message.Content.IsReadable()) return;
IByteBuffer byteBuffer = message.Content;
byte[] buffer = new byte[byteBuffer.ReadableBytes];
byteBuffer.ReadBytes(buffer);


+ 12
- 3
src/JT808.DotNetty.Core/JT808SimpleTcpClient.cs Переглянути файл

@@ -18,9 +18,18 @@ namespace JT808.DotNetty.Core
Task.Run(()=> {
while (true)
{
byte[] buffer = new byte[100];
tcpClient.GetStream().Read(buffer, 0, 100);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " "+string.Join(" ", buffer));
try
{
byte[] buffer = new byte[100];
tcpClient.GetStream().Read(buffer, 0, 100);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " " + string.Join(" ", buffer));
}
catch
{

}
Thread.Sleep(1000);
}
});


+ 13
- 3
src/JT808.DotNetty.Core/JT808SimpleUdpClient.cs Переглянути файл

@@ -15,11 +15,21 @@ namespace JT808.DotNetty.Core
{
udpClient = new UdpClient();
udpClient.Connect(remoteAddress);
Task.Run(() => {
Task.Run(() =>
{
while (true)
{
string tmp = string.Join(" ", udpClient.Receive(ref remoteAddress));
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " " + tmp);
try
{
string tmp = string.Join(" ", udpClient.Receive(ref remoteAddress));
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " " + tmp);
Thread.Sleep(1000);
}
catch
{


}
Thread.Sleep(1000);
}
});


+ 4
- 13
src/JT808.DotNetty.Core/Services/JT808UnificationTcpSendService.cs Переглянути файл

@@ -22,19 +22,10 @@ namespace JT808.DotNetty.Internal
{
var session = jT808SessionManager.GetSession(terminalPhoneNo);
if (session != null)
{
if (session.Channel.Open)
{
session.Channel.WriteAndFlushAsync(Unpooled.WrappedBuffer(data));
resultDto.Code = JT808ResultCode.Ok;
resultDto.Data = true;
}
else
{
resultDto.Code = JT808ResultCode.Ok;
resultDto.Data = false;
resultDto.Message = "offline";
}
{
session.Channel.WriteAndFlushAsync(Unpooled.WrappedBuffer(data));
resultDto.Code = JT808ResultCode.Ok;
resultDto.Data = true;
}
else
{


+ 3
- 12
src/JT808.DotNetty.Core/Services/JT808UnificationUdpSendService.cs Переглянути файл

@@ -24,18 +24,9 @@ namespace JT808.DotNetty.Internal
var session = jT808SessionManager.GetSession(terminalPhoneNo);
if (session != null)
{
if (session.Channel.Open)
{
session.Channel.WriteAndFlushAsync(new DatagramPacket(Unpooled.WrappedBuffer(data), session.Sender));
resultDto.Code = JT808ResultCode.Ok;
resultDto.Data = true;
}
else
{
resultDto.Code = JT808ResultCode.Ok;
resultDto.Data = false;
resultDto.Message = "offline";
}
session.Channel.WriteAndFlushAsync(new DatagramPacket(Unpooled.WrappedBuffer(data), session.Sender));
resultDto.Code = JT808ResultCode.Ok;
resultDto.Data = true;
}
else
{


+ 9
- 6
src/JT808.DotNetty.Core/Session/JT808TcpSessionManager.cs Переглянути файл

@@ -123,13 +123,16 @@ namespace JT808.DotNetty.Core
//todo: 设备离线可以进行通知
//todo: 使用Redis 发布订阅
var terminalPhoneNos = SessionIdDict.Where(w => w.Value.Channel.Id == channel.Id).Select(s => s.Key).ToList();
foreach (var key in terminalPhoneNos)
if (terminalPhoneNos.Count > 0)
{
SessionIdDict.TryRemove(key, out JT808TcpSession jT808SessionRemove);
}
string nos = string.Join(",", terminalPhoneNos);
logger.LogInformation($">>>{nos} Channel Remove.");
jT808SessionPublishing.PublishAsync(JT808Constants.SessionOffline,nos);
foreach (var key in terminalPhoneNos)
{
SessionIdDict.TryRemove(key, out JT808TcpSession jT808SessionRemove);
}
string nos = string.Join(",", terminalPhoneNos);
logger.LogInformation($">>>{nos} Channel Remove.");
jT808SessionPublishing.PublishAsync(JT808Constants.SessionOffline, nos);
}
}

public IEnumerable<JT808TcpSession> GetAll()


+ 49
- 13
src/JT808.DotNetty.Core/Session/JT808UdpSessionManager.cs Переглянути файл

@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using JT808.DotNetty.Abstractions;
using JT808.DotNetty.Core.Metadata;
using DotNetty.Transport.Channels;

namespace JT808.DotNetty.Core
{
@@ -51,20 +52,38 @@ namespace JT808.DotNetty.Core
}

public void TryAdd(JT808UdpSession appSession)
{
// 解决了设备号跟通道绑定到一起,不需要用到通道本身的SessionId
// 不管设备下发更改了设备终端号,只要是没有在内存中就当是新的
// 存在的问题:
// 1.原先老的如何销毁
// 2.这时候用的通道是相同的,设备终端是不同的
// 当设备主动或者服务器断开以后,可以释放,这点内存忽略不计,况且更改设备号不是很频繁。
if (SessionIdDict.TryAdd(appSession.TerminalPhoneNo, appSession))
{
//1.先判断是否在缓存里面
if(SessionIdDict.TryGetValue(appSession.TerminalPhoneNo,out JT808UdpSession jT808UdpSession))
{
//使用场景:
//部标的超长待机设备,不会像正常的设备一样一直连着,可能10几分钟连上了,然后发完就关闭连接,
//这时候想下发数据需要知道设备什么时候上线,在这边做通知最好不过了。
//todo: 有设备关联上来可以进行通知 例如:使用Redis发布订阅
jT808SessionPublishing.PublishAsync(JT808Constants.SessionOnline, appSession.TerminalPhoneNo);
//处理缓存
//判断设备的终结点是否相同
if (jT808UdpSession.Sender.Equals(appSession.Sender))
{
//相同 更新最后上线时间
//每次使用最新的通道
//将设备第一次上线时间赋值给当前上线的时间
appSession.StartTime = jT808UdpSession.StartTime;
SessionIdDict.TryUpdate(appSession.TerminalPhoneNo, appSession, appSession);
}
else
{
//不同 算成新设备上来并且推送通知
SessionIdDict.TryUpdate(appSession.TerminalPhoneNo, appSession, appSession);
jT808SessionPublishing.PublishAsync(JT808Constants.SessionOnline, appSession.TerminalPhoneNo);
}
}
else
{
//添加缓存
if (SessionIdDict.TryAdd(appSession.TerminalPhoneNo, appSession))
{
//使用场景:
//部标的超长待机设备,不会像正常的设备一样一直连着,可能10几分钟连上了,然后发完就关闭连接,
//这时候想下发数据需要知道设备什么时候上线,在这边做通知最好不过了。
//todo: 有设备关联上来可以进行通知 例如:使用Redis发布订阅
jT808SessionPublishing.PublishAsync(JT808Constants.SessionOnline, appSession.TerminalPhoneNo);
}
}
}

@@ -99,6 +118,23 @@ namespace JT808.DotNetty.Core
}
}

public void RemoveSessionByChannel(IChannel channel)
{
//todo: 设备离线可以进行通知
//todo: 使用Redis 发布订阅
var terminalPhoneNos = SessionIdDict.Where(w => w.Value.Channel.Id == channel.Id).Select(s => s.Key).ToList();
if (terminalPhoneNos.Count > 0)
{
foreach (var key in terminalPhoneNos)
{
SessionIdDict.TryRemove(key, out JT808UdpSession jT808SessionRemove);
}
string nos = string.Join(",", terminalPhoneNos);
logger.LogInformation($">>>{nos} Channel Remove.");
jT808SessionPublishing.PublishAsync(JT808Constants.SessionOffline, nos);
}
}

public IEnumerable<JT808UdpSession> GetAll()
{
return SessionIdDict.Select(s => s.Value).ToList();


+ 3
- 1
src/JT808.DotNetty.Hosting/JT808.DotNetty.Hosting.csproj Переглянути файл

@@ -15,7 +15,9 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\JT808.DotNetty\JT808.DotNetty.csproj" />
<ProjectReference Include="..\JT808.DotNetty.Tcp\JT808.DotNetty.Tcp.csproj" />
<ProjectReference Include="..\JT808.DotNetty.Udp\JT808.DotNetty.Udp.csproj" />
<ProjectReference Include="..\JT808.DotNetty.WebApi\JT808.DotNetty.WebApi.csproj" />
</ItemGroup>

<ItemGroup>


+ 0
- 22
src/JT808.DotNetty.Hosting/JT808MsgIdCustomHandler.cs Переглянути файл

@@ -1,22 +0,0 @@
using JT808.DotNetty.Metadata;
using Microsoft.Extensions.Logging;

namespace JT808.DotNetty.Hosting
{
public class JT808MsgIdCustomHandler : JT808MsgIdHandlerBase
{
private readonly ILogger<JT808MsgIdCustomHandler> logger;
public JT808MsgIdCustomHandler(
ILoggerFactory loggerFactory,
JT808SessionManager sessionManager) : base(sessionManager)
{
logger = loggerFactory.CreateLogger<JT808MsgIdCustomHandler>();
}

public override JT808Response Msg0x0102(JT808Request request)
{
logger.LogDebug("Msg0x0102");
return base.Msg0x0102(request);
}
}
}

+ 11
- 6
src/JT808.DotNetty.Hosting/Program.cs Переглянути файл

@@ -1,4 +1,8 @@
using Microsoft.Extensions.Configuration;
using JT808.DotNetty.Core;
using JT808.DotNetty.Tcp;
using JT808.DotNetty.Udp;
using JT808.DotNetty.WebApi;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
@@ -24,16 +28,17 @@ namespace JT808.DotNetty.Hosting
.ConfigureLogging((context, logging) =>
{
logging.AddConsole();
logging.SetMinimumLevel(LogLevel.Debug);
logging.SetMinimumLevel(LogLevel.Trace);
})
.ConfigureServices((hostContext, services) =>
{
services.AddSingleton<ILoggerFactory, LoggerFactory>();
services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
// 自定义消息处理业务
services.Replace(new ServiceDescriptor(typeof(JT808MsgIdHandlerBase), typeof(JT808MsgIdCustomHandler), ServiceLifetime.Singleton));
})
.UseJT808Host();
services.AddJT808Core(hostContext.Configuration)
.AddJT808TcpHost()
.AddJT808UdpHost()
.AddJT808WebApiHost();
});

await serverHostBuilder.RunConsoleAsync();
}


+ 3
- 1
src/JT808.DotNetty.Hosting/appsettings.json Переглянути файл

@@ -13,6 +13,8 @@
}
},
"JT808Configuration": {
"Port": 6565
"TcpPort": 12808,
"UdpPort": 12818,
"WebApiPort": 12828
}
}

+ 13
- 0
src/JT808.DotNetty.Tests/JT808.DotNetty.Tcp.Test/JT808SessionServiceTest.cs Переглянути файл

@@ -77,5 +77,18 @@ namespace JT808.DotNetty.Tcp.Test
var result2 = jT808SessionServiceDefaultImpl.RemoveByTerminalPhoneNo("123456789001");
var result3 = jT808SessionServiceDefaultImpl.GetAll();
}

[TestMethod]
public void Test3()
{
// 判断通道是否关闭
IJT808TcpSessionService jT808SessionServiceDefaultImpl = ServiceProvider.GetService<IJT808TcpSessionService>();
JT808TcpSessionManager jT808TcpSessionManager = ServiceProvider.GetService<JT808TcpSessionManager>();
var result1 = jT808SessionServiceDefaultImpl.GetAll();
SimpleTcpClient1.Down();
Thread.Sleep(5000);
var session = jT808TcpSessionManager.GetSession("123456789001");
Thread.Sleep(100000);
}
}
}

+ 17
- 0
src/JT808.DotNetty.Tests/JT808.DotNetty.Udp.Test/JT808SessionServiceTest.cs Переглянути файл

@@ -33,6 +33,10 @@ namespace JT808.DotNetty.Udp.Test

public JT808SessionServiceTest()
{
JT808SimpleUdpClient SimpleUdpClient11 = new JT808SimpleUdpClient(new IPEndPoint(IPAddress.Parse("157.255.57.82"), 12818));

JT808Package jT808Package11 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789001");
SimpleUdpClient11.WriteAsync(JT808Serializer.Serialize(jT808Package11));
SimpleUdpClient1 = new JT808SimpleUdpClient(endPoint);
SimpleUdpClient2 = new JT808SimpleUdpClient(endPoint);
SimpleUdpClient3 = new JT808SimpleUdpClient(endPoint);
@@ -75,5 +79,18 @@ namespace JT808.DotNetty.Udp.Test
var result2 = jT808SessionServiceDefaultImpl.RemoveByTerminalPhoneNo("123456789001");
var result3 = jT808SessionServiceDefaultImpl.GetAll();
}

[TestMethod]
public void Test3()
{
// 判断通道是否关闭
IJT808UdpSessionService jT808SessionServiceDefaultImpl = ServiceProvider.GetService<IJT808UdpSessionService>();
JT808UdpSessionManager jT808UdpSessionManager = ServiceProvider.GetService<JT808UdpSessionManager>();
var result1 = jT808SessionServiceDefaultImpl.GetAll();
SimpleUdpClient1.Down();
var session = jT808UdpSessionManager.GetSession("123456789001");
var result3 = jT808UdpSessionManager.GetAll();
Thread.Sleep(100000);
}
}
}

+ 4
- 0
src/JT808.DotNetty.Udp/Handlers/JT808UdpServerHandler.cs Переглянути файл

@@ -9,6 +9,7 @@ using JT808.DotNetty.Abstractions;
using JT808.DotNetty.Core.Services;
using JT808.DotNetty.Core;
using JT808.DotNetty.Core.Handlers;
using System.Threading.Tasks;

namespace JT808.DotNetty.Udp.Handlers
{
@@ -85,5 +86,8 @@ namespace JT808.DotNetty.Udp.Handlers
}
}
}

public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush();

}
}

+ 1
- 1
src/JT808.DotNetty.Udp/JT808UdpDotnettyExtensions.cs Переглянути файл

@@ -15,7 +15,7 @@ namespace JT808.DotNetty.Udp
{
public static IServiceCollection AddJT808UdpHost(this IServiceCollection serviceDescriptors)
{
serviceDescriptors.TryAddSingleton<JT808UdpSessionManager>();
serviceDescriptors.TryAddSingleton<JT808UdpSessionManager>();
serviceDescriptors.TryAddSingleton<JT808UdpAtomicCounterService>();
serviceDescriptors.TryAddSingleton<JT808MsgIdUdpHandlerBase, JT808MsgIdDefaultUdpHandler>();
serviceDescriptors.TryAddScoped<JT808UdpDecoder>();


+ 1
- 1
src/JT808.DotNetty.Udp/JT808UdpServerHost.cs Переглянути файл

@@ -55,7 +55,7 @@ namespace JT808.DotNetty.Udp
{
IChannelPipeline pipeline = channel.Pipeline;
using (var scope = serviceProvider.CreateScope())
{
{
pipeline.AddLast("jt808UdpDecoder", scope.ServiceProvider.GetRequiredService<JT808UdpDecoder>());
pipeline.AddLast("jt808UdpService", scope.ServiceProvider.GetRequiredService<JT808UdpServerHandler>());
}


+ 8
- 15
src/JT808.DotNetty.sln Переглянути файл

@@ -21,15 +21,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.WebApi", "JT
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Core.Test", "JT808.DotNetty.Tests\JT808.DotNetty.Core.Test\JT808.DotNetty.Core.Test.csproj", "{1C4CCE9B-761B-4581-B5DA-5B6D83572D56}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Test", "JT808.DotNetty.Test\JT808.DotNetty.Test.csproj", "{7BF06DFC-5F7B-4DEB-8005-B460ADF65B95}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty", "JT808.DotNetty\JT808.DotNetty.csproj", "{042C719D-9545-44C4-9EFE-500C1DA5DAC2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Tcp.Test", "JT808.DotNetty.Tests\JT808.DotNetty.Tcp.Test\JT808.DotNetty.Tcp.Test.csproj", "{AEF1E1E2-C861-4268-86F6-6F376FAF79A7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Udp.Test", "JT808.DotNetty.Tests\JT808.DotNetty.Udp.Test\JT808.DotNetty.Udp.Test.csproj", "{E503BFD8-D90A-4610-97C7-5B9A0497303B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JT808.DotNetty.WebApi.Test", "JT808.DotNetty.Tests\JT808.DotNetty.WebApi.Test\JT808.DotNetty.WebApi.Test.csproj", "{EDE77A29-0840-450C-8B08-2D3388845AE5}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.WebApi.Test", "JT808.DotNetty.Tests\JT808.DotNetty.WebApi.Test\JT808.DotNetty.WebApi.Test.csproj", "{EDE77A29-0840-450C-8B08-2D3388845AE5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Hosting", "JT808.DotNetty.Hosting\JT808.DotNetty.Hosting.csproj", "{2E95C08A-B512-4252-A412-84E4EDCAE717}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -65,14 +63,6 @@ Global
{1C4CCE9B-761B-4581-B5DA-5B6D83572D56}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1C4CCE9B-761B-4581-B5DA-5B6D83572D56}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1C4CCE9B-761B-4581-B5DA-5B6D83572D56}.Release|Any CPU.Build.0 = Release|Any CPU
{7BF06DFC-5F7B-4DEB-8005-B460ADF65B95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7BF06DFC-5F7B-4DEB-8005-B460ADF65B95}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7BF06DFC-5F7B-4DEB-8005-B460ADF65B95}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7BF06DFC-5F7B-4DEB-8005-B460ADF65B95}.Release|Any CPU.Build.0 = Release|Any CPU
{042C719D-9545-44C4-9EFE-500C1DA5DAC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{042C719D-9545-44C4-9EFE-500C1DA5DAC2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{042C719D-9545-44C4-9EFE-500C1DA5DAC2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{042C719D-9545-44C4-9EFE-500C1DA5DAC2}.Release|Any CPU.Build.0 = Release|Any CPU
{AEF1E1E2-C861-4268-86F6-6F376FAF79A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AEF1E1E2-C861-4268-86F6-6F376FAF79A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AEF1E1E2-C861-4268-86F6-6F376FAF79A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -85,6 +75,10 @@ Global
{EDE77A29-0840-450C-8B08-2D3388845AE5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EDE77A29-0840-450C-8B08-2D3388845AE5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EDE77A29-0840-450C-8B08-2D3388845AE5}.Release|Any CPU.Build.0 = Release|Any CPU
{2E95C08A-B512-4252-A412-84E4EDCAE717}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2E95C08A-B512-4252-A412-84E4EDCAE717}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2E95C08A-B512-4252-A412-84E4EDCAE717}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2E95C08A-B512-4252-A412-84E4EDCAE717}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -92,11 +86,10 @@ Global
GlobalSection(NestedProjects) = preSolution
{9FCA2EE9-8253-41AA-A64C-9883413864F9} = {B5A80356-5AF6-449F-9D8B-3C1BBB9D2443}
{1C4CCE9B-761B-4581-B5DA-5B6D83572D56} = {3BD7FF02-8516-4A77-A385-9FDCDD792E22}
{7BF06DFC-5F7B-4DEB-8005-B460ADF65B95} = {B5A80356-5AF6-449F-9D8B-3C1BBB9D2443}
{042C719D-9545-44C4-9EFE-500C1DA5DAC2} = {B5A80356-5AF6-449F-9D8B-3C1BBB9D2443}
{AEF1E1E2-C861-4268-86F6-6F376FAF79A7} = {3BD7FF02-8516-4A77-A385-9FDCDD792E22}
{E503BFD8-D90A-4610-97C7-5B9A0497303B} = {3BD7FF02-8516-4A77-A385-9FDCDD792E22}
{EDE77A29-0840-450C-8B08-2D3388845AE5} = {3BD7FF02-8516-4A77-A385-9FDCDD792E22}
{2E95C08A-B512-4252-A412-84E4EDCAE717} = {3BD7FF02-8516-4A77-A385-9FDCDD792E22}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FC0FFCEA-E1EF-4C97-A1C5-F89418B6834B}


Завантаження…
Відмінити
Зберегти