diff --git a/Unility/WebService.cs b/Api/BaseApi.cs similarity index 52% rename from Unility/WebService.cs rename to Api/BaseApi.cs index 1278d6b..4fa6c5b 100644 --- a/Unility/WebService.cs +++ b/Api/BaseApi.cs @@ -1,14 +1,12 @@ using Newtonsoft.Json; - -using OnlineUserPool.Model; - +using OnlineUserPool.Unility; +using System; using System.Collections.Generic; -using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; -namespace OnlineUserPool.Unility +namespace OnlineUserPool.Api { public class BaseApi { @@ -19,7 +17,7 @@ namespace OnlineUserPool.Unility httpClient.DefaultRequestHeaders.UserAgent.Add(new System.Net.Http.Headers.ProductInfoHeaderValue("OnlineUserPool", "1.0.0")); } - protected static async Task PostAsync(string url, object data) + public static async Task PostAsync(string url, object data) { StringContent stringContent; if (data != null) @@ -35,36 +33,11 @@ namespace OnlineUserPool.Unility return JsonConvert.DeserializeObject(result); } - protected static async Task GetAsync(string url) + public static async Task GetAsync(string url) { var response = await httpClient.GetAsync(ConfigHelp.Host + url, HttpCompletionOption.ResponseContentRead).ConfigureAwait(false); var result = await response.Content.ReadAsStringAsync(); return JsonConvert.DeserializeObject(result); } } - public class WebService : BaseApi - { - /// - /// 从服务端获取记录信息 - /// - /// - public static List GetRecordFileFromServer(List id) - { - if (!id.Any()) - { - return new List(); - } - return PostAsync>>("MapRecord/GetRandomList", id).ConfigureAwait(false).GetAwaiter().GetResult().data; - } - - public static List GetMapRouteRandomRecord(int top, IEnumerable routeIds) - { - var routeIdsStr = ""; - if(routeIds != null) - { - routeIdsStr = string.Join(",", routeIds); - } - return GetAsync>>($"MapRecord/GetRandomRankingUserRecord?top={ top }&routeIds={ routeIdsStr }").ConfigureAwait(false).GetAwaiter().GetResult().data; - } - } } diff --git a/Api/Model/MultiUserModel.cs b/Api/Model/MultiUserModel.cs new file mode 100644 index 0000000..43de4ad --- /dev/null +++ b/Api/Model/MultiUserModel.cs @@ -0,0 +1,41 @@ +using OnlineUserPool.Model; +using OnlineUserPool.Unility; +using System; +using System.Collections.Generic; +using System.Text; + +namespace OnlineUserPool.Api.Model +{ + public class MultiUserModel + { + public List users { get; set; } + + public MapDataModel route { get; set; } + + + public class User + { + public int Id { get; set; } + + public string NickName { get; set; } + + public string WxHeadImg { get; set; } + + public double Weight { get; set; } + + public User() + { + Speed = CommonHelper.GenerateRandomInteger(0, 30); + + //Distance = CommonHelper.GenerateRandomInteger(0, 30)/1000D; + } + + + public double Speed { + get;set; + } + + public double Distance { get; set; } + } + } +} diff --git a/Api/WebService.cs b/Api/WebService.cs new file mode 100644 index 0000000..4194c43 --- /dev/null +++ b/Api/WebService.cs @@ -0,0 +1,38 @@ +using Newtonsoft.Json; + +using OnlineUserPool.Model; + +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace OnlineUserPool.Api +{ + public class WebService : BaseApi + { + /// + /// 从服务端获取记录信息 + /// + /// + public static List GetRecordFileFromServer(List id) + { + if (!id.Any()) + { + return new List(); + } + return PostAsync>>("MapRecord/GetRandomList", id).ConfigureAwait(false).GetAwaiter().GetResult().data; + } + + public static List GetMapRouteRandomRecord(int top, IEnumerable routeIds) + { + var routeIdsStr = ""; + if(routeIds != null) + { + routeIdsStr = string.Join(",", routeIds); + } + return GetAsync>>($"MapRecord/GetRandomRankingUserRecord?top={ top }&routeIds={ routeIdsStr }").ConfigureAwait(false).GetAwaiter().GetResult().data; + } + } +} diff --git a/App.config b/App.config index 0e9cc1c..1bcae48 100644 --- a/App.config +++ b/App.config @@ -2,9 +2,8 @@ - + - \ No newline at end of file diff --git a/Hander/IHandle.cs b/Hander/IHandle.cs new file mode 100644 index 0000000..35a9ba6 --- /dev/null +++ b/Hander/IHandle.cs @@ -0,0 +1,14 @@ +using OnlineUserPool.Model; +using System; +using System.Collections.Generic; +using System.Text; + +namespace OnlineUserPool.Hander +{ + interface IHandle + { + List GetVirtualUserData(); + + void RemoveEndAndAddNewVirtualUser(int customerCount); + } +} diff --git a/Hander/MapRecordRankingHander.cs b/Hander/MapRecordRankingHander.cs index f74f157..f9d60f4 100644 --- a/Hander/MapRecordRankingHander.cs +++ b/Hander/MapRecordRankingHander.cs @@ -1,4 +1,5 @@ -using OnlineUserPool.Model; +using OnlineUserPool.Api; +using OnlineUserPool.Model; using OnlineUserPool.Unility; using Serilog; @@ -10,11 +11,11 @@ using System.Text; namespace OnlineUserPool.Hander { - public class MapRecordRankingHander + public class MapRecordRankingHander : IHandle { - private static List mapRecordRankings; + private static List mapRecordRankings = new List(); private static object locker = new object();//防止服务端响应慢,多次加载 - private int top = ConfigHelp.Top; + //private int top = ConfigHelp.Top; public MapRecordRankingHander() { @@ -23,13 +24,15 @@ namespace OnlineUserPool.Hander private void Init() { - var records = WebService.GetMapRouteRandomRecord(top, null); - var pageSize = 10; - var pageCount = (int)Math.Ceiling(records.Count / (double)pageSize); - mapRecordRankings = new List(); - for (int i = 0; i < pageCount; i++) + if (ConfigHelp.Top > 0) { - mapRecordRankings.AddRange(WebService.GetRecordFileFromServer(records.Skip(i* pageSize).Take(pageSize).Select(n => n.Id).ToList())); + var records = WebService.GetMapRouteRandomRecord(ConfigHelp.Top, null); + var pageSize = 10; + var pageCount = (int)Math.Ceiling(records.Count / (double)pageSize); + for (int i = 0; i < pageCount; i++) + { + mapRecordRankings.AddRange(WebService.GetRecordFileFromServer(records.Skip(i * pageSize).Take(pageSize).Select(n => n.Id).ToList())); + } } } @@ -73,9 +76,9 @@ namespace OnlineUserPool.Hander //Prop = string.Join(',', prop), RouteId = item.RouteId, EndDistance = Math.Round(targetData._Distance, 6), - ShowVirtual = true, + //ShowVirtual = true, CommandType = 1, - IsVirtual = true, + //IsVirtual = true, Speed = targetData._Speed, WeightKg = weightKg, PreDistance = item.GetPreDistance() @@ -94,31 +97,31 @@ namespace OnlineUserPool.Hander /// /// 删除已经骑行完的人,添加新的人物进去 /// - public void RemoveEndAndAddNewVirtualUser() + public void RemoveEndAndAddNewVirtualUser(int customerCount) { lock (locker) { - var end = mapRecordRankings.FindAll(n => n.End); - //if (end.Count == mapRecordRankings.Count) - //{ - // Init(); - //} - if (end.Count > 0) - { - var virutalEndCount = mapRecordRankings.Count(d => d.UserId < 0 && d.End); - var routeIds = mapRecordRankings.Select(n => n.RouteId).Distinct().ToList(); - mapRecordRankings.RemoveAll(n => n.End); - if (virutalEndCount > 0) - { - var randomUser = WebService.GetMapRouteRandomRecord(virutalEndCount, routeIds); + mapRecordRankings.RemoveAll(n => n.End); + //var virutalEndCount = mapRecordRankings.Count(d => d.UserId < 0 && d.End); + var routeIds = mapRecordRankings.Select(n => n.RouteId).Distinct().ToList(); - //var str = "参数:" + (ConfigHelp.Top - virutalEndCount) + "," + string.Join(",",routeIds) + "\r\n"; - //str += "服务端返回:" + Newtonsoft.Json.JsonConvert.SerializeObject(randomUser) +"\r\n"; - //Log.Information(str); - var addRankings = WebService.GetRecordFileFromServer(randomUser.Select(n => n.Id).ToList()); - mapRecordRankings.AddRange(addRankings); - } + var top = ConfigHelp.Top; + if(customerCount > 9) + { + top = 0; } + if(top - mapRecordRankings.Count(d=>d.UserId<0) > 0) + { + var count = top - mapRecordRankings.Count(d => d.UserId < 0); + var randomUser = WebService.GetMapRouteRandomRecord(count, routeIds); + + //var str = "参数:" + (ConfigHelp.Top - virutalEndCount) + "," + string.Join(",",routeIds) + "\r\n"; + //str += "服务端返回:" + Newtonsoft.Json.JsonConvert.SerializeObject(randomUser) +"\r\n"; + //Log.Information(str); + var addRankings = WebService.GetRecordFileFromServer(randomUser.Select(n => n.Id).ToList()); + mapRecordRankings.AddRange(addRankings); + } + } } } diff --git a/Hander/MultiUserHandle.cs b/Hander/MultiUserHandle.cs new file mode 100644 index 0000000..60a84e1 --- /dev/null +++ b/Hander/MultiUserHandle.cs @@ -0,0 +1,84 @@ +using OnlineUserPool.Api; +using OnlineUserPool.Api.Model; +using OnlineUserPool.Model; +using OnlineUserPool.Unility; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; + +namespace OnlineUserPool.Hander +{ + public class MultiUserHandle : IHandle + { + private int _index = -1; + private MultiUserModel model; + private TurfHelper _turfHelper; + private int _routeId = 4297; //1660; + private int _size = 200; + public MultiUserHandle() + { + Init(); + } + + public async void Init() + { + model = await BaseApi.GetAsync($"/Map/sss?routeId={ _routeId }&size={ _size }"); + double i = 0; + foreach (var item in model.users) + { + i += 0.0005; + item.Distance = i; + } + _turfHelper = new TurfHelper(model.route.List.Select(d => d.Point)); + //Debug.WriteLine(res); + } + + public List GetVirtualUserData() + { + _index++; + + var msgModels = new List(); + if(model == null) + { + return msgModels; + } + foreach (var item in model.users) + { + var preDistance = 0D; + //item.Distance = CommonHelper.GenerateRandomInteger(0, 30) / 1000D; + if (_index > 0) + { + preDistance = item.Distance * (_index -1); + } + var point = _turfHelper.Along(item.Distance * _index); + var info = new MsgModel() + { + exit = false, + IsCompleted = false, + MemberId = item.Id,//虚拟的人Id变为负数 + Point = new double[] { Math.Round(point.Latitude, 6), Math.Round(point.Longitude, 6) }, + //Prop = string.Join(',', prop), + RouteId = _routeId, + EndDistance = item.Distance * _index, + //ShowVirtual = true, + CommandType = 1, + //IsVirtual = true, + Speed = item.Speed, + WeightKg = 0, + PreDistance = preDistance, + Competitionid = 30 + }; + msgModels.Add(info); + } + + return msgModels; + } + + public void RemoveEndAndAddNewVirtualUser(int customerCount) + { + + } + } +} diff --git a/Model/HostModel.cs b/Model/HostModel.cs index 2b828c5..1055329 100644 --- a/Model/HostModel.cs +++ b/Model/HostModel.cs @@ -1,4 +1,5 @@ -using Prism.Mvvm; +using OnlineUserPool.Services; +using Prism.Mvvm; using System; using System.Collections.Generic; using System.Net; @@ -50,9 +51,26 @@ namespace OnlineUserPool.Model /// public List ShowRouteId { get; set; } + ///// + ///// 是否展示在线的人 + ///// + //public bool ShowVirtual { get; set; } + /// - /// 是否展示在线的人 + /// 客户端支持的消息格式的版本号 /// - public bool ShowVirtual { get; set; } + public int V { get; set; } + + [Newtonsoft.Json.JsonIgnore] + public IService Service { get; set; } + + + public String ServiceName + { + get + { + return Service.ToString(); + } + } } } diff --git a/Model/MapDataModel.cs b/Model/MapDataModel.cs new file mode 100644 index 0000000..20df114 --- /dev/null +++ b/Model/MapDataModel.cs @@ -0,0 +1,103 @@ +using Newtonsoft.Json; +using OnlineUserPool.Unility; +using System; +using System.Collections.Generic; +using System.Text; + +namespace OnlineUserPool.Model +{ + public class MapDataModel + { + public string Type => "LineString"; + /// + /// 总距离(km) + /// + public double TotalDistance { get; set; } + + private List _List; + public List List + { + get + { + return _List; + } + set + { + _List = value; + if (_List == null) return; + this.CalcDistance(); + this.CalcGrade(); + } + } + + + public void CalcDistance() + { + for (int i = 0; i < _List.Count - 1; i++) + { + var value = CommonHelper.GetDistances(_List[i].Point[1], _List[i].Point[0], _List[i + 1].Point[1], _List[i + 1].Point[0]); + //var value = Turf.Distance(pt1, pt2, "kilometers") * 1000; + _List[i].Distance = Math.Round(value, 2); + } + } + /// + /// 计算坡度数据 + /// + private void CalcGrade() + { + for (int i = 0; i < this._List.Count - 1; i++) + { + var a = _List[i + 1].Elevation - _List[i].Elevation; + if (a == 0) + { + //grade.Add(0); + _List[i].Grade = 0; + continue; + } + //勾股定理 + var c = _List[i].Distance;//如果车停了下来,c为0 + if (c == 0 && i > 0) + { + _List[i].Grade = _List[i - 1].Grade; + } + else + { + var b = Math.Sqrt(c * c - a * a); + if (b == 0)//如果b等于,这就是垂直的墙壁 + { + _List[i].Grade = _List[i - 1].Grade; + } + else + { + _List[i].Grade = Math.Round(a / b * 100, 4); + } + } + } + } + + public MapDataModel() + { + //_List = new List(); + } + + public class Item + { + /// + /// 坐标(lat,lon) + /// + public double[] Point { get; set; } + /// + /// 距离(单位米) + /// + [JsonIgnore] + public double Distance { get; set; } + /// + /// 海拔m + /// + public double Elevation { get; set; } + + [JsonIgnore] + public double Grade { get; set; } + } + } +} diff --git a/Model/MsgModel.cs b/Model/MsgModel.cs index c03c117..a72a335 100644 --- a/Model/MsgModel.cs +++ b/Model/MsgModel.cs @@ -25,13 +25,13 @@ namespace OnlineUserPool.Model /// /// 是否展示在线的人 /// - public bool ShowVirtual { get; set; } + //public bool ShowVirtual { get; set; } /// /// 命令类型,0命令,1消息 /// public byte CommandType { get; set; } - public bool IsVirtual { get; set; } + //public bool IsVirtual { get; set; } //public double Power { get; set; } //public double Weight { get; set; } @@ -43,5 +43,15 @@ namespace OnlineUserPool.Model public int Competitionid { get; set; } public bool Saved { get; set; } + /// + /// 能处理的消息格式的版本号 + /// + public int V { get; set; } + + + public string ToString(int v) + { + return $"{ RouteId },{ MemberId },{ string.Join(":", Point) },{ Convert.ToInt32(IsCompleted) },{ Speed },{ PreDistance },{ EndDistance },{ WeightKg },{ Competitionid },{ Convert.ToInt32(Saved) }"; + } } } diff --git a/Model/TargetData.cs b/Model/TargetData.cs index d1df023..1561aaf 100644 --- a/Model/TargetData.cs +++ b/Model/TargetData.cs @@ -46,7 +46,11 @@ namespace OnlineUserPool.Model target._Speed = double.Parse(split[2]); target._Distance = double.Parse(split[3]); target._Cadence = double.Parse(split[4]); - target._HeartRate = int.Parse(split[5]); + + if (!string.IsNullOrWhiteSpace(split[5]) && split[5] != "null") + { + target._HeartRate = int.Parse(split[5]); + } if (split.Length > 6) { target._Lat = double.Parse(split[6]); diff --git a/OnlineUserPool.csproj b/OnlineUserPool.csproj index 8455ce9..55c0642 100644 --- a/OnlineUserPool.csproj +++ b/OnlineUserPool.csproj @@ -5,7 +5,13 @@ netcoreapp3.1 true + + + + + + @@ -14,5 +20,10 @@ + + + ..\PowerFun Server\PowerFun Server\lib\TurfCS.dll + + diff --git a/Services/IService.cs b/Services/IService.cs new file mode 100644 index 0000000..51bd1d8 --- /dev/null +++ b/Services/IService.cs @@ -0,0 +1,17 @@ +using OnlineUserPool.Model; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace OnlineUserPool.Services +{ + public interface IService + { + public void RunServer(Action action); + + public void Send(byte[] dgram, int bytes, IPEndPoint endPoint); + + public void Close(); + } +} diff --git a/Services/TcpService.cs b/Services/TcpService.cs new file mode 100644 index 0000000..5c94e75 --- /dev/null +++ b/Services/TcpService.cs @@ -0,0 +1,93 @@ +using OnlineUserPool.Model; +using OnlineUserPool.Unility; +using Serilog; +using SimpleTcp; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace OnlineUserPool.Services +{ + public class TcpService : IService + { + private bool _stop = false; + SimpleTcpServer socket; + Action _action; + + public void RunServer(Action action) + { + //throw new NotImplementedException(); + + _action = action; + //var ip = IPAddress.Parse("192.168.0.97"); + var b = IPAddress.Any; + socket = new SimpleTcpServer($"{ IPAddress.Any }:11001"); + socket.Events.ClientConnected += Events_ClientConnected; + socket.Events.ClientDisconnected += Events_ClientDisconnected; + socket.Events.DataReceived += Events_DataReceived; + + Task.Run(() => + { + socket.Start(); + }); + } + + private void Events_DataReceived(object sender, SimpleTcp.DataReceivedEventArgs e) + { + var returnData = Encoding.UTF8.GetString(e.Data); + Debug.WriteLine(returnData); + + foreach (var item in returnData.Split(new string[] { "}" }, StringSplitOptions.RemoveEmptyEntries)) + { + var msg = Newtonsoft.Json.JsonConvert.DeserializeObject(item +"}"); + var ipEndPoint = IPEndPoint.Parse(e.IpPort); + _action(ipEndPoint, msg, this); + } + } + + private void Events_ClientDisconnected(object sender, ClientDisconnectedEventArgs e) + { + + } + + private void Events_ClientConnected(object sender, ClientConnectedEventArgs e) + { + Debug.WriteLine("有新客户端连接" + e.IpPort); + } + + public void Send(byte[] dgram, int bytes, IPEndPoint endPoint) + { + if(dgram == null || !dgram.Any()) + { + return; + } + + foreach (var item in socket.GetClients()) + { + //try + //{ + if(socket.IsConnected(item) == false) + { + continue; + } + socket.Send(item, dgram); + //} + //catch(Exception e) + //{ + // Log.Error($"{ item.ToString() }:{ e.Message }\r\n{ e.StackTrace }"); + //} + } + } + + public void Close() + { + _stop = true; + socket.Dispose(); + } + } +} diff --git a/Services/TcpService1.cs b/Services/TcpService1.cs new file mode 100644 index 0000000..ee79564 --- /dev/null +++ b/Services/TcpService1.cs @@ -0,0 +1,118 @@ +using NetCoreServer; +using OnlineUserPool.Model; +using Serilog; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; + +namespace OnlineUserPool.Services +{ + public class TcpService1 : IService + { + private PfTcpServer _server; + + public void Close() + { + //throw new NotImplementedException(); + } + + public void RunServer(Action action) + { + //throw new NotImplementedException(); + //_action = action; + _server = new PfTcpServer(IPAddress.Any, 11001, (ip, model)=> { + action(ip, model, this); + }); + //_server.OptionNoDelay = true; + _server.Start(); + } + + + + public void Send(byte[] dgram, int bytes, IPEndPoint endPoint) + { + if (dgram == null || !dgram.Any()) + { + return; + } + _server.Multicast(dgram); + } + + class PfTcpServer : TcpServer + { + Action _action; + public PfTcpServer(IPAddress address, int port, Action action) :base(address, port) + { + _action = action; + } + + protected override TcpSession CreateSession() + { + //return base.CreateSession() + return new PfTcpSession(this, _action); + } + + protected override void OnError(SocketError error) + { + //base.OnError(error); + Debug.WriteLine(error); + } + } + + class PfTcpSession : TcpSession + { + Action _action; + public PfTcpSession(TcpServer server, Action action) : base(server) { + _action = action; + } + + protected override void OnConnected() + { + //base.OnConnected(); + Debug.WriteLine("有新的Tcp连接"); + } + + protected override void OnDisconnected() + { + //base.OnDisconnected(); + Debug.WriteLine("Tcp断开连接"); + } + + protected override void OnReceived(byte[] buffer, long offset, long size) + { + //base.OnReceived(buffer, offset, size); + var returnData = Encoding.UTF8.GetString(buffer, (int)offset, (int)size); + //Debug.WriteLine(returnData); + //Log.Information(returnData + "\r\n"); + try + { + //var msg = Newtonsoft.Json.JsonConvert.DeserializeObject(returnData); + //var ipEndPoint = IPEndPoint.Parse(this.Socket.RemoteEndPoint.ToString()); + //_action(ipEndPoint, msg); + + foreach (var item in returnData.Split(new string[] { "}" }, StringSplitOptions.RemoveEmptyEntries)) + { + //if (string.IsNullOrWhiteSpace(item)) continue; + var msg = Newtonsoft.Json.JsonConvert.DeserializeObject(item + "}"); + var ipEndPoint = IPEndPoint.Parse(this.Socket.RemoteEndPoint.ToString()); + _action(ipEndPoint, msg); + } + } + catch (Exception ex) + { + Log.Error($"接受到的数据处理时出错:{ returnData }\r\n{ ex.Message }\r\n{ ex.StackTrace }"); + } + } + + protected override void OnError(SocketError error) + { + //base.OnError(error); + Debug.WriteLine(error); + } + } + } +} diff --git a/Services/UdpService.cs b/Services/UdpService.cs new file mode 100644 index 0000000..dccba6e --- /dev/null +++ b/Services/UdpService.cs @@ -0,0 +1,80 @@ +using OnlineUserPool.Model; +using OnlineUserPool.Unility; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace OnlineUserPool.Services +{ + public class UdpService : IService + { + private bool _stop = false; + //private static IPEndPoint serverIpEndPoint; + private static UdpClient udpServer; + + public UdpService() + { + //serverIpEndPoint = new IPEndPoint(IPAddress.Parse(ConfigHelp.Ip), ConfigHelp.Port); + } + + public void RunServer(Action action) + { + udpServer = new UdpClient(ConfigHelp.Port); + uint IOC_IN = 0x80000000; + uint IOC_VENDOR = 0x18000000; + uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12; + udpServer.Client.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null); + var remoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0); + Task.Run(() => + { + while (_stop == false) + { + try + { + byte[] receiveBytes = udpServer.Receive(ref remoteIpEndPoint); + var returnData = Encoding.ASCII.GetString(receiveBytes); + var msg = Newtonsoft.Json.JsonConvert.DeserializeObject(returnData); + + //lock (locker) + { +#if DEBUG + WriteLine($"本次接收:{ remoteIpEndPoint.Address.ToString() }:{ remoteIpEndPoint.Port }收到消息:{ returnData }"); +#endif + action(remoteIpEndPoint, msg, this); + + //if (timer.AutoReset == false) + //{ + // timer.AutoReset = true; + //} + } + } + catch (Exception e) + { + Serilog.Log.Error("RunServer:" + e.Message); + } + } + }); + } + + + public void Send(byte[] dgram, int bytes, IPEndPoint endPoint) + { + udpServer.Send(dgram, bytes, endPoint); + } + + public void Close() + { + _stop = true; + udpServer.Close(); + } + + void WriteLine(string str) + { + //Debug.WriteLine(str); + } + } +} diff --git a/Unility/CommonHelper.cs b/Unility/CommonHelper.cs new file mode 100644 index 0000000..990e736 --- /dev/null +++ b/Unility/CommonHelper.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Security.Cryptography; +using System.Text; +using TurfCS; + +namespace OnlineUserPool.Unility +{ + public class CommonHelper + { + public static double GetDistances(double lon, double lat, double lon1, double lat1) + { + var pt1 = Turf.Point(new double[] { lon, lat }); + var pt2 = Turf.Point(new double[] { lon1, lat1 }); + + var value = Turf.Distance(pt1, pt2, "kilometers") * 1000; + return Math.Round(value, 2); + } + + public static int GenerateRandomInteger(int min = 0, int max = int.MaxValue) + { + var randomNumberBuffer = new byte[10]; + new RNGCryptoServiceProvider().GetBytes(randomNumberBuffer); + return new Random(BitConverter.ToInt32(randomNumberBuffer, 0)).Next(min, max); + } + } +} diff --git a/Unility/ConfigHelp.cs b/Unility/ConfigHelp.cs index 54473ff..a6010c7 100644 --- a/Unility/ConfigHelp.cs +++ b/Unility/ConfigHelp.cs @@ -7,20 +7,26 @@ namespace OnlineUserPool.Unility class ConfigHelp { public static string Host { get; set; } - public static int Top { get; private set; } + public static int Top + { + get + { + return Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["Top"]); + } + } public static bool ShowVirtualUser { get; set; } - public static string Ip { get; set; } + //public static string Ip { get; private set; } - public static int Port { get; set; } + public static int Port { get; private set; } static ConfigHelp() { Host = System.Configuration.ConfigurationManager.AppSettings["Host"]; - Top = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["Top"]); + //Top = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["Top"]); ShowVirtualUser = bool.Parse(System.Configuration.ConfigurationManager.AppSettings["ShowVirtualUser"]); - Ip = System.Configuration.ConfigurationManager.AppSettings["Ip"]; + //Ip = System.Configuration.ConfigurationManager.AppSettings["Ip"]; Port = int.Parse(System.Configuration.ConfigurationManager.AppSettings["Port"]); } } diff --git a/Unility/TurfHelper.cs b/Unility/TurfHelper.cs new file mode 100644 index 0000000..151afa4 --- /dev/null +++ b/Unility/TurfHelper.cs @@ -0,0 +1,38 @@ +using GeoJSON.Net.Geometry; +using System; +using System.Collections.Generic; +using System.Text; +using TurfCS; +using System.Linq; + +namespace OnlineUserPool.Unility +{ + public class TurfHelper + { + private LineString _line; + /// + /// + /// + /// 坐标(lat,lon) + public TurfHelper(IEnumerable points) + { + //var list = new GeoJSON.Net.Geometry.GeographicPosition(); + var list = points.Select(p => new GeoJSON.Net.Geometry.GeographicPosition(p[0], p[1])); + + _line = new LineString(list); + } + + /// + /// + /// + /// km + public GeographicPosition Along(double distance) + { + var pt1 = Turf.Along(_line, distance); + return ((GeographicPosition)((GeoJSON.Net.Geometry.Point)pt1.Geometry).Coordinates); + //new LineString() + //new GeoJSON.Net.Geometry.Point(new GeoJSON.Net.Geometry.GeographicPosition()) + //pt1.BoundingBoxes + } + } +} diff --git a/ViewModels/MainWindowViewModel.cs b/ViewModels/MainWindowViewModel.cs index 3e76464..9468500 100644 --- a/ViewModels/MainWindowViewModel.cs +++ b/ViewModels/MainWindowViewModel.cs @@ -13,18 +13,19 @@ using System.Net.Sockets; using System.Threading.Tasks; using System.Windows.Threading; using System.Diagnostics; +using OnlineUserPool.Services; +using Newtonsoft.Json; +using System.Collections.Concurrent; namespace OnlineUserPool.ViewModels { public class MainWindowViewModel : BindableBase - { - private static IPEndPoint serverIpEndPoint; - private static UdpClient udpServer; + { public ObservableCollection Clients { get; private set; } = new ObservableCollection(); - private static List receiveMes = new List(); + private static ConcurrentBag receiveMes = new ConcurrentBag(); private static object locker = new object(); public static System.Timers.Timer timer; - private static MapRecordRankingHander mapRecordRankingHander; + private static IHandle mapRecordRankingHander; private Dispatcher dispatcher; public ObservableCollection Customers { get; private set; } = @@ -40,23 +41,36 @@ namespace OnlineUserPool.ViewModels } } + private string _SendDataSize = ""; + public string SendDataSize + { + get { return _SendDataSize; } + set + { + SetProperty(ref _SendDataSize, value); + } + } + + //private IService _udpService; public MainWindowViewModel() { - Title = $"{ ConfigHelp.Ip }:{ ConfigHelp.Port }"; + Title = $"{ IPAddress.Any }:{ ConfigHelp.Port }"; dispatcher = Dispatcher.CurrentDispatcher; //Customers.Add("suntao"); WriteLine(DateTime.Now.ToShortDateString()); - WriteLine("加载日志组件"); LogHelper.Init(); - Log.Information("日志组件加载完成"); - Log.Information("加载虚拟人物"); mapRecordRankingHander = new MapRecordRankingHander(); - Log.Information("虚拟人物加载完成"); - Log.Information($"初始化连接,当前地址:{ConfigHelp.Ip}:{ConfigHelp.Port}"); - serverIpEndPoint = new IPEndPoint(IPAddress.Parse(ConfigHelp.Ip), ConfigHelp.Port); - RunServer(); + Log.Information($"初始化连接,当前地址:{ IPAddress.Any }:{ConfigHelp.Port}"); + + new TcpService1().RunServer(ReceivedData); + //var tet = new System.Collections.Concurrent.ConcurrentBag(); + + var _udpService = new UdpService(); + _udpService.RunServer(ReceivedData); + + Log.Information("服务启动成功"); timer = new System.Timers.Timer(1000); timer.Elapsed += Timer_Elapsed; @@ -66,96 +80,67 @@ namespace OnlineUserPool.ViewModels //Console.ReadKey(); } + private void ReceivedData(IPEndPoint remoteIpEndPoint, MsgModel msg, IService service) + { + dispatcher.Invoke(() => + { + if (!Clients.Any(c => c.Equals(remoteIpEndPoint))) + { + Clients.Add(new HostModel + { + IPEndPoint = remoteIpEndPoint, + LastActiveTime = DateTime.Now, + RouteId = msg.RouteId, + MemberId = msg.MemberId, + //ShowVirtual = msg.ShowVirtual + V = msg.V, + Service = service + }); + } + + if (msg.CommandType == 0) + { + var client = Clients.FirstOrDefault(n => n.Equals(remoteIpEndPoint)); + client.LastActiveTime = DateTime.Now; + //client.ShowVirtual = msg.ShowVirtual; + } + else if (msg.CommandType == 1) + { + receiveMes.Add(msg); + } + }); + } + private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { NotifyClient(); } - private void RunServer() - { - udpServer = new UdpClient(serverIpEndPoint.Port); - uint IOC_IN = 0x80000000; - uint IOC_VENDOR = 0x18000000; - uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12; - udpServer.Client.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null); - var remoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0); - Task.Run(() => - { - while (true) - { - try - { - byte[] receiveBytes = udpServer.Receive(ref remoteIpEndPoint); - var returnData = Encoding.ASCII.GetString(receiveBytes); - var msg = Newtonsoft.Json.JsonConvert.DeserializeObject(returnData); - lock (locker) - { -#if DEBUG - WriteLine($"本次接收:{ remoteIpEndPoint.Address.ToString() }:{ remoteIpEndPoint.Port }收到消息:{ returnData }"); -#endif - - dispatcher.Invoke(() => - { - if (!Clients.Any(c => c.Equals(remoteIpEndPoint))) - { - Clients.Add(new HostModel - { - IPEndPoint = remoteIpEndPoint, - LastActiveTime = DateTime.Now, - RouteId = msg.RouteId, - MemberId = msg.MemberId, - ShowVirtual = msg.ShowVirtual - }); - } - else - { - if (msg.CommandType == 0) - { - var client = Clients.FirstOrDefault(n => n.Equals(remoteIpEndPoint)); - client.LastActiveTime = DateTime.Now; - client.ShowVirtual = msg.ShowVirtual; - } - else if(msg.CommandType == 1) - { - receiveMes.Add(msg); - } - } - }); - //if (timer.AutoReset == false) - //{ - // timer.AutoReset = true; - //} - } - } - catch (Exception e) - { - Log.Error("RunServer:" + e.Message); - } - } - }); - } - private void NotifyClient() { try { lock (locker) { + var list = CloneJson(receiveMes).ToList(); + receiveMes.Clear(); + //加入虚拟人物消息 var virtualData = mapRecordRankingHander.GetVirtualUserData(); WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "-当前在线人数:" + Clients.Count + "-当前虚拟人数:" + virtualData.Count); + #if DEBUG - WriteLine($"在线人:{Newtonsoft.Json.JsonConvert.SerializeObject(receiveMes)}"); + WriteLine($"在线人:{Newtonsoft.Json.JsonConvert.SerializeObject(list)}"); //\r\n虚拟人:{Newtonsoft.Json.JsonConvert.SerializeObject(virtualData)} #endif - receiveMes.AddRange(virtualData); + list.AddRange(virtualData); - SendMessage(Clients, receiveMes); + SendMessage(Clients, list); dispatcher.Invoke(() => { Customers.Clear(); - foreach (var item in receiveMes) + foreach (var item in list) { if (Customers.Any(c => c.MemberId == item.MemberId)) { @@ -165,20 +150,21 @@ namespace OnlineUserPool.ViewModels } //移除下线的客户端 - for (int i = 0; i < receiveMes.Count; i++) + for (int i = 0; i < list.Count; i++) { - if (receiveMes[i].exit && !receiveMes[i].IsVirtual)//客户端退出,并且不是虚拟的人物 + if (list[i].exit && list[i].MemberId >0)//客户端退出,并且不是虚拟的人物 { //这个地方有严重的逻辑错误(虚拟的人物不能和真实的人用同一个名字) - var info = Clients.FirstOrDefault(n => n.MemberId == receiveMes[i].MemberId); + var info = Clients.FirstOrDefault(n => n.MemberId == list[i].MemberId); if (info != null) { Clients.Remove(info); } } } - receiveMes.Clear();//删除已经发送的数据 - //clients.RemoveAll(i => i.Expire);//移除5钟内连接不上的客户端 + //删除已经发送的数据 + //receiveMes.Clear(); + //移除5钟内连接不上的客户端 Clients.ToList().ForEach(item => { if (item.Expire) @@ -189,7 +175,7 @@ namespace OnlineUserPool.ViewModels }); } //更新虚拟人物信息 - mapRecordRankingHander.RemoveEndAndAddNewVirtualUser(); + mapRecordRankingHander.RemoveEndAndAddNewVirtualUser(Customers.Count); } catch (Exception e) { @@ -197,9 +183,17 @@ namespace OnlineUserPool.ViewModels } } - private static void SendMessage(Collection clients, List msgModels) + private void SendMessage(Collection clients, List msgModels) { - string jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(msgModels.Select(m=> new { + var clients1 = clients.ToList(); + var list = CloneJson>(msgModels); + foreach (var item in list) + { + item.PreDistance = Math.Round(item.PreDistance, 5); + item.EndDistance = Math.Round(item.EndDistance, 5); + item.WeightKg = Math.Round(item.WeightKg, 2); + } + string jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(list.Select(m=> new { m.RouteId, m.MemberId, m.Point, @@ -208,21 +202,42 @@ namespace OnlineUserPool.ViewModels m.Speed, m.PreDistance, m.EndDistance, - m.IsVirtual,//后面要把这个字段过滤掉 + //m.IsVirtual,//后面要把这个字段过滤掉 m.WeightKg, m.Competitionid, - m.Saved + m.Saved, })); var data = Encoding.ASCII.GetBytes(jsonString); - foreach (var item in clients) + SendDataSize = (data.Length/1000D).ToString() +"KB"; + + var strV1 = string.Join("|", list.Select(m => m.ToString(1))); + WriteLine(strV1); + WriteLine(strV1.Length.ToString()); + var dataV1 = Encoding.ASCII.GetBytes(strV1); + + SendDataSize += $"\tV1:{ (strV1.Length/1000D) }KB"; + + foreach (var item in clients1) { try { - udpServer.Send(data, data.Length, item.IPEndPoint); + if(item == null) + { + continue; + } + if (item.V == 1) + { + item.Service.Send(dataV1, dataV1.Length, item.IPEndPoint); + } + else + { + item.Service.Send(data, data.Length, item.IPEndPoint); + } } catch (Exception e) { - Log.Error($"{ item.IPEndPoint.ToString() }:{ e.Message }\r\n{ e.StackTrace }"); + Debug.WriteLine(e.Message); + Log.Error($"{ item.IPEndPoint.ToString() }:{ e.Message }\r\n{ e.StackTrace }"); } } } @@ -230,7 +245,24 @@ namespace OnlineUserPool.ViewModels void WriteLine(string str) { - Debug.WriteLine(str); + //Debug.WriteLine(str); + } + + T CloneJson(T source) + { + // Don't serialize a null object, simply return the default for that object + if (Object.ReferenceEquals(source, null)) + { + return default(T); + } + + // initialize inner objects individually + // for example in default constructor some list property initialized with some values, + // but in 'source' these items are cleaned - + // without ObjectCreationHandling.Replace default constructor values will be added to result + var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace }; + + return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(source), deserializeSettings); } } } diff --git a/Views/MainWindow.xaml b/Views/MainWindow.xaml index cdd95d2..f26b8a0 100644 --- a/Views/MainWindow.xaml +++ b/Views/MainWindow.xaml @@ -8,6 +8,7 @@ Title="{ Binding Title }" Height="450" Width="800"> + @@ -28,7 +29,13 @@ --> - - + + + + + + + +