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

PLCと通信テスト

JavaのServletでPLCと通信を行います。三菱電機製PLCはSLMP (SeamLess Message Protocol) で通信できます。 SLMPはCC-Link協会が管理しています。 SLMPは物理層にシリアル通信やTCP/IPなどを使用できます。今回はTCP/IPを使って通信します。


PLCの設定

PLCの「Ethernetポート」パラメータで次の項目を設定します。

Ethernetポートパラメータ

IPアドレスとサブネットマスク

IPアドレスとサブネットマスクは使用するPLCに合わせます。

自ノード設定

相手機器接続構成設定

「相手機器接続構成設定」で「SLMP接続機器」を追加します。 プロトコルとPLC側のポート番号の指定が必要です。プロトコルにUDPを選択したときは、接続してくる機器のIPアドレスの設定が必要です。

次の図はNo.1にTCP、No.2にUDPを設定しています。

相手機器接続構成設定

注記

2人以上でアクセスするときは2台以上追加します。同じプロトコルを使うときは、ポート番号は違う値を設定します。 GX Worksで2名以上で接続するときは、「MELSOFT接続機器」も追加してください。

パラメータを設定したらプロジェクトをPLCに書き込んでリセットします。

ウォッチ機能を使用して、データレジスタD100とD101を監視します。

ウォッチ


開発環境の構築

Linuxに開発ツールをインストールします。

  • JDK21: Java Development Kit Version 21を使います。Version 21はLTS (Long Term Support) に指定されているバージョンで、長期間のサポートが約束されています。
  • MAVEN: プロジェクト管理ツールです。ライブラリの依存関係などを設定ファイル (pom.xml) で管理します。
  • Tomcat 10: Servletコンテナです。JavaのServletを使用するときに必よです。Tomcat以外に様々なコンテナが提供されています。アプリケーションサーバと呼ばれることもあります。

インストール方法は「サーバサイド編」を確認してください。

注記

Windowsで開発して、成果物 (WARファイル) たけをTomcatにデプロイすることもできます。今回は、Linuxで開発します。

Mavenプロジェクトの作成

Ubuntuのコマンドラインで、Mavenコマンドを使用してプロジェクトを作成します。

備考

次のコマンドは1行で入力します。

mvn archetype:generate -DgroupId=com.ample -DartifactId=plc-demo 
-DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

PLC接続用のライブラリの準備

SLMPライブラリをコピーします。Javaで使えるSLMPライブラリはCC-Link協会で提供されていません。OSS (Open Source Software) やフリーソフトで提供されているものが少ないため、ポリテクセンターで作成したライブラリを使います。

SLMPライブラリ(SlmpFrame-1.0.0.jar)を、WSL2のUbuntuのホームディレクトリにコピーします。

ヒント

ダウンロードしたファイル名が異なるときは、ファイル名をSlmpFrame-1.0.0.jarに変更してください。

<user name>は使用している環境に合わせてください。

wsl cp /mnt/c/users/<user name>/Desktop/SlmpFrame-1.0.0.jar /home/ubuntu/
注記

WSL2でインストールしたUbuntuは、\\wsl.localhost\UbuntuでWindowsのエクスプローラーからアクセスできます。wslコマンドを使わずに、コピーすることができます。

Mavenは、ライブラリを~/.m2/repository/に保存します。~/.m2/repository/にないときはインターネットのMVNリポジトリを検索します。 使用するSLMPライブラリはインターネットに公開されていないので、ローカルのMavenリポジトリにインストールします。

備考

次のコマンドは1行で入力します。

cd ~
mvn install:install-file -Dfile=./SlmpFrame-1.0.0.jar -DgroupId=plc.slmp
-DartifactId=SlmpFrame -Dversion=1.0.0 -Dpackaging=jar

正常にインストールされると、次のメッセージが表示されます。

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< org.apache.maven:standalone-pom >-------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] --- maven-install-plugin:2.4:install-file (default-cli) @ standalone-pom ---
[INFO] Installing /home/ubuntu/SlmpFrame-1.0.0.jar to /home/ubuntu/.m2/repository/plc/slmp/SlmpFrame/1.0.0/SlmpFrame-1.0.0.jar
[INFO] Installing /tmp/mvninstall7206990925643660675.pom to /home/ubuntu/.m2/repository/plc/slmp/SlmpFrame/1.0.0/SlmpFrame-1.0.0.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.260 s
[INFO] Finished at: 2025-11-22T11:10:11+09:00
[INFO] ------------------------------------------------------------------------

プロジェクトの依存関係

Mavenを使うときは、プロジェクトルートのpom.xmlファイルに依存関係を設定します。

WindowsのVS Codeを起動します。「Remote Development拡張機能」でWSL2のUbuntuに接続します。

pom.xmlを修正しして、依存関係を追加します。

  • サーブレット: Tomcat 10を使うので、(javaxではく) Jakarta.Servletを使用します。
  • JSONライブラリ: jacksonの依存関係を追加します。Java SE (Standard Edition) はJSONをサポートしていません。Java EE (Enterprise Edition) はサポートしていますが、導入が容易で実績の多いOSSのjacksonを使います。
  • SLMPライブラリ: ポリテクセンターが作成したSLMPライブラリを使用します。plc.slmpの依存関係を追加します。ローカルのMavenリポジトリにSlmpFrameライブラリが保存されていないとエラーになります。
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>plc-demo</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>plc-demo Maven Webapp</name>
<url>http://maven.apache.org</url>
<!-- JDK21 -->
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<!-- Servletの依存関係 -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>

<!-- ユニットテスト(JUnit5) の依存関係 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>

<!-- SLMPライブラリの依存関係 -->
<!-- Mavenのローカルリポジトリに灯篭しておくこと -->
<dependency>
<groupId>plc.slmp</groupId>
<artifactId>SlmpFrame</artifactId>
<version>1.0.0</version>
</dependency>

<!-- JSON用のライブラリの依存関係 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.0</version>
</dependency>

</dependencies>
<build>
<finalName>plc-demo</finalName>
<plugins>
<!-- コンパイラプラグイン(JDK21指定) -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>21</source>
<target>21</target>
</configuration>
</plugin>

<!-- WARプラグイン -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.4.0</version>
</plugin>

</plugins>
</build>
</project>

プログラムの作成

ブラウザでアクセスしたときに表示されるindex.html、サーブレットのファイルwritePLCServlet.javaとSLMPライブラリにアクセスするSlmpService.javaを作成します。

ヒント

生成AIを使用して作成しています。

ホームページの作成

D100とD101に値を書き込むホームページを作成します。HTMLのフォームを使用していますが、JavaScriptを使用して、データをJSONで送信 (POST) します。

src/main/webapp/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>PLCデータ書き込み</title>
<style>
table { border-collapse: collapse; margin-top: 20px; }
th, td { border: 1px solid #ccc; padding: 8px; }
</style>
</head>
<body>
<h1>D100とD101の値を入力してください</h1>
<form id="plcForm">
<label for="d100">D100:</label>
<input type="number" id="d100" name="d100" required><br><br>
<label for="d101">D101:</label>
<input type="number" id="d101" name="d101" required><br><br>
<button type="submit">送信</button>
</form>

<table id="resultTable">
<thead>
<tr><th>D100</th><th>D101</th><th>Status</th></tr>
</thead>
<tbody></tbody>
</table>

<script>
document.getElementById('plcForm').addEventListener('submit', function(event) {
event.preventDefault(); // submitが実行されないようにする
const d100 = document.getElementById('d100').value;
const d101 = document.getElementById('d101').value;

fetch('writePLC', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ d100: d100, d101: d101 })
})
.then(response => response.json())
.then(data => {
const tbody = document.querySelector('#resultTable tbody');
const row = document.createElement('tr'); // 行を作成
row.innerHTML = `<td>${data.d100}</td><td>${data.d101}</td><td>${data.status}</td>`;
tbody.appendChild(row); // 行を追加
})
.catch(error => console.error('Error:', error));
});
</script>
</body>
</html>

サーブレットの作成

サーブレットのWritePLCServlet.javaと、PLC用ライブラリを使用するSlmpService.javaを追加します。

src/main/java/com/example/SlmpService.java
package com.example;

import java.util.ArrayList;
import java.util.List;

import plc.slmp.client.SlmpClient;
import plc.slmp.client.body.BodyHandlers;
import plc.slmp.client.response.SlmpResponse;
import plc.slmp.slmpframe.Device;
import plc.slmp.slmpframe.DeviceAddress;
import plc.slmp.slmpframe.SlmpDeviceType;
import plc.slmp.slmpframe.SlmpFactory;
import plc.slmp.slmpframe.SlmpFrameType;
import plc.slmp.slmpframe.data.PlcData;
import plc.slmp.slmpframe.data.WordData;
import plc.slmp.slmpframe.request.WriteRequest;

public class SlmpService {
private final String host;
private final int port;

public SlmpService(String host, int port) {
this.host = host;
this.port = port;
}

public boolean writeWords(int startAddress, int[] values) throws Exception {
// SLMPライブラリの SlmpClientを使います。
try (SlmpClient client = SlmpClient.builder()
.udp(host, port)
.timeout(5000)
.factory(SlmpFactory.of(SlmpFrameType.TYPE_3E)) // SLMP 3Eフレームを使います。
.build()) {

// データレジスタ (D) を生成します。
Device device = new Device(SlmpDeviceType.D, new DeviceAddress(startAddress));
List<PlcData> writeData = new ArrayList<>();
for (int value: values) {
writeData.add(new WordData(value));
}
// 一括書込み要求を作成します。
WriteRequest writeRequest = WriteRequest.createWordWrite(device).writeAll(writeData);

// PLCに書き込みます。
SlmpResponse<Void> response = client.write(writeRequest, BodyHandlers.ofDiscarding());
return response.statusCode() == 0;
}
}
}
備考

SLMPライブラリの詳細はSLMPユーザーズマニュアルを参照してください。

サーブレットを準備します。/writePLCにアクセスすると起動します。

src/main/java/com/example/WritePLCServlet.java
package com.example;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@WebServlet("/writePLC") // 公開ポイント
public class WritePLCServlet extends HttpServlet {
private final ObjectMapper mapper = new ObjectMapper(); // JSON用のORM (Object Relation Mapper)

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Map<String, Object> result = new HashMap<>();
try {
// HTTP陸セストからJSONリクエストを読み込み
Map<String, Object> input = mapper.readValue(request.getInputStream(), Map.class);
// 値の取得
int d100 = Integer.parseInt(input.get("d100").toString());
int d101 = Integer.parseInt(input.get("d101").toString());
System.out.println("D100 = " + d100); // デバッグ用
System.out.println("D101 = " + d101); // デバッグ用

// PLC書き込み (★アドレスとポート番号が固定されています!)
SlmpService plcService = new SlmpService("192.168.4.100", 8000);
// SlmpServiceオブジェクトにPLCの書き込みを依頼
boolean success = plcService.writeWords(100, new int[]{d100, d101});
System.out.println("Result: " + success); // デバッグ用

// HTTPの応答データの準備
result.put("d100", d100);
result.put("d101", d101);
result.put("status", success ? "SUCCESS" : "FAIL");

} catch (Exception e) {
result.put("status", "ERROR");
result.put("message", e.getMessage());
}

response.setContentType("application/json; charset=UTF-8");
mapper.writeValue(response.getOutputStream(), result); // JSONに変換して応答を返す
}
}

ビルド

MavenのPackageを実行してビルドします。ビルドに成功するとtargetフォルダにplc-demo.warファイルが生成されます。

MavenのPackage

ヒント

WARフィルの書き込みでエラーが発生するときは、Mavenのcleanコマンドを実行した後に、packageコマンドを実行してください。

デプロイ

作業するターミナルと別のターミナルを起動してください。 次のコマンドを入力して、Tomcatのログを常時確認します。

sudo tail -f /var/log/tomcat10/catalina.out

生成されたWARファイルをTomcatの公開フォルダにコピーします。

sudo cp target/plc-demo.war /var/lib/tomcat10/webapps/

数秒後に、catalina.outファイルにデプロイ結果が表示されます。

[2025-11-22 11:13:56] [info] Deploying web application archive [/var/lib/tomcat10/webapps/plc-demo.war]
[2025-11-22 11:13:57] [info] At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
[2025-11-22 11:13:57] [info] Deployment of web application archive [/var/lib/tomcat10/webapps/plc-demo.war] has finished in [831] ms

動作確認

ブラウザを起動してhttp://localhost:8080/plc-demo/にアクセスします。 D100とD101に書き込みたい値を入力して「送信」をクリックします。 書き込みが正常に終了すると、画面下部に結果が表示されます。

D100とD101に書き込み(正常)

プログラムが動作しないときは、ブラウザの開発者ツールや、Tomcatのログファイルを確認します。ファイル名の日付の個所は、実行したときの日付です。

less /var/log/tomcat10/localhost.2025-11-22.log

課題

課題1 🤔

今回のプロジェクトは書き込み機能をサポートしています。SLMPライブラリのマニュアルを参考に、読み出し機能を追加します。読出しボタンを押すと、PLCから指定したデバイスのデータを読み出します。

課題2 🤔

先ほどのプログラムを修正して、読み出し周期を指定します。開始ボタンを押すと取得を開始します。停止ボタンを押すと読み出しを停止します。

課題3 🤔🤔

読み出したデータをデータベースに保存してください。データベースは研修で使用したリレーショナルデータベース(RDB)を使用します。データにタイムスタンプを付けて保存してください。


発展課題

さらに新しい技術を勉強したい方向けです。

課題4 🤔🤔🤔

リレーショナルデータベース(MySQLなど)は構造化されたデータ(正規化)に適しています。ログなどの時系列データは別の方式のデータベースが適している場合があります。時系列のデータに適したデータベースを調査してください。時系列データベースにデータを保存してください。

課題5 🤔🤔🤔

アプリケーションはコンテナ化してデプロイする方式が増えています。コンテナ技術Dockerなどを調べてください。Tomcatやデータベースの公式イメージの利用法や、作成したアプリをコンテナ化する方法などを学習してください。