# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Build Commands ```bash # Build entire solution dotnet build WIDESEAWCS_Server.sln # Build and run server cd WIDESEAWCS_Server dotnet run # Run tests cd WIDESEAWCS_Tests dotnet test ``` ## Architecture Overview This is a **WCS (Warehouse Control System)** built with ASP.NET Core 6.0, using: - **Autofac** for DI with automatic service discovery via `IDependency` marker interface - **Quartz.NET** for scheduled job execution (device communication loops) - **SqlSugar ORM** for database access - **Redis** (via `WIDESEAWCS_RedisService`) for distributed caching with L1+L2 hybrid pattern - **StackExchange.Redis** for Redis operations - **TCP Socket Server** for real-time device communication - **HslCommunication** library for PLC/hardware communication ## Project Structure ``` WIDESEAWCS_Server/ # Main ASP.NET Core API server WIDESEAWCS_Core/ # Core infrastructure: base classes, DI, extensions, middleware WIDESEAWCS_Model/ # Data models and DTOs WIDESEAWCS_Communicator/ # Hardware communication drivers (Siemens, Omron, Modbus, etc.) WIDESEAWCS_QuartzJob/ # Job scheduling infrastructure and device abstractions WIDESEAWCS_Tasks/ # Quartz job implementations (device communication loops) WIDESEAWCS_RedisService/ # Redis services: Cache, Lock, Counter, PubSub, etc. WIDESEAWCS_*Repository/ # Data access layer implementations WIDESEAWCS_*Service/ # Business service layer WIDESEAWCS_Tests/ # Unit tests ``` ## Dependency Injection - IDependency Pattern Services are **automatically registered** with Autofac by implementing the empty `IDependency` marker interface: ```csharp // In WIDESEAWCS_Core/IDependency.cs public interface IDependency { } // Your service gets auto-registered public class MyService : IDependency // Automatically registered as scoped { // ... } ``` Registration happens in `AutofacModuleRegister` which scans all project assemblies for `IDependency` implementations. **Important**: When adding services to `IServiceCollection` (e.g., in `Program.cs`), they can be overridden by Autofac's registrations. Use `Remove()` to replace existing registrations: ```csharp // In RedisServiceSetup.cs - removes MemoryCacheService before adding HybridCacheService var existing = services.FirstOrDefault(d => d.ServiceType == typeof(ICacheService)); if (existing != null) services.Remove(existing); ``` ## Caching - ICacheService The system uses a **hybrid L1 (Memory) + L2 (Redis)** cache pattern via `ICacheService`. Three implementations exist: - `MemoryCacheService` - Memory only - `RedisCacheService` - Redis only - `HybridCacheService` - L1+L2 with fallback (default when Redis enabled) **Common methods**: - `Add/AddObject` - Add cache - `Get/Get` - Retrieve cached values - `Remove` - Delete single key - `RemoveByPrefix/RemoveByPattern` - Bulk delete by pattern - `GetOrAdd` - Retrieve or add with factory - `TryAdd/TryUpdate/TryUpdateIfChanged` - ConcurrentDictionary-style operations **Configuration** in `appsettings.json`: ```json "RedisConfig": { "Enabled": true, "ConnectionString": "127.0.0.1:6379,password=P@ssw0rd,...", "KeyPrefix": "wcs:" } ``` ## Quartz Jobs - Device Communication Jobs inherit from `JobBase` and implement Quartz's `IJob`: ```csharp public class MyDeviceJob : JobBase, IJob { public async Task Execute(IJobExecutionContext context) { ExecuteJob(context, async () => { // Job logic here WriteDebug("MyDevice", "Debug message"); WriteInfo("MyDevice", "Info message"); }); } } ``` Jobs are registered dynamically via `SchedulerCenterServer` using device info from `Dt_DeviceInfo` table. **Device types**: - `IStackerCrane` - Stacker cranes - `IConveyorLine` - Conveyor lines - `IShuttleCar` - Shuttle cars - `IRobot` - Robot cranes ## Hardware Communication Communicator classes wrap the `HslCommunication` library: - `SiemensS7Communicator` / `SiemensS7200SmartCommunicator` - Siemens PLCs - `OmronEtherNetCommunicator` - Omron PLCs - `ModbusTcpCommunicator` - Modbus TCP - `SerialPortCommunicator` - Serial port devices ## TCP Socket Server The `TcpSocketServer` (port 2000) handles real-time device communication: - Managed as a Singleton with `SocketServerHostedService` - Client connections stored in `ConcurrentDictionary` - Messages handled via `OnDataReceived` event ## Robot Communication System The robot crane system uses a modular architecture with specialized components. **Components:** - `RobotClientManager` - Manages TCP client connections and subscriptions - `RobotStateManager` - Manages robot state cache with safe concurrent updates - `RobotMessageHandler` - Processes incoming TCP messages from robots - `RobotTaskProcessor` - Handles task execution and state transitions - `RobotBarcodeGenerator` - Generates tray/barcode identifiers **Task Types** (from `RobotTaskTypeEnum`): - `GroupPallet (500)` - 组盘任务 - `ChangePallet (510)` - 换盘任务 - `SplitPallet (520)` - 拆盘任务 **State Flow:** 1. Robot connects via TCP → ClientManager tracks connection 2. Job polls for tasks → TaskProcessor gets pending tasks 3. Message received → MessageHandler parses and updates state 4. State transitions → TaskProcessor sends commands back to robot ## Common Constants **Communication Timeouts** (`CommunicationConst`): - `WaitIntervalMs: 500` - Device wait interval - `WaitTimeoutBaseMs: 6000` - Timeout base - `WaitTotalTimeoutMs: 60000` - Total timeout (10 × base) - `PingIntervalMs: 100` - Ping check interval - `HttpDefaultTimeoutSeconds: 60` - HTTP timeout **System Integration URLs** (`BaseAPI`): - `WMSBaseUrl: "http://localhost:9291/api/"` - WMS system - `WCSBaseUrl: "http://localhost:9292/api/"` - WCS system (this server) - `MESBaseUrl: "http://localhost:9293/api/"` - MES system - `ERPBaseUrl: "http://localhost:9294/api/"` - ERP system **Redis Cache Prefixes** (`RedisPrefix`): - `System: "System"` - System-level cache - `User: "User"` - User-specific cache - `Code: "Code"` - Code/configuration cache Use these prefixes with `ICacheService.RemoveByPrefix()` for bulk cache invalidation. ## Configuration Settings Key settings in `appsettings.json`: - `"urls": "http://*:9292"` - Server port - `"QuartzJobAutoStart": true` - Auto-start scheduled jobs - `"SocketServer:Enabled": true` - Enable TCP server - `"RedisConfig:Enabled": true` - Enable Redis caching - `"LogAOPEnable": false` - Enable AOP logging - `"DBType": "SqlServer"` - Database type ## Service Layer Pattern Services follow a layered pattern: - **Interface** in `WIDESEAWCS_IService/` (e.g., `ITaskInfoService`) - **Implementation** in `WIDESEAWCS_Service/` (e.g., `TaskInfoService`) - Both implement `IDependency` for auto-registration ## Base Classes - `ServiceBase` - Base service with CRUD operations - `RepositoryBase` - Base repository with SqlSugar ORM - `ApiBaseController` - Base API controller with common functionality - `JobBase` - Base Quartz job with logging helpers ## Adding New Features 1. **New Service**: Create interface in `I*Service/` and class in `*Service/`, implement `IDependency` 2. **New Job**: Inherit from `JobBase` and `IJob` in `WIDESEAWCS_Tasks/` 3. **New Device Type**: Add interface in `WIDESEAWCS_QuartzJob/Device/` and implement ## Important Notes - The application uses **CamelCase** JSON serialization - All services use **scoped** lifetime by default via Autofac - Redis connection uses **Lazy initialization** - first access triggers connection - Use `ConsoleHelper.WriteSuccessLine()` / `WriteErrorLine()` for console output in jobs - TCP Socket server runs independently of the HTTP API