350 lines
20 KiB
PHP
350 lines
20 KiB
PHP
|
<?php
|
|||
|
|
|||
|
namespace app\action;
|
|||
|
|
|||
|
use libs\listener\Event;
|
|||
|
use libs\db\Db;
|
|||
|
use Exception;
|
|||
|
|
|||
|
// 物料投入
|
|||
|
class SetMaterialInput
|
|||
|
{
|
|||
|
public function execute($post)
|
|||
|
{
|
|||
|
return $this->do_set_material_input($post);
|
|||
|
}
|
|||
|
/*
|
|||
|
制片段设备物料投料出现以下场景:
|
|||
|
一、一个或多个原材料 => 半成品
|
|||
|
例子:镍钴锰酸锂+导电剂 => 正极搅拌半成品
|
|||
|
二、半成品+原材料 => 新半成品
|
|||
|
例子:正极搅拌半成品 + 铝箔 => 涂布半成品
|
|||
|
三、半成品 => 新半成品
|
|||
|
例子:涂布半成品 => 辊压半成品
|
|||
|
接口api示例
|
|||
|
{
|
|||
|
"action": "set_material_input",
|
|||
|
"param": {
|
|||
|
"batch": "", #当batch不为空,代表正极制片段、负极制片段、装配段第一个工序;当batch为空时,代表正极制片段、负极制片段、装配段除了第一个工序其他工序,batch以半成品(wip)的批次为准
|
|||
|
"workingsubclass": "ZJ", # 工序单元
|
|||
|
"device_code": "FJZJ-1", # 设备编码
|
|||
|
"in": [{
|
|||
|
"item_code":"tongbo", #物料编码
|
|||
|
"item_batch": "fengezi-1", #物料批次
|
|||
|
"item_quantity":"345.34", #投入数量
|
|||
|
"item_id": "-1" #物料虚拟码,当item_id=-1时,代表原材料;当item_id!=-1,代表半成品(wip)
|
|||
|
}]
|
|||
|
}
|
|||
|
}
|
|||
|
*/
|
|||
|
|
|||
|
/*
|
|||
|
* 通过batch->flow->model->bom->workingsubclass->BomRelationship对应的bom结构
|
|||
|
* 1.获取请求体设备编码参数
|
|||
|
* 2.判断工序单元、设备编码是存在?根据设备编码查询批次工序日志表,判断该设备是否已经存在生产中物料信息
|
|||
|
* 3.获取生产批次关联工艺流程,工艺流程关联型号,型号关联bom,bom关联bom关系,bom关系关联物料信息
|
|||
|
* 4.通过以上关联信息查询物料编码
|
|||
|
* 4.1.获取bom的物料编码
|
|||
|
* 4.2.获取param_in结构的物料编码
|
|||
|
* 5.检查上传param参数中in结构是否缺少物料
|
|||
|
* 6.剔除上传param参数中in结构多余物料信息,获取最终需要处理param_in
|
|||
|
* 7.判断原材料或半成品是否已经投入批次工序日志缓存表,不允许重复再投,只能先把上一次投入物料结束,才能允许再投入;
|
|||
|
* 8.拼接存储结构,上传数据到批次工序日志缓存表;原材料新增数据,半成品更改半成品状态为0
|
|||
|
* 9.半成品更改半成品状态为0,表示进行设备投料
|
|||
|
* 10.原材料数组数据写入缓存表
|
|||
|
*/
|
|||
|
|
|||
|
private function do_set_material_input($post)
|
|||
|
{
|
|||
|
// 验证数据
|
|||
|
$param = $post['param'];
|
|||
|
//获取上一工作站
|
|||
|
try {
|
|||
|
if (empty($param["workingsubclass"])) {
|
|||
|
throw new Exception("缺少workingsubclass参数");
|
|||
|
}
|
|||
|
if (empty($param["device_code"])) {
|
|||
|
throw new Exception("缺少device_code参数");
|
|||
|
}
|
|||
|
if (empty($param["in"]) || count($param["in"]) < 0) {
|
|||
|
throw new Exception("缺少in参数");
|
|||
|
}
|
|||
|
|
|||
|
//1.获取请求体设备编码参数
|
|||
|
$workingsubclass = $param["workingsubclass"];
|
|||
|
$batch = $param["batch"];
|
|||
|
$device_code = $param["device_code"];
|
|||
|
$param_in = $param["in"];
|
|||
|
|
|||
|
// 缓存wip的item_id,为后续通过item_id获取batch
|
|||
|
$wip_item_id = '';
|
|||
|
|
|||
|
// 判断in结构参数是否上传
|
|||
|
foreach ($param_in as $key => $values) {
|
|||
|
if (empty($values["item_code"])) {
|
|||
|
throw new Exception("上传的in结构内,第" . ($key + 1) . "个item_code参数缺少数据");
|
|||
|
}
|
|||
|
|
|||
|
if (empty($values["item_batch"])) {
|
|||
|
throw new Exception("上传的in结构内,第" . ($key + 1) . "个item_batch参数缺少数据");
|
|||
|
}
|
|||
|
|
|||
|
if (!isset($values["item_quantity"]) && $values["item_quantity"] == "") {
|
|||
|
throw new Exception("上传的in结构内,第" . ($key + 1) . "个item_quantity参数缺少数据");
|
|||
|
}
|
|||
|
|
|||
|
if (empty($values["item_id"])) {
|
|||
|
throw new Exception("上传的in结构内,第" . ($key + 1) . "个item_id参数缺少数据");
|
|||
|
}
|
|||
|
|
|||
|
if ($wip_item_id == '' && $values["item_id"] != '-1') {
|
|||
|
$wip_item_id = $values["item_id"];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ($batch == '' && $wip_item_id == '') {
|
|||
|
throw new Exception("不允许上传batch为空,同时param参数in结构内容item_id为-1情况;当batch为空时,代表除了工序段第一个工序其他工序,item_id必须有一个不为-1的值");
|
|||
|
}
|
|||
|
|
|||
|
// 判断in结构内item_code、item_id是否有重复值,同时忽略键值对[['key'=> 'item_id','value'=> '-1']
|
|||
|
// 指定需要校验的键名
|
|||
|
$check_keys = ['item_code', 'item_id'];
|
|||
|
// 忽略条件,可能存在多个原材料投入,所以需要忽略item=-1
|
|||
|
$ignore_key_value_pairs = [
|
|||
|
['key' => 'item_id', 'value' => '-1'],
|
|||
|
];
|
|||
|
$duplicates = findDuplicates($param_in, $check_keys, $ignore_key_value_pairs);
|
|||
|
if (!empty($duplicates)) {
|
|||
|
$duplicates_str = '上传的in结构内,';
|
|||
|
foreach ($duplicates as $key => $value) {
|
|||
|
$duplicates_str .= '参数' . $key . '存在重复值【' . implode("、", $value) . '】,';
|
|||
|
}
|
|||
|
throw new Exception($duplicates_str);
|
|||
|
}
|
|||
|
|
|||
|
//2.判断工序单元、设备编码是存在?
|
|||
|
$sql = "SELECT id FROM hf_mes_process_workingsubclass WHERE code='" . $workingsubclass . "'";
|
|||
|
$ret = Db::fetch($sql);
|
|||
|
if (empty($ret)) {
|
|||
|
throw new Exception("工序单元[$workingsubclass]不存在");
|
|||
|
}
|
|||
|
|
|||
|
$sql = "SELECT id FROM hf_mes_device WHERE code='" . $device_code . "'";
|
|||
|
$ret = Db::fetch($sql);
|
|||
|
if (empty($ret)) {
|
|||
|
throw new Exception("设备编码[$device_code]不存在");
|
|||
|
}
|
|||
|
|
|||
|
//判断是否存在已经上料的物料
|
|||
|
$param_in_item_code = array_column($param_in, 'item_code');
|
|||
|
$sql = "SELECT item_code,batch FROM hf_mes_bkv_batch_process_log WHERE device_code='" . $device_code . "' AND workingsubclass='" . $workingsubclass . "' AND item_code IN ('" . implode("','", $param_in_item_code) . "') AND status=0";
|
|||
|
$ret = Db::fetchAll($sql);
|
|||
|
if ($ret) {
|
|||
|
throw new Exception('工序单元[' . $workingsubclass . ']下设备编码[' . $device_code . ']在批次[' . implode(',', array_column($ret, 'batch')) . ']中已经存在投入[' . implode(',', array_column($ret, 'item_code')) . ']物料,请先下料后再上料!');
|
|||
|
}
|
|||
|
//3.获取生产批次关联工艺流程,工艺流程关联型号,型号关联bom,bom关联bom关系,bom关系关联物料信息
|
|||
|
if (empty($batch)) {
|
|||
|
// 当batch为空时,代表正极制片段、负极制片段、装配段除了第一个工序其他工序,batch以半成品(wip)的批次为准
|
|||
|
$sql = "SELECT batch FROM hf_mes_production_battery_map WHERE battery_id='" . $wip_item_id . "' ORDER BY id DESC LIMIT 1";
|
|||
|
$ret = Db::fetch($sql);
|
|||
|
if (empty($ret)) {
|
|||
|
throw new Exception('半成品' . $wip_item_id . '找不到对应批次');
|
|||
|
}
|
|||
|
$batch = $ret['batch'];
|
|||
|
}
|
|||
|
|
|||
|
$subbatch = '';
|
|||
|
$process_code = '';
|
|||
|
$sql = "SELECT b.subbatch,c.process FROM hf_mes_production_planning_management_batch a
|
|||
|
INNER JOIN hf_mes_production_planning_management_subbatch b ON a.id = b.batch_id
|
|||
|
INNER JOIN hf_mes_technology_flow c ON b.flow_id = c.id
|
|||
|
WHERE batch='" . $batch . "' ORDER BY b.id DESC LIMIT 1";
|
|||
|
$ret = Db::fetch($sql);
|
|||
|
if (empty($ret)) {
|
|||
|
throw new Exception("批次[" . $batch . "]不存在子批次信息,请检查批次是否已创建");
|
|||
|
}
|
|||
|
$subbatch = $ret['subbatch'];
|
|||
|
// 拿到工序编码
|
|||
|
foreach (json_decode($ret['process'], true) as $val) {
|
|||
|
if ($val[4] == $workingsubclass) {
|
|||
|
$process_code = $val[2];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
$param['process_code'] = $process_code;
|
|||
|
$param['class_name'] = get_class($this);
|
|||
|
// 写入process_log的数据(同步)
|
|||
|
$response = Event::emit('ExtScriptEvent.extScriptBegin', $param);
|
|||
|
if ($response['ext_script_code'] != 0) {
|
|||
|
throw new Exception($response['ext_script_msg']);
|
|||
|
}
|
|||
|
|
|||
|
//4.通过以上关联信息查询物料编码
|
|||
|
$sql = "SELECT e.code as bom_source_code,e.name as bom_source_name,d.bom_source_id,d.bom_id,f.code as bom_source_category_code FROM hf_mes_production_planning_management_batch a
|
|||
|
INNER JOIN hf_mes_technology_flow b ON a.flow_id = b.id
|
|||
|
INNER JOIN hf_mes_product_bom c ON b.product_model_id = c.product_model_id
|
|||
|
INNER JOIN hf_mes_product_bom_relationship d ON c.id = d.bom_id
|
|||
|
INNER JOIN hf_mes_product_bom_source e ON d.bom_source_id = e.id
|
|||
|
INNER JOIN hf_mes_product_bom_source_category f ON e.bom_source_category_id = f.id
|
|||
|
WHERE a.batch='" . $batch . "' AND d.in_workingsubclass='" . $workingsubclass . "'";
|
|||
|
$ret = Db::fetchAll($sql);
|
|||
|
if (empty($ret)) {
|
|||
|
throw new Exception('批次' . $batch . '下的工序单元' . $workingsubclass . ',bom关系不存在in结构物料编码');
|
|||
|
}
|
|||
|
|
|||
|
//4.1获取bom的物料编码
|
|||
|
$bom_relationship_array = $ret;
|
|||
|
$bom_relationship_item_code = array_column($ret, 'bom_source_code');
|
|||
|
//4.2获取param_in结构的物料编码
|
|||
|
$param_in_item_code = array_column($param_in, 'item_code');
|
|||
|
|
|||
|
//5.检查上传param参数中in结构的物料是否正确
|
|||
|
$missing_material = [];
|
|||
|
foreach ($param_in_item_code as $key => $val) {
|
|||
|
if (!in_array($val, $bom_relationship_item_code)) {
|
|||
|
$missing_material[] = $val;
|
|||
|
}
|
|||
|
}
|
|||
|
if (count($missing_material) > 0) {
|
|||
|
throw new Exception("上传param中in结构的item_code物料[" . implode(',', $missing_material) . "]不在本生产批次[$batch]的[$workingsubclass]工序中投料");
|
|||
|
}
|
|||
|
$uuid = [];
|
|||
|
$forward_tracing_uuid = [];
|
|||
|
$wip_uuid = [];
|
|||
|
$pid = [];
|
|||
|
// 6、剔除上传param参数中in结构多余物料信息,获取最终需要处理param_in
|
|||
|
foreach ($param_in as $key => $value) {
|
|||
|
if (!in_array($value['item_code'], $bom_relationship_item_code)) {
|
|||
|
unset($param_in[$key]);
|
|||
|
} else {
|
|||
|
// 7、判断原材料或半成品是否已经投入批次工序日志缓存表,不允许重复再投,只能先把上一次投入物料结束,才能允许再投入;
|
|||
|
// 如果是某一物料追加再投,用其他接口进行处理
|
|||
|
#物料虚拟码,当item_id=-1时,代表原材料;当item_id!=-1,代表半成品(wip)
|
|||
|
if ($value['item_id'] == '-1') {
|
|||
|
// 判断原材料是否已投入但还未结束,如果是则报警不给投入
|
|||
|
$sql = "SELECT id FROM hf_mes_bkv_batch_process_log WHERE workingsubclass='" . $workingsubclass . "' AND item_device_code='" . $device_code . "' AND item_code='" . $value['item_code'] . "' AND status=0 ORDER BY id DESC LIMIT 1";
|
|||
|
$ret = Db::fetch($sql);
|
|||
|
if (!empty($ret)) {
|
|||
|
throw new Exception("工序单元[$workingsubclass]下设备[$device_code]已有正在生产中的物料[" . $value['item_code'] . "],如需投料请先结束生产中的物料");
|
|||
|
}
|
|||
|
//同一个物料编码和物料批次只能对应一个UUID如果直接获取 一开始就追加forward_tracing_uuid生成,需要保证forward_tracing_uuid的唯一性
|
|||
|
//通过物料编码和物料批次获取forward_tracing_uuid,如果不存再数据则直接生成
|
|||
|
$sql = "SELECT forward_tracing_uuid FROM hf_mes_bkv_batch_process_log WHERE item_batch='" . $value['item_batch'] . "' AND item_code='" . $value['item_code'] . "'ORDER BY id DESC LIMIT 1";
|
|||
|
$ret = Db::fetch($sql);
|
|||
|
if (empty($ret)) {
|
|||
|
//直接生成UUID和forward_tracing_uui
|
|||
|
$forward_tracing_uuid[] = [generateUniqueId()];
|
|||
|
} else {
|
|||
|
$forward_tracing_uuid[] = json_decode($ret["forward_tracing_uuid"]);
|
|||
|
}
|
|||
|
} else {
|
|||
|
//判警不给投断半成品是否存在,且状态是未结束;如果不存在或状态是结束,则报入
|
|||
|
//追加正向追溯uuidforward_tracing_uuid数据写入
|
|||
|
$sql = "SELECT id,uuid,status,pid,forward_tracing_uuid FROM hf_mes_bkv_batch_process_log WHERE item_id='" . $value['item_id'] . "' ORDER BY id DESC LIMIT 1";
|
|||
|
$ret = Db::fetch($sql);
|
|||
|
if (empty($ret)) {
|
|||
|
throw new Exception("物料[" . $value['item_code'] . "]不存在半成品信息[" . $value['item_id'] . "],请检查半成品信息是否存在批次工序日志缓存表");
|
|||
|
}
|
|||
|
|
|||
|
if ($ret['status'] != '-1' && $ret['status'] != '-2') {
|
|||
|
throw new Exception("物料[" . $value['item_code'] . "]对应半成品信息[" . $value['item_id'] . "已投料或已生产完成,不允许重复投料,请检查物料状态信息");
|
|||
|
}
|
|||
|
$forward_tracing_uuid[] = json_decode($ret["forward_tracing_uuid"]);
|
|||
|
$uuid = array_merge($uuid, json_decode($ret["uuid"]));
|
|||
|
$wip_uuid[$value['item_id']] = $ret["uuid"];
|
|||
|
$pid[$value['item_id']] = $ret["pid"];
|
|||
|
}
|
|||
|
|
|||
|
// 把bom_source_id、bom_id、bom_source_category_code加进param,为后续拼接insert sql做准备
|
|||
|
foreach ($bom_relationship_array as $k => $v) {
|
|||
|
if ($v['bom_source_code'] == $value['item_code']) {
|
|||
|
$param_in[$key]['bom_source_id'] = $v['bom_source_id'];
|
|||
|
$param_in[$key]['bom_source_name'] = $v['bom_source_name'];
|
|||
|
$param_in[$key]['bom_id'] = $v['bom_id'];
|
|||
|
$param_in[$key]['bom_source_category_code'] = $v['bom_source_category_code'];
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
// 冻结解冻列表设置拦截工序调用该接口就会反馈给class和classname到设备
|
|||
|
if ($value['item_id'] != '-1') {
|
|||
|
$freeze_data = Event::emit('FreezeInterceptionEvent.handle', [
|
|||
|
'battery_id' => $v['item_id'],
|
|||
|
'workingsubclass' => $ret['next_workingsubclass'],
|
|||
|
'ropes_time' => date("Y-m-d H:i:s"),
|
|||
|
]);
|
|||
|
$h = $k + 1;
|
|||
|
if (!empty($freeze_data)) {
|
|||
|
// 检查 status 是否为 5 和 workingsubclass为ALLPROCESS
|
|||
|
if ($freeze_data['status'] == 5 && $freeze_data['workingsubclass'] = 'ALLPROCESS') {
|
|||
|
throw new Exception("(!!!紧急冻结全工序!!!)第[{$h}]位置半成品物料{$value['item_id']}在工序[{$workingsubclass}]已被标记[{$freeze_data['classname']}]冻结,需要设备排出或人工取出!!!");
|
|||
|
// 检查 status 是否为 1
|
|||
|
} elseif ($freeze_data['status'] == 1) {
|
|||
|
throw new Exception("(!!!请先解冻再复投!!!)第[{$h}]位置半成品物料{$value['item_id']}在工序[{$workingsubclass}]已被标记[{$freeze_data['classname']}]冻结,需要设备排出或人工取出!!!");
|
|||
|
}
|
|||
|
|
|||
|
// 默认冻结报错逻辑
|
|||
|
throw new Exception("第[{$h}]位置半成品物料{$value['item_id']}在工序[{$workingsubclass}]已被标记[{$freeze_data['classname']}]冻结,需要设备排出或人工取出!!!");
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
// 如果存在半成品uuid,则用半成品uuid,否则默认为[]
|
|||
|
$uuid = json_encode(array_unique($uuid));
|
|||
|
$finish_time = date("Y-m-d H:i:s");
|
|||
|
} catch (Exception $e) {
|
|||
|
throw new Exception($e->getMessage());
|
|||
|
}
|
|||
|
// 开启事务
|
|||
|
Db::beginTrans();
|
|||
|
try {
|
|||
|
// 8、拼接存储结构,上传数据到批次工序日志缓存表;原材料新增数据,半成品更改半成品状态为0
|
|||
|
$material_insert_sql_head = 'INSERT INTO "hf_mes_bkv_batch_process_log" (item_id, batch, subbatch, workingsubclass, process_code, device_code, item_device_code, status, start_time, finish_time, bom_source_id, item_batch, item_quantity, pid, uuid, bom_id, item_code,item_name, lot, tray,forward_tracing_uuid) VALUES';
|
|||
|
$material_insert_sql_val = [];
|
|||
|
foreach ($param_in as $key => $value) {
|
|||
|
$item_id = $value['item_id'];
|
|||
|
if ($value['item_id'] == '-1') {
|
|||
|
// 生成MES内部item_id
|
|||
|
$item_id = generate_material_item_id($workingsubclass, $value['bom_source_category_code'], $value['item_code']);
|
|||
|
}
|
|||
|
$material_insert_sql_val[] = sprintf(
|
|||
|
"('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s','%s','%s')",
|
|||
|
$item_id,
|
|||
|
$batch,
|
|||
|
$subbatch,
|
|||
|
$workingsubclass,
|
|||
|
$process_code,
|
|||
|
$device_code,
|
|||
|
$device_code,
|
|||
|
0,
|
|||
|
$finish_time,
|
|||
|
$finish_time,
|
|||
|
$value['bom_source_id'],
|
|||
|
$value['item_batch'],
|
|||
|
$value['item_quantity'],
|
|||
|
$value['item_id'] == '-1' ? -1 : $pid[$value['item_id']],
|
|||
|
$value['item_id'] == '-1' ? $uuid : $wip_uuid[$value['item_id']],
|
|||
|
$value['bom_id'],
|
|||
|
$value['item_code'],
|
|||
|
$value['bom_source_name'],
|
|||
|
strtotime(date('Y-m-d H:i:s')),
|
|||
|
'-1',
|
|||
|
json_encode($forward_tracing_uuid[$key])
|
|||
|
);
|
|||
|
}
|
|||
|
//10.原材料数组数据写入缓存表
|
|||
|
$sql = $material_insert_sql_head . implode(',', $material_insert_sql_val);
|
|||
|
$row = Db::query($sql);
|
|||
|
if ($row === NULL) {
|
|||
|
throw new Exception("原材料新增批次工序日志缓存表失败");
|
|||
|
}
|
|||
|
} catch (Exception $e) {
|
|||
|
Db::rollBackTrans();
|
|||
|
throw new Exception($e->getMessage());
|
|||
|
}
|
|||
|
|
|||
|
Db::commitTrans();
|
|||
|
|
|||
|
return '';
|
|||
|
}
|
|||
|
}
|