--[[ 版本: Version 2.1 创建日期: 2025-3-26 创建人: HAN WMS-Basis-Model-Version: V15.5 共用程序包 名称: wms_putaway 应用: -- 用于入库时计算库区中的可用货位,以及入库作业等相关操作 -- 和上架相关的一些函数 函数: -- Get_StorageCache_Loc 获取立库入库缓存区及立库内存储货位 更改记录: --]] wms_wh = require ("wms_wh") wms_out = require ("wms_outbound") wms_alg = require ("wms_base_algorithm") wms_base = require ("wms_base") wms_station = require( "wms_station" ) local wms_putaway = {_version = "0.2.1"} --[[ 3# Get_StorageCache_Loc 获取输送线存储缓存区货位 5# Get_Usable_Lane 获取可用巷道(有些堆垛机故障后这些巷道会不可用) 6# Get_Station_ExtData 获取机台的扩展数据(里面有mac地址,货位,去向库区等) 7# Get_Usable_Lane 获取机台的货位 8# Get_EmptyBox_Loc ***获取指定库区内的空料箱 Set_Outbound_Wave_Force_Finish 出库波次强制完成 Set_Inbound_Wave_Force_Finish 入库波次强制完成 ]] --////////////////////////////////////////////////////////////////////////// -- 获取巷道关联货位 local function get_aisle_connect_loc( aisle_connect_loc_set, aisle ) local n for n = 1, #aisle_connect_loc_set do if ( aisle_connect_loc_set[n].aisle == aisle ) then return aisle_connect_loc_set[n] end end return '' end -- 巷道内货位排序 local function aisle_loc_sort( loc1, loc2 ) if ( loc1.abc_cls < loc2.abc_cls ) then return true elseif ( loc1.abc_cls == loc2.abc_cls ) then -- 距离入口近的优先 return loc1.weight < loc2.weight end return false end -- 巷道内任务+空料箱排序 local function lane_list_sort( loc1, loc2 ) if ( loc1.abc_cls < loc2.abc_cls ) then return true elseif ( loc1.abc_cls == loc2.abc_cls ) then -- 巷道空位多优先 return loc1.aisle_empty_num > loc2.aisle_empty_num end return false end -- 任务数量abc分类 local function task_num_abc_cls( aisle_list ) local max_task_num = 0 local n, nCount nCount = #aisle_list if ( 0 == nCount ) then return 0 end for n = 1, nCount do if ( aisle_list[n].task_num > max_task_num ) then max_task_num = aisle_list[n].task_num end end if ( max_task_num <= 3 ) then for n = 1, nCount do if ( aisle_list[n].task_num == 0 ) then aisle_list[n].abc_cls = "A" elseif ( aisle_list[n].task_num >= 2 ) then aisle_list[n].abc_cls = "C" else aisle_list[n].abc_cls = "B" end end else for n = 1, nCount do if ( aisle_list[n].task_num == 0 ) then aisle_list[n].abc_cls = "A" elseif ( aisle_list[n].task_num < max_task_num/2 ) then aisle_list[n].abc_cls = "B" else aisle_list[n].abc_cls = "C" end end end table.sort( aisle_list, aisle_list_sort ) end --[[ 适用于立库+输送线的入库作业生成 来源: 巨星二期料箱库 计算料箱库入库区货位(任务均衡),考虑巷道货位均衡, 基本原则是: 巷道任务少优先,巷道空货位多的优先 返回入库缓存区货位和巷道内存储货位(2个货位) V2.0 HAN 20241120 根据站台位置获取相应的 堆垛机入库缓存区,增加一个站台编码 station 参数: station -- 入库站台,站台和立库用输送线连接 --]] function wms_putaway.Get_StorageCache_Loc( strLuaDEID, station ) local nRet, strRetInfo local station_attr --[[ station_attr = {"loc_code":"S-A","entry_area_name":"A","code":"A","factory":"0001","storage_area":"K2","entry_area_code":"K4","mac":"A"} entry_area -- 入库接驳区 storage_area -- 存储区 loc_orde = 0 根据列顺序 1 -- 倒序 ]] nRet, station_attr = jx_base.Get_Station_ExtData( strLuaDEID, station ) if ( nRet ~= 0 ) then return 2, station_attr end local loc_order = lua.Get_NumAttrValue( station_attr.loc_order ) -- 站台指定的入库接驳区 local storage_cache_arae = lua.Get_StrAttrValue( station_attr.entry_area_code ) if ( storage_cache_arae == '') then return 2, "'机台-"..station.."'没有定义入库口区域编码信息!" end -- 获取入库接驳区货位的使用情况 local strCondition = "C_ENABLE = 'Y' AND S_AREA_CODE = '"..storage_cache_arae.."'" local strOrder = "S_CODE" local loc_objs local aisle_connect_loc_set = {} nRet, loc_objs = m3.QueryDataObject( strLuaDEID, "Location", strCondition, strOrder ) if (nRet ~= 0) then lua.Error( strLuaDEID, debug.getinfo(1), "获取【Location】信息失败! " .. loc_objs ) end for n = 1, #loc_objs do obj_attrs = m3.KeyValueAttrsToObjAttr(loc_objs[n].attrs) -- 这些货位里把这个货位对应的巷道信息保存在 S_EXT_ATTR aisle = lua.StrToNumber( obj_attrs.S_EXT_ATTR ) if ( aisle <= 0 or aisle > 4 ) then return 2, "输送线入库缓存位'"..obj_attrs.S_CODE.."'没有定义对应的巷道号或巷道号不合法" end local connect_loc = { aisle = aisle, loc_code = obj_attrs.S_CODE, } table.insert( aisle_connect_loc_set, connect_loc ) end -- 站台指定的入库存储区 local storage_area = lua.Get_StrAttrValue( station_attr.storage_area ) if ( storage_area == '') then return 2, "'机台-"..station.."'没有定义存储区编码信息!" end -- 获取库区 storage_area 内设备状态 local stacker_dev nRet, stacker_dev = wms_wcs.Get_Area_Stacker_Dev_State( strLuaDEID, area_code, station ) if ( nRet ~= 0 ) then return 2, stacker_dev end local n, m local storage_cache_list = {} local obj_attrs local task_num, max_task_num, empty_loc_num local loc_count = #loc_objs local loc_index local connect_loc -- 获取立库巷道口货位的基本信息,保存到 storage_cache_list max_task_num = 0 for n = 1, #stacker_dev do -- 获取设备所在巷道 aisle = stacker_dev[n].aisle -- 这里要改进 **** if ( aisle <= 0 or aisle > 4 ) then return 2, "库区"..storage_area.."中的巷道号只能在1~4!" end -- enable = 1 表示巷道堆垛机可用 if ( stacker_dev[n].enable == 1 ) then task_num = stacker_dev[n].cntr_num if ( task_num > max_task_num ) then max_task_num = task_num end -- 查询货位启用、空货位、在本巷道、没被锁的货位数量 strCondition = "N_AISLE = "..aisle.." AND S_AREA_CODE = '"..storage_area.."' AND C_ENABLE = 'Y' AND N_CURRENT_NUM = 0 AND N_LOCK_STATE = 0" nRet, strRetInfo = mobox.getDataObjCount( strLuaDEID, "Location", strCondition ) if ( nRet ~= 0 ) then return nRet, strRetInfo end empty_loc_num = lua.StrToNumber( strRetInfo ) if ( empty_loc_num > 0 ) then -- 获取该巷道的 入库接驳位 loc_index = 0 for m = 1, #aisle_connect_loc_set do if ( aisle_connect_loc_set[m].lane == aisle ) then loc_index = m break end end if ( loc_index ~= 0 ) then local cache_loc = { loc_code = aisle_connect_loc_set[loc_index].loc_code, aisle = aisle, task_num = task_num, -- 任务数量 aisle_empty_num = empty_loc_num, -- 对应巷道的空货位数量 abc_cls = "" -- ABC 分类 } table.insert( storage_cache_list, cache_loc ) end end end end -- 计算 各入库缓存区的 任务数量 ABC 分类值 没有任务的为 A,任务数大于 0 小于 max_task_num/2 的为B,其余为 C local nCount = #storage_cache_list if ( nCount == 0 ) then return 1, "系统无法分配货位!" end -- 排序 abc_cls 按ABC排序,同样等级的abc_cls 根据 aisle_empty_num 排序,空货位多的在前面 task_num_abc_cls( storage_cache_list ) -- 获取巷道内空货位 local storage_arae nRet, storage_arae = wms_base.Get_sConst2( strLuaDEID, "料箱库存储区" ) if ( nRet ~= 0 ) then lua.Stop( strLuaDEID, "系统无法获取常量'料箱库存储区'") return end local ret_loc -- strLuaDEID, 库区, 巷道号, 货位排序方法, 顺序/倒序, 附加条件, 取货位数量 nRet, ret_loc = wms_alg.Get_Aisle_Empty_Loc( strLuaDEID, storage_arae, storage_cache_list[1].aisle, 0, loc_order, "", 1 ) if ( nRet ~= 0 ) then return 1, "计算巷道"..storage_cache_list[1].aisle.."内货位失败!"..ret_loc end nRet, loc = wms_wh.GetLocInfo( ret_loc.loc_code ) if ( nRet ~= 0 ) then return 1, "获取货位'"..ret_loc.loc_code.."'信息失败! "..loc end -- 返回 立库巷道口入库货位,巷道内存储货位 return 0, storage_cache_list[1].loc_code, ret_loc.loc_code end --[[ 根据站台位置创建一个入库作业,处理以下事情 -- 从立库中获取一个空货位 -- 创建一个作业 -- 给获取的空货位加入库锁 输入参数: -- source_sys 来源系统 -- station 站台位置 -- cntr_code 容器号 -- op_def_name 作业定义名称 -- ext_info 扩展参数{pac_no,bs_type,bs_no,cc_no,dc_no} pac_no 组盘用 cc_no 盘点 dc_no 分拣 返回: nRet operation ]] function jx_base.Create_StorageOperation( strLuaDEID, source_sys, station, cntr_code, op_def_name, ext_info ) local nRet, strRetInfo local loc_code nRet, loc_code = wms_station.Get_Station_Loc( strLuaDEID, station ) if ( nRet ~= 0 ) then return 1, loc_code end local loc nRet, loc = wms_wh.GetLocInfo( loc_code ) if ( nRet ~= 0 ) then return 1, '获取货位信息失败! '..loc_code end --V2.0 创建作业前对容器进行判断,如容器已经存在 active 作业,不能创建 nRet, can_usedin_op = wms_cntr.CanUsedInOperation( strLuaDEID, cntr_code ) if ( nRet ~= 0 ) then return 2, can_usedin_op end if ( can_usedin_op == false ) then return 1, "料箱'"..cntr_code.."'存在未完成的作业,不能继续创建作业!" end local operation = m3.AllocObject(strLuaDEID,"Operation") operation.source_sys = source_sys operation.start_wh_code = loc.wh_code operation.start_area_code = loc.area_code operation.start_loc_code = loc_code -- 计算料箱入库货位,首先计算到哪个输送线入库缓存区 local str_end_loc_code, aisle_in_loc nRet, aisle_in_loc, str_end_loc_code = jx_base.Get_StorageCache_Loc( strLuaDEID, station ) if ( nRet ~= 0 ) then if ( nRet == 1 ) then return 1, "jx_base.Get_StorageCache_Loc 失败!"..aisle_in_loc end return 2, aisle_in_loc end local end_loc nRet, end_loc = wms_wh.GetLocInfo( str_end_loc_code ) if ( nRet ~= 0 ) then return 2, '获取货位信息失败! '..str_end_loc_code end operation.end_wh_code = end_loc.wh_code operation.end_area_code = end_loc.area_code operation.end_loc_code = str_end_loc_code operation.ext_data = aisle_in_loc -- 巷道口入库缓存货位 operation.op_type = wms_base.Get_nConst(strLuaDEID, "作业类型-入库") operation.op_def_name = op_def_name operation.cntr_code = cntr_code if ( ext_info ~= nil and ext_info ~= '') then if ( ext_info.pac_no ~= nil ) then -- 有组盘的(先空料箱呼出) operation.pac_no = ext_info.pac_no end if ( ext_info.cc_no ~= nil ) then -- 盘点回库 operation.cc_no = ext_info.cc_no end if ( ext_info.dc_no ~= nil ) then -- 盘点回库 operation.dc_no = ext_info.dc_no end operation.bs_type = lua.Get_StrAttrValue( ext_info.bs_type ) operation.bs_no = lua.Get_StrAttrValue( ext_info.bs_no ) end nRet, operation = m3.CreateDataObj( strLuaDEID, operation ) if ( nRet ~= 0 ) then return 2, '创建【作业】失败!'..operation end return 0, operation end return wms_putaway