|
|
|
|
@ -0,0 +1,226 @@
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Data;
|
|
|
|
|
using MySqlConnector;
|
|
|
|
|
using Dapper;
|
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
|
using CncModels.Entity;
|
|
|
|
|
using CncModels.Dto.CollectLog;
|
|
|
|
|
using CncService.Interface;
|
|
|
|
|
using CncRepository.Interface;
|
|
|
|
|
using CncRepository.Impl.Log;
|
|
|
|
|
using CncRepository.Impl;
|
|
|
|
|
using CncRepository.Base;
|
|
|
|
|
|
|
|
|
|
namespace CncService.Impl
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 回放服务实现
|
|
|
|
|
/// 通过日志库日活日志与业务库表实现回放能力
|
|
|
|
|
/// 注意:此实现尽量复用现有 SQL,避免引入额外依赖
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class ReplayService : IReplayService
|
|
|
|
|
{
|
|
|
|
|
private readonly string _businessConn;
|
|
|
|
|
private readonly string _logConn;
|
|
|
|
|
|
|
|
|
|
public ReplayService(string businessConn, string logConn)
|
|
|
|
|
{
|
|
|
|
|
_businessConn = businessConn ?? throw new ArgumentNullException(nameof(businessConn));
|
|
|
|
|
_logConn = logConn ?? throw new ArgumentNullException(nameof(logConn));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>预览回放影响范围(不做写操作)</summary>
|
|
|
|
|
public ReplayPreview PreviewReplay(DateTime date)
|
|
|
|
|
{
|
|
|
|
|
using (var logConn = new MySqlConnection(_logConn))
|
|
|
|
|
{
|
|
|
|
|
logConn.Open();
|
|
|
|
|
// 原始日志数量
|
|
|
|
|
var rawCount = logConn.ExecuteScalar<int>(@"SELECT COUNT(1) FROM log_collect_raw WHERE DATE(request_time) = @Date AND is_success = 1", new { Date = date.Date });
|
|
|
|
|
// 预计影响的记录/机器/段落数量
|
|
|
|
|
var rebuiltCount = 0; // 预览阶段不写入,返回0
|
|
|
|
|
using (var b = new MySqlConnection(_businessConn))
|
|
|
|
|
{
|
|
|
|
|
b.Open();
|
|
|
|
|
var machineCount = b.ExecuteScalar<int>(@"SELECT COUNT(DISTINCT machine_id) FROM cnc_collect_record WHERE DATE(collect_time) = @Date", new { Date = date.Date });
|
|
|
|
|
var segmentCount = b.ExecuteScalar<int>(@"SELECT COUNT(1) FROM cnc_production_segment WHERE production_date = @Date", new { Date = date.Date });
|
|
|
|
|
var recCount = b.ExecuteScalar<int>(@"SELECT COUNT(1) FROM cnc_collect_record WHERE DATE(collect_time) = @Date", new { Date = date.Date });
|
|
|
|
|
return new ReplayPreview
|
|
|
|
|
{
|
|
|
|
|
Date = date.Date,
|
|
|
|
|
RawLogCount = rawCount,
|
|
|
|
|
AffectedMachineCount = machineCount,
|
|
|
|
|
AffectedRecordCount = recCount,
|
|
|
|
|
AffectedSegmentCount = segmentCount,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>执行回放:清空当天数据并重新写入(简化实现)</summary>
|
|
|
|
|
public ReplayResult ExecuteReplay(DateTime date)
|
|
|
|
|
{
|
|
|
|
|
int clearedRecordCount = 0;
|
|
|
|
|
int clearedSegmentCount = 0;
|
|
|
|
|
int rebuiltRecordCount = 0;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// 1) 读取当天成功的原始日志
|
|
|
|
|
List<CollectRaw> rawLogs;
|
|
|
|
|
using (var logConn = new MySqlConnection(_logConn))
|
|
|
|
|
{
|
|
|
|
|
logConn.Open();
|
|
|
|
|
string sql = @"SELECT * FROM log_collect_raw WHERE DATE(request_time) = @Date AND is_success = 1 ORDER BY request_time ASC";
|
|
|
|
|
rawLogs = logConn.Query<CollectRaw>(sql, new { Date = date.Date }).ToList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2) 业务库清空(按外键依赖的顺序)
|
|
|
|
|
using (var conn = new MySqlConnection(_businessConn))
|
|
|
|
|
{
|
|
|
|
|
conn.Open();
|
|
|
|
|
using (var tran = conn.BeginTransaction())
|
|
|
|
|
{
|
|
|
|
|
// 2.1 清空依赖表
|
|
|
|
|
clearedRecordCount = conn.Execute("DELETE FROM cnc_production_adjustment WHERE DATE(created_at) = @Date", new { Date = date.Date }, tran);
|
|
|
|
|
// 逐表清空,确保单条语句执行兼容性
|
|
|
|
|
int c1 = conn.Execute("DELETE FROM cnc_worker_daily_summary WHERE production_date = @Date", new { Date = date.Date }, tran);
|
|
|
|
|
int c2 = conn.Execute("DELETE FROM cnc_daily_production WHERE production_date = @Date", new { Date = date.Date }, tran);
|
|
|
|
|
int c3 = conn.Execute("DELETE FROM cnc_machine_daily_status WHERE production_date = @Date", new { Date = date.Date }, tran);
|
|
|
|
|
int c4 = conn.Execute("DELETE FROM cnc_production_segment WHERE production_date = @Date", new { Date = date.Date }, tran);
|
|
|
|
|
int c5 = conn.Execute("DELETE FROM cnc_collect_record WHERE DATE(collect_time) = @Date", new { Date = date.Date }, tran);
|
|
|
|
|
clearedSegmentCount = c1;
|
|
|
|
|
tran.Commit();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3) 逐条 raw 日志解析并写入 cnc_collect_record(简化实现)
|
|
|
|
|
using (var conn = new MySqlConnection(_businessConn))
|
|
|
|
|
{
|
|
|
|
|
conn.Open();
|
|
|
|
|
foreach (var raw in rawLogs)
|
|
|
|
|
{
|
|
|
|
|
// 简单 JSON 解析,提取每个 device 的信息并写入 cnc_collect_record
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var devices = JArray.Parse(raw.RawJson);
|
|
|
|
|
foreach (JObject d in devices)
|
|
|
|
|
{
|
|
|
|
|
string deviceCode = d?.Value<string>("device") ?? null;
|
|
|
|
|
if (string.IsNullOrWhiteSpace(deviceCode)) continue;
|
|
|
|
|
// 通过设备代码获取机器ID
|
|
|
|
|
var machineId = conn.QuerySingleOrDefault<int?>("SELECT id FROM cnc_machine WHERE device_code = @Code", new { Code = deviceCode });
|
|
|
|
|
if (machineId == null) continue;
|
|
|
|
|
|
|
|
|
|
// 收集 tag 值
|
|
|
|
|
var tags = d?.Value<JArray>("tags");
|
|
|
|
|
var programName = ExtractTagValue(tags, "Tag5");
|
|
|
|
|
var partCount = ParseDecimal(ExtractTagValue(tags, "Tag8"));
|
|
|
|
|
var runStatus = ExtractTagValue(tags, "Tag9");
|
|
|
|
|
var operateMode = ExtractTagValue(tags, "Tag11");
|
|
|
|
|
var spindleSet = ParseDecimal(ExtractTagValue(tags, "Tag17"));
|
|
|
|
|
var spindleActual = ParseDecimal(ExtractTagValue(tags, "Tag19"));
|
|
|
|
|
var machiningStatus = ExtractTagValue(tags, "Tag26");
|
|
|
|
|
|
|
|
|
|
var collectTime = raw.RequestTime;
|
|
|
|
|
|
|
|
|
|
var rec = new CollectRecord
|
|
|
|
|
{
|
|
|
|
|
MachineId = machineId.Value,
|
|
|
|
|
CollectTime = collectTime,
|
|
|
|
|
ProgramName = programName,
|
|
|
|
|
PartCount = partCount,
|
|
|
|
|
RunStatus = runStatus,
|
|
|
|
|
OperateMode = operateMode,
|
|
|
|
|
SpindleSpeedSet = spindleSet,
|
|
|
|
|
SpindleSpeedActual = spindleActual,
|
|
|
|
|
MachiningStatus = machiningStatus,
|
|
|
|
|
CreatedAt = DateTime.Now
|
|
|
|
|
};
|
|
|
|
|
string insertSql = @"INSERT INTO cnc_collect_record (machine_id, collect_time, program_name, part_count, run_status, operate_mode, spindle_speed_set, spindle_speed_actual, machining_status, created_at) VALUES (@MachineId, @CollectTime, @ProgramName, @PartCount, @RunStatus, @OperateMode, @SpindleSpeedSet, @SpindleSpeedActual, @MachiningStatus, @CreatedAt)";
|
|
|
|
|
conn.Execute(insertSql, new
|
|
|
|
|
{
|
|
|
|
|
MachineId = rec.MachineId,
|
|
|
|
|
CollectTime = rec.CollectTime,
|
|
|
|
|
ProgramName = rec.ProgramName,
|
|
|
|
|
PartCount = rec.PartCount,
|
|
|
|
|
RunStatus = rec.RunStatus,
|
|
|
|
|
OperateMode = rec.OperateMode,
|
|
|
|
|
SpindleSpeedSet = rec.SpindleSpeedSet,
|
|
|
|
|
SpindleSpeedActual = rec.SpindleSpeedActual,
|
|
|
|
|
MachiningStatus = rec.MachiningStatus,
|
|
|
|
|
CreatedAt = rec.CreatedAt
|
|
|
|
|
});
|
|
|
|
|
rebuiltRecordCount++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
// 忽略单条日志的解析错误,继续处理下一条
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4) 重新执行日终汇总(调用同样的 SQL 做聚合)
|
|
|
|
|
using (var conn = new MySqlConnection(_businessConn))
|
|
|
|
|
{
|
|
|
|
|
conn.Open();
|
|
|
|
|
// 以昨天日期执行日终汇总,使用 DailySummaryJob 风格的实现
|
|
|
|
|
// 结账活跃段
|
|
|
|
|
conn.Execute(@"UPDATE cnc_production_segment SET is_settled = 1, close_reason = 'replay' WHERE production_date = @Date AND is_settled = 0", new { Date = date.Date });
|
|
|
|
|
// 产量汇总(简化:重新计算所有段的 quantity)
|
|
|
|
|
conn.Execute(@"UPDATE cnc_production_segment SET quantity = GREATEST(0, COALESCE(end_part_count, 0) - start_part_count) WHERE production_date = @Date", new { Date = date.Date });
|
|
|
|
|
// 汇总日产量(简化版本)
|
|
|
|
|
conn.Execute(@"DELETE FROM cnc_daily_production WHERE production_date = @Date", new { Date = date.Date });
|
|
|
|
|
conn.Execute(@"INSERT INTO cnc_daily_production (machine_id, production_date, program_name, total_quantity, segment_count, created_at, updated_at) SELECT machine_id, production_date, program_name, SUM(quantity), COUNT(*), NOW(), NOW() FROM cnc_production_segment WHERE production_date = @Date GROUP BY machine_id, production_date, program_name", new { Date = date.Date });
|
|
|
|
|
// 更新机床日状态
|
|
|
|
|
conn.Execute(@"DELETE FROM cnc_machine_daily_status WHERE production_date = @Date", new { Date = date.Date });
|
|
|
|
|
conn.Execute(@"INSERT INTO cnc_machine_daily_status (machine_id, production_date, data_status, created_at, updated_at) SELECT machine_id, production_date, 'normal', NOW(), NOW() FROM cnc_daily_production WHERE production_date = @Date GROUP BY machine_id", new { Date = date.Date });
|
|
|
|
|
// 汇总员工日产量(简化)
|
|
|
|
|
conn.Execute(@"DELETE FROM cnc_worker_daily_summary WHERE production_date = @Date", new { Date = date.Date });
|
|
|
|
|
conn.Execute(@"INSERT INTO cnc_worker_daily_summary (worker_id, production_date, total_quantity, machine_count, program_count, created_at, updated_at) SELECT 0, production_date, SUM(total_quantity), COUNT(DISTINCT machine_id), COUNT(DISTINCT program_name), NOW(), NOW() FROM cnc_daily_production WHERE production_date = @Date", new { Date = date.Date });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new ReplayResult
|
|
|
|
|
{
|
|
|
|
|
Date = date.Date,
|
|
|
|
|
ClearedRecordCount = clearedRecordCount,
|
|
|
|
|
ClearedSegmentCount = clearedSegmentCount,
|
|
|
|
|
RebuiltRecordCount = rebuiltRecordCount,
|
|
|
|
|
Success = true
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
return new ReplayResult
|
|
|
|
|
{
|
|
|
|
|
Date = date.Date,
|
|
|
|
|
ClearedRecordCount = 0,
|
|
|
|
|
ClearedSegmentCount = 0,
|
|
|
|
|
RebuiltRecordCount = 0,
|
|
|
|
|
Success = false,
|
|
|
|
|
ErrorMessage = ex.Message
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// helpers
|
|
|
|
|
private static string ExtractTagValue(JArray tags, string id)
|
|
|
|
|
{
|
|
|
|
|
if (tags == null) return null;
|
|
|
|
|
foreach (JObject t in tags)
|
|
|
|
|
{
|
|
|
|
|
if (t?.Value<string>("id") == id) return t?.Value<string>("value");
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static decimal? ParseDecimal(string s)
|
|
|
|
|
{
|
|
|
|
|
if (decimal.TryParse(s, out var d)) return d;
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|