メインコンテンツまでスキップ

一定周期で更新

プロジェクト名: 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.jshexToDecimalPadded関数は別のプロジェクトで使えるので、deviceRead.jsに移動します。

deviceRead.htmldeviceReadHtml.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)を使用します。

  • 引数: func delayミリ秒が経過するたびに実行する関数です。
  • 引数: 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; // 連打対策
});