Validator for Mitsubishi MELSEC iQ-R device and constant strings used in GX Works3
Project description
iqr_device_validator
Mitsubishi MELSEC iQ-R シリーズのデバイス文字列・定数を検証する Python ライブラリです。GX Works3 で使われる表記 (X0, D100.A, K4M0, J1\W10, SA\D100, K1234, T#1h30m など) を正規表現+構文木でパースし、設定可能範囲の上限と修飾子の組み合わせ可否をチェックします。
A validator for device & constant strings used in Mitsubishi MELSEC iQ-R PLC programs (GX Works3 syntax). Validates not just the form of a device reference, but also whether the device number is within the configurable range and whether modifiers (digit/bit/index/local/indirect) are legal for that device kind.
Features
- General devices —
X,Y,M,L,B,F,SB,V,S,T,ST,LT,LST,C,LC,D,W,SW,FX,FY,FD,SM,SD,Z,LZ,R,ZR,RD,P,I,N - Direct access I/O —
DX,DY(e.g.DX10,DY25,K4DX0) - SFC devices —
BL(block device),TR(transition device), and the in-block step reference formBL<n>\S<m>(e.g.BL0,BL10\S100,TR5) - Link direct devices —
J<n>\<dev>(e.g.J1\W10,J239\K4B0) - Unit access devices —
U<n>\G<addr>,U3E<n>\G<addr>,U3E<n>\HG<addr> - Safety devices —
SA\X,SA\Y,SA\M,SA\B,SA\T,SA\ST,SA\C,SA\D,SA\W,SA\SM,SA\SDwith the standard safety-program restrictions enforced (no index modification, no indirect, limited local prefix) - All modifiers — bit (
.A), digit (K4), index (Z/LZ/ZZ), indirect (@), local (#) - Continuity check for digit specification:
K8X2FF0is rejected because the 32 bits would extend pastX2FFF - Constants —
TRUE/FALSE, decimal (K123/123), hex (HFF/16#FF), binary (2#01101010), octal (8#377), real (E1.234/1.0E6), string ('ABC'), wstring ("ABC"), time (T#1d2h3m4s5ms) - String escape sequences —
$$(literal$),$'/$"(escaped quotes),$L/$N/$T/$R/$P(LF/newline/tab/CR/FF, case-insensitive), and$<HH>(ASCII hex byte). Decodedvaluereflects the actual bytes, e.g.'A$LB'→"A\nB". - Yen-mark normalization —
\,¥,¥,\are all accepted as the path separator (common in Japanese docs) - No dependencies — pure standard library, single file
Installation
pip install iqr_device_validator
Requires Python 3.10 or later.
Quick start
from iqr_device_validator import validate
# Devices
print(validate("D100.A").ok) # True (D100, bit A specification)
print(validate("X3000").ok) # False (out of range; max is X2FFF)
print(validate("K8X2FF0").ok) # False (K8 would extend past X2FFF)
print(validate("@D10Z2.A").ok) # True (indirect + index + bit)
print(validate("DX10").ok) # True (direct access input)
print(validate("DY5E").ok) # True (direct access output)
print(validate("@DX0").ok) # False (indirect not allowed on direct I/O)
print(validate("BL10\\S100").ok) # True (SFC: step 100 of block 10)
print(validate("BL320\\S0").ok) # False (block out of range)
print(validate("SA\\D100").ok) # True (safety device)
print(validate("@SA\\D0").ok) # False (safety devices forbid @)
# Constants
print(validate("K1234").ok) # True (decimal constant)
print(validate("HFF").ok) # True (hex constant)
print(validate("E1.234").ok) # True (real constant)
print(validate("T#1h30m").ok) # True (time constant)
print(validate("'ABC'").ok) # True (string constant)
print(validate("'A$LB'").value) # "A\nB" (escape decoded)
print(validate("'$X'").ok) # False (invalid escape)
API
validate(text: str) -> ValidationResult
The single entry point. Returns a ValidationResult dataclass:
@dataclass
class ValidationResult:
ok: bool # True if valid
category: str # "device" | "constant"
error: str # human-readable message when ok=False
# --- device fields (when category == "device") ---
kind: str # "simple" | "link_direct" | "un_g" | "u3e_g" | "safety"
prefix: str # device prefix, e.g. "D", "J\\W", "SA\\D"
number: int # parsed device number
bit_no: Optional[int] # 0..15 for word-bit specs
digits: Optional[int] # 1..8 for digit specs
digit_end_no: Optional[int] # base + digits*4 - 1
network: Optional[int] # network No. for J\
unit: Optional[int] # unit No. for U\G
cpu: Optional[int] # CPU No. for U3E\G
area: Optional[str] # "G" | "HG" for U3E
indirect: bool # @ prefix present
local: bool # # prefix present
safety: bool # SA\ prefix present
index: Optional[IndexMod] # index modification, if any
# --- constant fields (when category == "constant") ---
const_type: str # "bool"|"dec"|"hex"|"bin"|"oct"|"real"|"string"|"wstring"|"time"
value: object # parsed value (int/float/bool/str/dict)
Example: lint a list of operands
from iqr_device_validator import validate
operands = ["X0", "D100", "K4M0", "X3000", "Z24", "@FX0", "K1234", "'hello'"]
for op in operands:
r = validate(op)
if r.ok:
print(f"OK {op:10s} ({r.category} / {r.kind or r.const_type})")
else:
print(f"FAIL {op:10s} {r.error}")
Output:
OK X0 (device / simple)
OK D100 (device / simple)
OK K4M0 (device / simple)
FAIL X3000 device number out of range (got X3000, max X2FFF)
FAIL Z24 device number out of range (got Z24, max Z23)
FAIL @FX0 indirect (@) not supported by FX
OK K1234 (constant / dec)
OK 'hello' (constant / string)
What gets validated
Device range
Each device's max settable count is taken from the iQ-R reference manual MELSEC iQ-R CPU User's Manual (sh082487h), section "デバイス点数の使用範囲" (P.410). The library uses the maximum configurable values (i.e. the upper bound of what GX Works3 will let you set), assuming the largest CPU model with extended SRAM cassette.
| Category | Examples |
|---|---|
| Bit devices | X0..X2FFF, M0..M161882111, B0..B9A61FFF, L0..L32767, S0..S16383 |
| Direct I/O | DX0..DX2FFF, DY0..DY2FFF (same hex range as X/Y) |
| Word devices | D0..D10117631, W0..W9A61FF, R0..R32767, RD0..RD1048575 |
| System | SM0..SM4095, SD0..SD4095, FX0..FXF, FY0..FYF, FD0..FD4 |
| SFC | BL0..BL319 (block), TR0..TR16383 (transition), BL<n>\S0..S511 (in-block step) |
| Index | Z0..Z23, LZ0..LZ11 |
| Pointer/Nest | P0..P32767, I0..I1023, N0..N14 |
| Safety | SA\X0..SA\X2FFF, SA\M0..SA\M638975, SA\D0..SA\D39935, ... |
Modifier compatibility
The validator enforces the modifier-applicability matrix from the iQ-R Programming Manual (CPU Instructions / FUN/FB) §1.2 and the Application Manual §35.1 / §39.1.
| Modifier | Example | Allowed on |
|---|---|---|
Bit spec .X |
D100.A |
Word devices: D, W, SW, FD, SD, R, ZR, RD, U\G, U3E\G, J\W, J\SW |
Digit spec K<n> |
K4M0 |
Bit devices: X, Y, M, L, B, F, SB, V, S, FX, FY, SM |
Indirect @ |
@D0 |
T, ST, C, D, W, SW, FD, SD, R, ZR, RD, U\G, J\W, J\SW, U3E\G |
Local # |
#D0 |
M, V, T, ST, C, LC, LT, LST, D, Z, LZ |
Index Z |
D0Z2 |
Most word/bit devices (not FX, FY, N, Z itself) |
Index LZ / ZZ |
D0LZ0 / D0Z0Z1 |
M, B, SB, T, ST, LT, LST, C, LC, D, W, SW, R, ZR, RD |
Safety SA\ |
SA\D100 |
X, Y, M, B, T, ST, C, D, W, SM, SD |
Combinational rules
- The prefix order is fixed:
[@] [SA\] [K<n>] [#] <base>— out-of-order combinations are rejected. @with bit-spec is allowed (e.g.@D100.A) because indirect dereferences to a word.- Bit-spec and digit-spec cannot both appear on one operand.
ZZnotation requires consecutive registers:Z3Z4OK,Z3Z2andZ3Z5rejected.- Safety devices forbid
@and index modification (per Application Manual §39.1). - The continuity check rejects
K<n><dev><base>wherebase + n*4 - 1 > max_no.
Constants
| Type | Examples | Range |
|---|---|---|
| Boolean | TRUE, FALSE |
— |
| Decimal | K123, +123, -123, 12_3 |
-2,147,483,648 .. 4,294,967,295 |
| Hex | HFF, H1234, 16#FF |
H0 .. HFFFFFFFF |
| Binary | 2#0110_1010 |
(within the integer range) |
| Octal | 8#377 |
(within the integer range) |
| Real | E1.234, 1.0E6, E1.001-6 |
IEEE 754 double; flags single-precision fit |
| String | 'ABC', 'A$LB', '$41$42' |
up to 255 bytes (CP932) |
| WString | "ABC", "$22quoted$22" |
up to 255 Unicode characters |
| Time | T#1d2h3m4s5ms, TIME#-31m23s |
-24d20h31m23s648ms .. 24d20h31m23s647ms |
String escape sequences
Inside a string literal, $ is the escape character (per iQ-R Application Manual §26.5):
| Sequence | Meaning |
|---|---|
$$ |
literal $ |
$' |
literal ' (use inside single-quoted strings) |
$" |
literal " (use inside double-quoted strings) |
$L / $l |
line feed (LF, 0x0A) |
$N / $n |
new line (LF, 0x0A) |
$P / $p |
form feed / page break (0x0C) |
$R / $r |
carriage return (0x0D) |
$T / $t |
horizontal tab (0x09) |
$<HH> |
the ASCII character with hex code HH (must be ≤ 0x7F) |
The validator decodes escape sequences in result.value (e.g. 'A$LB' produces value = "A\nB") and rejects malformed escapes such as '$X', '$1G', '$80' (out of ASCII range), or a trailing lone $.
Devices and constructs that are NOT supported
The following are intentionally outside the scope of this validator. Strings using these forms will either be rejected, parsed differently than GX Works3 would, or accepted only because their textual form happens to overlap with a supported construct.
Devices
| Construct | Example | Reason |
|---|---|---|
| MELSEC-Q legacy devices | Q\X0 |
Q-series compatibility prefixes are not common in iQ-R native programs |
| Network No.0 link direct | J0\W0 |
The iQ-R spec defines J1..J239 only |
Custom user devices via DEVICE_SPECS extension |
MyDev0 |
The validator uses a fixed device table; new device kinds require code changes |
Labels
| Construct | Example | Reason |
|---|---|---|
| Global / local labels | bMyFlag, gCounter |
Label resolution requires the project's GX Works3 label table; this validator works on raw device strings only |
| Unit labels | RX1.bModuleReady, Q01\stEvent |
Generated by GX Works3 from each unit's FB reference; out of scope |
| Structure / array members | stMotor.iSpeed, arrPos[5], arrPos[5,2] |
Requires user-defined type information |
| System labels | iQ Works system label name |
iQ Works integration data is not parsed |
Constants and operands
| Construct | Example | Reason |
|---|---|---|
| Constant range matching to instruction operand types | MOV K70000 D0 |
Whether K70000 fits a 16-bit signed operand depends on the instruction (MOV vs DMOV); this validator only checks the broadest 32-bit range |
| Per-CPU model device counts | R04CPU has different defaults than R120CPU | The validator uses the maximum configurable range across all CPU models. A device accepted here may not fit a small CPU's actual allocation |
Project-aware constraints
The validator is stateless — it does not read CPU parameters or label tables. The following checks would require project-aware logic and are NOT performed:
- User-allocated device range — whether the device fits within the project's actual
デバイス点数setting (rather than the maximum configurable count). For example, a project might allocate only 8K points toM, soM10000would be invalid in that project even though we acceptM0..M161882111. - File register file selection —
ZRupper bound depends on which.QDRfile is loaded and the extended SRAM cassette size. - Network No. existence —
J239\W0is syntactically valid but only meaningful if network 239 is actually configured. - Unit number existence —
U7F\G100is syntactically valid but only meaningful if a unit is mounted at I/O7F0..7FF. - Safety CPU vs. general CPU — Safety device strings (
SA\...) are accepted regardless of whether the target is actually a safety CPU model. - Read-only / write-only operand correctness — e.g.
OUT X0would compile-error in GX Works3 becauseXis read-only, but this validator only checks thatX0is a valid device reference.
If your use case needs any of these, treat this validator as a fast first-pass screen and combine it with project-aware checks driven by the actual .gx3 project data.
Limitations
-
The
ZRupper bound is set to0x7FFFFF(assuming a 16 MB extended SRAM cassette). If your project uses a smaller file-register allocation, you can override it:from iqr_device_validator import DEVICE_SPECS, DeviceSpec DEVICE_SPECS["ZR"] = DeviceSpec(radix=10, max_no=131071, ...) # for 256KB
-
For real constants near the boundary, the
fits_singleflag in the result indicates whether the value is representable in single-precision. The library always accepts the value if it fits in double-precision, since the actual instruction width determines which is required. -
The SFC transition device
TR<n>is accepted syntactically, but per the iQ-R reference manual it is only usable as a device comment, not as an instruction operand. The validator does not enforce the comment-only restriction since that requires knowing the surrounding instruction context. -
The default SFC block range (
BL0..BL319) reflects the maximum for R04CPU and above. R00/R01/R02CPU support onlyBL0..BL127; if you target those models, overrideDEVICE_SPECS["BL"].max_no = 127.
Running tests
git clone https://github.com/mokouliszt/iqr_device_validator.git
cd iqr_device_validator
python -m pytest
The test suite includes 88+ cases covering every device kind, modifier combination, range boundary, and constant type.
References
The behaviour is grounded in the following Mitsubishi reference manuals:
- MELSEC iQ-R CPU ユニット ユーザーズマニュアル (sh082487h) — general device list and configurable ranges (§27.1, §27.2)
- MELSEC iQ-R CPU ユニット ユーザーズマニュアル (応用編) — safety devices (§35.1, §39.1), constants (§26, §31)
- MELSEC iQ-R プログラミングマニュアル (CPUユニット用命令/汎用FUN/汎用FB編) — bit/digit specification syntax, indirect (
@), index modification
This library is not affiliated with or endorsed by Mitsubishi Electric. MELSEC, iQ-R, and GX Works3 are trademarks of Mitsubishi Electric Corporation.
License
MIT License. See LICENSE for details.
Contributing
Bug reports and pull requests are welcome on GitHub. If you find a device range that disagrees with your project's setup, please file an issue with:
- The exact device string that was rejected (or accepted incorrectly)
- The CPU model and extended SRAM cassette in your project
- The expected behaviour with a manual reference if possible
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file iqr_device_validator-0.1.1.tar.gz.
File metadata
- Download URL: iqr_device_validator-0.1.1.tar.gz
- Upload date:
- Size: 25.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6c98df064047d0e9a9ad66d93cb63f450de984fa4b823e9ce80ede14abf5b36c
|
|
| MD5 |
7c228d58ae57bfbb5a6647b0507e4cbd
|
|
| BLAKE2b-256 |
660e3f982454a3a92569b8df5963cf6a1e43d9fcaf25f8a5c76badd9c12f4ea1
|
File details
Details for the file iqr_device_validator-0.1.1-py3-none-any.whl.
File metadata
- Download URL: iqr_device_validator-0.1.1-py3-none-any.whl
- Upload date:
- Size: 18.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
be96c29412e61a6c90b87e881acf695c43b33110644247cef9f6df873a8e3b24
|
|
| MD5 |
1d247ae77bc4ef4eb86041ccda5719a5
|
|
| BLAKE2b-256 |
9a7b1ed3567a4297c83118a8f034049c861e8b96a420b133eaba3b21cf174606
|