【JAVA】利用Redisson和Spring實現高效物聯溫度控制鏈路,確保溫度調節的準確性和效率,定時鏈路執行使用案例,一環扣一環

主要功能和場景

  1. 柔性調溫策略:這個類主要用于管理一個溫度調節流程,通過不同的策略(如策略1和策略2)來調節溫度,確保設備或環境中的溫度達到預設的目標。

  2. 緊急停止機制:在流程執行過程中,如果需要緊急停止,可以通過設置一個標志來立即停止所有正在進行的任務。

  3. 定時任務管理:使用Java的ScheduledExecutorService來管理定時任務,如定期檢查溫度、執行特定的溫度調節策略等。

  4. Redis集成:使用Redisson客戶端與Redis數據庫交互,存儲和檢索緊急停止的狀態。

代碼詳細描述

  • 類結構

    • 使用@Slf4j注解來自動生成日志記錄器。
    • 使用@RequiredArgsConstructor注解來自動注入對象
  • 常量定義

    • STRATEGY_DURATION:定義了策略的持續時間,這里是5分鐘減去30秒。
    • EMERGENCY_STOPPED_KEY:在Redis中存儲緊急停止狀態的鍵。
    • EMERGENCY_STOPPEDRESTART_STOPPED:分別代表緊急停止和重啟的狀態。
  • 方法

    • startProcessChain(Long planId):啟動整個流程,首先檢查是否需要緊急停止,然后開始執行柔性調溫策略1。
    • applyFlexibleTempStrategy1(Long planId)applyFlexibleTempStrategy2(Long planId):分別實現策略1和策略2,包括下發溫度調節指令和召測命令,以及根據召測結果調整策略。
    • waitFor5MinutesAfterStrategy1(Long planId)waitFor10MinutesAfterStrategy2(Long planId):在策略1和策略2之后等待一定時間,然后執行下一步。
    • applyTargetPower(Long planId):在策略2之后執行目標功率控制。
    • waitFor10MinutesBeforeStop(Long planId):在停止前等待10分鐘。
    • emergencyStop(Long planId)restart(Long planId):分別用于緊急停止流程和重啟流程。
    • isEmergencyStopped(Long planId)setEmergencyStopped(Long planId):用于檢查和設置緊急停止狀態。

使用場景

這個類適用于需要精確控制溫度的場景,如數據中心、實驗室或工業生產環境,其中溫度的精確控制對于設備的正常運行至關重要。通過這個流程管理器,可以確保在各種情況下都能有效地調節溫度,同時提供緊急停止機制以應對突發情況。

ProcessManager類中,每個流程都創建了一個ScheduledExecutorService實例,通過Executors.newScheduledThreadPool(1)創建了一個大小為1的線程池。這種設計有幾個好處:

  1. 資源控制:通過限制線程池的大小為1,可以確保每個流程在其生命周期內只使用一個線程。這有助于防止資源過度消耗,特別是在高并發環境中,可以避免因創建過多線程而導致的系統資源耗盡。

  2. 任務串行執行:由于線程池大小為1,所有提交給該線程池的任務將按順序串行執行。這意味著一個流程中的所有任務都是順序執行的,不會并發執行,這有助于簡化任務之間的同步和數據依賴問題。

  3. 簡化同步:在某些情況下,流程中的任務可能需要訪問共享資源或狀態,串行執行可以減少或消除對這些資源進行復雜同步的需求,因為任務不會并發地訪問這些資源。

  4. 避免競態條件:在單線程環境中,不會出現競態條件(race conditions),因為任務是按順序執行的。這對于確保流程的正確性和可預測性非常重要。

  5. 易于管理:單線程池使得任務的管理和監控更加簡單。例如,如果需要取消所有任務,只需調用scheduler.shutdown()即可。

  6. 適合定時任務ScheduledExecutorService特別適合執行定時任務,如周期性任務或延遲任務。通過使用單線程池,可以確保這些任務按照預定的時間表執行,而不會因為線程爭用而產生時間偏差。

總之,為每個流程創建一個單線程的ScheduledExecutorService可以提供一個簡單、可控且高效的方式來管理流程中的定時任務,同時確保流程的穩定性和可預測性。

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hutool.core.text.CharSequenceUtil;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;/*** 能力認定流程manager*/
@Slf4j
@Component
@RequiredArgsConstructor
public class ProcessManager {/*** 5分鐘,單位為毫秒(策略持續時間),30秒執行一次,減少30秒是由于第一次進入默認執行一次召測*/private static final long STRATEGY_DURATION = 5 * 60 * 1000 - 30000;/*** Redis中存儲緊急停止值*/private static final String EMERGENCY_STOPPED_KEY = "IOT:EMERGENCY:STOPPED:VAL:";/*** 緊急停止值*/private static final String EMERGENCY_STOPPED = "1";/*** 重啟*/private static final String RESTART_STOPPED = "0";/*** Redisson客戶端*/private final RedissonClient redissonClient;/*** 啟動任務*/public void startProcessChain(Long planId) {// 否緊急停止if (isEmergencyStopped(planId)) {return;}ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);log.info("Step 0 startProcessChain with task ID: " + planId);// 立即執行柔性調溫策略1scheduler.schedule(() -> applyFlexibleTempStrategy1(planId), 0, TimeUnit.SECONDS);// 立馬取消當前任務scheduler.shutdown();}/*** 柔性調溫策略1*/private void applyFlexibleTempStrategy1(Long planId) {// 緊急停止if (isEmergencyStopped(planId)) {return;}log.info("Step 1 下發遙調指令task ID: " + planId);// 下發遙調指令String tempValue = "18";// 下發遙調任務失敗,流程結束if (packageCommandTempIssuance()) {return;}long startTime = System.currentTimeMillis();// 整個流程持續五分鐘,30秒執行一次ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);scheduler.scheduleAtFixedRate(() -> {// 緊急停止if (isEmergencyStopped(planId)) {return;}log.info("Step 1 下發召測命令 task ID: " + planId);int unConformNum = packageCallForTest();// 不滿足數量=0時表示都滿足18° ± 0.5°,立即執行下一步if (unConformNum == 0) {log.info("Step 1 策略1條件已滿足,執行下一步等待 task ID: " + planId);// 滿足條件立馬執行:"等待五分鐘"策略scheduler.schedule(() -> waitFor5MinutesAfterStrategy1(planId), 0, TimeUnit.SECONDS);// 立馬取消當前任務scheduler.shutdown();}long elapsedTime = System.currentTimeMillis() - startTime;// 5分鐘內所有通道溫度未達到18° ± 0.5°,任務終止if (unConformNum != 0 && elapsedTime >= STRATEGY_DURATION) {log.info("Step 1 下發召測命令不滿足要求:planId:{},不滿足數量:{} ", planId, unConformNum);emergencyStop(planId);scheduler.shutdown();}}, 0, 30, TimeUnit.SECONDS);}/*** 柔性調溫策略1之后等待五分鐘:配置碼值動態獲取*/private void waitFor5MinutesAfterStrategy1(Long planId) {// 緊急停止if (isEmergencyStopped(planId)) {return;}String value = "5";log.info("After step 1 waiting for " + value + " minutes for task ID: " + planId);ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);// 五分鐘之后執行applyFlexibleTempStrategy2scheduler.schedule(() -> applyFlexibleTempStrategy2(planId), Long.parseLong(value),TimeUnit.MINUTES);// 立馬取消當前任務scheduler.shutdown();}/*** 柔性調溫策略2*/private void applyFlexibleTempStrategy2(Long planId) {// 緊急停止if (isEmergencyStopped(planId)) {return;}log.info("Step 2 策略2下發遙調指令 task ID: " + planId);// 下發遙調指令String tempValue = "26";// 下發遙調任務失敗,流程結束if (packageCommandTempIssuance()) {emergencyStop(planId);return;}long startTime = System.currentTimeMillis();// 整個流程持續五分鐘ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);scheduler.scheduleAtFixedRate(() -> {// 緊急停止if (isEmergencyStopped(planId)) {return;}log.info("Step 2 策略2下發召測指令 task ID: " + planId);int unConformNum = packageCallForTest();// 不滿足數量=0時表示都滿足26° ± 0.5°,立即執行下一步if (unConformNum == 0) {log.info("Step 2 策略2條件已滿足,執行下一步等待 task ID: " + planId);// 滿足條件立馬執行:"等待五分鐘"策略scheduler.schedule(() -> waitFor10MinutesAfterStrategy2(planId), 0, TimeUnit.SECONDS);// 立馬取消當前任務scheduler.shutdown();}long elapsedTime = System.currentTimeMillis() - startTime;// 5分鐘內所有通道溫度未達到26° ± 0.5°,任務終止if (unConformNum != 0 && elapsedTime >= STRATEGY_DURATION) {log.info("Step 2 下發召測命令不滿足要求:planId:{},不滿足數量:{} ", planId, unConformNum);emergencyStop(planId);// 立馬取消當前任務scheduler.shutdown();}}, 0, 30, TimeUnit.SECONDS);}/*** 柔性調溫策略2之后等10分鐘:配置碼值動態獲取*/private void waitFor10MinutesAfterStrategy2(Long planId) {if (isEmergencyStopped(planId)) {return;}String value = "10";log.info("After step 2 Waiting for " + value + " minutes task: " + planId);ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);// 等10分鐘目標功率控制scheduler.schedule(() -> applyTargetPower(planId), Long.parseLong(value), TimeUnit.MINUTES);// 立馬取消當前任務scheduler.shutdown();}/*** 目標功率控制*/private void applyTargetPower(Long planId) {if (isEmergencyStopped(planId)) {return;}log.info("Step 3 目標功率控制 task ID: " + planId);// 下發遙調任務失敗,流程結束if (packageCommandTempIssuance()) {emergencyStop(planId);return;}ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);// 立即執行停止前等待10分鐘scheduler.schedule(() -> waitFor10MinutesBeforeStop(planId), 0,TimeUnit.SECONDS);// 立馬取消當前任務scheduler.shutdown();}/*** 停止前等待10分鐘:配置碼值動態獲取*/private void waitFor10MinutesBeforeStop(Long planId) {// 緊急停止if (isEmergencyStopped(planId)) {return;}String tempValue = "10";log.info("After step 3 wait for " + tempValue + " minutes task ID: " + planId);ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);scheduler.schedule(() -> {log.info("waitFor10MinutesBeforeStop step 4 stop task ID: " + planId);// 立馬取消當前任務scheduler.shutdown();}, Long.parseLong(tempValue), TimeUnit.MINUTES);}/*** 緊急停止*/public void emergencyStop(Long planId) {log.info("Emergency stopping the process with task ID: " + planId);setEmergencyStopped(planId);}/*** 重啟一鍵能力認證流程*/public void restart(Long planId) {// 1:true,0:falseredissonClient.getBucket(EMERGENCY_STOPPED_KEY + planId).set(RESTART_STOPPED);}/*** 是否緊急停止*/private boolean isEmergencyStopped(Long planId) {RBucket < String > bucket = redissonClient.getBucket(EMERGENCY_STOPPED_KEY + planId);String isEmergencyStopped = bucket.get();return isEmergencyStopped != null && CharSequenceUtil.equals(isEmergencyStopped, EMERGENCY_STOPPED);}/*** 設置緊急停止值*/private void setEmergencyStopped(Long planId) {// 1:true,0:falseredissonClient.getBucket(EMERGENCY_STOPPED_KEY + planId).set(EMERGENCY_STOPPED);}/*** 下發遙調指令*/private boolean packageCommandTempIssuance() {// 下發遙調指令,示意代碼return true;}/*** 下發召測命令** @return 不滿足數量*/private Integer packageCallForTest() {// 下召測命令,拿內機數據,示意代碼return 0;}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/39191.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/39191.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/39191.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

AI模型大宗師Transformer的Encoder魔法棒

在AI大模型數字王國里&#xff0c;有一位名叫Transformer的魔法大宗師。他有一個神奇的百寶箱&#xff0c;里面有很多魔法工具&#xff0c;其中有個工具叫Encoder&#xff0c;這個工具擁有一種神奇的力量&#xff0c;可以將復雜的輸入信息進行編碼&#xff0c;提取出關鍵的特征…

以 Vue 3 項目為例,你是否經常遇到 import 語句順序混亂的問題?要想解決它其實很容易!

大家好,我是CodeQi! 在項目開發過程中,我們經常會遇到項目中的 import 語句順序混亂的問題。 這不僅會影響代碼的可讀性,還可能使我們代碼在提交的時候產生不必要的沖突。 面對這種情況,要想解決它其實很容易。 通過合理的規范和自動化工具,我們可以確保 import 語句…

計算機網絡 —— 路由協議:RIP、OSPF、BGP、MPLS

路由協議 1. 定義2. IGP2.1 RIP2.2 OSPF 3. BGP4. MPLS 1. 定義 互聯網中需要通過路由將數據發送至目標主機。 路由器根據路由控制表(RoutingTable)轉發數據包&#xff0c;它根據所收到的數據包中目標主機的IP地址與路由控制表的比較得出下一個應該接收的路由器。 &#xff…

大學生放學后一定要做的4件事情

不知道有多少學生們&#xff0c;和我當年一樣&#xff0c;上課不想去&#xff0c;找人幫著點名。放學后&#xff0c;去網吧&#xff0c;瞎玩&#xff0c;玩著玩著就畢業了&#xff0c;現在想想啊&#xff0c;真是不應該。所以&#xff0c;下面這4件事情&#xff0c;我建議你去做…

Linux/Unix命令

這篇是另一篇內容的前置知識。因為項目部署測試需要&#xff0c;向公司申請了一個虛擬機做服務器用。以下是回溯的命令&#xff0c;多了解了解&#xff0c;拓寬知識面吧。PS&#xff1a;本人unix/linux知識0&#xff0c;見啥都稀奇&#xff0c;小白一個&#xff0c;知識淺顯&am…

CSharp——Encoding編碼詳情

CSharp-Encoding編碼 在網絡通信中&#xff0c;很多情況下都是將字符信息轉成字節序列進行傳輸。將字符序列轉為字節序列的過程稱為編碼。當這些字節傳送到接收方&#xff0c;接收方需要逆向將字節序列轉為字符序列。這個過程就是解碼。 常見編碼有ASCII字符集 &#xff0c;非…

SQL | join 的目的是什么?

如是我聞&#xff1a; 在 SQL 中使用 JOIN 的目的是將兩個或多個數據庫表的數據組合在一起&#xff0c;這樣我們就可以在一個查詢中獲取這些表的相關信息。 假設我們有兩本不同的書&#xff0c;一本記錄了孩子們的名字和他們的愛好&#xff0c;另一本記錄了他們的家庭地址。如…

盛元廣通打造智慧校園實驗室安全管理系統

盛元廣通智慧校園實驗室安全管理系統以安全為重點&#xff0c;構建由學校、二級單位、實驗室組成的三級聯動的實驗室安全多級管理體系、多類用戶角色&#xff0c;內置教育部標準檢查表&#xff0c;支撐實驗室相關業務過程的智慧管理。實現通過PC端/手機移動端開展檢查工作、手機…

sh腳本筆記2

test條件測試 語法 條件測試語法說明語法1&#xff1a;test <測試表達式>這是利用test命令進行條件測試表達式的方法。test命令和“<測試表達式>”之間至少有一個空格語法2&#xff1a;[ <測試表達式> ]這是通過[]&#xff08;單中括號&#xff09;進行條件…

將exe文件添加到注冊表中,實現開機時自動運行

目錄 一、前言 二、代碼 三、使用步驟 1.編譯生成exe文件、 2.以管理員身份運行代碼 3.打開注冊表&#xff0c;驗證結果 一、前言 在Windows操作系統中&#xff0c;將exe文件的路徑添加到注冊表下&#xff0c;主要用于實現程序的開機自動運行功能。 注冊表路徑為&#xf…

白騎士的C語言教學基礎篇 1.3 控制流

系列目錄 上一篇&#xff1a;白騎士的C語言教學基礎篇 1.2 C語言基礎語法 在這一節中&#xff0c;我們將介紹C語言中的控制流結構&#xff0c;包括條件語句、循環語句以及循環控制語句。這些結構允許我們根據不同的條件執行不同的代碼塊&#xff0c;從而使程序更具靈活性和功能…

Python中的并發編程(5)PyQt 多線程

PyQt 多線程 1 卡住的計時器 我們定義了一個計時器&#xff0c;每秒鐘更新一次顯示的數字。此外我們定義了一個耗時5秒的任務oh_no&#xff0c;和按鈕“危險”綁定。 當我們點擊“危險”按鈕時&#xff0c;程序去執行oh_no&#xff0c;導致顯示停止更新了。 import sys im…

Aspose.PDF功能演示:在程序中合并 JPG 文件

Aspose.PDF 是一款高級PDF處理API&#xff0c;可以在跨平臺應用程序中輕松生成&#xff0c;修改&#xff0c;轉換&#xff0c;呈現&#xff0c;保護和打印文檔。無需使用Adobe Acrobat。此外&#xff0c;API提供壓縮選項&#xff0c;表創建和處理&#xff0c;圖形和圖像功能&am…

Kile鐘優化等級講解

這里直接說說kile的優化等級: 這里有4個等級,分別為:-O0、-O1、-O2、-O3 為什么要進行編譯優化? 主要目的: 1. 提高代碼執行的速度; 2. 減少內存占用; 3. 降低能耗,延長電池壽命; 4. 消除代碼中冗余和不必要的代碼,提高程序穩定性和可靠性。 Kile等級描述: -O0:這…

leetcode-21-回溯-全排列及其去重

一、[46]全排列 給定一個 沒有重復 數字的序列&#xff0c;返回其所有可能的全排列。 示例: 輸入: [1,2,3]輸出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ] 其中&#xff0c;不需要使用startIndex used數組&#xff0c;其實就是記錄此時path里都有哪些元素…

【圖論】200. 島嶼問題

200. 島嶼問題 難度&#xff1a;中等 力扣地址&#xff1a;https://leetcode.cn/studyplan/top-100-liked/ 問題描述 給你一個由 1&#xff08;陸地&#xff09;和 0&#xff08;水&#xff09;組成的的二維網格&#xff0c;請你計算網格中島嶼的數量。 島嶼總是被水包圍&…

一個專為Android平臺設計的高度可定制的日歷庫

大家好&#xff0c;今天給大家分享一個高度可定制的日歷庫kizitonwose/Calendar。 Calendar專為Android平臺設計&#xff0c;支持RecyclerView和Compose框架。它提供了豐富的功能&#xff0c;允許開發者根據需求定制日歷的外觀和功能。 項目介紹 此庫是開發Android應用時&…

大型語言模型評估調查

原文鏈接&#xff1a;A Survey on Evaluation of Large Language Models | ACM Transactions on Intelligent Systems and Technology 本文從三個關鍵維度&#xff1a;評價什么、在哪里評價和如何評價&#xff0c;對這些 LLMs 評價方法進行了全面回顧。 首先&#xff0c;我們…

第十四屆藍橋杯省賽C++A組F題【買瓜】題解(AC)

70pts 題目要求我們在給定的瓜中選擇一些瓜&#xff0c;可以選擇將瓜劈成兩半&#xff0c;使得最后的總重量恰好等于 m m m。我們的目標是求出至少需要劈多少個瓜。 首先&#xff0c;我們注意到每個瓜的重量最多為 1 0 9 10^9 109&#xff0c;而求和的重量 m m m 也最多為…

C++ 徹底搞懂指針(1)

當有人問起,什么是指針時,我會毫不猶豫地回答,指針變量存放的是地址!然后呢,好像也說不出什么了,今天就再來詳細看一下指針吧。 本文提綱如下: ? 指針變量 ? 未初始化的指針 ? NULL ? void指針 ? 指針的指針 首先要明白幾點: ? 每個字節都有…