TestController.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. <?php
  2. namespace App\Http\Controllers\Api;
  3. use App\Model\DispatchSub;
  4. use App\Model\Equipment;
  5. use App\Model\SystemL;
  6. use App\Service\Box\BoxService;
  7. use App\Service\ProductionOrderService;
  8. use Illuminate\Support\Facades\Config;
  9. use Illuminate\Support\Facades\DB;
  10. class TestController extends BaseController
  11. {
  12. public function delTestQ(){
  13. (new BoxService())->delBoxLock();
  14. dd(1);
  15. }
  16. public function delTestQ2(){
  17. (new ProductionOrderService())->delBoxLock();
  18. dd(1);
  19. }
  20. public function tt(){dd(1);
  21. $model_box = DB::connection("mysqlT9");
  22. $u8 = $model_box->table('setting')
  23. ->where('setting_name','u8')
  24. ->where('setting_value','<>','')
  25. ->first();
  26. if(empty($u8)) return [false, 'u8配置参数不存在!'];
  27. $u8 = $u8->setting_value;
  28. // 使用 eval() 函数执行字符串并转换为数组
  29. $u8 = eval("return {$u8};");
  30. if(empty($u8['domain'])) return [false, '外部域名不能为空!'];
  31. if(empty($u8['u8_api_port'])) return [false, 'u8程序API端口不能为空!'];
  32. if(empty($u8['u8_database_port'])) return [false, 'u8程序数据库端口不能为空!'];
  33. if(empty($u8['database'])) return [false, 'u8程序数据库不能为空!'];
  34. if(empty($u8['database_account'])) return [false, 'u8程序数据库登录账号不能为空!'];
  35. if(empty($u8['database_password'])) return [false, 'u8程序数据库登录密码不能为空!'];
  36. if(empty($u8['sAccID'])) return [false, 'u8程序sAccID不能为空!'];
  37. if(empty($u8['sServer'])) return [false, 'u8程序sServer不能为空!'];
  38. if(empty($u8['sUserID'])) return [false, 'u8程序sUserID不能为空!'];
  39. if(empty($u8['sPassword'])) return [false, 'u8程序sPassword不能为空!'];
  40. $config = [
  41. 'driver' => 'sqlsrv',
  42. 'host' => $u8['domain'],
  43. 'port' => $u8['u8_database_port'],
  44. 'database' => $u8['database'],
  45. 'username' => $u8['database_account'],
  46. 'password' => $u8['database_password'],
  47. ];
  48. // 数据库配置设置
  49. Config::set('database.connections.sqlsrvs', $config);
  50. // 连接
  51. try {
  52. $pdo = DB::connection('sqlsrvs')->getPdo();
  53. if ($pdo instanceof \PDO) {
  54. // 连接成功的逻辑代码
  55. $db = DB::connection('sqlsrvs');
  56. $result = $db->table('ST_SNState')
  57. ->select('cinvCode as code','cInvSN as sn','AutoID as auto_id')
  58. ->whereIn("cWhCode", [001,003])
  59. ->where("cInvCode", "100000003")
  60. ->where("iSNState", 2)
  61. ->orderBy('cSNDefine1','desc')
  62. ->orderBy('AutoID','asc')
  63. ->get()->toArray();
  64. dd($result);
  65. } else {
  66. dd(1233);
  67. }
  68. } catch (\Throwable $e) {
  69. dd($e->getMessage());
  70. }
  71. }
  72. public function test(){dd(1);
  73. $dispatch = DispatchSub::where('del_time',0)
  74. ->where('finished_num', '>',0)
  75. ->where('process_id',14)
  76. ->select('finished_num','dispatch_time_start as upd_time','device_id')
  77. ->orderBy('dispatch_time_start','asc')
  78. ->get()->toArray();
  79. foreach ($dispatch as $key => $value){
  80. if(! $value['device_id']) $dispatch[$key]['device_id'] = 12;
  81. }
  82. // 生成数据
  83. $generatedData = (new FeederDataGenerator)->generateData($dispatch);
  84. dd("ok");
  85. }
  86. }
  87. class FeederDataGenerator
  88. {
  89. // 设备配置参数
  90. const CAPACITY = 0.6; // 容器容量(吨)
  91. const SUCTION_CAPACITY = 0.1; // 容器容量(吨) 下料
  92. const SUCTION_WEIGHT = 0.1; // 每次吸料重量(吨)
  93. const SUCTION_TIME_MIN = 300; // 吸料最短时间(秒) - 5分钟
  94. const SUCTION_TIME_MAX = 360; // 吸料最长时间(秒) - 6分钟
  95. const SUCTION_INTERVAL_MIN = 300; // 下次吸料最小间隔(秒) -
  96. const SUCTION_INTERVAL_MAX = 360; // 下次吸料最大间隔(秒) -
  97. const DISCHARGE_TIME_MIN = 240; // 下料最短时间(秒) - 4分钟
  98. const DISCHARGE_TIME_MAX = 300; // 下料最长时间(秒) - 5分钟
  99. const DISCHARGE_INTERVAL_MIN = 300; // 下次下料最小间隔(秒) -
  100. const DISCHARGE_INTERVAL_MAX = 360; // 下次下料最大间隔(秒) -
  101. private $devices = []; // 存储设备数据
  102. /**
  103. * 处理原始数据并生成设备运行明细
  104. * @param array $rawData 原始数据源
  105. * @return array 生成的设备运行明细数据
  106. */
  107. public function generateData(array $rawData): array
  108. {
  109. // 首先按设备和日期分组汇总产量
  110. $this->processRawData($rawData);
  111. dd($this->devices);
  112. // 为每个设备生成运行明细
  113. $result = [];
  114. foreach ($this->devices as $deviceId => $deviceData) {
  115. // 为每一天生成数据
  116. foreach ($deviceData['daily_totals'] as $date => $total) {
  117. $details = $this->generateDeviceDayData($deviceData['device'], $date, $total);
  118. $result = array_merge($result, $details);
  119. }
  120. }
  121. try {
  122. DB::beginTransaction();
  123. if(! empty($result)){
  124. $chunkSize = 200; // 每次插入的数量
  125. foreach (array_chunk($result, $chunkSize) as $chunk) {
  126. SystemL::insert($chunk); // 使用模型的 insert 方法批量插入
  127. echo '200条写入成功' . "\n";
  128. }
  129. }
  130. DB::commit();
  131. }catch (\Throwable $exception){
  132. DB::rollBack();
  133. return [false, $exception->getMessage()];
  134. }
  135. return [true, ''];
  136. }
  137. /**
  138. * 处理原始数据,按设备和日期汇总产量
  139. * @param array $rawData 原始数据源
  140. */
  141. private function processRawData(array $rawData)
  142. {
  143. $map = Equipment::select('id','code','title')->get()->toArray();
  144. foreach ($map as $value){
  145. $map1[$value['id']] = $value;
  146. }
  147. foreach ($rawData as $item) {
  148. $deviceId = $item['device_id'];
  149. $t = $map1[$deviceId];
  150. $timestamp = $item['upd_time'];
  151. $finishedNum = floatval($item['finished_num']);
  152. // 解析日期
  153. $date = date('Y-m-d', $timestamp);
  154. // 初始化设备数据
  155. if (!isset($this->devices[$deviceId])) {
  156. $this->devices[$deviceId] = [
  157. 'device' => $t,
  158. 'total_production' => 0,
  159. 'daily_totals' => []
  160. ];
  161. }
  162. // 累加总产量和每日产量
  163. $this->devices[$deviceId]['total_production'] += $finishedNum;
  164. if (!isset($this->devices[$deviceId]['daily_totals'][$date])) {
  165. $this->devices[$deviceId]['daily_totals'][$date] = 0;
  166. }
  167. $this->devices[$deviceId]['daily_totals'][$date] += $finishedNum;
  168. }
  169. }
  170. /**
  171. * 为单个设备的一天生成详细的运行数据
  172. * @param int $deviceId 设备ID
  173. * @param string $date 日期(格式:YYYY-MM-DD)
  174. * @param float $totalProduction 当天总产量
  175. * @return array 生成的详细数据条目
  176. */
  177. private function generateDeviceDayData(array $device, string $date, float $totalProduction): array
  178. {
  179. $result = [];
  180. // 计算当天需要完成多少轮完整的吸料-下料循环
  181. $fullCycles = floor($totalProduction / self::SUCTION_CAPACITY);
  182. $filledAmount = $fullCycles * self::SUCTION_CAPACITY;
  183. $remainingWeight = bcsub($totalProduction, $filledAmount,2);
  184. // 设置当天的起始时间戳(早上7:30)
  185. $currentTime = strtotime($date . ' 07:30:00');
  186. // 执行完整循环
  187. for ($i = 0; $i < $fullCycles; $i++) {
  188. // 执行一次完整的吸料-下料过程
  189. $cycleData = $this->generateFullCycle($device, $currentTime);
  190. $result = array_merge($result, $cycleData);
  191. // 更新当前时间
  192. $currentTime = end($cycleData)['time'];
  193. // // 添加随机间隔时间
  194. // $interval = rand(self::SUCTION_INTERVAL_MIN, self::SUCTION_INTERVAL_MAX);
  195. // $currentTime += $interval;
  196. }
  197. // 如果有剩余量,执行部分吸料过程
  198. if ($remainingWeight > 0) {
  199. $lastKey = array_key_last($result); // 获取最后一个键
  200. $result[$lastKey]['value'] += $remainingWeight;
  201. // $remainingCyleData = $this->generatePartialCycle($device, $currentTime, $remainingWeight);
  202. // $result = array_merge($result, $remainingCyleData);
  203. }
  204. return $result;
  205. }
  206. /**
  207. * 随机插入急停事件,并模拟急停持续时间
  208. *
  209. * @param int $deviceId 设备ID
  210. * @param int &$currentTime 当前时间戳(引用传递,用于更新)
  211. * @param array &$result 结果数组(引用传递,用于添加急停记录)
  212. */
  213. private function maybeInsertEmergencyStop(array $device, int &$currentTime, array &$result, $data_point_id, $data_point_name): void
  214. {
  215. if (rand(1, 100) <= 3) { // 3% 的概率触发急停
  216. $emergencyStopTime = $currentTime;
  217. // 插入急停开始事件
  218. $result[] = [
  219. 'time' => $emergencyStopTime,
  220. 'value' => -1, // 标识为急停
  221. 'device_no' => $device['code'],
  222. 'device_name' => $device['title'],
  223. 'data_point_id' => $data_point_id,
  224. 'data_point_name' => $data_point_name,
  225. 'push_time' => $emergencyStopTime,
  226. // 'push_time_day' => date("Y-m-d H:i:s",$suctionStartTime)
  227. ];
  228. // 模拟急停持续时间(10~15秒)
  229. $stopDuration = rand(10, 15);
  230. $currentTime += $stopDuration;
  231. // 可选:插入急停结束事件(也可以不插,只用时间推移表示)
  232. // $result[] = [
  233. // 'time' => $currentTime,
  234. // 'value' => -2, // 急停恢复
  235. // 'device_no' => $deviceId,
  236. // 'push_time' => $currentTime
  237. // ];
  238. }
  239. }
  240. /**
  241. * 生成一个完整的吸料-下料循环数据
  242. * @param int $deviceId 设备ID
  243. * @param int $startTime 起始时间戳
  244. * @return array 生成的数据条目
  245. */
  246. private function generateFullCycle(array $device, int $startTime): array
  247. {
  248. $result = [];
  249. $currentTime = $startTime;
  250. // 吸料阶段
  251. for ($i = 0; $i < 1; $i++) {
  252. // 【吸料前】尝试插入急停
  253. $this->maybeInsertEmergencyStop($device, $currentTime, $result,5,"吸料急停");
  254. // 吸料开始
  255. $suctionStartTime = $currentTime;
  256. $result[] = [
  257. 'time' => $suctionStartTime,
  258. 'value' => 1,
  259. 'device_no' => $device['code'],
  260. 'device_name' => $device['title'],
  261. 'data_point_id' => 1,
  262. 'data_point_name' => "吸料",
  263. 'push_time' => $suctionStartTime,
  264. // 'push_time_day' => date("Y-m-d H:i:s",$suctionStartTime)
  265. ];
  266. // 吸料结束
  267. $suctionDuration = rand(self::SUCTION_TIME_MIN, self::SUCTION_TIME_MAX);
  268. $currentTime += $suctionDuration;
  269. // 【吸料后】尝试插入急停
  270. $this->maybeInsertEmergencyStop($device, $currentTime, $result,5,"吸料急停");
  271. $result[] = [
  272. 'time' => $currentTime,
  273. 'value' => self::SUCTION_WEIGHT,
  274. 'device_no' => $device['code'],
  275. 'device_name' => $device['title'],
  276. 'data_point_id' => 2,
  277. 'data_point_name' => "吸料结束且本次吸料重量",
  278. 'push_time' => $currentTime,
  279. // 'push_time_day' => date("Y-m-d H:i:s",$currentTime)
  280. ];
  281. // 添加随机间隔时间
  282. if ($i < 1) { // 最后一次吸料后不需要等待
  283. // $interval = rand(self::SUCTION_INTERVAL_MIN, self::SUCTION_INTERVAL_MAX);
  284. $interval = rand(10, 15);
  285. $currentTime += $interval;
  286. }
  287. }
  288. // 【下料前】尝试插入急停
  289. $this->maybeInsertEmergencyStop($device, $currentTime, $result,6,"下料急停");
  290. // 下料阶段
  291. $dischargeStartTime = $currentTime;
  292. $result[] = [
  293. 'time' => $dischargeStartTime,
  294. 'value' => 1,
  295. 'device_no' => $device['code'],
  296. 'device_name' => $device['title'],
  297. 'data_point_id' => 3,
  298. 'data_point_name' => "下料",
  299. 'push_time' => $dischargeStartTime,
  300. // 'push_time_day' => date("Y-m-d H:i:s",$dischargeStartTime)
  301. ];
  302. $dischargeDuration = rand(self::DISCHARGE_TIME_MIN, self::DISCHARGE_TIME_MAX);
  303. $currentTime += $dischargeDuration;
  304. // 【下料后】尝试插入急停
  305. $this->maybeInsertEmergencyStop($device, $currentTime, $result,6,"下料急停");
  306. $result[] = [
  307. 'time' => $currentTime,
  308. 'value' => self::SUCTION_CAPACITY,
  309. 'device_no' => $device['code'],
  310. 'device_name' => $device['title'],
  311. 'data_point_id' => 4,
  312. 'data_point_name' => "下料结束且本次下料重量",
  313. 'push_time' => $currentTime,
  314. // 'push_time_day' => date("Y-m-d H:i:s",$currentTime)
  315. ];
  316. return $result;
  317. }
  318. /**
  319. * 生成一个不完整的循环(只有吸料部分)
  320. * @param int $deviceId 设备ID
  321. * @param int $startTime 起始时间戳
  322. * @param float $requiredWeight 需要吸的重量
  323. * @return array 生成的数据条目
  324. */
  325. private function generatePartialCycle(array $device, int $startTime, float $requiredWeight): array
  326. {
  327. $result = [];
  328. $currentTime = $startTime;
  329. // 计算需要多少次吸料
  330. $suctionsNeeded = ceil($requiredWeight / self::SUCTION_WEIGHT);
  331. $totalSuctionWeight = 0;
  332. for ($i = 0; $i < $suctionsNeeded; $i++) {
  333. // 吸料开始
  334. $suctionStartTime = $currentTime;
  335. $result[] = [
  336. 'time' => $suctionStartTime,
  337. 'value' => 1,
  338. 'device_no' => $device['code'],
  339. 'device_name' => $device['title'],
  340. 'data_point_id' => 1,
  341. 'data_point_name' => "吸料",
  342. 'push_time' => $suctionStartTime,
  343. // 'push_time_day' => => date("Y-m-d H:i:s",$suctionStartTime)
  344. ];
  345. // 吸料结束
  346. $suctionDuration = rand(self::SUCTION_TIME_MIN, self::SUCTION_TIME_MAX);
  347. $currentTime += $suctionDuration;
  348. // 判断这次吸料是否会导致超过所需总量
  349. $addedWeight = ($i == $suctionsNeeded - 1) ? $requiredWeight - $totalSuctionWeight : self::SUCTION_WEIGHT;
  350. $totalSuctionWeight += $addedWeight;
  351. $result[] = [
  352. 'time' => $currentTime,
  353. 'value' => $addedWeight,
  354. 'device_no' => $device['code'],
  355. 'device_name' => $device['title'],
  356. 'data_point_id' => 2,
  357. 'data_point_name' => "吸料结束且本次吸料重量",
  358. 'push_time' => $currentTime,
  359. // 'push_time_day' => => date("Y-m-d H:i:s",$currentTime)
  360. ];
  361. // 添加随机间隔时间(在40-50分钟之间)
  362. if ($i < $suctionsNeeded - 1) {
  363. $interval = rand(self::SUCTION_INTERVAL_MIN, self::SUCTION_INTERVAL_MAX);
  364. $currentTime += $interval;
  365. }
  366. }
  367. return $result;
  368. }
  369. }