客户端获取硬件信息,单元测试通过

This commit is contained in:
大石头 2020-03-15 14:46:28 +08:00
parent 889765144a
commit 9178790639
7 changed files with 436 additions and 136 deletions

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="coverlet.collector" Version="1.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Stardust\Stardust.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,78 @@
using System;
using NewLife;
using NewLife.Security;
using Stardust;
using Xunit;
namespace ClientTest
{
public class StarClientTests
{
public String Server { get; set; } = "http://localhost:6600";
//public StarClient Client { get; }
public StarClientTests()
{
//Client = new StarClient(Server);
//Client.Add("default", new Uri(Server));
}
[Fact]
public void GetLoginInfoTest()
{
var client = new StarClient
{
Code = Rand.NextString(8),
Secret = Rand.NextString(16)
};
var inf = client.GetLoginInfo();
Assert.NotNull(inf);
Assert.NotNull(inf.Node);
Assert.Equal(client.Code, inf.Code);
Assert.Equal(client.Secret.MD5(), inf.Secret);
var node = client.GetNodeInfo();
var mi = MachineInfo.Current;
Assert.Equal(mi.UUID, node.UUID);
Assert.Equal(mi.Guid, node.MachineGuid);
}
[Theory(DisplayName = "登录测试")]
[InlineData("abcd", "1234")]
[InlineData(null, "1234")]
[InlineData("abcd", null)]
public async void LoginTest(String code, String secret)
{
var client = new StarClient(Server)
{
Code = code,
Secret = secret
};
var rs = await client.Login();
Assert.NotNull(rs);
Assert.NotNull(client.Info);
Assert.True(client.Logined);
}
[Fact]
public async void LogoutTest()
{
var client = new StarClient(Server);
await client.Login();
await client.Logout("test");
}
[Fact]
public void GetHeartInfoTest()
{
var client = new StarClient();
var inf = client.GetHeartInfo();
Assert.NotNull(inf);
Assert.NotEmpty(inf.Macs);
}
}
}

View File

@ -1,29 +1,23 @@
using NewLife;
using System;
using System.Collections.Generic;
using NewLife;
using NewLife.Agent;
using NewLife.Log;
using NewLife.Net;
using NewLife.Remoting;
using NewLife.Threading;
using Stardust;
using System;
using System.Collections.Generic;
namespace StarAgent
{
class Program
{
static void Main(String[] args)
{
new MyService().Main();
}
static void Main(String[] args) => new MyService().Main();
}
/// <summary>服务类。名字可以自定义</summary>
class MyService : AgentServiceBase<MyService>
{
/// <summary>是否使用线程池调度。false表示禁用线程池改用Agent线程</summary>
public Boolean Pooling { get; set; } = true;
public MyService()
{
ServiceName = "StarAgent";
@ -38,13 +32,13 @@ namespace StarAgent
if (_Client == null)
{
var set = Setting.Current;
if (!set.Server.IsNullOrEmpty())
InitClient(set.Server);
else
{
WriteLog("未配置服务端地址,开始自动发现");
StartDiscover();
}
//if (!set.Server.IsNullOrEmpty())
InitClient(set.Server);
//else
//{
// WriteLog("未配置服务端地址,开始自动发现");
// StartDiscover();
//}
}
}
@ -58,14 +52,14 @@ namespace StarAgent
var client = new StarClient(server)
{
UserName = Environment.MachineName,
Password = Environment.MachineName,
Code = Environment.MachineName,
Secret = Environment.MachineName,
Log = XTrace.Log,
};
if (set.Debug) client.EncoderLog = XTrace.Log;
//if (set.Debug) client.EncoderLog = XTrace.Log;
client.Open();
client.LoginAsync();
//client.Open();
client.Login();
_Client = client;
}
@ -96,63 +90,63 @@ namespace StarAgent
}
#region
private ApiClient _udp;
private TimerX _udp_timer;
private void StartDiscover()
{
var tc = new ApiClient("udp://255.255.255.255:6666")
{
UsePool = false,
Log = XTrace.Log,
EncoderLog = XTrace.Log,
Timeout = 1_000
};
//private ApiClient _udp;
//private TimerX _udp_timer;
//private void StartDiscover()
//{
// var tc = new ApiClient("udp://255.255.255.255:6666")
// {
// UsePool = false,
// Log = XTrace.Log,
// EncoderLog = XTrace.Log,
// Timeout = 1_000
// };
tc.Open();
// tc.Open();
// 定时广播
_udp_timer = new TimerX(OnDiscover, tc, 0, 5_000) { Async = true };
// // 定时广播
// _udp_timer = new TimerX(OnDiscover, tc, 0, 5_000) { Async = true };
_udp = tc;
}
// _udp = tc;
//}
private void OnDiscover(Object state)
{
//var udp = new UdpServer();
//udp.Log = XTrace.Log;
//private void OnDiscover(Object state)
//{
// //var udp = new UdpServer();
// //udp.Log = XTrace.Log;
//var ep = new IPEndPoint(IPAddress.Broadcast, 6666);
//var session = udp.CreateSession(ep);
//session.Send("Hello");
// //var ep = new IPEndPoint(IPAddress.Broadcast, 6666);
// //var session = udp.CreateSession(ep);
// //session.Send("Hello");
var tc = state as ApiClient;
// var tc = state as ApiClient;
var dic = tc.Invoke<IDictionary<String, Object>>("Discover", new { state = DateTime.Now.ToFullString() });
if (dic == null || dic.Count == 0) return;
// var dic = tc.Invoke<IDictionary<String, Object>>("Discover", new { state = DateTime.Now.ToFullString() });
// if (dic == null || dic.Count == 0) return;
var str = dic["Server"] + "";
if (str.IsNullOrEmpty()) return;
// var str = dic["Server"] + "";
// if (str.IsNullOrEmpty()) return;
//WriteLog("收到[{0}]{1}", tc, str);
// //WriteLog("收到[{0}]{1}", tc, str);
if (!str.IsNullOrEmpty())
{
var uri = new NetUri(str);
if (!uri.Host.IsNullOrEmpty() && uri.Port > 0)
{
WriteLog("发现服务器:{0}", uri);
// if (!str.IsNullOrEmpty())
// {
// var uri = new NetUri(str);
// if (!uri.Host.IsNullOrEmpty() && uri.Port > 0)
// {
// WriteLog("发现服务器:{0}", uri);
// 停止广播
_udp_timer.TryDispose();
_udp_timer = null;
// // 停止广播
// _udp_timer.TryDispose();
// _udp_timer = null;
_udp.TryDispose();
_udp = null;
// _udp.TryDispose();
// _udp = null;
InitClient(str);
}
}
}
// InitClient(str);
// }
// }
//}
#endregion
/// <summary>数据测试菜单t</summary>

View File

@ -51,8 +51,8 @@ namespace Stardust.Models
/// <summary>MAC地址</summary>
public String Macs { get; set; }
/// <summary>串口</summary>
public String COMs { get; set; }
///// <summary>串口</summary>
//public String COMs { get; set; }
/// <summary>安装路径</summary>
public String InstallPath { get; set; }

View File

@ -1,12 +1,17 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using NewLife;
using NewLife.Log;
using NewLife.Net;
using NewLife.Reflection;
using NewLife.Remoting;
using NewLife.Serialization;
using NewLife.Threading;
using Stardust.Models;
using Stardust.Services;
namespace Stardust
{
@ -14,95 +19,292 @@ namespace Stardust
public class StarClient : ApiHttpClient
{
#region
/// <summary>用户名</summary>
public String UserName { get; set; }
/// <summary>证书</summary>
public String Code { get; set; }
/// <summary>密</summary>
public String Password { get; set; }
/// <summary>密</summary>
public String Secret { get; set; }
/// <summary>是否已登录</summary>
public Boolean Logined { get; set; }
/// <summary>最后一次登录成功后的消息</summary>
public IDictionary<String, Object> Info { get; private set; }
public LoginResponse Info { get; private set; }
/// <summary>请求到服务端并返回的延迟时间。单位ms</summary>
public Int32 Delay { get; set; }
/// <summary>命令队列</summary>
public IQueueService<CommandModel> CommandQueue { get; } = new QueueService<CommandModel>();
#endregion
#region
#region
/// <summary>实例化</summary>
public StarClient()
{
Log = XTrace.Log;
_task = MachineInfo.RegisterAsync();
}
/// <summary>实例化</summary>
/// <param name="uri"></param>
public StarClient(String uri)
/// <param name="urls"></param>
public StarClient(String urls) : this()
{
//if (!uri.IsNullOrEmpty())
//{
// var u = new Uri(uri);
// Servers = new[] { "{2}://{0}:{1}".F(u.Host, u.Port, u.Scheme) };
// var us = u.UserInfo.Split(":");
// if (us.Length > 0) UserName = us[0];
// if (us.Length > 1) Password = us[1];
//}
if (!urls.IsNullOrEmpty())
{
var ss = urls.Split(",");
for (var i = 0; i < ss.Length; i++)
{
Add("service" + (i + 1), new Uri(ss[i]));
}
}
}
#endregion
#region
///// <summary>连接后自动登录</summary>
///// <param name="client">客户端</param>
///// <param name="force">强制登录</param>
//protected override async Task<Object> OnLoginAsync(ISocketClient client, Boolean force)
//{
// if (Logined && !force) return null;
/// <summary>登录</summary>
/// <returns></returns>
public async Task<Object> Login()
{
XTrace.WriteLine("登录:{0}", Code);
// var asmx = AssemblyX.Entry;
var info = GetLoginInfo();
// var arg = new
// {
// user = UserName,
// pass = Password.MD5(),
// machine = Environment.MachineName,
// processid = Process.GetCurrentProcess().Id,
// version = asmx?.Version,
// compile = asmx?.Compile,
// };
Logined = false;
// var rs = await base.InvokeWithClientAsync<Object>(client, "Login", arg);
// if (Setting.Current.Debug) XTrace.WriteLine("登录{0}成功!{1}", client, rs.ToJson());
var rs = Info = await LoginAsync(info);
if (rs != null && !rs.Code.IsNullOrEmpty())
{
XTrace.WriteLine("下发证书:{0}/{1}", rs.Code, rs.Secret);
Code = rs.Code;
Secret = rs.Secret;
}
// Logined = true;
// 登录后设置用于用户认证的token
Token = rs.Token;
// return Info = rs as IDictionary<String, Object>;
//}
Logined = true;
if (Logined && _timer == null)
{
lock (this)
{
if (_timer == null)
{
_timer = new TimerX(s => Ping(), null, 5_000, 60_000, "Device") { Async = true };
}
}
}
return Info;
}
/// <summary>获取登录信息</summary>
/// <returns></returns>
public LoginInfo GetLoginInfo()
{
var di = GetNodeInfo();
var ext = new LoginInfo
{
Code = Code,
Secret = Secret.IsNullOrEmpty() ? null : Secret.MD5(),
Node = di,
};
return ext;
}
private readonly Task<MachineInfo> _task;
/// <summary>获取设备信息</summary>
/// <returns></returns>
public NodeInfo GetNodeInfo()
{
var mi = MachineInfo.Current ?? _task.Result;
var asm = AssemblyX.Entry ?? AssemblyX.Create(Assembly.GetExecutingAssembly());
//var ps = System.IO.Ports.SerialPort.GetPortNames();
var mcs = NetHelper.GetMacs().Select(e => e.ToHex("-")).OrderBy(e => e).Join(",");
var di = new NodeInfo
{
Version = asm.FileVersion,
Compile = asm.Compile,
OSName = mi.OSName,
OSVersion = mi.OSVersion,
MachineName = Environment.MachineName,
UserName = Environment.UserName,
ProcessorCount = Environment.ProcessorCount,
Memory = mi.Memory,
AvailableMemory = mi.AvailableMemory,
Processor = mi.Processor,
CpuID = mi.CpuID,
CpuRate = mi.CpuRate,
UUID = mi.UUID,
MachineGuid = mi.Guid,
Macs = mcs,
//COMs = ps.Join(","),
InstallPath = ".".GetFullPath(),
Runtime = Environment.Version + "",
Time = DateTime.Now,
};
return di;
}
/// <summary>注销</summary>
/// <param name="reason"></param>
/// <returns></returns>
public async Task<Object> Logout(String reason)
{
if (!Logined) return null;
XTrace.WriteLine("注销:{0} {1}", Code, reason);
try
{
var rs = await LogoutAsync(reason);
if (rs != null)
{
// 更新令牌
Token = rs.Token;
}
Logined = false;
return rs;
}
catch (Exception ex)
{
XTrace.WriteException(ex);
return null;
}
}
/// <summary>登录</summary>
/// <param name="inf">登录信息</param>
/// <returns></returns>
private async Task<LoginResponse> LoginAsync(LoginInfo inf) => await PostAsync<LoginResponse>("Node/Login", inf);
/// <summary>注销</summary>
/// <returns></returns>
private async Task<LoginResponse> LogoutAsync(String reason) => await GetAsync<LoginResponse>("Node/Logout", new { reason });
#endregion
#region
/// <summary>获取心跳信息</summary>
public PingInfo GetHeartInfo()
{
var asm = AssemblyX.Entry;
//var ps = System.IO.Ports.SerialPort.GetPortNames();
var pcs = new List<Process>();
foreach (var item in Process.GetProcesses().OrderBy(e => e.SessionId).ThenBy(e => e.ProcessName))
{
var name = item.ProcessName;
if (name.EqualIgnoreCase("svchost", "dllhost", "conhost")) continue;
if (!pcs.Contains(item)) pcs.Add(item);
}
var mi = MachineInfo.Current;
mi.Refresh();
var mcs = NetHelper.GetMacs().Select(e => e.ToHex("-")).OrderBy(e => e).Join(",");
var ext = new PingInfo
{
AvailableMemory = mi.AvailableMemory,
CpuRate = mi.CpuRate,
Macs = mcs,
//COMs = ps.Join(","),
Processes = pcs.Join(",", e => e.ProcessName),
Time = DateTime.Now.ToLong(),
Delay = Delay,
};
return ext;
}
private TimerX _timer;
/// <summary>心跳</summary>
/// <returns></returns>
public async Task<Object> Ping()
{
XTrace.WriteLine("心跳");
var inf = GetHeartInfo();
try
{
var rs = await PingAsync(inf);
if (rs != null)
{
var dt = rs.Time.ToDateTime();
if (dt.Year > 2000)
{
// 计算延迟
var ts = DateTime.Now - dt;
var ms = (Int32)ts.TotalMilliseconds;
if (Delay > 0)
Delay = (Delay + ms) / 2;
else
Delay = ms;
}
// 推队列
if (rs.Commands != null && rs.Commands.Length > 0)
{
foreach (var item in rs.Commands)
{
CommandQueue.Public(item.Command, item);
}
}
}
return rs;
}
catch (Exception ex)
{
if (ex is AggregateException agg)
{
if (agg.InnerExceptions[0] is ApiException aex && aex.Code == 402)
{
XTrace.WriteLine("重新登录");
return Login();
}
}
XTrace.WriteLine("心跳异常 {0}", (String)ex.GetTrue().Message);
throw;
}
}
/// <summary>心跳</summary>
/// <param name="inf"></param>
/// <returns></returns>
private async Task<PingResponse> PingAsync(PingInfo inf) => await PostAsync<PingResponse>("Node/Ping", inf);
/// <summary>上报命令结果,如截屏、抓日志</summary>
/// <param name="id"></param>
/// <returns></returns>
private async Task<Object> ReportAsync(Int32 id, Byte[] data) => await PostAsync<Object>("Device/Report?Id=" + id, data);
#endregion
#region
///// <summary>上报服务</summary>
///// <param name="nameSpace"></param>
///// <param name="services"></param>
///// <returns></returns>
//public async Task<Boolean> ReportAsync(String nameSpace, String[] services)
//{
// return await InvokeAsync<Boolean>("Report", new { nameSpace, services });
//}
#endregion
#region
//#if DEBUG
// /// <summary>创建</summary>
// /// <param name="svr"></param>
// /// <returns></returns>
// protected override ISocketClient OnCreate(String svr)
// {
// var client = base.OnCreate(svr);
// if (client != null) client.Log = Log;
// return client;
// }
//#endif
#endregion
#region
/// <summary>更新</summary>
/// <param name="channel"></param>
/// <returns></returns>
public async Task<UpgradeInfo> UpgradeAsync(String channel) => await GetAsync<UpgradeInfo>("Node/Upgrade", new { channel });
#endregion
}
}

View File

@ -26,8 +26,8 @@ namespace Test
var star = new StarClient("tcp://127.0.0.1:6666")
{
UserName = "test",
Password = "pass"
Code = "test",
Secret = "pass"
};
sc.Star = star;

View File

@ -27,6 +27,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.editorconfig = .editorconfig
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClientTest", "ClientTest\ClientTest.csproj", "{7DE10A4D-1749-4474-A6B2-F52CA8462813}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -61,6 +63,10 @@ Global
{0FF65D90-214F-405E-9674-6C0992BC61FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0FF65D90-214F-405E-9674-6C0992BC61FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0FF65D90-214F-405E-9674-6C0992BC61FD}.Release|Any CPU.Build.0 = Release|Any CPU
{7DE10A4D-1749-4474-A6B2-F52CA8462813}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7DE10A4D-1749-4474-A6B2-F52CA8462813}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7DE10A4D-1749-4474-A6B2-F52CA8462813}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7DE10A4D-1749-4474-A6B2-F52CA8462813}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE