lzh
2025-06-19 3a6436e0c88042c6ce8dca2fe8adb0109f0ad9e4
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
wms_cntr = require("wms_container")
require("WMS-Roadway")
wms_op = require("wms_operation")
-- index 必须是table才能作为引用参数,函数内的变化才会返回
local function get_empty_loc(strLuaDEID, strCondition, empty_loc_set, index, priority)
    local nRet, strRetInfo
    local strOrder = "N_COL, N_LAYER" -- 最近巷道口
 
    -- 最多获取 10 条
    nRet, strRetInfo = mobox.queryDataObjAttr3(strLuaDEID, "Location", strCondition, 10, strOrder)
    if (nRet ~= 0) then
        return 1, "获取货位信息错误! " .. strRetInfo .. " SQL条件: " .. strCondition
    end
 
    if (strRetInfo ~= '') then
        local loc
        local retObjs = json.decode(strRetInfo)
 
        for n = 1, #retObjs do
            nRet, loc = m3.ObjAttrStrToLuaObj("Location", lua.table2str(retObjs[n].attrs))
            if (nRet ~= 0) then lua.Error(strLuaDEID, debug.getinfo(1), "m3.ObjAttrStrToLuaObj(Location) 失败! " .. loc) end
 
            local empty_loc = {}
            empty_loc.priority = priority -- 分配优先级最高
            empty_loc.loc_code = loc.code
            empty_loc.row = loc.row
            empty_loc.col = loc.col
            empty_loc.layer = loc.layer
            empty_loc_set[index.value] = empty_loc
            index.value = index.value + 1
            if (index.value == index.max) then break end
        end
    end
    return 0, ""
end
 
-- 空货位根据优先级排序 priority, priority 相等根据 col, layer 排序
local function empty_loc_sort(a, b)
    if (a.priority < b.priority) then
        return true
    elseif (a.priority == b.priority) then
        if (a.col < b.col) then
            return true
        elseif (a.col == b.col) then
            return a.layer < b.layer
        else
            return false
        end
    else
        return false
    end
end
 
-- 巷道任务数量少的放前面, 任务数量 相同balanc值小的放前面
local function roadway_balance_sort(a, b)
    if (a.op_num < b.op_num) then
        return true
    elseif (a.op_num == b.op_num) then
        return a.balance < b.balance
    else
        return false
    end
end
 
-- 获取【巷道】对象
local function get_roadway_dataobject(roadway_list, zone_code)
    for n = 1, #roadway_list do
        if (roadway_list[n].zone_code == zone_code) then
            return roadway_list[n]
        end
    end
    return nil
end
 
-- 获取 物料\空托 在立库中的内深位对应的外深位
--[[
    输入:
        area_code   -- 库区
        roadway     -- 巷道
        batch_no    -- 批次号
        is_kt       -- true:空托 false:非空托
        cntr_code   -- 托盘号
        is_true     -- true:起点为一楼左侧回库口,设备限制只能入5、6巷道
    返回:
        0,{loc_code:xxx}
--]]
function QueryLocInfo(strLuaDEID, data)
    local nRet, strRetInfo
    -- 设置返回获取的货位
    local ret_value = {}
    local str_area_code = data.area_code -- 库区
    local str_roadway = data.roadway     -- 巷道
    local str_batch_no = data.batch_no   -- 批次号
    local is_kt = data.is_kt             -- 是否是空托
    local cntr_code = data.cntr_code     -- 托盘号
    local is_true = data.is_true         -- 获取空托外深位时,如果起点为一楼左侧回库口则设备限制只能入5、6巷道
 
    -- 获取前10条的货位信息
    local strCondition = "N_LOCK_STATE = 0 AND N_POS = 2 AND S_AREA_CODE = '" .. str_area_code .. "' AND S_CODE IN"
    if (is_kt == false) then
        strCondition = strCondition .. " (SELECT S_LOC_CODE FROM TN_Loc_Container WHERE S_CNTR_CODE IN"
        strCondition = strCondition ..
            "(SELECT S_CNTR_CODE FROM TN_CG_Detail WHERE S_BATCH_NO = '" .. str_batch_no .. "'))"
    else
        -- 托盘和胶框的前6位编码规则不同
        -- 托盘工装(9000片):106EGPAA0001~106EGPAA3000
        -- 胶框(42000片):106EGRCA0001~106EGRCA9999和106EGRCB0001~106EGRCB4001
        local cntr_rule = string.sub(cntr_code, 1, 6) -- 截取前6位字符,用来判断胶框、托盘
        strCondition = strCondition ..
            " (SELECT S_LOC_CODE FROM TN_Loc_Container WHERE S_CNTR_CODE LIKE '" .. cntr_rule .. "%'"
        strCondition = strCondition .. " AND S_CNTR_CODE NOT IN (SELECT S_CNTR_CODE FROM TN_CG_Detail))"
 
        -- 获取空托外深位时,如果起点为一楼左侧回库口则设备限制只能入5、6巷道
        if (is_true == true) then
            str_roadway = wms_base.Get_sConst(strLuaDEID, "佳通-一楼左侧回库口巷道")
        end
    end
    -- 巷道不为空则根据条件筛选巷道
    if (str_roadway ~= nil and str_roadway ~= '') then
        strCondition = strCondition .. " AND N_ROADWAY IN (" .. str_roadway .. ")"
    end
    nRet, strRetInfo = mobox.queryDataObjAttr3(strLuaDEID, "Location", strCondition, 50, "")
    if (nRet ~= 0) then
        return 2, "queryDataObjAttr3失败!" .. strRetInfo
    end
 
    -- 没有相同批次物料的货位直接返回
    if (strRetInfo == '') then return 0, "" end
    local retObjs = json.decode(strRetInfo)
 
    local next_loc = {} -- 用来存放外深位编号
    for i = 1, #retObjs do
        local attr, roadway, row_group
        attr = retObjs[i].attrs
        attr = m3.KeyValueAttrsToObjAttr(attr)          -- 数组下标为 i 的货位信息
        if (attr == nil) then goto coroutine end
        row_group = tonumber(attr.N_ROW_GROUP)          -- 当前货位的排组号
        local loc_code = attr.S_CODE
        local loc_code_table = lua.split(loc_code, "-") -- 通过符号分割字符串为数组
        local loc_row = loc_code_table[2]
 
        -- 通过库区、排组号 定位巷道信息
        strCondition = "S_AREA_CODE = '" .. str_area_code .. "' AND (N_LEFT_ROW_GROUP = "
            .. row_group .. " or N_RIGHT_ROW_GROUP = " .. row_group .. " )"
        nRet, roadway = mobox.queryDataObjAttr3(strLuaDEID, "Roadway", strCondition, 1, "")
        if (nRet ~= 0) then
            return 2, "queryDataObjAttr3失败!" .. strRetInfo
        end
 
        roadway = json.decode(roadway)
        roadway = roadway[1].attrs
        roadway = m3.KeyValueAttrsToObjAttr(roadway) -- 巷道信息
        if (roadway == nil) then goto coroutine end
 
        local row = "" -- 所属巷道信息中的 (内/外 深位)2排 的字符串信息(例: 21,22,)
        if (row_group == tonumber(roadway.N_LEFT_ROW_GROUP)) then
            row = roadway.S_LEFT_ROW
        else
            row = roadway.S_RIGHT_ROW
        end
        local row_table = lua.split(row, ",")
        local str = ""
        for j = 1, #row_table do
            if (loc_row ~= row_table[j]) then
                str = row_table[j]
            end
        end
 
        if (str == "") then goto coroutine end
        if (#str == 1) then str = "0" .. str end
 
        local new_loc_code = loc_code_table[1] .. "-" .. str .. "-" .. loc_code_table[3] .. "-" .. loc_code_table[4]
        strCondition = "N_LOCK_STATE = 0 AND N_CURRENT_NUM = 0 AND S_CODE = '" .. new_loc_code .. "'"
        nRet, strRetInfo = mobox.getDataObjCount(strLuaDEID, "Location", strCondition)
        if (nRet ~= 0) then return 1, strRetInfo end
        if (tonumber(strRetInfo) > 0) then
            if (#next_loc == 0) then
                ret_value.loc_code = new_loc_code
            end
        end
        ::coroutine::
    end
    lua.Debug(strLuaDEID, debug.getinfo(1), 'ret_value', ret_value)
    return 0, ret_value
end
 
-- 获取立库内深位对应的外深位
function QueryOutLoc(strLuaDEID, loc_code)
    local loc_code_table = lua.split(loc_code, "-") -- 通过符号分割字符串为数组
    local loc_row = loc_code_table[2]
 
    -- 获取货位的信息
    local nRet, loc_info = wms_wh.GetLocInfo(loc_code)
    if (nRet ~= 0) then return 2, 'GetLocInfo失败!' .. loc_info end
 
    -- 通过库区、排组号 定位巷道信息
    local roadway
    local strCondition = "S_AREA_CODE = 'LK' AND (N_LEFT_ROW_GROUP = "
        .. loc_info.row_group .. " or N_RIGHT_ROW_GROUP = " .. loc_info.row_group .. " )"
    nRet, roadway = mobox.queryDataObjAttr3(strLuaDEID, "Roadway", strCondition, 1, "")
    if (nRet ~= 0) then return 2, "queryDataObjAttr3失败!" .. strRetInfo end
 
    roadway = json.decode(roadway)
    roadway = roadway[1].attrs
    roadway = m3.KeyValueAttrsToObjAttr(roadway) -- 巷道信息
    if (roadway == nil) then return 2, "巷道信息获取失败!" end
 
    local row = "" -- 所属巷道信息中的 (内/外 深位)2排 的字符串信息(例: 21,22,)
    if (loc_info.row_group == tonumber(roadway.N_LEFT_ROW_GROUP)) then
        row = roadway.S_LEFT_ROW
    else
        row = roadway.S_RIGHT_ROW
    end
    local row_table = lua.split(row, ",")
    local str = ""
    for j = 1, #row_table do
        if (loc_row ~= row_table[j]) then
            str = row_table[j]
        end
    end
 
    if (str == "") then return 2, "获取排失败!" end
    if (#str == 1) then str = "0" .. str end
 
    local new_loc_code = loc_code_table[1] .. "-" .. str .. "-" .. loc_code_table[3] .. "-" .. loc_code_table[4]
    return 0, new_loc_code
end
 
-- 获取库区里的巷道对象
--[[
    输入:
        area_code 库区编码
        bNoLock 可以不输入,默认= false, true 表示获取的zoon不能被禁用
    返回:
    字符串 [{roadway-data-object},{}]
--]]
function GetRoadWayList(strLuaDEID, area_code, bNoLock, condition)
    local nRet, strRetInfo, strCondition
    local strOrder = "N_ROADWAY"
 
    if (bNoLock == nil) then
        bNoLock = false
    else
        bNoLock = true
    end
 
    if (area_code == nil or area_code == '') then
        lua.Error(strLuaDEID, debug.getinfo(1), "调用 WMS_GetZoneListByGroup 函数时参数不正确,库区编码不能为空!")
    end
 
    if (condition ~= nil and condition ~= '') then
        strCondition = "S_AREA_CODE = '" .. area_code .. "' AND N_ROADWAY IN (" .. condition .. ")"
    else
        strCondition = "S_AREA_CODE = '" .. area_code .. "'"
    end
 
    if (bNoLock) then
        strCondition = strCondition .. " AND N_LOCK_STATE = 0"
    end
    nRet, strRetInfo = mobox.queryDataObjAttr(strLuaDEID, "Roadway", strCondition, strOrder)
    if (nRet ~= 0) then lua.Error(strLuaDEID, debug.getinfo(1), "获取【巷道】信息失败! " .. strRetInfo) end
 
    local return_data = {}
    if (strRetInfo ~= '') then
        local retObjs = json.decode(strRetInfo)
        local n
 
        for n = 1, #retObjs do
            local roadway = {}
            nRet, roadway = m3.ObjAttrStrToLuaObj("Roadway", lua.table2str(retObjs[n].attrs))
            roadway.id = lua.trim_guid_str(retObjs[n].id)
            roadway.cls = "Roadway"
            if (nRet ~= 0) then
                lua.Error(strLuaDEID, debug.getinfo(1),
                    "m3.ObjAttrStrToLuaObj(CG_Detail) 失败! " .. cg_detail)
            end
            return_data[n] = roadway
        end
    end
    return return_data
end
 
--[[
    注:对深位>2的无效
    area_code -- 库区编码
    roadway_balance -- 巷道信息
    ext_condition  -- 扩展条件,比如 S_BATCH_NO = 'A1'
 
    返回:
    [{loc_code:"",priority:1~3, row:x, col:y, layer:12 }]
--]]
function GT_GetEmptyLocInRoadway(strLuaDEID, area_code, roadway_balance, ext_condition)
    local nRet, strRetInfo
    local strCondition = ''
    local empty_loc_set = {} -- 空货位集
    local index = {}
    index.value = 1
    index.max = 11 -- 空货位集最多10个
 
    if (roadway_balance == nil) then
        return 1, "无效参数!"
    end
    if (ext_condition == nil) then ext_condition = '' end
 
    -- 获取最里面的空货位
    -- 左边是双深位
    if (roadway_balance.left_deep == 2) then
        -- 获取左边最里面的空货位
        strCondition = "N_CURRENT_NUM = 0 AND S_AREA_CODE = '" ..
            area_code .. "' AND N_ROW_GROUP = " .. roadway_balance.left_row_group
        strCondition = strCondition .. " AND N_LOCK_STATE = 0 AND C_ENABLE = 'Y' AND N_POS = 2"
        if (ext_condition ~= '') then
            strCondition = strCondition .. "AND N_LAYER IN (" .. ext_condition .. ")"
        end
        nRet, strRetInfo = get_empty_loc(strLuaDEID, strCondition, empty_loc_set, index, 2)
        if (nRet ~= 0) then return 1, strRetInfo end
        if (index.value >= index.max) then goto go_back end
    end
    -- 右边是双深位
    if (roadway_balance.right_deep == 2) then
        -- 获取右边最里面的空货位
        strCondition = "N_CURRENT_NUM = 0 AND S_AREA_CODE = '" ..
            area_code .. "' AND N_ROW_GROUP = " .. roadway_balance.right_row_group
        strCondition = strCondition .. " AND N_LOCK_STATE = 0 AND C_ENABLE = 'Y' AND N_POS = 2"
        if (ext_condition ~= '') then
            strCondition = strCondition .. "AND N_LAYER IN (" .. ext_condition .. ")"
        end
        nRet, strRetInfo = get_empty_loc(strLuaDEID, strCondition, empty_loc_set, index, 2)
        if (nRet ~= 0) then return 1, strRetInfo end
        if (index.value >= index.max) then goto go_back end
    end
 
    -- 获取左边最外面的空货位
    strCondition = "N_CURRENT_NUM = 0 AND S_AREA_CODE = '" ..
        area_code .. "' AND N_ROW_GROUP = " .. roadway_balance.left_row_group
    strCondition = strCondition .. " AND N_LOCK_STATE = 0 AND C_ENABLE = 'Y' AND N_POS = 1"
    if (ext_condition ~= '') then
        strCondition = strCondition .. "AND N_LAYER IN (" .. ext_condition .. ")"
    end
    nRet, strRetInfo = get_empty_loc(strLuaDEID, strCondition, empty_loc_set, index, 3)
    if (nRet ~= 0) then return 1, strRetInfo end
    if (index.value >= index.max) then goto go_back end
 
    -- 获取右边最外面的空货位
    strCondition = "N_CURRENT_NUM = 0 AND S_AREA_CODE = '" ..
        area_code .. "' AND N_ROW_GROUP = " .. roadway_balance.right_row_group
    strCondition = strCondition .. " AND N_LOCK_STATE = 0 AND C_ENABLE = 'Y' AND N_POS = 1"
    if (ext_condition ~= '') then
        strCondition = strCondition .. "AND N_LAYER IN (" .. ext_condition .. ")"
    end
    nRet, strRetInfo = get_empty_loc(strLuaDEID, strCondition, empty_loc_set, index, 3)
    if (nRet ~= 0) then return 1, strRetInfo end
 
    ::go_back::
    --  排序
    table.sort(empty_loc_set, empty_loc_sort)
    return 0, empty_loc_set
end
 
-- 初始化巷道物料均衡值
-- 输入参数:
-- roadway_list 【巷道】数据对象
-- input_paramter 入库的物料信息,{"item_code":"","batch_no":"zz",...}
-- 返回: 0 -- 成功 1 -- 没有空余库位 2 -- 错误
--[[
      [
        {
            "roadway":1~, "zone_code":"a","amount":0~999, "balance":1~3,"op_num":0,
            "left_deep":2,"right_deep":1,"left_row_group":1,"right_row_group":2,
            "location":{"code":"","priority":1~3}},
        -- roadway  巷道编号  zone_code -- 巷道逻辑库区编码 amount -- 巷道内符合条件的货品所在货位数量
        -- balance  均衡度
        -- op_num   作业数量
        -- location 巷道内优先上架的货位
        ...
      ]
    -- balance 是货物在巷道内的均衡度 巷道内同批次物料数量为0时,该值=1
    -- 巷道内同批次物料数量小于目前同批次物料(总数/巷道数)时该值=2,其余为3
--]]
function Set_roadway_balance(strLuaDEID, roadway_list, input_paramter)
    local nRet, strRetInfo
 
    if (input_paramter.batch_no == nil or input_paramter.batch_no == '') then
        return 2, "输入参数中必须要有物料批次信息(batch_no)"
    end
 
    -- 获取 roadway_list 中的逻辑库区编码集合
    local roadway_code
    nRet, roadway_code = wms_wh.GetRoadwayZoneCodeSet(roadway_list)
    if (nRet ~= 0) then return nRet, roadway_code end
 
    local nTotal, nNum
    local cg_detail_condtion = "S_BATCH_NO = '" .. input_paramter.batch_no .. "'"
    local roadway_balance_set = {}
 
    if (#roadway_code == 0) then
        return 2, "无可用巷道"
    end
    -- 获取逻辑库区中的数量信息,判断如果巷道没空货位就不继续判断
    nRet, strRetInfo = wms.wms_GetZoneNumInfo(lua.table2str(roadway_code))
 
 
    -- 返回 [{ "code": "XXX", "store_max": X, "available_in": X, "empty_min": X, "empty_max": X },..]
    if (nRet ~= 0) then
        return 2, "wms_GetZoneNumInfo失败!" .. strRetInfo
    end
    local roadway_code_info = json.decode(strRetInfo)
    local nIndex = 1
 
    nTotal = 0 -- 已经存在的货品在要分配货位巷道中的总数量
 
    local obj
    for n = 1, #roadway_code_info do
        -- 如果有空货位
        if (roadway_code_info[n].available_in > 0) then
            -- 获取巷道内存储货品批次 = batch_no 的货位数量
            nNum = wms_cntr.Get_CG_Detail_Count_InZone(strLuaDEID, roadway_code_info[n].code, cg_detail_condtion)
            -- 统计该逻辑库区 执行中的任务的 批次入库数量
            -- 条件
            -- 1、任务的巷道为当前逻辑库区
            -- 2、任务的容器批次号要和当前入库批次相同
            -- 3、任务的类型为 5 立库入库搬运,任务的状态为 1 已推送
            local strCondition =
                "S_CODE IN (SELECT S_OP_CODE FROM TN_TASK WHERE N_TYPE = 5 AND N_B_STATE = 1 AND N_ROADWAY = (SELECT N_ROADWAY FROM TN_Roadway WHERE S_ZONE_CODE = '" ..
                roadway_code_info[n].code .. "'))"
            strCondition = strCondition ..
                " AND S_CNTR_CODE IN (SELECT S_CNTR_CODE FROM TN_CG_Detail WHERE " .. cg_detail_condtion .. ")"
            nRet, strRetInfo = mobox.getDataObjCount(strLuaDEID, "Operation", strCondition)
            if (nRet ~= 0) then
                lua.Error(strLuaDEID, debug.getinfo(1), "getDataObjCount 失败! " .. strRetInfo)
            end
            local num = lua.StrToNumber(strRetInfo)
            nNum = nNum + num -- 巷道内存储货品批次 = batch_no 的货位数量 + 搬运执行中 货品批次 = batch_no 的货位数量
 
            nTotal = nTotal + nNum
 
            local roadway_balance = {}
            roadway_balance.zone_code = roadway_code_info[n].code
            roadway_balance.amount = nNum
            roadway_balance.balance = 0
            roadway_balance.location = {}
            -- 获取巷道对象
            obj = get_roadway_dataobject(roadway_list, roadway_balance.zone_code)
            -- 通过逻辑库区编码获取执行中的巷道任务数量
            strCondition = "N_ROADWAY = (SELECT N_ROADWAY FROM TN_Roadway WHERE S_ZONE_CODE = '" .. roadway_balance.zone_code .. "') AND N_B_STATE = 1"
            nRet, strRetInfo = mobox.getDataObjCount(strLuaDEID, "Task", strCondition)
            if (nRet ~= 0) then lua.Error(strLuaDEID, debug.getinfo(1), "getDataObjCount 失败! " .. strRetInfo) end
            local op_num = lua.StrToNumber(strRetInfo)
 
            if (obj ~= nil) then
                roadway_balance.op_num = op_num
                roadway_balance.left_deep = obj.left_deep
                roadway_balance.right_deep = obj.right_deep
                roadway_balance.left_row_group = obj.left_row_group
                roadway_balance.right_row_group = obj.right_row_group
            else
                roadway_balance.op_num = op_num
                roadway_balance.left_deep = 0
                roadway_balance.right_deep = 0
                roadway_balance.left_row_group = 0
                roadway_balance.right_row_group = 0
            end
 
            roadway_balance_set[nIndex] = roadway_balance
            nIndex = nIndex + 1
        end
    end
 
    local nCount = #roadway_balance_set
    if (nCount == 0) then
        return 1, "巷道没有空余货位!"
    end
 
    -- 计算 balance
    local fAverage = nTotal / nCount -- 巷道平均值
    for n = 1, nCount do
        if (roadway_balance_set[n].amount == 0) then
            roadway_balance_set[n].balance = 1
        elseif (roadway_balance_set[n].amount <= fAverage) then
            roadway_balance_set[n].balance = 2
        else
            roadway_balance_set[n].balance = 3
        end
    end
 
    -- 根据 balance 排序
    table.sort(roadway_balance_set, roadway_balance_sort)
    return 0, roadway_balance_set
end
 
-- 初始化巷道空工装均衡值
-- 输入参数:
-- roadway_list 【巷道】数据对象
-- 返回: 0 -- 成功 1 -- 没有空余库位 2 -- 错误
--[[
      [
        {
            "roadway":1~, "zone_code":"a","amount":0~999, "balance":1~3,"op_num":0,
            "left_deep":2,"right_deep":1,"left_row_group":1,"right_row_group":2,
            "location":{"code":"","priority":1~3}},
        -- roadway  巷道编号  zone_code -- 巷道逻辑库区编码 amount -- 巷道内符合条件的货品所在货位数量
        -- balance  均衡度
        -- op_num   作业数量
        -- location 巷道内优先上架的货位
        ...
      ]
    -- balance 是KGZ在巷道内的均衡度 巷道内KGZ数量为0时,该值=1
    -- 巷道内KGZ数量小于目前KGZ(总数/巷道数)时该值=2,其余为3
--]]
function Set_KGZ_roadway_balance(strLuaDEID, roadway_list)
    local nRet, strRetInfo
    -- 获取 roadway_list 中的逻辑库区编码集合
    local roadway_code
    nRet, roadway_code = wms_wh.GetRoadwayZoneCodeSet(roadway_list)
    if (nRet ~= 0) then return nRet, roadway_code end
 
    local nTotal, nNum
    local cg_detail_condtion = "S_ITEM_CODE = 'KGZ'"
    local roadway_balance_set = {}
 
    if (#roadway_code == 0) then
        return 2, "无可用巷道"
    end
    -- 获取逻辑库区中的数量信息,判断如果巷道没空货位就不继续判断
    nRet, strRetInfo = wms.wms_GetZoneNumInfo(lua.table2str(roadway_code))
 
 
    -- 返回 [{ "code": "XXX", "store_max": X, "available_in": X, "empty_min": X, "empty_max": X },..]
    if (nRet ~= 0) then
        return 2, "wms_GetZoneNumInfo失败!" .. strRetInfo
    end
    local roadway_code_info = json.decode(strRetInfo)
    local nIndex = 1
 
    nTotal = 0 -- 已经存在的货品在要分配货位巷道中的总数量
 
    local obj
    for n = 1, #roadway_code_info do
        -- 如果有空货位
        if (roadway_code_info[n].available_in > 0) then
            -- 获取巷道内存储货品批次 = batch_no 的货位数量
            nNum = wms_cntr.Get_CG_Detail_Count_InZone(strLuaDEID, roadway_code_info[n].code, cg_detail_condtion)
            -- 统计该逻辑库区 执行中的任务的 批次入库数量
            -- 条件
            -- 1、任务的巷道为当前逻辑库区
            -- 2、任务的容器批次号要和当前入库批次相同
            -- 3、任务的类型为 5 立库入库搬运,任务的状态为 1 已推送
            local strCondition =
                "S_CODE IN (SELECT S_OP_CODE FROM TN_TASK WHERE N_TYPE = 5 AND N_B_STATE = 1 AND N_ROADWAY = (SELECT N_ROADWAY FROM TN_Roadway WHERE S_ZONE_CODE = '" ..
                roadway_code_info[n].code .. "'))"
            strCondition = strCondition ..
                " AND S_CNTR_CODE IN (SELECT S_CNTR_CODE FROM TN_CG_Detail WHERE " .. cg_detail_condtion .. ")"
            nRet, strRetInfo = mobox.getDataObjCount(strLuaDEID, "Operation", strCondition)
            if (nRet ~= 0) then
                lua.Error(strLuaDEID, debug.getinfo(1), "getDataObjCount 失败! " .. strRetInfo)
            end
            local num = lua.StrToNumber(strRetInfo)
            nNum = nNum + num -- 巷道内存储货品批次 = batch_no 的货位数量 + 搬运执行中 货品批次 = batch_no 的货位数量
 
            nTotal = nTotal + nNum
 
            local roadway_balance = {}
            roadway_balance.zone_code = roadway_code_info[n].code
            roadway_balance.amount = nNum
            roadway_balance.balance = 0
            roadway_balance.location = {}
            -- 获取巷道对象
            obj = get_roadway_dataobject(roadway_list, roadway_balance.zone_code)
            -- 通过逻辑库区编码获取执行中的巷道任务数量
            strCondition = "N_ROADWAY = (SELECT N_ROADWAY FROM TN_Roadway WHERE S_ZONE_CODE = '" .. roadway_balance.zone_code .. "') AND N_B_STATE = 1"
            nRet, strRetInfo = mobox.getDataObjCount(strLuaDEID, "Task", strCondition)
            if (nRet ~= 0) then lua.Error(strLuaDEID, debug.getinfo(1), "getDataObjCount 失败! " .. strRetInfo) end
            local op_num = lua.StrToNumber(strRetInfo)
 
            if (obj ~= nil) then
                roadway_balance.op_num = op_num
                roadway_balance.left_deep = obj.left_deep
                roadway_balance.right_deep = obj.right_deep
                roadway_balance.left_row_group = obj.left_row_group
                roadway_balance.right_row_group = obj.right_row_group
            else
                roadway_balance.op_num = op_num
                roadway_balance.left_deep = 0
                roadway_balance.right_deep = 0
                roadway_balance.left_row_group = 0
                roadway_balance.right_row_group = 0
            end
 
            roadway_balance_set[nIndex] = roadway_balance
            nIndex = nIndex + 1
        end
    end
 
    local nCount = #roadway_balance_set
    if (nCount == 0) then
        return 1, "巷道没有空余货位!"
    end
 
    -- 计算 balance
    local fAverage = nTotal / nCount -- 巷道平均值
    for n = 1, nCount do
        if (roadway_balance_set[n].amount == 0) then
            roadway_balance_set[n].balance = 1
        elseif (roadway_balance_set[n].amount <= fAverage) then
            roadway_balance_set[n].balance = 2
        else
            roadway_balance_set[n].balance = 3
        end
    end
 
    -- 根据 balance 排序
    table.sort(roadway_balance_set, roadway_balance_sort)
    return 0, roadway_balance_set
end