2. Domain ID Rules
All master data in PlantPulse follows server-side regex validation. The server enforces regex matching; violations are rejected with E1002 (VALIDATION_FAILED). Accurate ID design is the starting point for building a scalable IoT system.
📖 For a summary of platform-wide conventions, refer to the Domain ID Naming Convention page.
2.1 ID Rules at a Glance (Based on Server Regex)
New IDs follow the «Prefix Full Name + Number» format. When the Platform issues an ID automatically, it appends a six-digit number after the prefix
(SITE_000001, TAG_000001). Each domain has its own sequence, and the number of digits increases once it exceeds 999999 (ORDER_1000000).
| Domain | Official Prefix | Old Prefix (Still Valid) | Issuance Example |
|---|---|---|---|
| Enterprise | ENTERPRISE_ | — | ENTERPRISE_000001 |
| Site | SITE_ | — | SITE_000001 |
| Area | AREA_ | ASSET_ | AREA_000001 |
| Line | LINE_ | ASSET_ | LINE_000001 |
| Equipment | EQUIPMENT_ | ASSET_ | EQUIPMENT_000001 |
| Connection (OPC) | OPC_ | — | OPC_000001 |
| Tag | TAG_ | — | TAG_000001 |
| Edge | EDGE_ | — | Not issued — defined manually like EDGE_LINE1_01 |
| AlarmConfig | ALARM_CONFIG_ | Five-digit number, ALARM_CONFIG_TAG_… | ALARM_CONFIG_000001 |
| Employee | EMPLOYEE_ | EMP_ | EMPLOYEE_000001 |
| Customer | CUSTOMER_ | — | CUSTOMER_000001 |
| Product | PRODUCT_ | — | PRODUCT_000001 |
| Flow | FLOW_ | — | FLOW_000001 |
| Order | ORDER_ | ORD_ | ORDER_000001 |
| Calendar | CALENDAR_ | CAL_ | CALENDAR_000001 |
| Shift override | SHIFT_OVERRIDE_ | OV_ | SHIFT_OVERRIDE_000001 |
| Process segment | PROCESS_SEGMENT_ | SEG_ | PROCESS_SEGMENT_000001 |
| Asset relation | ASSET_RELATION_ | REL_ | ASSET_RELATION_000001 |
| Asset command | ASSET_COMMAND_ | — | ASSET_COMMAND_000001 |
| Command area | COMMAND_AREA_ | CMDAREA_ | COMMAND_AREA_000001 |
| Image | IMAGE_ | IMG_ | IMAGE_000001 |
| Maintenance order | MAINTENANCE_ORDER_ | Numeric only | MAINTENANCE_ORDER_000001 |
| User | None | — | Not issued — ^[A-Z][A-Z0-9_]*$ |
The prefix is followed only by uppercase letters, numbers, and _ ([A-Z0-9_]+). New IDs for maintenance orders and alarm settings only accept numbers of six digits or more.
asset_type, not IDOld IDs are not re-keyed and will continue to appear. Since ASSET_ was used across areas, lines, and equipment,
guessing the layer based on the prefix is incorrect. Always read the layer from asset_type (E · S · A · L · M).
When distinguishing domains by prefix, the longest prefix takes precedence — ASSET_RELATION_… and ASSET_COMMAND_… are
not ASSET_, and SHIFT_PATTERN_… is not SHIFT_.
If an ID is provided during creation, it is stored exactly as given, provided it passes format validation. Numbers are issued only when the ID field is left blank.
In V5, blank IDs are permitted for areas, lines, equipment (asset_id), and calendars (calendar_id); however, POST /api/v5/site must receive site_id. New entries for areas, lines, and equipment are also accepted using old prefixes.
Numbers are not sequential — since rejected creation attempts consume a number, it is normal for gaps to exist in the numbering.
⚠️ Validation Enforcement: The server validates these rules. Violations are rejected with HTTP 422, code
E1002. If an ID already exists, HTTP 409 is returned.⚠️ Full Names are Required:
CUST_andPROD_are not valid prefixes for any period and will be rejected.EMP_,ORD_, andCAL_are old prefixes used for existing IDs; use full names when creating new ones.⚠️
opc_idis exclusivelyOPC_: Whether from an Edge source or an external API channel, it must start withOPC_.EDGE_,TEST_, andAPI_are rejected — Edge sources are attached afterOPC_, such asOPC_EDGE_00303.edge_idis a separate domain and only acceptsEDGE_.
2.2 Site ID
정규식: ^SITE_[A-Z0-9_]+$
허용: SITE_… 만
Virtual or aggregation sites use the same prefix — names are created within the prefix, such as SITE_V_…. This is a naming convention rather than a separate pattern.
Examples
import plantpulse.api.v5.dto.request.SiteRequestV5;
import plantpulse.api.v5.dto.response.SiteResponseV5;
SiteRequestV5 req = new SiteRequestV5();
req.setSite_id("SITE_DJ"); // 대전공장
req.setSite_name("대전 1공장");
req.setLat("36.3504");
req.setLng("127.3845");
SiteResponseV5 created = client.site().create(req);
if (created == null) {
System.err.println("등록 실패 — site_id 정규식 확인 필요");
}
// 가상 사이트 (테스트·집계용) — 접두는 SITE_ 그대로
SiteRequestV5 vsite = new SiteRequestV5();
vsite.setSite_id("SITE_V_AGG_01");
vsite.setSite_name("집계 가상 사이트");
client.site().create(vsite);
❌ Incorrect Examples (All return null in V5)
req.setSite_id("DJ_FACTORY"); // ❌ SITE_ 누락 → E1002
req.setSite_id("site_00001"); // ❌ 소문자 → E1002
req.setSite_id("00001"); // ❌ 접두사 없음 → E1002
req.setSite_id("VSITE_01"); // ❌ V 변형 접두는 없다 → E1002
2.3 OPC ID
정규식: ^OPC_[A-Z0-9_]+$
허용: OPC_… 만
The OPC ID represents the physical or logical source of data collection. When semantic separation is required within the same OPC type, hierarchies are expressed using _.
Recommended Patterns by OPC Type
| OPC Type | Purpose | Recommended ID Prefix |
|---|---|---|
OPC | OPC UA / OPC DA server | OPC_<ua-server-name>_<number> |
PLC | Siemens, Mitsubishi, Allen-Bradley, etc. | OPC_PLC_<line>_<number> |
MODBUS | Modbus TCP/RTU device | OPC_MB_<equipment>_<number> |
DATABASE | External RDBMS polling | OPC_DB_<system> |
FILE | REST API/File import | OPC_FILE_<system>_<number> |
VIRTUAL | Test/Simulation | OPC_TEST_VIRTUAL_<number> |
| Edge Gateway | Edge gateway (Separate edge_id domain — ^EDGE_[A-Z0-9_]+$) | EDGE_<location>_<number> |
⚠️ Use the
OPC_FILE_…·OPC_TEST_…format in the OPC domain, whether for external API/file channels or testing.
Examples
import plantpulse.api.v5.dto.request.OPCRequestV5;
OPCRequestV5 req = new OPCRequestV5();
req.setOpc_id("OPC_PLC_L1_001");
req.setOpc_name("Line1 메인 PLC");
req.setOpc_protocol("S7"); // 프로토콜 코드 — 소스 유형(PLC)은 여기서 파생
req.setSite_id("SITE_DJ");
req.setOpc_source_ip("192.168.10.50");
req.setOpc_source_port(102);
client.opc().create(req);
// ERP 연동 — REST API 로 들어오는 연결
OPCRequestV5 erp = new OPCRequestV5();
erp.setOpc_id("OPC_API_ERP_001");
erp.setOpc_protocol("API");
erp.setSite_id("SITE_DJ");
client.opc().create(erp);
// 테스트·시뮬레이션
OPCRequestV5 testOpc = new OPCRequestV5();
testOpc.setOpc_id("OPC_TEST_001"); // TEST_ 로 시작하면 E1002
testOpc.setOpc_protocol("API");
testOpc.setSite_id("SITE_DJ");
client.opc().create(testOpc);
// 엣지 자기감시 연결 OPC_SYSTEM_<edge_id> 는 엣지가 sync 로 선언한다 — 직접 만들지 않는다.
2.4 Edge ID
정규식: ^EDGE_[A-Z0-9_]+$
edge_id is an identifier for Edge gateway devices and is a different domain from opc_id.
req.setEdge_id("EDGE_LINE1_01");
// req.setEdge_id("OPC_EDGE_00303"); // ❌ OPC_ 는 edge_id 의 접두사가 아니다
// req.setEdge_id("TEST_EDGE_01"); // ❌ TEST_ 는 어느 도메인에서도 접두사가 아니다
2.5 Asset ID — Core of Hierarchy
The asset hierarchy consists of five layers: Enterprise → Site → Area → Line → Equipment (ISA-95). All five layers are nodes in the asset tree, and the layer of a node is defined by asset_type.
| asset_type | Layer | Regex |
|---|---|---|
E | Enterprise | ^ENTERPRISE_[A-Z0-9_]+$ |
S | Site | ^SITE_[A-Z0-9_]+$ |
A | Area | ^(AREA_[A-Z0-9_]+|ASSET_[A-Z0-9_]+)$ |
L | Line | ^(LINE_[A-Z0-9_]+|ASSET_[A-Z0-9_]+)$ |
M | Equipment (Machine) | ^(EQUIPMENT_[A-Z0-9_]+|ASSET_[A-Z0-9_]+)$ |
💡 Why the EQUIPMENT code is
M: Equipment = Machine. A short, clear single-character code is used for searching and filtering.
There is only one Enterprise per installation. Creating a second one or deleting the only existing Enterprise is rejected with 409 (enterprise.only_one) — see the Site service.
The layer of a node cannot be changed. Modifications attempting to change an Area to a Line will be rejected; if the layer is incorrect, create a new node rather than moving it.
Standard ID Pattern Example
One Area, one Line within that Area, and one Equipment above it for Daejeon Plant (SITE_DJ) — when created with blank IDs, they appear as follows:
AREA_000001 ← 구역
└ LINE_000001 ← 라인
└ EQUIPMENT_000001 ← 설비
Example — Asset Tree Creation
import plantpulse.api.v5.dto.request.AssetRequestV5;
import plantpulse.api.v5.dto.response.AssetResponseV5;
// 1) 사이트 직속 Area
AssetRequestV5 area = new AssetRequestV5();
area.setSite_id("SITE_DJ");
area.setParent_asset_id("SITE_DJ"); // 부모는 사이트 ID
area.setAsset_name("조립구역");
area.setAsset_type("A");
client.asset().create(area);
// asset_id 를 비워 두면 플랫폼이 발급합니다 — 응답에서 AREA_000001 을 받습니다
// 2) Area 산하 Line
AssetRequestV5 line = new AssetRequestV5();
line.setSite_id("SITE_DJ");
line.setParent_asset_id("AREA_000001"); // 부모는 Area
line.setAsset_name("1라인");
line.setAsset_type("L");
client.asset().create(line);
// 3) Line 산하 Equipment
AssetRequestV5 equip = new AssetRequestV5();
equip.setSite_id("SITE_DJ");
equip.setParent_asset_id("LINE_000001"); // 부모는 Line
equip.setAsset_name("CNC #1");
equip.setAsset_type("M");
equip.setAsset_class_code("CNC"); // 설비 분류 — 기준정보에 있는 코드
client.asset().create(equip);
parent_asset_id Rules
| Child Type | parent_asset_id Value |
|---|---|
Area (A) | Site ID (e.g., SITE_DJ) |
Line (L) | Area asset_id (e.g., AREA_000001) |
Equipment (M) | Line asset_id (e.g., LINE_000001) |
The parent must be the immediately preceding layer. Skipping layers or assigning a node from a different site as the parent will result in rejection.
2.6 Tag ID
정규식: ^TAG_[A-Z0-9_]+$
허용: TAG_… 만 (가상 태그도 TAG_ 로 시작한다)
A Tag is a definition of a data point (sensor value, counter, status, etc.). It has a 1:1 relationship with an OPC and a 0:N (optional connection) relationship with an Asset.
Recommended Patterns
| Scenario | Pattern | Example |
|---|---|---|
| OPC connection tag | TAG_<part-of-opc-id>_<channel-no> | TAG_EDGE_00303_90007 |
| Manually defined tag | TAG_<domain>_<number> | TAG_PROD_COUNT_001 |
| Virtual Tag | TAG_V_<purpose>_<number> | TAG_V_OEE_LINE1 |
💡 Virtual Tag: Calculation and aggregation tags also use the prefix
TAG_. If distinction is needed, the conventionTAG_V_is used (naming convention, not a separate pattern). Whether a tag is a «calculated value» is determined by the tag's calculation settings (use_calculation). See Virtual Tag for creation instructions.
Examples
import plantpulse.api.v5.dto.request.TagRequestV5;
TagRequestV5 req = new TagRequestV5();
req.setTag_id("TAG_EDGE_00303_90007");
req.setTag_name("Spindle RPM");
req.setOpc_id("OPC_EDGE_00303");
req.setSite_id("SITE_DJ");
req.setLinked_asset_id("EQUIPMENT_000001");
req.setJava_type("Double");
req.setUnit("RPM");
req.setDescription("Spindle 회전수 (RPM)");
client.tag().create(req);
// Virtual Tag — 라인별 OEE 집계
TagRequestV5 vtag = new TagRequestV5();
vtag.setTag_id("TAG_V_OEE_LINE1");
vtag.setTag_name("Line1 OEE");
vtag.setSite_id("SITE_DJ");
client.tag().create(vtag);
tag_id vs tag_name vs alias_name
| Field | Changeable in V5 | Purpose |
|---|---|---|
tag_id | Not changeable (Recreation recommended) | Internal system unique key. Immutable |
tag_name | update() or patchBasic() | Name visible to user |
alias_name | patchMetadata() | Alias for mapping with external systems (e.g., HMI tag name) |
It is recommended to keep
tag_idimmutable and usetag_name/alias_namefor display names. Changing the tag_id may cause consistency issues with external time series data.
2.7 AlarmConfig / Employee / Customer / Product / Flow
All these domains are subject to mandatory server validation. Use the accurate full prefix. AlarmConfig IDs only accept numeric formats (ALARM_CONFIG_000001).
// Alarm Config
AlarmConfigRequestV5 alarm = new AlarmConfigRequestV5();
alarm.setAlarm_config_id("ALARM_CONFIG_000001");
alarm.setSite_id("SITE_DJ");
// Employee
EmployeeRequestV5 emp = new EmployeeRequestV5();
emp.setEmployee_id("EMPLOYEE_E12345");
emp.setRole_code("OPERATOR"); // OPERATOR, SUPERVISOR 등
// Customer — CUSTOMER_ (전체 단어, CUST_ 아님!)
CustomerRequestV5 cust = new CustomerRequestV5();
cust.setCustomer_id("CUSTOMER_001");
cust.setExternal_customer_id("ERP_C001");
// Product — PRODUCT_ (전체 단어, PROD_ 아님!)
ProductRequestV5 prod = new ProductRequestV5();
prod.setProduct_id("PRODUCT_A001");
prod.setProduct_code("SKU-A001");
// Flow
FlowRequestV5 flow = new FlowRequestV5();
flow.setFlow_id("FLOW_OEE_DAILY");
flow.setSite_id("SITE_DJ");
❌ Common Rejection Cases
cust.setCustomer_id("CUST_001"); // ❌ CUST_ 아님 → E1002
prod.setProduct_id("PROD_A001"); // ❌ PROD_ 아님 → E1002
emp.setEmployee_id("E12345"); // ❌ EMPLOYEE_ 누락 → E1002
flow.setFlow_id("flow_oee_daily"); // ❌ 소문자 → E1002
External System Name (external_system)
The external_system for Customer, Product, and Employee only accepts 1–50 uppercase letters, numbers, _, and : (^[A-Z0-9_:]{1,50}$).
: is used when appending a range to make the number unique within that system — e.g., SAP PM client-specific SAP_PM:100.
The Platform does not interpret the range and uses the external_system + external_id pair as the key.
Hyphens, such as in SAP-PM, are rejected, and the rejection message specifies the allowed characters.
2.8 User ID (Login Account)
정규식: ^[A-Z][A-Z0-9_]*$
규칙: 대문자로 시작, 이후 [A-Z0-9_]만 허용
Unlike other domains, it has no fixed prefix, and the first letter must be uppercase.
✅ ADMIN
✅ OPERATOR_01
✅ KOPENS_USER
❌ admin → 소문자 시작
❌ 1USER → 숫자 시작
❌ _ADMIN → _ 시작
2.9 Calendar / Order
The server also validates the prefixes for these two domains. Calendar is ^(CALENDAR_[A-Z0-9_]+|CAL_[A-Z0-9_]+)$, and Work Order is ^(ORDER_[A-Z0-9_]+|ORD_[A-Z0-9_]+)$.
Calendar
import plantpulse.api.v5.dto.request.CalendarRequestV5;
CalendarRequestV5 cal = new CalendarRequestV5();
// calendar_id 를 비워 두면 플랫폼이 CALENDAR_000001 꼴로 발급합니다
cal.setSite_id("SITE_DJ");
cal.setAsset_id("EQUIPMENT_000001");
cal.setTarget_type("MTN"); // 점검 일정
| target_type | Meaning |
|---|---|
MTN | Maintenance |
HOLIDAY | Holiday/Non-operation |
INSPECTION | Inspection |
MEETING | Meeting |
Order
import plantpulse.api.v5.dto.Quantity;
import plantpulse.api.v5.dto.request.OrderRequestV5;
OrderRequestV5 order = new OrderRequestV5();
order.setOrder_id("ORDER_000001");
order.setSite_id("SITE_DJ");
order.setAsset_id("EQUIPMENT_000001");
order.setCustomer_id("CUSTOMER_001"); // CUSTOMER_ 주의!
order.setProduct_id("PRODUCT_A001"); // PRODUCT_ 주의!
order.setEmployee_id("EMPLOYEE_E12345");
order.setPlanned_quantity(Quantity.of("500", "EA")); // ISA-95 Quantity — 값 + UN/ECE Rec 20 단위
Work order status (ISA-88): WAIT → START → END / ABORTED (For more details, see Order service)
2.10 ID Design Best Practices
Recommendations
- Maintain Immutability — Do not change an ID once assigned. If a change is necessary, migrating to a new ID is recommended.
- Do Not Embed Meaning in IDs — Layers are defined by
asset_type, and locations are defined by parents/paths. If manually creating IDs, adhere to the prefix full name. - Use Automatic IDs — If the ID field is left blank, the Platform issues an ID consisting of the prefix + six digits.
- Fix Uppercase and Underscores — Avoid mixing cases across the system and maintain the
[A-Z0-9_]pattern. - External System IDs in Separate Fields — Store original ERP/MES IDs in the
external_*_idfield and maintain the PlantPulse ID's own system.
Patterns to Avoid
- ❌ Use of Korean, spaces, or special characters —
LINE_라인1,OPC 001 - ❌ IDs that are too short or meaningless —
S1,A,T01 - ❌ Missing prefixes or incorrect abbreviations —
CUST_001(Correct:CUSTOMER_001),PROD_A001(Correct:PRODUCT_A001) - ❌ Embedding meaning in IDs, making them unchangeable —
LINE_DJ_OLD_BROKEN_LINE
2.11 Checking Validation Failure Responses
In V5, upon validation failure, the method returns null (or false). Detailed errors can be found in the envelope.
import plantpulse.api.v5.service.BaseServiceV5;
import plantpulse.json.JSONObject;
// V5 서비스 내부는 envelope를 외부에 노출하지 않으나,
// 디버그 모드를 켜면 로그에 출력됩니다.
client = new APIClient_V5(proto, host, port, user, token, true); // debug=true
Response envelope example:
{
"_status": "ERROR",
"_code": "E1002",
"_message": "validation failed: site_id must match ^SITE_[A-Z0-9_]+$",
"_http_status": 422
}
For detailed error codes, refer to Response Format and Error Handling.
2.12 Pre-receiving IDs — POST /api/v5/domain-ids
Used by simulators or external systems that need to know the ID before creating an object.
Request {"domain": "EQUIPMENT", "count": 3} → Response {"domain": "EQUIPMENT", "ids": ["EQUIPMENT_000123", ...]}
| Column | Value |
|---|---|
domain | Domain name (case-insensitive). Only for those with sequences — ASSET · EDGE · SHIFT · SHIFT_PATTERN · HOLIDAY · BLOB are not allowed |
count | 1–1000, default 1 |
Requires ADMIN or API tokens. Since it retrieves a number without creating a row, it is normal for unused numbers to be missing. Calling it again yields a new number (unless the same Idempotency-Key is sent).
| Response | Meaning |
|---|---|
400 domain_id.domain_required | No domain |
404 domain_id.unknown | Domain does not exist or has no sequence |
422 domain_id.count_invalid | count is outside the 1–1000 range |
ID Field in the Console Registration Screen
To the right of the ID field in the registration screen, there is a Generate icon (shows «Generate» on hover). Clicking it fills the field with a new number. Manually entered text is converted to uppercase, and format/duplicate checks are performed when the field loses focus, with issues displayed below the field. In the edit screen, the ID field is locked and the icon is absent. Domains without an issuance sequence (Edge, Shift, Work Pattern, Holiday, Object) also lack the icon.
The ID field is mandatory in the Console. Use Generate or enter manually. The only field that is issued when left blank is the process segment. Generation and duplicate checks are available only to users with ADMIN or API roles.
Next Steps
- Response Format and Error Handling — Error code mapping
- Site service — Start with site creation
- Asset service — Build asset tree
- Tag service — Define data points