AsyncServiceImpl.java 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. package com.ruoyi.biz.service.impl;
  2. import cn.hutool.json.JSONArray;
  3. import cn.hutool.json.JSONObject;
  4. import com.ruoyi.biz.domain.*;
  5. import com.ruoyi.biz.service.IIotService;
  6. import com.ruoyi.biz.tools.Tools;
  7. import lombok.extern.slf4j.Slf4j;
  8. import org.springframework.scheduling.annotation.Async;
  9. import org.springframework.scheduling.annotation.AsyncResult;
  10. import org.springframework.stereotype.Service;
  11. import javax.annotation.Resource;
  12. import java.math.BigDecimal;
  13. import java.math.RoundingMode;
  14. import java.util.*;
  15. import java.util.concurrent.Future;
  16. import java.util.stream.Collectors;
  17. /**
  18. * 多线程执行任务
  19. *
  20. * @author wukai
  21. * @date 2024/5/4 20:35
  22. */
  23. @Service
  24. @Slf4j
  25. public class AsyncServiceImpl {
  26. @Resource
  27. private IIotService iotService;
  28. @Async("threadPoolTaskExecutor")
  29. public Future<Map<String, Object>> currData(TwinDevice twinDevice) {
  30. String table = twinDevice.getDevicePath();
  31. String sql = "select last * from " + table;
  32. JSONObject jsonObject = iotService.query(sql);
  33. JSONObject data = jsonObject.getJSONObject("data");
  34. JSONArray values = data.getJSONArray("values");
  35. Map<String, Object> dataMap = Tools.json2Map(values, table);
  36. dataMap.put("device", twinDevice);
  37. dataMap.put("total", values.size());
  38. return new AsyncResult<>(dataMap);
  39. }
  40. @Async("threadPoolTaskExecutor")
  41. public Future<Map<String, List<?>>> process(TwinDevice twinDevice, Date date, long startTime, long endTime, int period) {
  42. Map<String, List<?>> result = new HashMap<>(16);
  43. List<TwinCalcHour> calcHours = new ArrayList<>();
  44. String table = twinDevice.getDevicePath();
  45. TwinCalcHour hour = new TwinCalcHour();
  46. hour.setDeviceId(twinDevice.getDeviceId());
  47. hour.setDataDate(date);
  48. hour.setHour(period);
  49. Map<String, Object> map = calc(table, startTime, endTime);
  50. //0.已织造米数 1.总重量 2.总能耗
  51. float[] total = (float[]) map.get("total");
  52. hour.setLength(BigDecimal.valueOf(total[0]));
  53. hour.setWeight(BigDecimal.valueOf(total[1]));
  54. hour.setKwh(BigDecimal.valueOf(total[2]));
  55. hour.setOpenTime((long) total[3]);
  56. hour.setCloseTime((long) total[4]);
  57. calcHours.add(hour);
  58. List<TwinPanHeadInfo> panHeadInfo = (List<TwinPanHeadInfo>) map.get("panHead");
  59. panHeadInfo.forEach(info -> info.setDeviceId(twinDevice.getDeviceId()));
  60. List<TwinRecordStop> stopRecord = (List<TwinRecordStop>) map.get("stopRecord");
  61. stopRecord.forEach(stop -> {
  62. stop.setDeviceId(twinDevice.getDeviceId());
  63. stop.setDataDate(date);
  64. stop.setHour(period);
  65. });
  66. List<TwinRecordAlarms> alarmRecord = (List<TwinRecordAlarms>) map.get("alarmRecord");
  67. alarmRecord.forEach(alarm -> {
  68. alarm.setDeviceId(twinDevice.getDeviceId());
  69. alarm.setDataDate(date);
  70. alarm.setHour(period);
  71. });
  72. result.put("calc", calcHours);
  73. result.put("stopRecord", stopRecord);
  74. result.put("alarmRecord", alarmRecord);
  75. result.put("panHead", panHeadInfo);
  76. return new AsyncResult<>(result);
  77. }
  78. /**
  79. * 字段列表,方便看下标
  80. */
  81. private final String[] fields = {"Capacity_data_2", "Capacity_data_37", "Capacity_data_38", "Capacity_data_39", "Capacity_data_42",
  82. "Capacity_data_43", "Capacity_data_44", "Capacity_data_48", "Formula_data_3", "Formula_data_13",
  83. "Capacity_data_36", "Capacity_data_41", "Alarm_unit_1", "Alarm_unit_2", "Alarm_unit_3",
  84. "Alarm_unit_4", "Alarm_unit_5", "Alarm_unit_6", "Alarm_unit_7", "Alarm_unit_8",
  85. "Alarm_unit_9", "Alarm_unit_10", "Alarm_unit_11", "Alarm_unit_12", "Alarm_unit_13",
  86. "Alarm_unit_14", "Alarm_unit_15", "Alarm_unit_16", "Alarm_unit_17", "Alarm_unit_18",
  87. "Alarm_unit_19", "Alarm_unit_20", "Alarm_unit_21", "Alarm_unit_22", "Alarm_unit_23",
  88. "Alarm_unit_24", "Alarm_unit_25", "Alarm_unit_26", "Alarm_unit_27", "Capacity_data_33",
  89. "Capacity_data_15", "Capacity_data_16", "Capacity_data_17", "Capacity_data_18", "Capacity_data_19"
  90. };
  91. /**
  92. * 字段列表,方便查找位置
  93. */
  94. private final List<String> fieldList = Arrays.stream(fields).collect(Collectors.toList());
  95. /**
  96. * // 0 Capacity_data_2 已织造米数
  97. * // 1 Capacity_data_37 A班组开机时间
  98. * // 2 Capacity_data_38 A班组停机时间
  99. * // 3 Capacity_data_39 A班当前产量
  100. * // 4 Capacity_data_42 B班组开机时间
  101. * // 5 Capacity_data_43 B班组停机时间
  102. * // 6 Capacity_data_44 B班当前产量
  103. * // 7 Capacity_data_48 停机状态
  104. * // 8 Formula_data_3 米克重
  105. * // 9 Formula_data_13 卷曲幅宽
  106. */
  107. public Map<String, Object> calc(String table, long startTime, long endTime) {
  108. String sql = "select %s from %s where time>%s and time <=%s";
  109. sql = String.format(sql, Arrays.stream(fields).collect(Collectors.joining(",")), table, startTime, endTime);
  110. long s = System.currentTimeMillis();
  111. JSONObject jsonObject = iotService.query(sql);
  112. long e = System.currentTimeMillis();
  113. log.info("接口耗时:{}ms,table:{},time:{}", e - s, table, new Date(endTime));
  114. JSONObject data = jsonObject.getJSONObject("data");
  115. JSONArray values = data.getJSONArray("values");
  116. JSONArray timestamps = data.getJSONArray("timestamps");
  117. //初始时间点数据
  118. //0-data2,1-data37,2-data38,3-data39,4-data42,5-data-43,6data-44
  119. //2024-06-26 1-6不要了 0织造米长
  120. float[] first = new float[1];
  121. //上一个时间点数据
  122. float[] last = new float[1];
  123. //统计数据,后面2个分别代表0.织造米长 1.重量,2.电量,3.开机时间,4.停机时间
  124. float[] total = new float[5];
  125. //上一个时间点盘头数据
  126. int[] lastPanHead = new int[5];
  127. //米克重
  128. int lastMkz = 0;
  129. //卷曲幅宽
  130. float lastFk = 0f;
  131. int last48 = 0;
  132. //上一轮最后一条停机记录
  133. JSONArray old = null;
  134. //上一轮最后一条告警记录
  135. // Object oldV = CacheUtils.get(Constants.IOT_TOKEN, table + "-stop");
  136. // if (oldV != null) {
  137. // //获取存储的最后一次停机状态值
  138. // old = (JSONArray) oldV;
  139. // }
  140. //告警时间记录
  141. List<TwinRecordAlarms> alarmRecord = new ArrayList<>();
  142. //停机记录
  143. List<TwinRecordStop> stopRecord = new ArrayList<>();
  144. //盘头记录
  145. List<TwinPanHeadInfo> panHeadInfo = new ArrayList<>();
  146. for (int i = 0; i < timestamps.size(); i++) {
  147. JSONArray da = values.getJSONArray(i);
  148. //0-data2,1-data37,2-data38,3-data39,4-data42,5-data-43,6data-44
  149. //当前时间数据
  150. float[] curr = {da.getFloat(0)};
  151. int[] currPan = {da.getInt(fieldList.indexOf("Capacity_data_15")), da.getInt(fieldList.indexOf("Capacity_data_16")), da.getInt(fieldList.indexOf("Capacity_data_17")),
  152. da.getInt(fieldList.indexOf("Capacity_data_18")), da.getInt(fieldList.indexOf("Capacity_data_19"))};
  153. int curr48 = da.getInt(7);
  154. if (i == 0) {
  155. //第一次数据是上次最后一条,只做记录用,不做处理
  156. first = curr.clone();
  157. last = curr.clone();
  158. lastPanHead = currPan.clone();
  159. lastMkz = da.getInt(8);
  160. lastFk = da.getFloat(9);
  161. last48 = curr48;
  162. continue;
  163. }
  164. //计算电量
  165. calcKwh(da, total);
  166. //计算盘头
  167. calcPan(currPan, lastPanHead, panHeadInfo, timestamps.getLong(i));
  168. for (int j = 0; j < first.length; j++) {
  169. //如果当前值为小于上一个,且上一个值不为0,则计算
  170. //因为会出现数据波动,停机再启动之后的第一个点不为0,为0.00几几几的
  171. //计算累加类的数据
  172. if (curr[j] < last[j] && last[j] != 0f) {
  173. calcTotal(j, last, first, total, lastMkz, lastFk);
  174. first[j] = curr[j];
  175. }
  176. }
  177. calcAlarms(values, old, timestamps.getLong(i), alarmRecord, i);
  178. calcStops(curr48, last48, timestamps.getLong(i), total, stopRecord, old, i);
  179. //复制数组,设置last值为当前值
  180. last = curr.clone();
  181. lastPanHead = currPan.clone();
  182. lastMkz = da.getInt(8);
  183. lastFk = da.getFloat(9);
  184. last48 = curr48;
  185. }
  186. //最后再补一次计算
  187. for (int j = 0; j < first.length; j++) {
  188. calcTotal(j, last, first, total, lastMkz, lastFk);
  189. }
  190. //存入最后一条记录的停机状态
  191. // CacheUtils.put(Constants.IOT_TOKEN, table, values.getJSONArray(values.size() - 1));
  192. total[3] = (endTime - startTime) / 1000 + 1 - total[4];
  193. Map<String, Object> result = new HashMap<>(16);
  194. result.put("total", total);
  195. result.put("stopRecord", stopRecord);
  196. result.put("alarmRecord", alarmRecord);
  197. result.put("panHead", panHeadInfo);
  198. return result;
  199. }
  200. /**
  201. * 计算盘头信息
  202. * 数组 分别是GB1-GB5
  203. *
  204. * @param currPan 当前剩余圈数
  205. * @param lastPanHead 上一条记录剩余圈数
  206. * @param panHeadInfo info
  207. * @param time 时间戳
  208. */
  209. private void calcPan(int[] currPan, int[] lastPanHead, List<TwinPanHeadInfo> panHeadInfo, Long time) {
  210. //如果当前记录大于上一条记录,则证明是重新叫料了,需要记录下当前值
  211. for (int i = 0; i < currPan.length; i++) {
  212. if (currPan[i] > lastPanHead[i]) {
  213. TwinPanHeadInfo info = new TwinPanHeadInfo();
  214. info.setRecordTime(new Date(time));
  215. info.setPhNum((long) (i + 1));
  216. info.setPhMax((long) currPan[i]);
  217. panHeadInfo.add(info);
  218. }
  219. }
  220. }
  221. /**
  222. * 能耗计算
  223. * total[2].总电量
  224. *
  225. * @param da 当前数据
  226. * @param total 统计数组
  227. */
  228. private void calcKwh(JSONArray da, float[] total) {
  229. BigDecimal v = da.getBigDecimal(fieldList.indexOf("Capacity_data_33"));
  230. //单位为W 换算成当前这一秒的用电量 需要除以1000变成千瓦,再除以3600 变成千瓦时
  231. v = v.divide(BigDecimal.valueOf(3600), 5, RoundingMode.HALF_UP);
  232. v = v.divide(BigDecimal.valueOf(1000), 5, RoundingMode.HALF_UP);
  233. float kwh = v.floatValue();
  234. total[2] = total[2] + kwh;
  235. }
  236. /**
  237. * 停机次数计算
  238. */
  239. /**
  240. * 停机处理
  241. *
  242. * @param curr 当前值
  243. * @param last 上一条记录的值
  244. * @param time 时间
  245. * @param total 统计
  246. * @param stopRecord 记录
  247. * @param oldV 上次的最后一条记录的值
  248. * @param i 当前时序
  249. */
  250. private void calcStops(int curr, int last, long time, float[] total, List<TwinRecordStop> stopRecord, JSONArray oldV, int i) {
  251. //取消毫秒
  252. time = time / 1000 * 1000;
  253. TwinRecordStop stop = new TwinRecordStop();
  254. //如果第一条记录为0,需要判断之前有木有没有结束时间的停机
  255. //暂时取消这个判断
  256. // if (i == 1 && oldV != null) {
  257. // int old = oldV.getInt(7);
  258. // if (last != old) {
  259. // stop.setStopType(old);
  260. // stop.setEndTime(new Date(time));
  261. // stopRecord.add(stop);
  262. // return;
  263. // }
  264. // }
  265. //如果第一条记录不为0,则直接记录停机开始时间
  266. if (i == 1 && curr != 0) {
  267. stop = new TwinRecordStop();
  268. //记录停机开始时间
  269. stop.setStopType(curr);
  270. stop.setStartTime(new Date(time));
  271. stopRecord.add(stop);
  272. return;
  273. }
  274. if (curr != 0) {
  275. total[4]++;
  276. if (curr != last) {
  277. if (last != 0) {
  278. //记录上个停机类型的结束时间
  279. stop.setStopType(last);
  280. stop.setEndTime(new Date(time));
  281. stopRecord.add(stop);
  282. }
  283. stop = new TwinRecordStop();
  284. //记录停机开始时间
  285. stop.setStopType(curr);
  286. stop.setStartTime(new Date(time));
  287. stopRecord.add(stop);
  288. }
  289. } else if (last != 0) {
  290. //判断停机结束之后的开机
  291. stop.setStopType(last);
  292. stop.setEndTime(new Date(time));
  293. stopRecord.add(stop);
  294. }
  295. }
  296. /**
  297. * 告警计算
  298. *
  299. * @param values 所有记录
  300. * @param old 上次存储的
  301. * @param time 当前时间
  302. * @param alarmRecord 告警记录
  303. * @param i 时序
  304. */
  305. private void calcAlarms(JSONArray values, JSONArray old, long time, List<TwinRecordAlarms> alarmRecord, int i) {
  306. JSONArray curr = values.getJSONArray(i);
  307. JSONArray last = values.getJSONArray(i - 1);
  308. TwinRecordAlarms recordAlarms;
  309. int index = 26;
  310. /*暂时取消
  311. // if (i == 1 && old != null) {
  312. // //判断第一条记录为0,并且上一轮存储的值,需要判断之前有没有未结束的告警
  313. // int j = 0;
  314. // for (; j < index; j++) {
  315. // boolean oldV = curr.getBool(j + 12);
  316. // boolean lastV = last.getBool(j + 12);
  317. // if (oldV && !lastV) {
  318. // recordAlarms = new TwinRecordAlarms();
  319. // recordAlarms.setAlarmType(j + 1);
  320. // recordAlarms.setEndTime(new Date(time));
  321. // alarmRecord.add(recordAlarms);
  322. // }
  323. // }
  324. // //需要单独计算alarm27
  325. // //数据位置
  326. // int pos = j + 12;
  327. // int old27 = curr.getInt(pos);
  328. // //上面已经处理过i=0,所以这里i不可能等于0
  329. // int last27 = last.getInt(pos);
  330. // if (old27 != 0 && last27 == 0) {
  331. // recordAlarms = new TwinRecordAlarms();
  332. // recordAlarms.setAlarmType(j + 1);
  333. // recordAlarms.setEndTime(new Date(time));
  334. // alarmRecord.add(recordAlarms);
  335. // }
  336. // }
  337. */
  338. boolean[] flags = new boolean[index + 1];
  339. for (int kk = 0; kk < flags.length; kk++) {
  340. flags[kk] = true;
  341. }
  342. if (i == 1) {
  343. int j = 0;
  344. for (; j < index; j++) {
  345. boolean currV = curr.getBool(j + 12);
  346. if (currV) {
  347. flags[j] = false;
  348. recordAlarms = new TwinRecordAlarms();
  349. recordAlarms.setAlarmType(j + 1);
  350. recordAlarms.setStartTime(new Date(time));
  351. alarmRecord.add(recordAlarms);
  352. }
  353. }
  354. //需要单独计算alarm27
  355. //数据位置
  356. int pos = j + 12;
  357. int curr27 = curr.getInt(pos);
  358. if (curr27 != 0) {
  359. flags[j] = false;
  360. recordAlarms = new TwinRecordAlarms();
  361. recordAlarms.setAlarmType(j + 1);
  362. recordAlarms.setStartTime(new Date(time));
  363. alarmRecord.add(recordAlarms);
  364. }
  365. }
  366. int j = 0;
  367. for (; j < index; j++) {
  368. if (flags[j]) {
  369. boolean currV = curr.getBool(j + 12);
  370. boolean lastV = last.getBool(j + 12);
  371. if (currV && !lastV) {
  372. recordAlarms = new TwinRecordAlarms();
  373. recordAlarms.setAlarmType(j + 1);
  374. recordAlarms.setStartTime(new Date(time));
  375. alarmRecord.add(recordAlarms);
  376. }
  377. if (!currV && lastV) {
  378. recordAlarms = new TwinRecordAlarms();
  379. recordAlarms.setAlarmType(j + 1);
  380. recordAlarms.setEndTime(new Date(time));
  381. alarmRecord.add(recordAlarms);
  382. }
  383. }
  384. }
  385. if (flags[j]) {
  386. //需要单独计算alarm27
  387. //数据位置
  388. int pos = j + 12;
  389. int alarm27 = curr.getInt(pos);
  390. //上面已经处理过i=0,所以这里i不可能等于0
  391. int last27 = last.getInt(pos);
  392. if (alarm27 != 0 && last27 == 0) {
  393. recordAlarms = new TwinRecordAlarms();
  394. recordAlarms.setAlarmType(j + 1);
  395. recordAlarms.setStartTime(new Date(time));
  396. alarmRecord.add(recordAlarms);
  397. }
  398. if (alarm27 == 0 && last27 != 0) {
  399. recordAlarms = new TwinRecordAlarms();
  400. recordAlarms.setAlarmType(j + 1);
  401. recordAlarms.setEndTime(new Date(time));
  402. alarmRecord.add(recordAlarms);
  403. }
  404. }
  405. }
  406. /**
  407. * 重量计算 提取公共方法
  408. */
  409. private void calcTotal(int j, float[] last, float[] first, float[] total, int lastMkz, float lastFk) {
  410. float v = last[j] - first[j];
  411. total[j] += v;
  412. if (j == 0) {
  413. //如果是米长,则计算重量
  414. float weight = BigDecimal.valueOf(v * lastMkz * lastFk / 1000 / 1000).setScale(2, RoundingMode.HALF_UP).floatValue();
  415. total[1] = total[1] + weight;
  416. }
  417. }
  418. }