Shimlinkブロックの自動生成


本スクリプトは、Excelファイルに記載されたブロック情報をもとに、Simulinkモデル内にブロックを自動生成するツールです。
手作業でブロックを配置する手間を削減し、統一されたフォーマットでブロックを作成できます。

仕様

  1. Excelファイルに「データストア名」と「ブロックタイプ(Read または Write)」を記載
  2. 記載されたブロックタイプに応じて、DataStoreWrite または DataStoreRead ブロックを生成
  3. 生成されたブロックを縦並びで配置(ReadブロックとWriteブロックは別の列で生成)

Excelファイルの形式

  • ファイル名: “generate_simulink_blocks.xlsx ※任意の名称でOK
  • シート名: Sheet1 ←ここは変更しない様に!
  • 列構成:
    • A列: 変数名
    • B列: ブロックタイプ(Read または Write
    • C列: データ型 ※Inportブロックのみ反映でき、入力した内容がそのまま反映されるため注意

  ※値の入力は、3行目から!!(1行目はヘッダー、2行目は空白)

変数名データタイプデータ型(Inportのみ)
  
DataStoreRead_Block01Read
DataStoreWrite_Block01Write
Goto_Block01Goto
From_Block01From
Inport_Block01Inportuint8
Outport_BlockOutport

MATLABスクリプト

MATLABのホームウィンドウから、「新規スクリプト」を作成し、下記のコードをコピペ

  • 6種のブロックに対応
  • DataStoreブロックのみ対応
function generate_simulink_blocks(filename, modelName)
    % generate_simulink_blocks
    % Excelファイルに基づいてSimulinkモデルに指定されたブロックを自動生成します。
    %
    % 使用方法:
    % generate_simulink_blocks('blocks.xlsx', 'your_model_name')
    %
    % 引数:
    % filename  - ブロック情報を記載したExcelファイル
    % modelName - 操作対象のSimulinkモデル名

    % ------------------------
    % 設定
    % ------------------------
    % ブロックサイズ設定(種類ごとに異なるサイズ)
    BLOCK_SIZES = struct(...
        'Inport', [30, 14], ...    % 幅30, 高さ14
        'Outport', [30, 14], ...   % 幅30, 高さ14
        'Goto', [250, 28], ...     % 幅250, 高さ28
        'From', [250, 28], ...     % 幅250, 高さ28
        'Read', [250, 30], ...     % 幅250, 高さ30
        'Write', [250, 30] ...     % 幅250, 高さ30
    );

    % 基準座標の設定
    positions = struct(...
        'Inport', [75, 100], ...
        'Outport', [215, 100], ...
        'Goto', [400, 100], ...
        'From', [700, 100], ...
        'Read', [-600, 400], ...
        'Write', [-300, 400] ...
    );
    yOffset = 35;  % 縦方向の間隔

    % ------------------------
    % Excelファイルからデータを読み込む
    % ------------------------
    data = readtable(filename, 'Sheet', 'Sheet1', 'ReadRowNames', false);

    % ヘッダー行をスキップして2行目以降のデータを取得
    data = data(2:end, :);

    % 空白行や欠損値を削除
    data = data(~any(ismissing(data), 2), :);

    % Simulinkモデルを開く
    load_system(modelName);

    % ------------------------
    % ブロック生成
    % ------------------------
    blockCounts = struct('Inport', 0, 'Outport', 0, 'Goto', 0, 'From', 0, 'Read', 0, 'Write', 0);

    for i = 1:height(data)
        % 変数名とブロックタイプを取得
        blockName = data{i, 1}{1}; % 変数名称
        blockType = data{i, 2}{1}; % ブロックタイプ

        % 空白セルや不正なデータをスキップ
        if isempty(blockName) || isempty(blockType)
            continue;
        end

        % ブロックの基準座標とカウンタを取得
        if isfield(positions, blockType)
            basePosition = positions.(blockType);
            count = blockCounts.(blockType);

            % ブロックのサイズを取得(種類ごとに異なる)
            if isfield(BLOCK_SIZES, blockType)
                blockSize = BLOCK_SIZES.(blockType);
            else
                blockSize = [250, 30]; % デフォルトのサイズ
            end

            position = [
                basePosition(1), ...
                basePosition(2) + count * yOffset, ...
                basePosition(1) + blockSize(1), ...
                basePosition(2) + count * yOffset + blockSize(2)
            ];

            % Inportブロックの型指定用データを取得
            if strcmp(blockType, 'Inport') && width(data) >= 3
                dataType = data{i, 3}{1}; % C列のデータを取得
                if isempty(dataType)
                    dataType = 'Inherit: auto'; % デフォルトのデータ型を設定
                end
            else
                dataType = ''; % Inport以外は型指定しない
            end

            % ブロック生成
            switch blockType
                case 'Inport'
                    add_block('simulink/Sources/In1', ...
                        sprintf('%s/%s', modelName, blockName), ...
                        'Position', position, ...
                        'ShowName', 'on', ... % ブロック名を表示
                        'Port', num2str(count + 1), ... % ポート番号を設定
                        'OutDataTypeStr', dataType); % Inportのみ型指定
                case 'Outport'
                    add_block('simulink/Sinks/Out1', ...
                        sprintf('%s/%s', modelName, blockName), ...
                        'Position', position, ...
                        'ShowName', 'on', ... % ブロック名を表示
                        'Port', num2str(count + 1)); % ポート番号を設定
                case 'Goto'
                    add_block('simulink/Signal Routing/Goto', ...
                        sprintf('%s/Goto_%d', modelName, count + 1), ...
                        'Position', position, ...
                        'GotoTag', blockName, ...
                        'ShowName', 'off');
                case 'From'
                    add_block('simulink/Signal Routing/From', ...
                        sprintf('%s/From_%d', modelName, count + 1), ...
                        'Position', position, ...
                        'GotoTag', blockName, ...
                        'ShowName', 'off');
                case 'Read'
                    add_block('simulink/Signal Routing/Data Store Read', ...
                        sprintf('%s/DataStoreRead_%d', modelName, count + 1), ...
                        'Position', position, ...
                        'DataStoreName', blockName, ...
                        'ShowName', 'off');
                case 'Write'
                    add_block('simulink/Signal Routing/Data Store Write', ...
                        sprintf('%s/DataStoreWrite_%d', modelName, count + 1), ...
                        'Position', position, ...
                        'DataStoreName', blockName, ...
                        'ShowName', 'off');
                otherwise
                    warning('不明なブロックタイプ: %s (行 %d)', blockType, i);
            end

            % カウンタを更新
            blockCounts.(blockType) = blockCounts.(blockType) + 1;
        else
            warning('無効なブロックタイプ: %s', blockType);
        end
    end

    % ------------------------
    % モデルを保存
    % ------------------------
    save_system(modelName);

    % ------------------------
    % 総数とタイプ別の数を表示
    % ------------------------
    totalCount = sum(struct2array(blockCounts)); % 総数

    fprintf('------------------------\n');
    fprintf('生成されたデータの総数\n');
    fprintf('総数: %4d\n', totalCount);
    fields = fieldnames(blockCounts);
    for k = 1:numel(fields)
        fprintf('%s ブロック数: %4d\n', fields{k}, blockCounts.(fields{k}));
    end
    fprintf('ブロックの生成が完了しました!\n');
end
function generate_simulink_blocks(filename, modelName)
    % generate_simulink_blocks
    % Excelファイルに基づいてSimulinkモデルにDataStoreWriteおよび
    % DataStoreReadブロックを自動生成します。
    %
    % 使用方法:
    % generate_simulink_blocks('datastore_blocks.xlsx', 'your_model_name')
    %
    % 引数:
    % filename  - データストア情報を記載したExcelファイル
    % modelName - 操作対象のSimulinkモデル名
    
    % ------------------------
    % 設定
    % ------------------------
    % ブロックサイズ設定
    BLOCK_WIDTH = 250;  % ブロックの幅
    BLOCK_HEIGHT = 30;  % ブロックの高さ

    % 基準座標の設定
    readBasePosition = [100, 100];  % Readブロックの基準位置 (x, y)
    writeBasePosition = [500, 100]; % Writeブロックの基準位置 (x, y)
    yOffset = 35;                   % 縦方向の間隔 (変更)

    % ------------------------
    % Excelファイルからデータを読み込む
    % ------------------------
    data = readtable(filename, 'Sheet', 'Sheet1', 'ReadRowNames', false);

    % ヘッダー行をスキップして2行目以降のデータを取得
    data = data(2:end, :);

    % 空白行や欠損値を削除
    data = data(~any(ismissing(data), 2), :);

    % Simulinkモデルを開く
    load_system(modelName);

    % ------------------------
    % ブロック生成
    % ------------------------
    writeCount = 0; % Writeブロックの数
    readCount = 0;  % Readブロックの数

    for i = 1:height(data)
        % データストア名とブロックタイプを取得
        datastoreName = data{i, 1}{1}; % データストア名
        blockType = data{i, 2}{1};     % ブロックタイプ (Read or Write)

        % 空白セルや不正なデータをスキップ
        if isempty(datastoreName) || isempty(blockType)
            continue;
        end

        % Readブロックを先に生成
        if strcmp(blockType, 'Read')
            position = [
                readBasePosition(1), ...
                readBasePosition(2) + readCount * yOffset, ...
                readBasePosition(1) + BLOCK_WIDTH, ...
                readBasePosition(2) + readCount * yOffset + BLOCK_HEIGHT
            ];
            blockName = sprintf('%s/DataStoreRead_%d', modelName, readCount + 1);
            add_block('simulink/Signal Routing/Data Store Read', blockName, ...
                      'Position', position, ...
                      'DataStoreName', datastoreName, ...
                      'ShowName', 'off'); % ブロック名を非表示
            readCount = readCount + 1; % Readカウンタを更新

        % 次にWriteブロックを生成
        elseif strcmp(blockType, 'Write')
            position = [
                writeBasePosition(1), ...
                writeBasePosition(2) + writeCount * yOffset, ...
                writeBasePosition(1) + BLOCK_WIDTH, ...
                writeBasePosition(2) + writeCount * yOffset + BLOCK_HEIGHT
            ];
            blockName = sprintf('%s/DataStoreWrite_%d', modelName, writeCount + 1);
            add_block('simulink/Signal Routing/Data Store Write', blockName, ...
                      'Position', position, ...
                      'DataStoreName', datastoreName, ...
                      'ShowName', 'off'); % ブロック名を非表示
            writeCount = writeCount + 1; % Writeカウンタを更新
        else
            warning('不明なブロックタイプ: %s (行 %d)', blockType, i);
        end
    end

    % ------------------------
    % モデルを保存
    % ------------------------
    save_system(modelName);

    % 総数とデータタイプごとの数を表示
    totalCount = writeCount + readCount; % 総数

    % 4桁で表示
    fprintf('------------------------\n');
    fprintf('生成されたデータの数\n');
    fprintf('総数: %4d\n', totalCount);   % 4桁表示
    fprintf('DataStoreRead  ブロック数: %4d\n', readCount);  % 4桁表示
    fprintf('DataStoreWrite ブロック数: %4d\n', writeCount); % 4桁表示
    fprintf('ブロックの生成が完了しました!\n');
end

使用方法

スクリプトを保存

  • 上記のコードをgenerate_datastore_blocks.mという名前で保存します
  • 保存先は、MATLABの作業ディレクトリにしてください
    ※それ以外でも問題ないが、わかりにくくなるため

Simulinkモデルを準備

  • モデルを作成または既存モデルを準備します
  • 例: my_model.slxとして保存

※良きせぬバグを防ぐため、新規モデルを作成し、そこに生成されたブロックをコピペで使用すること!

Excelファイルを準備

  • 仕様どおりにExcelファイル(例: datastore_blocks.xlsx)を作成し、データストア名とブロックタイプを記載します

スクリプトを実行

  • MATLABコマンドウィンドウで以下を入力します
    'datastore_blocks.xlsx‘と'my_model‘は適宜変更してください
generate_simulink_blocks('datastore_blocks.xlsx', 'my_model');

全て同一ディレクトリ内に保存してください!

結果を確認

  • コマンドウィンドウに生成されたデータの総数が表示され、エラーがないか確認
  • 指定したSimulinkモデルを開き、ブロックが生成されていることを確認

スクリプトの説明

  1. Excelデータの読み込み
    • readtableを使用してExcelファイルを読み込み、データストア名とブロックタイプを取得
    • 値の入力は、3行目から!!(1行目はヘッダー、2行目は空白)
  2. 座標計算
    • 各ブロックのPositionを基準に、yOffsetを加算して縦方向に並べる
    • ブロックの種類ごとに分けて生成
  3. ブロックの生成
    • ブロックタイプに応じてadd_blockを使用し、適切なブロックを追加
  4. エラー処理
    • 不明なブロックタイプは警告を表示

注意点

  • モデルの保存前にバックアップを取ることをお勧めします
  • Excelの列やシート名が異なる場合はコードを調整してください
  • your_model_nameを実際のモデル名に置き換えてください

6. よくある質問(FAQ)

Q1. 実行してもブロックが追加されません
  • Excelのシート名が Sheet1 になっているか確認してください。
  • Simulinkモデルが保存されているか確認してください。
  • コマンドを実行する際に、ExcelファイルとSimulinkモデルが同じフォルダにあるか確認してください。
Q2. 既存のブロックが削除されてしまうの?
  • いいえ、このスクリプトは 既存のブロックには影響を与えず、新規ブロックを追加 します。
    ただ、場合によっては不具合が発生する可能性があるので、空のモデルファイルを作って生成することを推奨します。
Q3. Inportの型が設定されていません
  • Excelの C列 に適切なデータ型を記入してください。(例: double, int8, uint16
  • 空欄の場合は double になります。
Q4. 追加のブロックタイプを増やしたい
  • スクリプトの BLOCK_SIZESpositions に新しいブロックタイプを追加してください。

動作確認済 バージョン

  • MATLAB Simulink 2018b(9.5.0.1586782)64-bit

タイトルとURLをコピーしました