--[[ 版本: Version 1.0 创建日期: 2025-6-26 创建人: KUN 功能: wms_wh Lua程序包整合了一些和【仓库】【库区】【货位】【巷道】这些仓库基础构成对象相关的操作 -- PICK_LOC 获取推荐货位 -- OperationCancel WCS调用取消作业 更改说明: --]] wms_base = require ("wms_base") wms_cntr = require ("wms_container") wms_cntr = require("wms_container") wms_op = require("wms_operation") wms_task = require("wms_task") wms_wh = require("wms_wh") local ams_base = {_version = "0.1.1"} -- 内侧排定义 local InsideRows = { [1] = {1, 4}, [2] = {5, 8}, } -- 内外排映射 local innerOuterMap = { [1] = 2, [4] = 3, [5] = 6, [8] = 7, } -- 查询所有货位(缓存方式) local function buildLocationCache(strLuaDEID, aisle) local cache = {} for row = 1, 8 do cache[row] = {} for layer = 1, 10 do local cond = string.format("C_ENABLE='Y' AND N_AISLE=%d AND N_ROW=%d AND N_LAYER=%d", aisle, row, layer) local nRet, strRetInfo = mobox.queryDataObjAttr2(strLuaDEID, "Location", cond, "N_COL ASC", 200, "S_CODE", "N_COL", "N_ROW", "N_LAYER", "N_POS", "S_AREA_CODE", "N_CURRENT_NUM", "N_LOCK_STATE") if nRet == 0 and strRetInfo then local ok, queryInfo = pcall(json.decode, strRetInfo) if ok and queryInfo.dataSet then local locs = {} for _, item in ipairs(queryInfo.dataSet) do local loc = {} for _, attr in ipairs(item.attrs or {}) do loc[attr.attr] = tonumber(attr.value) or attr.value end table.insert(locs, loc) end cache[row][layer] = locs end end end end return cache end local function isInnerLocUsable(cache, loc) local outerRow = innerOuterMap[loc.N_ROW] if not outerRow then return true end local outerLocs = cache[outerRow] and cache[outerRow][loc.N_LAYER] or {} for _, oLoc in ipairs(outerLocs) do if oLoc.N_COL == loc.N_COL and (oLoc.N_CURRENT_NUM > 0 or oLoc.N_LOCK_STATE > 0) then return false end end return true end local function isOuterLocUsable(cache, loc) local innerRow = nil for k, v in pairs(innerOuterMap) do if v == loc.N_ROW then innerRow = k break end end if not innerRow then return true end local innerLocs = cache[innerRow] and cache[innerRow][loc.N_LAYER] or {} for _, iLoc in ipairs(innerLocs) do if iLoc.N_COL == loc.N_COL and iLoc.N_LOCK_STATE > 0 then return false end end return true end local function pickAdjacentSameRow(locList, containerCount) table.sort(locList, function(a, b) return a.N_COL < b.N_COL end) for i = 1, #locList - (containerCount - 1) do local continuous = true for j = 1, containerCount - 1 do if locList[i + j].N_COL ~= locList[i].N_COL + j then continuous = false break end end if continuous then local pair = {} for k = 0, containerCount - 1 do table.insert(pair, locList[i + k]) end return pair end end return nil end local function tryAssignFixedLayerOrder(cache, rows, containerCount, layerOrder, isInner, occupiedSet) for _, layer in ipairs(layerOrder) do for _, row in ipairs(rows) do local locs = cache[row] and cache[row][layer] or {} local filtered = {} for _, loc in ipairs(locs) do if loc.N_LOCK_STATE == 0 and loc.N_CURRENT_NUM == 0 and not occupiedSet[loc.S_CODE] then if isInner and isInnerLocUsable(cache, loc) then table.insert(filtered, loc) elseif not isInner and isOuterLocUsable(cache, loc) then table.insert(filtered, loc) end end end if containerCount >= 2 then local pair = pickAdjacentSameRow(filtered, containerCount) if pair then return pair end end if #filtered >= containerCount then local res = {} for i = 1, containerCount do table.insert(res, filtered[i]) end return res end end end return nil end local function Recommend(strLuaDEID, taskList) local result = {} local aisle = taskList[1].curPos if not aisle then return false, "任务参数curPos无效" end local innerRows = InsideRows[aisle] if not innerRows then return false, "未定义巷道内侧排" end local outerRows = {} for r = 1, 8 do local isInner = false for _, v in ipairs(innerRows) do if v == r then isInner = true break end end if not isInner then table.insert(outerRows, r) end end local occupiedSet = {} local cache = buildLocationCache(strLuaDEID, aisle) for _, task in ipairs(taskList) do local cntr_code = task.containerCode local nRet, cntr_obj = wms_cntr.GetInfo(strLuaDEID, cntr_code) if nRet ~= 0 or cntr_obj == '' then return false, "料箱不存在: "..cntr_code end local containerCount = cntr_obj.nContain or 1 local containerType = cntr_obj.ctd_code or "" if containerType == "" then return false, "容器未定义类型: "..cntr_code end local priLayers, secLayers = {}, {} if containerType == "CTD-001" then for l = 5, 10 do table.insert(priLayers, l) end for l = 1, 4 do table.insert(secLayers, l) end elseif containerType == "CTD-002" then for l = 1, 4 do table.insert(priLayers, l) end end local locs = tryAssignFixedLayerOrder(cache, innerRows, containerCount, priLayers, true, occupiedSet) if (not locs or #locs == 0) and #secLayers > 0 then locs = tryAssignFixedLayerOrder(cache, innerRows, containerCount, secLayers, true, occupiedSet) end if not locs or #locs == 0 then locs = tryAssignFixedLayerOrder(cache, outerRows, containerCount, priLayers, false, occupiedSet) if (not locs or #locs == 0) and #secLayers > 0 then locs = tryAssignFixedLayerOrder(cache, outerRows, containerCount, secLayers, false, occupiedSet) end end if not locs or #locs == 0 then return false, string.format("容器%s在巷道%d无可用货位", cntr_code, aisle) end for _, loc in ipairs(locs) do occupiedSet[loc.S_CODE] = true lua.Debug(strLuaDEID, debug.getinfo(1), "分配货位", loc.S_CODE) end table.insert(result, { taskNo = task.taskNo or "", containerCode = cntr_code, recommendLocation = locs[1].S_CODE }) end return true, result end function ams_base.PICK_LOC(strLuaDEID, strJsonParam) local success, ret = Recommend(strLuaDEID, strJsonParam) if not success then return 2, ret end return 0, json.encode(ret) end function ams_base.OperationCancel(strLuaDEID, body) -- 输入参数验证 if not body or not body.code then return 1, "请求参数错误缺少code" end local nRet, strRetInfo local operation_objs, operation, task_objs, wave_obj local cancel_count = 0 -- 取消作业数量 -- 1. 获取作业信息 nRet, operation = wms_op.GetInfo(strLuaDEID, body.code) if nRet ~= 0 then return 1, "获取作业信息失败: "..tostring(operation) end lua.Debug(strLuaDEID, debug.getinfo(1), "operation", operation) -- 2. 检查作业状态是否可取消 -- bs_state=2(完成)不可取消, task_state=2(已开始执行)不可取消 if operation.bs_state == 2 then return 1, "作业已完成,不可取消" end if operation.task_state == 2 then return 1, "作业任务已开始执行,不可直接取消" end -- 3. 查询待取消的任务 local strCondition = "S_OP_CODE = '"..operation.code.."' AND (N_B_STATE = 0 OR N_B_STATE = 1)" nRet, task_objs = m3.QueryDataObject(strLuaDEID, "Task", strCondition) if nRet ~= 0 then return 1, "获取任务信息失败: "..operation.code end if not task_objs or #task_objs == 0 then return 1, "作业下无符合条件的任务: "..operation.code end -- 4. 更新任务状态为取消(5) strUpdateSql = "N_B_STATE = 5" nRet, strRetInfo = mobox.updateDataAttrByCondition(strLuaDEID, "Task", strCondition, strUpdateSql) if nRet ~= 0 then return 1, "更新任务状态失败: "..tostring(strRetInfo) end -- 5. 更新作业状态为取消 nRet, strRetInfo = wms_op.SetCancel(strLuaDEID, operation) if nRet ~= 0 then return 1, "更新作业状态失败: "..tostring(strRetInfo) end -- 6. 处理波次相关逻辑 if operation.bs_type == "Inbound_Wave" then nRet, wave_obj = m3.GetDataObjectByKey(strLuaDEID, operation.bs_type, "S_WAVE_NO", operation.bs_no) if nRet ~= 0 then return 1, "获取入库波次信息失败: "..tostring(wave_obj) end -- 入库波次状态=6(关闭中)时需要触发强制完成 if wave_obj.b_state == 6 then local add_wfp = { wfp_type = 1, cls = "Inbound_Wave", obj_id = wave_obj.id, obj_name = "入库波次号'"..operation.bs_no.."'-->强制完成", trigger_event = "强制完成" } nRet, strRetInfo = m3.AddSysWFP(strLuaDEID, add_wfp) if nRet ~= 0 then return 1, "添加强制完成事件失败: "..tostring(strRetInfo) end end elseif operation.bs_type == "Outbound_Wave" then nRet, wave_obj = m3.GetDataObjectByKey(strLuaDEID, operation.bs_type, "S_WAVE_NO", operation.bs_no) if nRet ~= 0 then return 1, "获取出库波次信息失败: "..tostring(wave_obj) end -- 出库波次状态=8(关闭中)时需要触发强制完成 if wave_obj.b_state == 8 then local add_wfp = { wfp_type = 1, cls = "Outbound_Wave", obj_id = wave_obj.id, obj_name = "出库波次号'"..operation.bs_no.."'-->强制完成", trigger_event = "强制完成" } nRet, strRetInfo = m3.AddSysWFP(strLuaDEID, add_wfp) if nRet ~= 0 then return 1, "添加强制完成事件失败: "..tostring(strRetInfo) end end end -- 7. 返回成功结果 return 0, "作业取消成功" end return ams_base