サンプルプログラム/ソーラーパネルの出力を定期的に取得する

Last-modified: 2015-07-11 (土) 08:22:31

例えば、ローターの先にソーラーパネルとジャイロとコントロールシートを付けて、太陽にソーラーパネルを真っ直ぐ向ける発電施設を作ったとします。

pic1.jpg

ソーラーパネルの現在の出力はターミナル画面の右下で確認できますが、複数のパネルの出力を一斉に確認したい、あるいは合計出力を一目で確認したいという場合、プログラムを書けばそれも可能になります。

また、ソーラーパネルは太陽光が当たる限り常に発電し続けるブロックです。今の出力を知りたいときに、いちいちプログラムをRunするのは面倒ですよね。何も操作しなくても定期的にプログラムを実行してくれるなら、その方が楽です。

という訳で今回は、タイマーを使ってプログラムを定期的に自動実行する方法もあわせて解説します。

サンプルプログラム

用意するもの

pic2.jpg
  • Solar Panel……お好きな枚数
  • Programable Block
  • Timer Block
  • LCD Panel、Wide LCD Panel または Text Panelのどれか(以下、「パネル」と表記」)

動かし方

ブロックを設置したら、パネルのOwner設定を変更します(手順はHello,World!のページを参照して下さい)。

次に、Programmable blockのエディタを開き、以下のプログラムを入力します。

void Main(string argument){
  IMyTextPanel tpTarget = GridTerminalSystem.GetBlockWithName(argument) as IMyTextPanel;
  if(tpTarget != null) {
    string writeText = "";
    float totalOutput = 0.0f;
    var solarPanels = new List<IMyTerminalBlock> ();
    GridTerminalSystem.GetBlocksOfType<IMySolarPanel>(solarPanels);
    for (int i = 0; i < solarPanels.Count; i++) {
      string name = solarPanels[i].CustomName;
      IMyPowerProducer powerProducer = solarPanels[i] as IMyPowerProducer;
      if(powerProducer != null) {
        float output = powerProducer.MaxPowerOutput;
        writeText += name + " : " + (output * 1000) + "kW\r\n";
        totalOutput += output;
      }
    }
    writeText += "\r\nTotal output : " + (totalOutput * 1000) + "kW\r\n";
    tpTarget.WritePublicText(writeText, false);
  }
}

次に、Timer Blockを設定します。Delay」のスライダーをドラッグして適度な秒数にして、「Setup Trigger」ボタンを押します。

pic3.jpg

コックピットに座ってGキーを押したときと同様の画面が表示されます。
下のツールバーの1番目と2番目に、アクションを設定します。

  • 1番目 ブロック:Programmable Block アクション:Run
  • 2番目 ブロック:Timer Block アクション:Start
pic4.jpg

Programmable BlockのRunを設定したときに表示されるダイアログには、パネルの名前を入力します。

ターミナル画面に戻ったら、「Trigger Now」ボタンを押します。すると……

pic5.jpg

パネルにはソーラーパネルの出力が表示され、Delayで指定した時間が経つと自動的に更新されます。
(ラージブロックのソーラーパネルの最大出力は120kWなので、理想的な向きから少しずれているのが分かります)

pic6.jpg

試しに、ソーラーパネルを1枚破壊してみると……

pic7.jpg

破壊したパネルの出力が無くなったことが、パネルの出力にも反映されます。

プログラム解説

プログラムの概要

ステーション内のすべてのソーラーパネルの、名前と現在の最大出力を取得し表示、さらに出力の合計を計算して最後に表示するプログラムです。

このプログラムだけでは、実行したその瞬間の出力しか取得できません。Space Engineersでは、定期的にプログラムを実行する時にはTimer Blockを使います。
プログラムをRunした後に自分自身をStartするタイマーを動かせば、一定時間毎にプログラムが実行されるようになるわけです。

コード解説

2行目:パネルを取得

  IMyTextPanel tpTarget = GridTerminalSystem.GetBlockWithName(argument) as IMyTextPanel;

引数で指定した名前のパネルブロックを取得します。Hello,World!のプログラムと同じですね。
Hello,WorldではProgrammable Blockに引数を入力しましたが、タイマーで定期実行するプログラムの引数は、タイマーにアクション登録するときに入力します。
(上記の動かし方の「ダイアログにパネルの名前を入力」したところです)

3行目:エラー対策

  if(tpTarget != null) {

2行目でパネルブロックを取得できなかった(おそらく、名前が間違ってる)場合、以下のコードを実行しません。

4~5行目:変数の準備

    string writeText = "";
    float totalOutput = 0.0f;

writeTextは最終的にパネルに出力する文字列、totalOutputはソーラーパネルの出力の合計値です。

7~8行目:ソーラーパネルを取得

   var solarPanels = new List<IMyTerminalBlock> ();
   GridTerminalSystem.GetBlocksOfType<IMySolarPanel>(solarPanels);

複数のInterior Lightの設定変更でInterior Lightを取得したときと同じです。ソーラーパネルを表す種類は「IMySolarPanel」です。

10行目:繰り返しの開始

   for (int i = 0; i < solarPanels.Count; i++) {

ソーラーパネルの数だけ、11~17行目のコードを繰り返します。

11行目:ソーラーパネル1枚の名前を取得

     string name = solarPanels[i].CustomName;

i番目のソーラーパネルの名前(CustomName)を、変数nameに代入します。
(代入せず直接solarPanels[i].CustomNameから取得してもいいんですが、この方が後のコードが読みやすくなります)

12~14行目:ソーラーパネルを「発電ブロックである」とみなし、現在の最大出力を取得する

     IMyPowerProducer powerProducer = solarPanels[i] as IMyPowerProducer;
     if(powerProducer != null) {
       float output = powerProducer.MaxPowerOutput;

12行目はこのプログラムの一番重要なポイントです。これをしないと14行目のコードが動きません。

Space Engineersのシステムにおいて、ソーラーパネル(IMySolarPanel)は、「ブロック(IMyCubeBlock)」であり、「ターミナルに表示されるブロック(IMyTerminalBlock)」であり、「発電するブロック(IMyPowerProducer)」でもあります。
そして、「ブロックの名前(CustomName)」は「ターミナルに表示されるブロック」が持っている情報で、「現在の最大出力(MaxPowerOutput)」は「発電するブロック」が持っている情報です。

ここでもう一度7行目を見てみましょう。

   var solarPanels = new List<IMyTerminalBlock> ();

そう、変数solarPanelsは「ターミナルに表示されるブロック」のリストです。
このため、そのリストの1項目であるsolarPanels[i]のMaxPowerOutputを取得しようとしても、「『ターミナルに表示されるブロック』が『現在の最大出力』の情報を持ってるわけないじゃん」とエラーメッセージを返されてしまいます。
(プレイヤーからすれば「いやこれどう見ても発電ブロックでしょ?」と思うかもしれませんが、愚直なプログラムは「『ターミナルに表示されるブロック』って書いてあるし……」と動くのです)

ですので、12行目の「as IMyPowerProducer」の記述で、「このブロックを『発電するブロック』とみなしなさい」と指定してやる必要があるのです。

ちなみに13行目は、「間違ったブロックを『発電するブロックとみなしなさい』と指定してしまった場合」の対策です。
IMyPowerProduerでないブロックをas IMyPowerProducerとした場合、 変数powerProducerにはnullが入ります。

15行目:ソーラーパネル一枚分のテキスト出力を作る

       writeText += name + " : " + (output * 1000) + "kW\r\n";

変数writeTextの末尾に、"[ソーラーパネルの名前] : [ソーラーパネルの現在の最大出力]kW(改行)"という文字列を追記します。
outputを1000倍しているのは、MaxPowerOutputの単位がMWだからです。ターミナル右下の表示はkWなのにね。

16行目:最大出力の合計値を積算

        totalOutput += output;

そうそう、15行目ではさらっと流しましたが、「A += B」は「A = A + B」と同じ意味です。

19行目:最大出力のテキスト出力を作る

   writeText += "\r\nTotal output : " + (totalOutput * 1000) + "kW\r\n";

16行目で加算した合計値をここでテキスト出力に反映します。
writeTextに追記される文字列は、"(改行)Total output : [最大出力の合計値]kW(改行)"となります。

20行目:ここまで作ったテキスト出力をパネルに表示する

   tpTarget.WritePublicText(writeText, false);

このように、複雑な出力テキストは、その都度パネルに書き出すより文字列変数にまとめてから出力したほうが、ソースがすっきりします。