Hosan开源项目是液压、电力等行业的仿真开源软件,由瑞典林平大学开发,可以仿真电力、液压等。更具体的了解,请参考:《Hopsan -- 液压、电力等行业的仿真开源软件》

      Hosan开源项目编译参考:《Hopsan完全编译构建指南》《Hopsan的源码编译》

代码梳理、流程分析如下:

(1):HopsanEssentials的构造函数

在HopsanGUI/CoreAccess.cpp文件的顶部声明了一个hopsan::HopsanEssentials类型的全局变量gHopsanCore,本工程启动时,首先就会调用HopsanEssentials的构造函数,流程如下:

  1. 创建NodeFactory对象mpNodeFactory。
  2. 创建ComponentFactory对象mpComponentFactory。
  3. 如果是第一次调用构造函数,则创建QuantityRegister类对象mpQuantityRegister。在该类中,构造测试量和其单位的映射,如:速度为键,速度的单位m/s。
  4. 构造LoadExternal类对象mpExternalLoader,以便加载外部dll中包含的元件。
  5. 然后以步骤1创建的对象mpNodeFactory为参数,调用register_default_nodes函数,该函数返回后,mpNodeFactory就包含了系统所有默认的节点。
  6. 调用步骤2的mpComponentFactory对象中的registerCreatorFunction函数。注意:这个函数的第二个参数是一个函数指针,该函数指针是以类模板实现的,能支持任何返回值类型且无参数的任何函数。每个被创建的对象所在的类都有一个静态的Creator()函数,在其中创建该类的对象,如下
  7. 做一些错误检测及清除错误状态的工作,如:看已经注册过的组件是否重复注册等。

(1.1)ClassFactory类

ClassFactory类是一个模板类,如下:

template <typename _Key, typename _Base, typename _Predicator = std::less<_Key> >
 class ClassFactory
{
}

说明:

  • _Base 是创出函数的返回值,即1节中6步骤中提到的registerCreatorFunction函数第2个参数表示的函数指针的返回值。
  • Key是区分创建组件的关键字,本项目一般是字符串类型,如:“NodeSignal”字符串表示NodeSignal类的组件、DummyComponent表示DummyComponent类的组件等等。
  • _Predicator是map的排序准则,默认是按第一个参数key小写排序。

NodeFactory、ComponentFactory都是ClassFactory模板类的实例,如下:

typedef ClassFactory<HString, Node> NodeFactory;
typedef ClassFactory<HString, Component> ComponentFactory;

(1.2)registerCreatorFunction 

ClassFactory模板类的registerCreatorFunction功能如下:

  • 将以key为键,创建组件的构造器也即函数指针为值的键值对, 插入名为mFactoryMap的map中,方便以后查找。
  • 将key为键,创建组件的构造器也即函数指针为值构成的pair插入语 mRegStatusVector,以便在构造函数提示信息,参见1节中的步骤7。

(1.3)类的继承关系

(2)执行流程 

(2.1)界面启动流程

  1. main函数被调用。
  2. 创建DesktopHandler对象,并调用该对象的setupPaths()函数安装一些目录。
  3. 创建Configuration对象,设置一些配置信息。
  4. 创建CopyStack对象。
  5. 创建GUIMessageHandler对象,用于处理应用程序的警告、错误、普通消息。
  6. 创建MainWindow对象,并调用其createContents函数创建各种面板、主界面等。
  7. 处理命令行参数。
  8. 闪屏提示。
  9. 调用MainWindow对象的initializeWorkspace函数,加载各种组件库等。
  10. 主程序、主界面启动。

流程如下:

(2.2) initializeWorkspace

流程如下:

  1. 以路径graphics/builtinCAF/hidden/builtin_hidden.xml为参数,InternalLib为第二个参数,Hidden为第三个参数调用LibraryHandler类loadLibrary,即加载隐藏的内部组件。
  2. 调用LibraryHandler类loadLibrary 函数加载

 ../componentLibraries/defaultLibrary/defaultComponentLibrary.xml配置文件中的组件。

    3 调用LibraryHandler类loadLibrary 函数加载如下目录下的组件:

../componentLibraries/autoLibs/

    4 调用LibraryHandler类loadLibrary 函数加载

 graphics/builtinCAF/visible/builtin_visible.xml配置文件中的组件。

    5 加载以前创建的外部库(在元件树)

    6 创建plot widget对象gpPlotWidget,并隐藏。

     7 创建FindWidget对象,并隐藏。

    8 处理命令行中参数包含.hmf的参数,认为此参数就是模型文件,并加载该模型到视图。

     9 调整元件树窗体部件、向闪屏界面发送加载组件完成信息。

(2.3)LibraryHandler的loadLibrary函数(第1个参数是表示路径的字符串)

流程如下:

(2.4) LibraryHandler类的loadLibrary函数(第1个参数是SharedComponentLibraryPtrT类型

  1. 读取第1参数即pLibrary的xml文件指向的信息,并绑定这些信息到pLibrary对象。
  2. 将pLibrary对象对象加入到类型为Qlist的mLoadedLibraries成员变量。
  3. 调用CoreLibraryAccess类的loadComponentLib函数加载pLibrary表示的库。
  4. 调用recompileLibrary函数编译该库。
  5. 再次调用CoreLibraryAccess类的loadComponentLib函数加载pLibrary表示的库。
  6. 如果pLibrary库的版本号小于0.3,则递归搜索pLibrary库目录下的xml文件且xml节点是hopsanobjectappearance的文件,并把该文件的绝对路径加入到pLibrary库的cafFiles
  7. 遍历pLibrary库的cafFiles,对每个遍历到的元素构建ModelObjectAppearance对象,也就是组件的外表对象,如:组件在树视图、设计视图上的名称、图标等。
  8. 如果步骤7)中的ModelObjectAppearance对象的typeName类型不是subsystem且不是ConditionalSubsystem,则去1.1节中的ClassFactory类中看这种类型是不是注册过,如果没注册或者其hmf文件不存在,则弹出加载这种类型的组件失败的错误。
  9. 构造ComponentLibraryEntry对象,并设置该对象的各种属性,并将该对象加入到mLibraryEntries,并告知闪屏界面,正在加载这个组件。
  10. 加载类型不为InternalLib的其它用户自定义的类型到配置文件。
  11. 发送contentsChanged信号,以便形成组件树视图。

(2.5)CoreLibraryAccess类的loadComponentLib

 流程如下:

(2.6)组件注册文件的生成

 子目录下的后缀名为.cci的文件内容如下:

pComponentFactory->registerCreatorFunction("SignalFirstOrderFilter",SignalFirstOrderFilter::Creator);

这些组件注册代码是由generateLibraryFiles.py通过python生成的

Logo

为开发者提供按需使用的算力基础设施。

更多推荐