CodeSys#RunTime中にPython Scriptを実行する

今回はCodeSysをインストールしたPi4のRunTimeでPython Scriptを実行する方法を書きたいと思います。

#ライブラリインストール
Python ScriptをPi4で自動実行するにはシステムのライブラリをインストール必要があります。
https://help.codesys.com/webapp/idx-SysProcess%20Implementation-lib;product=SysProcess%20Implementation;version=3.5.16.0

##SysFile
Fileを処理するライブラリ

##SysTypes
File、コマンド実行するときに使われてる構造体

##SysProcess
コマンド実行するライブラリ

#チエック
ではCodeSysの中にそれらのライブラリがあるかどうかをCheckします。
Library Repositoryクリックし、Findを押します。
SearchのところにSysFileを入力します。もしあったらすでにインストールされている状態。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/70e412eb-8f2b-f41c-3776-94d988f9d0aa.png)

Openを押すと“Installed libraries”がの中にSysFileとそのバージョンも表示されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/bbccf041-2ffe-0697-0327-e42dcdc76779.png)

##Import
インストールされたこと確認したら、次は自分のプロジェクトの中にImportすればOKです。Library Manager>Add Libraryをクリクします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/3076a4bf-143d-b771-a18d-de8b0ece1ebf.png)
Advanced..をクリクします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/e096375d-b1c7-1333-1089-fbfc8e7bb26d.png)
Add LibraryのPopupが出てきます。
Sysfileを入力し、SysFile,3.5.15.0を選んで、OKします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/14f2cc4b-a783-a853-b421-9d9cf8bf141f.png)

そうすると、自分のプロジェクトにはこのライブラリを使えるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/b4e1b6ed-a799-2734-b044-ec5232eb5172.png)
同じようにSysProcessを入れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/54183400-8f94-76c9-4bd3-dd387591360f.png)
SysTypesも。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/6af16a46-8c50-edd1-cb69-eb26cc6f892f.png)
たまにCodeSys再起動するとProject Environmentは最新じゃありませんよ～というメッセージが出てきますが、“Set All to Newest”でよいと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/c77bff94-afc3-30b4-600d-b361d0996364.png)

# External file
Python Scriptを追加するには“External file”というObjectを使うことになります。
Online Helpはこちらです：
https://help.codesys.com/webapp/_cds_obj_external_file;product=codesys;version=3.5.16.0

>An External File is any file that you add to the project in the POUs view or Devices view. Click Project ‣ Add Object to open the Add External File dialog and define how the file belongs to the project.
An external file which was inserted in the POUs view is never downloaded to the controller.
An external file which was added in the Devices view is always downloaded to the controller when an online change or a download is performed due to an IEC code change.
When an external file is downloaded to the controller, it is not updated in the project

External FileはあなたがPOUs ViewかDevices Viewに任意のFileを追加するできるObjectです。Project>Add ObjectするとExternal fileのPop-upが出て、その中にFileがどこのものなのかを定義します。

- もしExternal FileはPOUsの中に追加された場合は、Controllerにダウンロードさせません。
- もしExternal FileはDevicesの中に追加された場合、Online Change・IEC code ChangeするとそのFileがControllerにダウンロードされます。

そしてExternal FileがControllerにダウンロードされても、ProjectにUpdateされることがありません。

##Python Scriptを追加する
Add Object>External Fileをクリクします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/7050ce0e-06db-944a-888e-93b7acfd2968.png)

ここでFile Path・Nameを定義します。設定終わったら“Add”。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/c93cae7c-671f-85cd-fa6f-e1b83e0b4a3e.png)

Edit Objectで先設定したFile Handling・更新タイミングなどもやり直しすることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/4bad0a73-e0e6-019b-a22e-706a621714b3.png)

LoginしExternal Fileをダウンロードします。
一応ちゃんとControllerの中に入ってるかどうかを確認しましょう。
Deviceのところをダブルクリック。
Fileの先にクリック。
PathがPlcLogic/Applicationです。
うん、Hello.pyが入っていますね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/c0132ffb-e634-0700-92f1-442aec18350a.png)

###Script
単なる現在時間とってそのままLoggingするだけです。

```Python
import datetime
print('Hello,CodeSys')
d=datetime.datetime.now()

path='log.txt'

f=open(path,"a")
f.write(str(d)+'\n')
f.close()
```

#PI4修正
まずViの使え方はこちらで：
https://prev.net-newbie.com/linux/commands/vi.html
https://jsapachehtml.hatenablog.com/entry/2014/11/23/124350

```
[SysProcess]
Command=AllowAll
```
を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/1c7fc332-19da-aed8-6e35-a6f387cb8712.png)

#プログラム
##初期化プログラム

ControllerがStop>Runになるとき走るプログラムを作ります。それは毎回もちゃんとScriptの実行権限・最新Fileなどを確実するためです。System Eventsが使うことになります。
Online Helpはこちらで：
https://help.codesys.com/api-content/2/codesys/3.5.13.0/en/_cds_obj_task_config_sys_event/#e5ddda49857aab52c0a8640e01f6cda7-id-8eb22e41857aab52c0a8640e009f5f0b

Task Configurationをクリクします。
System EventsのTabを開きます。
Add Event Handlerで新しいイベントを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/e32da4a2-be5c-efe8-5fb3-93133fdb6336.png)

Online HelpのScreenShotですが、使うのはそのPrepareStartです。
Call before starting the applicationってつまりControllerがStop>Runになるときです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/1a277b04-7c69-2450-a59b-f3f20dc4e436.png)
Add Event Handlerを押すと、以下のPopupが出てきます。
ここでどんなEventでどんなプログラムを走るのかを決めます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/4f641d5b-3930-7c7b-fc0e-e5986ffa6373.png)
EventのところにPrepareStartを選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/530526e2-918a-91c0-a535-c1e42e09158b.png)
Function CallでそのEventがトリガーしたとき走るプログラムを設定します。
ここで名前入れるだけOKを押すと勝手にFunctionが作成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/2ca608b6-6a1f-d5f2-f850-71838df30e82.png)
GVLでGlobal変数を作ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/93785968-2a0b-f46b-5005-3c55792acdb6.png)
そして初期値をFalseします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/41a55e24-c9bc-1679-fe6a-023083929053.png)

##実行プログラム
_PythonExecuteのPOUを作ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/52d4592d-e59a-6fe0-1f68-f21922f9b92a.png)

###使うFunction
最初にライブラリをImportすることが覚えていますか？そのライブラリの中に2つのFunctionを使うことになります。
Library Manager>Libraryを選び>Functionを選び>4番の所にそのFunctionの説明が書いています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/2bb9f332-4c0f-3352-f044-a49e24c0ba97.png)

####SysProcessExecuteCommand2
https://help.codesys.com/webapp/SysProcessExecuteCommand2;product=SysProcess%20Implementation;version=3.5.16.0
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/e5e920e2-04e3-424d-cae2-26e14c60c1b7.png)

- Return:DINT
- Input：
    - pszcommand:実行するコマンド
    - udisStdOutLen:コマンド実行した後Terminal出力された文字列
    - pReseult:実行結果。0=エラーなし、だと思います。

システムコマンド実行するためにFunctionです。注意するのはコマンドの出力はOSに依存します。

####SysFileCopy
https://help.codesys.com/webapp/SysFileCopy;product=SysFile;version=3.5.9.0
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/24a97cf2-784b-4f41-8295-32f2f6bebeab.png)

- Return：RTS_IEC_RESULT、Runtimeシステムのエラーコード
- szDestFileName:Copy先のPath
- szSourceFileName:Copy元のPath
- pulCopied:CopyしたByte数

#実装
##Interface
```
PROGRAM _PythonExecute
VAR
	dwCopySize: DWORD;
	dutResult : RTS_IEC_RESULT;
	szCommand : STRING(200);
	fbTimer : TON;
	szStdOout : STRING(1000);
	fbTimer1 : TON;
	Command : STRING(200);
END_VAR
```
##Program
```
(*
   /etc/CODESYSControl.cfg:
   [SysProcess]
   Command=AllowAll 
*)

IF NOT GVL.init THEN
	
	//Copy the scripts to local
	SysFile.SysFileCopy('/var/opt/codesys/hello.py'
						,'/var/opt/codesys/PlcLogic/Application/hello.py'
						,ADR(dwCopySize));
	
	
	//Change the permission that can execute
	SysProcess_Implementation.SysProcessExecuteCommand2(pszCommand:='chmod +x *.py'
														, pszStdOut:=szStdOout
														, udiStdOutLen:= SIZEOF(szStdOout)
														, pResult := ADR(dutResult));
	
	//Init is finished
	GVL.init:=TRUE;
	
END_IF

//Flash Timer
fbTimer1(IN:= TRUE, PT:=T#1S); 

//Command 
Command:= 'sudo python hello.py';

//Timer is ON
IF fbTimer1.Q  THEN
		//Reset it and flash again
		fbTimer1(IN:= FALSE);
		//Execute
		SysProcess.SysProcessExecuteCommand2(pszCommand:=Command
											, pszStdOut:=szStdOout
											, udiStdOutLen:= SIZEOF(szStdOout)
											, pResult := ADR(dutResult));
END_IF
```
実行すると、まずScriptが/var/opt/codesys/hello.pyにCopyされるとわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/a00355e3-a177-ada5-9cbd-8fce5d942108.png)

そしてlog.txtが作成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/b0245d60-ce95-eb46-28f2-98940afe7292.png)

Logの中時間が記録されてます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/4f86861c-4350-65b0-67dc-12879090baa6.png)

はーい、お疲れ様です。