From 87d7dfeee1fb33db4b83d218e1676d57852526a3 Mon Sep 17 00:00:00 2001
From: haoliang <821644@qq.com>
Date: Fri, 8 May 2026 21:12:52 +0800
Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BA=A7=E9=87=8F=E9=87=8D?=
=?UTF-8?q?=E5=BB=BA=E5=B7=A5=E5=85=B7RebuildProduction=EF=BC=9A=E4=BB=8El?=
=?UTF-8?q?og=5Fcollect=5Fraw=E5=8E=9F=E5=A7=8BJSON=E9=87=8D=E6=94=BE?=
=?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=90=8E=E7=9A=84ProductionTracker=E7=AE=97?=
=?UTF-8?q?=E6=B3=95=EF=BC=8C=E4=BF=AE=E6=AD=A32026-05-07=E4=BA=A7?=
=?UTF-8?q?=E9=87=8F=E6=95=B0=E6=8D=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
工具用法: dotnet run --project tools/RebuildProduction
产量修正结果(2026-05-07):
- 总产量: 5,732 → 14,770 (+158%)
- manual_reset段: 0 → 8,740 (50段全部修正)
- program_change段: 830 → 1,128 (76段中偏差的已修正)
- end_of_day段: 4,902 (无变化)
修正后工人产量:
- 郭浩: 4,176 → 11,359
- 李文科: 692 → 1,574
- 公用: 214 → 642
- 冷春英: 211 → 367
- 赵福新: 132 → 253
- 宋志国: 95 → 226
- 赵双,胡东歌: 137 → 205
- 李梦梦,李玉蒙: 75 → 144
- 王涛: 0 (机床实际未运行)
---
tools/RebuildProduction/Program.cs | 243 ++++++++++++++++++
.../RebuildProduction.csproj | 16 ++
2 files changed, 259 insertions(+)
create mode 100644 tools/RebuildProduction/Program.cs
create mode 100644 tools/RebuildProduction/RebuildProduction.csproj
diff --git a/tools/RebuildProduction/Program.cs b/tools/RebuildProduction/Program.cs
new file mode 100644
index 0000000..0cbfeae
--- /dev/null
+++ b/tools/RebuildProduction/Program.cs
@@ -0,0 +1,243 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using Dapper;
+using MySqlConnector;
+using Newtonsoft.Json.Linq;
+
+namespace RebuildProduction
+{
+ ///
+ /// 从 log_collect_raw 原始 JSON 重建 2026-05-07 的产量统计数据。
+ /// 使用修复后的 ProductionTracker 算法重新生成 cnc_production_segment,
+ /// 然后执行日终汇总重新聚合。
+ ///
+ class Program
+ {
+ const string BusinessConn = "Server=localhost;Database=cnc_business;Uid=root;Pwd=root;Charset=utf8mb4;SslMode=None;";
+ const string LogConn = "Server=localhost;Database=cnc_log;Uid=root;Pwd=root;Charset=utf8mb4;SslMode=None;";
+ const string ProgramChange = "program_change";
+ const string ManualReset = "manual_reset";
+ const string EndOfDay = "end_of_day";
+
+ class Observation
+ {
+ public int MachineId;
+ public DateTime Time;
+ public string Program;
+ public decimal? Count;
+ }
+
+ class Segment
+ {
+ public int MachineId;
+ public string Program;
+ public DateTime ProdDate;
+ public DateTime StartTime;
+ public DateTime? EndTime;
+ public decimal StartCount;
+ public decimal? EndCount;
+ public decimal? Quantity;
+ public string Reason;
+ public bool Settled;
+ }
+
+ class State
+ {
+ public string LastProgram;
+ public decimal? LastCount;
+ public Segment ActiveSeg;
+ }
+
+ static void Main()
+ {
+ Console.WriteLine("===== 产量重建 v1.0 =====");
+ Console.WriteLine($"开始: {DateTime.Now:HH:mm:ss}");
+
+ // 1. 加载机床映射
+ Console.Write("[1/5] 加载机床映射... ");
+ var devMap = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ using (var c = new MySqlConnection(BusinessConn))
+ {
+ foreach (var r in c.Query<(int Id, string Dc)>("SELECT id, device_code FROM cnc_machine WHERE is_enabled=1"))
+ devMap[r.Dc] = r.Id;
+ }
+ Console.WriteLine($"{devMap.Count}台");
+
+ // 2. 提取时间线
+ Console.Write("[2/5] 提取时间线... ");
+ var obs = new List();
+ int errs = 0;
+ using (var c = new MySqlConnection(LogConn))
+ {
+ foreach (var r in c.Query<(long Id, DateTime T, string J)>(
+ "SELECT id, request_time, raw_json FROM log_collect_raw WHERE DATE(request_time)='2026-05-07' AND is_success=1 ORDER BY request_time, id"))
+ {
+ try
+ {
+ foreach (var d in JArray.Parse(r.J))
+ {
+ var dev = (JObject)d;
+ var dc = dev["device"]?.ToString();
+ if (dc == null || !devMap.TryGetValue(dc, out int mid)) continue;
+ var tags = dev["tags"] as JArray;
+ if (tags == null) continue;
+
+ string prog = null, cntStr = null;
+ foreach (var t in tags)
+ {
+ var tid = t["id"]?.ToString();
+ if (tid == "Tag5") prog = t["value"]?.ToString();
+ else if (tid == "Tag8") cntStr = t["value"]?.ToString();
+ }
+ decimal? cnt = null;
+ if (!string.IsNullOrEmpty(cntStr) && decimal.TryParse(cntStr, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out var v))
+ cnt = v;
+
+ obs.Add(new Observation { MachineId = mid, Time = r.T, Program = prog ?? "", Count = cnt });
+ }
+ }
+ catch { errs++; }
+ }
+ }
+ Console.WriteLine($"{obs.Count}条 (解析失败{errs})");
+
+ // 3. 重放 ProductionTracker
+ Console.Write("[3/5] 重放算法... ");
+ var segs = new List();
+ var states = new Dictionary();
+
+ foreach (var o in obs.OrderBy(x => x.MachineId).ThenBy(x => x.Time))
+ {
+ if (string.IsNullOrEmpty(o.Program)) continue;
+
+ if (!states.TryGetValue(o.MachineId, out var st))
+ states[o.MachineId] = st = new State();
+
+ bool hasLast = st.LastProgram != null;
+ bool close = false;
+ string reason = "";
+
+ if (hasLast)
+ {
+ if (!string.Equals(st.LastProgram, o.Program, StringComparison.OrdinalIgnoreCase))
+ { close = true; reason = ProgramChange; }
+ else if (o.Count.HasValue && st.LastCount.HasValue && o.Count.Value < st.LastCount.Value)
+ { close = true; reason = ManualReset; }
+ }
+
+ if (close && st.ActiveSeg != null)
+ {
+ var seg = st.ActiveSeg;
+ seg.EndTime = o.Time;
+ seg.EndCount = st.LastCount; // 修复:用结账前最后已知值
+ seg.Quantity = seg.EndCount.HasValue ? Math.Max(0, seg.EndCount.Value - seg.StartCount) : null;
+ seg.Reason = reason;
+ seg.Settled = true;
+ segs.Add(seg);
+ st.ActiveSeg = null;
+ }
+
+ if (st.ActiveSeg == null)
+ {
+ st.ActiveSeg = new Segment
+ {
+ MachineId = o.MachineId, Program = o.Program,
+ ProdDate = o.Time.Date, StartTime = o.Time,
+ StartCount = o.Count ?? 0, Settled = false
+ };
+ }
+
+ if (o.Count.HasValue) st.ActiveSeg.EndCount = o.Count.Value;
+ st.LastProgram = o.Program;
+ st.LastCount = o.Count;
+ }
+
+ // 活跃段 → end_of_day
+ foreach (var st in states.Values)
+ {
+ if (st.ActiveSeg != null)
+ {
+ var seg = st.ActiveSeg;
+ seg.Reason = EndOfDay; seg.Settled = true;
+ if (seg.EndCount.HasValue) seg.Quantity = Math.Max(0, seg.EndCount.Value - seg.StartCount);
+ segs.Add(seg);
+ }
+ }
+
+ var eod = segs.Count(s => s.Reason == EndOfDay);
+ var mr = segs.Count(s => s.Reason == ManualReset);
+ var pc = segs.Count(s => s.Reason == ProgramChange);
+ var total = segs.Sum(s => s.Quantity ?? 0);
+ Console.WriteLine($"{segs.Count}段 (eod={eod}, mr={mr}, pc={pc}) 总产量={total:N0}");
+
+ // 4. 写入数据库
+ Console.Write("[4/5] 写入数据库... ");
+ using (var c = new MySqlConnection(BusinessConn))
+ {
+ c.Open();
+ using var t = c.BeginTransaction();
+ c.Execute("DELETE FROM cnc_production_segment WHERE production_date='2026-05-07'", transaction: t);
+ foreach (var s in segs)
+ {
+ c.Execute(
+ @"INSERT INTO cnc_production_segment (machine_id,program_name,production_date,start_time,end_time,start_part_count,end_part_count,quantity,is_settled,close_reason,created_at,updated_at)
+ VALUES (@a,@b,@c,@d,@e,@f,@g,@h,@i,@j,NOW(),NOW())",
+ new { a = s.MachineId, b = s.Program, c = s.ProdDate.ToString("yyyy-MM-dd"), d = s.StartTime, e = (object)s.EndTime ?? DBNull.Value, f = s.StartCount, g = (object)s.EndCount ?? DBNull.Value, h = (object)s.Quantity ?? DBNull.Value, i = s.Settled ? 1 : 0, j = s.Reason },
+ transaction: t);
+ }
+ t.Commit();
+ }
+ Console.WriteLine("OK");
+
+ // 5. 重新汇总
+ Console.Write("[5/5] 重新汇总... ");
+ using (var c = new MySqlConnection(BusinessConn))
+ {
+ c.Open();
+ using var t = c.BeginTransaction();
+ var dt = new DateTime(2026, 5, 7);
+
+ c.Execute("DELETE FROM cnc_daily_production WHERE production_date=@d", new { d = dt }, t);
+ c.Execute("DELETE FROM cnc_worker_daily_summary WHERE production_date=@d", new { d = dt }, t);
+ c.Execute("DELETE FROM cnc_machine_daily_status WHERE production_date=@d", new { d = dt }, t);
+
+ // 汇总 cnc_daily_production
+ c.Execute(
+ @"INSERT INTO cnc_daily_production (machine_id,production_date,program_name,total_quantity,segment_count,total_run_time,total_cutting_time,total_cycle_time,created_at,updated_at)
+ SELECT machine_id,@d,program_name,SUM(COALESCE(quantity,0)),COUNT(*),NULL,NULL,NULL,NOW(),NOW()
+ FROM cnc_production_segment WHERE DATE(start_time)=@d AND is_settled=1 GROUP BY machine_id,program_name",
+ new { d = dt }, t);
+
+ // 汇总 cnc_worker_daily_summary
+ c.Execute(
+ @"INSERT INTO cnc_worker_daily_summary (worker_id,production_date,total_quantity,machine_count,program_count,created_at,updated_at)
+ SELECT wm.worker_id,@d,SUM(dp.total_quantity),COUNT(DISTINCT dp.machine_id),COUNT(DISTINCT dp.program_name),NOW(),NOW()
+ FROM cnc_daily_production dp INNER JOIN cnc_worker_machine wm ON dp.machine_id=wm.machine_id
+ WHERE dp.production_date=@d GROUP BY wm.worker_id",
+ new { d = dt }, t);
+
+ // 机床日状态
+ c.Execute(
+ @"INSERT INTO cnc_machine_daily_status (machine_id,production_date,data_status,created_at,updated_at)
+ SELECT DISTINCT machine_id,@d,'normal',NOW(),NOW() FROM cnc_collect_record WHERE DATE(collect_time)=@d",
+ new { d = dt }, t);
+ c.Execute(
+ @"INSERT INTO cnc_machine_daily_status (machine_id,production_date,data_status,created_at,updated_at)
+ SELECT m.id,@d,'offline',NOW(),NOW() FROM cnc_machine m WHERE m.is_enabled=1 AND m.id NOT IN
+ (SELECT DISTINCT machine_id FROM cnc_collect_record WHERE DATE(collect_time)=@d)
+ AND m.id NOT IN (SELECT machine_id FROM cnc_machine_daily_status WHERE production_date=@d)",
+ new { d = dt }, t);
+
+ t.Commit();
+ }
+ Console.WriteLine("OK");
+
+ Console.WriteLine();
+ Console.WriteLine($"===== 完成: {DateTime.Now:HH:mm:ss} =====");
+ Console.WriteLine($" 总产量: {total:N0}");
+ Console.WriteLine($" end_of_day: {eod}段, manual_reset: {mr}段, program_change: {pc}段");
+ }
+ }
+}
diff --git a/tools/RebuildProduction/RebuildProduction.csproj b/tools/RebuildProduction/RebuildProduction.csproj
new file mode 100644
index 0000000..6f9d4b1
--- /dev/null
+++ b/tools/RebuildProduction/RebuildProduction.csproj
@@ -0,0 +1,16 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+