7. Asset Service
Access via client.asset(). Manage the five-layer tree of the PlantPulse asset model: Enterprise > Site > Area > Line > Equipment.
ID Rules: Area
AREA_· LineLINE_· EquipmentEQUIPMENT_(existingASSET_are also valid). Ifasset_idis left blank, the Platform will issue one. The tree layer is determined byasset_type, not the ID. For more details, refer to Domain ID Rules.
7.1 Method List
| Method | Return Type | HTTP | Endpoint |
|---|---|---|---|
create(AssetRequestV5) | AssetResponseV5 or null | POST | /api/v5/asset |
update(asset_id, AssetRequestV5) | AssetResponseV5 or null | PUT | /api/v5/asset/{id} |
delete(asset_id) | boolean | DELETE | /api/v5/asset/{id} |
get(asset_id) | AssetResponseV5 or null | GET | /api/v5/asset/{id} |
list() | List<AssetResponseV5> | GET | /api/v5/asset |
exists(asset_id) | boolean | GET | /api/v5/asset/{id}/exists |
getByExternal(system, external_id) | AssetResponseV5 or null | GET | /api/v5/asset/by-external/{system}/{external_id} |
patchProperties(asset_id, 요청) | AssetResponseV5 or null | PATCH | /api/v5/asset/{id}/properties |
The Asset service focuses on simplified CRUD. For tree hierarchy lookup, use the Path Service or client-side filtering (filter
asset_typeafterlist()).
7.2 Asset Type Values
| asset_type | Meaning |
|---|---|
E | Enterprise — One per installation, root of the tree |
S | Site |
A | Area |
L | Line |
M | Equipment, Machine |
Creating with POST /api/v5/asset consists of A · L · M. Sites are created via the Site Service.
GET /api/v5/asset returns Area, Line, and Equipment if asset_type is not provided; if asset_type=E or S is provided, it returns that specific layer.
Do not count E · S as equipment. Layers are determined by this value, not the ID prefix.
7.3 AssetRequestV5 / AssetResponseV5
| Field | Type | Description |
|---|---|---|
asset_id | String | Asset ID (PK). Issued if left blank |
asset_name | String | Display Name |
parent_asset_id | String | Parent Asset ID — the layer immediately above |
asset_order | String | Sorting order among siblings |
asset_type | String | A/L/M (Response also includes E · S). Cannot be changed after creation |
asset_class_code | String | Equipment classification code (CNC, INJECTION, etc.). Must be a classification for that layer, otherwise 422 |
site_id | String | Affiliated Site |
description | String | Description (Required) |
operational_status_code | String | (Response) Operation status |
external_system / external_id | String | (Response) Number used to refer to this node by other systems |
insert_date / update_date | String | (Response Only) Time |
Single lookups include unique attributes of that layer, while lists include only common fields (operational_status_code · external_system · external_id).
Unique Attributes — /api/v5/asset/{id}/properties
Each layer has its own unique attributes. Read with GET and modify with PATCH (or PUT). Only sent keys are used; null indicates deletion, and unsent keys remain unchanged.
| Layer | Key |
|---|---|
| Common (excl. Enterprise) | operational_status_code, external_system + external_id (Both present or both absent) |
Site S | timezone, country, site_code, address, lat, lng |
Area A | area_function_code(PRODUCTION · STORAGE · UTILITY), responsible_employee_id |
Line L | line_kind_code(PRODUCTION_LINE · PROCESS_CELL · PRODUCTION_UNIT), design_rate(per hour, > 0) + design_rate_unit_of_measure(UN/ECE Rec 20, both present or both absent), responsible_employee_id |
Equipment M | manufacturer, model, serial_number, installed_date, commissioned_date(after installation date), warranty_end_date(yyyy-MM-dd), criticality_code(A · B · C), functional_location(unique within site) |
| Response | Meaning |
|---|---|
422 asset.invalid.<key> | Key not in layer, unknown code, partial pair, date order violation |
422 asset.invalid.asset_type | Attempt to change layer — layer remains unchanged |
409 asset.duplicate.external_id | Other node using the same external number |
409 asset.duplicate.functional_location | Other equipment in the same site using the same functional location |
The effective_responsible_employee_id in the response is the actual person in charge — if their field is blank, they inherit from the nearest superior (Line → Area), and effective_responsible_from_asset_id indicates where it was inherited from.
The qr_payload in the GET /api/v5/asset/{id} response is the address printed on the QR Label. This call does not create a token — it is only populated for equipment where a token was created by opening the label in the console; otherwise, it is "" if it doesn't exist or the console address is blank.
Find by External Number
GET /api/v5/asset/by-external/MAXIMO/BEDFORD%2F11430
GET /api/v5/asset/by-external?system=MAXIMO&external_id=BEDFORD/11430
Searches across all layers. If not found, returns 404 asset.not_found; if only one of the system or number is provided, returns 422.
7.4 Usage Examples
Creating Area → Line → Equipment Tree
import plantpulse.api.v5.dto.request.AssetRequestV5;
// 1) Area
AssetRequestV5 area = new AssetRequestV5();
area.setSite_id("SITE_DJ");
area.setParent_asset_id("SITE_DJ");
area.setAsset_name("조립구역");
area.setAsset_type("A");
client.asset().create(area);
// asset_id 를 비워 두면 플랫폼이 발급합니다 — 응답에서 AREA_000001 을 받습니다
// 2) Line
AssetRequestV5 line = new AssetRequestV5();
line.setSite_id("SITE_DJ");
line.setParent_asset_id("AREA_000001");
line.setAsset_name("1번 조립 라인");
line.setAsset_type("L");
client.asset().create(line);
// 3) Equipment (CNC 머신)
AssetRequestV5 cnc = new AssetRequestV5();
cnc.setSite_id("SITE_DJ");
cnc.setParent_asset_id("LINE_000001");
cnc.setAsset_name("CNC #1");
cnc.setAsset_type("M");
cnc.setAsset_class_code("CNC");
cnc.setDescription("DMG MORI NLX-2500");
client.asset().create(cnc);
Single Lookup
import plantpulse.api.v5.dto.response.AssetResponseV5;
AssetResponseV5 cnc = client.asset().get("EQUIPMENT_000001");
if (cnc != null) {
System.out.println(cnc.getAsset_name() + " (" + cnc.getAsset_type() + ")");
System.out.println("부모: " + cnc.getParent_asset_id());
}
Full List + Type Filtering
Server-side filters are GET /api/v5/asset?asset_type=M · operational_status_code= · external_system=. Since list() of the Java client does not accept filters, filter after receiving.
import java.util.List;
import java.util.stream.Collectors;
List<AssetResponseV5> all = client.asset().list();
// Equipment만
List<AssetResponseV5> equipments = all.stream()
.filter(a -> "M".equals(a.getAsset_type()))
.collect(Collectors.toList());
// 대전공장의 Line만
List<AssetResponseV5> djLines = all.stream()
.filter(a -> "SITE_DJ".equals(a.getSite_id()))
.filter(a -> "L".equals(a.getAsset_type()))
.collect(Collectors.toList());
Tree Reconstruction (Client-side)
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
// asset_id 인덱스
Map<String, AssetResponseV5> byId = new HashMap<>();
for (AssetResponseV5 a : client.asset().list()) {
byId.put(a.getAsset_id(), a);
}
// 부모-자식 매핑
Map<String, List<AssetResponseV5>> children = new HashMap<>();
for (AssetResponseV5 a : byId.values()) {
children
.computeIfAbsent(a.getParent_asset_id(), k -> new ArrayList<>())
.add(a);
}
// 사이트(SITE_DJ) 산하 트리 출력
printTree(children, "SITE_DJ", 0);
static void printTree(Map<String, List<AssetResponseV5>> children, String id, int depth) {
List<AssetResponseV5> kids = children.getOrDefault(id, List.of());
for (AssetResponseV5 c : kids) {
System.out.println(" ".repeat(depth) + c.getAsset_id() + " — " + c.getAsset_name());
printTree(children, c.getAsset_id(), depth + 1);
}
}
Asset Modification
AssetRequestV5 update = new AssetRequestV5();
update.setSite_id("SITE_DJ");
update.setParent_asset_id("LINE_000001");
update.setAsset_id("EQUIPMENT_000001");
update.setAsset_name("CNC #1 (보수 후)");
update.setAsset_type("M");
update.setAsset_class_code("CNC");
update.setDescription("2026-05-10 베어링 교체 완료");
client.asset().update("EQUIPMENT_000001", update);
Asset Deletion
⚠️ All tags and child assets connected to the asset must be deleted before deleting the asset itself.
boolean ok = client.asset().delete("EQUIPMENT_OLD");
7.5 Application Scenarios
Scenario — Equipment list and tag count per site
String siteId = "SITE_DJ";
List<AssetResponseV5> equipments = client.asset().list().stream()
.filter(a -> siteId.equals(a.getSite_id()))
.filter(a -> "M".equals(a.getAsset_type()))
.collect(Collectors.toList());
for (AssetResponseV5 eq : equipments) {
long tagCount = client.tag().countByAsset(eq.getAsset_id());
System.out.printf("%s — Tag %d개%n", eq.getAsset_name(), tagCount);
}
Scenario — Line-unit OEE dashboard data collection
// 1) Line 목록
List<AssetResponseV5> lines = client.asset().list().stream()
.filter(a -> "L".equals(a.getAsset_type()))
.filter(a -> "SITE_DJ".equals(a.getSite_id()))
.collect(Collectors.toList());
// 2) 각 Line 산하 Equipment 조회 → 자산 경로 → 작업지시 → KPI 집계
for (AssetResponseV5 line : lines) {
List<AssetResponseV5> machines = client.asset().list().stream()
.filter(a -> line.getAsset_id().equals(a.getParent_asset_id()))
.collect(Collectors.toList());
for (AssetResponseV5 m : machines) {
long now = System.currentTimeMillis();
// 현재 작업지시는 Order 서비스로 조회
// ...
}
}
Next Steps
- Tag Service — Connecting tags to assets
- Path Service — Querying tree paths in one go