---
title: 進階排程自動控制
slideOptions:
transition: slide
transitionSpeed: 'fast'
theme: league
---
<!-- .slide: data-background="https://i.imgur.com/gJLfldF.jpg" data-background-color="#111111" data-background-opacity="0.2" -->
###### tags: `iot-greenhouse` `lab` 返回[物聯網自動化溫室監控系統](/s/PJIS24sFR76axKI0xOrnjQ)
## <span style="color:#F9BF45;">進階排程自動控制</span>
###### [點我開啟簡報模式](/@BEExANT-ta/r1omARwot#)
###### <kbd>ESC</kbd> 鍵進入總覽模式
###### <kbd>←</kbd> <kbd>↑</kbd> <kbd>↓</kbd> <kbd>→</kbd> 切換頁面
---
## 目標
**在 [控制幫浦與電磁閥](/J5myhWWZRviz1FPdEeWYpA) 中已了解如何設定開始與結束時間進行控制,再來要設計多種排程模式自動控制電磁閥啟動,能夠實際應用在現實案例中。**
---
## 設計原理
- 將區域變數的啟動時間改為全域變數,在到達設定的時間後時,重新設定啟動時間,就可以設計出 ==每隔一定時間啟動排程== 的控制模式。
- 從時間取出星期的值,便能判斷要在一週中的星期幾進行控制,可以設計出 ==每週指定的星期進行分區灑水== 的控制模式。
- 加入指定站別時間設定,能夠在設定時間中啟動對應站別及時間,可以設計出 ==每週指定的星期,根據不同作物的站別進行不同時間的噴灑== 的控制模式,以達到精準給水的效果。
----
:::info
:globe_with_meridians: 參考資源
- [JavaScript Date getDay()](https://www.w3schools.com/jsref/jsref_getday.asp)
:::
---
## 範例程式碼
新增程式檔並命名 ==進階排程自動控制==,將以下程式碼複製貼上程式編輯區執行。
### 每隔10分鐘啟動一次排程,排程中每站輪流噴灑5秒
```javascript=
window.count = 0;
window.finish = false;
window.start = new Date("01/01/2022 12:00:00");
let date = new Date(Date.now());
let time = 600 * 1000;
let step = 5 * 1000;
if(date.getTime() >= start.getTime() && !finish)
{
console.log("已達啟動時間,開始各站灑水。");
valveOn.set(5);
delay(step);
valveOff.set(5);
delay(1000);
valveOn.set(6);
delay(step);
valveOff.set(6);
delay(1000);
valveOn.set(7);
delay(step);
valveOff.set(7);
delay(1000);
valveOn.set(8);
delay(step);
valveOff.set(8);
delay(1000);
finish = true;
count = count + 1;
}
if(finish)
{
finish = false;
date = new Date(Date.now());
start = new Date(date.getTime() + time);
console.log("第 "+count+" 次排程結束,下次啟動時間為:\n" + start);
}
```
### 每週一、三、五、日的中午12點啟動一次排程,排程中每站輪流噴灑5秒
```javascript=
window.count = 0;
window.finish = false;
let date = new Date(Date.now());
let start_week = [1,3,5,0];
let start_hour = 12;
let start_minute = 0;
let step = 5 * 1000;
if(start_week.includes(date.getDay()) && start_hour == date.getHours() && start_minute == date.getMinutes() && !finish)
{
console.log("已達啟動時間,開始各站灑水。");
valveOn.set(5);
delay(step);
valveOff.set(5);
delay(1000);
valveOn.set(6);
delay(step);
valveOff.set(6);
delay(1000);
valveOn.set(7);
delay(step);
valveOff.set(7);
delay(1000);
valveOn.set(8);
delay(step);
valveOff.set(8);
delay(1000);
finish = true;
count = count + 1;
}
if(finish)
{
finish = false;
console.log("第 "+count+" 次排程結束,現在時間為:\n" + date);
}
```
### 每週二、四、六的上午(6)、中午(12)、晚上(18)各啟動一次排程,排程中依據設定的站別時間啟動(單位為秒)
```javascript=
window.count = 0;
window.finish = false;
let date = new Date(Date.now());
let start_week = [2,4,6];
let schedules = [[12,0,5,5,5,5],[6,0,5,5,0,0],[18,0,0,5,0,5]];
if(start_week.includes(date.getDay()) && schedules.some((s)=>s[0] == date.getHours() && s[1] == date.getMinutes()) && !finish)
{
let sche = schedules.filter((s)=>s[0] == date.getHours() && s[1] == date.getMinutes());
console.log("已達啟動時間,開始執行排程:\n"+ sche);
sche = sche[0];
if(sche[2] > 0 )
{
valveOn.set(5);
delay(sche[2] * 1000);
valveOff.set(5);
delay(1000);
}
if(sche[3] > 0 )
{
valveOn.set(6);
delay(sche[3] * 1000);
valveOff.set(6);
delay(1000);
}
if(sche[4] > 0 )
{
valveOn.set(7);
delay(sche[4] * 1000);
valveOff.set(7);
delay(1000);
}
if(sche[5] > 0 )
{
valveOn.set(8);
delay(sche[5] * 1000);
valveOff.set(8);
delay(1000);
}
finish = true;
count = count + 1;
}
if(finish)
{
finish = false;
console.log("第 "+count+" 次排程結束,現在時間為:\n" + date);
}
```
---
## 程式解說
逐行講解程式意義。
----
### 每隔10分鐘啟動一次排程,排程中每站輪流噴灑5秒
```javascript=
window.count = 0;
window.finish = false;
window.start = new Date("01/01/2022 12:00:00");
```
- 定義全域變數count,作為記錄啟動的次數。
- 定義全域變數finish,作為儲存是否完成排程的變數。
- 定義全域變數start,設定啟動的時間。
----
```javascript=
let date = new Date(Date.now());
let time = 600 * 1000;
let step = 5 * 1000;
```
- 定義區域變數date,設定為現在時間。
- 定義區域變數time,設為10分鐘(600秒)。
- 定義區域變數step,設為5秒鐘。
----
```javascript=
if(date.getTime() >= start.getTime() && !finish)
{
console.log("已達啟動時間,開始各站灑水。");
valveOn.set(5);
delay(step);
valveOff.set(5);
delay(1000);
valveOn.set(6);
delay(step);
valveOff.set(6);
delay(1000);
valveOn.set(7);
delay(step);
valveOff.set(7);
delay(1000);
valveOn.set(8);
delay(step);
valveOff.set(8);
delay(1000);
finish = true;
count = count + 1;
}
```
- 當現在時間==大於等於==啟動時間且==finish為false==時,輪流啟動電磁閥,每站==間隔時間==為設定的==step==,結束控制後將==finish設為true==表示此次==排程完成==,count 增加一次。
----
```javascript=
if(finish)
{
finish = false;
date = new Date(Date.now());
start = new Date(date.getTime() + time);
console.log("第 "+count+" 次排程結束,下次啟動時間為:\n" + start);
}
```
- 當finish為true時,將finish設為false,重新取的目前時間,將start啟動時間設為date時間加上設定的time,顯示排程啟動次數與下次啟動時間。
----
### 每週一、三、五、日的中午12點啟動一次排程,排程中每站輪流噴灑5秒
```javascript=
let start_week = [1,3,5,0];
let start_hour = 12;
let start_minute = 0;
```
- 定義三種區域變數,分別代表==啟動的星期,小時與分鐘==。
----
```javascript=
if(start_week.includes(date.getDay()) && start_hour == date.getHours() && start_minute == date.getMinutes() && !finish)
{
console.log("已達啟動時間,開始各站灑水。");
valveOn.set(5);
delay(step);
valveOff.set(5);
delay(1000);
valveOn.set(6);
delay(step);
valveOff.set(6);
delay(1000);
valveOn.set(7);
delay(step);
valveOff.set(7);
delay(1000);
valveOn.set(8);
delay(step);
valveOff.set(8);
delay(1000);
finish = true;
count = count + 1;
}
```
- 與第一種控制模式判斷方式不同,先判斷設定的==啟動星期有無包含現在時間的星期==,再判斷設定的==啟動小時與分鐘是否等於現在時間的小時與分鐘==。
----
### 每週二、四、六的上午(6)、中午(12)、晚上(18)各啟動一次排程,排程中依據設定的站別時間啟動(單位為秒)
```javascript=
let start_week = [2,4,6];
let schedules = [[12,0,5,5,5,5],[6,0,5,5,0,0],[18,0,0,5,0,5]];
```
- 定義schedules陣列,用來儲存不同的排程,每一個排程的格式為 ==[時,分,一站時間,二站時間,三站時間,四站時間]==。
----
```javascript=
if(start_week.includes(date.getDay()) && schedules.some((s)=>s[0] == date.getHours() && s[1] == date.getMinutes()) && !finish)
{
let sche = schedules.filter((s)=>s[0] == date.getHours() && s[1] == date.getMinutes());
console.log("已達啟動時間,開始執行排程:\n"+ sche);
sche = sche[0];
if(sche[2] > 0 )
{
valveOn.set(5);
delay(sche[2] * 1000);
valveOff.set(5);
delay(1000);
}
if(sche[3] > 0 )
{
valveOn.set(6);
delay(sche[3] * 1000);
valveOff.set(6);
delay(1000);
}
if(sche[4] > 0 )
{
valveOn.set(7);
delay(sche[4] * 1000);
valveOff.set(7);
delay(1000);
}
if(sche[5] > 0 )
{
valveOn.set(8);
delay(sche[5] * 1000);
valveOff.set(8);
delay(1000);
}
finish = true;
count = count + 1;
}
```
- 判斷方式與前一種相似,不同的是要==取出schedules中的各個陣列去判斷有無符合現在時間==。
- 當判斷為真時,利用==filter=這個方法取出目前使用的排程。
- ==sche = sche[0];== 由於上面filter回傳二維陣列,所以重新設定sche取得一維陣列。
- 對sche中每一站設定時間進行判斷,若大於0則執行設定的時間。
---
## 參數修改
為方便實作,以下會將範例程式中可修改的參數標示出來,進行實作時只需修改對應參數,並觀察結果即可。
:::warning
:zap: 詳細內建JS參數參考 - [內建Js參數及功能總覽](/s/1TmPhQPFRyKsDRf5d3xyzg)
:::
----
:::success
**window.start = new Date("01/01/2022 12:00:00");**
:::
- 在第一種控制模式中,可以設定啟動的時間,格式為 "mm/dd/yy hr:min:sec" (24小時制)。
----
:::success
**let time = ==x==;
let step = ==y==;**
:::
- x為每次排程的間隔時間,單位為毫秒。
- y為每站噴灑的間隔時間,單位為毫秒。
----
:::success
**let start_week = ==x==;
let start_hour = ==y==;
let start_minute = ==z==;**
:::
- x為啟動排程的星期陣列,輸入 ==[1,3,5,0]== 表示==星期一、三、五、日==。
- y為啟動排程的小時。
- z為啟動排程的分鐘。
----
:::success
**let schedules = ==x==;**
:::
- x為排程陣列,格式為 ==[[時,分,一站時間,二站時間,三站時間,四站時間],[時,分,一站時間,二站時間,三站時間,四站時間],[時,分,一站時間,二站時間,三站時間,四站時間]]== ,可以新增多個排程。
---
## 範例影片
{%youtube lAZ4OPx6pa8 %}
----
{%youtube 3_EmkEiwbNg %}
----
{%youtube cU23GQbFqpQ %}
<a class="btn btn-warning" style="width:100%;color:#333333;" href="/s/plbQC05nQROOJRu_QgnIfw" role="button"> Line訊息異常通報 **⇨** </a>
<a class="btn btn-primary" style="width:100%;" href="/s/nSWrH8KxTpualN8Ws8kkwA" role="button"> **⇦** 裝置互聯設定與遠程控制
</a>