--[[ 和项目关联比较紧密的一些函数 项目开发和实施人员 可以把 prj_base 改成各自项目特别的 xx_base 比如巨星项目就用 jx_base prj_base 是 WMS Basis 里的标准用法 --]] wms_base = require ("wms_base") wms_station = require( "wms_station" ) wms_wh = require( "wms_wh" ) wms_out = require ("wms_outbound") prj_api = require ( "prj_api" ) wms_alg = require ("wms_base_algorithm") wms_inv = require ("wms_inventory") wms_op = require( "wms_operation" ) local prj_base = {_version = "0.1.1"} --[[ 根据物料的长中短尺寸计算适用料箱, long 长边 middle 中边 short 短边 返回参数: (1)nRet 0 成功 非0失败 (2) box_style --]] function prj_base.GetBoxStyle( strLuaDEID, long, middle, short ) local nRet, strRetInfo -- 校验输入参数 if ( long == nil or long <= 0 or long >= 60) then return 1, "长边值不符合规范! 长边不能超过 60 " end if ( middle == nil or middle <= 0 or middle >= 40) then return 1, "中边值不符合规范! 中边不能超过 40 " end if ( short == nil or short <= 0 or short >= 30) then return 1, "短边边值不符合规范! 短边不能超 30 " end if ( middle > long or short > middle ) then return 1, "长中短边顺序不对!" end -- 计算料箱型号 if ( long < 20 ) then return 0, '["A","B","C","D","E"]' elseif ( long >= 20 and long < 30) then if ( middle < 20 ) then return 0, '["A","B","C","D","E"]' elseif ( middle >= 20 and middle < 30 ) then if ( short < 20 ) then return 0, '["A","B","C","D"]' else return 0, '["A","B"]' end end elseif ( long >= 30 and long < 40) then if ( middle < 20 ) then return 0, '["A","B","C"]' elseif ( middle >= 20 and middle < 30 ) then if ( short < 20 ) then return 0, '["A","B","C"]' else return 0, '["A","B"]' end elseif ( middle >= 30 and middle < 40 ) then return 0, '["A"]' end elseif ( long >= 40 and long < 60) then return 0, '["A"]' end return 1, "长边参数不对!" end function prj_base.Generate_Batch_No( item_code ) local nRet, strRetInfo if ( item_code == '' or item_code == nil ) then return 1, "generate_batch_no 输入参数必须有值" end local strCode = '' local strHeader = item_code..'_'..os.date("%y%m%d")..'-' nRet, strCode = mobox.getSerialNumber( "入库批次", strHeader, 3 ) if ( nRet ~= 0 ) then lua.Error( strLuaDEID, debug.getinfo(1), '申请入库批次号失败!'..strCode ) end local seg = lua.split( strCode, "_" ) return 0, seg[2] end --[[ 获取某个库区内设备的状态(在JX项目中特指库区内的堆垛机状态) 输入参数: area_code -- 库区编码 返回参数: nRet = 0 成功 非零 失败 stacker_dev = [{"dev_no"::xxx",aisle:1,aisle_no:"A01",enable:0/1,cntr_num:0}] -- cntr_num 巷道入库接驳位容器数量 -- aisle 巷道编码 -- dev_no 堆垛机号 --]] function prj_base.Get_stacker_dev_state( strLuaDEID, area_code ) local nRet, strRetInfo if ( station == nil ) then station = '' end -- 获取 area_code 库区堆垛机设备工作状态,确定哪些巷道可以使用 local const_name = "Area-"..area_code.."-StackerCrane" nRet, strRetInfo = wms_base.Get_sConst2( const_name ) if ( nRet ~= 0 ) then return 1, "系统无法获取常量'"..const_name.."'" end if ( strRetInfo == '') then return 1, "常量'"..const_name.."' 不能为空! " end --[{"dev_no"::xxx",aisle:1,aisle_no:"A01",enable:false/true}] local stacker_dev, success success, stacker_dev = pcall( json.decode, strRetInfo) if ( success == false ) then return 1, "常量'"..const_name.."'的JSON格式非法! --> "..strRetInfo end local n, m local dev_codes = '' for n = 1, #stacker_dev do dev_codes = dev_codes..stacker_dev[n].dev_no.."," end if ( dev_codes == '') then return 1, "库区'"..area_code.."'没有定义设备!" end dev_codes = lua.trim_laster_char( dev_codes ) -- 调用WCS接口获取设备状态 下面的函数可以更新具体项目做调整 -- [{"DVC_NO":"TC21","IS_USE":0/1,"CON_NUM":1},...] nRet, dev_state = prj_api.Get_DEV_State( strLuaDEID, dev_codes ) if ( nRet ~= 0 ) then return nRet, dev_state end local find for n = 1, #stacker_dev do find = false for m = 1, #dev_state do if ( stacker_dev[n].dev_no == dev_state[m].DVC_NO ) then find = true stacker_dev[n].enable = lua.Get_NumAttrValue( dev_state[m].IS_USE ) stacker_dev[n].cntr_num = lua.Get_NumAttrValue( dev_state[m].CON_NUM ) break end end if ( find == false ) then return 1, "WCS返回的设备状态中没有编码='"..stacker_dev[n].dev_no.."'的设备状态!" end end return 0, stacker_dev end -- 获取库区库可用的堆垛机巷道编码 -- 'A01', 'A02', 'A04' function prj_base.Get_Available_Aisle( strLuaDEID, area_code ) local nRet, stacker_dev nRet, stacker_dev = prj_base.Get_stacker_dev_state( strLuaDEID, area_code ) if ( nRet ~= 0 ) then return nRet, stacker_dev end local n local aisle = '' for n = 1, #stacker_dev do if ( stacker_dev[n].enable == 1 ) then aisle = aisle.."'"..stacker_dev[n].aisle_no.."'," end end aisle = lua.trim_laster_char( aisle ) return 0, aisle end --[[ 创建一个预分配料箱出库作业 pac_obj 预分配料箱对象 { S_PAC_NO,N_B_STATE, S_STATION_NO, S_CNTR_CODE, S_BS_TYPE, S_BS_NO, S_OUT_OP_NAME } ]] function prj_base.Create_Pre_Alloc_CNTR_OutOperation ( strLuaDEID, pac_obj ) local nRet, strRetInfo local msg local pac_no = pac_obj.S_PAC_NO local b_state = lua.Get_NumAttrValue( pac_obj.N_B_STATE ) local to_station = pac_obj.S_STATION_NO local cntr_code = lua.Get_StrAttrValue( pac_obj.S_CNTR_CODE ) local bs_type = lua.Get_StrAttrValue( pac_obj.S_BS_TYPE ) local bs_no = lua.Get_StrAttrValue( pac_obj.S_BS_NO ) local out_op_def = lua.Get_StrAttrValue( pac_obj.S_OUT_OP_NAME ) -- 【预分配容器】数据对象属性判断,不合法的终止程序执行 if ( b_state ~= PAC_STATE.InStock ) then msg = "预分配号'"..pac_no.."'的状态不是未执行状态,不能启动料箱出库作业!" lua.Warning( strLuaDEID, debug.getinfo(1), msg ) return 1, msg end if out_op_def == '' then return 1, "预分配号'"..pac_no.."'的容器预分配对象中没有定义出库作业类型!" end if ( cntr_code == '' ) then msg = "预分配号'"..pac_no.."'中的容器编码为空!" lua.Warning( strLuaDEID, debug.getinfo(1), msg ) return 1, msg end local from_loc_code = wms_wh.GetLocCodeByCNTR( strLuaDEID, cntr_code ) if ( from_loc_code == nil or from_loc_code == '' ) then msg = "预分配号'"..pac_no.."'中的容器'"..cntr_code.."'没有绑定货位!" return 1, msg end local from_loc nRet, from_loc = wms_wh.GetLocInfo( from_loc_code ) if ( nRet ~= 0 ) then return 1, '获取货位信息失败! '..loc_code end -- 创建【料箱出库】作业 -- 获取站点货位,站点货位定义在常量中 local to_loc_code nRet, to_loc_code = wms_station.Get_Station_Loc( strLuaDEID, to_station ) if ( nRet ~= 0 ) then return 1, to_loc_code end local to_loc nRet, to_loc = wms_wh.GetLocInfo( to_loc_code ) if ( nRet ~= 0 ) then return 1, '获取货位信息失败! '..to_loc end local operation = m3.AllocObject(strLuaDEID,"Operation") operation.bs_state = 8 -- 待启动前,这些作业有待后台脚本来设置为状态 0 operation.start_wh_code = from_loc.wh_code operation.start_area_code = from_loc.area_code operation.start_loc_code = from_loc_code operation.end_wh_code = to_loc.wh_code operation.end_area_code = to_loc.area_code operation.end_loc_code = to_loc_code operation.op_type = OPERATION_TYPE.Outbound operation.op_def_name = out_op_def -- 注意这些可能会根据项目的不同而不同,就是出库的作业类型 operation.cntr_code = cntr_code operation.carry_cb_cls = "Pre_Alloc_Container" -- 预分配容器 operation.carry_cb_no = pac_no nRet, operation = m3.CreateDataObj( strLuaDEID, operation ) if ( nRet ~= 0 ) then lua.Error( strLuaDEID, debug.getinfo(1), '创建【作业】失败!'..operation ) end -- 更新【预分配容器】对象属性 -- N_B_STATE = 1/PAC_STATE.Outbound 表示预分配容器已经安排作业进行搬运 local strUpdateSql = "S_FROM_LOC = '"..from_loc_code.."', N_B_STATE = "..PAC_STATE.Outbound..", S_OUT_OP_NO = '"..operation.code.."'" strCondition = "S_PAC_NO = '"..pac_no.."'" nRet, strRetInfo = mobox.updateDataAttrByCondition( strLuaDEID, "Pre_Alloc_Container", strCondition, strUpdateSql ) if ( nRet ~= 0 ) then lua.Error( strLuaDEID, debug.getinfo(1), "更新【预分配容器】信息失败!"..strRetInfo ) end -- 获取【预分配容器】的业务来源,设置业务来源的状态 if ( bs_type == "Inbound_Wave" and bs_no ~= '') then -- N_B_STATE = 2/IN_WAVE_STATE.Op_Start 表示入库波次已经开始作业 strUpdateSql = "N_B_STATE = "..IN_WAVE_STATE.Op_Start..", S_B_STATE = 'Start'" strCondition = "S_WAVE_NO = '"..bs_no.."'" nRet, strRetInfo = mobox.updateDataAttrByCondition( strLuaDEID, "Inbound_Wave", strCondition, strUpdateSql ) if ( nRet ~= 0 ) then lua.Error( strLuaDEID, debug.getinfo(1), "更新【入库波次】信息失败!"..strRetInfo ) end elseif ( bs_type == "Inbound_Order" and bs_no ~= '' ) then -- N_B_STATE = 1 表示分配料箱开始作业 strUpdateSql = "N_B_STATE = "..INBOUND_STATE.Op_Start..", S_B_STATE = 'Start'" strCondition = "S_NO = '"..bs_no.."'" nRet, strRetInfo = mobox.updateDataAttrByCondition( strLuaDEID, "Inbound_Order", strCondition, strUpdateSql ) if ( nRet ~= 0 ) then lua.Error( strLuaDEID, debug.getinfo(1), "更新【入库单】信息失败!"..strRetInfo ) end end return 0 end --[[ 检查一下预分配容器流水号 = pac_no 下面的预分配容器明细是否已经完成处理(码盘完成),如果完成处理 创建一个 入库作业 适合预分配料箱入库作业,station 为站台编码, cntr_code 是料箱编码 输入参数: pac_no -- 分配容器流水号 station -- 站台编码 cntr_code -- 容器编码 返回值: nRet, action ]] function prj_base.Pre_Alloc_CNTR_PostProcess( strLuaDEID, pac_no, station, cntr_code ) local nRet, strRetInfo if ( lua.StrIsEmpty( pac_no ) ) then return 2, "预分配料箱流水号必须有值!" end -- 检查一下当前料箱的入库任务是否已经全部完成,如果完成就创建一个【货品入库】作业 -- N_B_STATE = 1 表示科执行的入库任务 local strCondition = "S_PAC_NO = '"..pac_no.."' AND N_B_STATE = 1 " nRet, strRetInfo = mobox.getDataObjCount( strLuaDEID, "Pre_Alloc_CNTR_Detail", strCondition ) if ( nRet ~= 0 ) then return 2, strRetInfo end local nCount = lua.StrToNumber( strRetInfo ) local operation = {} local strUpdateSql local action = { { action_type = "refresh_master_panel", value = { sub_page = {"当前任务", "未执行任务"} } } } if ( nCount == 0 ) then -- *** -- 发现过相同有相同预分配容器的入库作业,这是严重的数据错误,这里加一个判断 N_TYPE = 1 是执行的意思 strCondition = "S_CARRY_CB_NO = '"..pac_no.."' AND N_TYPE = 1 AND S_CARRY_CB_CLS = 'Pre_Alloc_Container'" nRet, strRetInfo = mobox.existThisData( strLuaDEID, "Operation", strCondition ) if ( nRet ~= 0 ) then return 2, strRetInfo end -- 如果该车辆编码的动作已经在队列,返回,不做处理 if ( strRetInfo == "yes" ) then return 2, "容器号'"..cntr_code.."'不能重复创建货品入库作业!" end -- 获取【预分配容器】中定义的回库作业定义 local pac nRet, pac = m3.GetDataObjectByKey(strLuaDEID, "Pre_Alloc_Container", "S_PAC_NO", pac_no ) if nRet ~= 0 then return 1, "无法获取编码 = '"..pac_no.."' 的预分配容器!" end if lua.StrIsEmpty( pac.back_op_name ) then return 1, "【预分配容器】对象中 S_BACK_OP_NAME 必须有值!" end -- 容器里加入货品明细,如果容器有混箱规则,系统会在容器的扩展属性表加混箱值,通过这些值可以计算入库货位 local container nRet, container = wms_cntr.GetInfo( strLuaDEID, cntr_code ) if (nRet ~= 0) then return 2, "获取【容器】信息失败! " .. container end -- 加库存量 INV_Detail 加【预分配容器明细】中的内容 nRet, strRetInfo = wms_inv.Add_INV_Detail_By_PAC_Detail( strLuaDEID, cntr_code, pac_no, container.position ) if ( nRet ~= 0 ) then return 2, 'wms_inv.Add_INV_Detail_By_PAC_Detail!'..strRetInfo end --重置料箱信息 nRet, strRetInfo = wms_cntr.Reset( strLuaDEID, container ) if ( nRet ~= 0 ) then return 2, strRetInfo end local parameter = { carry_cb_cls = "Pre_Alloc_Container", carry_cb_no = pac_no, bs_type = pac.bs_type, bs_no = pac.bs_no, factory = pac.factory, op_def_name = pac.back_op_name, } -- 创建作业 nRet, operation = wms_op.Create_Inbound_Operation( strLuaDEID, station, cntr_code, parameter ) if ( nRet ~= 0 ) then mobox.setInfo( strLuaDEID, "预分配料箱组盘完成后,无法创建入库作业! 错误号:100 -->"..operation ) -- 7 组盘完成入库时发现入不了库,回库出现问题, 问题编码 5100 strUpdateSql = "N_B_STATE = "..PAC_STATE.Error..", S_B_STATE = 'Err', N_ERR_CODE = 5100, S_ERR_MSG = '"..lua.FormatSQLString(operation).."'" strCondition = "S_PAC_NO = '"..pac_no.."'" nRet, strRetInfo = mobox.updateDataAttrByCondition( strLuaDEID, "Pre_Alloc_Container", strCondition, strUpdateSql ) if ( nRet ~= 0 ) then return 2, "更新【预分配容器】信息失败!"..strRetInfo end return 0, action end --【预分配容器】+ 回库作业号, 状态 = 4/Inbound (回库) local strUpdateSql = "S_BACK_OP_NO = '"..operation.code.."', N_B_STATE = "..PAC_STATE.Inbound strCondition = "S_PAC_NO = '"..pac_no.."'" nRet, strRetInfo = mobox.updateDataAttrByCondition( strLuaDEID, "Pre_Alloc_Container", strCondition, strUpdateSql ) if ( nRet ~= 0 ) then return 2, "更新【预分配容器】信息失败!"..strRetInfo end -- -- 入库作业产生需要对入库作业的终点获取加入库锁 -- -- 如果作业定义的类型 = [Station]->[Picking-AS/RS] 不需要锁定目标货位 -- local op_def -- nRet, op_def = wms_base.GetOpDefInfo( factory, operation.op_def_name ) -- if nRet ~= 0 then -- return 1, "系统无法获取名为'"..op_def_name.."'的作业类型! "..op_def -- end -- if op_def.hand_proc ~= "[Station]->[Picking-AS/RS]" then -- -- Pickingc 车不需要对最终位置加锁 -- nRet, strRetInfo = wms.wms_LockLocation(strLuaDEID, operation.end_loc_code, LOCK_TYPE.Inbound,"", operation.code, operation.op_def_name ) -- if (nRet ~= 0) then return 2, "wms_LockLocation 失败!"..strRetInfo end -- end action[2] = { action_type = "set_dlg_attr", value = { { attr = "UPC", value = "", enable = true, prompt = "请扫商品条码..." }, { attr = "S_CNTR_CODE", value = "", enable = false }, { attr = "S_CELL_NO", value = "", enable = false }, { attr = "S_ITEM_CODE", F_QTY = "", enable = false }, { attr = "F_QTY", value = "", enable = false }, { attr = "F_ACT_QTY", value = "", enable = false }, { attr = "S_ITEM_NAME", value = "", enable = false } } } action[3] = { action_type = "refresh_related_panel", value = { { panel_name = "料格显示", input_parameter = { cell_no = "" } } } } action[4] = { action_type = "set_master_panel_cursor", value = { form_name = "basis-3055 TOP VIEW", ctrl_id = "S_CNTR_CODE", value = ""} } end return 0, action end --[[ 检查一下当前正在分拣的料箱容器 dc_no (配盘容器) 下面的 detail 是否已经完成处理完成, 如果完成处理表示该容器分拣完成,创建一个料箱入库 输入参数: parameter -- 分拣输入面板的参数 返回值: action -- ]] function prj_base.Distribution_CNTR_PostProcess( strLuaDEID, parameter ) local nRet, strRetInfo local strCondition, strUpdateSql -- 检查一下当前料箱的分拣出库任务是否已经全部完成,如果完成就创建一个【料箱入库】作业 -- N_B_STATE = 1 表示科执行的入库任务 strCondition = "S_CNTR_CODE = '"..parameter.cntr_code.."' AND N_B_STATE = 1 AND S_WAVE_NO = '"..parameter.wave_no.."'" nRet, strRetInfo = mobox.getDataObjCount( strLuaDEID, "Distribution_CNTR_Detail", strCondition ) if ( nRet ~= 0 ) then return 2, strRetInfo end local nCount = lua.StrToNumber( strRetInfo ) -- 容器有分拣出库后, 设置容器强制置满标记 C_FORCED_FILL = 'N' strCondition = "S_CODE = '"..parameter.cntr_code.."'" strUpdateSql = "C_FORCED_FILL = 'N'" nRet, strRetInfo = mobox.updateDataAttrByCondition( strLuaDEID, "Container", strCondition, strUpdateSql ) if ( nRet ~= 0 ) then return 2, "更新【Container】信息失败!"..strRetInfo end -- 当前的配盘中已经没有需要执行的出库任务 local operation = {} local action = { { action_type = "refresh_master_panel", value = { sub_page = {"当前任务", "未执行任务"} } } } if ( nCount == 0 ) then -- 创建分拣完成后回库作业(backop_def_name). 【配盘容器】状态改为4(拣货完成) local dc nRet, dc = m3.GetDataObjectByKey(strLuaDEID, "Distribution_CNTR", "S_DC_NO", parameter.dc_no ) if nRet ~= 0 then return 1, "无法获取编码 = '"..dc_no.."' 的【配盘】!" end if lua.StrIsEmpty( dc.back_op_name ) then return 1, "【配盘】对象中 S_BACK_OP_NAME 必须有值!" end --库存量表变化 并且生成下架记录 local loc if parameter.loc_code == nil or parameter.loc_code == '' then lua.DebugEx( strLuaDEID, "Distribution_CNTR_PostProcess-->parameter 不合规!", parameter ) return 2, 'prj_base.Distribution_CNTR_PostProcess 函数中的 parameter 中没定义 loc_code/拣货台货位' end nRet, loc = wms_wh.GetLocInfo( parameter.loc_code ) if ( nRet ~= 0 ) then lua.Stop( strLuaDEID, '获取货位信息失败! 货位 -->['..parameter.loc_code.."] 原因: "..loc ) return end nRet, strRetInfo = wms_out.Distribution_CNTR_Detail_PostProcess( strLuaDEID, loc.wh_code, loc.area_code, parameter.loc_code, parameter.dc_no ) if ( nRet ~= 0 ) then return 2, 'wms_out.Distribution_CNTR_Detail_PostProcess!'..strRetInfo end --重置料箱信息 local container nRet, container = wms_cntr.GetInfo( strLuaDEID, parameter.cntr_code ) if (nRet ~= 0) then return 2, "获取【容器】信息失败! " .. container end nRet, strRetInfo = wms_cntr.Reset( strLuaDEID, container ) if ( nRet ~= 0 ) then return 2, strRetInfo end local op_parameter = { carry_cb_cls = "Distribution_CNTR", carry_cb_no = parameter.dc_no, bs_type = dc.bs_type, bs_no = dc.bs_no, factory = dc.factory, op_def_name = dc.back_op_name } nRet, operation = wms_op.Create_Inbound_Operation( strLuaDEID, parameter.station, parameter.cntr_code, op_parameter ) if ( nRet ~= 0 ) then mobox.setInfo( strLuaDEID, operation ) -- 9 分拣完成,回库出现问题 strUpdateSql = "N_B_STATE = 9" strCondition = "S_DC_NO = '"..parameter.dc_no.."'" nRet, strRetInfo = mobox.updateDataAttrByCondition( strLuaDEID, "Distribution_CNTR", strCondition, strUpdateSql ) if ( nRet ~= 0 ) then return 2, "更新【配盘】信息失败!"..strRetInfo end return 0, action end -- 【配盘】+ 回库作业号, 状态 = 5 (回库) strUpdateSql = "S_BACK_OP_NO = '"..operation.code.."', N_B_STATE = 5" strCondition = "S_DC_NO = '"..parameter.dc_no.."'" nRet, strRetInfo = mobox.updateDataAttrByCondition( strLuaDEID, "Distribution_CNTR", strCondition, strUpdateSql ) if ( nRet ~= 0 ) then return 2, "更新【配盘】信息失败!"..strRetInfo end local distribution_cntr nRet, distribution_cntr = m3.GetDataObjByCondition( strLuaDEID, "Distribution_CNTR", strCondition ) if ( nRet ~= 0 ) then return 2, "获取【配盘】信息失败!"..strRetInfo end -- 增加一个后台进程对配盘进行处理,触发配盘明细中的 出库单是否可以完成 local add_wfp = { wfp_type = 1, cls = "Distribution_CNTR", obj_id = distribution_cntr.id, obj_name = "配盘'"..parameter.dc_no.."'-->出库后处理", trigger_event = "出库后处理" } nRet, strRetInfo = m3.AddSysWFP( strLuaDEID, add_wfp ) if ( nRet ~= 0 ) then return 2, "AddSysWFP失败!"..strRetInfo end -- 位置加入库锁 nRet, strRetInfo = wms.wms_LockLocation(strLuaDEID, operation.end_loc_code, wms_base.Get_nConst( strLuaDEID, "锁类型-入库锁" ), "", operation.code, operation.op_def_name ) if (nRet ~= 0) then return 2, "wms_LockLocation 失败!"..strRetInfo end action[2] = { action_type = "set_dlg_attr", value = { { attr = "UPC", value = "", enable = true, prompt = "请扫商品条码..." }, { attr = "Prompt", value = "请扫商品条码..." }, { attr = "S_CNTR_CODE", value = "", enable = false }, { attr = "S_CELL_NO", value = "", enable = false }, { attr = "S_ITEM_CODE", value = "", enable = false }, { attr = "F_QTY", value = "", enable = false }, { attr = "F_ACC_P_QTY", value = "", enable = false }, { attr = "S_ITEM_NAME", value = "", enable = false }, { attr = "Remaining_Inventory", value = "", enable = false } } } action[3] = { action_type = "refresh_related_panel", value = { { panel_name = "料格显示", input_parameter = { cell_no = "" } }, { panel_name = "拣料箱显示", input_parameter = { pick_box_code = "", -- 拣料箱号 } } } } action[4] = { action_type = "set_master_panel_cursor", value = { form_name = "3055 TOP VIEW", ctrl_id = "S_CNTR_CODE", value = ""} } lua.DebugEx( strLuaDEID, "action-->", action ) end return 0, action end return prj_base