--[[
|
版本: 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_wcs = require ("wms_wcs")
|
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 aisle_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
|
|
--[[ ****
|
适用于立库+输送线的入库作业生成
|
来源: 巨星二期料箱库
|
|
通过站台确定从立库的哪一个入库接驳区进入立库
|
站台的扩展属性
|
{
|
"loc_code":"S-A", -- 站台货位
|
"entry_area_name":"A", -- 接驳区名称
|
"code":"A", -- 站台编码
|
"factory":"81", -- 工厂标识
|
"storage_area":"K2", -- 存储区编码
|
"entry_area_code":"K4", -- 接驳区
|
"mac":"A" -- 站台电脑的mac地址
|
}
|
entry_area -- 入库接驳区 storage_area -- 存储区 loc_orde = 0 根据列顺序 1 -- 倒序
|
计算料箱库入库区货位(任务均衡),考虑巷道货位均衡, 基本原则是: 巷道任务少优先,巷道空货位多的优先
|
返回入库缓存区货位和巷道内存储货位(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 = wms_station.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.Stop( strLuaDEID, "获取【Location】信息失败! " .. loc_objs )
|
return
|
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, storage_area, 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
|
-- 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].aisle == 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 ret_loc
|
|
-- strLuaDEID, 库区, 巷道号, 货位排序方法, 顺序/倒序, 附加条件, 取货位数量
|
nRet, ret_loc = wms_alg.Get_Aisle_Empty_Loc( strLuaDEID, storage_area, 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
|
|
-- 从获取空货位数量, 和查询空货位的SQL
|
local function get_empty_loc_num( strLuaDEID, storage_area )
|
|
local strCondition = "S_WH_CODE = '"..storage_area.wh_code.."'"
|
|
if lua.StrIsEmpty( storage_area.loc_set ) then
|
if not lua.StrIsEmpty( storage_area.area_code ) then
|
strCondition = strCondition.." AND S_AREA_CODE = '"..storage_area.area_code.."'"
|
end
|
if not lua.StrIsEmpty( storage_area.aisle_code ) then
|
strCondition = strCondition.." AND S_AISLE_CODE = '"..storage_area.aisle_code.."'"
|
end
|
if not lua.StrIsEmpty( storage_area.col_set ) then
|
strCondition = strCondition.." AND N_COL = IN ("..storage_area.col_set..") "
|
end
|
if not lua.StrIsEmpty( storage_area.layer_set ) then
|
strCondition = strCondition.." AND N_LAYER = IN ("..storage_area.layer_set..") "
|
end
|
else
|
strCondition = strCondition.." AND S_CODE = IN ("..storage_area.loc_set..") "
|
end
|
strCondition = strCondition.." 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 2, strRetInfo
|
end
|
empty_loc_num = lua.StrToNumber( strRetInfo )
|
return 0, empty_loc_num, strCondition
|
end
|
|
--[[
|
根据上架策略获取容器的储存位置
|
输入参数
|
|
返回参数:
|
nRet, loc_code
|
nRet = 0 找到货位 = 1 没合适货位 2 -- 错误
|
--]]
|
function wms_putaway.Get_Storage_LocArea( strLuaDEID, strategy_code, cntr_code, wh_code )
|
local nRet, strRetInfo
|
|
if wh_code == nil then wh_code = '' end
|
if lua.StrIsEmpty( strategy_code ) then
|
return 2, " wms_putaway.Get_Storage_LocArea 函数中 strategy_code 必须有值"
|
end
|
if lua.StrIsEmpty( cntr_code ) then
|
return 2, " wms_putaway.Get_Storage_LocArea 函数中 cntr_code 必须有值"
|
end
|
|
local strategy
|
nRet, strategy = wms_base.GetStrategyInfo( "Putaway_Strategy", strategy_code )
|
if nRet ~= 0 then
|
return 2, "在获取'上架策略' "..strategy_code.." 时失败! "..strategy
|
end
|
|
local cntr_ext_data = {}
|
nRet, cntr_ext_data = wms_cntr.Get_Container_ExtInfo( strLuaDEID, cntr_code )
|
if nRet ~= 0 then
|
return 2, "获取容器'"..cntr_code.."'的扩展属性失败! -->"..cntr_ext_data
|
end
|
local storage_area_set
|
nRet, storage_area_set = wms_base.Get_Storage_Area_By_Strategy( strategy, cntr_ext_data )
|
if nRet ~= 0 then
|
return 2, storage_area_set
|
end
|
-- 对入库单中的库区和 空料箱呼出的库区 进行Check,如果不一致要报错
|
-- 根据优先级进行分级,同一优先级的要考虑货位均衡(选空货位多)
|
local storage_area_group_list = {} -- 把相同优先级的入库区域放一起
|
|
for _, storage_area in ipairs( storage_area_set ) do
|
if wh_code ~= '' then
|
if storage_area.wh_code ~= wh_code then
|
return 2, "空料箱呼出策略 '"..strategy_code.."' 中定义的仓库编码和入库单中的仓库不一致!"
|
end
|
end
|
if lua.StrIsEmpty( storage_area.area_code ) then
|
return 2, "空料箱呼出策略 '"..strategy_code.."' 中定义的策略必须指定到库区编码!"
|
end
|
|
find = false
|
for _, group in ipairs( storage_area_group_list ) do
|
if group.priority == storage_area.priority then
|
table.insert( group.list, storage_area )
|
find = true
|
break
|
end
|
end
|
|
if not find then
|
local group = {
|
priority = storage_area.priority,
|
list = {}
|
}
|
table.insert( group.list, storage_area )
|
table.insert( storage_area_group_list, group )
|
end
|
end
|
|
local strCondition, empty_loc_num, strOrder, loc_objs, data_attrs
|
local loc_code = ''
|
|
for _, group in ipairs( storage_area_group_list ) do
|
for _, storage_area in ipairs( group.list ) do
|
nRet, empty_loc_num, strCondition = get_empty_loc_num( strLuaDEID, storage_area )
|
if nRet ~= 0 then
|
return 2, empty_loc_num
|
end
|
|
if empty_loc_num > 0 then
|
strOrder = "N_POS_WEIGHT"
|
nRet, loc_objs = m3.QueryDataObject3(strLuaDEID, "Location", strCondition, strOrder, 1 )
|
if (nRet ~= 0) then
|
return 2, "QueryDataObject失败!"..loc_objs
|
end
|
if ( loc_objs ~= '') then
|
data_attrs = m3.KeyValueAttrsToObjAttr(loc_objs[1].attrs)
|
loc_code = data_attrs.S_CODE
|
end
|
if not lua.StrIsEmpty( loc_code ) then
|
return 0, loc_code
|
end
|
end
|
end
|
end
|
return 1, "没有空货位!"
|
end
|
return wms_putaway
|