using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Threading; using System.Timers; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using CncSimulator.Config; using CncSimulator.Device; using CncSimulator.Generator; using CncSimulator.Core; namespace CncSimulator.Core { /// 单个地址的 HTTP 服务器及设备仿真集合 public class SimulatorServer { public AddressConfig Address { get; private set; } public List Devices { get; private set; } private readonly FanucDataGenerator _generator = new FanucDataGenerator(); private readonly LogRecorder _logRecorder = new LogRecorder(); private HttpListener _http; private Timer _tickTimer; public bool IsRunning { get; private set; } = false; public int RequestCount { get; private set; } = 0; public int TotalDeviceCount => Devices?.Count ?? 0; public int OnlineDeviceCount => Devices?.Count(d => d.State?.IsOnline == true) ?? 0; public DateTime StartTime { get; private set; } public SimulatorServer(AddressConfig address, List devices) { Address = address; Devices = devices ?? new List(); } public void Start() { if (IsRunning) return; _http = new HttpListener(); _http.Prefixes.Add($"http://+:{Address.Port}/"); _http.Start(); StartTime = DateTime.Now; // 每个地址用一个定时器驱动数据变化 _tickTimer = new Timer(Address.DataChangeInterval * 1000); _tickTimer.Elapsed += (s, e) => TickDevices(); _tickTimer.Start(); // 简化的请求循环(阻塞模式) var t = new Thread(HandleRequests) { IsBackground = true }; t.Start(); IsRunning = true; } public void Stop() { if (!IsRunning) return; _tickTimer?.Stop(); _http?.Close(); IsRunning = false; } private void TickDevices() { foreach (var d in Devices) { d.Tick(); } } private void HandleRequests() { while (_http != null && _http.IsListening) { try { var ctx = _http.GetContext(); ProcessContext(ctx); } catch (Exception) { // 忽略单次请求异常,继续监听 } } } private void ProcessContext(HttpListenerContext ctx) { RequestCount++; var req = ctx.Request; var resp = ctx.Response; resp.ContentType = "application/json"; string path = req.Url.AbsolutePath.ToLower(); if (path == "/" || path == "/data") { // 生成当前设备数据集合 var sw = new System.Diagnostics.Stopwatch(); sw.Start(); var list = new List(); foreach (var d in Devices) { var json = _generator.GenerateDevice(d.State); if (json != null) list.Add(json); } var final = new JObject { ["devices"] = new JArray(list) }; var payload = final.ToString(); sw.Stop(); _logRecorder.Record(Devices.Count, string.Join(" ", Devices.Select(v => v.State?.DeviceCode ?? "")), payload, sw.ElapsedMilliseconds); WriteResponse(resp, payload); } else if (path.StartsWith("/admin")) { string html = "

管理界面开发中

"; WriteResponse(resp, html, contentType: "text/html"); } else if (path == "/admin/api/logs") { var logs = _logRecorder.GetRecentLogs(50); var arr = new JArray(); foreach (var l in logs) { arr.Add(new JObject { ["timestamp"] = l.Timestamp.ToString("yyyy-MM-dd HH:mm:ss"), ["deviceCount"] = l.DeviceCount, ["keyData"] = l.KeyData, ["durationMs"] = l.DurationMs }); } var payload = arr.ToString(); WriteResponse(resp, payload); } else { WriteResponse(resp, "{}", contentType: "application/json"); } resp.Close(); } private void WriteResponse(HttpListenerResponse resp, string content, string contentType = "application/json") { var buffer = System.Text.Encoding.UTF8.GetBytes(content); resp.ContentType = contentType; resp.ContentLength64 = buffer.Length; resp.OutputStream.Write(buffer, 0, buffer.Length); } } }