1
Jianw
9 天以前 70f29da38121b9a467841253e3268feb5df02902
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
--[[
    版本:     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