wsz
2025-06-10 70eedd3bf9810bc022967da6446dd8a0ff6f43b7
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
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
--[[
        和项目关联比较紧密的一些函数
        项目开发和实施人员 可以把 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_station = require( "wms_station" )
external_api = require ( "prj_api" )
 
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_code.."-堆垛机设备"
    nRet, strRetInfo = wms_base.Get_sConst2( strLuaDEID, 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 = external_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_Lane( 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 
    }    
]]
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 )
 
    -- 【组盘容器】数据对象属性判断,不合法的终止程序执行
    if ( b_state ~= 0 ) then
        msg = "预分配号'"..pac_no.."'的状态不是未执行状态,不能启动料箱出库作业!"
        lua.Warning( strLuaDEID, debug.getinfo(1), msg )
        return 1, msg
    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 = wms_base.Get_nConst(strLuaDEID, "作业类型-出库")
    operation.op_def_name = "料箱出库"                 -- 注意这些可能会根据项目的不同而不同,就是出库的作业类型
    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 表示组盘容器已经安排作业进行搬运
    local strUpdateSql = "S_FROM_LOC = '"..from_loc_code.."', N_B_STATE = 1, 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 表示入库波次已经开始作业
        strUpdateSql = "N_B_STATE = 2, 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 = 2, 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
 
--//////////////////////////////////////////////////////////////////////////
-- 巷道任务+空料箱排序
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
 
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
function prj_base.Get_StorageCache_Loc( strLuaDEID, station )
    local nRet, strRetInfo
    local task_type = wms_base.Get_nConst( strLuaDEID, "任务类型-输送线入库搬运" )
 
    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.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,
            --capacity = lua.StrToNumber( obj_attrs.N_CAPACITY ),
            --cur_num = lua.StrToNumber( obj_attrs.N_CURRENT_NUM ),            
        } 
        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 = prj_base.Get_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
        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].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 根据roadway_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     扩展参数{carry_cb_cls,carry_cb_no, bs_type,bs_no,cc_no,dc_no}
                        carry_cb_cls -- 作业携带容器的业务类型(预分配容器,配盘容器,盘点容器)
    返回:
        nRet
        operation
]] 
 
function prj_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 = prj_base.Get_StorageCache_Loc( strLuaDEID, station )
    if ( nRet ~= 0 ) then
        if ( nRet == 1 ) then
            return 1, "prj_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
        operation.carry_cb_cls = lua.Get_StrAttrValue( ext_info.carry_cb_cls )
        operation.carry_cb_no = lua.Get_StrAttrValue( ext_info.carry_cb_no )
        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
 
--[[
 
    检查一下预入库容器 pac_no 下面的 detail 是否已经完成处理完成,如果完成处理 创建一个 入库作业
    适合预分配料箱入库作业,station 为站台编码, cntr_code 是料箱编码
    返回值:
        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        
 
        -- 创建【货品入库】作业. 【组盘容器】状态改为3, CG_Detail 加【组盘容器明细】中的内容
        local ext_info = { carry_cb_cls = "Pre_Alloc_Container", carry_cb_no = pac_no }
        nRet, operation = prj_base.Create_StorageOperation( strLuaDEID, "WMS", station, cntr_code, "货品入库", ext_info )
        if ( nRet ~= 0 ) then
            mobox.setInfo( strLuaDEID, "预分配料箱组盘完成后,无法创建入库作业! 错误号:100 -->"..operation )
            -- 7 组盘完成入库时发现入不了库,回库出现问题, 问题编码 5100
            strUpdateSql = "N_B_STATE = 7, 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 (回库)
        local strUpdateSql = "S_BACK_OP_NO = '"..operation.code.."', N_B_STATE = 4"
        strCondition = "S_PAC_NO = '"..pac_no.."'"
        nRet, strRetInfo = mobox.updateDataAttrByCondition( strLuaDEID, "Pre_Alloc_Container", strCondition, strUpdateSql )
        if ( nRet ~= 0 ) then  return 2, "更新【组盘容器】信息失败!"..strRetInfo end   
 
        --容器中的【CG_Detail】新增,把【组盘容器明细】中的内容加入
        nRet, strRetInfo = wms_cntr.Add_CG_Detail_By_PAC_Detail( strLuaDEID, cntr_code, pac_no )
        if ( nRet ~= 0 ) then return 2, 'wms_cntr.Add_CG_Detail_By_PAC_Detail!'..strRetInfo end 
         
        --重置料箱信息
        local container
        nRet, container = wms_cntr.GetInfo( strLuaDEID, 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 
        
        --入库作业产生需要对入库作业的终点获取加入库锁
        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 = "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 = "3055 TOP VIEW", ctrl_id = "S_CNTR_CODE", value = ""}
        }                        
    end   
 
    return 0, action  
end
 
return  prj_base