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
--[[
    版本:     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