wanshenmean
2026-03-27 dcbd4934d063f471c01cbcf93574c2e2ac5f16b5
feat: 提交WCS与WMS代码调整
已删除6个文件
已修改13个文件
985 ■■■■■ 文件已修改
.gitignore 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/.claude/settings.local.json 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/v18/DocumentLayout.json 270 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Core/LogHelper/QuartzLogger.cs 358 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Core/WIDESEAWCS_Core.csproj 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/QuartzNet/QuartzNetExtension.cs 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/QuartzNet/SchedulerCenterServer.cs 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Connection/RedisConnectionManager.cs 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Program.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/WIDESEAWCS_Server.csproj 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotClientManager.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotMessageHandler.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/v18/DocumentLayout.backup.json 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/v18/DocumentLayout.json 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/.vs/WIDESEAWCS_S7Simulator.slnx/v18/DocumentLayout.backup.json 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/.vs/WIDESEAWCS_S7Simulator.slnx/v18/DocumentLayout.json 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/src/views/RobotClientsView.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
@@ -434,3 +434,8 @@
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/v18/DocumentLayout.json
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/appsettings.json
/Code/WMS/WIDESEA_WMSClient/.omc
# Local IDE and assistant environment files
**/.claude/settings.local.json
**/.vs/**/DocumentLayout.json
**/.vs/**/DocumentLayout.backup.json
Code/WCS/WIDESEAWCS_Server/.claude/settings.local.json
ÎļþÒÑɾ³ý
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/v18/DocumentLayout.json
ÎļþÒÑɾ³ý
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Core/LogHelper/QuartzLogger.cs
@@ -1,85 +1,345 @@
using System;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Numeric;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WIDESEAWCS_Core.Helper;
namespace WIDESEAWCS_Core.LogHelper
{
    public class QuartzLogger
    /// <summary>
    /// æ—¥å¿—等级枚举
    /// </summary>
    public enum LogLevel
    {
        static ReaderWriterLockSlim LogWriteLock = new ReaderWriterLockSlim();
        static string folderPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"Log\\{DateTime.Now.ToString("yyyy-MM-dd")}");
        DEBUG = 0,
        INFO = 1,
        WARN = 2,
        ERROR = 3,
        FATAL = 4
    }
        public static void WriteLogToFile(string fileName, string log)
    /// <summary>
    /// æ—¥å¿—条目:封装单条日志的完整信息
    /// </summary>
    public class LogEntry
    {
        /// <summary>
        /// æ—¥å¿—æ—¶é—´
        /// </summary>
        public DateTime Time { get; set; }
        /// <summary>
        /// æ—¥å¿—等级
        /// </summary>
        public LogLevel Level { get; set; }
        /// <summary>
        /// æ—¥å¿—消息内容
        /// </summary>
        public string Message { get; set; } = string.Empty;
        /// <summary>
        /// æ—¥å¿—来源/文件名
        /// </summary>
        public string? Source { get; set; }
        /// <summary>
        /// å¼‚常信息(可选)
        /// </summary>
        public string? Exception { get; set; }
        /// <summary>
        /// æ ¼å¼åŒ–为标准日志字符串
        /// </summary>
        public string ToFormattedString()
        {
            var sb = new StringBuilder();
            sb.Append($"【{Time:HH:mm:ss}】【{Level}】:【{Message}】");
            if (!string.IsNullOrEmpty(Exception))
            {
                sb.AppendLine();
                sb.Append($"【异常】:{Exception}");
            }
            return sb.ToString();
        }
    }
    /// <summary>
    /// é˜Ÿåˆ—化集中日志写入器
    /// ä½¿ç”¨ç”Ÿäº§è€…-消费者模式,后台线程批量写入文件,提高性能
    /// </summary>
    public class QueuedLogWriter : IDisposable
    {
        private static readonly Lazy<QueuedLogWriter> _instance = new Lazy<QueuedLogWriter>(() => new QueuedLogWriter());
        public static QueuedLogWriter Instance => _instance.Value;
        private readonly BlockingCollection<LogEntry> _logQueue;
        private readonly CancellationTokenSource _cts;
        private readonly Task _writeTask;
        private readonly ReaderWriterLockSlim _fileLock = new ReaderWriterLockSlim();
        private readonly string _logFolder;
        private readonly int _maxFileSize;
        private readonly string _fileExt;
        private bool _disposed;
        /// <summary>
        /// æ—¥å¿—队列写入器单例
        /// </summary>
        private QueuedLogWriter()
        {
            _logFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"Log{Path.DirectorySeparatorChar}{DateTime.Now:yyyy-MM-dd}");
            _maxFileSize = 10 * 1024 * 1024; // 10MB
            _fileExt = ".log";
            _logQueue = new BlockingCollection<LogEntry>(new ConcurrentQueue<LogEntry>());
            _cts = new CancellationTokenSource();
            // ç¡®ä¿æ—¥å¿—目录存在
            if (!Directory.Exists(_logFolder))
            {
                Directory.CreateDirectory(_logFolder);
            }
            // å¯åŠ¨åŽå°å†™å…¥çº¿ç¨‹
            _writeTask = Task.Run(WriteLoop, _cts.Token);
        }
        /// <summary>
        /// åŽå°å†™å…¥å¾ªçޝ
        /// </summary>
        private void WriteLoop()
        {
            var batch = new List<LogEntry>();
            while (!_cts.Token.IsCancellationRequested)
            {
                try
                {
                    // æ‰¹é‡å–出日志,最多100条或等待500ms
                    batch.Clear();
                    if (_logQueue.TryTake(out var entry, 500))
                    {
                        batch.Add(entry);
                        while (batch.Count < 100 && _logQueue.TryTake(out entry, 50))
                        {
                            batch.Add(entry);
                        }
                        WriteBatch(batch);
                    }
                }
                catch (OperationCanceledException)
                {
                    break;
                }
                catch (Exception)
                {
                    // å¿½ç•¥å†™å…¥å¼‚常,防止后台线程崩溃
                }
            }
            // å–消前将剩余日志写出
            while (_logQueue.TryTake(out var remainingEntry, 100))
            {
                batch.Add(remainingEntry);
            }
            if (batch.Count > 0)
            {
                WriteBatch(batch);
            }
        }
        /// <summary>
        /// æ‰¹é‡å†™å…¥æ—¥å¿—到文件
        /// </summary>
        private void WriteBatch(List<LogEntry> entries)
        {
            if (entries.Count == 0) return;
            try
            {
                // è¿›å…¥å†™é”
                LogWriteLock.EnterWriteLock();
                // å¦‚果文件夹不存在,则创建文件夹
                if (!Directory.Exists(folderPath))
                _fileLock.EnterWriteLock();
                foreach (var entry in entries)
                {
                    Directory.CreateDirectory(folderPath);
                    string fileName = GetLogFileName(entry.Source);
                    string filePath = Path.Combine(_logFolder, fileName);
                    string content = entry.ToFormattedString() + Environment.NewLine;
                    ConsoleHelper.WriteInfoLine(content);
                    File.AppendAllText(filePath, content);
                }
                // èŽ·å–æ—¥å¿—æ–‡ä»¶è·¯å¾„
                string logFilePath = Path.Combine(folderPath, GetLastAccessFileName(fileName));
                // èŽ·å–å½“å‰æ—¶é—´
                DateTime now = DateTime.Now;
                // æž„造日志内容
                string logContent = $"【{now}】{Environment.NewLine}{log}";
                // å°†æ—¥å¿—内容追加到日志文件中
                File.AppendAllText(logFilePath, logContent);
            }
            catch { }
            catch (Exception)
            {
                // é™é»˜å¤„理,防止文件IO异常影响主线程
            }
            finally
            {
                // é€€å‡ºå†™é”
                LogWriteLock.ExitWriteLock();
                _fileLock.ExitWriteLock();
            }
        }
        static int size = 10 * 1024 * 1024;
        static string ext = ".log";
        private static string GetLogFilePath(string folderPath, string fileName)
        {
            // èŽ·å–æŒ‡å®šæ–‡ä»¶å¤¹ä¸‹çš„æ‰€æœ‰æ–‡ä»¶
            var allFiles = new DirectoryInfo(folderPath);
            // èŽ·å–ç¬¦åˆæ¡ä»¶çš„æ–‡ä»¶ï¼ŒæŒ‰æ–‡ä»¶åé™åºæŽ’åˆ—
            var selectFiles = allFiles.GetFiles().Where(fi => fi.Name.ToLower().Contains(fileName.ToLower()) && fi.Extension.ToLower() == ext.ToLower() && fi.Length < size).OrderByDescending(d => d.Name).ToList();
            FileInfo? file = selectFiles.FirstOrDefault();
            // å¦‚果有符合条件的文件,返回第一个文件的完整路径
            if (file != null)
        /// <summary>
        /// èŽ·å–æˆ–åˆ›å»ºä¸€ä¸ªæ—¥å¿—æ–‡ä»¶è·¯å¾„ï¼ˆæŒ‰å¤§å°åˆ†æ–‡ä»¶ï¼‰
        /// </summary>
        private string GetLogFileName(string? source)
        {
            string prefix = string.IsNullOrEmpty(source) ? "WCS" : source;
            string searchPattern = $"{prefix}*.log";
            if (!Directory.Exists(_logFolder))
            {
                return file.FullName;
                Directory.CreateDirectory(_logFolder);
            }
            // å¦‚果没有符合条件的文件,返回一个新的文件路径,文件名为原文件名加上当前时间
            return Path.Combine(folderPath, $@"{fileName}_{DateTime.Now.ToString("HH-mm-ss")}.log");
        }
            var files = Directory.GetFiles(_logFolder, searchPattern)
                .Select(f => new FileInfo(f))
                .Where(f => f.Extension.Equals(_fileExt, StringComparison.OrdinalIgnoreCase))
                .OrderByDescending(f => f.Name)
                .ToList();
        private static string GetLastAccessFileName(string fileName)
        {
            foreach (var m in GetExistLogFileNames(fileName))
            // æŸ¥æ‰¾æœ‰ç©ºé—´çš„现有文件
            foreach (var file in files)
            {
                FileInfo fileInfo = new FileInfo(m);
                if (fileInfo.Length < size)
                if (file.Length < _maxFileSize)
                {
                    return m;
                    return file.Name;
                }
            }
            // è¿”回一个新的默认当前时间的日志名称
            return $@"{fileName}_{DateTime.Now.ToString("HH-mm-ss")}.log";
            // åˆ›å»ºæ–°æ–‡ä»¶
            return $"{prefix}_{DateTime.Now:HH-mm-ss}{_fileExt}";
        }
        public static string[] GetExistLogFileNames(string fileName)
        /// <summary>
        /// å†™å…¥æ—¥å¿—(生产端)
        /// </summary>
        public void Enqueue(LogEntry entry)
        {
            string[] fileNames = Directory.GetFiles(folderPath, fileName + "*.log");
            return fileNames;
            if (_disposed) return;
            _logQueue.Add(entry);
        }
        /// <summary>
        /// å†™å…¥æ—¥å¿—的便捷方法
        /// </summary>
        public void Log(LogLevel level, string message, string? source = null, string? exception = null)
        {
            if (_disposed) return;
            var entry = new LogEntry
            {
                Time = DateTime.Now,
                Level = level,
                Message = message,
                Source = source,
                Exception = exception
            };
            _logQueue.Add(entry);
        }
        /// <summary>
        /// å…³é—­æ—¥å¿—写入器
        /// </summary>
        public void Dispose()
        {
            if (_disposed) return;
            _disposed = true;
            _cts.Cancel();
            try
            {
                _writeTask.Wait(3000);
            }
            catch (AggregateException) { }
            _cts.Dispose();
            _logQueue.Dispose();
            _fileLock.Dispose();
        }
    }
    /// <summary>
    /// é™æ€æ—¥å¿—工具类(兼容原有接口)
    /// æä¾›å¤šç§æ ¼å¼åŒ–方法和队列化写入
    /// </summary>
    public class QuartzLogger
    {
        private static readonly QueuedLogWriter _writer = QueuedLogWriter.Instance;
        /// <summary>
        /// å…¼å®¹æ—§æŽ¥å£ï¼šå°†æ—¥å¿—写入文件
        /// </summary>
        /// <param name="fileName">日志文件名(作为Source)</param>
        /// <param name="log">日志内容</param>
        public static void WriteLogToFile(string fileName, string log)
        {
            _writer.Log(LogLevel.INFO, log, fileName);
        }
        /// <summary>
        /// å†™å…¥è°ƒè¯•日志
        /// </summary>
        public static void Debug(string message, string? source = null)
        {
            _writer.Log(LogLevel.DEBUG, message, source);
        }
        /// <summary>
        /// å†™å…¥ä¿¡æ¯æ—¥å¿—
        /// </summary>
        public static void Info(string message, string? source = null)
        {
            _writer.Log(LogLevel.INFO, message, source);
        }
        /// <summary>
        /// å†™å…¥è­¦å‘Šæ—¥å¿—
        /// </summary>
        public static void Warn(string message, string? source = null)
        {
            _writer.Log(LogLevel.WARN, message, source);
        }
        /// <summary>
        /// å†™å…¥é”™è¯¯æ—¥å¿—
        /// </summary>
        public static void Error(string message, string? source = null, Exception? exception = null)
        {
            _writer.Log(LogLevel.ERROR, message, source, exception?.ToString());
        }
        /// <summary>
        /// å†™å…¥è‡´å‘½é”™è¯¯æ—¥å¿—
        /// </summary>
        public static void Fatal(string message, string? source = null, Exception? exception = null)
        {
            _writer.Log(LogLevel.FATAL, message, source, exception?.ToString());
        }
        /// <summary>
        /// ä½¿ç”¨æŒ‡å®šæ ¼å¼å†™å…¥æ—¥å¿—
        /// </summary>
        /// <param name="format">格式化字符串</param>
        /// <param name="args">格式化参数</param>
        public static void WriteFormatted(string format, params object[] args)
        {
            string message = string.Format(format, args);
            _writer.Log(LogLevel.INFO, message);
        }
        /// <summary>
        /// ç›´æŽ¥å†™å…¥ä¸€ä¸ªæ—¥å¿—条目(最灵活)
        /// </summary>
        public static void WriteLogEntry(LogEntry entry)
        {
            _writer.Enqueue(entry);
        }
        /// <summary>
        /// é‡Šæ”¾èµ„源(应用关闭时调用)
        /// </summary>
        public static void Shutdown()
        {
            _writer.Dispose();
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Core/WIDESEAWCS_Core.csproj
@@ -62,6 +62,9 @@
        <PackageReference Include="MiniProfiler.AspNetCore.Mvc" Version="4.3.8" />
        <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
        <PackageReference Include="OfficeOpenXml.Core.ExcelPackage" Version="1.0.0" />
        <PackageReference Include="Serilog" Version="4.3.1" />
        <PackageReference Include="Serilog.AspNetCore" Version="6.0.0" />
        <PackageReference Include="Serilog.Sinks.Seq" Version="9.0.0" />
        <PackageReference Include="SkiaSharp" Version="2.88.8" />
        <PackageReference Include="SqlSugarCore" Version="5.1.4.152" />
        <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/QuartzNet/QuartzNetExtension.cs
@@ -3,6 +3,7 @@
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.Caches;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_QuartzJob.DTO;
using WIDESEAWCS_QuartzJob.Service;
@@ -118,7 +119,7 @@
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("调度服务开启异常" + ex.ToString());
                            QuartzLogger.Error($"调度服务开启异常", "QuartzNetExtension", ex);
                        }
                    }
                    else
@@ -141,13 +142,22 @@
                        _ => targetDevice.Device
                    };
                    WebResponseContent responseContent = await _schedulerCenter.AddScheduleJobAsync(dispatches[i]);
                    if (responseContent.Status) ConsoleHelper.WriteSuccessLine(dispatches[i].Name + "调度服务添加成功"); else ConsoleHelper.WriteErrorLine(dispatches[i].Name + "调度服务添加失败");
                    if (responseContent.Status)
                    {
                        QuartzLogger.Info($"{dispatches[i].Name}调度服务添加成功", "QuartzNetExtension");
                        ConsoleHelper.WriteSuccessLine(dispatches[i].Name + "调度服务添加成功");
                    }
                    else
                    {
                        QuartzLogger.Error($"{dispatches[i].Name}调度服务添加失败", "QuartzNetExtension");
                        ConsoleHelper.WriteErrorLine(dispatches[i].Name + "调度服务添加失败");
                    }
                }
                //await _schedulerCenter.StartScheduleAsync();
            }
            catch (Exception ex)
            {
                Console.WriteLine("调度服务开启异常" + ex.ToString());
                QuartzLogger.Error($"调度服务开启异常", "QuartzNetExtension", ex);
                throw;
            }
        }
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/QuartzNet/SchedulerCenterServer.cs
@@ -31,6 +31,7 @@
using WIDESEAWCS_QuartzJob.DTO;
using WIDESEAWCS_QuartzJob.CustomException;
using Quartz.Impl.Matchers;
using WIDESEAWCS_Core.LogHelper;
namespace WIDESEAWCS_QuartzJob
{
@@ -103,7 +104,7 @@
                {
                    //等待任务运行完成
                    await this._scheduler.Start();
                    await Console.Out.WriteLineAsync(QuartzJobInfoMessage.StartJobSuccess);
                    QuartzLogger.Info(QuartzJobInfoMessage.StartJobSuccess);
                    result = WebResponseContent.Instance.OK(QuartzJobInfoMessage.StartJobSuccess);
                    return result;
                }
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Connection/RedisConnectionManager.cs
@@ -3,6 +3,7 @@
using StackExchange.Redis;
using System.Linq;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_RedisService.Options;
namespace WIDESEAWCS_RedisService.Connection
@@ -59,16 +60,16 @@
                var connection = ConnectionMultiplexer.Connect(configOptions);
                connection.ConnectionFailed += (_, e) =>
                    ConsoleHelper.WriteErrorLine($"Redis连接失败: {e.FailureType}");
                    QuartzLogger.Info($"Redis连接失败: {e.FailureType}");
                connection.ConnectionRestored += (_, e) =>
                    ConsoleHelper.WriteSuccessLine($"Redis连接恢复: {e.EndPoint}");
                    QuartzLogger.Info($"Redis连接恢复: {e.EndPoint}");
                ConsoleHelper.WriteSuccessLine($"Redis连接成功: {string.Join(",", configOptions.EndPoints)}");
                QuartzLogger.Info($"Redis连接成功: {string.Join(",", configOptions.EndPoints)}");
                return connection;
            }
            catch (Exception ex)
            {
                ConsoleHelper.WriteErrorLine($"Redis连接创建失败:{ex.Message}");
                QuartzLogger.Error($"Redis连接创建失败:{ex.Message}",null, ex);
                throw;
            }
        }
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Program.cs
@@ -46,7 +46,7 @@
        .ReadFrom.Configuration(context.Configuration) // ä»Žåº”用程序配置中读取Serilog相关设置(如appsettings.json)
        .ReadFrom.Services(services)  // ä»Žä¾èµ–注入容器中读取服务配置,允许在配置中使用已注册的服务
        .Enrich.FromLogContext()  // å¯ç”¨æ—¥å¿—上下文,可以在日志中包含如请求ID、用户ID等动态属性
        .Enrich.WithProperty("Application", "WCS")
        // è®¾ç½®Microsoft命名空间的日志级别为Information
        // è¿™æ ·å¯ä»¥å‡å°‘Microsoft框架本身的详细日志,避免过多的Debug日志
        .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/WIDESEAWCS_Server.csproj
@@ -65,11 +65,6 @@
    </ItemGroup>
    <ItemGroup>
        <PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
        <PackageReference Include="Serilog.Settings.Configuration" Version="8.0.4" />
        <PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
        <PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
        <PackageReference Include="Serilog.Sinks.Seq" Version="9.0.0" />
        <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
    </ItemGroup>
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotClientManager.cs
@@ -102,7 +102,7 @@
                // ç»‘定客户端断开连接的事件处理
                _tcpSocket.RobotReceived += OnRobotReceived;
                // è®°å½•日志(注意:日志内容为"客户端已断开连接",可能是遗留的占位文本)
                QuartzLogger.Error($"客户端已断开连接", robotCrane.DeviceName);
                QuartzLogger.Warn($"客户端已断开连接", robotCrane.DeviceName);
            }
            // ä»Ž TCP æœåŠ¡å™¨çš„å®¢æˆ·ç«¯å­—å…¸ä¸­èŽ·å– TcpClient å¯¹è±¡
@@ -124,7 +124,7 @@
            if (!alreadyStarted)
            {
                // è®°å½•日志
                QuartzLogger.Error($"启动客户端消息处理", robotCrane.DeviceName);
                QuartzLogger.Info($"启动客户端消息处理", robotCrane.DeviceName);
                // èŽ·å–æœ€æ–°çš„çŠ¶æ€å¯¹è±¡
                var latestStateForSubscribe = _stateManager.GetState(ipAddress);
@@ -142,7 +142,7 @@
                            if (t.IsFaulted)
                            {
                                // è®°å½•错误日志
                                QuartzLogger.Error($"监听客户端消息事件异常", robotCrane.DeviceName);
                                QuartzLogger.Info($"监听客户端消息事件异常", robotCrane.DeviceName);
                                Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] HandleClientAsync error: {t.Exception?.GetBaseException().Message}");
                                // å‘生错误时,移除启动标志,允许下次重试
                                _handleClientStarted.TryRemove(ipAddress, out _);
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotMessageHandler.cs
@@ -117,7 +117,7 @@
        {
            // è®°å½•接收到的消息日志
            _logger.LogInformation($"接收到客户端【{state.RobotCrane.DeviceName}】发送消息【{message}】");
            QuartzLogger.Error($"接收到客户端消息【{message}】", state.RobotCrane.DeviceName);
            QuartzLogger.Info($"接收到客户端消息【{message}】", state.RobotCrane.DeviceName);
            // æž„建缓存键,检查 Redis ä¸­æ˜¯å¦å­˜åœ¨è¯¥è®¾å¤‡çš„状态
            var cacheKey = $"{RedisPrefix.Code}:{RedisName.SocketDevices}:{client.Client.RemoteEndPoint}";
@@ -141,7 +141,7 @@
                // å¤„理成功后,将原消息回写到客户端(保持原有行为)
                await _socketClientGateway.SendMessageAsync(client, message);
                _logger.LogInformation($"发送消息【{message}】");
                QuartzLogger.Error($"发送消息:【{message}】", state.RobotCrane.DeviceName);
                QuartzLogger.Info($"发送消息:【{message}】", state.RobotCrane.DeviceName);
                // å®‰å…¨æ›´æ–°çŠ¶æ€åˆ° Redis
                _stateManager.TryUpdateStateSafely(activeState.IPAddress, activeState);
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs
@@ -156,7 +156,7 @@
            if (result)
            {
                // å‘送成功,记录日志
                QuartzLogger.Error($"下发放货指令,指�?: {taskString}", task.RobotRoadway);
                QuartzLogger.Info($"下发放货指令,指�?: {taskString}", task.RobotRoadway);
                // æ›´æ–°ä»»åŠ¡çŠ¶æ€ä¸º"机器人执行中"
                task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode();
@@ -232,7 +232,7 @@
                    stateForUpdate.CellBarcode.Add(trayBarcode2);
                    // è®°å½•日志
                    QuartzLogger.Error($"ȡ�������о�ţ���о: {trayBarcode1}+{trayBarcode2}", stateForUpdate.RobotCrane.DeviceName);
                    QuartzLogger.Info($"ȡ�������о�ţ���о: {trayBarcode1}+{trayBarcode2}", stateForUpdate.RobotCrane.DeviceName);
                    // å‘送取货指令
                    await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate);
Code/WMS/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/v18/DocumentLayout.backup.json
ÎļþÒÑɾ³ý
Code/WMS/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/v18/DocumentLayout.json
ÎļþÒÑɾ³ý
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
@@ -381,7 +381,7 @@
                };
                var taskDtos = task.Adapt<WMSTaskDTO>();
                var addResult = await BaseDal.AddDataAsync(task);
                var addResult = await BaseDal.AddDataAsync(task) > 0;
                if (!addResult)
                    return WebResponseContent.Instance.Error("任务创建失败");
                return WebResponseContent.Instance.OK("任务创建成功", taskDtos);
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/.vs/WIDESEAWCS_S7Simulator.slnx/v18/DocumentLayout.backup.json
ÎļþÒÑɾ³ý
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/.vs/WIDESEAWCS_S7Simulator.slnx/v18/DocumentLayout.json
ÎļþÒÑɾ³ý
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/src/views/RobotClientsView.vue
@@ -203,7 +203,7 @@
  serverId: 'robot-client-1',
  listenIp: '127.0.0.1',
  listenPort: 2000,
  localPort: 2001
  localPort: 62212
})
const status = ref<RobotClientStatusResponse | null>(null)