一定周期で更新
プロジェクト名: project_read_period
読出しボタンを押したら、指定された周期でPLCからデータを読み出します。読み出したデータはHTMLに表示します。
仕様
- 更新周期を入力します。初期値は1秒とします。
- 開始ボタンを押すと、指定された周期でデータを取得してページを更新します。
- 停止ボタンを押すと、ページの更新を停止します。
HTMLの作成
「読み出し」ボタンの下に2つのボタンを追加します。
<button id="start">開始</button>
<button id="stop" disabled>停止</button>

JavaScriptの修正
プログラムが長くなると認知的負荷が増えます。
前の章 応答データをHTMLに表示 で作成したHTMLファイル (deviceRead.html) に記述されているscriptタグの内容を、新しいファイルdeviceReadHtml.jsに移動します。
deviceRead.htmlの<script src="deviceRead.js"></script>の下に次のタグを追加します。
<script src="deviceReadHtml.js"></script>
deviceReadHtml.jsのhexToDecimalPadded関数は別のプロジェクトで使えるので、deviceRead.jsに移動します。
deviceRead.htmlとdeviceReadHtml.jsをPLCに転送して動作を確認します。
ボタンの有効/無効の設定
- ページが表示されたときは、読み出しと開始ボタンだけ有効にします。
- 開始ボタンが押されると、開始ボタンと読み出しボタンは無効、停止ボタンは有効にします。
- 停止ボタンが押されると、停止ボタンは無効、読み出しボタンと開始ボタンを有効にします。
開始ボタンと停止ボタンのオブジェクトを取得します。読み出しボタンはdeviceReadHtml.jsで取得しているので不要です。
// const send = document.querySelector("#send"); // deviceReadHtml.jsで定義済み
const start = document.querySelector("#start");
const stop = document.querySelector("#stop");
「周期更新中」を示す変数を宣言します。
let period_state = false; // 周期更新開始フラグ
開始ボタンが押されたときの処理を追加します。
let starting = false; // 開始ボタンの連打対策
start.addEventListener("click", () => {
if (starting) return; // 連打対策
starting = true; // 開始中
start.disabled = true;
period_state = true; // 周期更新中
send.disabled = true; // 送信ボタンを無効
stop.disabled = false; // 停止ボタンを無効
});
停止ボタンが押されたときの処理を追加します。
let stopping = false; // 停止ボタンの連打対策
stop.addEventListener("click", () => {
if (stopping) return; // 連打対策
stopping = true; // 停止中
stop.disabled = true; // 停止ボタンを無効
period_state = false; // 周期更新停止
send.disabled = false; // 送信ボタンを有効
start.disabled = false; // 開始ボタンを有効
stopping.disabled = false; // 連打対策
});
deviceRead.htmlを転送して、動作を確認します。
更新処理の追加
開始ボタンが押されたらクエリパラメータをPLCに送信して、応答データでページを更新します。
JavaScriptで定周期処理を行う方法は複数あります。今回は、setInterval関数とclearInterval関数を使用します。
setInterval関数
setInterval(func, delay)を使用します。
- 引数:
funcdelayミリ秒が経過するたびに実行する関数です。 - 引数:
delay指定した関数を実行する前にタイマーが待つべき時間をミリ秒単位で指定します。 - 返値: インターバルタイマを一意に識別する正の整数を返します。
clearInterval関数に渡すことで、指定した関数の実行を停止できます。
clearInterval(intervalID)を使用します。
引数: intervalID 取り消したい繰り返し動作の識別子です。
HTMLファイルの修正
deviceRead.htmlファイルを修正します。
開始ボタンと停止ボタンで使用する変数を宣言します。
let period_state = false; // 周期更新開始フラグ
let timer = null; // タイマー
let inFlight = false; // 要求中フラグ
開始ボタンが押されたときのイベントリスナーを修正します。
/* 開始ボタンの処理 */
let starting = false; // 開始ボタンの連打対策
start.addEventListener("click", async () => {
if (starting) return; // 連打対策
starting = true; // 開始中
start.disabled = true;
// ★以下を追加
if (timer) clearInterval(timer); // タイマが動作中のときは停止
timer = setInterval(async () => {
if (inFlight) return; // 前回が完了していない
inFlight = true; // 開始中
try {
const params = createQueryParams(requestDevice);
const responseJson = await sendQueryParams(plcIPAddress, cgiPath, params);
if (!responseJson) {
console.warn("応答が受信できません。");
} else {
for (let i = 0; i < dataObjs.length; i++) {
const dataObj = dataObjs[i];
const responseData = hexToDecimalPadded(responseJson.DATA[i], 5);
dataObj.textContent = responseData;
}
}
} catch (error) {
console.error('ポーリング中エラー:', error);
} finally {
inFlight = false;
}
}, 1000);
period_state = true; // 周期更新中
// ★ここまで
send.disabled = true; // 送信ボタンを無効
stop.disabled = false; // 停止ボタンを無効
starting = false; // 開始中
});
停止ボタンが押されたときのイベントリスナーを修正します。
/* 停止ボタンの処理 */
let stopping = false; // 停止ボタンの連打対策
stop.addEventListener("click", () => {
if (stopping) return; // 連打対策
stopping = true; // 停止中
stop.disabled = true; // 停止ボタンを無効
// ★以下を追加
period_state = false; // 周期更新停止
if (timer) {
clearInterval(timer);
timer = null;
}
inFlight = false;
// ★ここまで
send.disabled = false; // 送信ボタンを有効
start.disabled = false; // 開始ボタンを有効
stopping = false; // 連打対策
});