外部からシミュレーションを開始する方法

中岡様,

3年も前のことですが,GitHubのissueに書いたことを最新版で再開したいと考えています.

当時は,

現状ではそのような機能はありませんが、プラグインを書けば可能だと思います。
Choreonoid側でシミュレータの操作を行うためのRTCを生成するようにして、それがactivateされたときにシミュレーションを開始するような実装にしておけばよろしいかと。

というような回答をいただきました.プログインを作りその中からシミュレーションを制御するコードを書いたのですが,うまく動作しませんでした.

現在の開発版でこの目的を達成するにはどうするのが近道でしょうか?
アドバイスをいただけると幸いです.

BodyIoRTCを継承した何もしないクラスを作り,

    SimulationBar *sb;
  sb = SimulationBar::instance();

としておいて,onActivated()の中で,

  sb->startSimulation();

することによって,このRTCのアクティベートでシミュレーションの開始ができました.しかし,シミュレーションの停止の方法がわかりません.SimulationBar::stopSimulation()の引数はどのようにセットすればいいのでしょうか?

Choreonoid側のソースコードを変更し,SimulationBarクラスの

    void onStopSimulationClicked();

をprivateからpublicへ移し,上記のBodyIoRTCを継承したクラスのonDeactivate()の中で,

  sb->onStopSimulationClicked();

することによって,このRTCのディアクティベートでシミュレーションの停止もできました.

こんなやり方でいいのでしょうか?

短いものですので,プログラム全体を以下に載せます.

/**
@author Yasuhiro Masutani
*/

#include <cnoid/BodyIoRTC>
#include <cnoid/MessageView>
#include <cnoid/SimulationBar>

using namespace std;
using namespace cnoid;

namespace {

  class SimulationStartStopIoRTC : public BodyIoRTC
  {
    MessageView *mv;
    SimulationBar *sb;

  public:
    SimulationStartStopIoRTC(RTC::Manager* manager);
    ~SimulationStartStopIoRTC();

    virtual RTC::ReturnCode_t onActivated(RTC::UniqueId ec_id) override;
    virtual RTC::ReturnCode_t onDeactivated(RTC::UniqueId ec_id) override;

  };

  const char* spec[] =
  {
    "implementation_id", "SimulationStartStopIoRTC",
    "type_name",         "SimulationStartStopIoRTC",
    "description",       "BodyIoRTC to start and stop simulation",
    "version",           "0.1",
    "vendor",            "MasutaniLab",
    "category",          "Gneric",
    "activity_type",     "DataFlowComponent",
    "max_instance",      "10",
    "language",          "C++",
    "lang_type",         "compile",
    ""
  };

}

SimulationStartStopIoRTC::SimulationStartStopIoRTC(RTC::Manager* manager)
  : BodyIoRTC(manager)
{
  mv = MessageView::instance();
  sb = SimulationBar::instance();
}


SimulationStartStopIoRTC::~SimulationStartStopIoRTC()
{

}

RTC::ReturnCode_t SimulationStartStopIoRTC::onActivated(RTC::UniqueId ec_id)
{
  mv->putln("SimulationStartStopIoRTC::onActivated()");
  sb->startSimulation();
  return RTC::RTC_OK;
}

RTC::ReturnCode_t SimulationStartStopIoRTC::onDeactivated(RTC::UniqueId ec_id)
{
  mv->putln("SimulationStartStopIoRTC::onDeactivated()");
  sb->onStopSimulationClicked();
  return RTC::RTC_OK;
}

extern "C"
{
  DLL_EXPORT void SimulationStartStopIoRTCInit(RTC::Manager* manager)
  {
    coil::Properties profile(spec);
    manager->registerFactory(
      profile, RTC::Create<SimulationStartStopIoRTC>, RTC::Delete<SimulationStartStopIoRTC>);
  }
};

この目的のためならば,BodyIoRTCでなく,普通のRTCでも良かろうと,RTC::DataFlowComponentBaseを直接継承するクラスで同じことをやってみました.シミュレーションの開始と停止はできているようなのですが,Choreonoidの中の時間表示やシーンビューが連動しません.何が足りないのでしょうか?

上述のBodyIoRTCを継承したクラスですが,Choreonoidの外部からRT System Editorなどでアクティベートすすると,シミュレーションは開始しますが,Choreonoidの中の時間表示やシーンビューが連動しません.Choreonoidの内部でアクティベートするのと外部からアクティベートする違いは何でしょうか?

@nakaoka 様,助けてください.

Choreonoid内でBodyIoRTCのモジュールとして仕込んでも挙動がおかしい場合があります.
おそらく方法が正しくないか,何か手続きが抜けているのだと思います.
色々試してみたのですが,行き詰ってしましました.

お騒がせしました.自己解決しました.

正しいやり方かどうかわかりませんが,以下のようにして目的を達成しました.

src/BodyPlugin/SimulationBar.h において,SimulationBarクラスにメンバ関数とメンバ変数を追加.

class CNOID_EXPORT SimulationBar : public ToolBar
{
public:
(中略)
    void clickStartButton();
    void clickStopButton();

private:
(中略)
    ToolButton* startButton;
    ToolButton* stopButton;
};

src/BodyPlugin/SimulationBar.cpp において,以下のコードを追加.

SimulationBar::SimulationBar()
    : ToolBar(N_("SimulationBar"))
{
(中略)
    startButton =
        addButton(QIcon(":/Body/icons/start-simulation.png"), _("Start simulation from the beginning"));
    startButton ->
        sigClicked().connect(
            std::bind(static_cast<void(SimulationBar::*)(bool)>(&SimulationBar::startSimulation), this, true));

    stopButton =
        addButton(QIcon(":/Body/icons/stop-simulation.png"), _("Stop simulation"));
    stopButton ->
        sigClicked().connect(std::bind(&SimulationBar::onStopSimulationClicked, this));
}
void SimulationBar::clickStartButton()
{
    startButton->click();
}

void SimulationBar::clickStopButton()
{
    stopButton->click();
}

そして,RTC::DataFlowComponentBaseを継承したクラスで,以下のようにコードを書きました.

RTC::ReturnCode_t SimulationStartStopRTC::onActivated(RTC::UniqueId ec_id)
{
  MessageView::instance()->putln("SimulationStartStopRTC::onActivated()");
  SimulationBar::instance()->clickStartButton(); //SimulationBarクラスを改変したChoreonoidが前提
  return RTC::RTC_OK;
}


RTC::ReturnCode_t SimulationStartStopRTC::onDeactivated(RTC::UniqueId ec_id)
{
  MessageView::instance()->putln("SimulationStartStopRTC::onDeactivated()");
  SimulationBar::instance()->clickStopButton(); //SimulationBarクラスを改変したChoreonoidが前提
  return RTC::RTC_OK;
}

これをビルドしたDLLファイルをChoreonoidのRTCアイテムのRTCモジュールに設定します.これで,外部からRTSEなどでこのRTCをアクティベート・ディアクティベートすることでシミュレーションが開始・停止し,時間表示やシーンビューも連動しています.

コメントが遅くなってしまいすみません。
解決されたとのこと、大変お疲れ様でした。

いちおうコメントさせていただくと、理想を言えば以下のようにした方がよろしいかと思います。

  1. シミュレーションの制御については、SimulationBarではなく、SimulatorItemを介して行うようにする
  2. 制御を行うRTCについては、BodyIoRTCではなく、SimulatorItemと関連付ける専用のアイテムを作ってその内部で生成する

Choreonoidでは同時に複数のWorldやSimulatorを管理できますが、上記のようにすることにより、それらを個別に制御することが可能となります。また、今回行いたいのはシミュレーションの制御なので、Bodyに紐付けるBodyIoRTCで制御を行うのは、設計とマッチしていないところがあります。

これを実装するにあたって参考にできそうなのは、BodyStateSubscribrerRTCItemでしょうか。そちらも紐つけているのはBodyアイテムになりますが、それをSimulatorItemに置き換えるような感じです。

また、シミュレーションとビューが連動しない件については、対象のSimulatorItemが選択状態になっていない可能性があります。上記のようにChoreonoidでは複数のシミュレーションを同時に管理できますが、タイムバーの処理と関連づくのは選択されているシミュレータアイテムとなります。

@nakaoka 様,

(連動の件で間違ったことを書いていしまいましたので,編集しました)

応答ありがとうございます.

最初はBodyIoRTCアイテムで試していましたが,最終的にはRTCアイテムにしました.

今回は,GUIのシミュレータ開始ボタンと停止ボタンを外部から操作したいと考えました.

連動の件,確かにアイテムビューでSimulatioinItem(AISTSimulator)にチェックが入っていませんでした.
チェックを入れて,onActivated()で

SimulationBar::instance()->startSimulation();

をするRTCモジュールを試しましたが,連動しませんでした.