HTTP / MOTORSENSE Driver
Overview
Two modes based on the HTTP protocol:
HTTP— when an external system pushes a value via/api/v1/tag/{tagId}/valueor/api/http, it is bound to the edge internal cache (current_value_map). The cache is read on every collection cycle.MOTOR_SENSE— polls the KOPENS Motor Sense API to collect signal/status data. A separateplantpulse-edge-driver-motorsensor.jaris loaded via reflection.
| Item | HTTP | MOTOR_SENSE |
|---|---|---|
opc_type | HTTP | MOTOR_SENSE |
| Implementation class | plantpulse.driver.protocol.http.HTTPDriver | plantpulse.driver.protocol.motor_sense.MotorSenseDriver (separate jar) |
| read | ✅ (cache) | ✅ (HTTP polling) |
| write | ✅ (bind()) | ❌ |
| Security | None (use a proxy if authentication is required at the REST layer) | API Key + customer-id |
HTTP Mode
OPC Registration Form
| Field | Meaning | Example |
|---|---|---|
opc_agent_ip | (not actually used, for identification) | 127.0.0.1 |
opc_agent_port | (not actually used) | 0 |
timecycle | Cache read cycle (ms) | 1000 |
Tag plc_address Format
A free-form key string. Maps 1:1 to the io_address of bind(io_address, value).
| Notation | Meaning |
|---|---|
temperature | Value that arrived externally via bind("temperature", "25.7") |
line/A/rpm | Free-form string, slashes allowed |
Writing Values (POST /api/v1/tag/{tagId}/value)
curl -X POST http://<edge-host>/api/v1/tag/OPC_HTTP_TAG_00001/value \
-H "Content-Type: application/json" \
-d '{ "value": "42.5" }'
Internally this calls HTTPDriver.bind(plc_address, "42.5") → the value is read on the next polling cycle.
curl Registration Example (HTTP)
curl -X POST http://<edge-host>/api/v1/opc \
-H "Content-Type: application/json" \
-d '{
"opc_id": "OPC_HTTP_BIND",
"opc_type": "HTTP",
"opc_name": "External HTTP Bind",
"opc_agent_ip": "127.0.0.1",
"opc_agent_port": "0",
"site_id": "SITE_00001",
"auto_collect": true,
"timecycle": 1000,
"tag_list": [
{
"tag_id": "OPC_HTTP_BIND_TAG_00001",
"tag_name": "ExtTemp",
"plc_address": "temperature",
"data_type": "Float"
}
]
}'
MOTOR_SENSE Mode
Characteristics
- No IP / Port entry required (uses the HTTP protocol, though the registration form auto-fills the OPC agent IP/port)
- No manual tag entry required — tags are generated automatically (
signals,health_index, 2 tags) - Collects data in JSON format
- Displays data grouped by ASSET
OPC Registration Form
| Field | Meaning | Storage Location |
|---|---|---|
| API KEY | Motor Sense API authentication key | options.api-key |
| Customer ID | Customer identifier | options.customer-id |
| Asset List URL | URL for retrieving the asset list | options.asset-list-url |
| Asset Signal URL | URL for asset signal data | options.asset-signal-url |
| Asset Health Index URL | URL for the asset health index | options.asset-status-url |
| Load waveform/spectrum | Whether to load waveform/spectrum data | options.load-waveform-spectrum (optional) |
URLs must start with http:// or https:// (validated on the JS side).
Auto-generated Tags
tag_id | tag_name | plc_address | data_type |
|---|---|---|---|
<OPC_ID>_TAG_00001 | signals | signals | String |
<OPC_ID>_TAG_00002 | health_index | health_index | String |
VALUE is stored as a JSON array string (multiple ASSET entries within a single tag).
signals VALUE Example
[
{
"asset_id": "4d4c097f-4449-4cf0-a7ec-b770e3950e00",
"sensor_number": "1300",
"created_at": 1768376660,
"temperature": 25.7,
"voltage": 3.31,
"rms_x": 0.1733,
"rms_y": 0.4156,
"rms_z": 0.4697,
"rms_xyz": 0.5476
}
]
health_index VALUE Example
[
{
"asset_id": "4d4c097f-4449-4cf0-a7ec-b770e3950e2a",
"time": "2021-12-21",
"imbalance_health": 0.98,
"misalignment_health": 1.0,
"looseness_health": 1.0,
"bearing_health": 1.0,
"asset_health": 0.99
}
]
curl Registration Example (MOTOR_SENSE)
curl -X POST http://<edge-host>/api/v1/opc \
-H "Content-Type: application/json" \
-d '{
"opc_id": "OPC_MS_PLANT_A",
"opc_type": "MOTOR_SENSE",
"opc_name": "Motor Sense Plant A",
"opc_agent_ip": "127.0.0.1",
"opc_agent_port": "0",
"site_id": "SITE_00001",
"auto_collect": true,
"timecycle": 1000,
"options": {
"api-key": "YOUR_API_KEY",
"customer-id": "kopens-001",
"asset-list-url": "https://api.example.com/assets",
"asset-signal-url": "https://api.example.com/signals",
"asset-status-url": "https://api.example.com/health",
"load-waveform-spectrum": "false"
},
"tag_list": [
{
"tag_id": "OPC_MS_PLANT_A_TAG_00001",
"tag_name": "signals",
"plc_address": "signals",
"data_type": "String",
"description": "에셋의 시그널 데이터"
},
{
"tag_id": "OPC_MS_PLANT_A_TAG_00002",
"tag_name": "health_index",
"plc_address": "health_index",
"data_type": "String",
"description": "에셋의 상태 지수"
}
]
}'
Behavioral Differences Unique to MOTOR_SENSE
| Standard PLC | MOTOR_SENSE |
|---|---|
read is skipped if isConnected() is false | read is attempted even when isConnected()=false (HTTP-based, so brief disconnections are common) |
| Standard TAG table shown on screen | Cards per ASSET (HEALTH INDEX / SIGNAL) shown |
| Refresh = screen reload | Background refresh every 1 second (no loading indicator) |
Common Errors and Resolutions
| Message / Symptom | Cause | Resolution |
|---|---|---|
MOTOR_SENSE 드라이버를 사용하려면 plantpulse-edge-driver-motorsensor.jar를 classpath에 추가 | Separate jar not installed | Place the jar in WebContent/WEB-INF/lib/ and redeploy |
401 Unauthorized (Motor Sense) | API KEY / customer-id error | Use the exact key issued by KOPENS. Also verify the URL endpoint |
value is always empty in HTTP mode | bind() is never called, or plc_address and io_address do not match | Push via POST /api/v1/tag/{tagId}/value and wait for the next cycle |
tag write not supported for opc_type=... | write called on a non-HTTP protocol | write is only possible on HTTP protocol tags. See the follow-up Phase 3 |
Example Collection (by Data Type, HTTP Mode)
HTTP mode accepts the free-form string values pushed by external systems as-is. raw is a String, and data_type / fomula are used for post-processing.
Boolean
data_type | plc_address | Push example (POST /api/v1/tag/.../value) | Notes |
|---|---|---|---|
Boolean | pump1.run | {"value":"true"} | true/false string |
Boolean | door.open | {"value":"1"} | 1/0 also possible (cast in post-processing) |
Integer
data_type | plc_address | Push example | Notes |
|---|---|---|---|
Integer | counter.production | {"value":"12345"} | |
Long | counter.total | {"value":"9876543210"} | Exceeds 32-bit |
Floating Point
data_type | plc_address | Push example | Notes |
|---|---|---|---|
Float | temperature | {"value":"25.7"} | |
Double | power.kw | {"value":"1234.56789"} | High precision |
String
data_type | plc_address | Push example | Notes |
|---|---|---|---|
String | recipe.name | {"value":"R-2025-A"} | Free-form string |
String | barcode.last | {"value":"880699112"} | Barcode capture push |
Using Formulas (HTTP)
| Purpose | data_type | fomula | Notes |
|---|---|---|---|
| Scale an externally pushed value | Float | ${VALUE}*1.8+32 | C → F |
| Sum two external values | Float | ${VALUE}+${EXT_OFFSET} | References another HTTP tag |
Comprehensive curl Example (HTTP Push Flow)
# 1) OPC + 태그 등록
curl -X POST http://<edge-host>/api/v1/opc \
-H "Content-Type: application/json" \
-d '{
"opc_id": "OPC_HTTP_FULL",
"opc_type": "HTTP",
"opc_name": "External Bind Full",
"opc_agent_ip": "127.0.0.1",
"opc_agent_port": "0",
"site_id": "SITE_00001",
"auto_collect": true,
"timecycle": 1000,
"tag_list": [
{"tag_id":"OPC_HTTP_FULL_T01", "tag_name":"PumpRun", "plc_address":"pump1.run", "data_type":"Boolean"},
{"tag_id":"OPC_HTTP_FULL_T02", "tag_name":"Counter", "plc_address":"counter.prod", "data_type":"Integer"},
{"tag_id":"OPC_HTTP_FULL_T03", "tag_name":"TempC", "plc_address":"temperature", "data_type":"Float"},
{"tag_id":"OPC_HTTP_FULL_T04", "tag_name":"TempF", "plc_address":"temperature", "data_type":"Float", "fomula":"${VALUE}*1.8+32"},
{"tag_id":"OPC_HTTP_FULL_T05", "tag_name":"BatchName", "plc_address":"recipe.name", "data_type":"String"}
]
}'
# 2) 외부에서 값 push
curl -X POST http://<edge-host>/api/v1/tag/OPC_HTTP_FULL_T01/value \
-H "Content-Type: application/json" -d '{"value":"true"}'
curl -X POST http://<edge-host>/api/v1/tag/OPC_HTTP_FULL_T03/value \
-H "Content-Type: application/json" -d '{"value":"25.7"}'
# 3) 다음 polling 주기 후 조회
curl -s http://<edge-host>/api/v1/tag/OPC_HTTP_FULL_T03/value | jq
curl -s http://<edge-host>/api/v1/tag/OPC_HTTP_FULL_T04/value | jq # fomula 적용된 화씨값