cqp 3 mesiacov pred
rodič
commit
7cb3fa43c6

+ 2 - 1
app/Console/Commands/InsertDeviceData.php

@@ -5,6 +5,7 @@ namespace App\Console\Commands;
 use App\Http\Controllers\Api\FeederDataGenerator;
 use App\Model\ForDeviceData;
 use App\Model\SystemL;
+use App\Service\FeederDataGeneratorService;
 use Illuminate\Console\Command;
 
 class InsertDeviceData extends Command
@@ -47,7 +48,7 @@ class InsertDeviceData extends Command
             ->get()->toArray();
         if(! empty($dispatch)){
             $first_id = SystemL::max('id');
-            list($status, $msg) = (new FeederDataGenerator)->generateData($dispatch);
+            list($status, $msg) = (new FeederDataGeneratorService())->generateData($dispatch);
             if($status){
                 $end_id = SystemL::max('id');
                 ForDeviceData::whereIn('id',array_column($dispatch,'id'))

+ 335 - 0
app/Service/FeederDataGeneratorService.php

@@ -0,0 +1,335 @@
+<?php
+
+namespace App\Service;
+
+
+use App\Model\Equipment;
+use App\Model\SystemL;
+use Illuminate\Support\Facades\DB;
+
+class FeederDataGeneratorService extends Service
+{
+    // 设备配置参数
+    const CAPACITY = 0.6; // 容器容量(吨)
+    const SUCTION_CAPACITY = 0.1; // 容器容量(吨) 下料
+    const SUCTION_WEIGHT = 0.1; // 每次吸料重量(吨)
+    const SUCTION_TIME_MIN = 300; // 吸料最短时间(秒) - 5分钟
+    const SUCTION_TIME_MAX = 360; // 吸料最长时间(秒) - 6分钟
+    const SUCTION_INTERVAL_MIN = 300; // 下次吸料最小间隔(秒) -
+    const SUCTION_INTERVAL_MAX = 360; // 下次吸料最大间隔(秒) -
+    const DISCHARGE_TIME_MIN = 240; // 下料最短时间(秒) - 4分钟
+    const DISCHARGE_TIME_MAX = 300; // 下料最长时间(秒) - 5分钟
+    const DISCHARGE_INTERVAL_MIN = 300; // 下次下料最小间隔(秒) -
+    const DISCHARGE_INTERVAL_MAX = 360; // 下次下料最大间隔(秒) -
+
+    private $devices = []; // 存储设备数据
+
+    /**
+     * 处理原始数据并生成设备运行明细
+     * @param array $rawData 原始数据源
+     * @return array 生成的设备运行明细数据
+     */
+    public function generateData(array $rawData): array
+    {
+        // 首先按设备和日期分组汇总产量
+        $this->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;
+    }
+}