【設備制御プログラム】三菱PLC　SLMP送信プログラム C#

こんにちは。テラロイドです。

最近、PLCと通信するソフトを作成しましたが、記事が少なかったため投稿します。
本記事では、三菱電機のPLCと通信するためのプロトコル「SLMP」もしくは「MCプロトコル」の伝文作成について書きました。

伝文は条件により若干変わりますので条件書いときます。
PLCの型式:Qシリーズ(iQシリーズや他のシリーズではコマンドが変わることがあります。)
コードはASCIIとバイナリが可能ですが、本記事はバイナリで伝文を作成しています。

伝文フォーマットは下記の様な構成になっていますが、要求データを作成してから他の項目を作成していくようにしています。
伝文フォーマット = ヘッダ + サブヘッダ + アクセス経路 + 応答データ長 + 監視タイマー + 要求データ

```C#:要求データ作成(一括読出し)
/// <summary>一括読出し(batch read)</summary>
/// <param name="NumberOfDevicePoints">読出しデバイス点数</param>
/// <returns></returns>
public override byte[] ReadInBulkInWordUnits(int NumberOfDevicePoints) 
{

IEnumerable<byte> createCommand;

//コマンド
createCommand = new byte[]{0x01,0x04};

//サブコマンド
createCommand = createCommand.Concat(new byte[]{0x00,0x00});

//先頭デバイス番号
createCommand = createCommand.Concat(new byte[]{0x00,0x00,0x00});

//デバイスコード
createCommand = createCommand.Concat(new byte[]{0xA8}); 

//デバイス点数
byte upperHex = (byte)(numberOfDevicePoints / 256);
byte lowerHex = (byte)(numberOfDevicePoints % 256);
createCommand = createCommand.Concat(new byte[] {lowerHex,upperHex});

return createSendMessage((byte[])createCommand.ToArray());
}
```
```C#:要求データ作成(一括書込み)
/// <summary>一括書込み(ワード単位)(batch write)</summary>
/// <param name="WriteData">書込みデータ</param>
/// <returns></returns>
public override byte[] WriteInBulkInWordUnits(byte[] WriteData)
{

IEnumerable<byte> createCommand;

//コマンド
createCommand = new byte[]{0x01,0x14};

//サブコマンド
createCommand = createCommand.Concat(new byte[]{0x00,0x00});

//先頭デバイス番号
createCommand = createCommand.Concat(new byte[]{0x00,0x00,0x00});

//デバイスコード
createCommand = createCommand.Concat(new byte[]{0xA8}); 

//デバイス点数
createCommand = createCommand.Concat(createNumberOfDevicePoints(WriteData.Length / 2));

//書込みデータ
createCommand = createCommand.Concat(WriteData);

return createSendMessage((byte[])createCommand.ToArray());

        }
```

```C#:要求伝文作成
protected override byte[] createSendMessage(byte[] command)
{

IEnumerable<byte> createMessage;

//要求伝文フォーマット
//伝文フォーマット = ヘッダ + サブヘッダ + アクセス経路 + 応答データ長 + 監視タイマー + 要求データ 
//①TCPヘッダーの為、不要
//②サブヘッダ―
////サブヘッダ― = 要求伝文

createMessage = new byte[]{0x50,0x00};

//③アクセス経路
//アクセス経路 = ネットワーク番号 + PC番号 + 要求先ユニットI/O番号 + 要求先ユニット局番号
createMessage = createMessage.Concat(new byte[]{0x00});
createMessage = createMessage.Concat(new byte[]{0xFF});
createMessage = createMessage.Concat(new byte[]{0xFF,0x03});
createMessage = createMessage.Concat(new byte[]{0x00});

//④データ長
byte upperHex = (byte)((2 + command.Length) / 256);
byte lowerHex = (byte)((2 + command.Length) % 256);

if ((upperHex / 256) >= 1) { throw new Exception("データ長に不正な値が設定されました。(桁数オーバー)"); }
createMessage = createMessage.Concat(new byte[]{ lowerHex });
createMessage = createMessage.Concat(new byte[]{ upperHex });

//⑤監視タイマー
byte upperHex2 = (byte)((monitoringTimer) / 256);
byte lowerHex2 = (byte)((monitoringTimer) % 256);

if ((upperHex2 / 256) >= 1) { throw new Exception("監視タイマーに不正な値が設定されました。(桁数オーバー)"); }
createMessage = createMessage.Concat(new byte[]{ upperHex2 });
createMessage = createMessage.Concat(new byte[]{ lowerHex2 });

//⑥要求データ
//要求データ = コマンド + サブコマンド + 先頭デバイス + 
createMessage = createMessage.Concat(command);

//ログ出力
string tmp = "";
string text = "";
foreach (byte b in (byte[])createMessage){text = string.Format("{0,3:X2}", b);tmp = tmp + text;}
log.OutputDebug("[S]" + tmp);

return (byte[])createMessage.ToArray();

}
```