Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_ITaskInfoService/ITaskService.cs
@@ -61,6 +61,20 @@ WebResponseContent ReceiveWMSTask([NotNull] List<WMSTaskDTO> taskDTOs); /// <summary> /// æ¥æ¶WMSæå¨å建çä»»å¡ï¼å建WCSä»»å¡ /// </summary> /// <param name="taskDTOs">WMSä»»å¡å¯¹è±¡éå</param> /// <returns>è¿åå¤çç»æ</returns> WebResponseContent ReceiveManualTask([NotNull] List<WMSTaskDTO> taskDTOs); /// <summary> /// æ¥è¯¢æå®èµ·ç¹å°åçæ°å»ºæå¨å ¥åºä»»å¡ /// </summary> /// <param name="sourceAddress">èµ·ç¹å°å</param> /// <returns>ä»»å¡å表</returns> Dt_Task QueryManualInboundTask(string sourceAddress); /// <summary> /// æ ¹æ®æçå·ãèµ·å§å°ååWMS请æ±ä»»å¡ /// </summary> /// <param name="palletCode">æçå·</param> Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Controllers/Task/TaskController.cs
@@ -25,6 +25,12 @@ return Service.ReceiveWMSTask(taskDTOs); } [HttpPost, Route("ReceiveManualTask"), AllowAnonymous] public WebResponseContent ReceiveManualTask([FromBody] List<WMSTaskDTO> taskDTOs) { return Service.ReceiveManualTask(taskDTOs); } [HttpPost, HttpGet(), Route("UpdateTaskExceptionMessage")] public WebResponseContent UpdateTaskExceptionMessage(int taskNum, string message) { Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs
@@ -644,6 +644,41 @@ { return BaseDal.QueryFirst(x => x.TaskNum == taskNum); } /// <summary> /// æ¥æ¶WMSæå¨å建çä»»å¡ï¼å建WCSä»»å¡ /// </summary> /// <param name="taskDTOs">WMSä»»å¡å¯¹è±¡éå</param> /// <returns>è¿åå¤çç»æ</returns> public WebResponseContent ReceiveManualTask([NotNull] List<WMSTaskDTO> taskDTOs) { WebResponseContent content = new WebResponseContent(); try { // è°ç¨ ReceiveWMSTask å建 WCS ä»»å¡ content = ReceiveWMSTask(taskDTOs); return content; } catch (Exception ex) { content = WebResponseContent.Instance.Error($"æå¨ä»»å¡æ¥æ¶é误,é误信æ¯:{ex.Message}"); return content; } } /// <summary> /// æ¥è¯¢æå®èµ·ç¹å°åçæ°å»ºæå¨å ¥åºä»»å¡ /// </summary> /// <param name="sourceAddress">èµ·ç¹å°å</param> /// <returns>ä»»å¡å®ä½</returns> public Dt_Task QueryManualInboundTask(string sourceAddress) { return BaseDal.QueryFirst(x => x.TaskType == (int)TaskInboundTypeEnum.Inbound && x.TaskStatus == (int)TaskInStatusEnum.InNew && x.SourceAddress == sourceAddress && x.Creater == "WMS"); } } public enum ConveyorLineDBNameNew Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/CommonConveyorLineNewJob.cs
@@ -14,6 +14,7 @@ using WIDESEAWCS_Model.Models; using WIDESEAWCS_QuartzJob; using WIDESEAWCS_QuartzJob.Service; using ManualInboundTaskHandler = WIDESEAWCS_Tasks.ConveyorLineNewJob.ManualInbound.ManualInboundTaskHandler; namespace WIDESEAWCS_Tasks { @@ -212,6 +213,22 @@ // 妿 WCS_ACK 为 1ï¼å æ¸ é¤ï¼è¡¨ç¤ºå¤çè¿ä¸ä¸æ¬¡è¯·æ±ï¼ if (command.WCS_ACK == 1) conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)0, childDeviceCode); // å¤çæå¨å ¥åºä»»å¡ï¼èµ·ç¹ä¸ºçº¿ä½ç¹ä½çä»»å¡ï¼ try { var task = _taskService.QueryManualInboundTask(childDeviceCode); if (task != null) { var handler = new ManualInboundTaskHandler(_taskService); handler.WriteTaskToPlc(conveyorLine, childDeviceCode, task); } } catch (Exception ex) { _logger.LogError(ex, "å¤çæå¨å ¥åºä»»å¡å¼å¸¸"); QuartzLogger.Error($"å¤çæå¨å ¥åºä»»å¡å¼å¸¸: {ex.Message}", "CommonConveyorLineNewJob", ex); } continue; } Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs
@@ -210,15 +210,14 @@ // æ´æ°ä»»å¡ç¶æå°ä¸ä¸é¶æ®µï¼é常æ¯å®æï¼ if (_taskService.UpdateTaskStatusToNext(task).Status) { } // åå¤ ACK 确认 conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, childDeviceCode); _logger.LogInformation("ConveyorLineInFinishï¼å ¥åºå®æï¼ä»»å¡å·: {TaskNum}ï¼å设å¤: {ChildDeviceCode}", task.TaskNum, childDeviceCode); QuartzLogger.Info($"å ¥åºå®æï¼ä»»å¡å·: {task.TaskNum}", conveyorLine.DeviceCode); } } } /// <summary> Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ManualInbound/ManualInboundTaskHandler.cs
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,73 @@ using WIDESEAWCS_Core.LogHelper; using WIDESEAWCS_ITaskInfoService; using WIDESEAWCS_Model.Models; using WIDESEAWCS_QuartzJob; using WIDESEAWCS_QuartzJob.ConveyorLine; namespace WIDESEAWCS_Tasks.ConveyorLineNewJob.ManualInbound { /// <summary> /// æå¨å ¥åºä»»å¡å¤çå¨ /// </summary> /// <remarks> /// è´è´£å¤çæå¨å建çå ¥åºä»»å¡ï¼å½PLC请æ±å ¥åºæ¶ï¼æ ¹æ®childDeviceCodeæ¥æ¾ä»»å¡å¹¶åå ¥PLCã /// </remarks> public class ManualInboundTaskHandler { /// <summary> /// ä»»å¡æå¡ /// </summary> private readonly ITaskService _taskService; /// <summary> /// æé 彿° /// </summary> /// <param name="taskService">任塿å¡</param> public ManualInboundTaskHandler(ITaskService taskService) { _taskService = taskService; } /// <summary> /// åå ¥æå¨å ¥åºä»»å¡å°PLC /// </summary> /// <param name="conveyorLine">è¾é线设å¤å¯¹è±¡</param> /// <param name="childDeviceCode">å设å¤ç¼ç </param> /// <param name="task">ä»»å¡å®ä½</param> public void WriteTaskToPlc(CommonConveyorLine conveyorLine, string childDeviceCode, Dt_Task task) { if (conveyorLine == null || string.IsNullOrEmpty(childDeviceCode) || task == null) { QuartzLogger.Error("ManualInboundTaskHandler.WriteTaskToPlc: åæ°ä¸ºç©º", "ManualInbound"); return; } try { // åå ¥ä»»å¡å· conveyorLine.SetValue(ConveyorLineDBNameNew.TaskNo, (short)task.TaskNum, childDeviceCode); // åå ¥èµ·å§å°å conveyorLine.SetValue(ConveyorLineDBNameNew.Source, short.Parse(task.SourceAddress ?? "0"), childDeviceCode); // åå ¥ç®æ å°å conveyorLine.SetValue(ConveyorLineDBNameNew.Target, short.Parse(task.TargetAddress ?? "0"), childDeviceCode); // æ´æ°ä»»å¡ç¶æå°ä¸ä¸é¶æ®µ var updateResult = _taskService.UpdateTaskStatusToNext(task); if (!updateResult.Status) { QuartzLogger.Error($"ManualInboundTaskHandler: æ´æ°ä»»å¡ç¶æå¤±è´¥ï¼ä»»å¡å·ã{task.TaskNum}ãï¼é误信æ¯:{updateResult.Message}", conveyorLine.DeviceCode); return; } // åå ¥ACKæ å¿ conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, childDeviceCode); QuartzLogger.Info($"ManualInboundTaskHandler: æå¨ä»»å¡åå ¥PLCæåï¼ä»»å¡å·ã{task.TaskNum}ãï¼æºå°åã{task.SourceAddress}ã", conveyorLine.DeviceCode); } catch (Exception ex) { QuartzLogger.Error($"ManualInboundTaskHandler: åå ¥è¾é线任å¡å¼å¸¸ï¼é误信æ¯:{ex.Message}", "ManualInbound"); } } } } Code/WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/addManualTask.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,93 @@ <template> <div> <vol-box v-model="showBox" :lazy="true" width="500px" :padding="15" title="æå¨å建任å¡" > <el-form :model="formData" ref="form" label-width="100px"> <el-form-item label="ä»»å¡ç±»å" prop="taskType" required> <el-select v-model="formData.taskType" placeholder="è¯·éæ©ä»»å¡ç±»å"> <el-option label="å ¥åº" value="å ¥åº"></el-option> <el-option label="åºåº" value="åºåº"></el-option> <el-option label="ç§»åº" value="ç§»åº"></el-option> </el-select> </el-form-item> <el-form-item label="èµ·ç¹å°å" prop="sourceAddress" required> <el-input v-model="formData.sourceAddress" placeholder="请è¾å ¥èµ·ç¹å°å"></el-input> </el-form-item> <el-form-item label="ç»ç¹å°å" prop="targetAddress" required> <el-input v-model="formData.targetAddress" placeholder="请è¾å ¥ç»ç¹å°å"></el-input> </el-form-item> <el-form-item label="æ¡ç " prop="barcode" required> <el-input v-model="formData.barcode" placeholder="请è¾å ¥æ¡ç "></el-input> </el-form-item> <el-form-item label="ä»åºID" prop="warehouseId" required> <el-input v-model="formData.warehouseId" placeholder="请è¾å ¥ä»åºID"></el-input> </el-form-item> <el-form-item label="ä¼å 级"> <el-input v-model="formData.grade" readonly></el-input> </el-form-item> </el-form> <template #footer> <el-button type="primary" size="small" @click="submit">ç¡®å®</el-button> <el-button type="danger" size="small" @click="showBox = false">å ³é</el-button> </template> </vol-box> </div> </template> <script> import VolBox from "@/components/basic/VolBox.vue"; export default { components: { VolBox }, data() { return { showBox: false, formData: { taskType: "", sourceAddress: "", targetAddress: "", barcode: "", warehouseId: "", grade: 1, }, }; }, methods: { open() { this.showBox = true; this.resetForm(); }, resetForm() { this.formData = { taskType: "", sourceAddress: "", targetAddress: "", barcode: "", warehouseId: "", grade: 1, }; }, submit() { if (!this.formData.taskType) return this.$message.error("è¯·éæ©ä»»å¡ç±»å"); if (!this.formData.sourceAddress) return this.$message.error("请è¾å ¥èµ·ç¹å°å"); if (!this.formData.targetAddress) return this.$message.error("请è¾å ¥ç»ç¹å°å"); if (!this.formData.barcode) return this.$message.error("请è¾å ¥æ¡ç "); if (!this.formData.warehouseId) return this.$message.error("请è¾å ¥ä»åºID"); this.http .post("/api/Task/CreateManualTask", this.formData, "æ°æ®å¤çä¸...") .then((res) => { if (!res.status) return this.$message.error(res.message); this.$message.success("ä»»å¡å建æå"); this.showBox = false; this.$emit("parentCall", ($vue) => { $vue.refresh(); }); }); }, }, }; </script> Code/WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js
@@ -1,11 +1,12 @@ //æ¤jsæä»¶æ¯ç¨æ¥èªå®ä¹æ©å±ä¸å¡ä»£ç ï¼å¯ä»¥æ©å±ä¸äºèªå®ä¹é¡µé¢æè éæ°é ç½®çæç代ç import addManualTask from './extend/addManualTask.vue' let extension = { components: { //æ¥è¯¢ç颿©å±ç»ä»¶ gridHeader: '', gridBody: '', gridBody: addManualTask, gridFooter: '', //æ°å»ºãç¼è¾å¼¹åºæ¡æ©å±ç»ä»¶ modelHeader: '', @@ -17,6 +18,17 @@ methods: { //ä¸é¢è¿äºæ¹æ³å¯ä»¥ä¿çä¹å¯ä»¥å é¤ onInit() { //æ·»å "æå¨å建任å¡"æé® this.buttons.push({ name: 'æå¨å建任å¡', icon: 'el-icon-plus', type: 'primary', value: 'ManualCreateTask', onClick: () => { this.$refs.gridBody.open(); } }); let TaskHandCancelBtn = this.buttons.find(x => x.value == 'TaskHandCancel'); if (TaskHandCancelBtn) { TaskHandCancelBtn.onClick = function () { @@ -93,4 +105,3 @@ } }; export default extension; Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/CreateManualTaskDto.cs
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,47 @@ using System.Text.Json.Serialization; using WIDESEA_Common.TaskEnum; namespace WIDESEA_DTO.Task { /// <summary> /// æå¨å建任å¡Dto /// </summary> public class CreateManualTaskDto { /// <summary> /// ä»»å¡ç±»åï¼å ¥åº/åºåº/ç§»åº /// </summary> [JsonPropertyName("taskType")] public string TaskType { get; set; } /// <summary> /// èµ·ç¹å°å /// </summary> [JsonPropertyName("sourceAddress")] public string SourceAddress { get; set; } /// <summary> /// ç»ç¹å°å /// </summary> [JsonPropertyName("targetAddress")] public string TargetAddress { get; set; } /// <summary> /// æ¡ç /// </summary> [JsonPropertyName("barcode")] public string Barcode { get; set; } /// <summary> /// ä»åºID /// </summary> [JsonPropertyName("warehouseId")] public int WarehouseId { get; set; } /// <summary> /// ä¼å 级ï¼é»è®¤1 /// </summary> [JsonPropertyName("grade")] public int Grade { get; set; } = 1; } } Code/WMS/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITaskService.cs
@@ -160,6 +160,13 @@ /// <returns></returns> Task<WebResponseContent> CreateRobotChangePalletTaskAsync(StockDTO stock); /// <summary> /// æå¨åå»ºä»»å¡ /// </summary> /// <param name="dto">æå¨å建任å¡åæ°</param> /// <returns></returns> Task<WebResponseContent> CreateManualTaskAsync(CreateManualTaskDto dto); #region æå·åºä»»å¡æ¨¡å /// <summary> Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs
@@ -876,6 +876,89 @@ } } /// <summary> /// æå¨åå»ºä»»å¡ /// </summary> /// <param name="dto">æå¨å建任å¡åæ°</param> /// <returns></returns> public async Task<WebResponseContent> CreateManualTaskAsync(CreateManualTaskDto dto) { try { // 1. æ ¹æ®ä»»å¡ç±»ååç¬¦ä¸²ç¡®å® TaskType å TaskStatus int taskType; int taskStatus; switch (dto.TaskType) { case "å ¥åº": taskType = TaskTypeEnum.Inbound.GetHashCode(); taskStatus = TaskInStatusEnum.InNew.GetHashCode(); break; case "åºåº": taskType = TaskTypeEnum.Outbound.GetHashCode(); taskStatus = TaskOutStatusEnum.OutNew.GetHashCode(); break; case "ç§»åº": taskType = TaskTypeEnum.Relocation.GetHashCode(); taskStatus = TaskRelocationStatusEnum.RelocationNew.GetHashCode(); break; default: return WebResponseContent.Instance.Error($"䏿¯æçä»»å¡ç±»å: {dto.TaskType}"); } // 2. çæä»»å¡å· int taskNum = await BaseDal.GetTaskNo(); // 3. æå»ºä»»å¡å®ä½ var task = new Dt_Task { TaskNum = taskNum, PalletCode = dto.Barcode, SourceAddress = dto.SourceAddress, TargetAddress = dto.TargetAddress, TaskType = taskType, TaskStatus = taskStatus, Grade = dto.Grade, WarehouseId = dto.WarehouseId, CurrentAddress = dto.SourceAddress, NextAddress = dto.TargetAddress, Creater = "manual", CreateDate = DateTime.Now, ModifyDate = DateTime.Now }; // 4. ä¿åå°æ°æ®åº var result = await BaseDal.AddDataAsync(task) > 0; if (!result) return WebResponseContent.Instance.Error("å建任å¡å¤±è´¥"); // 5. åéå° WCS var wmsTaskDto = new WMSTaskDTO { TaskNum = task.TaskNum, PalletCode = task.PalletCode, SourceAddress = task.SourceAddress, TargetAddress = task.TargetAddress, TaskType = task.TaskType, TaskStatus = task.TaskStatus, WarehouseId = task.WarehouseId }; var wcsResult = _httpClientHelper.Post<WebResponseContent>( "http://localhost:9292/api/Task/ReceiveManualTask", wmsTaskDto.ToJson()); if (!wcsResult.IsSuccess || !wcsResult.Data.Status) return WebResponseContent.Instance.Error($"ä»»å¡å·²å建ä½åéç»WCS失败: {wcsResult.Data?.Message}"); return WebResponseContent.Instance.OK($"æå¨åå»ºä»»å¡æåï¼ä»»å¡å·: {taskNum}"); } catch (Exception ex) { return WebResponseContent.Instance.Error($"æå¨å建任å¡å¼å¸¸: {ex.Message}"); } } #endregion WCSé»è¾å¤ç } } Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs
@@ -47,6 +47,17 @@ } /// <summary> /// æå¨åå»ºä»»å¡ /// </summary> /// <param name="dto">æå¨å建任å¡åæ°</param> /// <returns></returns> [HttpGet, HttpPost, Route("CreateManualTask"), AllowAnonymous] public async Task<WebResponseContent?> CreateManualTaskAsync([FromBody] CreateManualTaskDto dto) { return await Service.CreateManualTaskAsync(dto); } /// <summary> /// è·åå¯å ¥åºè´§ä½ /// </summary> /// <param name="taskDto"></param> Code/docs/superpowers/plans/2026-04-11-manual-task-creation-plan.md
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,434 @@ # æå¨å建任å¡åè½å®æ½è®¡å > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** å¨WMSçé¢ä¸æ·»å æå¨å建任å¡åè½ï¼æ¯æå ¥åº/åºåº/ç§»åºä¸ç§ç±»åï¼ä»»å¡åéè³WCSåï¼WCSå¤æèµ·ç¹ä¸ºçº¿ä½ç¹ä½(11068/11010/11001)æ¶åå ¥è¾é线任å¡ã **Architecture:** WMSå端Vueé¡µé¢ â WMSå端API â WCS ReceiveTask â WCS FlowService夿并åå ¥è¾é线PLC **Tech Stack:** .NET 6 (WMS/WCS Backend), Vue3 (WMS Frontend), MapsterMapper, SqlSugar --- ## æä»¶ç»æ | æä»¶ | èè´£ | |------|------| | `WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js` | æ·»å æå¨åå»ºä»»å¡æé®åå¤çé»è¾ | | `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/CreateManualTaskDto.cs` | **æ°å»º** - æå¨å建任å¡è¯·æ±DTO | | `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs` | æ·»å CreateManualTask æ¥å£ | | `WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs` | æ·»å CreateManualTaskAsync æ¹æ³ | | `WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs` | ReceiveWMSTask å·²æååé»è¾ï¼ç¡®è®¤å ¥å£æ£ç¡® | | `WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/InboundTaskFlowService.cs` | æ·»å 线ä½ç¹ä½å¤æåè¾é线任å¡åå ¥é»è¾ | --- ## WMS å端 ### Task 1: æ·»å æå¨åå»ºä»»å¡æé® **æä»¶:** - Modify: `WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js:16` - [ ] **Step 1: å¨ buttons.box æ·»å æå¨å建æé®** å¨ `task.js` ç `buttons: { view: [], box: [], detail: [] }` 䏿·»å ï¼ ```javascript buttons: { view: [], box: [ { value: 'CreateManualTask', label: 'æå¨å建任å¡', onClick: function () { // å¼¹åºæå¨å建任å¡å¯¹è¯æ¡ this.$refs.grid.openModel('Add'); } } ], detail: [] }, ``` - [ ] **Step 2: å¨ modelFooter æ·»å èªå®ä¹å¼¹çª** å¨ `components.modelFooter` æ·»å Vue 模æ¿ï¼å¦ææ¡æ¶æ¯æèªå®ä¹å¼¹çªå 容ï¼ï¼æä½¿ç¨æ¡æ¶å ç½®ç `openModel` é å `addBefore` å¤çã > **注æ:** å ·ä½çå¼¹çªå®ç°åå³äºå½ååç«¯æ¡æ¶çæ©å±æºå¶ãè¥å½å页é¢ä¸æ¯æèªå®ä¹å段ï¼åéè¦å¨ `task.vue` 䏿·»å æ°ç页é¢ç»ä»¶ï¼æéè¿ `modelBody` æ©å±èªå®ä¹è¡¨åã - [ ] **Step 3: æäº¤** ```bash git add WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js git commit -m "feat(WMS): æ·»å æå¨åå»ºä»»å¡æé®" ``` --- ## WMS å端 ### Task 2: å建 DTO **æä»¶:** - Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/CreateManualTaskDto.cs` - [ ] **Step 1: ç¼å CreateManualTaskDto** ```csharp using System.ComponentModel.DataAnnotations; using System.Text.Json.Serialization; using WIDESEA_Common.TaskEnum; namespace WIDESEA_DTO.Task { /// <summary> /// æå¨å建任å¡è¯·æ±DTO /// </summary> public class CreateManualTaskDto { /// <summary> /// ä»»å¡ç±»åï¼1=å ¥åº, 2=åºåº, 3=ç§»åº /// </summary> [JsonPropertyName("taskType")] [Required(ErrorMessage = "ä»»å¡ç±»åä¸è½ä¸ºç©º")] public TaskTypeEnum TaskType { get; set; } /// <summary> /// èµ·ç¹å°å /// </summary> [JsonPropertyName("sourceAddress")] [Required(ErrorMessage = "èµ·ç¹å°åä¸è½ä¸ºç©º")] public string SourceAddress { get; set; } /// <summary> /// ç»ç¹å°å /// </summary> [JsonPropertyName("targetAddress")] [Required(ErrorMessage = "ç»ç¹å°åä¸è½ä¸ºç©º")] public string TargetAddress { get; set; } /// <summary> /// æ¡ç /// </summary> [JsonPropertyName("barcode")] [Required(ErrorMessage = "æ¡ç ä¸è½ä¸ºç©º")] public string Barcode { get; set; } /// <summary> /// ä»åºID /// </summary> [JsonPropertyName("warehouseId")] [Required(ErrorMessage = "ä»åºIDä¸è½ä¸ºç©º")] public int WarehouseId { get; set; } /// <summary> /// ä¼å 级ï¼é»è®¤1 /// </summary> [JsonPropertyName("grade")] public int Grade { get; set; } = 1; } } ``` - [ ] **Step 2: æäº¤** ```bash git add WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/CreateManualTaskDto.cs git commit -m "feat(WMS): æ°å¢CreateManualTaskDto" ``` --- ### Task 3: æ·»å Controller æ¥å£ **æä»¶:** - Modify: `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs` - [ ] **Step 1: æ·»å CreateManualTask æ¥å£** å¨ `TaskController` ç±»ä¸æ·»å ï¼ ```csharp /// <summary> /// æå¨åå»ºä»»å¡ /// </summary> /// <param name="dto">æå¨å建任å¡åæ°</param> /// <returns></returns> [HttpGet, HttpPost, Route("CreateManualTask"), AllowAnonymous] public async Task<WebResponseContent?> CreateManualTaskAsync([FromBody] CreateManualTaskDto dto) { return await Service.CreateManualTaskAsync(dto); } ``` - [ ] **Step 2: æäº¤** ```bash git add WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs git commit -m "feat(WMS): æ·»å æå¨åå»ºä»»å¡æ¥å£ CreateManualTask" ``` --- ### Task 4: æ·»å TaskService æ¹æ³ **æä»¶:** - Modify: `WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs` é¦å æ¥çç°æ `TaskService.cs` ç»æï¼ç¡®è®¤æ¹æ³ç¾åæ ¼å¼ã - [ ] **Step 1: æ·»å CreateManualTaskAsync æ¹æ³** å¨ `TaskService` ç±»ä¸æ·»å ï¼åè `CreateAutoOutboundTasksAsync` çæ¨¡å¼ï¼ï¼ ```csharp /// <summary> /// æå¨åå»ºä»»å¡ /// </summary> /// <param name="dto">æå¨å建任å¡åæ°</param> /// <returns></returns> public async Task<WebResponseContent> CreateManualTaskAsync(CreateManualTaskDto dto) { try { // 1. çæä»»å¡å· int taskNum = await BaseDal.GetTaskNo(); // 2. æ ¹æ®ä»»å¡ç±»åç¡®å®ç¶æå¼ int taskStatus = dto.TaskType switch { TaskTypeEnum.Inbound => TaskInStatusEnum.InNew.GetHashCode(), // 200 TaskTypeEnum.Outbound => TaskOutStatusEnum.OutNew.GetHashCode(), // 100 TaskTypeEnum.Relocation => TaskRelocationStatusEnum.New.GetHashCode(), // 300 _ => TaskStatusEnum.New.GetHashCode() }; // 3. æå»ºä»»å¡å®ä½ var task = new Dt_Task { TaskNum = taskNum, PalletCode = dto.Barcode, SourceAddress = dto.SourceAddress, TargetAddress = dto.TargetAddress, TaskType = dto.TaskType.GetHashCode(), TaskStatus = taskStatus, Grade = dto.Grade, WarehouseId = dto.WarehouseId, Creater = "manual", CreateDate = DateTime.Now, ModifyDate = DateTime.Now }; // 4. ä¿åå°æ°æ®åº var result = await BaseDal.Add(task); if (!result) return WebResponseContent.Instance.Error("å建任å¡å¤±è´¥"); // 5. åéä»»å¡å°WCS var wmsTaskDto = new WMSTaskDTO { TaskNum = task.TaskNum, PalletCode = task.PalletCode, SourceAddress = task.SourceAddress, TargetAddress = task.TargetAddress, TaskType = task.TaskType, TaskStatus = task.TaskStatus, WarehouseId = task.WarehouseId ?? 0 }; var wcsResult = await _httpClientHelper.PostAsync<WebResponseContent>( "http://localhost:9292/api/Task/ReceiveTask", wmsTaskDto.ToJson()); if (!wcsResult.IsSuccess || !wcsResult.Data.Status) return WebResponseContent.Instance.Error($"ä»»å¡å·²å建ä½åéç»WCS失败: {wcsResult.Data?.Message}"); return WebResponseContent.Instance.OK($"æå¨åå»ºä»»å¡æåï¼ä»»å¡å·: {taskNum}"); } catch (Exception ex) { return WebResponseContent.Instance.Error($"æå¨å建任å¡å¼å¸¸: {ex.Message}"); } } ``` > **注æ:** éè¦ç¡®è®¤ `TaskRelocationStatusEnum` æ¯å¦åå¨ï¼å¦ä¸åå¨åä½¿ç¨ `TaskStatusEnum.New`ã - [ ] **Step 2: æ·»å å¿ è¦ç using** ```csharp using WIDESEA_DTO.Task; using WIDESEA_Common.TaskEnum; using WIDESEAWCS_DTO; ``` - [ ] **Step 3: æäº¤** ```bash git add WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs git commit -m "feat(WMS): æ·»å æå¨åå»ºä»»å¡ CreateManualTaskAsync æ¹æ³" ``` --- ## WCS å端 ### Task 5: 确认 ReceiveWMSTask å ¥å£ **æä»¶:** - Modify: `WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs` - [ ] **Step 1: 确认 ReceiveWMSTask æ¹æ³åå¨** 确认 `ReceiveWMSTask` æ¥æ¶ `WMSTaskDTO` å表åè·¯ç±å°å¯¹åº FlowServiceã ```csharp public WebResponseContent ReceiveWMSTask([NotNull] List<WMSTaskDTO> taskDTOs) { // éåä»»å¡ï¼æ ¹æ® TaskType ååå°ä¸å FlowService foreach (var item in taskDTOs) { // æ ¹æ® item.TaskType å¤æè·¯ç±å° Inbound/Outbound/Relocation } } ``` - [ ] **Step 2: æäº¤** ```bash git add WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs git commit -m "feat(WCS): 确认 ReceiveWMSTask å ¥å£æ£ç¡®" ``` --- ### Task 6: å¨ InboundTaskFlowService æ·»å 线ä½ç¹ä½å¤ç **æä»¶:** - Modify: `WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/InboundTaskFlowService.cs` é¦å æ¥çç°æç `InitializeOnReceive` æ¹æ³å®æ´å®ç°ã - [ ] **Step 1: æ·»å 线ä½ç¹ä½å¸¸é** å¨ç±»ä¸æ·»å ï¼ ```csharp /// <summary> /// 线ä½å ¥åºç¹ä½ /// </summary> private static readonly string[] LINE_IN_POINTS = { "11068", "11010", "11001" }; ``` - [ ] **Step 2: ä¿®æ¹ InitializeOnReceive æ¹æ³** å¨ `InitializeOnReceive` æ¹æ³ä¸ï¼å¤æå¦æ `SourceAddress` æ¯çº¿ä½ç¹ä½ï¼ååå ¥è¾é线任å¡ï¼ ```csharp public void InitializeOnReceive([NotNull] Dt_Task task, [NotNull] WMSTaskDTO source) { // å æ§è¡åæçè·¯ç±é»è¾ Dt_Router routers = _routerService.QueryNextRoute(source.SourceAddress); if (routers.IsNullOrEmpty()) { return; } task.TaskStatus = (int)TaskInStatusEnum.InNew; task.CurrentAddress = source.SourceAddress; task.NextAddress = routers.ChildPosi; // å¦æèµ·ç¹æ¯çº¿ä½ç¹ä½(11068/11010/11001)ï¼ç´æ¥åå ¥è¾éçº¿ä»»å¡ if (LINE_IN_POINTS.Contains(source.SourceAddress)) { WriteConveyorLineTask(task, source.SourceAddress); } } /// <summary> /// åå ¥è¾é线任å¡å°PLC /// </summary> private void WriteConveyorLineTask(Dt_Task task, string sourceAddress) { // 1. éè¿ Storage.Devices æ¥æ¾å¯¹åºçè¾éçº¿è®¾å¤ IDevice? device = Storage.Devices .FirstOrDefault(x => x.DeviceProDTOs.Any(d => d.DeviceChildCode == sourceAddress)); if (device == null) { // è®°å½æ¥å¿ï¼æªæ¾å°å¯¹åºçè¾éçº¿è®¾å¤ QuartzLogger.Error($"æå¨å建任å¡ï¼æªæ¾å°æºå°åã{sourceAddress}ã对åºçè¾é线设å¤", "InboundTaskFlowService"); return; } // 2. 转æ¢ä¸º CommonConveyorLine ç±»å if (device is not CommonConveyorLine conveyorLine) { QuartzLogger.Error($"设å¤ã{device.DeviceCode}ã䏿¯è¾é线类å", "InboundTaskFlowService"); return; } // 3. è·åå设å¤ç¼ç string? childDeviceCode = device.DeviceProDTOs .FirstOrDefault(d => d.DeviceChildCode == sourceAddress)?.DeviceChildCode; if (string.IsNullOrEmpty(childDeviceCode)) { QuartzLogger.Error($"æºå°åã{sourceAddress}ãæªæ¾å°å¯¹åºçå设å¤ç¼ç ", "InboundTaskFlowService"); return; } // 4. æé ConveyorLineTaskCommandNew ConveyorLineTaskCommandNew command = new ConveyorLineTaskCommandNew { TaskNo = (short)task.TaskNum, Source = short.Parse(sourceAddress), Target = short.Parse(task.TargetAddress ?? "0"), Barcode = task.PalletCode ?? string.Empty, WCS_STB = 1, // WCSå·²åéæ å¿ WCS_ACK = 0, PLC_STB = 0, PLC_ACK = 0 }; // 5. åå ¥PLC bool success = conveyorLine.SendCommand(command, childDeviceCode); if (success) { QuartzLogger.Info($"æå¨åå»ºå ¥åºä»»å¡å·²åå ¥è¾é线ï¼ä»»å¡å·ã{task.TaskNum}ãï¼æºå°åã{sourceAddress}ã", conveyorLine.DeviceCode); } else { QuartzLogger.Error($"æå¨åå»ºå ¥åºä»»å¡åå ¥è¾é线失败ï¼ä»»å¡å·ã{task.TaskNum}ãï¼æºå°åã{sourceAddress}ã", conveyorLine.DeviceCode); } } ``` - [ ] **Step 3: æ·»å å¿ è¦ç using** ```csharp using WIDESEAWCS_QuartzJob; using WIDESEAWCS_QuartzJob.ConveyorLine; using WIDESEAWCS_Tasks; using WIDESEAWCS_Core.LogHelper; ``` - [ ] **Step 4: æäº¤** ```bash git add WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/InboundTaskFlowService.cs git commit -m "feat(WCS): å ¥åºä»»å¡çº¿ä½ç¹ä½æ¶åå ¥è¾é线任å¡" ``` --- ## éªè¯æ¸ å - [ ] WMS å端ï¼ç¹å»"æå¨å建任å¡"æé®å¼¹åºå¯¹è¯æ¡ - [ ] WMS å端ï¼å¡«å表ååæäº¤ï¼è¿åæå - [ ] WMS å端ï¼å建任å¡åå ¥æ°æ®åº - [ ] WMS å端ï¼ä»»å¡æååéç» WCS - [ ] WCS å端ï¼ReceiveTask æ¶å°ä»»å¡ - [ ] WCS å端ï¼èµ·ç¹ä¸ºçº¿ä½ç¹ä½æ¶ï¼ä»»å¡åå ¥ PLC æå - [ ] WCS å端ï¼èµ·ç¹ä¸ºæ®éç¹ä½æ¶ï¼èµ°åæè·¯ç±é»è¾ Code/docs/superpowers/specs/2026-04-11-manual-task-creation-design.md
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,151 @@ # æå¨å建任å¡åè½è®¾è®¡ ## 1. éæ±æ¦è¿° å¨ WMS çé¢ä¸æ·»å æå¨å建任å¡åè½ãç¨æ·è¾å ¥èµ·ç¹ãç»ç¹ãæ¡ç ãä»åºIDï¼ç³»ç»èªå¨çæä»»å¡å·çåæ®µï¼åéç» WCSãWCS æ ¹æ®èµ·ç¹æ¯å¦ä¸ºçº¿ä½ç¹ä½(11068/11010/11001)夿æ¯å¦éè¦åå ¥è¾é线任å¡ã ## 2. æ´ä½æµç¨ ``` ç¨æ·(WMSçé¢) â è¾å ¥ï¼ä»»å¡ç±»å/èµ·ç¹/ç»ç¹/æ¡ç /ä»åºID WMSå端 â è°ç¨ BaseDal.GetTaskNo() çæ TaskNum â æ ¹æ®ä»»å¡ç±»åè®¾ç½®ç¶æ â è°ç¨ WCS ReceiveTask WCSå端 â å¤æèµ·ç¹æ¯å¦ä¸ºçº¿ä½ç¹ä½(11068/11010/11001) â æ¯ â æ¥ Storage.Devices è·åè¾é线å®ä¾ â åå ¥è¾é线任å¡(èµ·ç¹/ç»ç¹/ä»»å¡å·ç) â è¿åæå ``` ## 3. WMS å端æ¹å¨ ### 3.1 ä»»å¡é¡µé¢ (`task.vue`) å¨ç°æä»»å¡ç®¡ç页颿·»å **æå¨å建任å¡**æé®ï¼å¼¹åºå¯¹è¯æ¡ã ### 3.2 å¯¹è¯æ¡å段 | åæ®µ | ç±»å | 说æ | |------|------|------| | ä»»å¡ç±»å | ä¸ææ¡ | å ¥åº / åºåº / ç§»åº | | èµ·ç¹å°å | è¾å ¥æ¡ | ç¨æ·è¾å ¥ï¼å¦ 11068 | | ç»ç¹å°å | è¾å ¥æ¡ | ç¨æ·è¾å ¥ | | æ¡ç | è¾å ¥æ¡ | ç¨æ·è¾å ¥ | | ä»åºID | è¾å ¥æ¡ | ç¨æ·è¾å ¥ | | ä¼å 级 | åªè¯» | é»è®¤å¼ 1 | ## 4. WMS å端æ¹å¨ ### 4.1 æ°å¢æ¥å£ **Controller:** `TaskController.cs` ``` POST /api/Task/CreateManualTask ``` ### 4.2 Service æ¹æ³ **TaskService.cs** æ°å¢ `CreateManualTaskAsync` æ¹æ³ï¼ ```csharp public async Task<WebResponseContent> CreateManualTaskAsync(CreateManualTaskDto dto) { // 1. è°ç¨ BaseDal.GetTaskNo() çæ TaskNum // 2. æ ¹æ®ä»»å¡ç±»åè®¾ç½®ç¶æ // - å ¥åº â InNew (200) // - åºåº â OutNew (100) // - ç§»åº â RelocationNew (300) // 3. æå»º Dt_Task å®ä½ // 4. è°ç¨ WCS ReceiveTask (POST /api/Task/ReceiveTask) // 5. è¿åç»æ } ``` ### 4.3 DTO å®ä¹ ```csharp public class CreateManualTaskDto { public int TaskType { get; set; } // 1=å ¥åº, 2=åºåº, 3=ç§»åº public string SourceAddress { get; set; } public string TargetAddress { get; set; } public string Barcode { get; set; } public int WarehouseId { get; set; } public int Grade { get; set; } = 1; // é»è®¤ä¼å 级1 } ``` ## 5. WCS å端æ¹å¨ ### 5.1 å·²æé»è¾å¤ç¨ `ReceiveWMSTask` æ¹æ³å·²æ¯ææ¥æ¶ WMS ä»»å¡å¹¶ååå°å¯¹åº FlowServiceï¼ - å ¥åºä»»å¡ â `InboundTaskFlowService.InitializeOnReceive()` - åºåºä»»å¡ â `OutboundTaskFlowService.InitializeOnReceive()` - ç§»åºä»»å¡ â `RelocationTaskFlowService.InitializeOnReceive()` ### 5.2 线ä½ç¹ä½å¤æ å¨ `InboundTaskFlowService.InitializeOnReceive()` ä¸ï¼å¤æ `SourceAddress` æ¯å¦ä¸ºçº¿ä½ç¹ä½(11068/11010/11001)ãè¿ä¸ä¸ªç¹ä½å为**å ¥åºçº¿ä½ç¹ä½**ã ### 5.3 è·åè¾é线å®ä¾ ```csharp var conveyorLine = Storage.Devices .FirstOrDefault(x => x.DeviceProDTOs.Any(d => d.DeviceChildCode == sourceAddress)); ``` ### 5.4 æ°å¢ï¼åå ¥è¾éçº¿ä»»å¡ **æ¤é»è¾ä¸ºæ°å¢**ï¼ç®å FlowService ä¸ä¸åå¨åå ¥è¾é线çé»è¾ï¼éè¦å¨å¤æä¸ºçº¿ä½ç¹ä½åæ°å¢ï¼ 1. è·åè¾é线å®ä¾æååï¼è¯»åæºçº¿ä½å· 2. æé `ConveyorLineTaskCommandNew` 对象 3. è°ç¨ `conveyorLine.WriteCustomer(sourceLineNo, command)` åå ¥ PLC åå ¥åæ®µï¼ - `TaskNo` = WCS åé çä»»å¡å· - `Source` = èµ·ç¹å°å - `Target` = ç»ç¹å°å - `Barcode` = æ¡ç - `WCS_STB` = 1 (æ è®°WCSå·²åé) ### 5.5 éå¤ä»»å¡å¤ç `ReceiveWMSTask` ä¸å·²æéå¤ä»»å¡æ£æ¥é»è¾ï¼æ ¹æ® TaskNum æ PalletCodeï¼ï¼æå¨å建æ¶å¦ééå¤è¿åé误ã ## 6. ä»»å¡ç¶ææä¸¾ | ç±»å | æ°å»ºç¶æ | ç¶æå¼ | |------|----------|--------| | å ¥åº | InNew | 200 | | åºåº | OutNew | 100 | | ç§»åº | RelocationNew | 300 | ## 7. é误å¤ç - ä»»å¡å·è·å失败 â è¿åé误 - WCS æ¥æ¾è¾é线å®ä¾å¤±è´¥ â è¿åéè¯¯ç» WMS - åå ¥ PLC 失败 â è¿åé误ï¼WMS ä»»å¡ç¶æä¿ææ°å»º - WCS è¿åéå¤ä»»å¡ â è¿åéè¯¯ç» WMS ## 8. æ¶åçæºæä»¶ ### WMS å端 - `WMS/WIDESEA_WMSClient/src/views/taskinfo/task.vue` - `WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js` ### WMS å端 - `WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs` - `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs` ### WCS å端 - `WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs` - `WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/InboundTaskFlowService.cs` - `WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/OutboundTaskFlowService.cs` - `WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/RelocationTaskFlowService.cs` - `WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs` (åèåå ¥æ¨¡å¼) - `WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLine/ConveyorLineTaskCommandNew.cs` (ä»»å¡å½ä»¤ç»æ)