@mokouliszt/mel-device 0.3.1 → 0.4.0

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.
package/README.ja.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  [English](README.md)
4
4
 
5
- 三菱電機 MELSEC / MXコントローラ および MELFAロボットコントローラのデバイス表記を、シリーズとCPU/コントローラ型名を含めて検証する、依存ライブラリなしのnpmパッケージです。
5
+ 三菱電機 MELSEC / MXコントローラ、MELFAロボットコントローラ、およびFRインバータのシーケンス機能のデバイス表記を、シリーズとCPU/コントローラ/インバータ型名を含めて検証する、依存ライブラリなしのnpmパッケージです。
6
6
 
7
- 対応シリーズは iQ-R、iQ-F、MX-R、MX-F、CR800-R/D/Qです。ES Modules と CommonJS の両方から利用できます。
7
+ 対応シリーズは iQ-R、iQ-F、MX-R、MX-F、CR800-R/D/Q、および FR-A800/A800 Plus/F800/E800 インバータ(シーケンス機能とSLMPデバイステーブルの両方)です。ES Modules と CommonJS の両方から利用できます。
8
8
 
9
9
  ## インストール
10
10
 
@@ -46,7 +46,7 @@ isValidDevice("U3E0\\G524287", {
46
46
 
47
47
  `analyzeDevice` は、単なる `true` / `false` ではなく、正規化後の型名、解釈したデバイス、失敗理由、参照マニュアルとページ、実機設定への依存有無を返します。入力欄のバリデーションにはこちらを推奨します。
48
48
 
49
- `model`を省略できるのは、`series`に`CR800-R`、`CR800-D`、`CR800-Q`のいずれかを直接指定した場合だけです。その他のシリーズでは従来どおり型名を指定してください。
49
+ `model`を省略できるのは、`series`に`CR800-R`、`CR800-D`、`CR800-Q`、`FR-A800`、`FR-A800-E`、`FR-F800`、`FR-E800`のような単一機種のショートハンドを直接指定した場合です。`iQ-R`、`iQ-F`、`MX-R`、`MX-F`、`CR800`、`FR800`では従来どおり型名を指定してください。
50
50
 
51
51
  ## 判定モード
52
52
 
@@ -59,7 +59,7 @@ PLCのM/D等はCPUパラメータで点数を変更できるため、型名だ
59
59
  | `configured` | 実機プロジェクトの厳密判定 | `configuredPoints` に渡した実際の点数で判定。可変デバイスの点数が未指定なら `MISSING_CONFIGURATION`。 |
60
60
  | `syntax` | エディタ入力途中・構文チェック | 型名が対応するデバイス文法のみを確認し、点数範囲を無視。 |
61
61
 
62
- CR800-R/D/Qのデバイス範囲はマニュアル上固定されているため、`default`、`maximum`、`configured`はいずれも同じ固定範囲を使用します。
62
+ CR800-R/D/QおよびFRインバータのデバイス範囲はマニュアル上固定されているため、これらのシリーズでは`configuredPoints`を参照しません。ただしインバータでは、`maximum`が後述のSERIAL依存のT/C 32点拡張と未割付のST点数を許可します。インバータが受け付ける点数指定は`configuredPoints.ST`のみです。
63
63
 
64
64
  ```js
65
65
  isValidDevice("D99", {
@@ -182,6 +182,123 @@ CR800-Qの表6-16にある`U3En\G512-G1023`は、表6-13の固定範囲`G10000-G
182
182
 
183
183
  定数(`K100`, `HFF`, 実数・文字列)は「デバイス」ではないため判定対象外です。ラベル、構造体メンバ、SLMPのバイナリデバイスコードも対象外です。
184
184
 
185
+ ## FRインバータ シーケンス機能
186
+
187
+ FR-A800/A800 Plus/F800/E800のシーケンス機能はデバイステーブルが1種類に固定されているため、シリーズを`FR800`、インバータのファミリを型名として扱います。単一ファミリのショートハンドを`series`に直接指定した場合は`model`を省略できます。
188
+
189
+ ```js
190
+ isValidDevice("X8F", { series: "FR-A800" }); // true(Xは16進で144点)
191
+ isValidDevice("X90", { series: "FR-A800" }); // false
192
+
193
+ analyzeDevice("P100", { series: "FR-E800" });
194
+ // valid: true, configurationDependent: true(PデバイスはSERIALの製造年月に依存)
195
+
196
+ analyzeDevice("P100", { series: "FR-A800" }).code; // "DEVICE_NOT_SUPPORTED"
197
+ ```
198
+
199
+ | デバイス | FR-A800 / FR-A800 Plus / FR-F800 | FR-E800 | 基数 |
200
+ | --- | --- | --- | --- |
201
+ | X、Y | `X0~X8F`、`Y0~Y8F`(各144点) | 同左 | 16進 |
202
+ | M | `M0~M127` | 同左 | 10進 |
203
+ | L | 点数なし(シーケンスパラメータで設定可能だがラッチしない) | 同左 | - |
204
+ | T、C | `0~15`、または下記拡張で`0~31` | `0~15` | 10進 |
205
+ | ST | 初期値0点。割付けにより`0~15`、下記拡張で`0~31`まで | 初期値0点。割付けにより`0~15`まで | 10進 |
206
+ | D | `D0~D255` | 同左 | 10進 |
207
+ | P | 記載なし | `P0~P127`、`P2048~P2175` | 10進 |
208
+ | SM、SD | `0~2047`(機能制限あり) | 同左 | 10進 |
209
+ | N | `N0~N14`(MC/MCRのネスティング) | 同左 | 10進 |
210
+
211
+ ### SERIAL(製造年月)依存機能
212
+
213
+ マニュアルには、インバータ定格名板のSERIALが示す製造年月によって対応可否が変わるデバイス機能が2つ記載されています。型名だけでは断定できないため、暗黙に許可せず別扱いで返します。
214
+
215
+ ```js
216
+ analyzeDevice("T16", { series: "FR-A800" });
217
+ // valid: false, code: "REQUIRES_SERIAL_SUPPORT", suggestedMode: "maximum"
218
+
219
+ isValidDevice("T16", { series: "FR-A800", mode: "maximum" }); // true(設定依存)
220
+ isValidDevice("T16", { series: "FR-A800", frFeatures: { extendedTimerPoints: true } }); // true(確定)
221
+ isValidDevice("T16", { series: "FR-A800", frFeatures: { extendedTimerPoints: false } }); // false
222
+ ```
223
+
224
+ | `frFeatures` | 対象 | 意味 |
225
+ | --- | --- | --- |
226
+ | `extendedTimerPoints` | FR-A800、FR-A800-E、FR-A800-CRN、FR-A800-LC、FR-F800、FR-F800-E | T/ST/Cの32点対応 |
227
+ | `pointerDevice` | FR-E800系 | Pデバイス256点対応 |
228
+
229
+ マニュアルが対応と記載していない型名に対して`true`を指定した場合は、範囲を黙って広げず`INVALID_FR_FEATURES`を返します。
230
+
231
+ ### 積算タイマは初期値0点
232
+
233
+ デバイス一覧では積算タイマSTの初期値が0点であり、シーケンスパラメータで割り付けて初めて使用できます。そのためPLCシリーズの点数変更可能デバイスと同じ扱いにしています。
234
+
235
+ ```js
236
+ analyzeDevice("ST0", { series: "FR-A800" });
237
+ // valid: false, code: "REQUIRES_CONFIGURATION", suggestedMode: "maximum"
238
+
239
+ isValidDevice("ST15", { series: "FR-A800", mode: "maximum" }); // true(設定依存)
240
+ isValidDevice("ST15", { series: "FR-A800", mode: "configured", configuredPoints: { ST: 16 } }); // true
241
+ isValidDevice("ST16", { series: "FR-A800", mode: "configured", configuredPoints: { ST: 16 } }); // false
242
+ ```
243
+
244
+ `configuredPoints.ST`の上限は16点、上記の32点拡張対応機種では32点です。
245
+
246
+ ### SLMPデバイステーブル
247
+
248
+ Ethernet仕様のインバータは、外部のSLMPクライアントに対してシーケンス機能とは別のデバイステーブルを公開しています。`frAccess: "slmp"`を指定するとそちらで判定し、結果にSLMPのデバイスコードとアクセス単位が付きます。
249
+
250
+ ```js
251
+ analyzeDevice("X7F", { series: "FR-A800-E", frAccess: "slmp" });
252
+ // valid: true, deviceCode: "H9C", deviceUnit: "bit"
253
+
254
+ isValidDevice("X80", { series: "FR-A800-E", frAccess: "slmp" }); // false(SLMPはH7Fまで)
255
+ isValidDevice("X80", { series: "FR-A800-E" }); // true (シーケンス機能はH8Fまで)
256
+ ```
257
+
258
+ | デバイス | コード | 単位 | 範囲 |
259
+ | --- | --- | --- | --- |
260
+ | SM | `H91` | ビット | `0~2047` |
261
+ | SD | `HA9` | ワード | `0~2047` |
262
+ | X | `H9C` | ビット | `H0~H7F` |
263
+ | Y | `H9D` | ビット | `H0~H7F` |
264
+ | M | `H90` | ビット | `0~127` |
265
+ | D | `HA8` | ワード | `0~255` |
266
+ | W | `HB4` | ワード | `0~8191`(FR-E800のEthernet仕様品のみ) |
267
+ | `TS` / `TC` / `TN` | `HC1` / `HC0` / `HC2` | ビット / ビット / ワード | `0~15` |
268
+ | `STS` / `STC` / `STN`(`SS` / `SC` / `SN`) | `HC7` / `HC6` / `HC8` | ビット / ビット / ワード | 初期値0点、割付けにより`0~15`まで |
269
+ | `CS` / `CC` / `CN` | `HC4` / `HC3` / `HC5` | ビット / ビット / ワード | `0~15` |
270
+
271
+ 補足:
272
+
273
+ - SLMPアクセスの記載はEthernet仕様品のみです。それ以外の型名では`MODEL_NOT_SUPPORTED`を返します。
274
+ - SLMPは接点・コイル・現在値を別デバイスとして指定するため、素の`T0`は無効とし、`TS0`/`TC0`/`TN0`を案内します。
275
+ - 積算タイマの各部は、FR-A800-E/F800-Eのマニュアルでは`STS`/`STC`/`STN`、FR-E800のマニュアルでは`SS`/`SC`/`SN`と表記されます。両方を受け付け、選択した型名のマニュアル表記を警告で示します。
276
+ - 両マニュアルのSLMP表はT/CとSTを16点で頭打ちにしているため、`extendedTimerPoints`では広がりません。
277
+ - リンクレジスタ`W`はパラメータとモニタデータに対応し、PLCシリーズと異なり10進です。`parseDevice("W999", { series: "FR-E800-E" })`のアドレスは999になります。
278
+ - P、N、L、桁指定はSLMPデバイステーブルに含まれません。
279
+
280
+ ### 桁指定と入力デバイス
281
+
282
+ 桁指定はビットデバイスX、Y、Mのみに記載があります。本パッケージは、指定した点数がデバイス範囲内に収まるかも判定します。
283
+
284
+ ```js
285
+ isValidDevice("K4X80", { series: "FR-A800" }); // true(X80~X8F)
286
+ analyzeDevice("K4X8C", { series: "FR-A800" }).code; // "DIGIT_RANGE_OVERFLOW"
287
+ analyzeDevice("K4D0", { series: "FR-A800" }).code; // "DIGIT_MODIFIER_NOT_SUPPORTED"
288
+ ```
289
+
290
+ 入力デバイスXは毎スキャン外部端子やネットワークからリフレッシュされるため、プログラムから駆動できません。`analyzeDevice`はCR800と同じフィールドでこれを返します。
291
+
292
+ ```js
293
+ analyzeDevice("X0", { series: "FR-A800", operation: "write" });
294
+ // valid: true, access: "read-only", operationAllowed: false
295
+ // warnings: インバータ運転状態制御にはSM1200/SM1255、SD1148/SD1149を使用する旨
296
+ ```
297
+
298
+ X/Yの`30H`以降はCC-Linkリモート入出力(`30H~3FH`)とEthernetインバータ間リンク入出力(`40H~8FH`)の領域であるため、領域名を含む警告とともに`configurationDependent: true`を返します。
299
+
300
+ ビット指定、間接指定、インデックス修飾、ローカルデバイス、ユニットアクセス、および`TS`/`TC`/`TN`形式の接点・コイル・現在値表記は、シーケンス機能のマニュアルに記載がないため保守的に無効とします。
301
+
185
302
  ## API
186
303
 
187
304
  ### `isValidDevice(value, options): boolean`
@@ -219,6 +336,7 @@ CR800-Qの表6-16にある`U3En\G512-G1023`は、表6-13の固定範囲`G10000-G
219
336
  - MX-R: MXR300-16/-32/-64、MXR500-128/-256
220
337
  - MX-F: MXF100系のマニュアル対象10機種(`MXF100` をファミリ別名として受付)
221
338
  - MELFA CR800: CR800-R、CR800-D、CR800-Q
339
+ - FRインバータ(`FR800`): FR-A800、FR-A800-E、FR-A800-P、FR-A800-CRN、FR-A800-LC、FR-A800-Plus、FR-F800、FR-F800-E、FR-E800、FR-E800-E、FR-E800-SCE、FR-E800-NC、FR-E806
222
340
 
223
341
  ## 資料
224
342
 
package/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  [日本語](README.ja.md)
4
4
 
5
- A dependency-free npm package for validating Mitsubishi Electric MELSEC/MX controller and MELFA robot controller device notation against a specified series and CPU/controller model.
5
+ A dependency-free npm package for validating Mitsubishi Electric MELSEC/MX controller, MELFA robot controller and FR inverter sequence-function device notation against a specified series and CPU/controller/inverter model.
6
6
 
7
- Supported series are iQ-R, iQ-F, MX-R, MX-F, and CR800-R/D/Q. The package supports both ES Modules and CommonJS.
7
+ Supported series are iQ-R, iQ-F, MX-R, MX-F, CR800-R/D/Q, and the FR-A800/A800 Plus/F800/E800 inverters, covering both their sequence function and their SLMP device table. The package supports both ES Modules and CommonJS.
8
8
 
9
9
  ## Installation
10
10
 
@@ -46,7 +46,7 @@ isValidDevice("U3E0\\G524287", {
46
46
 
47
47
  Instead of returning only `true` or `false`, `analyzeDevice` reports the normalized model, parsed device, reason for failure, referenced manual and page, and whether the result depends on the actual hardware configuration. It is the recommended API for validating user input.
48
48
 
49
- The `model` option may be omitted only when `series` is set directly to `CR800-R`, `CR800-D`, or `CR800-Q`. A model must still be specified for all other series.
49
+ The `model` option may be omitted when `series` is set directly to a single-model shorthand such as `CR800-R`, `CR800-D`, `CR800-Q`, `FR-A800`, `FR-A800-E`, `FR-F800`, or `FR-E800`. A model must still be specified for `iQ-R`, `iQ-F`, `MX-R`, `MX-F`, `CR800`, and `FR800`.
50
50
 
51
51
  ## Validation modes
52
52
 
@@ -59,7 +59,7 @@ The number of available points for PLC devices such as M and D can be changed in
59
59
  | `configured` | Exact validation for an actual project | Validates against the actual point counts supplied in `configuredPoints`. Returns `MISSING_CONFIGURATION` if the point count for a configurable device is omitted. |
60
60
  | `syntax` | In-progress editor input and syntax checking | Checks only whether the model supports the device syntax and ignores point-count ranges. |
61
61
 
62
- The device ranges for CR800-R/D/Q are fixed in the manuals, so `default`, `maximum`, and `configured` all use the same fixed ranges.
62
+ The device ranges for CR800-R/D/Q and for the FR inverter sequence function are fixed in the manuals, so `configuredPoints` is ignored for those series. `maximum` still differs for the inverters, because it opts into the SERIAL-dependent 32-point T/C extension and the unallocated ST points described below. `configuredPoints.ST` is the one point count the inverters accept.
63
63
 
64
64
  ```js
65
65
  isValidDevice("D99", {
@@ -182,6 +182,123 @@ Bit specifications, digit specifications, indirect specifications, index modific
182
182
 
183
183
  Constants (`K100`, `HFF`, real numbers, and strings) are outside the validation scope because they are not devices. Labels, structure members, and SLMP binary device codes are also outside the scope.
184
184
 
185
+ ## FR inverter sequence function
186
+
187
+ The FR-A800/A800 Plus/F800/E800 sequence function has one fixed device table, so the series is `FR800` and the inverter family is the model. Single-family shorthands are accepted as `series`, in which case `model` may be omitted.
188
+
189
+ ```js
190
+ isValidDevice("X8F", { series: "FR-A800" }); // true (X is hexadecimal, 144 points)
191
+ isValidDevice("X90", { series: "FR-A800" }); // false
192
+
193
+ analyzeDevice("P100", { series: "FR-E800" });
194
+ // valid: true, configurationDependent: true (the P device depends on the SERIAL production month)
195
+
196
+ analyzeDevice("P100", { series: "FR-A800" }).code; // "DEVICE_NOT_SUPPORTED"
197
+ ```
198
+
199
+ | Device | FR-A800 / FR-A800 Plus / FR-F800 | FR-E800 | Radix |
200
+ | --- | --- | --- | --- |
201
+ | X, Y | `X0-X8F`, `Y0-Y8F` (144 points each) | same | hexadecimal |
202
+ | M | `M0-M127` | same | decimal |
203
+ | L | no points (a latch range can be set but nothing latches) | same | - |
204
+ | T, C | `0-15`, or `0-31` with the extension below | `0-15` | decimal |
205
+ | ST | 0 points by default; allocatable up to `0-15`, or `0-31` with the extension below | 0 points by default; allocatable up to `0-15` | decimal |
206
+ | D | `D0-D255` | same | decimal |
207
+ | P | not listed | `P0-P127`, `P2048-P2175` | decimal |
208
+ | SM, SD | `0-2047` (not every number has a function) | same | decimal |
209
+ | N | `N0-N14` (MC/MCR nesting) | same | decimal |
210
+
211
+ ### SERIAL-dependent features
212
+
213
+ The manual lists two device features whose availability depends on the inverter production month printed on the SERIAL plate. Because the model name alone cannot decide them, they are reported separately instead of being silently allowed.
214
+
215
+ ```js
216
+ analyzeDevice("T16", { series: "FR-A800" });
217
+ // valid: false, code: "REQUIRES_SERIAL_SUPPORT", suggestedMode: "maximum"
218
+
219
+ isValidDevice("T16", { series: "FR-A800", mode: "maximum" }); // true (configurationDependent)
220
+ isValidDevice("T16", { series: "FR-A800", frFeatures: { extendedTimerPoints: true } }); // true (asserted)
221
+ isValidDevice("T16", { series: "FR-A800", frFeatures: { extendedTimerPoints: false } }); // false
222
+ ```
223
+
224
+ | `frFeatures` | Applies to | Meaning |
225
+ | --- | --- | --- |
226
+ | `extendedTimerPoints` | FR-A800, FR-A800-E, FR-A800-CRN, FR-A800-LC, FR-F800, FR-F800-E | 32-point T/ST/C extension |
227
+ | `pointerDevice` | FR-E800 family | 256-point P pointer device |
228
+
229
+ Setting a feature to `true` for a model the manual does not list returns `INVALID_FR_FEATURES` rather than quietly widening the range.
230
+
231
+ ### The accumulating timer starts with no points
232
+
233
+ The device list gives the accumulating timer ST a default of 0 points, so it is only usable once the sequence parameters allocate it. It is therefore treated like the configurable devices of the PLC series rather than like a fixed range.
234
+
235
+ ```js
236
+ analyzeDevice("ST0", { series: "FR-A800" });
237
+ // valid: false, code: "REQUIRES_CONFIGURATION", suggestedMode: "maximum"
238
+
239
+ isValidDevice("ST15", { series: "FR-A800", mode: "maximum" }); // true (configurationDependent)
240
+ isValidDevice("ST15", { series: "FR-A800", mode: "configured", configuredPoints: { ST: 16 } }); // true
241
+ isValidDevice("ST16", { series: "FR-A800", mode: "configured", configuredPoints: { ST: 16 } }); // false
242
+ ```
243
+
244
+ The ceiling for `configuredPoints.ST` is 16 points, or 32 on a model that has the extension above.
245
+
246
+ ### SLMP device table
247
+
248
+ The Ethernet inverters expose a different device table to external SLMP clients. Pass `frAccess: "slmp"` to validate against it; the result then also carries the SLMP device code and access unit.
249
+
250
+ ```js
251
+ analyzeDevice("X7F", { series: "FR-A800-E", frAccess: "slmp" });
252
+ // valid: true, deviceCode: "H9C", deviceUnit: "bit"
253
+
254
+ isValidDevice("X80", { series: "FR-A800-E", frAccess: "slmp" }); // false (the SLMP table stops at H7F)
255
+ isValidDevice("X80", { series: "FR-A800-E" }); // true (the sequence map runs to H8F)
256
+ ```
257
+
258
+ | Device | Code | Unit | Range |
259
+ | --- | --- | --- | --- |
260
+ | SM | `H91` | bit | `0-2047` |
261
+ | SD | `HA9` | word | `0-2047` |
262
+ | X | `H9C` | bit | `H0-H7F` |
263
+ | Y | `H9D` | bit | `H0-H7F` |
264
+ | M | `H90` | bit | `0-127` |
265
+ | D | `HA8` | word | `0-255` |
266
+ | W | `HB4` | word | `0-8191` (FR-E800 Ethernet models only) |
267
+ | `TS` / `TC` / `TN` | `HC1` / `HC0` / `HC2` | bit / bit / word | `0-15` |
268
+ | `STS` / `STC` / `STN` (`SS` / `SC` / `SN`) | `HC7` / `HC6` / `HC8` | bit / bit / word | 0 points by default, allocatable up to `0-15` |
269
+ | `CS` / `CC` / `CN` | `HC4` / `HC3` / `HC5` | bit / bit / word | `0-15` |
270
+
271
+ Points worth knowing:
272
+
273
+ - SLMP access is documented for the Ethernet models only. Any other model returns `MODEL_NOT_SUPPORTED`.
274
+ - The SLMP table addresses the contact, coil, and current value separately, so a bare `T0` is rejected with a message naming `TS0`, `TC0`, and `TN0`.
275
+ - The accumulating-timer parts are spelled `STS`/`STC`/`STN` in the FR-A800-E/F800-E manual and `SS`/`SC`/`SN` in the FR-E800 manual. Both spellings are accepted, and a warning names the spelling the selected model's manual uses.
276
+ - Both SLMP tables cap T/C and ST at 16 points, so `extendedTimerPoints` does not widen them.
277
+ - The link register `W` maps to parameters and monitor data and is decimal, not hexadecimal as on the PLC series. `parseDevice("W999", { series: "FR-E800-E" })` gives address 999.
278
+ - P, N, L, and digit designation are not part of the SLMP device table.
279
+
280
+ ### Digit designation and input devices
281
+
282
+ Digit designation is documented for the bit devices X, Y, and M only, and the package also checks that the designated points fit inside the device range.
283
+
284
+ ```js
285
+ isValidDevice("K4X80", { series: "FR-A800" }); // true (X80-X8F)
286
+ analyzeDevice("K4X8C", { series: "FR-A800" }).code; // "DIGIT_RANGE_OVERFLOW"
287
+ analyzeDevice("K4D0", { series: "FR-A800" }).code; // "DIGIT_MODIFIER_NOT_SUPPORTED"
288
+ ```
289
+
290
+ Input device X is refreshed from the terminal or network on every scan, so a program cannot drive it. `analyzeDevice` reports this through the same fields used for CR800:
291
+
292
+ ```js
293
+ analyzeDevice("X0", { series: "FR-A800", operation: "write" });
294
+ // valid: true, access: "read-only", operationAllowed: false
295
+ // warnings: use SM1200/SM1255 and SD1148/SD1149 for inverter operation control
296
+ ```
297
+
298
+ X/Y addresses from `30H` upward belong to the CC-Link remote area (`30H-3FH`) and the Ethernet inverter-to-inverter link area (`40H-8FH`), so they return `configurationDependent: true` with a warning naming the area.
299
+
300
+ Bit selection, indirect specification, index modification, local devices, module access, and the `TS`/`TC`/`TN` style contact/coil/current notation are not documented for the inverter sequence function and are conservatively rejected.
301
+
185
302
  ## API
186
303
 
187
304
  ### `isValidDevice(value, options): boolean`
@@ -219,6 +336,7 @@ Returns the list of models covered by the manuals.
219
336
  - MX-R: MXR300-16/-32/-64 and MXR500-128/-256
220
337
  - MX-F: the 10 models covered by the MXF100-series manual (`MXF100` is accepted as a family alias)
221
338
  - MELFA CR800: CR800-R, CR800-D, and CR800-Q
339
+ - FR inverters (`FR800`): FR-A800, FR-A800-E, FR-A800-P, FR-A800-CRN, FR-A800-LC, FR-A800-Plus, FR-F800, FR-F800-E, FR-E800, FR-E800-E, FR-E800-SCE, FR-E800-NC, and FR-E806
222
340
 
223
341
  ## References
224
342
 
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- const SERIES = ["iQ-R", "iQ-F", "MX-R", "MX-F", "CR800"];
3
+ const SERIES = ["iQ-R", "iQ-F", "MX-R", "MX-F", "CR800", "FR800"];
4
4
 
5
5
  const IQ_R_MODELS = [
6
6
  "R00CPU", "R01CPU", "R02CPU", "R04CPU", "R04ENCPU", "R08CPU", "R08ENCPU",
@@ -13,13 +13,19 @@ const MX_F_MODELS = [
13
13
  "MXF100-Y32N", "MXF100-Y32P", "MXF100-H32N", "MXF100-H32P", "MXF100-Y16R"
14
14
  ];
15
15
  const CR800_MODELS = ["CR800-R", "CR800-D", "CR800-Q"];
16
+ const FR800_MODELS = [
17
+ "FR-A800", "FR-A800-E", "FR-A800-P", "FR-A800-CRN", "FR-A800-LC", "FR-A800-Plus",
18
+ "FR-F800", "FR-F800-E",
19
+ "FR-E800", "FR-E800-E", "FR-E800-SCE", "FR-E800-NC", "FR-E806"
20
+ ];
16
21
 
17
22
  const MODEL_LISTS = {
18
23
  "iQ-R": IQ_R_MODELS,
19
24
  "iQ-F": IQ_F_MODELS,
20
25
  "MX-R": MX_R_MODELS,
21
26
  "MX-F": MX_F_MODELS,
22
- CR800: CR800_MODELS
27
+ CR800: CR800_MODELS,
28
+ FR800: FR800_MODELS
23
29
  };
24
30
 
25
31
  const BIT_PREFIXES = new Set(["X", "DX", "Y", "DY", "M", "L", "B", "F", "SB", "V", "S", "SM", "FX", "FY"]);
@@ -109,7 +115,8 @@ const SOURCES = {
109
115
  "iQ-R": { manual: "SH-082487-J", pages: [65, 408, 409, 410, 435, 436] },
110
116
  "MX-R": { manual: "SH-082640-D", pages: [350, 351, 352, 380, 381] },
111
117
  "MX-F": { manual: "SH-082633-E", pages: [277, 416, 417, 418] },
112
- CR800: { manual: "BFP-A3477-AB", pages: [489, 490, 491, 618, 619, 654, 655, 656, 657] }
118
+ CR800: { manual: "BFP-A3477-AB", pages: [489, 490, 491, 618, 619, 654, 655, 656, 657] },
119
+ FR800: { manual: "IB-0600491-Q", pages: [8, 11, 22, 23, 24, 25, 26, 27, 28, 107, 125, 126, 157] }
113
120
  };
114
121
 
115
122
  // CPU buffer memory access devices (U3En\G, U3En\HG).
@@ -166,6 +173,99 @@ const CR800_ALLOCATION_LABELS = {
166
173
  ccLinkIef: "CC-Link IE Field"
167
174
  };
168
175
 
176
+ // Inverter sequence function (FR-A800/A800 Plus/F800/E800).
177
+ // IB-0600491-Q p.107 (3.9.1 device list) fixes every point count; p.8 lists the
178
+ // features whose availability depends on the SERIAL production date; p.22-28
179
+ // give the X/Y device map, p.125-126 the K1-K8 digit designation rules for
180
+ // X/Y/M, and p.157 the N0-N14 master-control nesting range.
181
+ const FR_COMMON_LIMITS = { X: 0x8f, Y: 0x8f, M: 127, D: 255, SM: 2047, SD: 2047, N: 14 };
182
+ const FR_POINTER_RANGES = [[0, 127], [2048, 2175]];
183
+ const FR_DIGIT_PREFIXES = new Set(["X", "Y", "M"]);
184
+ const FR_FEATURE_NAMES = new Set(["extendedTimerPoints", "pointerDevice"]);
185
+
186
+ // timerPoints: [base, extended]. The 32-point T/ST/C extension is only listed
187
+ // for FR-A800 (excluding FR-A800-P), FR-A800 Plus (FR-A800-CRN/LC) and FR-F800,
188
+ // and only from the January 2021 production month onward (p.8).
189
+ // timerPoints: [base, SERIAL-extended] for T and C. ST always defaults to 0
190
+ // points (p.11 note *8) and is allocated by the sequence parameters up to the
191
+ // same figure. slmp/linkRegister follow the Ethernet manuals; stSpelling is the
192
+ // accumulating-timer spelling each SLMP table uses.
193
+ const FR800_RULES = {
194
+ "FR-A800": { timerPoints: [16, 32], pointer: false, slmp: false, linkRegister: false, stSpelling: "ST" },
195
+ "FR-A800-E": { timerPoints: [16, 32], pointer: false, slmp: true, linkRegister: false, stSpelling: "ST" },
196
+ "FR-A800-P": { timerPoints: [16, 16], pointer: false, slmp: false, linkRegister: false, stSpelling: "ST" },
197
+ "FR-A800-CRN": { timerPoints: [16, 32], pointer: false, slmp: false, linkRegister: false, stSpelling: "ST" },
198
+ "FR-A800-LC": { timerPoints: [16, 32], pointer: false, slmp: false, linkRegister: false, stSpelling: "ST" },
199
+ "FR-A800-Plus": { timerPoints: [16, 16], pointer: false, slmp: false, linkRegister: false, stSpelling: "ST" },
200
+ "FR-F800": { timerPoints: [16, 32], pointer: false, slmp: false, linkRegister: false, stSpelling: "ST" },
201
+ "FR-F800-E": { timerPoints: [16, 32], pointer: false, slmp: true, linkRegister: false, stSpelling: "ST" },
202
+ "FR-E800": { timerPoints: [16, 16], pointer: true, slmp: false, linkRegister: false, stSpelling: "S" },
203
+ "FR-E800-E": { timerPoints: [16, 16], pointer: true, slmp: true, linkRegister: true, stSpelling: "S" },
204
+ "FR-E800-SCE": { timerPoints: [16, 16], pointer: true, slmp: true, linkRegister: true, stSpelling: "S" },
205
+ "FR-E800-NC": { timerPoints: [16, 16], pointer: true, slmp: false, linkRegister: false, stSpelling: "S" },
206
+ "FR-E806": { timerPoints: [16, 16], pointer: true, slmp: true, linkRegister: true, stSpelling: "S" }
207
+ };
208
+
209
+ // SLMP device codes and ranges (IB-0600627-D p.37, IB-0600870-S p.132-133).
210
+ // The SLMP tables cap T/C at 16 points and ST at 16 points regardless of the
211
+ // 32-point sequence-function extension, and cut X/Y at H7F rather than H8F.
212
+ const FR_SLMP_DEVICES = {
213
+ SM: { code: "H91", unit: "bit", max: 2047 },
214
+ SD: { code: "HA9", unit: "word", max: 2047 },
215
+ X: { code: "H9C", unit: "bit", max: 0x7f },
216
+ Y: { code: "H9D", unit: "bit", max: 0x7f },
217
+ M: { code: "H90", unit: "bit", max: 127 },
218
+ D: { code: "HA8", unit: "word", max: 255 },
219
+ W: { code: "HB4", unit: "word", max: 8191, requires: "linkRegister" },
220
+ TS: { code: "HC1", unit: "bit", max: 15 },
221
+ TC: { code: "HC0", unit: "bit", max: 15 },
222
+ TN: { code: "HC2", unit: "word", max: 15 },
223
+ STS: { code: "HC7", unit: "bit", allocated: true },
224
+ STC: { code: "HC6", unit: "bit", allocated: true },
225
+ STN: { code: "HC8", unit: "word", allocated: true },
226
+ CS: { code: "HC4", unit: "bit", max: 15 },
227
+ CC: { code: "HC3", unit: "bit", max: 15 },
228
+ CN: { code: "HC5", unit: "word", max: 15 }
229
+ };
230
+ const FR_SLMP_ST_MAX = 16;
231
+ const FR_SLMP_SPELLING = { STS: "SS", STC: "SC", STN: "SN" };
232
+
233
+ // The accumulating-timer parts are spelled STS/STC/STN in the FR-A800-E/F800-E
234
+ // manual and SS/SC/SN in the FR-E800 manual; both spellings are accepted.
235
+ const FR_TIMER_ALIAS = {
236
+ TS: ["T", "contact"], TC: ["T", "coil"], TN: ["T", "current"],
237
+ STS: ["ST", "contact"], STC: ["ST", "coil"], STN: ["ST", "current"],
238
+ SS: ["ST", "contact"], SC: ["ST", "coil"], SN: ["ST", "current"],
239
+ CS: ["C", "contact"], CC: ["C", "coil"], CN: ["C", "current"]
240
+ };
241
+
242
+ const FR_SERIES_ALIASES = {
243
+ FR800: null, FR8: null,
244
+ FRA800: "FR-A800", FRA800E: "FR-A800-E", FRA800P: "FR-A800-P",
245
+ FRA800CRN: "FR-A800-CRN", FRA800LC: "FR-A800-LC", FRA800PLUS: "FR-A800-Plus",
246
+ FRF800: "FR-F800", FRF800E: "FR-F800-E",
247
+ FRE800: "FR-E800", FRE800E: "FR-E800-E", FRE800SCE: "FR-E800-SCE",
248
+ FRE800NC: "FR-E800-NC", FRE806: "FR-E806"
249
+ };
250
+
251
+ const FR_MODEL_ALIASES = {
252
+ A800: "FR-A800", A800E: "FR-A800-E", A800P: "FR-A800-P",
253
+ A800CRN: "FR-A800-CRN", A800LC: "FR-A800-LC", A800PLUS: "FR-A800-Plus",
254
+ CRN: "FR-A800-CRN", LC: "FR-A800-LC", PLUS: "FR-A800-Plus",
255
+ F800: "FR-F800", F800E: "FR-F800-E",
256
+ E800: "FR-E800", E800E: "FR-E800-E", E800SCE: "FR-E800-SCE",
257
+ E800NC: "FR-E800-NC", E806: "FR-E806"
258
+ };
259
+
260
+ // X/Y map areas (p.22-28). Everything from X30/Y30 upward only exists while the
261
+ // matching network is configured, so those addresses stay configuration-dependent.
262
+ const FR_IO_AREAS = [
263
+ [0x00, 0x1f, "external and built-in option I/O", false],
264
+ [0x20, 0x2f, "inverter system I/O", false],
265
+ [0x30, 0x3f, "CC-Link remote I/O", true],
266
+ [0x40, 0x8f, "Ethernet inverter-to-inverter link I/O", true]
267
+ ];
268
+
169
269
  class MelDeviceError extends Error {
170
270
  constructor(message, result) {
171
271
  super(message);
@@ -175,22 +275,35 @@ class MelDeviceError extends Error {
175
275
  }
176
276
  }
177
277
 
278
+ function compactName(value) {
279
+ return value.normalize("NFKC").trim().toUpperCase().replace(/[\s_-]/g, "");
280
+ }
281
+
178
282
  function canonicalSeries(value) {
179
283
  if (typeof value !== "string") return null;
180
- const compact = value.normalize("NFKC").trim().toUpperCase().replace(/[\s_-]/g, "");
284
+ const compact = compactName(value);
285
+ if (Object.prototype.hasOwnProperty.call(FR_SERIES_ALIASES, compact)) return "FR800";
181
286
  return ({ IQR: "iQ-R", IQF: "iQ-F", MXR: "MX-R", MXF: "MX-F", CR800: "CR800", CR800R: "CR800", CR800D: "CR800", CR800Q: "CR800" })[compact] || null;
182
287
  }
183
288
 
184
289
  function cr800ModelFromSeries(value) {
185
290
  if (typeof value !== "string") return null;
186
- const compact = value.normalize("NFKC").trim().toUpperCase().replace(/[\s_-]/g, "");
187
- return ({ CR800R: "CR800-R", CR800D: "CR800-D", CR800Q: "CR800-Q" })[compact] || null;
291
+ return ({ CR800R: "CR800-R", CR800D: "CR800-D", CR800Q: "CR800-Q" })[compactName(value)] || null;
292
+ }
293
+
294
+ function fr800ModelFromSeries(value) {
295
+ if (typeof value !== "string") return null;
296
+ return FR_SERIES_ALIASES[compactName(value)] || null;
297
+ }
298
+
299
+ function modelFromSeries(value) {
300
+ return cr800ModelFromSeries(value) || fr800ModelFromSeries(value);
188
301
  }
189
302
 
190
303
  function normalizeModel(series, value) {
191
304
  const s = canonicalSeries(series);
192
305
  if (!s) return null;
193
- if (s === "CR800" && value == null) return cr800ModelFromSeries(series);
306
+ if ((s === "CR800" || s === "FR800") && value == null) return modelFromSeries(series);
194
307
  if (typeof value !== "string") return null;
195
308
  const model = value.normalize("NFKC").trim().toUpperCase().replace(/\s+/g, "");
196
309
  if (s === "iQ-R") {
@@ -210,16 +323,20 @@ function normalizeModel(series, value) {
210
323
  return MX_R_MODELS.includes(model) ? model : null;
211
324
  }
212
325
  if (s === "CR800") {
213
- const compact = String(value).normalize("NFKC").trim().toUpperCase().replace(/[\s_-]/g, "");
326
+ const compact = compactName(String(value));
214
327
  return ({ R: "CR800-R", D: "CR800-D", Q: "CR800-Q", CR800R: "CR800-R", CR800D: "CR800-D", CR800Q: "CR800-Q" })[compact] || null;
215
328
  }
329
+ if (s === "FR800") {
330
+ const compact = compactName(String(value));
331
+ return FR_SERIES_ALIASES[compact] || FR_MODEL_ALIASES[compact] || null;
332
+ }
216
333
  if (model === "MXF100") return model;
217
334
  return MX_F_MODELS.includes(model) ? model : null;
218
335
  }
219
336
 
220
337
  function getSupportedModels(series) {
221
338
  const s = canonicalSeries(series);
222
- const inferred = cr800ModelFromSeries(series);
339
+ const inferred = modelFromSeries(series);
223
340
  if (inferred) return [inferred];
224
341
  return s ? [...MODEL_LISTS[s]] : [];
225
342
  }
@@ -246,6 +363,9 @@ function invalid(input, series, model, mode, code, message, extra = {}) {
246
363
  }
247
364
 
248
365
  function radixFor(series, prefix) {
366
+ // Both inverter manuals print X/Y in hexadecimal and every other device,
367
+ // including the link register W, in decimal.
368
+ if (series === "FR800") return prefix === "X" || prefix === "Y" ? 16 : 10;
249
369
  const radix = RADIX[prefix];
250
370
  if (radix === "series-x") return series === "iQ-F" ? 8 : 16;
251
371
  return radix || 10;
@@ -259,13 +379,14 @@ function parseNumber(text, radix) {
259
379
  }
260
380
 
261
381
  function parseDirect(text, series) {
262
- const timerPrefixes = Object.keys(TIMER_ALIAS).sort((a, b) => b.length - a.length);
382
+ const aliases = series === "FR800" ? FR_TIMER_ALIAS : TIMER_ALIAS;
383
+ const timerPrefixes = Object.keys(aliases).sort((a, b) => b.length - a.length);
263
384
  for (const alias of timerPrefixes) {
264
385
  if (!text.startsWith(alias)) continue;
265
386
  const addressText = text.slice(alias.length);
266
387
  const address = parseNumber(addressText, 10);
267
388
  if (address !== null) {
268
- const [prefix, timerPart] = TIMER_ALIAS[alias];
389
+ const [prefix, timerPart] = aliases[alias];
269
390
  return { kind: "direct", prefix, notationPrefix: alias, address, addressText, radix: 10, timerPart };
270
391
  }
271
392
  }
@@ -582,6 +703,265 @@ function analyzeCr800(value, parsed, series, model, mode, normalized, operation,
582
703
  };
583
704
  }
584
705
 
706
+ function validateFrFeatures(features, model) {
707
+ if (features === undefined) return null;
708
+ if (!features || typeof features !== "object" || Array.isArray(features)) {
709
+ return "frFeatures must be an object when supplied.";
710
+ }
711
+ for (const [name, state] of Object.entries(features)) {
712
+ if (!FR_FEATURE_NAMES.has(name)) return `Unknown FR feature: ${name}.`;
713
+ if (typeof state !== "boolean") return `frFeatures.${name} must be boolean.`;
714
+ }
715
+ const rule = FR800_RULES[model];
716
+ if (features.extendedTimerPoints === true && rule.timerPoints[0] === rule.timerPoints[1]) {
717
+ return `${model} is not listed as supporting the 32-point T/ST/C extension.`;
718
+ }
719
+ if (features.pointerDevice === true && !rule.pointer) {
720
+ return `${model} is not listed as supporting the P pointer device.`;
721
+ }
722
+ return null;
723
+ }
724
+
725
+ function frTimerCeiling(model, mode, features) {
726
+ const [base, extended] = FR800_RULES[model].timerPoints;
727
+ const declared = typeof features.extendedTimerPoints === "boolean" ? features.extendedTimerPoints : null;
728
+ if (declared !== null) return { points: declared ? extended : base, dependent: false };
729
+ if (mode === "maximum") return { points: extended, dependent: extended !== base };
730
+ return { points: base, dependent: false, serialExtendedTo: extended !== base ? extended : null };
731
+ }
732
+
733
+ // Sequence-function ranges (IB-0600491-Q p.107). ST has no points until the
734
+ // sequence parameters allocate them, so it behaves like the other configurable
735
+ // devices in this package rather than like a fixed range.
736
+ function frSequenceRange(prefix, model, mode, options, features) {
737
+ const rule = FR800_RULES[model];
738
+ if (prefix === "T" || prefix === "C") {
739
+ const ceiling = frTimerCeiling(model, mode, features);
740
+ return {
741
+ ranges: [[0, ceiling.points - 1]],
742
+ dependent: ceiling.dependent,
743
+ serialExtendedTo: ceiling.serialExtendedTo == null ? null : ceiling.serialExtendedTo - 1
744
+ };
745
+ }
746
+ if (prefix === "ST") {
747
+ const ceiling = frTimerCeiling(model, mode, features);
748
+ if (mode === "configured") {
749
+ const points = options.configuredPoints && options.configuredPoints.ST;
750
+ if (!Number.isInteger(points) || points < 0) return { missingConfiguration: true };
751
+ if (points > ceiling.points) return { invalidConfiguration: true, ceiling: ceiling.points };
752
+ return { ranges: [[0, points - 1]], dependent: false };
753
+ }
754
+ if (mode === "maximum") return { ranges: [[0, ceiling.points - 1]], dependent: true, allocated: true };
755
+ return { ranges: [], dependent: false, allocatableTo: ceiling.points - 1 };
756
+ }
757
+ if (prefix === "P") {
758
+ if (!rule.pointer) return null;
759
+ return { ranges: FR_POINTER_RANGES, dependent: features.pointerDevice !== true };
760
+ }
761
+ const limit = FR_COMMON_LIMITS[prefix];
762
+ if (limit === undefined) return null;
763
+ return { ranges: [[0, limit]], dependent: false };
764
+ }
765
+
766
+ // SLMP ranges (IB-0600627-D p.37, IB-0600870-S p.132-133).
767
+ function frSlmpRange(entry, model, mode, options) {
768
+ if (!entry.allocated) return { ranges: [[0, entry.max]], dependent: false };
769
+ if (mode === "configured") {
770
+ const points = options.configuredPoints && options.configuredPoints.ST;
771
+ if (!Number.isInteger(points) || points < 0) return { missingConfiguration: true };
772
+ if (points > FR_SLMP_ST_MAX) return { invalidConfiguration: true, ceiling: FR_SLMP_ST_MAX };
773
+ return { ranges: [[0, points - 1]], dependent: false };
774
+ }
775
+ if (mode === "maximum") return { ranges: [[0, FR_SLMP_ST_MAX - 1]], dependent: true, allocated: true };
776
+ return { ranges: [], dependent: false, allocatableTo: FR_SLMP_ST_MAX - 1 };
777
+ }
778
+
779
+ function frIoArea(address) {
780
+ return FR_IO_AREAS.find(([start, end]) => address >= start && address <= end) || null;
781
+ }
782
+
783
+ function analyzeFr(value, parsed, series, model, mode, normalized, operation, options) {
784
+ const features = options.frFeatures || {};
785
+ const access = options.frAccess || "sequence";
786
+ const rule = FR800_RULES[model];
787
+ const fail = (code, message, extra = {}) => invalid(value, series, model, mode, code, message, { normalized, parsed, ...extra });
788
+
789
+ if (parsed.kind !== "direct") {
790
+ return fail("DEVICE_NOT_SUPPORTED", `${parsed.kind} device notation is not listed for the inverter.`);
791
+ }
792
+ if (parsed.local || parsed.indirect || parsed.bit !== null || parsed.index !== null) {
793
+ return fail("MODIFIER_NOT_SUPPORTED", "The inverter manuals do not document bit selection, indirect specification, index modification, or local devices.");
794
+ }
795
+ if (prefixIsLatchRelay(parsed)) {
796
+ return fail("DEVICE_NOT_SUPPORTED", "The latch relay L has no points on the inverter; the sequence parameters accept a latch range but nothing is latched.");
797
+ }
798
+
799
+ if (access === "slmp") return analyzeFrSlmp(value, parsed, series, model, mode, normalized, operation, options, rule, fail);
800
+
801
+ if (parsed.timerPart) {
802
+ return fail("DEVICE_NOT_SUPPORTED", `${parsed.notationPrefix} contact/coil/current notation belongs to the SLMP device table, not to the sequence program; use ${parsed.prefix}${parsed.addressText}, or pass frAccess: "slmp".`);
803
+ }
804
+ const prefix = parsed.prefix;
805
+ if (parsed.digit !== null && !FR_DIGIT_PREFIXES.has(prefix)) {
806
+ return fail("DIGIT_MODIFIER_NOT_SUPPORTED", "K1-K8 digit designation is documented only for the bit devices X, Y, and M.");
807
+ }
808
+
809
+ const range = frSequenceRange(prefix, model, mode, options, features);
810
+ if (!range) {
811
+ return fail("DEVICE_NOT_SUPPORTED", `${prefix} is not listed as an inverter sequence function device for ${model}.`);
812
+ }
813
+ if (range.missingConfiguration) {
814
+ return fail("MISSING_CONFIGURATION", "configuredPoints.ST is required in configured mode; the accumulating timer defaults to 0 points.");
815
+ }
816
+ if (range.invalidConfiguration) {
817
+ return fail("INVALID_CONFIGURATION", `configuredPoints.ST exceeds the ${range.ceiling}-point maximum documented for ${model}.`);
818
+ }
819
+
820
+ return frResult(value, parsed, series, model, mode, normalized, operation, range, {
821
+ label: "inverter sequence function",
822
+ allocationHint: 'The accumulating timer ST defaults to 0 points; allocate it in the sequence parameters, then use mode "maximum" or configuredPoints.ST.',
823
+ checkDigit: true,
824
+ ioAreas: true
825
+ });
826
+ }
827
+
828
+ function prefixIsLatchRelay(parsed) {
829
+ return parsed.prefix === "L" && !parsed.timerPart;
830
+ }
831
+
832
+ function analyzeFrSlmp(value, parsed, series, model, mode, normalized, operation, options, rule, fail) {
833
+ if (!rule.slmp) {
834
+ return fail("MODEL_NOT_SUPPORTED", `SLMP device access is documented for the Ethernet inverters (FR-A800-E, FR-F800-E, FR-E800-E, FR-E800-SCE, FR-E806), not for ${model}.`);
835
+ }
836
+ if (parsed.digit !== null) {
837
+ return fail("DIGIT_MODIFIER_NOT_SUPPORTED", "SLMP addresses a head device and a point count; K1-K8 digit designation is not part of the device specification.");
838
+ }
839
+
840
+ const key = parsed.timerPart ? `${parsed.prefix}${{ contact: "S", coil: "C", current: "N" }[parsed.timerPart]}` : parsed.prefix;
841
+ const entry = FR_SLMP_DEVICES[key];
842
+ if (!entry) {
843
+ const parts = { T: ["TS", "TC", "TN"], ST: rule.stSpelling === "S" ? ["SS", "SC", "SN"] : ["STS", "STC", "STN"], C: ["CS", "CC", "CN"] }[parsed.prefix];
844
+ const hint = parts
845
+ ? ` The SLMP table addresses the contact, coil, and current value separately, so use ${parts.map(part => `${part}${parsed.addressText}`).join(", ")}.`
846
+ : "";
847
+ return fail("DEVICE_NOT_SUPPORTED", `${parsed.notationPrefix || parsed.prefix} is not listed in the SLMP device table.${hint}`);
848
+ }
849
+ if (entry.requires && !rule[entry.requires]) {
850
+ return fail("DEVICE_NOT_SUPPORTED", `The link register W is only listed in the FR-E800 SLMP device table, not for ${model}.`);
851
+ }
852
+
853
+ const range = frSlmpRange(entry, model, mode, options);
854
+ if (range.missingConfiguration) {
855
+ return fail("MISSING_CONFIGURATION", "configuredPoints.ST is required in configured mode; the accumulating timer defaults to 0 points.");
856
+ }
857
+ if (range.invalidConfiguration) {
858
+ return fail("INVALID_CONFIGURATION", `configuredPoints.ST exceeds the ${range.ceiling}-point maximum documented in the SLMP device table.`);
859
+ }
860
+
861
+ const spellingWarning = [];
862
+ const canonical = FR_SLMP_SPELLING[key];
863
+ if (canonical) {
864
+ const expected = rule.stSpelling === "S" ? canonical : key;
865
+ if ((parsed.notationPrefix || "") !== expected) {
866
+ spellingWarning.push(`The ${model} manual spells this accumulating-timer device ${expected}${parsed.addressText}; ${parsed.notationPrefix}${parsed.addressText} is accepted as the equivalent from the other inverter manual.`);
867
+ }
868
+ }
869
+ if (parsed.prefix === "SM" && entry.unit === "bit") {
870
+ spellingWarning.push("For word-unit SLMP access to SM, the head device number must be one listed in the special relay table, otherwise the read or write is not performed correctly.");
871
+ }
872
+
873
+ return frResult(value, parsed, series, model, mode, normalized, operation, range, {
874
+ label: "SLMP device table",
875
+ allocationHint: 'The accumulating timer defaults to 0 points; allocate it in the sequence parameters, then use mode "maximum" or configuredPoints.ST.',
876
+ checkDigit: false,
877
+ ioAreas: true,
878
+ deviceCode: entry.code,
879
+ deviceUnit: entry.unit,
880
+ extraWarnings: spellingWarning
881
+ });
882
+ }
883
+
884
+ function frResult(value, parsed, series, model, mode, normalized, operation, range, context) {
885
+ const prefix = parsed.prefix;
886
+ const fail = (code, message, extra = {}) => invalid(value, series, model, mode, code, message, { normalized, parsed, ...extra });
887
+ const withinRange = address => range.ranges.some(([start, end]) => address >= start && address <= end);
888
+ const warnings = [...(context.extraWarnings || [])];
889
+ let configurationDependent = range.dependent;
890
+
891
+ if (mode !== "syntax") {
892
+ if (!withinRange(parsed.address)) {
893
+ if (range.allocatableTo != null && parsed.address <= range.allocatableTo) {
894
+ return fail("REQUIRES_CONFIGURATION", `${parsed.notationPrefix || prefix}${parsed.addressText} needs sequence-parameter allocation. ${context.allocationHint}`,
895
+ { configurationDependent: true, suggestedMode: "maximum" });
896
+ }
897
+ if (range.serialExtendedTo != null && parsed.address <= range.serialExtendedTo) {
898
+ return fail("REQUIRES_SERIAL_SUPPORT",
899
+ `${prefix}${parsed.addressText} needs the 32-point T/C extension, which ${model} only provides from the January 2021 production month onward. Check the SERIAL plate, then use mode "maximum" or frFeatures.extendedTimerPoints.`,
900
+ { configurationDependent: true, suggestedMode: "maximum" });
901
+ }
902
+ return fail("ADDRESS_OUT_OF_RANGE", `${parsed.notationPrefix || prefix}${parsed.addressText} is outside the ${model} range in the ${context.label}.`);
903
+ }
904
+ if (context.checkDigit && parsed.digit !== null) {
905
+ const last = parsed.address + parsed.digit * 4 - 1;
906
+ if (!withinRange(last)) {
907
+ return fail("DIGIT_RANGE_OVERFLOW", `K${parsed.digit}${prefix}${parsed.addressText} covers ${parsed.digit * 4} points and runs past the end of the ${prefix} range.`);
908
+ }
909
+ }
910
+ if (range.dependent && (prefix === "T" || prefix === "C")) {
911
+ warnings.push("The 32-point T/C extension depends on the SERIAL production month; pass frFeatures.extendedTimerPoints once it is known.");
912
+ }
913
+ if (range.allocated) {
914
+ warnings.push("The accumulating timer is only usable once the sequence parameters allocate its points; pass configuredPoints.ST for an exact result.");
915
+ }
916
+ if (range.dependent && prefix === "P") {
917
+ warnings.push("The 256-point P pointer device depends on the SERIAL production month; pass frFeatures.pointerDevice once it is known.");
918
+ }
919
+ if (context.ioAreas && (prefix === "X" || prefix === "Y")) {
920
+ const area = frIoArea(parsed.address);
921
+ if (area && area[3]) {
922
+ configurationDependent = true;
923
+ warnings.push(`${prefix}${parsed.addressText} belongs to the ${area[2]} area, which only exists while that network is configured.`);
924
+ }
925
+ }
926
+ }
927
+
928
+ const readOnly = mode !== "syntax" && prefix === "X";
929
+ if (readOnly) {
930
+ warnings.push("Input device X is refreshed from the terminal or network every scan, so turning it on does not drive the corresponding signal. Use SM1200/SM1255 and SD1148/SD1149 for inverter operation control instead.");
931
+ }
932
+ const operationAllowed = operation === "write" ? !readOnly : operation === "read" ? true : null;
933
+
934
+ const common = {
935
+ valid: true, input: value, normalized, series, model, mode, operation, code: "VALID",
936
+ parsed, source: SOURCES[series]
937
+ };
938
+ if (context.deviceCode) {
939
+ common.deviceCode = context.deviceCode;
940
+ common.deviceUnit = context.deviceUnit;
941
+ }
942
+
943
+ if (mode === "syntax") {
944
+ return {
945
+ ...common,
946
+ message: `Valid ${context.label} device syntax; range and access were not evaluated in syntax mode.`,
947
+ configurationDependent: false, access: "not-evaluated", operationAllowed: null, warnings: []
948
+ };
949
+ }
950
+
951
+ return {
952
+ ...common,
953
+ message: operation === "write" && readOnly
954
+ ? "Valid device notation and range, but X is an input image that cannot be driven."
955
+ : configurationDependent
956
+ ? `Valid within the ${context.label} range; confirm the inverter SERIAL, sequence parameters, and network configuration.`
957
+ : `Valid within the ${context.label} range.`,
958
+ configurationDependent,
959
+ access: readOnly ? "read-only" : "read-write",
960
+ operationAllowed,
961
+ warnings
962
+ };
963
+ }
964
+
585
965
  function analyzeDevice(value, options = {}) {
586
966
  const mode = options.mode || "default";
587
967
  const inputMode = options.inputMode || "exact";
@@ -597,7 +977,7 @@ function analyzeDevice(value, options = {}) {
597
977
  }
598
978
  const series = canonicalSeries(options.series);
599
979
  if (!series) return invalid(value, null, null, mode, "UNSUPPORTED_SERIES", `Supported series: ${SERIES.join(", ")}.`);
600
- const seriesModel = cr800ModelFromSeries(options.series);
980
+ const seriesModel = modelFromSeries(options.series);
601
981
  const model = normalizeModel(options.series, options.model ?? seriesModel);
602
982
  if (!model) return invalid(value, series, null, mode, "UNSUPPORTED_MODEL", `Unsupported ${series} model: ${String(options.model)}.`);
603
983
  if (seriesModel && model !== seriesModel) return invalid(value, series, model, mode, "MODEL_SERIES_MISMATCH", `${String(options.series)} conflicts with model ${model}.`);
@@ -605,11 +985,19 @@ function analyzeDevice(value, options = {}) {
605
985
  const featureError = validateCr800Features(options.cr800Features);
606
986
  if (featureError) return invalid(value, series, model, mode, "INVALID_CR800_FEATURES", featureError);
607
987
  }
988
+ if (series === "FR800") {
989
+ const featureError = validateFrFeatures(options.frFeatures, model);
990
+ if (featureError) return invalid(value, series, model, mode, "INVALID_FR_FEATURES", featureError);
991
+ if (options.frAccess !== undefined && !["sequence", "slmp"].includes(options.frAccess)) {
992
+ return invalid(value, series, model, mode, "INVALID_FR_ACCESS", "frAccess must be sequence or slmp.");
993
+ }
994
+ }
608
995
  const parsed = parseDevice(value, { series, inputMode });
609
996
  const normalized = inputMode === "friendly" ? normalizeDevice(value) : (typeof value === "string" ? value : null);
610
997
  if (!parsed) return invalid(value, series, model, mode, "INVALID_SYNTAX", "The value is not a supported MELSEC/MELFA device notation.", { normalized });
611
998
 
612
999
  if (series === "CR800") return analyzeCr800(value, parsed, series, model, mode, normalized, operation, options.cr800Features);
1000
+ if (series === "FR800") return analyzeFr(value, parsed, series, model, mode, normalized, operation, options);
613
1001
 
614
1002
  if (series === "iQ-F" && ["V", "LT", "LST", "ZR", "RD", "FX", "FY", "FD"].includes(parsed.prefix)) {
615
1003
  return invalid(value, series, model, mode, "DEVICE_NOT_SUPPORTED", `${parsed.prefix} is not listed as an iQ-F device in the attached manual.`, { normalized, parsed });
package/dist/index.d.ts CHANGED
@@ -1,8 +1,16 @@
1
- export type MelSeries = "iQ-R" | "iQ-F" | "MX-R" | "MX-F" | "CR800" | "CR800-R" | "CR800-D" | "CR800-Q";
1
+ export type MelSeries =
2
+ | "iQ-R" | "iQ-F" | "MX-R" | "MX-F"
3
+ | "CR800" | "CR800-R" | "CR800-D" | "CR800-Q"
4
+ | "FR800"
5
+ | "FR-A800" | "FR-A800-E" | "FR-A800-P" | "FR-A800-CRN" | "FR-A800-LC" | "FR-A800-Plus"
6
+ | "FR-F800" | "FR-F800-E"
7
+ | "FR-E800" | "FR-E800-E" | "FR-E800-SCE" | "FR-E800-NC" | "FR-E806";
2
8
  export type ValidationMode = "syntax" | "default" | "maximum" | "configured";
3
9
  export type InputMode = "exact" | "friendly";
4
10
  export type DeviceOperation = "read" | "write" | "either";
5
11
  export type DeviceAccess = "read-write" | "read-only" | "configuration-dependent" | "not-evaluated";
12
+ /** "sequence" validates the sequence-program device table, "slmp" the SLMP device table of the Ethernet inverters. */
13
+ export type FrAccess = "sequence" | "slmp";
6
14
 
7
15
  export interface Cr800Features {
8
16
  /** CR800-R/Q sequencer I/O unit direct control (parameter QXYREAD). */
@@ -25,6 +33,22 @@ export interface Cr800Features {
25
33
  ccLinkIef?: boolean;
26
34
  }
27
35
 
36
+ export interface FrFeatures {
37
+ /**
38
+ * 32-point T/ST/C extension. Listed only for FR-A800/FR-A800-E (excluding
39
+ * FR-A800-P), FR-A800 Plus (FR-A800-CRN/LC) and FR-F800/FR-F800-E, from the
40
+ * January 2021 production month onward. Omit when the SERIAL plate has not
41
+ * been checked. Ignored for `frAccess: "slmp"`, whose table caps T/C/ST at
42
+ * 16 points.
43
+ */
44
+ extendedTimerPoints?: boolean;
45
+ /**
46
+ * 256-point P pointer device. Listed only for FR-E800, from the January 2021
47
+ * production month onward.
48
+ */
49
+ pointerDevice?: boolean;
50
+ }
51
+
28
52
  export interface DeviceOptions {
29
53
  series: MelSeries | string;
30
54
  model?: string;
@@ -34,6 +58,10 @@ export interface DeviceOptions {
34
58
  configuredPoints?: Partial<Record<string, number>>;
35
59
  /** Known CR800 feature states. Omitted properties are treated as unknown. */
36
60
  cr800Features?: Cr800Features;
61
+ /** Known inverter feature states. Omitted properties are treated as unknown. */
62
+ frFeatures?: FrFeatures;
63
+ /** Which inverter device table to validate against. Defaults to "sequence". */
64
+ frAccess?: FrAccess;
37
65
  }
38
66
 
39
67
  export interface ParsedDevice {
@@ -73,6 +101,10 @@ export interface DeviceAnalysis {
73
101
  operationAllowed?: boolean | null;
74
102
  activeAllocations?: string[];
75
103
  possibleAllocations?: string[];
104
+ /** SLMP device code, such as "H9C". Present for `frAccess: "slmp"` results. */
105
+ deviceCode?: string;
106
+ /** SLMP access unit for the device code. */
107
+ deviceUnit?: "bit" | "word";
76
108
  warnings?: string[];
77
109
  parsed?: ParsedDevice;
78
110
  suggestedMode?: ValidationMode;
@@ -11,6 +11,9 @@
11
11
  | MX-R | MELSEC MXコントローラ MX-Rモデルユーザーズマニュアル | SH-082640-D | 221(性能仕様)、350(デバイス一覧・デフォルト点数)、351-352(点数設定・最大使用範囲)、376-378(ユニット/CPUバッファアクセス指定方法)、380-381(インデックス修飾) |
12
12
  | MX-F | MELSEC MXコントローラ MX-Fモデルユーザーズマニュアル | SH-082633-E | 277(性能・デフォルト点数)、416(デバイス一覧)、417-418(点数設定・最大使用範囲)、441-443(ユニット/CPUバッファアクセス指定方法) |
13
13
  | CR800 | CR800シリーズコントローラ 機能と操作の詳細解説 | BFP-A3477-AB | 5-489~5-491(CPUバッファ対応)、6-618~6-619(シーケンサリンク)、6-654(対応デバイス一覧)、6-655~6-657(デバイス割付) |
14
+ | FR800 | FR-A800/F800/E800 シーケンス機能プログラミングマニュアル | IB-0600491-Q | 8(SERIAL別対応可否)、11-12(シーケンス機能仕様・注記*8)、22-28(デバイスマップ)、107(デバイス一覧)、125-126(桁指定)、157(ネスティング) |
15
+ | FR800(SLMP) | A800-E/F800-E Ethernet機能説明書 | IB-0600627-D | 37(SLMPデバイスコードと範囲)、38(デバイス番号指定)、94(インバータ間リンクデバイスマップ) |
16
+ | FR800(SLMP) | FR-E800取扱説明書(通信編) | IB-0600870-S | 12(Ethernet対応仕様品)、132-133(SLMPデバイスコードと範囲・リンクレジスタ)、136(デバイス番号指定)、222(インバータ間リンクデバイスマップ) |
14
17
 
15
18
  ## 基数
16
19
 
@@ -18,6 +21,7 @@
18
21
  | --- | --- | --- | --- |
19
22
  | iQ-F | X, Y | B, SB, W, SW | その他の通常デバイス |
20
23
  | iQ-R / MX-R / MX-F | - | X, Y, B, SB, W, SW, FX, FY | その他の通常デバイス |
24
+ | FR800 | - | X, Y | その他の通常デバイス |
21
25
 
22
26
  この差により、iQ-Fの `X1777` は有効候補ですが `X1780` は構文エラーです。一方、iQ-R/MXのX/Yは16進です。
23
27
 
@@ -153,3 +157,96 @@ iQ-F本文69ページではZによる全デバイスのインデックス修飾
153
157
  - 6.2.2/表6-4: `U3En\G10000-G10511`をロボットCPU No.nの領域として使用
154
158
 
155
159
  このため`U3En\G512-G1023`は有効範囲へ追加しません。該当入力には`MANUAL_RANGE_CONFLICT`を返します。IQMEMの意図された読み出し側アドレスと考えられる`U3En\G10512-G11023`は固定範囲としては有効ですが、表記不整合が解消できないため、IQMEM有効時でもR属性を断定せず`configuration-dependent`とします。これはマニュアル間の矛盾を隠して推測値を確定しないための意図的な保守性です。
160
+
161
+ ## FRインバータ シーケンス機能(FR-A800/A800 Plus/F800/E800)
162
+
163
+ 本文107ページ「3.9.1 デバイス一覧」が全デバイスの点数と使用範囲を確定しています。点数はシーケンスパラメータで変更できないため、`configuredPoints`は参照しません。
164
+
165
+ | 記号 | FR-A800 / FR-A800 Plus / FR-F800 | FR-E800 | 基数 |
166
+ | --- | --- | --- | --- |
167
+ | X | `X0-X8F`(144点) | 同左 | 16進 |
168
+ | Y | `Y0-Y8F`(144点) | 同左 | 16進 |
169
+ | M | `M0-M127`(128点) | 同左 | 10進 |
170
+ | L | ―(シーケンスパラメータ設定可能だがラッチしない) | 同左 | ― |
171
+ | T | `T0-T15` / `T0-T31` | `T0-T15` | 10進 |
172
+ | ST | 初期値0点(割付けにより `ST0-ST15` / `ST0-ST31`) | 初期値0点(割付けにより `ST0-ST15`) | 10進 |
173
+ | C | `C0-C15` / `C0-C31` | `C0-C15` | 10進 |
174
+ | D | `D0-D255`(256点) | 同左 | 10進 |
175
+ | P | 記載なし | `P0-P127`、`P2048-P2175`(256点、すべて共通ポインタ) | 10進 |
176
+ | SM | `SM0-SM2047`(機能制限あり) | 同左 | 10進 |
177
+ | SD | `SD0-SD2047`(機能制限あり) | 同左 | 10進 |
178
+
179
+ X/Yの144点は、本文22-28ページのデバイスマップが `X0-X1F`(外部入出力・内蔵オプション)、`X20-X2F`(システム入出力)、`X30-X3F`(CC-Linkリモート入出力)、`X40-X8F`(Ethernetインバータ間リンク、マスタ局は5スレーブ分)と連続的に割り付けていることと一致します。Y側も同一構成です。`X30`以降は該当ネットワークの構成時のみ存在するため、成功時も設定依存として警告を付けます。
180
+
181
+ ネスティング(N)はデバイス一覧には含まれませんが、本文157ページに「入れ子は15個(N0~14)まで」と明記されているため、`N0-N14`のみ有効としています。
182
+
183
+ ### SERIAL(製造年月)依存
184
+
185
+ 本文8ページ「1.1.1 SERIAL(製造番号)の確認」の表は、次の3機能を2021年1月以降の製造品のみ対応としています。
186
+
187
+ | 機能 | FR-A800(FR-A800-Pは除く)/FR-A800 Plus(FR-A800-CRN/LC) | FR-A800 Plus(FR-A800-CRN/LC以外) | FR-F800 | FR-E800 |
188
+ | --- | :-: | :-: | :-: | :-: |
189
+ | プログラム言語(ST)対応 | − | − | − | ○ |
190
+ | T/ST/Cのデバイス32点対応 | ○ | − | ○ | − |
191
+ | ポインタ(P)デバイス256点対応 | − | − | − | ○ |
192
+
193
+ ST言語対応は表記判定に影響しないため実装対象外です。残る2件は型名だけでは確定できないため、次のように分離しています。
194
+
195
+ - 既定(`default`)はSERIALを未確認とみなし、拡張前の点数で判定します。範囲外だが拡張範囲内の場合は`ADDRESS_OUT_OF_RANGE`ではなく`REQUIRES_SERIAL_SUPPORT`を返し、`suggestedMode: "maximum"`を添えます。
196
+ - `maximum`は拡張後の点数を許可しますが、成功時も`configurationDependent: true`とします。
197
+ - `frFeatures.extendedTimerPoints` / `frFeatures.pointerDevice` に真偽値を渡した場合のみ、設定依存を解除して確定判定します。マニュアルが非対応としている型名に`true`を渡した場合は`INVALID_FR_FEATURES`を返します。
198
+
199
+ FR-A800-Pは本文8ページの対応列から明示的に除外されているため、32点拡張の対象外として扱います。FR-A800 PlusのうちFR-A800-CRN/LC以外も同様に対象外です。
200
+
201
+ ### 桁指定
202
+
203
+ 本文125-126ページは、桁指定の対象をビットデバイス(X、Y、M)と明記し、16ビット命令でK1~K4、32ビット命令でK1~K8としています。本パッケージはK1~K8を受け付け、先頭デバイス+指定点数がデバイス範囲を超える場合に`DIGIT_RANGE_OVERFLOW`を返します。本文127ページの例(`K5M0`、`K5M10`)のとおり先頭番号のアライメント制約はないため、そこは判定しません。
204
+
205
+ ### 入力デバイスXの属性
206
+
207
+ 本文22・25ページの注記は「入力デバイスXは、外部端子の入力状態をモニタします。例えば、X00をシーケンスプログラム上でONしてもSTF信号をONすることはできません」とし、信号入力にはSM1200、SM1255、SD1148、SD1149を使うよう指示しています。Xはネットワーク側の領域(`X30`以降)も含めて毎スキャンリフレッシュされる入力イメージであるため、`access: "read-only"`として扱い、`operation: "write"`では`operationAllowed: false`を返します。
208
+
209
+ ### 意図的な保守性
210
+
211
+ - ビット指定、間接指定(`@`)、インデックス修飾(`Z`/`LZ`)、ローカルデバイス(`#`)、ユニットアクセス(`Un\G`)、CPUバッファアクセス(`U3En\G`)、リンクダイレクト(`Jn\`)は本マニュアルに記載がないため、すべて無効とします。
212
+ - `TS`/`TC`/`TN`のような接点・コイル・現在値の分離表記も記載がないため無効とし、`T0`形式を案内します。
213
+ - SM/SDは「機能制限あり」と注記されているとおり、全番号に機能が割り当てられているわけではありません。本パッケージは領域の範囲のみを判定します。
214
+
215
+ ### 積算タイマ(ST)の初期値0点
216
+
217
+ 本文11ページの積算タイマ行には注記*8が付き、12ページで「*8 初期値は0です。」と定義されています。IB-0600627-D 37ページおよびIB-0600870-S 132ページのSLMPデバイス表も、積算タイマの範囲を「0点(初期値。PCパラメータ割付けにより16点まで使用可能。)」としており、両者は一致します。
218
+
219
+ このためSTのみ、他シリーズの点数変更可能デバイスと同じ扱いにしています。
220
+
221
+ | モード | STの判定 |
222
+ | --- | --- |
223
+ | `default` | 有効点数0点。任意のST表記に対して`REQUIRES_CONFIGURATION`と`suggestedMode: "maximum"`を返します。 |
224
+ | `maximum` | 割付け上限(16点、32点拡張対応機種では32点)まで許可し、`configurationDependent: true`とします。 |
225
+ | `configured` | `configuredPoints.ST`の点数で判定します。未指定なら`MISSING_CONFIGURATION`、上限超過なら`INVALID_CONFIGURATION`です。 |
226
+
227
+ T・Cには同様の注記がないため、これらは従来どおり固定範囲(16点/SERIAL依存で32点)として扱います。
228
+
229
+ ## FRインバータ SLMPデバイステーブル
230
+
231
+ IB-0600627-D 37ページ(FR-A800-E/FR-F800-E)およびIB-0600870-S 132-133ページ(FR-E800)の「デバイス」表は、外部機器がSLMPで指定できるデバイスコードと範囲を規定しています。シーケンス機能のデバイス一覧とは範囲も表記も異なるため、`frAccess: "slmp"`で別テーブルとして判定します。
232
+
233
+ | デバイス | コード | 種別 | 範囲 | 備考 |
234
+ | --- | --- | --- | --- | --- |
235
+ | SM | `H91` | ビット | `0-2047` | ワード単位指定時は特殊リレー一覧のデバイスNo.を先頭に指定する必要あり |
236
+ | SD | `HA9` | ワード | `0-2047` | |
237
+ | X | `H9C` | ビット | `H0-H7F` | シーケンス機能の`H0-H8F`より狭い |
238
+ | Y | `H9D` | ビット | `H0-H7F` | 同上 |
239
+ | M | `H90` | ビット | `0-127` | |
240
+ | D | `HA8` | ワード | `0-255` | |
241
+ | W | `HB4` | ワード | `0-8191`(8192点) | IB-0600870-S 132ページ。FR-E800側の表にのみ記載 |
242
+ | TS / TC / TN | `HC1` / `HC0` / `HC2` | ビット / ビット / ワード | `0-15` | |
243
+ | STS / STC / STN(SS / SC / SN) | `HC7` / `HC6` / `HC8` | ビット / ビット / ワード | 0点(初期値。割付けにより16点まで) | |
244
+ | CS / CC / CN | `HC4` / `HC3` / `HC5` | ビット / ビット / ワード | `0-15` | |
245
+
246
+ - IB-0600870-S 12ページは「Ethernet通信は、Ethernet仕様品、安全通信仕様品、IP67仕様品で使用可能です」と規定しています。SLMPを許可する型名は、これとIB-0600627-Dの適用範囲から FR-A800-E、FR-F800-E、FR-E800-E、FR-E800-SCE、FR-E806 に限定し、その他は`MODEL_NOT_SUPPORTED`とします。
247
+ - 積算タイマの各部の表記は、IB-0600627-D 37ページが`STS`/`STC`/`STN`、IB-0600870-S 132ページが`SS`/`SC`/`SN`です。デバイスコードは同一(HC7/HC6/HC8)のため両表記を受け付け、選択した型名のマニュアル表記と異なる場合に警告を返します。
248
+ - リンクレジスタWは、IB-0600870-S 133ページ以降でPr.番号(`W0-W999`)、モニタ、アラーム履歴、シリアルナンバー(`W8101-W8108`)等に対応づけられており、番号は10進です。PLCシリーズのWが16進であるのと異なるため、FR800では基数を分けています(FRで16進なのはX/Yのみ)。割付けは疎ですが、本パッケージは表が定める8192点の範囲のみを判定します。
249
+ - 両マニュアルのSLMP表はT/CおよびSTの上限を16点としており、シーケンス機能側の32点拡張には言及していません。したがって`frAccess: "slmp"`では`frFeatures.extendedTimerPoints`で範囲を広げません。
250
+ - SLMPは先頭デバイス番号と点数を別フィールドで送るため、桁指定は表記の一部ではありません。P・N・Lも表に存在しないため無効とします。
251
+
252
+ インバータ間リンクの入出力割付け(`X40-X8F` / `Y40-Y8F`)は、IB-0600627-D 94ページとIB-0600870-S 222ページがシーケンス機能プログラミングマニュアルと同一の内容を示しており、判定値の変更はありません。
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mokouliszt/mel-device",
3
- "version": "0.3.1",
4
- "description": "Model-aware Mitsubishi MELSEC and MELFA device notation validator.",
3
+ "version": "0.4.0",
4
+ "description": "Model-aware Mitsubishi MELSEC, MELFA and FR inverter device notation validator.",
5
5
  "license": "MIT",
6
6
  "author": "mokouliszt",
7
7
  "type": "module",
@@ -37,7 +37,11 @@
37
37
  "mx-r",
38
38
  "mx-f",
39
39
  "cr800",
40
- "melfa"
40
+ "melfa",
41
+ "inverter",
42
+ "fr-a800",
43
+ "fr-f800",
44
+ "fr-e800"
41
45
  ],
42
46
  "engines": {
43
47
  "node": ">=18"