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);
}
}
}