Skip to main content

18. Integration Examples

A collection of end-to-end scenarios frequently performed with the V5 client.

18.1 New Plant Bring-Up — Registering All Master Data

A scenario that registers a new site and sets up the asset tree (Area → Line → Equipment) → OPC → Tag in one pass.

import plantpulse.api.v5.APIClient_V5;
import plantpulse.api.v5.dto.request.*;
import plantpulse.api.v5.dto.response.*;

public class BootstrapNewSite {
public static void main(String[] args) throws Exception {

try (APIClient_V5 client = new APIClient_V5(
"HTTPS", "100.68.69.41", 7443, "api", TOKEN, false)) {

client.connect();

// 1) 사이트 등록
SiteRequestV5 site = new SiteRequestV5();
site.setSite_id("SITE_DJ");
site.setSite_name("대전 1공장");
site.setLat("36.3504");
site.setLng("127.3845");
client.site().create(site);

// 2) Area
AssetRequestV5 area = new AssetRequestV5();
area.setSite_id("SITE_DJ");
area.setParent_asset_id("SITE_DJ");
area.setAsset_id("ASSET_DJ_A_0001");
area.setAsset_name("조립구역");
area.setAsset_type("A");
client.asset().create(area);

// 3) Line
AssetRequestV5 line = new AssetRequestV5();
line.setSite_id("SITE_DJ");
line.setParent_asset_id("ASSET_DJ_A_0001");
line.setAsset_id("ASSET_DJ_L_0001");
line.setAsset_name("1번 조립 라인");
line.setAsset_type("L");
client.asset().create(line);

// 4) Equipment (3대)
for (int i = 1; i <= 3; i++) {
AssetRequestV5 eq = new AssetRequestV5();
eq.setSite_id("SITE_DJ");
eq.setParent_asset_id("ASSET_DJ_L_0001");
eq.setAsset_id(String.format("ASSET_DJ_M_%04d", i));
eq.setAsset_name("CNC #" + i);
eq.setAsset_type("M");
eq.setTable_type("CNC");
client.asset().create(eq);
}

// 5) OPC (PLC)
OPCRequestV5 plc = new OPCRequestV5();
plc.setOpc_id("OPC_PLC_L1_001");
plc.setOpc_name("Line1 메인 PLC");
plc.setOpc_type("PLC");
plc.setOpc_sub_type("SIEMENS_S7");
plc.setSite_id("SITE_DJ");
plc.setOpc_server_ip("192.168.10.50");
plc.setOpc_agent_ip("192.168.10.5");
plc.setOpc_agent_port(60000);
client.opc().create(plc);

// 6) Tag (각 Equipment에 RPM/온도/카운트 3개씩)
for (int i = 1; i <= 3; i++) {
String equipId = String.format("ASSET_DJ_M_%04d", i);
for (String metric : new String[]{"RPM", "TEMP", "COUNT"}) {
TagRequestV5 tag = new TagRequestV5();
tag.setSite_id("SITE_DJ");
tag.setOpc_id("OPC_PLC_L1_001");
tag.setLinked_asset_id(equipId);
tag.setTag_id(String.format("TAG_PLC_L1_M%04d_%s", i, metric));
tag.setTag_name("CNC#" + i + " " + metric);
tag.setTag_source("OPC");
tag.setJava_type("Double");
tag.setUnit(metricUnit(metric));
client.tag().create(tag);
}
}

System.out.println("부팅 완료");
}
}

private static String metricUnit(String m) {
switch (m) {
case "RPM": return "RPM";
case "TEMP": return "C";
case "COUNT": return "EA";
default: return "";
}
}
}

18.2 ERP Master Synchronization (Idempotent)

Synchronizes customer/product/employee data received from ERP into the V5 master. Check exists(), then branch to create() or update().

public static void syncCustomers(APIClient_V5 client, List<ErpCustomer> erpData) {
for (ErpCustomer erp : erpData) {
String customerId = "CUST_" + erp.code;

CustomerRequestV5 req = new CustomerRequestV5();
req.setCustomer_id(customerId);
req.setCustomer_name(erp.name);
req.setSite_id(erp.siteId);
req.setExternal_customer_id(erp.id);
req.setCompany_code(erp.companyCode);
req.setManager_name(erp.contact);
req.setManager_email(erp.email);

if (client.customer().exists(customerId)) {
client.customer().update(customerId, req);
} else {
client.customer().create(req);
}
}
}

18.3 Creating, Starting, and Closing MES Work Orders

public static void runMesWorkOrder(APIClient_V5 client, MesOrder mes) {
// 1) Order 생성
String orderId = "ORD_" + mes.date + "_" + String.format("%03d", mes.seq);
OrderRequestV5 req = new OrderRequestV5();
req.setOrder_id(orderId);
req.setTitle(mes.title);
req.setSite_id("SITE_DJ");
req.setAsset_id(mes.equipId);
req.setProduct_id(mes.productId);
req.setEmployee_id(mes.operatorId);
req.setCustomer_id(mes.customerId);
req.setTarget_units(mes.qty);
req.setUnit("EA");
req.setTime_per_unit_in_ms(mes.cycleTime);
req.setFix_start_timestamp(mes.plannedStart);
req.setFix_end_timestamp(mes.plannedEnd);
req.setExternal_order_id(mes.mesOrderId);

OrderResponseV5 created = client.order().create(req);
if (created == null) {
log.error("작업지시 생성 실패: " + orderId);
return;
}

// 2) 작업 시작 (생성 직후가 아닌, 라인 IDLE 신호 받은 시점에 호출)
if (!client.order().start(orderId)) {
log.warn("시작 실패 — 현재 상태 확인 필요");
return;
}

// 3) 작업 진행 모니터링 ...
// ...

// 4) 작업 종료
client.order().end(orderId);
}

18.4 Line-Wide Health Dashboard

A dashboard data collector that gathers the tag count, recent alarms, and current work orders for all equipment under a line in one pass.

import java.util.List;
import java.util.stream.Collectors;

public static DashboardSnapshot collectLineHealth(APIClient_V5 client, String lineId) {
DashboardSnapshot snap = new DashboardSnapshot();

// 1) Line 산하 Equipment
List<AssetResponseV5> equipments = client.asset().list().stream()
.filter(a -> lineId.equals(a.getParent_asset_id()))
.filter(a -> "M".equals(a.getAsset_type()))
.collect(Collectors.toList());

snap.equipmentCount = equipments.size();

// 2) 각 Equipment 통계
for (AssetResponseV5 eq : equipments) {
long tagCount = client.tag().countByAsset(eq.getAsset_id());
snap.totalTags += tagCount;

PathResponseV5 path = client.path().getByAsset(eq.getAsset_id());
snap.paths.put(eq.getAsset_id(),
String.format("%s > %s > %s > %s",
path.getSite_name(), path.getArea_name(),
path.getLine_name(), path.getEquipment_name()));
}

// 3) 최근 알람 — 라인 산하 자산만
List<String> assetIds = equipments.stream()
.map(AssetResponseV5::getAsset_id)
.collect(Collectors.toList());

snap.recentAlarms = client.alarm().list(200).stream()
.filter(a -> a.getTag_location() != null
&& assetIds.stream()
.anyMatch(id -> a.getTag_location().contains(id)))
.collect(Collectors.toList());

// 4) 현재 진행중 작업지시
snap.activeOrders = client.order().list().stream()
.filter(o -> "START".equals(o.getStatus())
|| "PAUSE".equals(o.getStatus()))
.filter(o -> assetIds.contains(o.getAsset_id()))
.collect(Collectors.toList());

return snap;
}

18.5 Bulk Application of Tag Alarm Thresholds

Applies alarm thresholds in bulk to every temperature tag on a given line following a change to the operating environment.

public static void applyTempAlarmsToLine(APIClient_V5 client, String lineId) {
// 1) 라인 산하 Equipment
List<AssetResponseV5> equipments = client.asset().list().stream()
.filter(a -> lineId.equals(a.getParent_asset_id()))
.collect(Collectors.toList());

// 2) 각 Equipment의 온도 태그
for (AssetResponseV5 eq : equipments) {
List<TagResponseV5> tempTags = client.tag().listByAsset(eq.getAsset_id())
.stream()
.filter(t -> "C".equals(t.getUnit()))
.collect(Collectors.toList());

// 3) patchAlarm으로 임계치 일괄 적용 (서버에서 CEP/EQL 재배포)
for (TagResponseV5 t : tempTags) {
TagAlarmPatchRequestV5 alarm = new TagAlarmPatchRequestV5();
alarm.setUse_alarm("Y");
alarm.setLo_lo("0");
alarm.setLo("10");
alarm.setHi("80");
alarm.setHi_hi("95");
alarm.setTrip_hi("100");
alarm.setDuplicate_check_minutes(5);
alarm.setSend_email("Y");
client.tag().patchAlarm(t.getTag_id(), alarm);
}
}
}

18.6 Checking Inspection Schedules for Work Order Conflicts

import java.util.Set;
import java.util.stream.Collectors;

public static List<OrderResponseV5> conflictsWithMaintenance(
APIClient_V5 client, String fromIso, String toIso) {

// 1) 점검 일정의 자산 ID 집합
Set<String> mtnAssets = client.calendar().search(fromIso, toIso).stream()
.filter(c -> "MTN".equals(c.getTarget_type()))
.map(CalendarResponseV5::getAsset_id)
.collect(Collectors.toSet());

// 2) 같은 기간 진행중·예정 작업지시 중 점검 대상 자산
return client.order().list().stream()
.filter(o -> "WAIT".equals(o.getStatus()) || "START".equals(o.getStatus()))
.filter(o -> mtnAssets.contains(o.getAsset_id()))
.collect(Collectors.toList());
}

18.7 Flow Health Notifications

A monitoring worker that sends a notification whenever it detects a Flow in an error state.

public static void monitorFlowHealth(APIClient_V5 client) throws InterruptedException {
Map<String, Long> lastErrorCounts = new HashMap<>();

while (true) {
for (FlowResponseV5 flow : client.flow().list()) {
long prev = lastErrorCounts.getOrDefault(flow.getFlow_id(), 0L);
long curr = flow.getError_count();

if (curr > prev) {
long newErrors = curr - prev;
sendAlert(String.format("Flow %s 에서 신규 에러 %d건 발생",
flow.getFlow_name(), newErrors));
}
lastErrorCounts.put(flow.getFlow_id(), curr);
}
Thread.sleep(30_000);
}
}

18.8 Tag CUD Lifecycle (Test Code Pattern)

A pattern often used in smoke tests and integration tests — create/read/update/delete, followed by cleanup of isolated resources.

public static void tagCudCycle(APIClient_V5 client) {
long ts = System.currentTimeMillis();
String opcId = "OPC_TEST_" + ts;
String tagId = "TAG_TEST_" + ts;

// 1) 부모 OPC 생성 (Tag의 FK)
OPCRequestV5 opcReq = new OPCRequestV5();
opcReq.setOpc_id(opcId);
opcReq.setOpc_name("test-opc-" + ts);
opcReq.setOpc_type("VIRTUAL");
opcReq.setSite_id("SITE_00001");
opcReq.setOpc_agent_ip("127.0.0.1");
opcReq.setOpc_agent_port(0);
client.opc().create(opcReq);

try {
// 2) Tag 생성
TagRequestV5 req = new TagRequestV5();
req.setTag_id(tagId);
req.setTag_name("test-tag-" + ts);
req.setOpc_id(opcId);
req.setSite_id("SITE_00001");
req.setJava_type("Double");
req.setUnit("C");
req.setTag_source("OPC");
TagResponseV5 created = client.tag().create(req);
assert created != null;

// 3) 조회 + 존재 확인
TagResponseV5 fetched = client.tag().get(tagId);
assert fetched != null;
assert client.tag().exists(tagId);

// 4) 수정
req.setTag_name("test-tag-" + ts + "-updated");
TagResponseV5 updated = client.tag().update(tagId, req);
assert updated != null;
assert req.getTag_name().equals(updated.getTag_name());

// 5) 삭제
assert client.tag().delete(tagId);
assert !client.tag().exists(tagId);
} finally {
// 6) 임시 OPC 정리
client.opc().delete(opcId);
}
}

Next Steps

  • For detailed per-domain manuals, choose from the table of contents in the README.
  • It is worth reviewing the ID design principles again in Domain ID Rules.