using System; using System.IO; using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace IvfTl.ControlHost { /// /// control 进程内的本地 HTTP 小服务,只监听 127.0.0.1:port。 /// 阶段1:/ping、/status。阶段2:/status 补全(rich) + /serial/pause|resume(借串口) + /shutdown(受护栏停止)。 /// public class ControlHttpServer { private readonly int _port; private readonly Func _pingProvider; // /ping 轻量存活 private readonly Func _statusProvider; // /status 完整快照(阶段2 §6 三块) private readonly Func _shutdownHandler; // /shutdown(token 校验后安全停机) private readonly Func _serialPauseHandler; // /serial/pause(借串口:control 让路该舱) private readonly Func _serialResumeHandler; // /serial/resume(归还:恢复采集) private readonly Action _log; private HttpListener _listener; private CancellationTokenSource _cts; public ControlHttpServer( int port, Func pingProvider, Func statusProvider, Func shutdownHandler, Func serialPauseHandler, Func serialResumeHandler, Action log) { _port = port; _pingProvider = pingProvider; _statusProvider = statusProvider; _shutdownHandler = shutdownHandler; _serialPauseHandler = serialPauseHandler; _serialResumeHandler = serialResumeHandler; _log = log ?? (_ => { }); } public void Start() { _listener = new HttpListener(); // 仅本机回环,拒绝外部访问(防外部调停机/借串口)。 _listener.Prefixes.Add($"http://127.0.0.1:{_port}/"); _listener.Start(); _cts = new CancellationTokenSource(); _log($"ControlHttpServer 监听 http://127.0.0.1:{_port}/"); Task.Run(() => Loop(_cts.Token)); } private async Task Loop(CancellationToken token) { while (!token.IsCancellationRequested) { HttpListenerContext ctx; try { ctx = await _listener.GetContextAsync(); } catch (Exception ex) { if (!token.IsCancellationRequested) _log("HttpListener 异常:" + ex.Message); break; } try { Handle(ctx); } catch (Exception ex) { _log("处理请求异常:" + ex.Message); } } } private void Handle(HttpListenerContext ctx) { string path = ctx.Request.Url.AbsolutePath.TrimEnd('/').ToLowerInvariant(); string method = ctx.Request.HttpMethod.ToUpperInvariant(); string body; int code = 200; switch (path) { case "/ping": body = JsonConvert.SerializeObject(_pingProvider()); break; case "/status": body = JsonConvert.SerializeObject(_statusProvider()); break; case "/shutdown": if (method != "POST") { code = 405; body = Err("method not allowed"); break; } { string token = ReadField(ctx, "token"); bool ok = _shutdownHandler != null && _shutdownHandler(token ?? ""); code = ok ? 200 : 403; body = "{\"ok\":" + (ok ? "true" : "false") + (ok ? "" : ",\"error\":\"token invalid\"") + "}"; } break; case "/serial/pause": case "/serial/resume": if (method != "POST") { code = 405; body = Err("method not allowed"); break; } { int houseSn = ReadIntField(ctx, "houseSn"); bool isPause = path == "/serial/pause"; var handler = isPause ? _serialPauseHandler : _serialResumeHandler; bool ok = handler != null && houseSn > 0 && handler(houseSn); code = ok ? 200 : 400; body = "{\"ok\":" + (ok ? "true" : "false") + ",\"houseSn\":" + houseSn + (ok ? "" : ",\"error\":\"bad houseSn or handler\"") + "}"; } break; default: code = 404; body = Err("not found"); break; } byte[] buf = Encoding.UTF8.GetBytes(body); ctx.Response.StatusCode = code; ctx.Response.ContentType = "application/json"; ctx.Response.ContentLength64 = buf.Length; ctx.Response.OutputStream.Write(buf, 0, buf.Length); ctx.Response.OutputStream.Close(); } private static string Err(string msg) => "{\"ok\":false,\"error\":\"" + msg + "\"}"; /// 读 POST JSON body 的某字符串字段(失败返回 null)。 private string ReadField(HttpListenerContext ctx, string field) { try { using (var sr = new StreamReader(ctx.Request.InputStream, ctx.Request.ContentEncoding ?? Encoding.UTF8)) { string raw = sr.ReadToEnd(); if (string.IsNullOrEmpty(raw)) return null; var jo = JObject.Parse(raw); return jo[field]?.ToString(); } } catch (Exception ex) { _log("解析请求体异常:" + ex.Message); return null; } } private int ReadIntField(HttpListenerContext ctx, string field) { string s = ReadField(ctx, field); return int.TryParse(s, out int v) ? v : -1; } public void Stop() { try { _cts?.Cancel(); _listener?.Stop(); _listener?.Close(); } catch (Exception ex) { _log("ControlHttpServer 停止异常:" + ex.Message); } } } }