# 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 ## 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