EKA-GPS Knowledge Base
Version: 1.6.0-hotfix-1 | Last Updated: Feb 2026
1. Overview
EKA-GPS is a TCP server microservice that receives real-time GPS telemetry from vehicle tracking devices (trucks), persists location data to MySQL, and manages geofencing-based pickup/delivery workflow state via Redis.
It is deployed on a dedicated server and serves as the central ingestion point for all fleet GPS data within the EKA logistics ecosystem.
2. Connection Details
For Third-Party GPS Device Providers
| Protocol | TCP (raw socket, plain text) |
| Host | ekagps.iotapp.my |
| Port | 6091 |
| Encoding | ASCII / UTF-8 |
| Authentication | None (open TCP) |
The server expects a plain text string sent over a raw TCP socket connection. No HTTP, no WebSocket, no headers.
3. Data Format
Packet Structure
Each GPS update must be a single line, comma-separated, with exactly 10 fields:
IGT,CLIENTCODE-PLATENUMBER,YYYY-MM-DD,HH:MM:SS,IgnitionStatus,Latitude,Longitude,Speed,Direction,PTOStatus
Critical: Packets with fewer or more than 10 fields are silently dropped.
Field Reference
| # | Field | Type | Example | Notes |
|---|---|---|---|---|
| 1 | Provider Code | String | IGT |
Fixed identifier from GPS provider |
| 2 | Client-Plate | String | EKJY-JUS9065 |
CLIENTCODE-PLATENUMBER or just PLATENUMBER if no hyphen |
| 3 | Date | String | 2025-02-26 |
Format: YYYY-MM-DD |
| 4 | Time | String | 16:30:39 |
Format: HH:MM:SS — must be Malaysia time (UTC+08:00) |
| 5 | Ignition | String | On / Off |
Case-sensitive |
| 6 | Latitude | Float | 1.528220 |
Decimal degrees |
| 7 | Longitude | Float | 103.818243 |
Decimal degrees |
| 8 | Speed | Integer | 70 |
Numeric, in km/h |
| 9 | Direction | String | North / South-East |
Cardinal or intercardinal direction |
| 10 | PTO Status | String | ON / OFF |
Power Take-Off (hydraulic system) |
Client-Plate Parsing Rule
"EKJY - JUS9065" → clientCode = "EKJY", plateNumber = "JUS9065"
"M3" → clientCode = null, plateNumber = "M3"
If the field contains a hyphen (-), it is split into clientCode and plateNumber. Whitespace is trimmed from both.
Example Packets
IGT,EKJY-JUS9065,2025-02-26,16:30:39,On,1.528220,103.818243,70,South-East,OFF
IGT,TSC-MDM7648,2025-02-26,16:30:39,On,2.010985,103.058758,28,South-West,ON
IGT,M3,2025-02-26,13:42:40,Off,2.994904,101.453437,0,North,OFF
4. How to Test Connection
From Linux/Mac (netcat)
echo "IGT,EKJY-JUS9065,2025-02-26,10:00:00,On,1.528220,103.818243,0,North,OFF" | nc ekagps.iotapp.my 6091
From Windows (PowerShell)
$client = New-Object System.Net.Sockets.TcpClient("ekagps.iotapp.my", 6091)
$stream = $client.GetStream()
$data = [System.Text.Encoding]::ASCII.GetBytes("IGT,EKJY-JUS9065,2025-02-26,10:00:00,On,1.528220,103.818243,0,North,OFF")
$stream.Write($data, 0, $data.Length)
$client.Close()
From Node.js
const net = require('net');
const client = new net.Socket();
client.connect(6091, 'ekagps.iotapp.my', () => {
client.write('IGT,EKJY-JUS9065,2025-02-26,10:00:00,On,1.528220,103.818243,0,North,OFF');
client.end();
});
On Server (SSH)
node -e "const net=require('net');const c=new net.Socket();c.connect(6091,'127.0.0.1',()=>{c.write('IGT,EKJY-JUS9065,2025-02-26,10:00:00,On,1.528220,103.818243,0,North,OFF');c.end();});c.on('close',()=>console.log('Done'));c.on('error',e=>console.error(e.message));"
5. Server-Side Processing Logic
Step-by-Step Data Flow
GPS Device
│
│ TCP packet (raw text, 10 fields)
▼
TCP Server @ ekagps.iotapp.my:6091
│
├─ [1] Validate: exactly 10 comma-separated fields?
│ NO → Log error, drop packet, close
│ YES → Continue
│
├─ [2] Parse all fields
│ • Split Client-Plate by "-" to extract clientCode + plateNumber
│ • Convert datetime from Malaysia (UTC+08:00) → UTC
│ • Cast latitude, longitude, speed to numbers
│
├─ [3] Multicast (Production only)
│ • Forward raw packet to: 10.10.0.81:6091, 10.10.0.160:6091
│ • Dev/Staging: skipped (TCP_HOST is empty)
│
└─ [4] MySQL Insert
• Table: truck_location
• ON DUPLICATE KEY UPDATE (keyed by plate_number)
• Stores latest GPS snapshot per truck
What Gets Stored (truck_location table)
| Column | Value |
|---|---|
provider_code |
e.g. IGT |
client_code |
e.g. EKJY (or NULL) |
plate_number |
e.g. JUS9065 |
datetime |
UTC timestamp |
ignition |
On / Off |
latitude |
float |
longitude |
float |
speed |
integer |
direction |
e.g. South-East |
pto_status |
ON / OFF |
6. Architecture & Infrastructure
Tech Stack
| Component | Technology |
|---|---|
| Runtime | Node.js |
| TCP Server | net (Node.js built-in) |
| Database | MySQL 8 (ekadb schema) |
| Cache / Pub-Sub | Redis 6 |
| Geofencing | geolib v3.3.4 |
| Datetime | luxon v3.5.0 |
| Process Manager | PM2 |
Production Multicast Targets
Production forwards every GPS packet to two additional servers:
10.10.0.81:609110.10.0.160:6091
Key Database Tables
| Table | Purpose |
|---|---|
truck_location |
Latest GPS snapshot per truck (inserted/updated on every packet) |
7. Error Handling & Known Behaviors
| Scenario | Behavior |
|---|---|
| Packet has ≠ 10 fields | Dropped silently, error logged |
| No hyphen in Client-Plate field | clientCode = null, no crash |
| Redis down | TCP server continues, MySQL writes still work |
| TCP_HOST empty | Multicast skipped, no error |
| Duplicate plate_number in DB | Row updated (not duplicated) via ON DUPLICATE KEY UPDATE |
8. Version History (Summary)
| Version | Date | Key Change |
|---|---|---|
| v1.0.0 | Sep 2024 | Initial release |
| v1.3.0 | Apr 2025 | Redis + geofencing + multicast added |
| v1.5.0 | Apr 2025 | Redis pub/sub for status updates |
| v1.6.0 | Apr 2025 | Code cleanup and uncommented logic |
| v1.6.0-hotfix-1 | Oct 2025 | DB host changed to localhost (security fix) |
9. Monitoring
# View live logs (PM2)
pm2 logs eka-gps
# View last 50 lines
pm2 logs eka-gps --lines 50
# Process status
pm2 status
Normal Startup Log
Server listening on *:6091
Redis clients connected
Normal Data Receipt Log
Client connected: 1.2.3.4 54321
Raw data: IGT,EKJY-JUS9065,2025-02-26,16:30:39,On,1.528220,103.818243,70,South-East,OFF
Data received: { providerCode: 'IGT', plateNumber: 'JUS9065', ... }
Client disconnected