From 58376d519aeb76ef78d38b737c0c57f8d982fb52 Mon Sep 17 00:00:00 2001
From: wanshenmean <cathay_xy@163.com>
Date: 星期五, 13 三月 2026 14:29:01 +0800
Subject: [PATCH] feat: 完成S7 PLC模拟器完整实现

---
 Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Create.cshtml                            |  160 ++
 Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Core/Interfaces/ISimulatorInstanceManager.cs       |  133 ++
 Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Core/Manager/SimulatorInstanceManager.cs           |  474 +++++++
 Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/wwwroot/css/site.css                           |  128 +
 Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Controllers/SimulatorInstancesController.cs |  375 ++++++
 Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Details.cshtml.cs                        |   61 +
 Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Edit.cshtml.cs                           |  182 +++
 Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Controllers/MemoryController.cs             |  219 +++
 Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/wwwroot/js/site.js                             |  190 +++
 Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Index.cshtml                             |  224 +++
 Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Shared/_Layout.cshtml                    |   51 
 Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Core/Interfaces/IS7ServerInstance.cs               |   66 +
 Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Core/Server/S7ServerInstance.cs                    |  444 +++++++
 Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Controllers/ClientsController.cs            |  110 +
 Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Create.cshtml.cs                         |  141 ++
 Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Index.cshtml.cs                          |    8 
 Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Details.cshtml                           |  338 +++++
 Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/_ViewImports.cshtml                      |    2 
 Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Edit.cshtml                              |  245 ++++
 Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Shared/_ValidationScriptsPartial.cshtml  |   16 
 20 files changed, 3,528 insertions(+), 39 deletions(-)

diff --git a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Core/Interfaces/IS7ServerInstance.cs b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Core/Interfaces/IS7ServerInstance.cs
new file mode 100644
index 0000000..8cf9681
--- /dev/null
+++ b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Core/Interfaces/IS7ServerInstance.cs
@@ -0,0 +1,66 @@
+using WIDESEAWCS_S7Simulator.Core.Entities;
+
+namespace WIDESEAWCS_S7Simulator.Core.Interfaces
+{
+    /// <summary>
+    /// S7鏈嶅姟鍣ㄥ疄渚嬫帴鍙�
+    /// 绠$悊鍗曚釜S7 PLC浠跨湡瀹炰緥鐨勭敓鍛藉懆鏈�
+    /// </summary>
+    public interface IS7ServerInstance : IDisposable
+    {
+        /// <summary>
+        /// 瀹炰緥閰嶇疆
+        /// </summary>
+        InstanceConfig Config { get; }
+
+        /// <summary>
+        /// 瀹炰緥鐘舵��
+        /// </summary>
+        InstanceState State { get; }
+
+        /// <summary>
+        /// 鍐呭瓨瀛樺偍
+        /// </summary>
+        IMemoryStore MemoryStore { get; }
+
+        /// <summary>
+        /// 鍚姩S7鏈嶅姟鍣�
+        /// </summary>
+        /// <returns>鍚姩鏄惁鎴愬姛</returns>
+        bool Start();
+
+        /// <summary>
+        /// 鍋滄S7鏈嶅姟鍣�
+        /// </summary>
+        void Stop();
+
+        /// <summary>
+        /// 閲嶅惎S7鏈嶅姟鍣�
+        /// </summary>
+        /// <returns>閲嶅惎鏄惁鎴愬姛</returns>
+        bool Restart();
+
+        /// <summary>
+        /// 鑾峰彇瀹炰緥鐘舵�佸揩鐓�
+        /// </summary>
+        /// <returns>鐘舵�佸揩鐓�</returns>
+        InstanceState GetState();
+
+        /// <summary>
+        /// 娓呯┖鎵�鏈夊唴瀛�
+        /// </summary>
+        void ClearMemory();
+
+        /// <summary>
+        /// 瀵煎嚭鍐呭瓨鏁版嵁
+        /// </summary>
+        /// <returns>鍐呭瓨鏁版嵁</returns>
+        Dictionary<string, byte[]> ExportMemory();
+
+        /// <summary>
+        /// 瀵煎叆鍐呭瓨鏁版嵁
+        /// </summary>
+        /// <param name="data">鍐呭瓨鏁版嵁</param>
+        void ImportMemory(Dictionary<string, byte[]> data);
+    }
+}
diff --git a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Core/Interfaces/ISimulatorInstanceManager.cs b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Core/Interfaces/ISimulatorInstanceManager.cs
new file mode 100644
index 0000000..db97b5c
--- /dev/null
+++ b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Core/Interfaces/ISimulatorInstanceManager.cs
@@ -0,0 +1,133 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using WIDESEAWCS_S7Simulator.Core.Entities;
+using WIDESEAWCS_S7Simulator.Core.Enums;
+
+namespace WIDESEAWCS_S7Simulator.Core.Interfaces
+{
+    /// <summary>
+    /// 浠跨湡鍣ㄥ疄渚嬬鐞嗗櫒鎺ュ彛
+    /// 绠$悊澶氫釜S7鏈嶅姟鍣ㄥ疄渚嬬殑鐢熷懡鍛ㄦ湡
+    /// </summary>
+    public interface ISimulatorInstanceManager
+    {
+        /// <summary>
+        /// 瀹炰緥鐘舵�佸彉鍖栦簨浠�
+        /// </summary>
+        event EventHandler<InstanceStateChangedEventArgs>? InstanceStateChanged;
+
+        /// <summary>
+        /// 鑾峰彇鎵�鏈夊疄渚�
+        /// </summary>
+        /// <returns>瀹炰緥鍒楄〃</returns>
+        IReadOnlyList<IS7ServerInstance> GetAllInstances();
+
+        /// <summary>
+        /// 鏍规嵁ID鑾峰彇瀹炰緥
+        /// </summary>
+        /// <param name="instanceId">瀹炰緥ID</param>
+        /// <returns>瀹炰緥锛屼笉瀛樺湪鍒欒繑鍥瀗ull</returns>
+        IS7ServerInstance? GetInstance(string instanceId);
+
+        /// <summary>
+        /// 妫�鏌ュ疄渚嬫槸鍚﹀瓨鍦�
+        /// </summary>
+        /// <param name="instanceId">瀹炰緥ID</param>
+        /// <returns>鏄惁瀛樺湪</returns>
+        bool InstanceExists(string instanceId);
+
+        /// <summary>
+        /// 鍒涘缓鏂板疄渚�
+        /// </summary>
+        /// <param name="config">瀹炰緥閰嶇疆</param>
+        /// <returns>鍒涘缓鐨勫疄渚�</returns>
+        Task<IS7ServerInstance> CreateInstanceAsync(InstanceConfig config);
+
+        /// <summary>
+        /// 鍚姩瀹炰緥
+        /// </summary>
+        /// <param name="instanceId">瀹炰緥ID</param>
+        /// <returns>鏄惁鎴愬姛</returns>
+        Task<bool> StartInstanceAsync(string instanceId);
+
+        /// <summary>
+        /// 鍋滄瀹炰緥
+        /// </summary>
+        /// <param name="instanceId">瀹炰緥ID</param>
+        Task StopInstanceAsync(string instanceId);
+
+        /// <summary>
+        /// 閲嶅惎瀹炰緥
+        /// </summary>
+        /// <param name="instanceId">瀹炰緥ID</param>
+        /// <returns>鏄惁鎴愬姛</returns>
+        Task<bool> RestartInstanceAsync(string instanceId);
+
+        /// <summary>
+        /// 鍒犻櫎瀹炰緥
+        /// </summary>
+        /// <param name="instanceId">瀹炰緥ID</param>
+        /// <param name="deleteConfig">鏄惁鍒犻櫎閰嶇疆鏂囦欢</param>
+        Task DeleteInstanceAsync(string instanceId, bool deleteConfig = true);
+
+        /// <summary>
+        /// 鑾峰彇瀹炰緥鐘舵��
+        /// </summary>
+        /// <param name="instanceId">瀹炰緥ID</param>
+        /// <returns>瀹炰緥鐘舵�侊紝涓嶅瓨鍦ㄥ垯杩斿洖null</returns>
+        InstanceState? GetInstanceState(string instanceId);
+
+        /// <summary>
+        /// 鑾峰彇鎵�鏈夊疄渚嬬姸鎬�
+        /// </summary>
+        /// <returns>瀹炰緥鐘舵�佸垪琛�</returns>
+        IReadOnlyList<InstanceState> GetAllInstanceStates();
+
+        /// <summary>
+        /// 鍔犺浇鎵�鏈夊凡淇濆瓨鐨勫疄渚�
+        /// </summary>
+        /// <param name="autoStart">鏄惁鑷姩鍚姩鏍囪涓篈utoStart鐨勫疄渚�</param>
+        Task LoadSavedInstancesAsync(bool autoStart = true);
+
+        /// <summary>
+        /// 鍋滄鎵�鏈夊疄渚�
+        /// </summary>
+        Task StopAllInstancesAsync();
+
+        /// <summary>
+        /// 鑾峰彇杩愯涓殑瀹炰緥鏁伴噺
+        /// </summary>
+        int GetRunningInstanceCount();
+
+        /// <summary>
+        /// 鑾峰彇瀹炰緥鎬绘暟
+        /// </summary>
+        int GetTotalInstanceCount();
+    }
+
+    /// <summary>
+    /// 瀹炰緥鐘舵�佸彉鍖栦簨浠跺弬鏁�
+    /// </summary>
+    public class InstanceStateChangedEventArgs : EventArgs
+    {
+        /// <summary>
+        /// 瀹炰緥ID
+        /// </summary>
+        public string InstanceId { get; set; } = string.Empty;
+
+        /// <summary>
+        /// 鏃х姸鎬�
+        /// </summary>
+        public InstanceStatus OldStatus { get; set; }
+
+        /// <summary>
+        /// 鏂扮姸鎬�
+        /// </summary>
+        public InstanceStatus NewStatus { get; set; }
+
+        /// <summary>
+        /// 瀹炰緥鐘舵��
+        /// </summary>
+        public InstanceState InstanceState { get; set; } = new();
+    }
+}
diff --git a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Core/Manager/SimulatorInstanceManager.cs b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Core/Manager/SimulatorInstanceManager.cs
new file mode 100644
index 0000000..93575e4
--- /dev/null
+++ b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Core/Manager/SimulatorInstanceManager.cs
@@ -0,0 +1,474 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using WIDESEAWCS_S7Simulator.Core.Entities;
+using WIDESEAWCS_S7Simulator.Core.Enums;
+using WIDESEAWCS_S7Simulator.Core.Interfaces;
+using WIDESEAWCS_S7Simulator.Core.Persistence;
+using WIDESEAWCS_S7Simulator.Core.Server;
+
+namespace WIDESEAWCS_S7Simulator.Core.Manager
+{
+    /// <summary>
+    /// 浠跨湡鍣ㄥ疄渚嬬鐞嗗櫒瀹炵幇
+    /// 绠$悊澶氫釜S7鏈嶅姟鍣ㄥ疄渚嬬殑鐢熷懡鍛ㄦ湡锛屾彁渚涚嚎绋嬪畨鍏ㄧ殑CRUD鎿嶄綔
+    /// </summary>
+    public class SimulatorInstanceManager : ISimulatorInstanceManager
+    {
+        private readonly ConcurrentDictionary<string, IS7ServerInstance> _instances = new();
+        private readonly IPersistenceService _persistenceService;
+        private readonly ILogger<SimulatorInstanceManager> _logger;
+        private readonly ILoggerFactory _loggerFactory;
+
+        /// <inheritdoc/>
+        public event EventHandler<InstanceStateChangedEventArgs>? InstanceStateChanged;
+
+        /// <summary>
+        /// 鏋勯�犲嚱鏁�
+        /// </summary>
+        /// <param name="persistenceService">鎸佷箙鍖栨湇鍔�</param>
+        /// <param name="logger">鏃ュ織璁板綍鍣�</param>
+        /// <param name="loggerFactory">鏃ュ織宸ュ巶</param>
+        public SimulatorInstanceManager(
+            IPersistenceService persistenceService,
+            ILogger<SimulatorInstanceManager> logger,
+            ILoggerFactory loggerFactory)
+        {
+            _persistenceService = persistenceService ?? throw new ArgumentNullException(nameof(persistenceService));
+            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+            _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
+        }
+
+        /// <inheritdoc/>
+        public IReadOnlyList<IS7ServerInstance> GetAllInstances()
+        {
+            return _instances.Values.ToList().AsReadOnly();
+        }
+
+        /// <inheritdoc/>
+        public IS7ServerInstance? GetInstance(string instanceId)
+        {
+            if (string.IsNullOrWhiteSpace(instanceId))
+            {
+                return null;
+            }
+
+            _instances.TryGetValue(instanceId, out var instance);
+            return instance;
+        }
+
+        /// <inheritdoc/>
+        public bool InstanceExists(string instanceId)
+        {
+            return !string.IsNullOrWhiteSpace(instanceId) && _instances.ContainsKey(instanceId);
+        }
+
+        /// <inheritdoc/>
+        public async Task<IS7ServerInstance> CreateInstanceAsync(InstanceConfig config)
+        {
+            if (config == null)
+            {
+                throw new ArgumentNullException(nameof(config));
+            }
+
+            // 濡傛灉娌℃湁鎻愪緵ID锛岀敓鎴愭柊鐨凣UID
+            if (string.IsNullOrWhiteSpace(config.Id))
+            {
+                config.Id = Guid.NewGuid().ToString("N");
+                _logger.LogDebug("涓哄疄渚嬬敓鎴愭柊ID: {InstanceId}", config.Id);
+            }
+
+            // 妫�鏌D鏄惁宸插瓨鍦�
+            if (_instances.ContainsKey(config.Id))
+            {
+                throw new InvalidOperationException($"瀹炰緥ID {config.Id} 宸插瓨鍦�");
+            }
+
+            try
+            {
+                // 鍒涘缓瀹炰緥
+                var instanceLogger = _loggerFactory.CreateLogger<S7ServerInstance>();
+                var instance = new S7ServerInstance(config, instanceLogger);
+
+                // 娣诲姞鍒板瓧鍏�
+                if (!_instances.TryAdd(config.Id, instance))
+                {
+                    throw new InvalidOperationException($"鏃犳硶灏嗗疄渚� {config.Id} 娣诲姞鍒扮鐞嗗櫒");
+                }
+
+                // 淇濆瓨閰嶇疆
+                await _persistenceService.SaveInstanceConfigAsync(config);
+
+                // 瑙﹀彂鐘舵�佸彉鍖栦簨浠�
+                OnInstanceStateChanged(new InstanceStateChangedEventArgs
+                {
+                    InstanceId = config.Id,
+                    OldStatus = InstanceStatus.Stopped,
+                    NewStatus = InstanceStatus.Stopped,
+                    InstanceState = instance.GetState()
+                });
+
+                _logger.LogInformation("宸插垱寤哄疄渚� {InstanceId} ({InstanceName})", config.Id, config.Name);
+                return instance;
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "鍒涘缓瀹炰緥 {InstanceId} 鏃跺彂鐢熼敊璇�", config.Id);
+                throw;
+            }
+        }
+
+        /// <inheritdoc/>
+        public async Task<bool> StartInstanceAsync(string instanceId)
+        {
+            if (string.IsNullOrWhiteSpace(instanceId))
+            {
+                _logger.LogWarning("灏濊瘯鍚姩瀹炰緥鏃舵彁渚涗簡绌篒D");
+                return false;
+            }
+
+            if (!_instances.TryGetValue(instanceId, out var instance))
+            {
+                _logger.LogWarning("灏濊瘯鍚姩涓嶅瓨鍦ㄧ殑瀹炰緥 {InstanceId}", instanceId);
+                return false;
+            }
+
+            try
+            {
+                var oldState = instance.GetState();
+                var oldStatus = oldState.Status;
+
+                // 鍚姩瀹炰緥
+                var success = instance.Start();
+
+                if (success)
+                {
+                    var newState = instance.GetState();
+
+                    // 瑙﹀彂鐘舵�佸彉鍖栦簨浠�
+                    OnInstanceStateChanged(new InstanceStateChangedEventArgs
+                    {
+                        InstanceId = instanceId,
+                        OldStatus = oldStatus,
+                        NewStatus = newState.Status,
+                        InstanceState = newState
+                    });
+
+                    _logger.LogInformation("瀹炰緥 {InstanceId} 宸插惎鍔�", instanceId);
+                }
+                else
+                {
+                    _logger.LogWarning("瀹炰緥 {InstanceId} 鍚姩澶辫触", instanceId);
+                }
+
+                return success;
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "鍚姩瀹炰緥 {InstanceId} 鏃跺彂鐢熷紓甯�", instanceId);
+                return false;
+            }
+        }
+
+        /// <inheritdoc/>
+        public async Task StopInstanceAsync(string instanceId)
+        {
+            if (string.IsNullOrWhiteSpace(instanceId))
+            {
+                _logger.LogWarning("灏濊瘯鍋滄瀹炰緥鏃舵彁渚涗簡绌篒D");
+                return;
+            }
+
+            if (!_instances.TryGetValue(instanceId, out var instance))
+            {
+                _logger.LogWarning("灏濊瘯鍋滄涓嶅瓨鍦ㄧ殑瀹炰緥 {InstanceId}", instanceId);
+                return;
+            }
+
+            try
+            {
+                var oldState = instance.GetState();
+                var oldStatus = oldState.Status;
+
+                // 鍋滄瀹炰緥
+                instance.Stop();
+
+                // 鍚屾鍐呭瓨鏁版嵁鍒版寔涔呭寲瀛樺偍
+                await _persistenceService.SaveMemoryDataAsync(instanceId, instance.MemoryStore);
+
+                var newState = instance.GetState();
+
+                // 瑙﹀彂鐘舵�佸彉鍖栦簨浠�
+                OnInstanceStateChanged(new InstanceStateChangedEventArgs
+                {
+                    InstanceId = instanceId,
+                    OldStatus = oldStatus,
+                    NewStatus = newState.Status,
+                    InstanceState = newState
+                });
+
+                _logger.LogInformation("瀹炰緥 {InstanceId} 宸插仠姝�", instanceId);
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "鍋滄瀹炰緥 {InstanceId} 鏃跺彂鐢熷紓甯�", instanceId);
+            }
+        }
+
+        /// <inheritdoc/>
+        public async Task<bool> RestartInstanceAsync(string instanceId)
+        {
+            if (string.IsNullOrWhiteSpace(instanceId))
+            {
+                _logger.LogWarning("灏濊瘯閲嶅惎瀹炰緥鏃舵彁渚涗簡绌篒D");
+                return false;
+            }
+
+            if (!_instances.TryGetValue(instanceId, out var instance))
+            {
+                _logger.LogWarning("灏濊瘯閲嶅惎涓嶅瓨鍦ㄧ殑瀹炰緥 {InstanceId}", instanceId);
+                return false;
+            }
+
+            try
+            {
+                var oldState = instance.GetState();
+                var oldStatus = oldState.Status;
+
+                // 閲嶅惎瀹炰緥
+                var success = instance.Restart();
+
+                if (success)
+                {
+                    var newState = instance.GetState();
+
+                    // 瑙﹀彂鐘舵�佸彉鍖栦簨浠�
+                    OnInstanceStateChanged(new InstanceStateChangedEventArgs
+                    {
+                        InstanceId = instanceId,
+                        OldStatus = oldStatus,
+                        NewStatus = newState.Status,
+                        InstanceState = newState
+                    });
+
+                    _logger.LogInformation("瀹炰緥 {InstanceId} 宸查噸鍚�", instanceId);
+                }
+                else
+                {
+                    _logger.LogWarning("瀹炰緥 {InstanceId} 閲嶅惎澶辫触", instanceId);
+                }
+
+                return success;
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "閲嶅惎瀹炰緥 {InstanceId} 鏃跺彂鐢熷紓甯�", instanceId);
+                return false;
+            }
+        }
+
+        /// <inheritdoc/>
+        public async Task DeleteInstanceAsync(string instanceId, bool deleteConfig = true)
+        {
+            if (string.IsNullOrWhiteSpace(instanceId))
+            {
+                _logger.LogWarning("灏濊瘯鍒犻櫎瀹炰緥鏃舵彁渚涗簡绌篒D");
+                return;
+            }
+
+            if (!_instances.TryRemove(instanceId, out var instance))
+            {
+                _logger.LogWarning("灏濊瘯鍒犻櫎涓嶅瓨鍦ㄧ殑瀹炰緥 {InstanceId}", instanceId);
+                return;
+            }
+
+            try
+            {
+                var oldState = instance.GetState();
+
+                // 鍋滄瀹炰緥
+                instance.Stop();
+
+                // 閲婃斁瀹炰緥璧勬簮
+                instance.Dispose();
+
+                // 鍒犻櫎閰嶇疆鏂囦欢
+                if (deleteConfig)
+                {
+                    await _persistenceService.DeleteInstanceConfigAsync(instanceId);
+                }
+
+                // 瑙﹀彂鐘舵�佸彉鍖栦簨浠�
+                OnInstanceStateChanged(new InstanceStateChangedEventArgs
+                {
+                    InstanceId = instanceId,
+                    OldStatus = oldState.Status,
+                    NewStatus = InstanceStatus.Stopped,
+                    InstanceState = oldState
+                });
+
+                _logger.LogInformation("瀹炰緥 {InstanceId} 宸插垹闄�", instanceId);
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "鍒犻櫎瀹炰緥 {InstanceId} 鏃跺彂鐢熷紓甯�", instanceId);
+            }
+        }
+
+        /// <inheritdoc/>
+        public InstanceState? GetInstanceState(string instanceId)
+        {
+            if (string.IsNullOrWhiteSpace(instanceId))
+            {
+                return null;
+            }
+
+            if (!_instances.TryGetValue(instanceId, out var instance))
+            {
+                return null;
+            }
+
+            return instance.GetState();
+        }
+
+        /// <inheritdoc/>
+        public IReadOnlyList<InstanceState> GetAllInstanceStates()
+        {
+            return _instances.Values
+                .Select(i => i.GetState())
+                .ToList()
+                .AsReadOnly();
+        }
+
+        /// <inheritdoc/>
+        public async Task LoadSavedInstancesAsync(bool autoStart = true)
+        {
+            try
+            {
+                _logger.LogInformation("寮�濮嬪姞杞藉凡淇濆瓨鐨勫疄渚嬮厤缃�...");
+
+                // 鍔犺浇鎵�鏈夐厤缃�
+                var configs = await _persistenceService.LoadAllInstanceConfigsAsync();
+
+                if (configs == null || configs.Count == 0)
+                {
+                    _logger.LogInformation("娌℃湁鎵惧埌宸蹭繚瀛樼殑瀹炰緥閰嶇疆");
+                    return;
+                }
+
+                _logger.LogInformation("鎵惧埌 {Count} 涓凡淇濆瓨鐨勫疄渚嬮厤缃�", configs.Count);
+
+                foreach (var config in configs)
+                {
+                    try
+                    {
+                        // 鍒涘缓瀹炰緥
+                        var instanceLogger = _loggerFactory.CreateLogger<S7ServerInstance>();
+                        var instance = new S7ServerInstance(config, instanceLogger);
+
+                        // 娣诲姞鍒板瓧鍏�
+                        if (_instances.TryAdd(config.Id, instance))
+                        {
+                            // 鍔犺浇鍐呭瓨鏁版嵁
+                            try
+                            {
+                                await _persistenceService.LoadMemoryDataAsync(config.Id, instance.MemoryStore);
+                                _logger.LogDebug("宸插姞杞藉疄渚� {InstanceId} 鐨勫唴瀛樻暟鎹�", config.Id);
+                            }
+                            catch (Exception ex)
+                            {
+                                _logger.LogWarning(ex, "鍔犺浇瀹炰緥 {InstanceId} 鐨勫唴瀛樻暟鎹椂鍙戠敓璀﹀憡", config.Id);
+                            }
+
+                            // 濡傛灉閰嶇疆浜嗚嚜鍔ㄥ惎鍔紝鍒欏惎鍔ㄥ疄渚�
+                            if (autoStart && config.AutoStart)
+                            {
+                                _logger.LogInformation("鑷姩鍚姩瀹炰緥 {InstanceId} ({InstanceName})", config.Id, config.Name);
+                                var success = instance.Start();
+                                if (success)
+                                {
+                                    _logger.LogInformation("瀹炰緥 {InstanceId} 鑷姩鍚姩鎴愬姛", config.Id);
+                                }
+                                else
+                                {
+                                    _logger.LogWarning("瀹炰緥 {InstanceId} 鑷姩鍚姩澶辫触", config.Id);
+                                }
+                            }
+
+                            // 瑙﹀彂鐘舵�佸彉鍖栦簨浠�
+                            OnInstanceStateChanged(new InstanceStateChangedEventArgs
+                            {
+                                InstanceId = config.Id,
+                                OldStatus = InstanceStatus.Stopped,
+                                NewStatus = instance.State.Status,
+                                InstanceState = instance.GetState()
+                            });
+
+                            _logger.LogInformation("宸插姞杞藉疄渚� {InstanceId} ({InstanceName})", config.Id, config.Name);
+                        }
+                        else
+                        {
+                            _logger.LogWarning("瀹炰緥 {InstanceId} 宸插瓨鍦紝璺宠繃鍔犺浇", config.Id);
+                        }
+                    }
+                    catch (Exception ex)
+                    {
+                        _logger.LogError(ex, "鍔犺浇瀹炰緥 {InstanceId} 鏃跺彂鐢熼敊璇�", config.Id);
+                    }
+                }
+
+                _logger.LogInformation("瀹炰緥鍔犺浇瀹屾垚锛屽叡鍔犺浇 {Count} 涓疄渚�", _instances.Count);
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "鍔犺浇宸蹭繚瀛樼殑瀹炰緥鏃跺彂鐢熷紓甯�");
+            }
+        }
+
+        /// <inheritdoc/>
+        public async Task StopAllInstancesAsync()
+        {
+            _logger.LogInformation("寮�濮嬪仠姝㈡墍鏈夊疄渚�...");
+
+            var instanceIds = _instances.Keys.ToList();
+
+            foreach (var instanceId in instanceIds)
+            {
+                try
+                {
+                    await StopInstanceAsync(instanceId);
+                }
+                catch (Exception ex)
+                {
+                    _logger.LogError(ex, "鍋滄瀹炰緥 {InstanceId} 鏃跺彂鐢熷紓甯�", instanceId);
+                }
+            }
+
+            _logger.LogInformation("鎵�鏈夊疄渚嬪凡鍋滄");
+        }
+
+        /// <inheritdoc/>
+        public int GetRunningInstanceCount()
+        {
+            return _instances.Values.Count(i => i.State.Status == InstanceStatus.Running);
+        }
+
+        /// <inheritdoc/>
+        public int GetTotalInstanceCount()
+        {
+            return _instances.Count;
+        }
+
+        /// <summary>
+        /// 瑙﹀彂瀹炰緥鐘舵�佸彉鍖栦簨浠�
+        /// </summary>
+        /// <param name="e">浜嬩欢鍙傛暟</param>
+        protected virtual void OnInstanceStateChanged(InstanceStateChangedEventArgs e)
+        {
+            InstanceStateChanged?.Invoke(this, e);
+        }
+    }
+}
diff --git a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Core/Server/S7ServerInstance.cs b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Core/Server/S7ServerInstance.cs
new file mode 100644
index 0000000..f29afa6
--- /dev/null
+++ b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Core/Server/S7ServerInstance.cs
@@ -0,0 +1,444 @@
+using System.Collections.Concurrent;
+using HslCommunication;
+using HslCommunication.Profinet.Siemens;
+using HslCommunication.Reflection;
+using Microsoft.Extensions.Logging;
+using WIDESEAWCS_S7Simulator.Core.Entities;
+using WIDESEAWCS_S7Simulator.Core.Enums;
+using WIDESEAWCS_S7Simulator.Core.Interfaces;
+using WIDESEAWCS_S7Simulator.Core.Memory;
+
+namespace WIDESEAWCS_S7Simulator.Core.Server
+{
+    /// <summary>
+    /// S7鏈嶅姟鍣ㄥ疄渚嬪疄鐜�
+    /// 浣跨敤HSL Communication搴撳疄鐜癝7 PLC浠跨湡鏈嶅姟鍣�
+    /// </summary>
+    public class S7ServerInstance : IS7ServerInstance
+    {
+        private readonly ILogger<S7ServerInstance> _logger;
+        private readonly object _lock = new();
+        private SiemensS7Server? _server;
+        private bool _disposed;
+
+        /// <inheritdoc/>
+        public InstanceConfig Config { get; }
+
+        /// <inheritdoc/>
+        public InstanceState State { get; private set; }
+
+        /// <inheritdoc/>
+        public IMemoryStore MemoryStore { get; }
+
+        /// <summary>
+        /// 瀹㈡埛绔繛鎺ヨ拷韪�
+        /// </summary>
+        private readonly ConcurrentDictionary<string, S7ClientConnection> _clients = new();
+
+        /// <summary>
+        /// 鏋勯�犲嚱鏁�
+        /// </summary>
+        /// <param name="config">瀹炰緥閰嶇疆</param>
+        /// <param name="logger">鏃ュ織璁板綍鍣�</param>
+        public S7ServerInstance(InstanceConfig config, ILogger<S7ServerInstance> logger)
+        {
+            Config = config ?? throw new ArgumentNullException(nameof(config));
+            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+
+            // 鍒濆鍖栧唴瀛樺瓨鍌�
+            MemoryStore = new MemoryStore(config.MemoryConfig);
+
+            // 鍒濆鍖栫姸鎬�
+            State = new InstanceState
+            {
+                InstanceId = config.Id,
+                Status = InstanceStatus.Stopped,
+                ClientCount = 0,
+                TotalRequests = 0
+            };
+
+            _logger.LogInformation("S7鏈嶅姟鍣ㄥ疄渚� {InstanceId} ({InstanceName}) 宸插垱寤猴紝PLC绫诲瀷: {PLCType}, 绔彛: {Port}",
+                config.Id, config.Name, config.PLCType, config.Port);
+        }
+
+        /// <inheritdoc/>
+        public bool Start()
+        {
+            lock (_lock)
+            {
+                if (_disposed)
+                {
+                    _logger.LogError("鏃犳硶鍚姩宸查噴鏀剧殑瀹炰緥 {InstanceId}", Config.Id);
+                    return false;
+                }
+
+                if (State.Status == InstanceStatus.Running)
+                {
+                    _logger.LogWarning("瀹炰緥 {InstanceId} 宸插湪杩愯涓�", Config.Id);
+                    return true;
+                }
+
+                try
+                {
+                    // 鍒涘缓S7鏈嶅姟鍣�
+                    _server = new SiemensS7Server();
+
+                    // 璁剧疆婵�娲荤爜
+                    if (!string.IsNullOrWhiteSpace(Config.ActivationKey))
+                    {
+                        HslCommunication.Authorization.SetAuthorizationCode(Config.ActivationKey);
+                        _logger.LogDebug("宸茶缃縺娲荤爜");
+                    }
+
+                    // 鍒濆鍖朌B鍧楋紙鏍规嵁閰嶇疆锛�
+                    InitializeDbBlocks();
+
+                    // 浠嶮emoryStore鍚屾鍒濆鏁版嵁鍒版湇鍔″櫒
+                    SynchronizeMemoryToServer();
+
+                    // 鍚姩鏈嶅姟鍣�
+                    try
+                    {
+                        _server.ServerStart(Config.Port);
+                    }
+                    catch (Exception ex)
+                    {
+                        _logger.LogError(ex, "鍚姩S7鏈嶅姟鍣ㄥけ璐�");
+                        State.Status = InstanceStatus.Error;
+                        State.ErrorMessage = ex.Message;
+                        return false;
+                    }
+
+                    // 鏇存柊鐘舵��
+                    State.Status = InstanceStatus.Running;
+                    State.StartTime = DateTime.Now;
+                    State.ErrorMessage = null;
+
+                    _logger.LogInformation("S7鏈嶅姟鍣ㄥ疄渚� {InstanceId} 宸叉垚鍔熷惎鍔紝鐩戝惉绔彛: {Port}", Config.Id, Config.Port);
+                    return true;
+                }
+                catch (Exception ex)
+                {
+                    _logger.LogError(ex, "鍚姩S7鏈嶅姟鍣ㄥ疄渚� {InstanceId} 鏃跺彂鐢熷紓甯�", Config.Id);
+                    State.Status = InstanceStatus.Error;
+                    State.ErrorMessage = ex.Message;
+                    return false;
+                }
+            }
+        }
+
+        /// <inheritdoc/>
+        public void Stop()
+        {
+            lock (_lock)
+            {
+                if (_disposed || State.Status == InstanceStatus.Stopped)
+                {
+                    return;
+                }
+
+                try
+                {
+                    if (_server != null)
+                    {
+                        // 鍋滄鍓嶅悓姝ユ湇鍔″櫒鏁版嵁鍒癕emoryStore
+                        SynchronizeServerToMemory();
+
+                        _server.ServerClose();
+                        _server = null;
+                    }
+
+                    // 娓呯┖瀹㈡埛绔繛鎺�
+                    _clients.Clear();
+
+                    // 鏇存柊鐘舵��
+                    State.Status = InstanceStatus.Stopped;
+                    State.ClientCount = 0;
+                    State.StartTime = null;
+
+                    _logger.LogInformation("S7鏈嶅姟鍣ㄥ疄渚� {InstanceId} 宸插仠姝�", Config.Id);
+                }
+                catch (Exception ex)
+                {
+                    _logger.LogError(ex, "鍋滄S7鏈嶅姟鍣ㄥ疄渚� {InstanceId} 鏃跺彂鐢熷紓甯�", Config.Id);
+                    State.Status = InstanceStatus.Error;
+                    State.ErrorMessage = ex.Message;
+                }
+            }
+        }
+
+        /// <inheritdoc/>
+        public bool Restart()
+        {
+            Stop();
+            return Start();
+        }
+
+        /// <inheritdoc/>
+        public InstanceState GetState()
+        {
+            lock (_lock)
+            {
+                // 鏇存柊瀹㈡埛绔繛鎺ユ暟
+                State.ClientCount = _clients.Count;
+                State.Clients = _clients.Values.ToList();
+
+                // 杩斿洖鐘舵�佸壇鏈�
+                return new InstanceState
+                {
+                    InstanceId = State.InstanceId,
+                    Status = State.Status,
+                    ClientCount = State.ClientCount,
+                    TotalRequests = State.TotalRequests,
+                    StartTime = State.StartTime,
+                    LastActivityTime = State.LastActivityTime,
+                    Clients = new List<S7ClientConnection>(State.Clients),
+                    ErrorMessage = State.ErrorMessage
+                };
+            }
+        }
+
+        /// <inheritdoc/>
+        public void ClearMemory()
+        {
+            lock (_lock)
+            {
+                MemoryStore.Clear();
+
+                // 鍚屾椂娓呯┖鏈嶅姟鍣ㄥ唴閮ㄦ暟鎹�
+                if (_server != null && State.Status == InstanceStatus.Running)
+                {
+                    // 娓呯┖鍚勫唴瀛樺尯鍩�
+                    for (int i = 0; i < 100; i++)
+                    {
+                        _server.Write($"M{i}", (byte)0);
+                        _server.Write($"I{i}", (byte)0);
+                        _server.Write($"Q{i}", (byte)0);
+                    }
+
+                    // 娓呯┖DB鍧�
+                    for (ushort db = 1; db <= Config.MemoryConfig.DBBlockCount; db++)
+                    {
+                        for (int i = 0; i < 10; i++)
+                        {
+                            _server.Write($"DB{db}.DBD{i}", (byte)0);
+                        }
+                    }
+                }
+
+                _logger.LogInformation("瀹炰緥 {InstanceId} 鍐呭瓨宸叉竻绌�", Config.Id);
+            }
+        }
+
+        /// <inheritdoc/>
+        public Dictionary<string, byte[]> ExportMemory()
+        {
+            lock (_lock)
+            {
+                // 鍏堝悓姝ユ湇鍔″櫒鏁版嵁鍒癕emoryStore
+                if (_server != null && State.Status == InstanceStatus.Running)
+                {
+                    SynchronizeServerToMemory();
+                }
+                return MemoryStore.Export();
+            }
+        }
+
+        /// <inheritdoc/>
+        public void ImportMemory(Dictionary<string, byte[]> data)
+        {
+            lock (_lock)
+            {
+                MemoryStore.Import(data);
+
+                // 鍚屾鍒版湇鍔″櫒
+                if (_server != null && State.Status == InstanceStatus.Running)
+                {
+                    SynchronizeMemoryToServer();
+                }
+
+                _logger.LogInformation("瀹炰緥 {InstanceId} 鍐呭瓨鏁版嵁宸插鍏�", Config.Id);
+            }
+        }
+
+        /// <summary>
+        /// 鍒濆鍖朌B鍧�
+        /// </summary>
+        private void InitializeDbBlocks()
+        {
+            if (_server == null)
+                return;
+
+            try
+            {
+                // 鏍规嵁閰嶇疆娣诲姞DB鍧�
+                for (ushort i = 1; i <= Config.MemoryConfig.DBBlockCount; i++)
+                {
+                    _server.AddDbBlock(i, Config.MemoryConfig.DBBlockSize);
+                    _logger.LogDebug("宸叉坊鍔燚B鍧�: DB{DbNumber}, 澶у皬: {Size}", i, Config.MemoryConfig.DBBlockSize);
+                }
+            }
+            catch (Exception ex)
+            {
+                _logger.LogWarning(ex, "鍒濆鍖朌B鍧楁椂鍙戠敓璀﹀憡");
+            }
+        }
+
+        /// <summary>
+        /// 浠嶮emoryStore鍚屾鏁版嵁鍒版湇鍔″櫒
+        /// </summary>
+        private void SynchronizeMemoryToServer()
+        {
+            if (_server == null)
+                return;
+
+            try
+            {
+                var data = MemoryStore.Export();
+
+                // 鍚屾M鍖�
+                if (data.ContainsKey("M"))
+                {
+                    var mBytes = data["M"];
+                    for (int i = 0; i < Math.Min(mBytes.Length, Config.MemoryConfig.MRegionSize); i++)
+                    {
+                        _server.Write($"M{i}", mBytes[i]);
+                    }
+                }
+
+                // 鍚屾I鍖�
+                if (data.ContainsKey("I"))
+                {
+                    var iBytes = data["I"];
+                    for (int i = 0; i < Math.Min(iBytes.Length, Config.MemoryConfig.IRegionSize); i++)
+                    {
+                        _server.Write($"I{i}", iBytes[i]);
+                    }
+                }
+
+                // 鍚屾Q鍖�
+                if (data.ContainsKey("Q"))
+                {
+                    var qBytes = data["Q"];
+                    for (int i = 0; i < Math.Min(qBytes.Length, Config.MemoryConfig.QRegionSize); i++)
+                    {
+                        _server.Write($"Q{i}", qBytes[i]);
+                    }
+                }
+
+                // 鍚屾DB鍖�
+                if (data.ContainsKey("DB"))
+                {
+                    var dbBytes = data["DB"];
+                    int offset = 0;
+                    for (ushort db = 1; db <= Config.MemoryConfig.DBBlockCount; db++)
+                    {
+                        int blockSize = Math.Min(Config.MemoryConfig.DBBlockSize, dbBytes.Length - offset);
+                        for (int i = 0; i < blockSize; i++)
+                        {
+                            _server.Write($"DB{db}.DBD{i}", dbBytes[offset + i]);
+                        }
+                        offset += Config.MemoryConfig.DBBlockSize;
+                    }
+                }
+
+                _logger.LogDebug("宸插皢MemoryStore鏁版嵁鍚屾鍒癝7鏈嶅姟鍣�");
+            }
+            catch (Exception ex)
+            {
+                _logger.LogWarning(ex, "鍚屾鏁版嵁鍒版湇鍔″櫒鏃跺彂鐢熻鍛�");
+            }
+        }
+
+        /// <summary>
+        /// 浠庢湇鍔″櫒鍚屾鏁版嵁鍒癕emoryStore
+        /// </summary>
+        private void SynchronizeServerToMemory()
+        {
+            if (_server == null)
+                return;
+
+            try
+            {
+                var data = new Dictionary<string, byte[]>();
+
+                // 璇诲彇M鍖�
+                var mResult = _server.Read("M0", (ushort)Config.MemoryConfig.MRegionSize);
+                if (mResult.IsSuccess)
+                {
+                    data["M"] = mResult.Content;
+                }
+
+                // 璇诲彇I鍖�
+                var iResult = _server.Read("I0", (ushort)Config.MemoryConfig.IRegionSize);
+                if (iResult.IsSuccess)
+                {
+                    data["I"] = iResult.Content;
+                }
+
+                // 璇诲彇Q鍖�
+                var qResult = _server.Read("Q0", (ushort)Config.MemoryConfig.QRegionSize);
+                if (qResult.IsSuccess)
+                {
+                    data["Q"] = qResult.Content;
+                }
+
+                // 璇诲彇DB鍖�
+                var dbBytes = new List<byte>();
+                for (ushort db = 1; db <= Config.MemoryConfig.DBBlockCount; db++)
+                {
+                    var dbResult = _server.Read($"DB{db}.DBD0", (ushort)Config.MemoryConfig.DBBlockSize);
+                    if (dbResult.IsSuccess)
+                    {
+                        dbBytes.AddRange(dbResult.Content);
+                    }
+                }
+                data["DB"] = dbBytes.ToArray();
+
+                // 瀵煎叆鍒癕emoryStore
+                MemoryStore.Import(data);
+
+                _logger.LogDebug("宸插皢S7鏈嶅姟鍣ㄦ暟鎹悓姝ュ埌MemoryStore");
+            }
+            catch (Exception ex)
+            {
+                _logger.LogWarning(ex, "浠庢湇鍔″櫒鍚屾鏁版嵁鏃跺彂鐢熻鍛�");
+            }
+        }
+
+        /// <summary>
+        /// 澧炲姞璇锋眰璁℃暟骞舵洿鏂版椿鍔ㄦ椂闂�
+        /// </summary>
+        private void IncrementRequestCount()
+        {
+            State.TotalRequests++;
+            State.LastActivityTime = DateTime.Now;
+        }
+
+        /// <summary>
+        /// 閲婃斁璧勬簮
+        /// </summary>
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        /// <summary>
+        /// 閲婃斁璧勬簮
+        /// </summary>
+        /// <param name="disposing">鏄惁姝e湪閲婃斁鎵樼璧勬簮</param>
+        protected virtual void Dispose(bool disposing)
+        {
+            if (!_disposed)
+            {
+                if (disposing)
+                {
+                    Stop();
+                    MemoryStore?.Dispose();
+                }
+                _disposed = true;
+            }
+        }
+    }
+}
diff --git a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Controllers/ClientsController.cs b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Controllers/ClientsController.cs
new file mode 100644
index 0000000..5e065d4
--- /dev/null
+++ b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Controllers/ClientsController.cs
@@ -0,0 +1,110 @@
+using Microsoft.AspNetCore.Mvc;
+using WIDESEAWCS_S7Simulator.Core.Entities;
+using WIDESEAWCS_S7Simulator.Core.Interfaces;
+
+namespace WIDESEAWCS_S7Simulator.Server.Controllers
+{
+    /// <summary>
+    /// 瀹㈡埛绔繛鎺ョ鐞嗘帶鍒跺櫒
+    /// </summary>
+    [ApiController]
+    [Route("api/instances/{id}/[controller]")]
+    public class ClientsController : ControllerBase
+    {
+        private readonly ISimulatorInstanceManager _instanceManager;
+        private readonly ILogger<ClientsController> _logger;
+
+        public ClientsController(
+            ISimulatorInstanceManager instanceManager,
+            ILogger<ClientsController> logger)
+        {
+            _instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
+            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+        }
+
+        /// <summary>
+        /// 鑾峰彇杩炴帴鐨勫鎴风鍒楄〃
+        /// </summary>
+        [HttpGet]
+        [ProducesResponseType(typeof(List<S7ClientConnection>), StatusCodes.Status200OK)]
+        [ProducesResponseType(StatusCodes.Status404NotFound)]
+        public ActionResult<List<S7ClientConnection>> GetConnectedClients(string id)
+        {
+            try
+            {
+                if (string.IsNullOrWhiteSpace(id))
+                {
+                    return BadRequest(new { error = "Instance ID is required" });
+                }
+
+                var instance = _instanceManager.GetInstance(id);
+                if (instance == null)
+                {
+                    return NotFound(new { error = $"Instance with ID '{id}' not found" });
+                }
+
+                var state = instance.GetState();
+                return Ok(state.Clients);
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "Failed to get clients for instance {InstanceId}", id);
+                return StatusCode(StatusCodes.Status500InternalServerError, new { error = "Failed to retrieve clients" });
+            }
+        }
+
+        /// <summary>
+        /// 鏂紑鎸囧畾瀹㈡埛绔�
+        /// </summary>
+        [HttpDelete("{clientId}")]
+        [ProducesResponseType(StatusCodes.Status200OK)]
+        [ProducesResponseType(StatusCodes.Status404NotFound)]
+        public ActionResult DisconnectClient(string id, string clientId)
+        {
+            try
+            {
+                if (string.IsNullOrWhiteSpace(id))
+                {
+                    return BadRequest(new { error = "Instance ID is required" });
+                }
+
+                if (string.IsNullOrWhiteSpace(clientId))
+                {
+                    return BadRequest(new { error = "Client ID is required" });
+                }
+
+                var instance = _instanceManager.GetInstance(id);
+                if (instance == null)
+                {
+                    return NotFound(new { error = $"Instance with ID '{id}' not found" });
+                }
+
+                var state = instance.GetState();
+                var client = state.Clients.FirstOrDefault(c => c.ClientId == clientId);
+
+                if (client == null)
+                {
+                    return NotFound(new { error = $"Client with ID '{clientId}' not found" });
+                }
+
+                // Note: The actual disconnection logic would need to be implemented in IS7ServerInstance
+                // For now, we're returning a success response indicating the operation was requested
+                // In a real implementation, you would call instance.DisconnectClient(clientId)
+                _logger.LogInformation("Disconnect request for client {ClientId} on instance {InstanceId}", clientId, id);
+
+                return Ok(new
+                {
+                    message = "Client disconnect requested",
+                    clientId = clientId,
+                    instanceId = id,
+                    note = "Actual disconnection logic should be implemented in IS7ServerInstance"
+                });
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "Failed to disconnect client {ClientId} for instance {InstanceId}", clientId, id);
+                return StatusCode(StatusCodes.Status500InternalServerError, new { error = "Failed to disconnect client" });
+            }
+        }
+    }
+}
diff --git a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Controllers/MemoryController.cs b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Controllers/MemoryController.cs
new file mode 100644
index 0000000..8fe944d
--- /dev/null
+++ b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Controllers/MemoryController.cs
@@ -0,0 +1,219 @@
+using Microsoft.AspNetCore.Mvc;
+using WIDESEAWCS_S7Simulator.Core.Interfaces;
+
+namespace WIDESEAWCS_S7Simulator.Server.Controllers
+{
+    /// <summary>
+    /// 鍐呭瓨鎿嶄綔鎺у埗鍣�
+    /// </summary>
+    [ApiController]
+    [Route("api/instances/{id}/[controller]")]
+    public class MemoryController : ControllerBase
+    {
+        private readonly ISimulatorInstanceManager _instanceManager;
+        private readonly ILogger<MemoryController> _logger;
+
+        public MemoryController(
+            ISimulatorInstanceManager instanceManager,
+            ILogger<MemoryController> logger)
+        {
+            _instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
+            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+        }
+
+        /// <summary>
+        /// 璇诲彇鍐呭瓨鏁版嵁
+        /// </summary>
+        [HttpGet]
+        [ProducesResponseType(typeof(Dictionary<string, byte[]>), StatusCodes.Status200OK)]
+        [ProducesResponseType(StatusCodes.Status404NotFound)]
+        public ActionResult<Dictionary<string, byte[]>> ReadMemory(string id)
+        {
+            try
+            {
+                if (string.IsNullOrWhiteSpace(id))
+                {
+                    return BadRequest(new { error = "Instance ID is required" });
+                }
+
+                var instance = _instanceManager.GetInstance(id);
+                if (instance == null)
+                {
+                    return NotFound(new { error = $"Instance with ID '{id}' not found" });
+                }
+
+                var memoryData = instance.ExportMemory();
+                return Ok(memoryData);
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "Failed to read memory for instance {InstanceId}", id);
+                return StatusCode(StatusCodes.Status500InternalServerError, new { error = "Failed to read memory" });
+            }
+        }
+
+        /// <summary>
+        /// 鍐欏叆鍐呭瓨鏁版嵁
+        /// </summary>
+        [HttpPost]
+        [ProducesResponseType(StatusCodes.Status200OK)]
+        [ProducesResponseType(StatusCodes.Status404NotFound)]
+        [ProducesResponseType(StatusCodes.Status400BadRequest)]
+        public ActionResult WriteMemory(string id, [FromBody] Dictionary<string, byte[]> data)
+        {
+            try
+            {
+                if (string.IsNullOrWhiteSpace(id))
+                {
+                    return BadRequest(new { error = "Instance ID is required" });
+                }
+
+                if (data == null || data.Count == 0)
+                {
+                    return BadRequest(new { error = "Memory data is required" });
+                }
+
+                var instance = _instanceManager.GetInstance(id);
+                if (instance == null)
+                {
+                    return NotFound(new { error = $"Instance with ID '{id}' not found" });
+                }
+
+                instance.ImportMemory(data);
+                return Ok(new { message = "Memory data written successfully" });
+            }
+            catch (ArgumentException ex)
+            {
+                _logger.LogWarning(ex, "Invalid memory data for instance {InstanceId}", id);
+                return BadRequest(new { error = ex.Message });
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "Failed to write memory for instance {InstanceId}", id);
+                return StatusCode(StatusCodes.Status500InternalServerError, new { error = "Failed to write memory" });
+            }
+        }
+
+        /// <summary>
+        /// 娓呯┖鍐呭瓨鏁版嵁
+        /// </summary>
+        [HttpDelete]
+        [ProducesResponseType(StatusCodes.Status200OK)]
+        [ProducesResponseType(StatusCodes.Status404NotFound)]
+        public ActionResult ClearMemory(string id)
+        {
+            try
+            {
+                if (string.IsNullOrWhiteSpace(id))
+                {
+                    return BadRequest(new { error = "Instance ID is required" });
+                }
+
+                var instance = _instanceManager.GetInstance(id);
+                if (instance == null)
+                {
+                    return NotFound(new { error = $"Instance with ID '{id}' not found" });
+                }
+
+                instance.ClearMemory();
+                return Ok(new { message = "Memory cleared successfully" });
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "Failed to clear memory for instance {InstanceId}", id);
+                return StatusCode(StatusCodes.Status500InternalServerError, new { error = "Failed to clear memory" });
+            }
+        }
+
+        /// <summary>
+        /// 淇濆瓨鍐呭瓨蹇収
+        /// </summary>
+        [HttpPost("save")]
+        [ProducesResponseType(StatusCodes.Status200OK)]
+        [ProducesResponseType(StatusCodes.Status404NotFound)]
+        public ActionResult SaveMemorySnapshot(string id, [FromQuery] string? snapshotName = null)
+        {
+            try
+            {
+                if (string.IsNullOrWhiteSpace(id))
+                {
+                    return BadRequest(new { error = "Instance ID is required" });
+                }
+
+                var instance = _instanceManager.GetInstance(id);
+                if (instance == null)
+                {
+                    return NotFound(new { error = $"Instance with ID '{id}' not found" });
+                }
+
+                var memoryData = instance.ExportMemory();
+                var fileName = string.IsNullOrWhiteSpace(snapshotName)
+                    ? $"snapshot_{id}_{DateTime.UtcNow:yyyyMMdd_HHmmss}.json"
+                    : snapshotName;
+
+                // In a real implementation, you would save this to a file system or database
+                // For now, we'll return the data that would be saved
+                return Ok(new
+                {
+                    message = "Memory snapshot captured successfully",
+                    fileName = fileName,
+                    timestamp = DateTime.UtcNow,
+                    dataSize = memoryData.Sum(kvp => kvp.Value.Length),
+                    regions = memoryData.Keys.ToList()
+                });
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "Failed to save memory snapshot for instance {InstanceId}", id);
+                return StatusCode(StatusCodes.Status500InternalServerError, new { error = "Failed to save memory snapshot" });
+            }
+        }
+
+        /// <summary>
+        /// 鍔犺浇鍐呭瓨蹇収
+        /// </summary>
+        [HttpPost("load")]
+        [ProducesResponseType(StatusCodes.Status200OK)]
+        [ProducesResponseType(StatusCodes.Status404NotFound)]
+        [ProducesResponseType(StatusCodes.Status400BadRequest)]
+        public ActionResult LoadMemorySnapshot(string id, [FromBody] Dictionary<string, byte[]> snapshotData)
+        {
+            try
+            {
+                if (string.IsNullOrWhiteSpace(id))
+                {
+                    return BadRequest(new { error = "Instance ID is required" });
+                }
+
+                if (snapshotData == null || snapshotData.Count == 0)
+                {
+                    return BadRequest(new { error = "Snapshot data is required" });
+                }
+
+                var instance = _instanceManager.GetInstance(id);
+                if (instance == null)
+                {
+                    return NotFound(new { error = $"Instance with ID '{id}' not found" });
+                }
+
+                instance.ImportMemory(snapshotData);
+                return Ok(new
+                {
+                    message = "Memory snapshot loaded successfully",
+                    timestamp = DateTime.UtcNow,
+                    regions = snapshotData.Keys.ToList()
+                });
+            }
+            catch (ArgumentException ex)
+            {
+                _logger.LogWarning(ex, "Invalid snapshot data for instance {InstanceId}", id);
+                return BadRequest(new { error = ex.Message });
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "Failed to load memory snapshot for instance {InstanceId}", id);
+                return StatusCode(StatusCodes.Status500InternalServerError, new { error = "Failed to load memory snapshot" });
+            }
+        }
+    }
+}
diff --git a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Controllers/SimulatorInstancesController.cs b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Controllers/SimulatorInstancesController.cs
new file mode 100644
index 0000000..951d0ab
--- /dev/null
+++ b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Controllers/SimulatorInstancesController.cs
@@ -0,0 +1,375 @@
+using Microsoft.AspNetCore.Mvc;
+using WIDESEAWCS_S7Simulator.Core.Entities;
+using WIDESEAWCS_S7Simulator.Core.Interfaces;
+
+namespace WIDESEAWCS_S7Simulator.Server.Controllers
+{
+    /// <summary>
+    /// 浠跨湡鍣ㄥ疄渚嬬鐞嗘帶鍒跺櫒
+    /// </summary>
+    [ApiController]
+    [Route("api/[controller]")]
+    public class SimulatorInstancesController : ControllerBase
+    {
+        private readonly ISimulatorInstanceManager _instanceManager;
+        private readonly ILogger<SimulatorInstancesController> _logger;
+
+        public SimulatorInstancesController(
+            ISimulatorInstanceManager instanceManager,
+            ILogger<SimulatorInstancesController> logger)
+        {
+            _instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
+            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+        }
+
+        /// <summary>
+        /// 鑾峰彇鎵�鏈夊疄渚嬪垪琛�
+        /// </summary>
+        [HttpGet]
+        [ProducesResponseType(typeof(IEnumerable<InstanceState>), StatusCodes.Status200OK)]
+        public ActionResult<IEnumerable<InstanceState>> GetAllInstances()
+        {
+            try
+            {
+                var instances = _instanceManager.GetAllInstances();
+                var states = instances.Select(i => i.GetState()).ToList();
+                return Ok(states);
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "Failed to get all instances");
+                return StatusCode(StatusCodes.Status500InternalServerError, new { error = "Failed to retrieve instances" });
+            }
+        }
+
+        /// <summary>
+        /// 鍒涘缓鏂板疄渚�
+        /// </summary>
+        [HttpPost]
+        [ProducesResponseType(typeof(InstanceState), StatusCodes.Status201Created)]
+        [ProducesResponseType(StatusCodes.Status400BadRequest)]
+        public async Task<ActionResult<InstanceState>> CreateInstance([FromBody] InstanceConfig config)
+        {
+            try
+            {
+                if (config == null)
+                {
+                    return BadRequest(new { error = "Instance configuration is required" });
+                }
+
+                if (string.IsNullOrWhiteSpace(config.Id))
+                {
+                    return BadRequest(new { error = "Instance ID is required" });
+                }
+
+                if (string.IsNullOrWhiteSpace(config.Name))
+                {
+                    return BadRequest(new { error = "Instance name is required" });
+                }
+
+                if (_instanceManager.InstanceExists(config.Id))
+                {
+                    return Conflict(new { error = $"Instance with ID '{config.Id}' already exists" });
+                }
+
+                if (config.Port <= 0 || config.Port > 65535)
+                {
+                    return BadRequest(new { error = "Port must be between 1 and 65535" });
+                }
+
+                var instance = await _instanceManager.CreateInstanceAsync(config);
+                var state = instance.GetState();
+
+                return CreatedAtAction(
+                    nameof(GetInstance),
+                    new { id = config.Id },
+                    state);
+            }
+            catch (ArgumentException ex)
+            {
+                _logger.LogWarning(ex, "Invalid instance configuration");
+                return BadRequest(new { error = ex.Message });
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "Failed to create instance");
+                return StatusCode(StatusCodes.Status500InternalServerError, new { error = "Failed to create instance" });
+            }
+        }
+
+        /// <summary>
+        /// 鑾峰彇鎸囧畾瀹炰緥璇︽儏
+        /// </summary>
+        [HttpGet("{id}")]
+        [ProducesResponseType(typeof(InstanceState), StatusCodes.Status200OK)]
+        [ProducesResponseType(StatusCodes.Status404NotFound)]
+        public ActionResult<InstanceState> GetInstance(string id)
+        {
+            try
+            {
+                if (string.IsNullOrWhiteSpace(id))
+                {
+                    return BadRequest(new { error = "Instance ID is required" });
+                }
+
+                var instance = _instanceManager.GetInstance(id);
+                if (instance == null)
+                {
+                    return NotFound(new { error = $"Instance with ID '{id}' not found" });
+                }
+
+                var state = instance.GetState();
+                return Ok(state);
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "Failed to get instance {InstanceId}", id);
+                return StatusCode(StatusCodes.Status500InternalServerError, new { error = "Failed to retrieve instance" });
+            }
+        }
+
+        /// <summary>
+        /// 鏇存柊瀹炰緥閰嶇疆
+        /// </summary>
+        [HttpPut("{id}")]
+        [ProducesResponseType(typeof(InstanceState), StatusCodes.Status200OK)]
+        [ProducesResponseType(StatusCodes.Status400BadRequest)]
+        [ProducesResponseType(StatusCodes.Status404NotFound)]
+        public async Task<ActionResult<InstanceState>> UpdateInstance(string id, [FromBody] InstanceConfig config)
+        {
+            try
+            {
+                if (string.IsNullOrWhiteSpace(id))
+                {
+                    return BadRequest(new { error = "Instance ID is required" });
+                }
+
+                if (config == null)
+                {
+                    return BadRequest(new { error = "Instance configuration is required" });
+                }
+
+                var instance = _instanceManager.GetInstance(id);
+                if (instance == null)
+                {
+                    return NotFound(new { error = $"Instance with ID '{id}' not found" });
+                }
+
+                // Validate port if changed
+                if (config.Port <= 0 || config.Port > 65535)
+                {
+                    return BadRequest(new { error = "Port must be between 1 and 65535" });
+                }
+
+                // Delete existing instance and recreate with new config
+                // Note: This is a simplified approach. In production, you might want to support hot-reload
+                bool wasRunning = instance.GetState().Status == Core.Enums.InstanceStatus.Running;
+                await _instanceManager.DeleteInstanceAsync(id, deleteConfig: false);
+
+                config.Id = id; // Ensure ID matches route parameter
+                var newInstance = await _instanceManager.CreateInstanceAsync(config);
+
+                if (wasRunning)
+                {
+                    await _instanceManager.StartInstanceAsync(id);
+                }
+
+                var state = newInstance.GetState();
+                return Ok(state);
+            }
+            catch (ArgumentException ex)
+            {
+                _logger.LogWarning(ex, "Invalid instance configuration");
+                return BadRequest(new { error = ex.Message });
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "Failed to update instance {InstanceId}", id);
+                return StatusCode(StatusCodes.Status500InternalServerError, new { error = "Failed to update instance" });
+            }
+        }
+
+        /// <summary>
+        /// 鍒犻櫎瀹炰緥
+        /// </summary>
+        [HttpDelete("{id}")]
+        [ProducesResponseType(StatusCodes.Status204NoContent)]
+        [ProducesResponseType(StatusCodes.Status404NotFound)]
+        public async Task<IActionResult> DeleteInstance(string id, [FromQuery] bool deleteConfig = true)
+        {
+            try
+            {
+                if (string.IsNullOrWhiteSpace(id))
+                {
+                    return BadRequest(new { error = "Instance ID is required" });
+                }
+
+                if (!_instanceManager.InstanceExists(id))
+                {
+                    return NotFound(new { error = $"Instance with ID '{id}' not found" });
+                }
+
+                await _instanceManager.DeleteInstanceAsync(id, deleteConfig);
+                return NoContent();
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "Failed to delete instance {InstanceId}", id);
+                return StatusCode(StatusCodes.Status500InternalServerError, new { error = "Failed to delete instance" });
+            }
+        }
+
+        /// <summary>
+        /// 鍚姩瀹炰緥
+        /// </summary>
+        [HttpPost("{id}/start")]
+        [ProducesResponseType(StatusCodes.Status200OK)]
+        [ProducesResponseType(StatusCodes.Status404NotFound)]
+        [ProducesResponseType(StatusCodes.Status400BadRequest)]
+        public async Task<ActionResult<InstanceState>> StartInstance(string id)
+        {
+            try
+            {
+                if (string.IsNullOrWhiteSpace(id))
+                {
+                    return BadRequest(new { error = "Instance ID is required" });
+                }
+
+                if (!_instanceManager.InstanceExists(id))
+                {
+                    return NotFound(new { error = $"Instance with ID '{id}' not found" });
+                }
+
+                bool success = await _instanceManager.StartInstanceAsync(id);
+                if (!success)
+                {
+                    return BadRequest(new { error = "Failed to start instance" });
+                }
+
+                var instance = _instanceManager.GetInstance(id);
+                var state = instance?.GetState();
+                return Ok(state);
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "Failed to start instance {InstanceId}", id);
+                return StatusCode(StatusCodes.Status500InternalServerError, new { error = "Failed to start instance" });
+            }
+        }
+
+        /// <summary>
+        /// 鍋滄瀹炰緥
+        /// </summary>
+        [HttpPost("{id}/stop")]
+        [ProducesResponseType(StatusCodes.Status200OK)]
+        [ProducesResponseType(StatusCodes.Status404NotFound)]
+        public async Task<ActionResult<InstanceState>> StopInstance(string id)
+        {
+            try
+            {
+                if (string.IsNullOrWhiteSpace(id))
+                {
+                    return BadRequest(new { error = "Instance ID is required" });
+                }
+
+                if (!_instanceManager.InstanceExists(id))
+                {
+                    return NotFound(new { error = $"Instance with ID '{id}' not found" });
+                }
+
+                await _instanceManager.StopInstanceAsync(id);
+
+                var instance = _instanceManager.GetInstance(id);
+                var state = instance?.GetState();
+                return Ok(state);
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "Failed to stop instance {InstanceId}", id);
+                return StatusCode(StatusCodes.Status500InternalServerError, new { error = "Failed to stop instance" });
+            }
+        }
+
+        /// <summary>
+        /// 閲嶅惎瀹炰緥
+        /// </summary>
+        [HttpPost("{id}/restart")]
+        [ProducesResponseType(StatusCodes.Status200OK)]
+        [ProducesResponseType(StatusCodes.Status404NotFound)]
+        [ProducesResponseType(StatusCodes.Status400BadRequest)]
+        public async Task<ActionResult<InstanceState>> RestartInstance(string id)
+        {
+            try
+            {
+                if (string.IsNullOrWhiteSpace(id))
+                {
+                    return BadRequest(new { error = "Instance ID is required" });
+                }
+
+                if (!_instanceManager.InstanceExists(id))
+                {
+                    return NotFound(new { error = $"Instance with ID '{id}' not found" });
+                }
+
+                bool success = await _instanceManager.RestartInstanceAsync(id);
+                if (!success)
+                {
+                    return BadRequest(new { error = "Failed to restart instance" });
+                }
+
+                var instance = _instanceManager.GetInstance(id);
+                var state = instance?.GetState();
+                return Ok(state);
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "Failed to restart instance {InstanceId}", id);
+                return StatusCode(StatusCodes.Status500InternalServerError, new { error = "Failed to restart instance" });
+            }
+        }
+
+        /// <summary>
+        /// 鍚姩鎵�鏈夎嚜鍔ㄥ惎鍔ㄥ疄渚�
+        /// </summary>
+        [HttpPost("start-all")]
+        [ProducesResponseType(StatusCodes.Status200OK)]
+        public async Task<ActionResult> StartAllAutoStartInstances()
+        {
+            try
+            {
+                await _instanceManager.LoadSavedInstancesAsync(autoStart: true);
+                return Ok(new {
+                    message = "Started all auto-start instances",
+                    runningCount = _instanceManager.GetRunningInstanceCount()
+                });
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "Failed to start all instances");
+                return StatusCode(StatusCodes.Status500InternalServerError, new { error = "Failed to start instances" });
+            }
+        }
+
+        /// <summary>
+        /// 鍋滄鎵�鏈夊疄渚�
+        /// </summary>
+        [HttpPost("stop-all")]
+        [ProducesResponseType(StatusCodes.Status200OK)]
+        public async Task<ActionResult> StopAllInstances()
+        {
+            try
+            {
+                await _instanceManager.StopAllInstancesAsync();
+                return Ok(new {
+                    message = "Stopped all instances",
+                    runningCount = _instanceManager.GetRunningInstanceCount()
+                });
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "Failed to stop all instances");
+                return StatusCode(StatusCodes.Status500InternalServerError, new { error = "Failed to stop instances" });
+            }
+        }
+    }
+}
diff --git a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Create.cshtml b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Create.cshtml
new file mode 100644
index 0000000..8dc2f23
--- /dev/null
+++ b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Create.cshtml
@@ -0,0 +1,160 @@
+@page
+@model CreateModel
+@{
+    ViewData["Title"] = "鍒涘缓瀹炰緥";
+}
+
+<div class="row justify-content-center">
+    <div class="col-lg-8">
+        <div class="card shadow">
+            <div class="card-header bg-primary text-white">
+                <h4 class="mb-0">
+                    <i class="bi bi-plus-circle me-2"></i>鍒涘缓鏂板疄渚�
+                </h4>
+            </div>
+            <div class="card-body">
+                <form method="post" id="createForm">
+                    <div asp-validation-summary="ModelOnly" class="alert alert-danger" role="alert"></div>
+
+                    <!-- 鍩烘湰淇℃伅 -->
+                    <h5 class="mb-3">
+                        <i class="bi bi-info-circle me-2"></i>鍩烘湰淇℃伅
+                    </h5>
+
+                    <div class="row mb-3">
+                        <div class="col-md-6">
+                            <label asp-for="Input.Id" class="form-label required">瀹炰緥ID</label>
+                            <input asp-for="Input.Id" class="form-control" placeholder="渚嬪: plc-simulator-1" />
+                            <span asp-validation-for="Input.Id" class="text-danger"></span>
+                            <small class="text-muted">鍞竴鏍囪瘑绗︼紝鍙兘鍖呭惈瀛楁瘝銆佹暟瀛椼�佷笅鍒掔嚎鍜岃繛瀛楃</small>
+                        </div>
+                        <div class="col-md-6">
+                            <label asp-for="Input.Name" class="form-label required">瀹炰緥鍚嶇О</label>
+                            <input asp-for="Input.Name" class="form-control" placeholder="渚嬪: 1鍙稰LC浠跨湡鍣�" />
+                            <span asp-validation-for="Input.Name" class="text-danger"></span>
+                        </div>
+                    </div>
+
+                    <div class="row mb-3">
+                        <div class="col-md-6">
+                            <label asp-for="Input.PLCType" class="form-label required">PLC鍨嬪彿</label>
+                            <select asp-for="Input.PLCType" class="form-select">
+                                <option value="0">S7-200 Smart</option>
+                                <option value="1" selected>S7-1200</option>
+                                <option value="2">S7-1500</option>
+                                <option value="3">S7-300</option>
+                                <option value="4">S7-400</option>
+                            </select>
+                            <span asp-validation-for="Input.PLCType" class="text-danger"></span>
+                        </div>
+                        <div class="col-md-6">
+                            <label asp-for="Input.Port" class="form-label required">鐩戝惉绔彛</label>
+                            <input asp-for="Input.Port" class="form-control" type="number" min="1" max="65535" placeholder="102" />
+                            <span asp-validation-for="Input.Port" class="text-danger"></span>
+                            <small class="text-muted">S7榛樿绔彛涓�102</small>
+                        </div>
+                    </div>
+
+                    <div class="row mb-3">
+                        <div class="col-md-6">
+                            <label asp-for="Input.ActivationKey" class="form-label">HSL婵�娲荤爜</label>
+                            <input asp-for="Input.ActivationKey" class="form-control" placeholder="鍙��" />
+                            <span asp-validation-for="Input.ActivationKey" class="text-danger"></span>
+                            <small class="text-muted">鐢ㄤ簬HSL搴撶殑鍟嗕笟婵�娲伙紝鐣欑┖浣跨敤鍏嶈垂鐗�</small>
+                        </div>
+                        <div class="col-md-6">
+                            <label asp-for="Input.AutoStart" class="form-label">鑷姩鍚姩</label>
+                            <div class="form-check mt-2">
+                                <input asp-for="Input.AutoStart" class="form-check-input" />
+                                <label asp-for="Input.AutoStart" class="form-check-label">
+                                    鏈嶅姟鍣ㄥ惎鍔ㄦ椂鑷姩鍚姩姝ゅ疄渚�
+                                </label>
+                            </div>
+                        </div>
+                    </div>
+
+                    <hr class="my-4">
+
+                    <!-- 鍐呭瓨閰嶇疆 -->
+                    <div class="d-flex justify-content-between align-items-center mb-3">
+                        <h5 class="mb-0">
+                            <i class="bi bi-memory me-2"></i>鍐呭瓨閰嶇疆
+                        </h5>
+                        <button type="button" class="btn btn-outline-secondary btn-sm" onclick="resetMemoryConfig()">
+                            <i class="bi bi-arrow-counterclockwise me-1"></i>閲嶇疆涓洪粯璁ゅ��
+                        </button>
+                    </div>
+
+                    <div class="row mb-3">
+                        <div class="col-md-4">
+                            <label asp-for="Input.MRegionSize" class="form-label">M鍖哄煙澶у皬</label>
+                            <input asp-for="Input.MRegionSize" class="form-control" type="number" min="0" placeholder="1024" />
+                            <span asp-validation-for="Input.MRegionSize" class="text-danger"></span>
+                        </div>
+                        <div class="col-md-4">
+                            <label asp-for="Input.IRegionSize" class="form-label">I鍖哄煙澶у皬</label>
+                            <input asp-for="Input.IRegionSize" class="form-control" type="number" min="0" placeholder="256" />
+                            <span asp-validation-for="Input.IRegionSize" class="text-danger"></span>
+                        </div>
+                        <div class="col-md-4">
+                            <label asp-for="Input.QRegionSize" class="form-label">Q鍖哄煙澶у皬</label>
+                            <input asp-for="Input.QRegionSize" class="form-control" type="number" min="0" placeholder="256" />
+                            <span asp-validation-for="Input.QRegionSize" class="text-danger"></span>
+                        </div>
+                    </div>
+
+                    <div class="row mb-3">
+                        <div class="col-md-4">
+                            <label asp-for="Input.DBBlockCount" class="form-label">DB鍧楁暟閲�</label>
+                            <input asp-for="Input.DBBlockCount" class="form-control" type="number" min="0" placeholder="100" />
+                            <span asp-validation-for="Input.DBBlockCount" class="text-danger"></span>
+                        </div>
+                        <div class="col-md-4">
+                            <label asp-for="Input.DBBlockSize" class="form-label">DB鍧楀ぇ灏�</label>
+                            <input asp-for="Input.DBBlockSize" class="form-control" type="number" min="0" placeholder="1024" />
+                            <span asp-validation-for="Input.DBBlockSize" class="text-danger"></span>
+                        </div>
+                        <div class="col-md-2">
+                            <label asp-for="Input.TRegionCount" class="form-label">瀹氭椂鍣ㄦ暟閲�</label>
+                            <input asp-for="Input.TRegionCount" class="form-control" type="number" min="0" placeholder="64" />
+                            <span asp-validation-for="Input.TRegionCount" class="text-danger"></span>
+                        </div>
+                        <div class="col-md-2">
+                            <label asp-for="Input.CRegionCount" class="form-label">璁℃暟鍣ㄦ暟閲�</label>
+                            <input asp-for="Input.CRegionCount" class="form-control" type="number" min="0" placeholder="64" />
+                            <span asp-validation-for="Input.CRegionCount" class="text-danger"></span>
+                        </div>
+                    </div>
+
+                    <hr class="my-4">
+
+                    <!-- 鎻愪氦鎸夐挳 -->
+                    <div class="d-flex justify-content-between">
+                        <a asp-page="/Index" class="btn btn-secondary">
+                            <i class="bi bi-x-lg me-1"></i>鍙栨秷
+                        </a>
+                        <button type="submit" class="btn btn-primary">
+                            <i class="bi bi-check-lg me-1"></i>鍒涘缓瀹炰緥
+                        </button>
+                    </div>
+                </form>
+            </div>
+        </div>
+    </div>
+</div>
+
+@section Scripts {
+    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
+
+<script>
+    function resetMemoryConfig() {
+        document.querySelector('#Input_MRegionSize').value = '1024';
+        document.querySelector('#Input_IRegionSize').value = '256';
+        document.querySelector('#Input_QRegionSize').value = '256';
+        document.querySelector('#Input_DBBlockCount').value = '100';
+        document.querySelector('#Input_DBBlockSize').value = '1024';
+        document.querySelector('#Input_TRegionCount').value = '64';
+        document.querySelector('#Input_CRegionCount').value = '64';
+    }
+</script>
+}
diff --git a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Create.cshtml.cs b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Create.cshtml.cs
new file mode 100644
index 0000000..928a997
--- /dev/null
+++ b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Create.cshtml.cs
@@ -0,0 +1,141 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using System.ComponentModel.DataAnnotations;
+using WIDESEAWCS_S7Simulator.Core.Entities;
+using WIDESEAWCS_S7Simulator.Core.Enums;
+
+namespace WIDESEAWCS_S7Simulator.Web.Pages;
+
+/// <summary>
+/// 鍒涘缓瀹炰緥椤�
+/// </summary>
+public class CreateModel : PageModel
+{
+    private readonly ILogger<CreateModel> _logger;
+    private readonly HttpClient _httpClient;
+
+    public CreateModel(ILogger<CreateModel> logger, IHttpClientFactory httpClientFactory)
+    {
+        _logger = logger;
+        _httpClient = httpClientFactory.CreateClient();
+        _httpClient.BaseAddress = new Uri($"{Request.Scheme}://{Request.Host}");
+    }
+
+    [BindProperty]
+    public CreateInstanceInputModel Input { get; set; } = new();
+
+    public void OnGet()
+    {
+        _logger.LogInformation("Loading create instance page");
+    }
+
+    public async Task<IActionResult> OnPostAsync()
+    {
+        if (!ModelState.IsValid)
+        {
+            return Page();
+        }
+
+        try
+        {
+            var config = new InstanceConfig
+            {
+                Id = Input.Id,
+                Name = Input.Name,
+                PLCType = Input.PLCType,
+                Port = Input.Port,
+                ActivationKey = Input.ActivationKey ?? string.Empty,
+                AutoStart = Input.AutoStart,
+                MemoryConfig = new MemoryRegionConfig
+                {
+                    MRegionSize = Input.MRegionSize > 0 ? Input.MRegionSize : 1024,
+                    DBBlockCount = Input.DBBlockCount > 0 ? Input.DBBlockCount : 100,
+                    DBBlockSize = Input.DBBlockSize > 0 ? Input.DBBlockSize : 1024,
+                    IRegionSize = Input.IRegionSize > 0 ? Input.IRegionSize : 256,
+                    QRegionSize = Input.QRegionSize > 0 ? Input.QRegionSize : 256,
+                    TRegionCount = Input.TRegionCount > 0 ? Input.TRegionCount : 64,
+                    CRegionCount = Input.CRegionCount > 0 ? Input.CRegionCount : 64
+                }
+            };
+
+            var response = await _httpClient.PostAsJsonAsync("/api/SimulatorInstances", config);
+
+            if (response.IsSuccessStatusCode)
+            {
+                _logger.LogInformation("Instance {InstanceId} created successfully", Input.Id);
+                TempData["SuccessMessage"] = $"瀹炰緥 \"{Input.Id}\" 鍒涘缓鎴愬姛!";
+                return RedirectToPage("/Index");
+            }
+            else if (response.StatusCode == System.Net.HttpStatusCode.Conflict)
+            {
+                ModelState.AddModelError(string.Empty, "瀹炰緥ID宸插瓨鍦紝璇蜂娇鐢ㄥ叾浠朓D");
+            }
+            else
+            {
+                var error = await response.Content.ReadFromJsonAsync<object>();
+                ModelState.AddModelError(string.Empty, error?.ToString() ?? "鍒涘缓瀹炰緥澶辫触");
+            }
+        }
+        catch (HttpRequestException ex)
+        {
+            _logger.LogError(ex, "Failed to create instance");
+            ModelState.AddModelError(string.Empty, "缃戠粶閿欒锛岃绋嶅悗閲嶈瘯");
+        }
+
+        return Page();
+    }
+
+    /// <summary>
+    /// 鍒涘缓瀹炰緥杈撳叆妯″瀷
+    /// </summary>
+    public class CreateInstanceInputModel
+    {
+        /// <summary>
+        /// 瀹炰緥ID
+        /// </summary>
+        [Required(ErrorMessage = "瀹炰緥ID涓嶈兘涓虹┖")]
+        [StringLength(50, MinimumLength = 1, ErrorMessage = "瀹炰緥ID闀垮害蹇呴』鍦�1-50涓瓧绗︿箣闂�")]
+        [RegularExpression("^[a-zA-Z0-9_-]+$", ErrorMessage = "瀹炰緥ID鍙兘鍖呭惈瀛楁瘝銆佹暟瀛椼�佷笅鍒掔嚎鍜岃繛瀛楃")]
+        public string Id { get; set; } = string.Empty;
+
+        /// <summary>
+        /// 瀹炰緥鍚嶇О
+        /// </summary>
+        [Required(ErrorMessage = "瀹炰緥鍚嶇О涓嶈兘涓虹┖")]
+        [StringLength(100, ErrorMessage = "瀹炰緥鍚嶇О涓嶈兘瓒呰繃100涓瓧绗�")]
+        public string Name { get; set; } = string.Empty;
+
+        /// <summary>
+        /// PLC鍨嬪彿
+        /// </summary>
+        [Required(ErrorMessage = "PLC鍨嬪彿涓嶈兘涓虹┖")]
+        public SiemensPLCType PLCType { get; set; } = SiemensPLCType.S71200;
+
+        /// <summary>
+        /// 鐩戝惉绔彛
+        /// </summary>
+        [Required(ErrorMessage = "绔彛涓嶈兘涓虹┖")]
+        [Range(1, 65535, ErrorMessage = "绔彛蹇呴』鍦�1-65535涔嬮棿")]
+        public int Port { get; set; } = 102;
+
+        /// <summary>
+        /// HSL婵�娲荤爜
+        /// </summary>
+        [StringLength(200, ErrorMessage = "婵�娲荤爜涓嶈兘瓒呰繃200涓瓧绗�")]
+        public string? ActivationKey { get; set; }
+
+        /// <summary>
+        /// 鑷姩鍚姩
+        /// </summary>
+        public bool AutoStart { get; set; } = false;
+
+        // 鍐呭瓨閰嶇疆
+        public int MRegionSize { get; set; }
+        public int DBBlockCount { get; set; }
+        public int DBBlockSize { get; set; }
+        public int IRegionSize { get; set; }
+        public int QRegionSize { get; set; }
+        public int TRegionCount { get; set; }
+        public int CRegionCount { get; set; }
+    }
+}
diff --git a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Details.cshtml b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Details.cshtml
new file mode 100644
index 0000000..95b4ece
--- /dev/null
+++ b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Details.cshtml
@@ -0,0 +1,338 @@
+@page
+@model DetailsModel
+@{
+    ViewData["Title"] = $"瀹炰緥璇︽儏 - {Model.Instance?.InstanceId}";
+}
+
+@if (Model.Instance == null)
+{
+    <div class="alert alert-warning" role="alert">
+        <i class="bi bi-exclamation-triangle-fill me-2"></i>
+        瀹炰緥涓嶅瓨鍦ㄦ垨鍔犺浇澶辫触
+    </div>
+    <a asp-page="/Index" class="btn btn-primary">
+        <i class="bi bi-arrow-left me-1"></i>杩斿洖鍒楄〃
+    </a>
+}
+else
+{
+    <div class="d-flex justify-content-between align-items-center mb-4">
+        <div>
+            <h2 class="mb-0">
+                <i class="bi bi-info-circle me-2"></i>瀹炰緥璇︽儏: @Model.Instance.InstanceId
+            </h2>
+            <p class="text-muted mb-0 mt-1">鏌ョ湅鍜岀鐞嗕豢鐪熷櫒瀹炰緥鐨勮缁嗕俊鎭�</p>
+        </div>
+        <div>
+            <a asp-page="/Edit" asp-route-id="@Model.Instance.InstanceId" class="btn btn-primary me-2">
+                <i class="bi bi-pencil-fill me-1"></i>缂栬緫
+            </a>
+            <a asp-page="/Index" class="btn btn-secondary">
+                <i class="bi bi-arrow-left me-1"></i>杩斿洖鍒楄〃
+            </a>
+        </div>
+    </div>
+
+    <!-- 鐘舵�佸崱鐗� -->
+    <div class="row mb-4">
+        <div class="col-md-3">
+            <div class="card text-center">
+                <div class="card-body">
+                    <h6 class="card-subtitle mb-2 text-muted">鐘舵��</h6>
+                    <h4 class="card-title">
+                        <span class="badge @GetStatusBadgeClass(Model.Instance.Status) fs-6">
+                            @GetStatusText(Model.Instance.Status)
+                        </span>
+                    </h4>
+                </div>
+            </div>
+        </div>
+        <div class="col-md-3">
+            <div class="card text-center">
+                <div class="card-body">
+                    <h6 class="card-subtitle mb-2 text-muted">杩炴帴瀹㈡埛绔�</h6>
+                    <h4 class="card-title">
+                        <i class="bi bi-people-fill text-primary me-1"></i>@Model.Instance.ClientCount
+                    </h4>
+                </div>
+            </div>
+        </div>
+        <div class="col-md-3">
+            <div class="card text-center">
+                <div class="card-body">
+                    <h6 class="card-subtitle mb-2 text-muted">鎬昏姹傛暟</h6>
+                    <h4 class="card-title">
+                        <i class="bi bi-graph-up-arrow text-success me-1"></i>@Model.Instance.TotalRequests
+                    </h4>
+                </div>
+            </div>
+        </div>
+        <div class="col-md-3">
+            <div class="card text-center">
+                <div class="card-body">
+                    <h6 class="card-subtitle mb-2 text-muted">杩愯鏃堕棿</h6>
+                    <h4 class="card-title" id="runningTime">
+                        @if (Model.Instance.StartTime.HasValue)
+                        {
+                            <span>@GetRunningTime(Model.Instance.StartTime.Value)</span>
+                        }
+                        else
+                        {
+                            <span class="text-muted">-</span>
+                        }
+                    </h4>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <!-- 瀹炰緥淇℃伅 -->
+    <div class="card mb-4">
+        <div class="card-header">
+            <h5 class="mb-0">
+                <i class="bi bi-cpu me-2"></i>瀹炰緥淇℃伅
+            </h5>
+        </div>
+        <div class="card-body">
+            <div class="row">
+                <div class="col-md-6 mb-3">
+                    <strong>瀹炰緥ID:</strong>
+                    <div class="text-muted">@Model.Instance.InstanceId</div>
+                </div>
+                <div class="col-md-6 mb-3">
+                    <strong>鍚姩鏃堕棿:</strong>
+                    <div class="text-muted">@(Model.Instance.StartTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? "-")</div>
+                </div>
+                <div class="col-md-6 mb-3">
+                    <strong>鏈�鍚庢椿鍔�:</strong>
+                    <div class="text-muted">@(Model.Instance.LastActivityTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? "-")</div>
+                </div>
+                <div class="col-md-6 mb-3">
+                    <strong>閿欒淇℃伅:</strong>
+                    <div class="text-muted">@(Model.Instance.ErrorMessage ?? "鏃�")</div>
+                </div>
+            </div>
+        </div>
+        <div class="card-footer">
+            <div class="d-flex gap-2">
+                @if (Model.Instance.Status == WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Running)
+                {
+                    <button class="btn btn-warning" onclick="stopInstance('@Model.Instance.InstanceId')">
+                        <i class="bi bi-stop-fill me-1"></i>鍋滄
+                    </button>
+                    <button class="btn btn-info" onclick="restartInstance('@Model.Instance.InstanceId')">
+                        <i class="bi bi-arrow-clockwise me-1"></i>閲嶅惎
+                    </button>
+                }
+                else if (Model.Instance.Status == WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Stopped)
+                {
+                    <button class="btn btn-success" onclick="startInstance('@Model.Instance.InstanceId')">
+                        <i class="bi bi-play-fill me-1"></i>鍚姩
+                    </button>
+                }
+            </div>
+        </div>
+    </div>
+
+    <!-- 杩炴帴鐨勫鎴风 -->
+    <div class="card">
+        <div class="card-header d-flex justify-content-between align-items-center">
+            <h5 class="mb-0">
+                <i class="bi bi-people me-2"></i>杩炴帴鐨勫鎴风
+            </h5>
+            <span class="badge bg-primary">@Model.Clients.Count</span>
+        </div>
+        <div class="card-body">
+            @if (Model.Clients.Any())
+            {
+                <div class="table-responsive">
+                    <table class="table table-striped table-hover client-table">
+                        <thead>
+                            <tr>
+                                <th>瀹㈡埛绔疘D</th>
+                                <th>杩滅▼鍦板潃</th>
+                                <th>杩炴帴鏃堕棿</th>
+                                <th>鏈�鍚庢椿鍔�</th>
+                                <th>鎿嶄綔</th>
+                            </tr>
+                        </thead>
+                        <tbody>
+                            @foreach (var client in Model.Clients)
+                            {
+                                <tr>
+                                    <td><code>@client.ClientId</code></td>
+                                    <td>@client.RemoteEndPoint</td>
+                                    <td>@client.ConnectedTime.ToString("yyyy-MM-dd HH:mm:ss")</td>
+                                    <td>@client.LastActivityTime.ToString("yyyy-MM-dd HH:mm:ss")</td>
+                                    <td>
+                                        <button class="btn btn-sm btn-danger" onclick="disconnectClient('@Model.Instance.InstanceId', '@client.ClientId')">
+                                            <i class="bi bi-x-circle-fill"></i>鏂紑
+                                        </button>
+                                    </td>
+                                </tr>
+                            }
+                        </tbody>
+                    </table>
+                </div>
+            }
+            else
+            {
+                <div class="text-center text-muted py-4">
+                    <i class="bi bi-inbox" style="font-size: 2rem;"></i>
+                    <p class="mt-2">鏆傛棤杩炴帴鐨勫鎴风</p>
+                </div>
+            }
+        </div>
+    </div>
+}
+
+@{
+    string GetStatusBadgeClass(WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus status)
+    {
+        return status switch
+        {
+            WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Running => "bg-success",
+            WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Stopped => "bg-secondary",
+            WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Starting => "bg-info",
+            WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Stopping => "bg-warning text-dark",
+            WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Error => "bg-danger",
+            _ => "bg-secondary"
+        };
+    }
+
+    string GetStatusText(WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus status)
+    {
+        return status switch
+        {
+            WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Running => "杩愯涓�",
+            WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Stopped => "宸插仠姝�",
+            WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Starting => "鍚姩涓�",
+            WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Stopping => "鍋滄涓�",
+            WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Error => "閿欒",
+            _ => status.ToString()
+        };
+    }
+
+    string GetRunningTime(DateTime startTime)
+    {
+        var span = DateTime.Now - startTime;
+        if (span.TotalDays >= 1)
+            return $"{(int)span.TotalDays}澶� {span.Hours}灏忔椂";
+        else if (span.TotalHours >= 1)
+            return $"{(int)span.TotalHours}灏忔椂 {span.Minutes}鍒嗛挓";
+        else if (span.TotalMinutes >= 1)
+            return $"{(int)span.TotalMinutes}鍒嗛挓 {span.Seconds}绉�";
+        else
+            return $"{span.Seconds}绉�";
+    }
+}
+
+@section Scripts {
+<script>
+    // Auto refresh
+    const instanceId = '@Model.Instance?.InstanceId';
+    let refreshInterval = null;
+
+    document.addEventListener('DOMContentLoaded', function () {
+        if (instanceId) {
+            startAutoRefresh();
+            // Update running time every second
+            setInterval(updateRunningTime, 1000);
+        }
+    });
+
+    function startAutoRefresh() {
+        refreshInterval = setInterval(async () => {
+            try {
+                const [instance, clients] = await Promise.all([
+                    fetch(`${API_BASE_URL}/SimulatorInstances/${encodeURIComponent(instanceId)}`).then(r => r.json()),
+                    fetch(`${API_BASE_URL}/instances/${encodeURIComponent(instanceId)}/Clients`).then(r => r.json())
+                ]);
+
+                // Update status badges
+                updateInstanceStatus(instance);
+            } catch (error) {
+                console.error('Auto refresh failed:', error);
+            }
+        }, 5000);
+    }
+
+    function updateInstanceStatus(instance) {
+        // You can add logic here to update UI elements without full page reload
+        // For now, we rely on page refresh for major changes
+    }
+
+    function updateRunningTime() {
+        const startTimeElement = document.querySelector('#runningTime span');
+        if (startTimeElement && startTimeElement.textContent !== '-') {
+            // This would require storing the start time and calculating
+            // For simplicity, we'll skip the real-time update
+        }
+    }
+
+    async function startInstance(id) {
+        confirmAction(`纭畾瑕佸惎鍔ㄥ疄渚� "${id}" 鍚�?`, async () => {
+            try {
+                await apiCall(`${API_BASE_URL}/SimulatorInstances/${encodeURIComponent(id)}/start`, {
+                    method: 'POST'
+                });
+                showToast('鍚姩鍛戒护宸插彂閫�', 'success');
+                setTimeout(() => location.reload(), 1000);
+            } catch (error) {
+                console.error('Failed to start instance:', error);
+            }
+        });
+    }
+
+    async function stopInstance(id) {
+        confirmAction(`纭畾瑕佸仠姝㈠疄渚� "${id}" 鍚�?`, async () => {
+            try {
+                await apiCall(`${API_BASE_URL}/SimulatorInstances/${encodeURIComponent(id)}/stop`, {
+                    method: 'POST'
+                });
+                showToast('鍋滄鍛戒护宸插彂閫�', 'success');
+                setTimeout(() => location.reload(), 1000);
+            } catch (error) {
+                console.error('Failed to stop instance:', error);
+            }
+        });
+    }
+
+    async function restartInstance(id) {
+        confirmAction(`纭畾瑕侀噸鍚疄渚� "${id}" 鍚�?`, async () => {
+            try {
+                await apiCall(`${API_BASE_URL}/SimulatorInstances/${encodeURIComponent(id)}/restart`, {
+                    method: 'POST'
+                });
+                showToast('閲嶅惎鍛戒护宸插彂閫�', 'success');
+                setTimeout(() => location.reload(), 1000);
+            } catch (error) {
+                console.error('Failed to restart instance:', error);
+            }
+        });
+    }
+
+    async function disconnectClient(instanceId, clientId) {
+        confirmAction(`纭畾瑕佹柇寮�瀹㈡埛绔� "${clientId}" 鍚�?`, async () => {
+            try {
+                await apiCall(`${API_BASE_URL}/instances/${encodeURIComponent(instanceId)}/Clients/${encodeURIComponent(clientId)}`, {
+                    method: 'DELETE'
+                });
+                showToast('瀹㈡埛绔凡鏂紑', 'success');
+                setTimeout(() => location.reload(), 500);
+            } catch (error) {
+                console.error('Failed to disconnect client:', error);
+            }
+        });
+    }
+
+    // Stop auto-refresh when page is hidden
+    document.addEventListener('visibilitychange', function () {
+        if (document.hidden) {
+            if (refreshInterval) clearInterval(refreshInterval);
+        } else {
+            startAutoRefresh();
+        }
+    });
+</script>
+}
diff --git a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Details.cshtml.cs b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Details.cshtml.cs
new file mode 100644
index 0000000..bcbd46f
--- /dev/null
+++ b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Details.cshtml.cs
@@ -0,0 +1,61 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using WIDESEAWCS_S7Simulator.Core.Entities;
+
+namespace WIDESEAWCS_S7Simulator.Web.Pages;
+
+/// <summary>
+/// 瀹炰緥璇︽儏椤�
+/// </summary>
+public class DetailsModel : PageModel
+{
+    private readonly ILogger<DetailsModel> _logger;
+    private readonly HttpClient _httpClient;
+
+    public DetailsModel(ILogger<DetailsModel> logger, IHttpClientFactory httpClientFactory)
+    {
+        _logger = logger;
+        _httpClient = httpClientFactory.CreateClient();
+        _httpClient.BaseAddress = new Uri($"{Request.Scheme}://{Request.Host}");
+    }
+
+    public InstanceState? Instance { get; private set; }
+
+    public List<S7ClientConnection> Clients { get; private set; } = new();
+
+    public async Task<IActionResult> OnGetAsync(string id)
+    {
+        if (string.IsNullOrWhiteSpace(id))
+        {
+            return BadRequest("瀹炰緥ID涓嶈兘涓虹┖");
+        }
+
+        try
+        {
+            var response = await _httpClient.GetAsync($"/api/SimulatorInstances/{Uri.EscapeDataString(id)}");
+
+            if (response.IsSuccessStatusCode)
+            {
+                Instance = await response.Content.ReadFromJsonAsync<InstanceState>();
+
+                // Load clients
+                var clientsResponse = await _httpClient.GetAsync($"/api/instances/{Uri.EscapeDataString(id)}/Clients");
+                if (clientsResponse.IsSuccessStatusCode)
+                {
+                    Clients = await clientsResponse.Content.ReadFromJsonAsync<List<S7ClientConnection>>() ?? new();
+                }
+            }
+            else if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
+            {
+                return NotFound($"瀹炰緥 \"{id}\" 涓嶅瓨鍦�");
+            }
+        }
+        catch (HttpRequestException ex)
+        {
+            _logger.LogError(ex, "Failed to load instance details for {InstanceId}", id);
+            ModelState.AddModelError(string.Empty, "鍔犺浇瀹炰緥璇︽儏澶辫触锛岃绋嶅悗閲嶈瘯");
+        }
+
+        return Page();
+    }
+}
diff --git a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Edit.cshtml b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Edit.cshtml
new file mode 100644
index 0000000..dc89e76
--- /dev/null
+++ b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Edit.cshtml
@@ -0,0 +1,245 @@
+@page
+@model EditModel
+@{
+    ViewData["Title"] = $"缂栬緫瀹炰緥 - {Model.Input.Id}";
+}
+
+<div class="row justify-content-center">
+    <div class="col-lg-8">
+        <div class="card shadow">
+            <div class="card-header bg-warning text-dark">
+                <h4 class="mb-0">
+                    <i class="bi bi-pencil-square me-2"></i>缂栬緫瀹炰緥: @Model.Input.Id
+                </h4>
+            </div>
+            <div class="card-body">
+                @if (Model.IsRunning)
+                {
+                    <div class="alert alert-warning d-flex align-items-center" role="alert">
+                        <i class="bi bi-exclamation-triangle-fill flex-shrink-0 me-2"></i>
+                        <div>
+                            <strong>璀﹀憡:</strong> 瀹炰緥姝e湪杩愯涓�傛煇浜涢厤缃慨鏀瑰彲鑳介渶瑕侀噸鍚疄渚嬫墠鑳界敓鏁堛��
+                        </div>
+                    </div>
+                }
+
+                <form method="post" id="editForm">
+                    <div asp-validation-summary="ModelOnly" class="alert alert-danger" role="alert"></div>
+
+                    <!-- 鍩烘湰淇℃伅 -->
+                    <h5 class="mb-3">
+                        <i class="bi bi-info-circle me-2"></i>鍩烘湰淇℃伅
+                    </h5>
+
+                    <div class="row mb-3">
+                        <div class="col-md-6">
+                            <label class="form-label">瀹炰緥ID</label>
+                            <input type="text" class="form-control" value="@Model.Input.Id" disabled />
+                            <small class="text-muted">瀹炰緥ID涓嶅彲淇敼</small>
+                        </div>
+                        <div class="col-md-6">
+                            <label asp-for="Input.Name" class="form-label required">瀹炰緥鍚嶇О</label>
+                            <input asp-for="Input.Name" class="form-control" />
+                            <span asp-validation-for="Input.Name" class="text-danger"></span>
+                        </div>
+                    </div>
+
+                    <div class="row mb-3">
+                        <div class="col-md-6">
+                            <label asp-for="Input.PLCType" class="form-label required">PLC鍨嬪彿</label>
+                            @if (Model.IsRunning)
+                            {
+                                <select asp-for="Input.PLCType" class="form-select" disabled>
+                                    <option value="0">S7-200 Smart</option>
+                                    <option value="1">S7-1200</option>
+                                    <option value="2">S7-1500</option>
+                                    <option value="3">S7-300</option>
+                                    <option value="4">S7-400</option>
+                                </select>
+                                <small class="text-muted">杩愯涓笉鍙慨鏀�</small>
+                            }
+                            else
+                            {
+                                <select asp-for="Input.PLCType" class="form-select">
+                                    <option value="0">S7-200 Smart</option>
+                                    <option value="1">S7-1200</option>
+                                    <option value="2">S7-1500</option>
+                                    <option value="3">S7-300</option>
+                                    <option value="4">S7-400</option>
+                                </select>
+                            }
+                            <span asp-validation-for="Input.PLCType" class="text-danger"></span>
+                        </div>
+                        <div class="col-md-6">
+                            <label asp-for="Input.Port" class="form-label required">鐩戝惉绔彛</label>
+                            @if (Model.IsRunning)
+                            {
+                                <input asp-for="Input.Port" class="form-control" type="number" min="1" max="65535" disabled />
+                                <small class="text-muted">杩愯涓笉鍙慨鏀�</small>
+                            }
+                            else
+                            {
+                                <input asp-for="Input.Port" class="form-control" type="number" min="1" max="65535" />
+                            }
+                            <span asp-validation-for="Input.Port" class="text-danger"></span>
+                        </div>
+                    </div>
+
+                    <div class="row mb-3">
+                        <div class="col-md-6">
+                            <label asp-for="Input.ActivationKey" class="form-label">HSL婵�娲荤爜</label>
+                            <input asp-for="Input.ActivationKey" class="form-control" placeholder="鍙��" />
+                            <span asp-validation-for="Input.ActivationKey" class="text-danger"></span>
+                        </div>
+                        <div class="col-md-6">
+                            <label asp-for="Input.AutoStart" class="form-label">鑷姩鍚姩</label>
+                            <div class="form-check mt-2">
+                                <input asp-for="Input.AutoStart" class="form-check-input" />
+                                <label asp-for="Input.AutoStart" class="form-check-label">
+                                    鏈嶅姟鍣ㄥ惎鍔ㄦ椂鑷姩鍚姩姝ゅ疄渚�
+                                </label>
+                            </div>
+                        </div>
+                    </div>
+
+                    <hr class="my-4">
+
+                    <!-- 鍐呭瓨閰嶇疆 -->
+                    <div class="d-flex justify-content-between align-items-center mb-3">
+                        <h5 class="mb-0">
+                            <i class="bi bi-memory me-2"></i>鍐呭瓨閰嶇疆
+                        </h5>
+                        <button type="button" class="btn btn-outline-secondary btn-sm" onclick="resetMemoryConfig()">
+                            <i class="bi bi-arrow-counterclockwise me-1"></i>閲嶇疆涓洪粯璁ゅ��
+                        </button>
+                    </div>
+
+                    <div class="row mb-3">
+                        <div class="col-md-4">
+                            <label asp-for="Input.MRegionSize" class="form-label">M鍖哄煙澶у皬</label>
+                            <input asp-for="Input.MRegionSize" class="form-control" type="number" min="0" placeholder="1024" />
+                            <span asp-validation-for="Input.MRegionSize" class="text-danger"></span>
+                        </div>
+                        <div class="col-md-4">
+                            <label asp-for="Input.IRegionSize" class="form-label">I鍖哄煙澶у皬</label>
+                            <input asp-for="Input.IRegionSize" class="form-control" type="number" min="0" placeholder="256" />
+                            <span asp-validation-for="Input.IRegionSize" class="text-danger"></span>
+                        </div>
+                        <div class="col-md-4">
+                            <label asp-for="Input.QRegionSize" class="form-label">Q鍖哄煙澶у皬</label>
+                            <input asp-for="Input.QRegionSize" class="form-control" type="number" min="0" placeholder="256" />
+                            <span asp-validation-for="Input.QRegionSize" class="text-danger"></span>
+                        </div>
+                    </div>
+
+                    <div class="row mb-3">
+                        <div class="col-md-4">
+                            <label asp-for="Input.DBBlockCount" class="form-label">DB鍧楁暟閲�</label>
+                            <input asp-for="Input.DBBlockCount" class="form-control" type="number" min="0" placeholder="100" />
+                            <span asp-validation-for="Input.DBBlockCount" class="text-danger"></span>
+                        </div>
+                        <div class="col-md-4">
+                            <label asp-for="Input.DBBlockSize" class="form-label">DB鍧楀ぇ灏�</label>
+                            <input asp-for="Input.DBBlockSize" class="form-control" type="number" min="0" placeholder="1024" />
+                            <span asp-validation-for="Input.DBBlockSize" class="text-danger"></span>
+                        </div>
+                        <div class="col-md-2">
+                            <label asp-for="Input.TRegionCount" class="form-label">瀹氭椂鍣ㄦ暟閲�</label>
+                            <input asp-for="Input.TRegionCount" class="form-control" type="number" min="0" placeholder="64" />
+                            <span asp-validation-for="Input.TRegionCount" class="text-danger"></span>
+                        </div>
+                        <div class="col-md-2">
+                            <label asp-for="Input.CRegionCount" class="form-label">璁℃暟鍣ㄦ暟閲�</label>
+                            <input asp-for="Input.CRegionCount" class="form-control" type="number" min="0" placeholder="64" />
+                            <span asp-validation-for="Input.CRegionCount" class="text-danger"></span>
+                        </div>
+                    </div>
+
+                    <hr class="my-4">
+
+                    <!-- 鎻愪氦鎸夐挳 -->
+                    <div class="d-flex justify-content-between">
+                        <a asp-page="/Index" class="btn btn-secondary">
+                            <i class="bi bi-x-lg me-1"></i>鍙栨秷
+                        </a>
+                        <button type="submit" class="btn btn-primary">
+                            <i class="bi bi-check-lg me-1"></i>淇濆瓨淇敼
+                        </button>
+                    </div>
+                </form>
+            </div>
+        </div>
+
+        <!-- 褰撳墠鐘舵�佸崱鐗� -->
+        @if (Model.CurrentInstance != null)
+        {
+            <div class="card mt-4">
+                <div class="card-header">
+                    <h6 class="mb-0">
+                        <i class="bi bi-activity me-2"></i>褰撳墠鐘舵��
+                    </h6>
+                </div>
+                <div class="card-body">
+                    <div class="row">
+                        <div class="col-md-4">
+                            <strong>鐘舵��:</strong>
+                            <span class="badge @GetStatusBadgeClass(Model.CurrentInstance.Status)">
+                                @GetStatusText(Model.CurrentInstance.Status)
+                            </span>
+                        </div>
+                        <div class="col-md-4">
+                            <strong>瀹㈡埛绔暟:</strong> @Model.CurrentInstance.ClientCount
+                        </div>
+                        <div class="col-md-4">
+                            <strong>鎬昏姹傛暟:</strong> @Model.CurrentInstance.TotalRequests
+                        </div>
+                    </div>
+                </div>
+            </div>
+        }
+    </div>
+</div>
+
+@{
+    string GetStatusBadgeClass(WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus status)
+    {
+        return status switch
+        {
+            WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Running => "bg-success",
+            WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Stopped => "bg-secondary",
+            WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Starting => "bg-info",
+            WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Stopping => "bg-warning",
+            WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Error => "bg-danger",
+            _ => "bg-secondary"
+        };
+    }
+
+    string GetStatusText(WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus status)
+    {
+        return status switch
+        {
+            WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Running => "杩愯涓�",
+            WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Stopped => "宸插仠姝�",
+            WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Starting => "鍚姩涓�",
+            WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Stopping => "鍋滄涓�",
+            WIDESEAWCS_S7Simulator.Core.Enums.InstanceStatus.Error => "閿欒",
+            _ => status.ToString()
+        };
+    }
+}
+
+@section Scripts {
+    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
+
+<script>
+    function resetMemoryConfig() {
+        document.querySelector('#Input_MRegionSize').value = '1024';
+        document.querySelector('#Input_IRegionSize').value = '256';
+        document.querySelector('#Input_QRegionSize').value = '256';
+        document.querySelector('#Input_DBBlockCount').value = '100';
+        document.querySelector('#Input_DBBlockSize').value = '1024';
+        document.querySelector('#Input_TRegionCount').value = '64';
+        document.querySelector('#Input_CRegionCount').value = '64';
+    }
+</script>
+}
diff --git a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Edit.cshtml.cs b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Edit.cshtml.cs
new file mode 100644
index 0000000..d4b125d
--- /dev/null
+++ b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Edit.cshtml.cs
@@ -0,0 +1,182 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using System.ComponentModel.DataAnnotations;
+using WIDESEAWCS_S7Simulator.Core.Entities;
+using WIDESEAWCS_S7Simulator.Core.Enums;
+
+namespace WIDESEAWCS_S7Simulator.Web.Pages;
+
+/// <summary>
+/// 缂栬緫瀹炰緥椤�
+/// </summary>
+public class EditModel : PageModel
+{
+    private readonly ILogger<EditModel> _logger;
+    private readonly HttpClient _httpClient;
+
+    public EditModel(ILogger<EditModel> logger, IHttpClientFactory httpClientFactory)
+    {
+        _logger = logger;
+        _httpClient = httpClientFactory.CreateClient();
+        _httpClient.BaseAddress = new Uri($"{Request.Scheme}://{Request.Host}");
+    }
+
+    [BindProperty]
+    public EditInstanceInputModel Input { get; set; } = new();
+
+    public InstanceState? CurrentInstance { get; private set; }
+
+    public bool IsRunning { get; private set; }
+
+    public async Task<IActionResult> OnGetAsync(string id)
+    {
+        if (string.IsNullOrWhiteSpace(id))
+        {
+            return BadRequest("瀹炰緥ID涓嶈兘涓虹┖");
+        }
+
+        try
+        {
+            var response = await _httpClient.GetAsync($"/api/SimulatorInstances/{Uri.EscapeDataString(id)}");
+
+            if (response.IsSuccessStatusCode)
+            {
+                CurrentInstance = await response.Content.ReadFromJsonAsync<InstanceState>();
+                IsRunning = CurrentInstance?.Status == InstanceStatus.Running;
+
+                // Load existing config
+                var configResponse = await _httpClient.GetAsync($"/api/SimulatorInstances");
+                if (configResponse.IsSuccessStatusCode)
+                {
+                    var allInstances = await configResponse.Content.ReadFromJsonAsync<List<InstanceState>>();
+                    // For now, we'll initialize with defaults. In production, you'd have a separate endpoint to get instance config
+                }
+
+                // Initialize input with current values (default for now)
+                Input.Id = id;
+                Input.Name = CurrentInstance?.InstanceId ?? id;
+                Input.Port = 102; // Default
+            }
+            else if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
+            {
+                return NotFound($"瀹炰緥 \"{id}\" 涓嶅瓨鍦�");
+            }
+        }
+        catch (HttpRequestException ex)
+        {
+            _logger.LogError(ex, "Failed to load instance {InstanceId}", id);
+            ModelState.AddModelError(string.Empty, "鍔犺浇瀹炰緥澶辫触锛岃绋嶅悗閲嶈瘯");
+        }
+
+        return Page();
+    }
+
+    public async Task<IActionResult> OnPostAsync()
+    {
+        if (!ModelState.IsValid)
+        {
+            // Reload instance state
+            await LoadInstanceStateAsync(Input.Id);
+            return Page();
+        }
+
+        try
+        {
+            var config = new InstanceConfig
+            {
+                Id = Input.Id,
+                Name = Input.Name,
+                PLCType = Input.PLCType,
+                Port = Input.Port,
+                ActivationKey = Input.ActivationKey ?? string.Empty,
+                AutoStart = Input.AutoStart,
+                MemoryConfig = new MemoryRegionConfig
+                {
+                    MRegionSize = Input.MRegionSize > 0 ? Input.MRegionSize : 1024,
+                    DBBlockCount = Input.DBBlockCount > 0 ? Input.DBBlockCount : 100,
+                    DBBlockSize = Input.DBBlockSize > 0 ? Input.DBBlockSize : 1024,
+                    IRegionSize = Input.IRegionSize > 0 ? Input.IRegionSize : 256,
+                    QRegionSize = Input.QRegionSize > 0 ? Input.QRegionSize : 256,
+                    TRegionCount = Input.TRegionCount > 0 ? Input.TRegionCount : 64,
+                    CRegionCount = Input.CRegionCount > 0 ? Input.CRegionCount : 64
+                }
+            };
+
+            var response = await _httpClient.PutAsJsonAsync($"/api/SimulatorInstances/{Uri.EscapeDataString(Input.Id)}", config);
+
+            if (response.IsSuccessStatusCode)
+            {
+                _logger.LogInformation("Instance {InstanceId} updated successfully", Input.Id);
+                TempData["SuccessMessage"] = $"瀹炰緥 \"{Input.Id}\" 鏇存柊鎴愬姛!";
+                return RedirectToPage("/Index");
+            }
+            else if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
+            {
+                ModelState.AddModelError(string.Empty, "瀹炰緥涓嶅瓨鍦�");
+            }
+            else
+            {
+                var error = await response.Content.ReadFromJsonAsync<object>();
+                ModelState.AddModelError(string.Empty, error?.ToString() ?? "鏇存柊瀹炰緥澶辫触");
+            }
+        }
+        catch (HttpRequestException ex)
+        {
+            _logger.LogError(ex, "Failed to update instance");
+            ModelState.AddModelError(string.Empty, "缃戠粶閿欒锛岃绋嶅悗閲嶈瘯");
+        }
+
+        await LoadInstanceStateAsync(Input.Id);
+        return Page();
+    }
+
+    private async Task LoadInstanceStateAsync(string id)
+    {
+        try
+        {
+            var response = await _httpClient.GetAsync($"/api/SimulatorInstances/{Uri.EscapeDataString(id)}");
+            if (response.IsSuccessStatusCode)
+            {
+                CurrentInstance = await response.Content.ReadFromJsonAsync<InstanceState>();
+                IsRunning = CurrentInstance?.Status == InstanceStatus.Running;
+            }
+        }
+        catch
+        {
+            // Ignore errors when reloading state
+        }
+    }
+
+    /// <summary>
+    /// 缂栬緫瀹炰緥杈撳叆妯″瀷
+    /// </summary>
+    public class EditInstanceInputModel
+    {
+        public string Id { get; set; } = string.Empty;
+
+        [Required(ErrorMessage = "瀹炰緥鍚嶇О涓嶈兘涓虹┖")]
+        [StringLength(100, ErrorMessage = "瀹炰緥鍚嶇О涓嶈兘瓒呰繃100涓瓧绗�")]
+        public string Name { get; set; } = string.Empty;
+
+        [Required(ErrorMessage = "PLC鍨嬪彿涓嶈兘涓虹┖")]
+        public SiemensPLCType PLCType { get; set; } = SiemensPLCType.S71200;
+
+        [Required(ErrorMessage = "绔彛涓嶈兘涓虹┖")]
+        [Range(1, 65535, ErrorMessage = "绔彛蹇呴』鍦�1-65535涔嬮棿")]
+        public int Port { get; set; } = 102;
+
+        [StringLength(200, ErrorMessage = "婵�娲荤爜涓嶈兘瓒呰繃200涓瓧绗�")]
+        public string? ActivationKey { get; set; }
+
+        public bool AutoStart { get; set; } = false;
+
+        // 鍐呭瓨閰嶇疆
+        public int MRegionSize { get; set; }
+        public int DBBlockCount { get; set; }
+        public int DBBlockSize { get; set; }
+        public int IRegionSize { get; set; }
+        public int QRegionSize { get; set; }
+        public int TRegionCount { get; set; }
+        public int CRegionCount { get; set; }
+    }
+}
diff --git a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Index.cshtml b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Index.cshtml
index b5f0c15..e86b444 100644
--- a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Index.cshtml
+++ b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Index.cshtml
@@ -1,10 +1,226 @@
 锘緻page
 @model IndexModel
 @{
-    ViewData["Title"] = "Home page";
+    ViewData["Title"] = "瀹炰緥鍒楄〃";
 }
 
-<div class="text-center">
-    <h1 class="display-4">Welcome</h1>
-    <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
+<div class="d-flex justify-content-between align-items-center mb-4">
+    <div>
+        <h2 class="mb-0">
+            <i class="bi bi-cpu-fill me-2"></i>S7 PLC 浠跨湡鍣ㄥ疄渚�
+        </h2>
+        <p class="text-muted mb-0 mt-1">绠$悊鍜岀洃鎺� S7 PLC 浠跨湡鍣ㄥ疄渚�</p>
+    </div>
+    <a asp-page="/Create" class="btn btn-primary">
+        <i class="bi bi-plus-lg me-1"></i>鍒涘缓瀹炰緥
+    </a>
 </div>
+
+<div id="instancesContainer">
+    <!-- Loading state -->
+    <div id="loadingState" class="text-center py-5">
+        <div class="spinner-border text-primary" role="status">
+            <span class="visually-hidden">鍔犺浇涓�...</span>
+        </div>
+        <p class="mt-3 text-muted">姝e湪鍔犺浇瀹炰緥鍒楄〃...</p>
+    </div>
+
+    <!-- Empty state -->
+    <div id="emptyState" class="empty-state d-none">
+        <i class="bi bi-inbox"></i>
+        <h3>鏆傛棤瀹炰緥</h3>
+        <p>鐐瑰嚮涓婃柟"鍒涘缓瀹炰緥"鎸夐挳鏉ュ垱寤烘偍鐨勭涓�涓豢鐪熷櫒瀹炰緥</p>
+    </div>
+
+    <!-- Instances grid -->
+    <div id="instancesGrid" class="row row-cols-1 row-cols-md-2 row-cols-xl-3 g-4 d-none"></div>
+</div>
+
+@section Scripts {
+<script>
+    let autoRefreshInterval = null;
+
+    document.addEventListener('DOMContentLoaded', function () {
+        loadInstances();
+        startAutoRefresh(loadInstances, 5000);
+    });
+
+    async function loadInstances() {
+        try {
+            const instances = await apiCall(`${API_BASE_URL}/SimulatorInstances`);
+            renderInstances(instances);
+        } catch (error) {
+            console.error('Failed to load instances:', error);
+            document.getElementById('loadingState').classList.add('d-none');
+            document.getElementById('emptyState').classList.remove('d-none');
+        }
+    }
+
+    function renderInstances(instances) {
+        const loadingState = document.getElementById('loadingState');
+        const emptyState = document.getElementById('emptyState');
+        const grid = document.getElementById('instancesGrid');
+
+        loadingState.classList.add('d-none');
+        grid.classList.add('d-none');
+        emptyState.classList.add('d-none');
+
+        if (!instances || instances.length === 0) {
+            emptyState.classList.remove('d-none');
+            return;
+        }
+
+        grid.classList.remove('d-none');
+        grid.innerHTML = instances.map(instance => createInstanceCard(instance)).join('');
+    }
+
+    function createInstanceCard(instance) {
+        const statusClass = getStatusClass(instance.status);
+        const statusText = getStatusText(instance.status);
+        const plcTypeText = getPlcTypeText(instance.plcType);
+        const isRunning = instance.status === 'Running';
+        const isStopped = instance.status === 'Stopped';
+
+        return `
+            <div class="col">
+                <div class="card instance-card h-100 ${statusClass}">
+                    <div class="card-header d-flex justify-content-between align-items-center">
+                        <h5 class="card-title mb-0">${escapeHtml(instance.instanceId)}</h5>
+                        <span class="badge ${statusClass}">${statusText}</span>
+                    </div>
+                    <div class="card-body">
+                        <div class="instance-info mb-3">
+                            <div class="row mb-2">
+                                <div class="col-6">
+                                    <small class="instance-info-label">鍚嶇О</small>
+                                    <div class="instance-info-value">${escapeHtml(instance.name || '-')}</div>
+                                </div>
+                                <div class="col-6">
+                                    <small class="instance-info-label">PLC鍨嬪彿</small>
+                                    <div class="instance-info-value">${plcTypeText}</div>
+                                </div>
+                            </div>
+                            <div class="row mb-2">
+                                <div class="col-6">
+                                    <small class="instance-info-label">绔彛</small>
+                                    <div class="instance-info-value">${instance.port || '-'}</div>
+                                </div>
+                                <div class="col-6">
+                                    <small class="instance-info-label">瀹㈡埛绔�</small>
+                                    <div class="instance-info-value">
+                                        <i class="bi bi-people-fill me-1"></i>${instance.clientCount || 0}
+                                    </div>
+                                </div>
+                            </div>
+                            ${instance.startTime ? `
+                            <div class="row">
+                                <div class="col-12">
+                                    <small class="instance-info-label">鍚姩鏃堕棿</small>
+                                    <div class="instance-info-value small">${formatDate(instance.startTime)}</div>
+                                </div>
+                            </div>
+                            ` : ''}
+                            ${instance.errorMessage ? `
+                            <div class="alert alert-danger alert-sm mt-2 mb-0 py-2 small">
+                                <i class="bi bi-exclamation-triangle-fill me-1"></i>${escapeHtml(instance.errorMessage)}
+                            </div>
+                            ` : ''}
+                        </div>
+                    </div>
+                    <div class="card-footer bg-white">
+                        <div class="action-buttons d-flex gap-2">
+                            ${isRunning ? `
+                                <button class="btn btn-warning btn-sm flex-fill" onclick="stopInstance('${instance.instanceId}')" ${instance.status === 'Stopping' ? 'disabled' : ''}>
+                                    <i class="bi bi-stop-fill me-1"></i>鍋滄
+                                </button>
+                            ` : ''}
+                            ${isStopped ? `
+                                <button class="btn btn-success btn-sm flex-fill" onclick="startInstance('${instance.instanceId}')" ${instance.status === 'Starting' ? 'disabled' : ''}>
+                                    <i class="bi bi-play-fill me-1"></i>鍚姩
+                                </button>
+                            ` : ''}
+                            <a href="/Details?id=${encodeURIComponent(instance.instanceId)}" class="btn btn-info btn-sm text-white flex-fill">
+                                <i class="bi bi-info-circle-fill me-1"></i>璇︽儏
+                            </a>
+                            <a href="/Edit?id=${encodeURIComponent(instance.instanceId)}" class="btn btn-primary btn-sm flex-fill">
+                                <i class="bi bi-pencil-fill me-1"></i>缂栬緫
+                            </a>
+                            <button class="btn btn-danger btn-sm" onclick="deleteInstance('${instance.instanceId}')">
+                                <i class="bi bi-trash-fill"></i>
+                            </button>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        `;
+    }
+
+    function getStatusClass(status) {
+        const map = {
+            'Stopped': 'status-stopped',
+            'Starting': 'status-starting',
+            'Running': 'status-running',
+            'Stopping': 'status-stopping',
+            'Error': 'status-error'
+        };
+        return map[status] || '';
+    }
+
+    async function startInstance(id) {
+        confirmAction(`纭畾瑕佸惎鍔ㄥ疄渚� "${id}" 鍚�?`, async () => {
+            try {
+                await apiCall(`${API_BASE_URL}/SimulatorInstances/${encodeURIComponent(id)}/start`, {
+                    method: 'POST'
+                });
+                showToast(`瀹炰緥 "${id}" 鍚姩鍛戒护宸插彂閫乣, 'success');
+                setTimeout(() => loadInstances(), 1000);
+            } catch (error) {
+                console.error('Failed to start instance:', error);
+            }
+        });
+    }
+
+    async function stopInstance(id) {
+        confirmAction(`纭畾瑕佸仠姝㈠疄渚� "${id}" 鍚�?`, async () => {
+            try {
+                await apiCall(`${API_BASE_URL}/SimulatorInstances/${encodeURIComponent(id)}/stop`, {
+                    method: 'POST'
+                });
+                showToast(`瀹炰緥 "${id}" 鍋滄鍛戒护宸插彂閫乣, 'success');
+                setTimeout(() => loadInstances(), 1000);
+            } catch (error) {
+                console.error('Failed to stop instance:', error);
+            }
+        });
+    }
+
+    async function deleteInstance(id) {
+        confirmAction(`纭畾瑕佸垹闄ゅ疄渚� "${id}" 鍚�?姝ゆ搷浣滀笉鍙挙閿�!`, async () => {
+            try {
+                await apiCall(`${API_BASE_URL}/SimulatorInstances/${encodeURIComponent(id)}?deleteConfig=true`, {
+                    method: 'DELETE'
+                });
+                showToast(`瀹炰緥 "${id}" 宸插垹闄, 'success');
+                loadInstances();
+            } catch (error) {
+                console.error('Failed to delete instance:', error);
+            }
+        });
+    }
+
+    function escapeHtml(text) {
+        const div = document.createElement('div');
+        div.textContent = text;
+        return div.innerHTML;
+    }
+
+    // Stop auto-refresh when page is hidden
+    document.addEventListener('visibilitychange', function () {
+        if (document.hidden) {
+            stopAutoRefresh();
+        } else {
+            startAutoRefresh(loadInstances, 5000);
+        }
+    });
+</script>
+}
diff --git a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Index.cshtml.cs b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Index.cshtml.cs
index 76a2461..05e677f 100644
--- a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Index.cshtml.cs
+++ b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Index.cshtml.cs
@@ -1,8 +1,10 @@
-锘縰sing Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.RazorPages;
+锘縰sing Microsoft.AspNetCore.Mvc.RazorPages;
 
 namespace WIDESEAWCS_S7Simulator.Web.Pages;
 
+/// <summary>
+/// 瀹炰緥鍒楄〃椤�
+/// </summary>
 public class IndexModel : PageModel
 {
     private readonly ILogger<IndexModel> _logger;
@@ -14,6 +16,6 @@
 
     public void OnGet()
     {
-
+        _logger.LogInformation("Loading instance list page");
     }
 }
diff --git a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Shared/_Layout.cshtml b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Shared/_Layout.cshtml
index 09f6909..911de9c 100644
--- a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Shared/_Layout.cshtml
+++ b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Shared/_Layout.cshtml
@@ -1,18 +1,26 @@
-锘�<!DOCTYPE html>
-<html lang="en">
+<!DOCTYPE html>
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>@ViewData["Title"] - WIDESEAWCS_S7Simulator.Web</title>
-    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
+    <title>@ViewData["Title"] - S7 PLC Simulator</title>
+
+    <!-- Bootstrap 5 CSS -->
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
+
+    <!-- Bootstrap Icons -->
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
+
+    <!-- Custom CSS -->
     <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
-    <link rel="stylesheet" href="~/WIDESEAWCS_S7Simulator.Web.styles.css" asp-append-version="true" />
 </head>
 <body>
     <header>
-        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
-            <div class="container">
-                <a class="navbar-brand" asp-area="" asp-page="/Index">WIDESEAWCS_S7Simulator.Web</a>
+        <nav class="navbar navbar-expand-sm navbar-dark bg-primary shadow-sm mb-4">
+            <div class="container-fluid">
+                <a class="navbar-brand" asp-page="/Index">
+                    <i class="bi bi-cpu-fill me-2"></i>S7 PLC Simulator
+                </a>
                 <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                         aria-expanded="false" aria-label="Toggle navigation">
                     <span class="navbar-toggler-icon"></span>
@@ -20,32 +28,37 @@
                 <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                     <ul class="navbar-nav flex-grow-1">
                         <li class="nav-item">
-                            <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
-                        </li>
-                        <li class="nav-item">
-                            <a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
+                            <a class="nav-link" asp-page="/Index">
+                                <i class="bi bi-house-door me-1"></i>瀹炰緥鍒楄〃
+                            </a>
                         </li>
                     </ul>
                 </div>
             </div>
         </nav>
     </header>
-    <div class="container">
+
+    <div class="container-fluid px-4">
         <main role="main" class="pb-3">
             @RenderBody()
         </main>
     </div>
 
-    <footer class="border-top footer text-muted">
-        <div class="container">
-            &copy; 2026 - WIDESEAWCS_S7Simulator.Web - <a asp-area="" asp-page="/Privacy">Privacy</a>
+    <footer class="border-top py-3 mt-4 bg-light">
+        <div class="container-fluid text-center text-muted">
+            <small>&copy; 2026 - S7 PLC Simulator Management UI</small>
         </div>
     </footer>
 
-    <script src="~/lib/jquery/dist/jquery.min.js"></script>
-    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
+    <!-- Bootstrap 5 JS Bundle -->
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
+
+    <!-- Alpine.js -->
+    <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
+
+    <!-- Custom JS -->
     <script src="~/js/site.js" asp-append-version="true"></script>
 
     @await RenderSectionAsync("Scripts", required: false)
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Shared/_ValidationScriptsPartial.cshtml b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Shared/_ValidationScriptsPartial.cshtml
index 5a16d80..b94c97f 100644
--- a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Shared/_ValidationScriptsPartial.cshtml
+++ b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Shared/_ValidationScriptsPartial.cshtml
@@ -1,2 +1,14 @@
-锘�<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
-<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
+锘�<environment include="Development">
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.5/jquery.validate.min.js"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.12/jquery.validate.unobtrusive.min.js"></script>
+</environment>
+<environment exclude="Development">
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.5/jquery.validate.min.js"
+            asp-fallback-src="~/lib/jquery-validation/dist/jquery.validate.min.js"
+            asp-fallback-test="window.jQuery && window.jQuery.validator">
+    </script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.12/jquery.validate.unobtrusive.min.js"
+            asp-fallback-src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
+            asp-fallback-test="window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive">
+    </script>
+</environment>
diff --git a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/_ViewImports.cshtml b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/_ViewImports.cshtml
index 676b6dd..bac26ae 100644
--- a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/_ViewImports.cshtml
+++ b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/_ViewImports.cshtml
@@ -1,3 +1,5 @@
 锘緻using WIDESEAWCS_S7Simulator.Web
+@using WIDESEAWCS_S7Simulator.Core.Entities
+@using WIDESEAWCS_S7Simulator.Core.Enums
 @namespace WIDESEAWCS_S7Simulator.Web.Pages
 @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
diff --git a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/wwwroot/css/site.css b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/wwwroot/css/site.css
index f27e5ad..e43a1f9 100644
--- a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/wwwroot/css/site.css
+++ b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/wwwroot/css/site.css
@@ -1,18 +1,126 @@
 html {
-  font-size: 14px;
-}
-
-@media (min-width: 768px) {
-  html {
-    font-size: 16px;
-  }
-}
-
-html {
   position: relative;
   min-height: 100%;
 }
 
 body {
   margin-bottom: 60px;
+  background-color: #f8f9fa;
+}
+
+/* Status Badge Colors */
+.status-stopped {
+  background-color: #6c757d;
+}
+
+.status-starting {
+  background-color: #0dcaf0;
+}
+
+.status-running {
+  background-color: #198754;
+}
+
+.status-stopping {
+  background-color: #ffc107;
+  color: #000;
+}
+
+.status-error {
+  background-color: #dc3545;
+}
+
+/* Instance Card Styles */
+.instance-card {
+  transition: transform 0.2s, box-shadow 0.2s;
+  border-left: 4px solid transparent;
+}
+
+.instance-card:hover {
+  transform: translateY(-2px);
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+}
+
+.instance-card.status-running {
+  border-left-color: #198754;
+}
+
+.instance-card.status-stopped {
+  border-left-color: #6c757d;
+}
+
+.instance-card.status-error {
+  border-left-color: #dc3545;
+}
+
+.instance-card.status-starting,
+.instance-card.status-stopping {
+  border-left-color: #ffc107;
+}
+
+/* Instance Info Section */
+.instance-info-label {
+  font-weight: 600;
+  color: #495057;
+}
+
+.instance-info-value {
+  color: #212529;
+}
+
+/* Action Buttons */
+.action-buttons .btn {
+  min-width: 80px;
+}
+
+/* Loading Spinner */
+.spinner-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0, 0, 0, 0.5);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 9999;
+}
+
+/* Memory Region Table */
+.memory-table th {
+  background-color: #e9ecef;
+  font-weight: 600;
+}
+
+/* Client Table */
+.client-table {
+  font-size: 0.9rem;
+}
+
+/* Toast Container */
+.toast-container {
+  position: fixed;
+  top: 20px;
+  right: 20px;
+  z-index: 9999;
+}
+
+/* Empty State */
+.empty-state {
+  text-align: center;
+  padding: 60px 20px;
+  color: #6c757d;
+}
+
+.empty-state i {
+  font-size: 4rem;
+  margin-bottom: 20px;
+  opacity: 0.5;
+}
+
+/* Form Styles */
+.form-label.required::after {
+  content: " *";
+  color: #dc3545;
 }
\ No newline at end of file
diff --git a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/wwwroot/js/site.js b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/wwwroot/js/site.js
index ac49c18..4754930 100644
--- a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/wwwroot/js/site.js
+++ b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/wwwroot/js/site.js
@@ -1,4 +1,192 @@
 锘�// Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
 // for details on configuring this project to bundle and minify static web assets.
 
-// Write your JavaScript code.
+// API Base URL
+const API_BASE_URL = '/api';
+
+// Show toast notification
+function showToast(message, type = 'info') {
+    const toastContainer = document.getElementById('toastContainer') || createToastContainer();
+    const toastId = 'toast-' + Date.now();
+
+    const bgClass = {
+        'success': 'bg-success',
+        'danger': 'bg-danger',
+        'warning': 'bg-warning',
+        'info': 'bg-info',
+        'error': 'bg-danger'
+    }[type] || 'bg-info';
+
+    const toastHtml = `
+        <div id="${toastId}" class="toast align-items-center text-white ${bgClass} border-0" role="alert" aria-live="assertive" aria-atomic="true">
+            <div class="d-flex">
+                <div class="toast-body">
+                    ${message}
+                </div>
+                <button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
+            </div>
+        </div>
+    `;
+
+    toastContainer.insertAdjacentHTML('beforeend', toastHtml);
+    const toastElement = document.getElementById(toastId);
+    const toast = new bootstrap.Toast(toastElement, { delay: 3000 });
+    toast.show();
+
+    toastElement.addEventListener('hidden.bs.toast', () => {
+        toastElement.remove();
+    });
+}
+
+function createToastContainer() {
+    const container = document.createElement('div');
+    container.id = 'toastContainer';
+    container.className = 'toast-container';
+    document.body.appendChild(container);
+    return container;
+}
+
+// Show loading spinner
+function showLoading() {
+    const existingOverlay = document.getElementById('loadingOverlay');
+    if (existingOverlay) return;
+
+    const overlay = document.createElement('div');
+    overlay.id = 'loadingOverlay';
+    overlay.className = 'spinner-overlay';
+    overlay.innerHTML = `
+        <div class="spinner-border text-light" style="width: 3rem; height: 3rem;" role="status">
+            <span class="visually-hidden">Loading...</span>
+        </div>
+    `;
+    document.body.appendChild(overlay);
+}
+
+// Hide loading spinner
+function hideLoading() {
+    const overlay = document.getElementById('loadingOverlay');
+    if (overlay) {
+        overlay.remove();
+    }
+}
+
+// API call helper
+async function apiCall(url, options = {}) {
+    try {
+        showLoading();
+        const response = await fetch(url, {
+            ...options,
+            headers: {
+                'Content-Type': 'application/json',
+                ...options.headers
+            }
+        });
+
+        if (!response.ok) {
+            const errorData = await response.json().catch(() => ({ error: response.statusText }));
+            throw new Error(errorData.error || `HTTP ${response.status}: ${response.statusText}`);
+        }
+
+        return await response.json();
+    } catch (error) {
+        showToast(error.message, 'error');
+        throw error;
+    } finally {
+        hideLoading();
+    }
+}
+
+// Format date
+function formatDate(dateString) {
+    if (!dateString) return '-';
+    const date = new Date(dateString);
+    return date.toLocaleString('zh-CN', {
+        year: 'numeric',
+        month: '2-digit',
+        day: '2-digit',
+        hour: '2-digit',
+        minute: '2-digit',
+        second: '2-digit'
+    });
+}
+
+// Get status badge class
+function getStatusBadgeClass(status) {
+    const statusMap = {
+        'Stopped': 'status-stopped',
+        'Starting': 'status-starting',
+        'Running': 'status-running',
+        'Stopping': 'status-stopping',
+        'Error': 'status-error'
+    };
+    return statusMap[status] || 'bg-secondary';
+}
+
+// Get status text
+function getStatusText(status) {
+    const statusMap = {
+        'Stopped': '宸插仠姝�',
+        'Starting': '鍚姩涓�',
+        'Running': '杩愯涓�',
+        'Stopping': '鍋滄涓�',
+        'Error': '閿欒'
+    };
+    return statusMap[status] || status;
+}
+
+// Get PLC type text
+function getPlcTypeText(plcType) {
+    const plcTypeMap = {
+        'S7200Smart': 'S7-200 Smart',
+        'S71200': 'S7-1200',
+        'S71500': 'S7-1500',
+        'S7300': 'S7-300',
+        'S7400': 'S7-400'
+    };
+    return plcTypeMap[plcType] || plcType;
+}
+
+// Confirm action
+function confirmAction(message, callback) {
+    if (confirm(message)) {
+        callback();
+    }
+}
+
+// Redirect with delay
+function redirectWithDelay(url, delay = 1500) {
+    setTimeout(() => {
+        window.location.href = url;
+    }, delay);
+}
+
+// Format bytes
+function formatBytes(bytes, decimals = 2) {
+    if (bytes === 0) return '0 Bytes';
+    const k = 1024;
+    const dm = decimals < 0 ? 0 : decimals;
+    const sizes = ['Bytes', 'KB', 'MB', 'GB'];
+    const i = Math.floor(Math.log(bytes) / Math.log(k));
+    return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
+}
+
+// Auto-refresh interval handler
+let refreshInterval = null;
+
+function startAutoRefresh(callback, intervalMs = 5000) {
+    stopAutoRefresh();
+    callback(); // Initial call
+    refreshInterval = setInterval(callback, intervalMs);
+}
+
+function stopAutoRefresh() {
+    if (refreshInterval) {
+        clearInterval(refreshInterval);
+        refreshInterval = null;
+    }
+}
+
+// Page unload handler
+window.addEventListener('beforeunload', () => {
+    stopAutoRefresh();
+});

--
Gitblit v1.9.3