【Python＆STEP CADデータ】STEPCodeをPython3＆Windows環境で利用する（※注意）


先日に引き続き、Pythonを利用してSTEPCodeライブラリをインストールする方法を紹介する。
OSはWindows 10共通で行う。
(Linuxでの導入は別途。Linuxの方が数倍楽。）

STEPファイルについて知りたい人はこちらを見てくれ

https://qiita.com/belre/items/d5d8168f68f8cb062c06

# Python3ライブラリの経緯

**(情報：2021.10.7)**

元々Python2での対応であったが、**githubの有志の方[@Devonsparks](https://github.com/devonsparks/stepcode)によって、[Issue #407](https://github.com/stepcode/stepcode/issues/407)**が提示された。

その後、Pull Requestにより本人によってコードが変更されていて、概ねテストも完了しているようだが、
githubのPR Integration(**CI環境での統合テスト**)で作業が止まっているようだ。

改めて確認したところ、PR Integrationで使用しているサービス(AppVeyor)がWindowsでのcmakeビルドをやっているようだが、古い環境であるとのことで最新の状態では削除されて、ブランチ「check_pr_actions」の状態で、しばらく放置されている。
少なくとも私の環境では[@Devonsparks](https://github.com/devonsparks/stepcode)のリポジトリをクローンしても(おそらく)正常に動いているので、試しに使う分には問題ないはず。
このライブラリをPythonサーバーサイドで使うときとかは、注意して使う。

全体的にContributorが少なく、ネイティブなCの環境からゆっくりと移行しているので、最新のPython運用環境やCI環境と比べると着手が遅い。
(なので、盛り上がってくれると、その辺りがさらに良くなると期待できる！)

## STEPCodeの採用メリット

STEPCodeは昔からネイティブCで管理されており、**"STEPファイルを読み取れるライブラリでかつ制約なく商用利用出来る"意味で、独自のライブラリである**。

「**中・小規模のPythonのサービスでCADデータを取り扱える**」
ある意味、完全に差別化されたメリットである。
ただし、基本コードは**独自のスキーマで全て記述されていて、その変換コードはC++で記述されていることから**すごくレガシーでもある。
(Python3対応が遅れている現状からみてもご察しかもしれないが・・・）

## その他のSTEP CADライブラリ

### Open CASCADE

ちなみに、STEP CADデータが扱えるライブラリに**Open CASCADE**がある。
こちらはGPLであるなどの制約があったり、どうも未定義動作やテストで課題が出ているようだが、こちらの方がよく使われている印象。

https://qiita.com/_EnumHack/items/577b087c071e4e9cd3b3

もしかすると、コミュニティにまだ人がいてフリーでコード修正できるようなContributorがいるだけでも、STEPCodeはまだよいかもしれない・・・。。。。
(確認したところ、Open CASCADEの最新アップデートは2018年のようだ。）

### ST Developer

このほか、1991年に発案され今も管理されて続けている**ST Viewer (ST Developer)**と呼ばれるものがある。
ただしこちらは現在は無償ではなく(昔は無償だったんだけど)、ライセンス料を支払わなくてはいけない。

http://www.steptools.com/

https://oxon.hatenablog.com/entry/20100205/1265347204

このように、CADを扱えるライブラリの選択肢は非常に限られており、高額／有償の環境に頼るか、無償でチープで古臭い環境で頑張るかになる。
なので、こんな状況を少しでも変えて、少しでも布教して、色々な案件に応用出来たら・・・と思うばかりだ。

## STEPCodeで提供されているPythonコードの種類

Pythonでは、Parse処理のみ使用する場合と、スキーマを利用する場合と二つの手段がある。

https://github.com/stepcode/stepcode/wiki/python-generator

パース処理を利用する場合は、Pythonの固有コードのみで実装できる。非常に単純だ。

一方、AP203とかAP214とかのスキーマを定義する場合は、Generatorを使用して、pyモジュールを生成する必要があるようだ。
パース処理のみの場合はお手軽だが型の厳密性がないので、規格情報を使用する場合はこの過程を必ず挟む必要がある。

# インストール① (パース処理のみ)

## 動作環境

- Windows 10 Professional
 - バージョン20H2
- Python 3.7.8, 3.9.7で動作確認
- Visual Studio Code + Python Extensionを使用


## 1. Githubリポジトリからクローン

今回は[@Devonsparks](https://github.com/devonsparks/stepcode)のブランチpy3を使用する。
現状プルリクエストのレビュー中ですので、マージされたらSTEPCode本体のリポジトリからプルすると良い。

```
git clone https://github.com/devonsparks/stepcode
cd stepcode
git checkout py3
```

## 2. Python Build

pipで以下のライブラリをインストール

```py
python -m pip install wheel build
```

手順にもあるように、PyPAモジュールを使用するのと、インストール時はwheelファイルを使用する点に注意。
PyPIにはまだSTEPCodeはリポジトリとして挙がっていない。
(いつか使えるくらいのレベルになると、とてもうれしい)

https://github.com/devonsparks/stepcode/tree/py3/src/exp2python/python

次に、リポジトリのルートから**「.\src\exp2python\python」**に移動し、

```py
python -m build
```

を押すことで、wheelファイルがdistフォルダに生成される。
終了したらこんな感じ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281173/890e0c0d-b16f-9cf4-60a4-dd09cf15847f.png)

Wheelファイルが出来たら、

```
cd dist
python -m pip install SCL-0.6.1-py3-none-any.whl
```

とすることでSCL(STEPCode Library)がインストールされる。
なお、過去にPython2版SCLを入れている場合、SCLがアンインストールできないので、--ignore-installを追加してpip installする。

## 3. テスト

リポジトリのルートから**「.\src\exp2python\python\test」**に移動し

```
python test_parser.py
```

などとしてエラーが発生していなければ完了。

## 動作確認

C++で実施した機能と同じもの(単にデータ番号と型を並べるだけ)なら、規格の情報がいらないので以下の内容だけで十分実行出来る。
金型データに関しては以下のqiitaの記事を参照

https://qiita.com/belre/items/aeab3ba4b0a579927029#step%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB

```py:kana.py
from SCL.Part21 import Parser, SimpleEntity, ComplexEntity
import io

path="F:\\StepData\\BSP35B20-N-12.stp"
step_text = None

with open(path) as f:
	step_text = f.read()

parser = Parser()
model = parser.parse(step_text)

with open("log_out.txt", "w") as cout:
	for section in model.sections:
		for entity in section.entities:
			if type(entity) is SimpleEntity:
				cout.write("{0},{1}\n".format(entity.ref, entity.type_name))
			elif type(entity) is ComplexEntity:
				cout.write("{0} ComplexEntity\n".format(entity.ref))
				for list_parameter in entity.params:
					for parameter in list_parameter:
						cout.write("+ {0}\n".format(parameter.type_name))
```

**出力結果**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281173/53d7c5db-4e5e-0ae2-6039-07605b0a2c1c.png)



# インストール(②スキーマも使う)

## 何をするか

なんと、**規格ファイル(*.expファイル）をPythonで自動してくれる機能**があったりする。
これを使って、各規格データをpyコードで生成する。


## 動作環境

C++でビルド出来る環境が必要です。
追加部分は赤字で記す

- Windows 10 Professional
 - バージョン20H2
- Python 3.7.8, 3.9.7で動作確認
- Visual Studio Code + Python Extensionを使用
- <font color="red">cmake gui 3.19.0-rc2</font>
 - 現在の最新版は3.21.xなので若干動作が変わる可能性あり
- <font color="red">Visual Studio 2019 Professional</font>
- <font color="red">C++ 14</font>

## 1. cmake

リポジトリのルートディレクトリをcmakeでビルドする。
流れはVC++編と同じ

https://qiita.com/belre/items/aeab3ba4b0a579927029

入力を終えたらConfigureボタンを押す

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281173/6c8d3d1c-64eb-67b2-bf56-b80da21013e1.png)

Cmake Generatorで使用するIDEを選択する
この記事の作者はあまり詳しくないが、EclipseやKate, Sublimeなども指定できるようだ
終わったら「Finish」押す

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281173/4ac99b01-54b0-23d0-1dd3-83c22818123f.png)

終わったら「Generate」を押してプロジェクトを生成する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281173/22e8e703-f0ac-924a-3269-d265d3bc44ec.png)

## 2.cmakeしたソリューションを開いてビルドする

ターゲットディレクトリを開くと、直下に"SC.sln"というソリューションファイルが生成されるので、
これをVisual Studio 2019で開く。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281173/e6339b15-6290-a47a-2c55-4e23a590a458.png)

そうすると大量のC++のプロジェクトが連なって生成される。
そう、これを全てビルドするのだ。
メモリ16GB, CPU Core-i7-9700を搭載した私のPCでも1時間半ビルドに時間を要した。
それほど長い作業である。

Debugモードでビルドすると、ターゲットディレクトリに「build\bin」というフォルダが出来るはずだ。
この中にあるexp2python.exeを使用する。
とりあえず**環境設定を振っておくか、このディレクトリのパスをクリップボードにコピーして**、次の作業に移る

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281173/bcaa5351-15b4-ce97-82e3-d656bf14f376.png)



## 3. スキーマのスクリプトをpythonコード化

次に、リポジトリのルートディレクトリの直下に「data」というフォルダがあるので参照する。
そうすると、各規格の一覧がずらっと並んでいるのが分かるはず

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281173/2cef3dea-d6d4-4b97-3a2a-6d1a7ff7627d.png)

ここでは、例えば「ap203」を使用するものとする。
「ap203」を開いて、コマンドプロンプトで次のように書く。

```
exp2python ap203.exp
```

すると、ap203のフォルダの中に**config_control_design.py**というファイルが出来る。
これはC++の規格データからpythonコードに置き換えたものであるので、
これを後は、本体のPythonコードの近くにモジュールとして置いておけばよい。

## 動作確認

先ほどのkana.pyを少し変更して、
**定義されているEntity名がスキーマap203に存在しているならばTrueを返す**
という処理を追加しよう。

ただし、スキーマのEntity名は大文字になっているので、これは小文字に変換する。

```py:kana2.py
from config_control_design import *
from SCL.Part21 import Parser, SimpleEntity, ComplexEntity
import io

path="F:\\StepData\\BSP35B20-N-12.stp"
step_text = None

with open(path) as f:
    step_text = f.read()

parser = Parser()
model = parser.parse(step_text)

with open('log_out2.txt', 'w') as cout:
	for section in model.sections:
		for entity in section.entities:
			if type(entity) is SimpleEntity:
				cout.write("{0},{1},{2}\n".format(entity.ref, entity.type_name, entity.type_name.lower() in globals()))
			elif type(entity) is ComplexEntity:
				for list_parameter in entity.params:
					for parameter in list_parameter:
						cout.write("+ {0} {1}\n".format(parameter.type_name, parameter.type_name.lower() in globals()))
```

**実行結果**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281173/50f2c0ac-e826-58fe-6134-38d3abe7a3fb.png)

全数確認したが、Falseになっている項目はなく、全てのスキーマのEntityが定義されていることが判明した。

## ソースコード少し解説

ちなみに感覚的には、Pythonの方が扱いやすいと思う。
理由は、**クラス名を文字列から動的に名前付けする機能がPythonにはあるため、複数の規格が乱立するときにそれを一本化出来るためだ。**

恐らく、Pythonを普通に使ってて、めったに使わないこの機能をガシガシ使う必要がある。

https://qiita.com/shirakiya/items/0ef37987c8c81f1d1199

**このことを知って、なかなかすごいライブラリだなとは思った。**

上記のkana2.pyでいえば、

```py
cout.write("{0},{1},{2}\n".format(entity.ref, entity.type_name, entity.type_name.lower() in globals()))
```

特にここで言うglobals()とか（先ほどの記事ではgetattrとか）は、普通にプログラミングしている環境だと積極的に使うケースは少ない。
だが、このSTEPCodeではこれなしではやっていけないような気すらする。

**なぜこんなことが出来るか？**
それは、**先ほど見たap203.expにスキーマの定義全てが書いてあるからだ。**

そこで、一部抜粋すると

```ap203.exp(一部抜粋)
  ENTITY representation_map;
      mapping_origin        : representation_item;
      mapped_representation : representation;
    INVERSE
      map_usage : SET [1:?] OF mapped_item FOR mapping_source;
    WHERE
      wr1: item_in_context(SELF.mapping_origin,SELF.mapped_representation.
               context_of_items);
  END_ENTITY; -- representation_map

  ENTITY representation_relationship;
      name        : label;
      description : text;
      rep_1       : representation;
      rep_2       : representation;
  END_ENTITY; -- representation_relationship

  ENTITY representation_relationship_with_transformation
    SUBTYPE OF (representation_relationship);
      transformation_operator : transformation;
    WHERE
      wr1: (SELF\representation_relationship.rep_1.context_of_items :<>:
               SELF\representation_relationship.rep_2.context_of_items);
  END_ENTITY; -- representation_relationship_with_transformation
```

こんな感じで、**独自のSTEP用の言語仕様で**、要素(Entity)、継承関係、属性(Attribute)が記述されているのだ。
これをpythonに変換しているものだから、スキーマとして成立するのである。

このSTEPの独自仕様に関しては、今後取り扱うとして、
このSTEPCodeがどのようなライブラリなのか、若干想像がつくと思う。
独自仕様の**XML**などに似ている形式かもしれない。

つまり、**CADデータを一定のスキーマに基づいて管理するためのファイル＝STEPファイル**である。
そして、そのスキーマはISO10303の規格で取り決めされており、STLなどの形状定義フリーCADの形態と互換性を保っているのである。