processRawData($rawData); // dd($this->devices); // 为每个设备生成运行明细 $result = []; foreach ($this->devices as $deviceId => $deviceData) { // 为每一天生成数据 foreach ($deviceData['daily_totals'] as $date => $total) { $details = $this->generateDeviceDayData($deviceData['device'], $date, $total); $result = array_merge($result, $details); } } try { DB::beginTransaction(); if(! empty($result)){ $chunkSize = 200; // 每次插入的数量 foreach (array_chunk($result, $chunkSize) as $chunk) { SystemL::insert($chunk); // 使用模型的 insert 方法批量插入 echo '200条写入成功' . "\n"; } } DB::commit(); }catch (\Throwable $exception){ DB::rollBack(); return [false, $exception->getMessage()]; } return [true, '']; } /** * 处理原始数据,按设备和日期汇总产量 * @param array $rawData 原始数据源 */ private function processRawData(array $rawData) { $map = Equipment::select('id','code','title')->get()->toArray(); foreach ($map as $value){ $map1[$value['id']] = $value; } foreach ($rawData as $item) { $deviceId = $item['device_id']; $t = $map1[$deviceId]; $timestamp = $item['upd_time']; $finishedNum = floatval($item['finished_num']); // 解析日期 $date = date('Y-m-d', $timestamp); // 初始化设备数据 if (!isset($this->devices[$deviceId])) { $this->devices[$deviceId] = [ 'device' => $t, 'total_production' => 0, 'daily_totals' => [] ]; } // 累加总产量和每日产量 $this->devices[$deviceId]['total_production'] += $finishedNum; if (!isset($this->devices[$deviceId]['daily_totals'][$date])) { $this->devices[$deviceId]['daily_totals'][$date] = 0; } $this->devices[$deviceId]['daily_totals'][$date] += $finishedNum; } } /** * 为单个设备的一天生成详细的运行数据 * @param int $deviceId 设备ID * @param string $date 日期(格式:YYYY-MM-DD) * @param float $totalProduction 当天总产量 * @return array 生成的详细数据条目 */ private function generateDeviceDayData(array $device, string $date, float $totalProduction): array { $result = []; // 计算当天需要完成多少轮完整的吸料-下料循环 $fullCycles = floor($totalProduction / self::SUCTION_CAPACITY); $filledAmount = $fullCycles * self::SUCTION_CAPACITY; $remainingWeight = bcsub($totalProduction, $filledAmount,2); // 设置当天的起始时间戳(早上7:30) $currentTime = strtotime($date . ' 07:30:00'); // 执行完整循环 for ($i = 0; $i < $fullCycles; $i++) { // 执行一次完整的吸料-下料过程 $cycleData = $this->generateFullCycle($device, $currentTime); $result = array_merge($result, $cycleData); // 更新当前时间 $currentTime = end($cycleData)['time']; // // 添加随机间隔时间 // $interval = rand(self::SUCTION_INTERVAL_MIN, self::SUCTION_INTERVAL_MAX); // $currentTime += $interval; } // 如果有剩余量,执行部分吸料过程 if ($remainingWeight > 0) { $lastKey = array_key_last($result); // 获取最后一个键 $result[$lastKey]['value'] += $remainingWeight; // $remainingCyleData = $this->generatePartialCycle($device, $currentTime, $remainingWeight); // $result = array_merge($result, $remainingCyleData); } return $result; } /** * 随机插入急停事件,并模拟急停持续时间 * * @param int $deviceId 设备ID * @param int &$currentTime 当前时间戳(引用传递,用于更新) * @param array &$result 结果数组(引用传递,用于添加急停记录) */ private function maybeInsertEmergencyStop(array $device, int &$currentTime, array &$result, $data_point_id, $data_point_name): void { if (rand(1, 100) <= 3) { // 3% 的概率触发急停 $emergencyStopTime = $currentTime; // 插入急停开始事件 $result[] = [ 'time' => $emergencyStopTime, 'value' => -1, // 标识为急停 'device_no' => $device['code'], 'device_name' => $device['title'], 'data_point_id' => $data_point_id, 'data_point_name' => $data_point_name, 'push_time' => $emergencyStopTime, // 'push_time_day' => date("Y-m-d H:i:s",$suctionStartTime) ]; // 模拟急停持续时间(10~15秒) $stopDuration = rand(10, 15); $currentTime += $stopDuration; // 可选:插入急停结束事件(也可以不插,只用时间推移表示) // $result[] = [ // 'time' => $currentTime, // 'value' => -2, // 急停恢复 // 'device_no' => $deviceId, // 'push_time' => $currentTime // ]; } } /** * 生成一个完整的吸料-下料循环数据 * @param int $deviceId 设备ID * @param int $startTime 起始时间戳 * @return array 生成的数据条目 */ private function generateFullCycle(array $device, int $startTime): array { $result = []; $currentTime = $startTime; // 吸料阶段 for ($i = 0; $i < 1; $i++) { // 【吸料前】尝试插入急停 $this->maybeInsertEmergencyStop($device, $currentTime, $result,5,"吸料急停"); // 吸料开始 $suctionStartTime = $currentTime; $result[] = [ 'time' => $suctionStartTime, 'value' => 1, 'device_no' => $device['code'], 'device_name' => $device['title'], 'data_point_id' => 1, 'data_point_name' => "吸料", 'push_time' => $suctionStartTime, // 'push_time_day' => date("Y-m-d H:i:s",$suctionStartTime) ]; // 吸料结束 $suctionDuration = rand(self::SUCTION_TIME_MIN, self::SUCTION_TIME_MAX); $currentTime += $suctionDuration; // 【吸料后】尝试插入急停 $this->maybeInsertEmergencyStop($device, $currentTime, $result,5,"吸料急停"); $result[] = [ 'time' => $currentTime, 'value' => self::SUCTION_WEIGHT, 'device_no' => $device['code'], 'device_name' => $device['title'], 'data_point_id' => 2, 'data_point_name' => "吸料结束且本次吸料重量", 'push_time' => $currentTime, // 'push_time_day' => date("Y-m-d H:i:s",$currentTime) ]; // 添加随机间隔时间 if ($i < 1) { // 最后一次吸料后不需要等待 // $interval = rand(self::SUCTION_INTERVAL_MIN, self::SUCTION_INTERVAL_MAX); $interval = rand(10, 15); $currentTime += $interval; } } // 【下料前】尝试插入急停 $this->maybeInsertEmergencyStop($device, $currentTime, $result,6,"下料急停"); // 下料阶段 $dischargeStartTime = $currentTime; $result[] = [ 'time' => $dischargeStartTime, 'value' => 1, 'device_no' => $device['code'], 'device_name' => $device['title'], 'data_point_id' => 3, 'data_point_name' => "下料", 'push_time' => $dischargeStartTime, // 'push_time_day' => date("Y-m-d H:i:s",$dischargeStartTime) ]; $dischargeDuration = rand(self::DISCHARGE_TIME_MIN, self::DISCHARGE_TIME_MAX); $currentTime += $dischargeDuration; // 【下料后】尝试插入急停 $this->maybeInsertEmergencyStop($device, $currentTime, $result,6,"下料急停"); $result[] = [ 'time' => $currentTime, 'value' => self::SUCTION_CAPACITY, 'device_no' => $device['code'], 'device_name' => $device['title'], 'data_point_id' => 4, 'data_point_name' => "下料结束且本次下料重量", 'push_time' => $currentTime, // 'push_time_day' => date("Y-m-d H:i:s",$currentTime) ]; return $result; } /** * 生成一个不完整的循环(只有吸料部分) * @param int $deviceId 设备ID * @param int $startTime 起始时间戳 * @param float $requiredWeight 需要吸的重量 * @return array 生成的数据条目 */ private function generatePartialCycle(array $device, int $startTime, float $requiredWeight): array { $result = []; $currentTime = $startTime; // 计算需要多少次吸料 $suctionsNeeded = ceil($requiredWeight / self::SUCTION_WEIGHT); $totalSuctionWeight = 0; for ($i = 0; $i < $suctionsNeeded; $i++) { // 吸料开始 $suctionStartTime = $currentTime; $result[] = [ 'time' => $suctionStartTime, 'value' => 1, 'device_no' => $device['code'], 'device_name' => $device['title'], 'data_point_id' => 1, 'data_point_name' => "吸料", 'push_time' => $suctionStartTime, // 'push_time_day' => => date("Y-m-d H:i:s",$suctionStartTime) ]; // 吸料结束 $suctionDuration = rand(self::SUCTION_TIME_MIN, self::SUCTION_TIME_MAX); $currentTime += $suctionDuration; // 判断这次吸料是否会导致超过所需总量 $addedWeight = ($i == $suctionsNeeded - 1) ? $requiredWeight - $totalSuctionWeight : self::SUCTION_WEIGHT; $totalSuctionWeight += $addedWeight; $result[] = [ 'time' => $currentTime, 'value' => $addedWeight, 'device_no' => $device['code'], 'device_name' => $device['title'], 'data_point_id' => 2, 'data_point_name' => "吸料结束且本次吸料重量", 'push_time' => $currentTime, // 'push_time_day' => => date("Y-m-d H:i:s",$currentTime) ]; // 添加随机间隔时间(在40-50分钟之间) if ($i < $suctionsNeeded - 1) { $interval = rand(self::SUCTION_INTERVAL_MIN, self::SUCTION_INTERVAL_MAX); $currentTime += $interval; } } return $result; } }