do_set_wip_output_by_item_id($post); } /* 制片段、装配段设备半成品产出出现以下场景: 一、1个生产批次下物料投入 => 产出当前生产批次半成品 例子:正极制片段物料 => 正极制片段半成品(生产批次用物料投入生产批次) 二、2个以上生产批次下物料投入 => 产出新生产批次半成品 例子:正极制片物料半成品 + 负极制片物料半成品 + 原材料 => 装配段半成品(用上传批次作为生产批次) 接口api示例 { "action": "set_wip_output", "param": { "batch": "", #当batch不为空,代表装配段第一个工序,把正极制片段、负极制片段投入物料合并到装配段上;当batch为空时,代表正极制片段、负极制片段工序,batch以投入物料batch为准 "workingsubclass": "ZJ", # 工序单元 "device_code": "FJZJ-1", # 设备编码 "item_id":"", # 裸电芯条码 "input_item_id":"", # 上料裸电芯条码 "out": { "START_TIME":"tongbo", # 开始时间 "FINISH_TIME": "fengezi-1", # 结束时间 "DEVICE_CODE": "fengezi-1" # 设备编码 } } } */ /* * 通过batch->flow->model->bom->workingsubclass->BomRelationship对应的bom结构 * 1.获取请求体设备编码参数 * 2.判断工序单元、设备编码是存在?根据设备编码查询批次工序日志表,判断该设备是否已经存在生产中物料信息 * 3.判断是否有上传input_item_id参数 * 3.1有上传则判断上料裸电芯和下料裸电芯是否不相同 * 3.2不相同则判断item_id是否已经存在激活状态的数据 * 4.获取生产批次关联工艺流程,工艺流程关联型号,型号关联bom,bom关联bom关系,bom关系关联物料信息 * 5.通过以上关联信息查询物料编码 * 5.1.获取bom的物料编码 * 5.2.获取param_in结构的物料编码 * 6.检查上传param参数中in结构是否缺少物料 * 7.检查上传的结果参数是否缺少,并拼接结果参数 * 8.获取设备投入物料信息,为后续追加battery_map、bkv表做准备 * 9.通过item_id,判断物料是否已存在battery_map,不存在,则新增battery_map和bkv表;如果存在,不做处理 * 10.投入物料新增数据到battery_map、bkv表 * 11.产出半成品新增数据到process_log、battery_map、bkv表 * 12.如果存在上一工序,并且上传参数有上料裸电芯码,则取消上料裸电芯码在bkv表的状态 * 13.如果不存在uuid,证明是第一个工序第一次生成uuid,需要把uuid写回到投入物料uuid信息上 */ private function do_set_wip_output_by_item_id($post) { // 验证数据 $param = $post['param']; try { // 1.判断请求参数是否完整 if (empty($param["workingsubclass"])) { throw new Exception("缺少workingsubclass参数"); } if (empty($param["device_code"])) { throw new Exception("缺少device_code参数"); } if (empty($param["item_id"])) { throw new Exception("缺少item_id参数"); } if (empty($param["out"])) { throw new Exception("缺少out参数"); } $batch = ""; $workingsubclass = $param["workingsubclass"]; $device_code = $param["device_code"]; $item_id = $param["item_id"]; $param_out = $param["out"]; //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,device_category_id FROM hf_mes_device WHERE code='" . $device_code . "'"; $ret = Db::fetch($sql); if (empty($ret)) { throw new Exception("设备编码[$device_code]不存在"); } $device_category_id = $ret['device_category_id']; //3判断是否有上传input_item_id参数 $input_item_id = isset($param["input_item_id"]) ? $param["input_item_id"] : ''; //判断上料裸电芯和下料裸电芯是否不相同,不相同则判断下料码是否已经存在激活状态 if ($input_item_id != $item_id) { $sql = "SELECT * FROM hf_mes_production_battery_map WHERE battery_id='" . $item_id . "' ORDER BY id DESC LIMIT 1"; $item_id_ret = Db::fetch($sql); if (!empty($item_id_ret)) { //不为空代表存在这条裸电芯数据,防止重码下料 //查询对应的批次表看是否存在激活状态 $subbatch = $item_id_ret['subbatch']; $batch = $item_id_ret['batch']; $sql = "SELECT id FROM \"hf_mes_tmp_bkv_" . $subbatch . "\" WHERE active = 1 AND battery_id='{$item_id}' "; $item_id_info = Db::fetch($sql); if ($item_id_info) { throw new Exception('电芯码[' . $item_id . ']已存在[' . $batch . ']批次', 4001); } } } //4.获取生产批次关联工艺流程,工艺流程关联型号,型号关联bom,bom关联bom关系,bom关系关联物料信息 $sql = "SELECT batch,item_code,forward_tracing_uuid FROM hf_mes_bkv_batch_process_log WHERE workingsubclass='" . $workingsubclass . "' AND item_device_code='" . $device_code . "' AND status=0 ORDER BY id DESC "; $ret = Db::fetchAll($sql); if (empty($ret)) { throw new Exception('工序单元[' . $workingsubclass . ']下设备编码[' . $device_code . ']找不到对应投入物料生产批次信息'); } $item_code_in_process_log = array_column($ret, "item_code"); //获取正向追溯的uuid $forward_tracing_uuid_in_process_log = array_column($ret, "forward_tracing_uuid"); $forward_tracing_uuid = []; foreach ($forward_tracing_uuid_in_process_log as $json_str) { $decoded = json_decode($json_str, true); // 转成数组 if (is_array($decoded)) { $forward_tracing_uuid = array_merge($forward_tracing_uuid, $decoded); } } $batch = $ret[0]['batch']; //对比物料信息是否与process_log表的item_code是否相同 $sql = "SELECT b.code,b.name as bom_source_name FROM hf_mes_product_bom_relationship as a INNER JOIN hf_mes_product_bom_source b ON a.bom_source_id = b.id WHERE a.in_workingsubclass ='$workingsubclass' ;"; $ret = Db::fetchAll($sql); if (empty($ret)) { throw new Exception('批次' . $batch . '下的工序单元' . $workingsubclass . ',bom关系不存在in结构物料编码'); } $item_code_diff = array_diff(array_column($ret, "code"), $item_code_in_process_log); if (count($item_code_diff) > 0) { throw new Exception('工序单元[' . $workingsubclass . ']下设备编码[' . $device_code . ']找不到对应[' . implode(",", $item_code_diff) . ']投入物料生产批次信息'); } $bom_source_name_array = array_column($ret, 'bom_source_name', 'code'); $subbatch = ''; $process_code = ''; $next_process_code = '-1'; //下一工序编码 $previous_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']; $process = json_decode($ret['process'], true); // 拿到工序编码 foreach ($process as $key => $val) { if ($val[4] == $workingsubclass) { $process_code = $val[2]; if (isset($process[$key + 1])) { $next_process_code = $process[$key + 1][2]; } if ($key != 0) { $previous_process_code = $process[$key - 1][2]; } } } //5.通过以上关联信息查询物料编码 $sql = "SELECT a.flow_id,e.code as bom_source_code,d.bom_source_id,e.name as bom_source_name,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 LEFT JOIN hf_mes_product_bom_source_category f ON e.bom_source_category_id = f.id WHERE a.batch='" . $batch . "' AND d.out_workingsubclass='" . $workingsubclass . "'"; $ret = Db::fetch($sql); if (empty($ret)) { throw new Exception('批次' . $batch . '下的工序单元' . $workingsubclass . ',bom关系不存在out结构物料编码'); } //5.1获取bom的物料编码 $bom_relationship_array = $ret; // 6.获取批次、工序单元对应process_code的结果参数,对比工序结果参数与上传参数是否对应 $flow_id = $ret['flow_id']; $flow_process_code = $flow_id . '_' . $workingsubclass; $sql = "SELECT b.code,b.field_type FROM hf_mes_technology_process a INNER JOIN hf_mes_technology_process_result_param b ON a.id = b.process_id WHERE a.code='" . $flow_process_code . "'"; $ret = Db::fetchAll($sql); if (empty($ret)) { throw new Exception('工序编码' . $flow_process_code . '不存在结果参数'); } // 7.检查上传的结果参数是否缺少,并拼接结果参数 $sql_key_out_result_param = []; $sql_val_out_result_param = []; $result_param_out = array_keys($param_out); $ng_code = NULL; foreach ($ret as $key => $val) { if (!in_array($val['code'], $result_param_out)) { throw new Exception("上传的结果参数里不含有该参数: " . $val["code"]); } // 上传结果参数值 $param_out_val = $param_out[$val['code']]; // 数据类型 $field_type = $val['field_type']; // 验证上传结果参数值与数据类型是否一致 $verify_result = verify_data_type($val['code'], $param_out_val, $field_type); if ($verify_result != '') { throw new Exception($verify_result); } $sql_key_out_result_param[] = '"' . $flow_process_code . "." . $val["code"] . '"'; if ($param_out_val === "") { $sql_val_out_result_param[] = 'NULL'; } else { $sql_val_out_result_param[] = "'" . $param_out_val . "'"; } // 判断NG是否存在 if ($val['code'] == 'NG') { if ($param_out[$val['code']] != '') { $ng_code = $param_out[$val['code']]; } else { $ng_code = 0; } } } // 8.获取设备投入物料信息,为后续追加battery_map、bkv表做准备 $sql = "SELECT * FROM hf_mes_bkv_batch_process_log WHERE workingsubclass='" . $workingsubclass . "' AND item_device_code='" . $device_code . "' AND status=0"; $material_input_ret = Db::fetchAll($sql); if (empty($material_input_ret)) { throw new Exception('工序单元[' . $workingsubclass . ']下设备编码[' . $device_code . ']找不到对应投入物料信息'); } $pid = []; $uuid = []; $wip_uuid = []; //下料半成品uuid $battery_map_sql_val = []; $bkv_sql_val = []; // 9. 通过item_id,判断物料是否已存在battery_map,不存在,则新增battery_map和bkv表;如果存在,不做处理 foreach ($material_input_ret as $key => $value) { $battery_id = $value['item_id']; $pid[] = $battery_id; $wip_uuid = $value['pid'] != "-1" ? array_merge($wip_uuid, json_decode($value["uuid"])) : array_merge($wip_uuid, []); $uuid = array_merge($uuid, json_decode($value["uuid"])); $sql = "SELECT * FROM hf_mes_production_battery_map WHERE battery_id='" . $battery_id . "' ORDER BY id DESC LIMIT 1"; $material_input_battery_map_ret = Db::fetch($sql); if (empty($material_input_battery_map_ret)) { // 拼接插入battery_map_sql_val值 $battery_unique_value = "{$value['batch']}_{$value['subbatch']}_{$value['tray']}_{$value['lot']}_{$battery_id}"; $battery_map_sql_val[] = sprintf( "('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')", $value['bom_source_id'], $battery_unique_value, $battery_id, $value['batch'], $value['subbatch'], $value['lot'], $value['tray'], $value['item_batch'], $value['item_device_code'], $value['pid'], $value['uuid'], $value['bom_id'], $value['workingsubclass'], $value['item_code'], $bom_source_name_array[$value['item_code']] ); $bkv_sql_val[] = sprintf( "('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')", $battery_id, $value['tray'], $value['lot'], 1, $value['process_code'], $next_process_code, $value['item_code'], $bom_source_name_array[$value['item_code']], $value['item_batch'], $value['item_device_code'], $value['start_time'], $value['pid'], $value['uuid'], $value['bom_source_id'], $value['bom_id'] ); } } if ($input_item_id) { //查询batch_process_log表中该裸电芯上一工序的uuid $sql = "SELECT uuid,id FROM \"hf_mes_tmp_bkv_" . $subbatch . "\" WHERE active = 1 AND battery_id='{$input_item_id}' AND process_code='" . $previous_process_code . "'"; $input_item_uuid_info = Db::fetchAll($sql); if (empty($input_item_uuid_info)) { throw new Exception('工序单元[' . $previous_process_code . ']下裸电芯[' . $input_item_id . ']不存在或为非激活状态', 4001); } $wip_uuid = json_decode($input_item_uuid_info[0]["uuid"]); $uuid = array_merge($uuid, json_decode($input_item_uuid_info[0]["uuid"])); $pid[] = $input_item_id; $input_item_subatch_id = $input_item_uuid_info[0]["id"]; } } catch (Exception $e) { throw new Exception($e->getMessage()); } Db::beginTrans(); try { // 标记最后一条数据的状态 if (!empty($pid)) { Event::emit('SetProcessLogEvent.updateLastStatus', $pid); } // 10.投入物料新增数据到battery_map、bkv表 if (count($battery_map_sql_val) > 0) { $battery_map_sql_head = 'INSERT INTO "hf_mes_production_battery_map" (bom_source_id, battery_unique_value, battery_id, batch, subbatch, lot, tray, item_batch, item_device_code, pid, uuid, bom_id, workingsubclass, item_code,item_name) VALUES'; $bkv_sql_head = 'INSERT INTO "hf_mes_tmp_bkv_' . $subbatch . '" (battery_id, tray, lot, active, process_code,next_process_code, item_code,item_name, item_batch, item_device_code, item_batch_start_time, pid, uuid, bom_source_id, bom_id) VALUES'; $sql = $battery_map_sql_head . implode(',', $battery_map_sql_val); $row = Db::query($sql); if ($row === NULL) { throw new Exception("投入物料信息插入battery_map表失败"); } $sql = $bkv_sql_head . implode(',', $bkv_sql_val); $row = Db::query($sql); if ($row === NULL) { throw new Exception("投入物料信息插入bkv表失败"); } } // 11.产出半成品新增数据到process_log、battery_map、bkv表 // uuid如果为空数组,则自动创建uuid;否则用不为空数组做uuid if (count($uuid) <= 0) { $uuid[] = generateUniqueId(); $uuid = json_encode($uuid); } else { $uuid = json_encode(array_values(array_unique($uuid))); } //保证每个正向追溯的uuid都是唯一的 $forward_tracing_uuid_only[] = generateUniqueId(); $forward_tracing_uuid_only = json_encode($forward_tracing_uuid_only); $decoded_uuid = json_decode($forward_tracing_uuid_only, true); if (is_array($decoded_uuid)) { $forward_tracing_uuid = array_merge($forward_tracing_uuid, $decoded_uuid); } $forward_tracing_uuid = json_encode(array_unique($forward_tracing_uuid)); $wip_uuid = json_encode(array_values(array_unique($wip_uuid))); $now_date = date('Y-m-d H:i:s'); $start_time = isset($param_out['START_TIME']) ? $param_out['START_TIME'] : date('Y-m-d H:i:s'); $finish_time = isset($param_out['FINISH_TIME']) ? $param_out['FINISH_TIME'] : date('Y-m-d H:i:s'); $lot = strtotime($now_date); $battery_id = $item_id; // 新增数据到process_log表 $wip_process_log_sql_head = 'INSERT INTO "hf_mes_bkv_batch_process_log" (item_id, batch, subbatch, workingsubclass, process_code, device_code, status, start_time, finish_time, bom_source_id, item_batch, item_quantity, pid, uuid, bom_id, item_code, item_name, lot, tray, ng_code, device_category_id,last_status,forward_tracing_uuid) VALUES'; $wip_process_log_sql_val = sprintf( "('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')", $battery_id, $batch, $subbatch, $workingsubclass, $process_code, $device_code, -1, $start_time, $finish_time, $bom_relationship_array['bom_source_id'], '-1', 1, json_encode($pid), $wip_uuid, $bom_relationship_array['bom_id'], $bom_relationship_array['bom_source_code'], $bom_relationship_array['bom_source_name'], $lot, '-1', $ng_code, $device_category_id, 1, $forward_tracing_uuid ); $sql = $wip_process_log_sql_head . $wip_process_log_sql_val; $row = Db::query($sql); if ($row === NULL) { throw new Exception("半成品信息插入process_log表失败"); } $time_check_data = [ "item_id" => $battery_id, "batch" => $batch, "subbatch" => $subbatch, "workingsubclass" => $workingsubclass, "process_code" => $process_code, ]; // 新增数据到battery_map表 $battery_unique_value = "{$batch}_{$subbatch}_-1_{$lot}_{$battery_id}"; $wip_battery_map_sql_head = 'INSERT INTO "hf_mes_production_battery_map" (bom_source_id, battery_unique_value, battery_id, batch, subbatch, lot, tray, item_batch, pid, uuid, bom_id, workingsubclass, item_code,item_name) VALUES '; $wip_battery_map_sql_val = sprintf( "('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')", $bom_relationship_array['bom_source_id'], $battery_unique_value, $battery_id, $batch, $subbatch, $lot, '-1', -1, json_encode($pid), $wip_uuid, $bom_relationship_array['bom_id'], $workingsubclass, $bom_relationship_array['bom_source_code'], $bom_relationship_array['bom_source_name'] ); $sql = $wip_battery_map_sql_head . $wip_battery_map_sql_val; $row = Db::query($sql); if ($row === NULL) { throw new Exception("半成品信息插入battery_map表失败"); } // 新增数据到bkv表 $wip_bkv_sql_head = 'INSERT INTO "hf_mes_tmp_bkv_' . $subbatch . '" (battery_id, tray, lot, active, process_code,next_process_code, item_code, item_name, item_batch, item_quantity, item_batch_start_time, pid, uuid, bom_source_id, bom_id, ' . implode(',', $sql_key_out_result_param) . ') VALUES '; $wip_bkv_sql_val = sprintf( "('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s','%s','%s',%s)", $battery_id, '-1', $lot, $next_process_code == '-1' && !$ng_code ? 0 : 1, $process_code, $next_process_code, $bom_relationship_array['bom_source_code'], $bom_relationship_array['bom_source_name'], '-1', 1, $start_time, json_encode($pid), $wip_uuid, $bom_relationship_array['bom_source_id'], $bom_relationship_array['bom_id'], implode(',', $sql_val_out_result_param) ); $sql = $wip_bkv_sql_head . $wip_bkv_sql_val . " RETURNING id"; $row = Db::query($sql); if ($row === NULL) { throw new Exception("半成品信息插入bkv表失败"); } $item_subatch_id = $row; //12.如果存在上一工序,并且上传参数有上料裸电芯码,则取消上料裸电芯码在bkv表的状态 if (!empty($previous_process_code) && !empty($input_item_id)) { //取消激活上料的裸电芯的信息 $sql = "UPDATE \"hf_mes_tmp_bkv_" . $subbatch . "\" SET active=0 WHERE battery_id='{$input_item_id}' AND process_code='" . $previous_process_code . "'"; $row = Db::query($sql); if ($row === NULL) { throw new Exception("更新投入裸电芯[$input_item_id]状态失败"); } } // 13.把uuid写回到投入物料uuid信息上 foreach ($material_input_ret as $key => $value) { if ($value['pid'] != '-1') { continue; } $item_id = $value['item_id']; $sql = "UPDATE hf_mes_bkv_batch_process_log SET uuid='$uuid' WHERE item_id='{$item_id}' AND item_device_code='" . $device_code . "' AND status=0"; $row = Db::query($sql); if ($row === NULL) { throw new Exception("更新process_log表中投入物料[$item_id]对应uuid数据失败"); } $sql = "UPDATE hf_mes_production_battery_map SET uuid='$uuid' WHERE battery_id='{$item_id}' AND item_device_code='" . $device_code . "'"; $row = Db::query($sql); if ($row === NULL) { throw new Exception("更新battery_map表中投入物料[$item_id]对应uuid数据失败"); } $sql = "UPDATE \"hf_mes_tmp_bkv_" . $subbatch . "\" SET uuid='$uuid' WHERE battery_id='{$item_id}' AND item_device_code='" . $device_code . "'"; $row = Db::query($sql); if ($row === NULL) { throw new Exception("更新bkv表中投入物料[$item_id]对应uuid数据失败"); } } if (!$input_item_id) { // 写入批次计数数据(同步) Event::emit('SetProcessInputAmountEvent.handle', ["batch" => $batch]); } else { //前工序参数写入新数据 Event::emit('SetPreProcessParamEvent.handle', ["process" => $process, "process_code" => $process_code, "next_process_code" => $next_process_code, 'subbatch' => $subbatch, "item_subatch_id" => $item_subatch_id, 'input_item_subatch_id' => $input_item_subatch_id]); } // 结果参数转发到scada(异步) Event::emit('SendProcessResultParamsToScadaEvent.handle', ["workingsubclass" => $workingsubclass, "device_code" => $device_code, "param" => $param_out, "batch" => $batch]); } catch (Exception $e) { Db::rollBackTrans(); throw new Exception($e->getMessage()); } Db::commitTrans(); $data['process_code'] = $process_code; $data['class_name'] = get_class($this); $data['log_data'] = [$time_check_data]; try { // 写入process_log的数据(同步) $response = Event::emit('ExtScriptEvent.extScriptEnd', $data); if ($response['ext_script_code'] != 0) { throw new Exception($response['ext_script_msg']); } } catch (Exception $e) { throw new Exception($e->getMessage()); } return ''; } }