pm2cd 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. pm2cd-0.1.1/PKG-INFO +1454 -0
  2. pm2cd-0.1.1/README.md +1431 -0
  3. pm2cd-0.1.1/pyproject.toml +53 -0
  4. pm2cd-0.1.1/setup.cfg +4 -0
  5. pm2cd-0.1.1/src/pm2cd/__init__.py +3 -0
  6. pm2cd-0.1.1/src/pm2cd/core/__init__.py +12 -0
  7. pm2cd-0.1.1/src/pm2cd/core/communication_tcp.py +416 -0
  8. pm2cd-0.1.1/src/pm2cd/core/controller_status.py +35 -0
  9. pm2cd-0.1.1/src/pm2cd/core/data.py +32 -0
  10. pm2cd-0.1.1/src/pm2cd/core/errors.py +10 -0
  11. pm2cd-0.1.1/src/pm2cd/core/home_status.py +31 -0
  12. pm2cd-0.1.1/src/pm2cd/core/limit_status.py +71 -0
  13. pm2cd-0.1.1/src/pm2cd/core/limit_switch_setting.py +163 -0
  14. pm2cd-0.1.1/src/pm2cd/core/motor_setting.py +39 -0
  15. pm2cd-0.1.1/src/pm2cd/core/motor_status.py +120 -0
  16. pm2cd-0.1.1/src/pm2cd/core/stop_mode.py +27 -0
  17. pm2cd-0.1.1/src/pm2cd/core/version_info.py +129 -0
  18. pm2cd-0.1.1/src/pm2cd/pm2cd_ctrl.py +1355 -0
  19. pm2cd-0.1.1/src/pm2cd/pm2cd_device.py +2287 -0
  20. pm2cd-0.1.1/src/pm2cd/pm2cd_single.py +2105 -0
  21. pm2cd-0.1.1/src/pm2cd/server_fastapi/__init__.py +24 -0
  22. pm2cd-0.1.1/src/pm2cd/server_fastapi/__init__.pyi +15 -0
  23. pm2cd-0.1.1/src/pm2cd/server_fastapi/client_async.py +27 -0
  24. pm2cd-0.1.1/src/pm2cd/server_fastapi/client_async.pyi +108 -0
  25. pm2cd-0.1.1/src/pm2cd/server_fastapi/client_sync.py +27 -0
  26. pm2cd-0.1.1/src/pm2cd/server_fastapi/client_sync.pyi +107 -0
  27. pm2cd-0.1.1/src/pm2cd/server_fastapi/make_pyi.py +42 -0
  28. pm2cd-0.1.1/src/pm2cd/server_fastapi/server.py +62 -0
  29. pm2cd-0.1.1/src/pm2cd.egg-info/PKG-INFO +1454 -0
  30. pm2cd-0.1.1/src/pm2cd.egg-info/SOURCES.txt +31 -0
  31. pm2cd-0.1.1/src/pm2cd.egg-info/dependency_links.txt +1 -0
  32. pm2cd-0.1.1/src/pm2cd.egg-info/requires.txt +15 -0
  33. pm2cd-0.1.1/src/pm2cd.egg-info/top_level.txt +1 -0
pm2cd-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,1454 @@
1
+ Metadata-Version: 2.4
2
+ Name: pm2cd
3
+ Version: 0.1.1
4
+ Summary: tuji pm2cd
5
+ Author-email: Kengo NAKADA <kengo.nakada@email.com>
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: xlogger-py
9
+ Requires-Dist: dataclasses
10
+ Requires-Dist: coloredlogs
11
+ Requires-Dist: colorlog
12
+ Requires-Dist: pytest
13
+ Requires-Dist: pytest-asyncio
14
+ Requires-Dist: pyyaml
15
+ Requires-Dist: toml
16
+ Requires-Dist: nest_asyncio
17
+ Requires-Dist: sphinx
18
+ Requires-Dist: myst-parser
19
+ Requires-Dist: sphinx-autobuild
20
+ Requires-Dist: sphinx-rtd-theme
21
+ Requires-Dist: sphinxcontrib-napoleon
22
+ Requires-Dist: linkify-it-py
23
+
24
+ # pm2cd
25
+
26
+ PM2CD / PM2C-06A を Python から制御するためのパッケージです。
27
+
28
+ 本パッケージは、既存の C版/MADOCA Device handler
29
+ の実装思想と装置操作の意味を参照しながら、 Python
30
+ では通信、装置固有処理、データ解析、利用者向け制御、multi-process 実行、
31
+ FastAPI 通信を分離して構成しています。
32
+
33
+ 現在のバージョンは **v0.1.0** です。
34
+
35
+ この README は、単なるインストールメモではなく、PM2CD Python 版の
36
+ 総合マニュアル・設計仕様・チュートリアルとして記述しています。
37
+
38
+ ------------------------------------------------------------------------
39
+
40
+ ## 1. 対象
41
+
42
+ 対象装置は PM2CD / PM2C-06A です。
43
+
44
+ PM16C-16 と PM2CD / PM2C-06A は同一装置ではありません。 PM16C-16
45
+ の仕様をそのまま PM2CD の装置仕様として流用しません。
46
+
47
+ 一方、既存の C版 `dev_pm16c16_*` Device handler 群には、 PM2CD Python
48
+ 版を設計する上で重要な先行実装があります。
49
+
50
+ Python 版では、それら C ファイルを機械的に一対一移植するのではなく、
51
+
52
+ - 一つの意味ある装置操作を一つの Device handler として扱う
53
+ - 一つの操作に複数コマンドが必要なら Device 層で完結させる
54
+ - 通信そのものは共通通信層へ分離する
55
+ - raw response の複合解析は core parser へ分離する
56
+ - Ctrl は raw bit / nibble / command string を利用者へ露出しない
57
+
58
+ という設計思想を引き継いでいます。
59
+
60
+ ------------------------------------------------------------------------
61
+
62
+ ## 2. C版/MADOCA と Python 版の関係
63
+
64
+ ### 2.1 C版の位置づけ
65
+
66
+ 既存 C版は MADOCA 環境の Device handler として実装されてきたコードです。
67
+
68
+ 今回 Python 版の基準として参照しているのは、MADOCA 全体のネットワーク
69
+ API を Python へ移植することではなく、Device handler
70
+ 内部で実際に行われている
71
+
72
+ - command 生成
73
+ - send / receive
74
+ - 複数 command の処理順序
75
+ - response の変換
76
+ - 装置操作としての handler の粒度
77
+
78
+ です。
79
+
80
+ 確認してきた C版 handler には、rotate/check_rotate、number/rate 変換、
81
+ emergency stop、backlash、display、hold、limit、pause、pulse、rate、
82
+ speed、speed mode、status/status_all、SETLS、TTL、version、home、
83
+ ALLREP、remote、stop、CRLF 送受信などがあります。
84
+
85
+ ### 2.2 Python 版は C版の一対一翻訳ではない
86
+
87
+ Python 版では C版の責務を次のように再配置しています。
88
+
89
+ ``` text
90
+ C版/MADOCA
91
+ Device handler
92
+ |
93
+ | 装置操作の意味・処理順序を参照
94
+ v
95
+ Python
96
+ PM2CDDevice
97
+ + core parser
98
+ + Communication
99
+ + PM2CDsingle
100
+ + PM2CD
101
+ ```
102
+
103
+ 例えば C版の一つの handler が、
104
+
105
+ 1. 設定値を問い合わせる
106
+ 2. 必要な bit を変更する
107
+ 3. 設定 command を送る
108
+ 4. 本来の操作 command を送る
109
+
110
+ という処理を行う場合、Python 版でも「一つの意味ある装置操作」として
111
+ `PM2CDDevice` 内で完結させます。
112
+
113
+ `set_limit()` はその代表例です。
114
+
115
+ ``` text
116
+ SETLS?x
117
+ |
118
+ v
119
+ LimitSwitchSetting
120
+ |
121
+ +-- software limit が無効なら enable
122
+ |
123
+ v
124
+ SETLSx...
125
+ |
126
+ v
127
+ FLx... / BLx...
128
+ ```
129
+
130
+ 利用者に `SETLS?` と `FL/BL` を別々に操作させる設計にはしません。
131
+
132
+ ### 2.3 MADOCA と Python server は別レイヤー
133
+
134
+ C版で使用されてきた MADOCA と、Python 版で使用する FastAPI /
135
+ `ese774_frame` は同じ通信 framework ではありません。
136
+
137
+ Python 版 PM2CD は DeviceClass として単独でも利用でき、 必要な場合に
138
+ FastAPI server/client を上に載せます。
139
+
140
+ したがって Python 版の設計では、
141
+
142
+ ``` text
143
+ 装置制御ロジック
144
+ !=
145
+ ネットワーク framework
146
+ ```
147
+
148
+ を明確に分離しています。
149
+
150
+ ------------------------------------------------------------------------
151
+
152
+ ## 3. Python 版の全体構成
153
+
154
+ v0.1.0 の基本構造は次の通りです。
155
+
156
+ ``` text
157
+ 利用プログラム
158
+ |
159
+ +-- local
160
+ | |
161
+ | +--> PM2CDsingle
162
+ | |
163
+ | `--> PM2CD
164
+ |
165
+ `-- network
166
+ |
167
+ +--> SyncPM2CDClient / AsyncPM2CDClient
168
+ |
169
+ v
170
+ FastAPI / ese774_frame
171
+ |
172
+ v
173
+ PM2CD
174
+ |
175
+ v
176
+ ProcessExecutor
177
+ |
178
+ +--> PM2CDsingle worker 1
179
+ +--> PM2CDsingle worker 2
180
+ +--> ...
181
+ |
182
+ v
183
+ PM2CDDevice
184
+ |
185
+ v
186
+ shared Communication proxy
187
+ |
188
+ v
189
+ Communication x 1
190
+ |
191
+ v
192
+ TCP + CRLF
193
+ |
194
+ v
195
+ PM2CD / PM2C-06A
196
+ ```
197
+
198
+ 受信データについては、
199
+
200
+ ``` text
201
+ raw response
202
+ |
203
+ v
204
+ core parser
205
+ |
206
+ v
207
+ frozen dataclass / Python value
208
+ |
209
+ v
210
+ PM2CDDevice
211
+ |
212
+ v
213
+ PM2CDsingle
214
+ |
215
+ v
216
+ PM2CD / client
217
+ ```
218
+
219
+ という流れになります。
220
+
221
+ ------------------------------------------------------------------------
222
+
223
+ ## 4. 各層の責務
224
+
225
+ ### 4.1 `Communication`
226
+
227
+ TCP 接続と文字列送受信だけを担当します。
228
+
229
+ 主な責務は、
230
+
231
+ - TCP connect / disconnect
232
+ - command への CRLF 付加
233
+ - send
234
+ - receive
235
+ - query
236
+ - timeout / socket error 処理
237
+ - receive buffer 管理
238
+
239
+ です。
240
+
241
+ PM2CD 固有の `STS?`、`SETLS?`、`FL?` などの意味は知りません。
242
+
243
+ `query()` は send と receive を同一 lock 範囲で処理し、
244
+ 要求と応答の対応を維持します。
245
+
246
+ ### 4.2 `core`
247
+
248
+ 装置の複合 response を意味のある Python データへ変換します。
249
+
250
+ 現在の主要データ型は frozen dataclass として扱います。
251
+
252
+ - `MotorStatus`
253
+ - `ControllerStatus`
254
+ - `HomeStatus`
255
+ - `LimitStatus`
256
+ - `MotorSetting`
257
+ - `LimitSwitchSetting`
258
+ - `StopMode`
259
+
260
+ データ保持と parsing は分離します。
261
+
262
+ ``` text
263
+ raw response
264
+ |
265
+ v
266
+ parse_xxx()
267
+ |
268
+ v
269
+ @dataclass(frozen=True)
270
+ ```
271
+
272
+ 解析不能な response について、不正な状態 object を無理に生成せず、 取得
273
+ API の失敗として `None` を返す設計を基本とします。
274
+
275
+ ### 4.3 `PM2CDDevice`
276
+
277
+ 装置固有の実 handler 層です。
278
+
279
+ ここが C版 Device handler に最も近い責務を持ちます。
280
+
281
+ - command 名を知る
282
+ - command を組み立てる
283
+ - 入力値を検証する
284
+ - `Communication` を使用して send/query する
285
+ - 必要な複数 command を一つの操作として実行する
286
+ - core parser を呼ぶ
287
+ - 装置固有 response を Python 値へ変換する
288
+
289
+ Device を単純な `send()` / `query()` wrapper にはしません。
290
+
291
+ ### 4.4 `PM2CDsingle`
292
+
293
+ 単一 process で動作する高水準の制御クラスです。
294
+
295
+ 主な責務は、
296
+
297
+ - 利用者向け API
298
+ - default `motor`
299
+ - 移動コンテキスト
300
+ - `wait_for_complete()`
301
+ - backlash を含む位置決め完了判定
302
+ - Device API を意味ベースの公開 API へ整理
303
+
304
+ です。
305
+
306
+ `PM2CDsingle` は `Communication` の外部 injection に対応しています。
307
+
308
+ ``` python
309
+ ctrl = PM2CDsingle(
310
+ ip_address=IP_ADDRESS,
311
+ communication=communication,
312
+ )
313
+ ```
314
+
315
+ この dependency injection point は test 用だけではなく、 v0.1.0 の
316
+ multi-process 構成で一つの物理 Communication を 複数 worker
317
+ から使用するためにも利用します。
318
+
319
+ ### 4.5 `PM2CD`
320
+
321
+ v0.1.0 では、利用者向け `PM2CD` は `ProcessExecutor` を使用する
322
+ multi-process facade です。
323
+
324
+ 公開 API の意味は `PM2CDsingle` と揃え、 実際の handler 実行を worker
325
+ process へ dispatch します。
326
+
327
+ ``` text
328
+ PM2CD.method(...)
329
+ |
330
+ v
331
+ ProcessExecutor.submit(...)
332
+ |
333
+ v
334
+ PM2CDsingle.method(...)
335
+ |
336
+ v
337
+ ProcessExecutor.wait(...)
338
+ |
339
+ v
340
+ return value
341
+ ```
342
+
343
+ このため利用者から見る API は同期的ですが、 内部では handler が worker
344
+ process で実行されます。
345
+
346
+ ------------------------------------------------------------------------
347
+
348
+ ## 5. `PM2CDsingle` と `PM2CD` の使い分け
349
+
350
+ ### 5.1 `PM2CDsingle`
351
+
352
+ 一つの Python process 内で直接装置を制御する場合に使用します。
353
+
354
+ ``` python
355
+ from pm2cd.pm2cd_single import PM2CDsingle
356
+
357
+ ctrl = PM2CDsingle(
358
+ ip_address="10.170.10.62",
359
+ port=7777,
360
+ timeout_sec=3.0,
361
+ )
362
+
363
+ try:
364
+ ctrl.remote()
365
+ position = ctrl.get_position(0)
366
+ print(position)
367
+ finally:
368
+ ctrl.close()
369
+ ```
370
+
371
+ 構造は単純です。
372
+
373
+ ``` text
374
+ application
375
+ |
376
+ v
377
+ PM2CDsingle
378
+ |
379
+ v
380
+ PM2CDDevice
381
+ |
382
+ v
383
+ Communication
384
+ |
385
+ v
386
+ PM2CD
387
+ ```
388
+
389
+ ### 5.2 `PM2CD`
390
+
391
+ worker process を使用する通常の multi-process 構成です。
392
+
393
+ ``` python
394
+ from pm2cd.pm2cd_ctrl import PM2CD
395
+
396
+ ctrl = PM2CD(
397
+ ip_address="10.170.10.62",
398
+ port=7777,
399
+ timeout_sec=3.0,
400
+ worker_count=2,
401
+ )
402
+
403
+ try:
404
+ ctrl.remote()
405
+ position = ctrl.get_position(0)
406
+ print(position)
407
+ finally:
408
+ ctrl.close()
409
+ ```
410
+
411
+ `worker_count` は `ProcessExecutor` の worker process 数です。
412
+
413
+ ------------------------------------------------------------------------
414
+
415
+ ## 6. なぜ複数 worker でも Communication は一つなのか
416
+
417
+ ### 6.1 誤った構成
418
+
419
+ worker ごとに `PM2CDsingle` を通常生成すると、 各 worker が自分の
420
+ `Communication` を作ります。
421
+
422
+ ``` text
423
+ worker 1 -> Communication 1 --+
424
+ worker 2 -> Communication 2 --+--> same PM2CD
425
+ worker 3 -> Communication 3 --+
426
+ ```
427
+
428
+ 各 `Communication` の lock は別 process / 別 instance なので、
429
+ 装置全体の通信順序を保証しません。
430
+
431
+ ### 6.2 v0.1.0 の構成
432
+
433
+ v0.1.0 では `BaseManager` が一つの `Communication` を所有し、 各 worker
434
+ には proxy を渡します。
435
+
436
+ ``` text
437
+ worker 1 --+
438
+ worker 2 --+--> Communication proxy --> Communication x 1 --> PM2CD
439
+ worker N --+
440
+ ```
441
+
442
+ したがって、
443
+
444
+ ``` text
445
+ worker process N
446
+ PM2CDsingle N
447
+ Communication 1
448
+ TCP connection 1
449
+ ```
450
+
451
+ となります。
452
+
453
+ これは `ProcessExecutor` が PM2CD
454
+ の通信を自動的に共有しているのではありません。
455
+
456
+ **物理 resource の ownership は PM2CD 側の設計です。**
457
+
458
+ ------------------------------------------------------------------------
459
+
460
+ ## 7. `ProcessExecutor` との関係
461
+
462
+ `ProcessExecutor` は PM2CD 専用 framework ではありません。
463
+
464
+ PM2CD 側が利用している主な機能は、
465
+
466
+ - worker process の生成
467
+ - `worker_factory(context)`
468
+ - handler dispatch
469
+ - `submit()`
470
+ - `wait()`
471
+ - `SharedContext`
472
+ - manager-backed shared state
473
+ - process-safe lock
474
+
475
+ です。
476
+
477
+ 概念上の責務は次のように分離します。
478
+
479
+ ``` text
480
+ ProcessExecutor
481
+ |
482
+ +-- process を管理する
483
+ +-- job を dispatch する
484
+ +-- shared context を提供する
485
+ |
486
+ `-- PM2CD protocol は知らない
487
+
488
+ PM2CD
489
+ |
490
+ +-- PM2CDsingle を worker として構成する
491
+ +-- Communication を一つだけ所有する
492
+ +-- controller state を SharedContext へ置く
493
+ |
494
+ `-- PM2CD 固有の resource ownership を決める
495
+ ```
496
+
497
+ ### 7.1 `worker_factory(context)`
498
+
499
+ PM2CD は `worker_factory(context)` を使用して各 worker の `PM2CDsingle`
500
+ を生成します。
501
+
502
+ その際、
503
+
504
+ - 共通 `Communication` proxy
505
+ - `SharedContext.state`
506
+
507
+ を worker に接続します。
508
+
509
+ ### 7.2 shared state
510
+
511
+ worker が複数あると、通常の instance variable は worker ごとに別です。
512
+
513
+ PM2CD では少なくとも、
514
+
515
+ - default `motor`
516
+ - 移動コンテキスト
517
+
518
+ は worker をまたいで意味を維持する必要があります。
519
+
520
+ そのため `SharedContext.state` を使用します。
521
+
522
+ ------------------------------------------------------------------------
523
+
524
+ ## 8. 移動コンテキストと `wait_for_complete()`
525
+
526
+ `move()` / `move_relative()` は、単に command を送るだけではありません。
527
+
528
+ 位置決め完了判定のため、モーターごとに直前の移動条件を保持します。
529
+
530
+ 主な内容は、
531
+
532
+ - target position
533
+ - backlash 指定
534
+ - backlash enabled
535
+ - backlash value
536
+
537
+ です。
538
+
539
+ ### 8.1 backlash なし
540
+
541
+ 基本的な完了判定は、
542
+
543
+ ``` text
544
+ move
545
+ |
546
+ v
547
+ active
548
+ |
549
+ v
550
+ target position + inactive
551
+ |
552
+ v
553
+ complete
554
+ ```
555
+
556
+ です。
557
+
558
+ ただし短い移動では polling 間で active
559
+ 状態を観測できない場合があるため、 active
560
+ を観測したこと自体を必須条件にはしません。
561
+
562
+ ### 8.2 backlash あり
563
+
564
+ backlash 補正では概念的に、
565
+
566
+ ``` text
567
+ target
568
+ |
569
+ v
570
+ backlash point
571
+ |
572
+ v
573
+ target
574
+ ```
575
+
576
+ という追加動作が入ります。
577
+
578
+ そのため最初に target position で inactive になっただけでは、
579
+ 補正完了と即断できません。
580
+
581
+ `wait_for_complete()` は直前の移動コンテキストを参照して、 最終 target
582
+ への復帰までを判定します。
583
+
584
+ 短い backlash 動作では active/inactive 遷移が polling
585
+ 間で縮退する場合も考慮します。
586
+
587
+ ### 8.3 multi-process での共有
588
+
589
+ `move()` を worker 1、`wait_for_complete()` を worker 2
590
+ が処理する可能性があります。
591
+
592
+ そのため v0.1.0 の `PM2CD` では移動コンテキストを `SharedContext.state`
593
+ へ置きます。
594
+
595
+ ``` text
596
+ worker 1: move()
597
+ |
598
+ v
599
+ SharedContext.state
600
+ |
601
+ v
602
+ worker 2: wait_for_complete()
603
+ ```
604
+
605
+ これにより worker scheduling に依存せず同じ制御意味を維持します。
606
+
607
+ ------------------------------------------------------------------------
608
+
609
+ ## 9. `motor` property
610
+
611
+ `PM2CDsingle` / `PM2CD` は default motor を `motor` property
612
+ で保持します。
613
+
614
+ ``` python
615
+ ctrl.motor = 0
616
+ position = ctrl.get_position()
617
+ ```
618
+
619
+ 各 API で motor を明示した場合は、その値を優先します。
620
+
621
+ ``` python
622
+ position = ctrl.get_position(1)
623
+ ```
624
+
625
+ multi-process `PM2CD` では default motor も shared state として扱い、
626
+ worker ごとに異なる default motor にならないようにします。
627
+
628
+ ------------------------------------------------------------------------
629
+
630
+ ## 10. FastAPI / `ese774_frame`
631
+
632
+ PM2CD は `ese774_frame` の完全自動ディスパッチを使用します。
633
+
634
+ 基本設定は、
635
+
636
+ ``` text
637
+ FastApiServer
638
+ api_spec=None
639
+ object_name="pm2cd"
640
+ ```
641
+
642
+ です。
643
+
644
+ 個別の Pydantic model や `ApiSpec` を PM2CD 側で列挙せず、
645
+ 公開メソッドとアノテーションを利用して API を構成します。
646
+
647
+ ### 10.1 dataclass の転送
648
+
649
+ PM2CD は状態取得 API で frozen dataclass を返します。
650
+
651
+ 例:
652
+
653
+ - `MotorStatus`
654
+ - `ControllerStatus`
655
+ - `HomeStatus`
656
+ - `LimitStatus`
657
+ - `MotorSetting`
658
+ - `LimitSwitchSetting`
659
+ - `StopMode`
660
+
661
+ これらを完全自動ディスパッチで JSON 化し、 client
662
+ 側で元の型へ復元するため、 **`ese774_frame v0.6.0` 以上を使用します。**
663
+
664
+ ### 10.2 client
665
+
666
+ 同期 client と非同期 client を使用できます。
667
+
668
+ ``` text
669
+ SyncPM2CDClient
670
+ AsyncPM2CDClient
671
+ ```
672
+
673
+ client は `object_name="pm2cd"` で server と対応します。
674
+
675
+ ------------------------------------------------------------------------
676
+
677
+ ## 11. server の起動
678
+
679
+ ### 11.1 module 側
680
+
681
+ v0.1.0 の server module は worker 数を受け取れます。
682
+
683
+ ``` python
684
+ from pm2cd.server import run
685
+
686
+ run(
687
+ worker_count=2,
688
+ )
689
+ ```
690
+
691
+ 内部では、
692
+
693
+ ``` text
694
+ run(worker_count)
695
+ |
696
+ v
697
+ create_server(worker_count)
698
+ |
699
+ v
700
+ FastApiServer(device_kwargs)
701
+ |
702
+ v
703
+ PM2CD(worker_count=worker_count)
704
+ ```
705
+
706
+ と渡されます。
707
+
708
+ server process の数と、PM2CD 内部の `ProcessExecutor.worker_count` は
709
+ 別の概念です。
710
+
711
+ ### 11.2 example server
712
+
713
+ 実際の起動 example は worker 数をコード側で設定します。
714
+
715
+ ``` python
716
+ from pm2cd.server import run
717
+
718
+ WORKER_COUNT = 2
719
+
720
+
721
+ def main() -> None:
722
+ run(
723
+ worker_count=WORKER_COUNT,
724
+ )
725
+
726
+
727
+ if __name__ == "__main__":
728
+ main()
729
+ ```
730
+
731
+ worker 数のための CLI option は使用しません。
732
+
733
+ ------------------------------------------------------------------------
734
+
735
+ ## 12. 基本チュートリアル
736
+
737
+ 以下では `PM2CD` を使用します。
738
+
739
+ ### 12.1 接続と終了
740
+
741
+ ``` python
742
+ from pm2cd.pm2cd_ctrl import PM2CD
743
+
744
+ ctrl = PM2CD(
745
+ ip_address="10.170.10.62",
746
+ port=7777,
747
+ timeout_sec=3.0,
748
+ worker_count=2,
749
+ )
750
+
751
+ try:
752
+ print(ctrl.is_connected())
753
+ finally:
754
+ ctrl.close()
755
+ ```
756
+
757
+ ### 12.2 REMOTE へ切り替える
758
+
759
+ ``` python
760
+ ctrl.remote()
761
+ ```
762
+
763
+ LOCAL へ戻す場合は、
764
+
765
+ ``` python
766
+ ctrl.local()
767
+ ```
768
+
769
+ です。
770
+
771
+ ### 12.3 状態を取得する
772
+
773
+ ``` python
774
+ status = ctrl.get_status(0)
775
+
776
+ if status is not None:
777
+ print(status.position)
778
+ print(status.is_busy)
779
+ ```
780
+
781
+ 全軸:
782
+
783
+ ``` python
784
+ statuses = ctrl.get_status_all()
785
+ ```
786
+
787
+ ### 12.4 現在位置を取得する
788
+
789
+ ``` python
790
+ position = ctrl.get_position(0)
791
+ print(position)
792
+ ```
793
+
794
+ default motor を使用する場合:
795
+
796
+ ``` python
797
+ ctrl.motor = 0
798
+ position = ctrl.get_position()
799
+ ```
800
+
801
+ ### 12.5 絶対位置移動
802
+
803
+ ``` python
804
+ ctrl.move(
805
+ 0,
806
+ 1000,
807
+ backlash=False,
808
+ )
809
+
810
+ status = ctrl.wait_for_complete(
811
+ 0,
812
+ timeout_sec=30.0,
813
+ )
814
+ ```
815
+
816
+ ### 12.6 backlash 付き位置決め
817
+
818
+ ``` python
819
+ ctrl.move(
820
+ 0,
821
+ 1000,
822
+ backlash=True,
823
+ )
824
+
825
+ status = ctrl.wait_for_complete(
826
+ 0,
827
+ timeout_sec=30.0,
828
+ )
829
+ ```
830
+
831
+ `PM2CD` では移動コンテキストが worker 間で共有されるため、 `move()` と
832
+ `wait_for_complete()` が別 worker で実行されても backlash
833
+ 条件を引き継ぎます。
834
+
835
+ ### 12.7 相対移動
836
+
837
+ ``` python
838
+ ctrl.move_relative(
839
+ 0,
840
+ 100,
841
+ backlash=True,
842
+ )
843
+
844
+ ctrl.wait_for_complete(
845
+ 0,
846
+ timeout_sec=30.0,
847
+ )
848
+ ```
849
+
850
+ ### 12.8 通常停止と非常停止
851
+
852
+ 通常停止:
853
+
854
+ ``` python
855
+ ctrl.stop(0)
856
+ ctrl.stop_all()
857
+ ```
858
+
859
+ 非常停止:
860
+
861
+ ``` python
862
+ ctrl.emergency_stop(0)
863
+ ctrl.emergency_stop_all()
864
+ ```
865
+
866
+ 通常停止と非常停止は別 API として扱います。
867
+
868
+ ------------------------------------------------------------------------
869
+
870
+ ## 13. 速度・rate・backlash
871
+
872
+ ### 13.1 speed
873
+
874
+ ``` python
875
+ speed = ctrl.get_speed(0)
876
+ ctrl.set_speed(0, 1000)
877
+ ```
878
+
879
+ LOW / MIDDLE / HIGH をまとめて扱う API:
880
+
881
+ ``` python
882
+ profile = ctrl.get_speed_profile(0)
883
+ ```
884
+
885
+ 個別 API:
886
+
887
+ ``` text
888
+ get_high_speed / set_high_speed
889
+ get_middle_speed / set_middle_speed
890
+ get_low_speed / set_low_speed
891
+ ```
892
+
893
+ ### 13.2 rate
894
+
895
+ rate number:
896
+
897
+ ``` python
898
+ number = ctrl.get_rate_number(0)
899
+ ctrl.set_rate_number(0, number)
900
+ ```
901
+
902
+ rate 値:
903
+
904
+ ``` python
905
+ rate = ctrl.get_rate(0)
906
+ ctrl.set_rate(0, rate)
907
+ ```
908
+
909
+ 変換 API:
910
+
911
+ ``` python
912
+ rate = ctrl.number_to_rate(number)
913
+ number = ctrl.rate_to_number(rate)
914
+ ```
915
+
916
+ ### 13.3 backlash
917
+
918
+ ``` python
919
+ backlash = ctrl.get_backlash(0)
920
+ ctrl.set_backlash(0, backlash)
921
+ ```
922
+
923
+ ------------------------------------------------------------------------
924
+
925
+ ## 14. limit
926
+
927
+ PM2CD Python API では、無修飾の `limit` は software limit を意味します。
928
+
929
+ hardware limit は `hard_` を明示します。
930
+
931
+ ### 14.1 software limit position
932
+
933
+ ``` python
934
+ cw = ctrl.get_limit(0, "cw")
935
+ ccw = ctrl.get_limit(0, "ccw")
936
+
937
+ ctrl.set_limit(0, "cw", 10000)
938
+ ctrl.set_limit(0, "ccw", -10000)
939
+ ```
940
+
941
+ ### 14.2 software limit state
942
+
943
+ ``` python
944
+ status = ctrl.get_limit_status(0)
945
+ ```
946
+
947
+ 意味順序:
948
+
949
+ ``` text
950
+ [CW, CCW]
951
+ ```
952
+
953
+ ### 14.3 hardware limit state
954
+
955
+ ``` python
956
+ status = ctrl.get_hard_limit_status(0)
957
+ ```
958
+
959
+ 意味順序:
960
+
961
+ ``` text
962
+ [CW, CCW, HP]
963
+ ```
964
+
965
+ ### 14.4 enable
966
+
967
+ software/digital limit:
968
+
969
+ ``` python
970
+ enabled = ctrl.get_limit_enabled(0)
971
+ ctrl.set_limit_enabled(0, True)
972
+ ```
973
+
974
+ hardware limit:
975
+
976
+ ``` python
977
+ enabled = ctrl.get_hard_limit_enabled(0)
978
+ ctrl.set_hard_limit_enabled(0, True)
979
+ ```
980
+
981
+ ### 14.5 normally closed / open
982
+
983
+ ``` python
984
+ setting = ctrl.get_hard_limit_normally_closed(0)
985
+
986
+ ctrl.set_hard_limit_normally_closed(
987
+ 0,
988
+ "cw",
989
+ True,
990
+ )
991
+ ```
992
+
993
+ 公開 API では raw `DYYY0yyy` を直接扱いません。
994
+
995
+ ------------------------------------------------------------------------
996
+
997
+ ## 15. HOME / HOLD / PAUSE
998
+
999
+ HOME:
1000
+
1001
+ ``` text
1002
+ home
1003
+ find_home
1004
+ scan_home
1005
+ get_home_status
1006
+ set_home_status
1007
+ get_home_position
1008
+ set_home_position
1009
+ get_home_offset
1010
+ set_home_offset
1011
+ ```
1012
+
1013
+ preset / JOG:
1014
+
1015
+ ``` text
1016
+ get_preset_position
1017
+ set_preset_position
1018
+ get_jog_pulse
1019
+ set_jog_pulse
1020
+ ```
1021
+
1022
+ HOLD:
1023
+
1024
+ ``` text
1025
+ get_hold
1026
+ set_hold
1027
+ get_hold_on_time
1028
+ set_hold_on_time
1029
+ get_hold_off_time
1030
+ set_hold_off_time
1031
+ ```
1032
+
1033
+ PAUSE:
1034
+
1035
+ ``` text
1036
+ pause
1037
+ resume
1038
+ get_pause_status
1039
+ ```
1040
+
1041
+ ------------------------------------------------------------------------
1042
+
1043
+ ## 16. motor / stop setting
1044
+
1045
+ motor setting:
1046
+
1047
+ ``` text
1048
+ get_motor_setting
1049
+ set_motor_setting
1050
+ ```
1051
+
1052
+ stop mode:
1053
+
1054
+ ``` text
1055
+ get_stop_mode
1056
+ set_stop_mode
1057
+ ```
1058
+
1059
+ 取得値は dataclass として扱います。
1060
+
1061
+ ------------------------------------------------------------------------
1062
+
1063
+ ## 17. 主な公開 API
1064
+
1065
+ ### 接続・mode
1066
+
1067
+ ``` text
1068
+ motor
1069
+ is_connected
1070
+ connect
1071
+ disconnect
1072
+ remote
1073
+ local
1074
+ close
1075
+ ```
1076
+
1077
+ ### version / controller
1078
+
1079
+ ``` text
1080
+ version
1081
+ get_version
1082
+ get_hardware_version
1083
+ get_controller_status
1084
+ controller_status
1085
+ ```
1086
+
1087
+ ### motion
1088
+
1089
+ ``` text
1090
+ move
1091
+ move_relative
1092
+ scan
1093
+ constant_speed_scan
1094
+ jog
1095
+ scan_home
1096
+ home
1097
+ find_home
1098
+ stop
1099
+ stop_all
1100
+ emergency_stop
1101
+ emergency_stop_all
1102
+ get_rotate
1103
+ is_busy
1104
+ wait_for_complete
1105
+ ```
1106
+
1107
+ ### status / position
1108
+
1109
+ ``` text
1110
+ get_status
1111
+ get_status_all
1112
+ status_all
1113
+ get_position
1114
+ set_position
1115
+ zero_position
1116
+ ```
1117
+
1118
+ ### speed / rate / backlash
1119
+
1120
+ ``` text
1121
+ get_speed
1122
+ set_speed
1123
+ get_speed_profile
1124
+ set_speed_profile
1125
+ get_high_speed
1126
+ set_high_speed
1127
+ get_middle_speed
1128
+ set_middle_speed
1129
+ get_low_speed
1130
+ set_low_speed
1131
+ get_speed_mode
1132
+ set_speed_mode
1133
+ get_rate_number
1134
+ set_rate_number
1135
+ get_rate
1136
+ set_rate
1137
+ number_to_rate
1138
+ rate_to_number
1139
+ get_backlash
1140
+ set_backlash
1141
+ ```
1142
+
1143
+ ### limit
1144
+
1145
+ ``` text
1146
+ get_limit
1147
+ set_limit
1148
+ get_limit_status
1149
+ get_limit_status_all
1150
+ get_hard_limit_status
1151
+ get_hard_limit_status_all
1152
+ get_limit_enabled
1153
+ set_limit_enabled
1154
+ get_hard_limit_enabled
1155
+ set_hard_limit_enabled
1156
+ get_hard_limit_normally_closed
1157
+ set_hard_limit_normally_closed
1158
+ get_limit_switch_setting
1159
+ ```
1160
+
1161
+ ### pause / hold
1162
+
1163
+ ``` text
1164
+ pause
1165
+ resume
1166
+ get_pause_status
1167
+ get_hold
1168
+ set_hold
1169
+ get_hold_on_time
1170
+ set_hold_on_time
1171
+ get_hold_off_time
1172
+ set_hold_off_time
1173
+ ```
1174
+
1175
+ ### HOME / preset / JOG
1176
+
1177
+ ``` text
1178
+ get_home_status
1179
+ set_home_status
1180
+ get_home_position
1181
+ set_home_position
1182
+ get_home_offset
1183
+ set_home_offset
1184
+ get_preset_position
1185
+ set_preset_position
1186
+ get_jog_pulse
1187
+ set_jog_pulse
1188
+ ```
1189
+
1190
+ ### setting
1191
+
1192
+ ``` text
1193
+ get_motor_setting
1194
+ set_motor_setting
1195
+ get_stop_mode
1196
+ set_stop_mode
1197
+ ```
1198
+
1199
+ ------------------------------------------------------------------------
1200
+
1201
+ ## 18. 戻り値の基本方針
1202
+
1203
+ 装置制御 API は概ね次の方針です。
1204
+
1205
+ ``` text
1206
+ 操作成功/失敗 bool
1207
+ 値取得成功 値
1208
+ 値取得失敗 None
1209
+ ```
1210
+
1211
+ 取得 API は `Optional[...]` のアノテーションを持つものがあります。
1212
+
1213
+ 状態・設定 object は frozen dataclass として扱い、
1214
+ 取得後に状態を書き換える設計にはしません。
1215
+
1216
+ ------------------------------------------------------------------------
1217
+
1218
+ ## 19. エラーと入力検証
1219
+
1220
+ 入力値は可能な限り `PM2CDDevice` 側で検証し、
1221
+ 不正な値を装置へ送らない構成にします。
1222
+
1223
+ 通信層の error は通信責務として扱い、 装置 response の解析失敗は parser
1224
+ / Device の責務として扱います。
1225
+
1226
+ Ctrl は装置 protocol の raw error 表現を利用者 API
1227
+ の中心には置きません。
1228
+
1229
+ ------------------------------------------------------------------------
1230
+
1231
+ ## 20. server/client 利用時の構成
1232
+
1233
+ network 越しの利用では次の構成になります。
1234
+
1235
+ ``` text
1236
+ client application
1237
+ |
1238
+ v
1239
+ SyncPM2CDClient
1240
+ or AsyncPM2CDClient
1241
+ |
1242
+ v
1243
+ HTTP / FastAPI
1244
+ |
1245
+ v
1246
+ ese774_frame FastApiServer
1247
+ |
1248
+ v
1249
+ PM2CD
1250
+ |
1251
+ v
1252
+ ProcessExecutor
1253
+ |
1254
+ v
1255
+ PM2CDsingle worker x N
1256
+ |
1257
+ v
1258
+ shared Communication
1259
+ |
1260
+ v
1261
+ PM2CD
1262
+ ```
1263
+
1264
+ `PM2CD` の内部 worker 数を増やしても、 装置への TCP connection
1265
+ を増やさないことが v0.1.0 の重要な設計条件です。
1266
+
1267
+ ------------------------------------------------------------------------
1268
+
1269
+ ## 21. example の考え方
1270
+
1271
+ example は段階的に確認できるようにします。
1272
+
1273
+ ### 21.1 single local
1274
+
1275
+ 目的:
1276
+
1277
+ - framework を介さず PM2CDsingle 自体を確認する
1278
+ - 装置 protocol / Device / Ctrl の基準動作を確認する
1279
+
1280
+ ### 21.2 multi local
1281
+
1282
+ 目的:
1283
+
1284
+ - `PM2CD` + `ProcessExecutor` を確認する
1285
+ - worker process からの実制御を確認する
1286
+ - worker 数を2以上にしても Communication が一つである構成を確認する
1287
+
1288
+ ### 21.3 FastAPI server/client
1289
+
1290
+ 目的:
1291
+
1292
+ - `ese774_frame` の完全自動ディスパッチを確認する
1293
+ - dataclass の server/client 間転送を確認する
1294
+ - network 越しでも同じ PM2CD 公開 API を使用する
1295
+
1296
+ ### 21.4 server 起動 example
1297
+
1298
+ worker 数は example script の定数で指定します。
1299
+
1300
+ ``` python
1301
+ WORKER_COUNT = 2
1302
+ ```
1303
+
1304
+ CLI option で worker 数を与える方式にはしません。
1305
+
1306
+ ------------------------------------------------------------------------
1307
+
1308
+ ## 22. 推奨する確認順序
1309
+
1310
+ 装置を使った動作確認は、層を一度に増やさず次の順序で行います。
1311
+
1312
+ ``` text
1313
+ 1. Communication
1314
+ |
1315
+ 2. PM2CDDevice
1316
+ |
1317
+ 3. PM2CDsingle local
1318
+ |
1319
+ 4. PM2CD multi local
1320
+ |
1321
+ 5. FastAPI server
1322
+ |
1323
+ 6. Sync/Async client
1324
+ ```
1325
+
1326
+ multi-process の問題と network framework
1327
+ の問題を同時に切り分けないためです。
1328
+
1329
+ 特に新しい motion API の確認では、一つの method を小さく確認してから
1330
+ 次の層へ進めます。
1331
+
1332
+ ------------------------------------------------------------------------
1333
+
1334
+ ## 23. `close()` と resource ownership
1335
+
1336
+ multi-process `PM2CD` では `Communication` は全 worker の共有 resource
1337
+ です。
1338
+
1339
+ したがって、一つの worker に `PM2CDsingle.close()`
1340
+ を実行させてはいけません。
1341
+
1342
+ `PM2CD.close()` は、
1343
+
1344
+ ``` text
1345
+ ProcessExecutor.close()
1346
+ |
1347
+ v
1348
+ shared Communication.close()
1349
+ |
1350
+ v
1351
+ CommunicationManager.shutdown()
1352
+ ```
1353
+
1354
+ の順で終了します。
1355
+
1356
+ 物理 resource を作成した owner が最後に一度だけ閉じる、という構成です。
1357
+
1358
+ ------------------------------------------------------------------------
1359
+
1360
+ ## 24. 設計上の重要事項
1361
+
1362
+ ### 24.1 Ctrl と Device を混ぜない
1363
+
1364
+ Ctrl に command string を直接書き始めると、 C版で整理されていた Device
1365
+ handler の責務が崩れます。
1366
+
1367
+ 装置固有 command は Device に置きます。
1368
+
1369
+ ### 24.2 Device を薄くしすぎない
1370
+
1371
+ 一つの意味ある操作に複数 command が必要なら、 その一連の処理は Device
1372
+ handler に残します。
1373
+
1374
+ ### 24.3 core dataclass に処理を持たせすぎない
1375
+
1376
+ dataclass はデータを表します。
1377
+
1378
+ response parsing は parser 関数へ分離します。
1379
+
1380
+ ### 24.4 ProcessExecutor に装置固有責務を入れない
1381
+
1382
+ ProcessExecutor は PM2CD の TCP socket や protocol を知りません。
1383
+
1384
+ Communication の共有方法は PM2CD が決めます。
1385
+
1386
+ ### 24.5 shared は公開 API の概念にしない
1387
+
1388
+ worker 間で state を共有していても、 `get_shared_xxx()` のような専用 API
1389
+ を利用者へ追加しません。
1390
+
1391
+ shared state は multi-process 実装の内部事情です。
1392
+
1393
+ ------------------------------------------------------------------------
1394
+
1395
+ ## 25. dependency
1396
+
1397
+ FastAPI 完全自動ディスパッチを使用する場合:
1398
+
1399
+ ``` text
1400
+ ese774_frame >= 0.6.0
1401
+ ```
1402
+
1403
+ が必要です。
1404
+
1405
+ multi-process `PM2CD` は `ProcessExecutor` の、
1406
+
1407
+ ``` text
1408
+ worker_factory
1409
+ SharedContext
1410
+ shared_context
1411
+ submit
1412
+ wait
1413
+ ```
1414
+
1415
+ を使用します。
1416
+
1417
+ PM2CD と ProcessExecutor の責務は分離されており、 ProcessExecutor
1418
+ 自体には PM2CD 固有コードを入れません。
1419
+
1420
+ ------------------------------------------------------------------------
1421
+
1422
+ ## 26. v0.1.0 の位置づけ
1423
+
1424
+ v0.0.x では主に、
1425
+
1426
+ - C版 Device handler の意味の整理
1427
+ - Communication / core / Device / Ctrl の責務分離
1428
+ - `PM2CDsingle` API の拡張
1429
+ - backlash を含む `wait_for_complete()`
1430
+ - frozen dataclass + parser
1431
+ - FastAPI 完全自動ディスパッチ
1432
+
1433
+ を整備しました。
1434
+
1435
+ v0.1.0 では、その single-process 制御ロジックを壊さず、 `PM2CD`
1436
+ を実用的な multi-process facade として成立させました。
1437
+
1438
+ 中心となる設計は、
1439
+
1440
+ ``` text
1441
+ PM2CDsingle worker x N
1442
+ +
1443
+ SharedContext
1444
+ +
1445
+ Communication x 1
1446
+ +
1447
+ TCP connection x 1
1448
+ ```
1449
+
1450
+ です。
1451
+
1452
+ これにより、単一 process の装置制御クラス、multi-process 制御、 FastAPI
1453
+ server/client を同じ Device/Ctrl
1454
+ 設計の上に積み上げる構成になっています。