|
@@ -3,6 +3,9 @@
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
|
|
|
|
|
+use App\Model\DispatchSub;
|
|
|
+use App\Model\Equipment;
|
|
|
+use App\Model\SystemL;
|
|
|
use App\Service\Box\BoxService;
|
|
|
use App\Service\ProductionOrderService;
|
|
|
use Illuminate\Support\Facades\Config;
|
|
@@ -19,6 +22,7 @@ class TestController extends BaseController
|
|
|
(new ProductionOrderService())->delBoxLock();
|
|
|
dd(1);
|
|
|
}
|
|
|
+
|
|
|
public function tt(){dd(1);
|
|
|
$model_box = DB::connection("mysqlT9");
|
|
|
$u8 = $model_box->table('setting')
|
|
@@ -75,4 +79,350 @@ class TestController extends BaseController
|
|
|
dd($e->getMessage());
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ public function test(){dd(1111);
|
|
|
+ $dispatch = DispatchSub::where('del_time',0)
|
|
|
+ ->where('finished_num', '>',0)
|
|
|
+ ->where('process_id',14)
|
|
|
+ ->select('finished_num','upd_time','device_id')
|
|
|
+ ->orderBy('upd_time','asc')
|
|
|
+ ->get()->toArray();
|
|
|
+ foreach ($dispatch as $key => $value){
|
|
|
+ if(! $value['device_id']) $dispatch[$key]['device_id'] = 8;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成数据
|
|
|
+ $generatedData = (new FeederDataGenerator)->generateData($dispatch);
|
|
|
+
|
|
|
+ dd("ok");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+class FeederDataGenerator
|
|
|
+{
|
|
|
+ // 设备配置参数
|
|
|
+ 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;
|
|
|
+ }
|
|
|
}
|
|
|
+
|