1314 Protocol Translation Visualizer
Visualize Protocol Translation Flows
1314.1 IoT Protocol Translation Visualizer
Protocol translation is a critical capability in IoT systems where devices and services communicate using different protocols. This interactive tool helps you understand how messages are transformed between common IoT protocols, including header mapping, QoS translation, payload conversion, and latency overhead estimation.
This visualizer demonstrates translation between six major IoT protocols:
- MQTT: Lightweight publish/subscribe messaging for constrained devices
- CoAP: RESTful protocol optimized for low-power IoT devices
- HTTP: Standard web protocol with broad compatibility
- AMQP: Enterprise messaging with advanced routing capabilities
- Modbus: Industrial protocol for SCADA and automation systems
- OPC-UA: Industrial interoperability standard for secure data exchange
- Select the source protocol and target protocol
- Enter or modify the sample message (JSON or XML format)
- Click Translate to process the message
- Observe the translation pipeline stages: Parse, Transform, Encode
- Review the side-by-side comparison of original and translated messages
- Examine header field mapping, QoS mapping, and latency estimation
- Explore common translation patterns in the reference section
Show code
{
// Load D3.js v7
const d3 = await require("d3@7");
// IEEE Color palette
const colors = {
navy: "#2C3E50",
teal: "#16A085",
orange: "#E67E22",
gray: "#7F8C8D",
lightGray: "#ECF0F1",
white: "#FFFFFF",
red: "#E74C3C",
green: "#27AE60",
purple: "#9B59B6",
yellow: "#F1C40F",
darkBlue: "#1a252f"
};
// Protocol definitions with extended properties
const protocols = {
mqtt: {
name: "MQTT",
color: "#660066",
description: "Message Queuing Telemetry Transport",
headers: ["topic", "qos", "retain", "clientId", "messageId"],
qosLevels: ["QoS 0 (At most once)", "QoS 1 (At least once)", "QoS 2 (Exactly once)"],
port: 1883,
overhead: 2,
parseTime: 0.5,
encodeTime: 0.3,
format: "binary",
transport: "TCP"
},
coap: {
name: "CoAP",
color: "#006699",
description: "Constrained Application Protocol",
headers: ["uri-path", "uri-host", "content-format", "observe", "token"],
qosLevels: ["NON (Non-confirmable)", "CON (Confirmable)"],
port: 5683,
overhead: 4,
parseTime: 0.4,
encodeTime: 0.3,
format: "binary",
transport: "UDP"
},
http: {
name: "HTTP",
color: "#2E7D32",
description: "Hypertext Transfer Protocol",
headers: ["Content-Type", "Authorization", "X-Request-ID", "Accept", "Host"],
qosLevels: ["Best effort (TCP)", "With retry logic"],
port: 80,
overhead: 100,
parseTime: 1.2,
encodeTime: 0.8,
format: "text",
transport: "TCP"
},
amqp: {
name: "AMQP",
color: "#FF6600",
description: "Advanced Message Queuing Protocol",
headers: ["exchange", "routing-key", "delivery-mode", "content-type", "correlation-id"],
qosLevels: ["Non-persistent", "Persistent", "Transactional"],
port: 5672,
overhead: 8,
parseTime: 0.8,
encodeTime: 0.6,
format: "binary",
transport: "TCP"
},
modbus: {
name: "Modbus",
color: "#333333",
description: "Industrial Serial/TCP Protocol",
headers: ["unit-id", "function-code", "register-address", "quantity", "transaction-id"],
qosLevels: ["No ACK", "With ACK (TCP)"],
port: 502,
overhead: 12,
parseTime: 0.3,
encodeTime: 0.2,
format: "binary",
transport: "TCP/Serial"
},
opcua: {
name: "OPC-UA",
color: "#0066CC",
description: "OPC Unified Architecture",
headers: ["nodeId", "namespaceUri", "securityMode", "sessionId", "sequenceNumber"],
qosLevels: ["None", "Sign", "Sign & Encrypt"],
port: 4840,
overhead: 24,
parseTime: 1.5,
encodeTime: 1.2,
format: "binary/XML",
transport: "TCP"
}
};
// Comprehensive header mappings between all protocol pairs
const headerMappings = {
"mqtt->coap": {
"topic": "uri-path",
"qos": "observe/CON",
"retain": "max-age",
"clientId": "token",
"messageId": "message-id"
},
"mqtt->http": {
"topic": "URL path",
"qos": "Retry header",
"retain": "Cache-Control",
"clientId": "X-Client-ID",
"messageId": "X-Request-ID"
},
"mqtt->amqp": {
"topic": "routing-key",
"qos": "delivery-mode",
"retain": "expiration",
"clientId": "app-id",
"messageId": "message-id"
},
"mqtt->modbus": {
"topic": "register-address",
"qos": "function-code",
"retain": "N/A",
"clientId": "unit-id",
"messageId": "transaction-id"
},
"mqtt->opcua": {
"topic": "nodeId",
"qos": "securityMode",
"retain": "historizing",
"clientId": "sessionId",
"messageId": "sequenceNumber"
},
"coap->mqtt": {
"uri-path": "topic",
"observe": "qos",
"token": "clientId",
"content-format": "payload-format",
"uri-host": "broker"
},
"coap->http": {
"uri-path": "URL path",
"observe": "SSE/WebSocket",
"token": "Authorization",
"content-format": "Content-Type",
"uri-host": "Host"
},
"coap->amqp": {
"uri-path": "routing-key",
"observe": "delivery-mode",
"token": "correlation-id",
"content-format": "content-type",
"uri-host": "exchange"
},
"coap->modbus": {
"uri-path": "register-address",
"observe": "polling",
"token": "transaction-id",
"content-format": "data-type",
"uri-host": "unit-id"
},
"coap->opcua": {
"uri-path": "nodeId",
"observe": "subscription",
"token": "sessionId",
"content-format": "encoding",
"uri-host": "endpointUrl"
},
"http->mqtt": {
"URL path": "topic",
"Authorization": "username/password",
"Content-Type": "payload-format",
"X-Request-ID": "messageId",
"Host": "broker"
},
"http->coap": {
"URL path": "uri-path",
"Authorization": "token",
"Content-Type": "content-format",
"Accept": "accept",
"Host": "uri-host"
},
"http->amqp": {
"URL path": "routing-key",
"Authorization": "credentials",
"Content-Type": "content-type",
"X-Request-ID": "correlation-id",
"Host": "virtual-host"
},
"http->modbus": {
"URL path": "register-address",
"Authorization": "N/A",
"Content-Type": "data-format",
"X-Request-ID": "transaction-id",
"Host": "unit-id"
},
"http->opcua": {
"URL path": "nodeId",
"Authorization": "securityToken",
"Content-Type": "encoding",
"X-Request-ID": "requestHandle",
"Host": "endpointUrl"
},
"amqp->mqtt": {
"routing-key": "topic",
"delivery-mode": "qos",
"content-type": "payload-format",
"correlation-id": "messageId",
"exchange": "broker"
},
"amqp->coap": {
"routing-key": "uri-path",
"delivery-mode": "CON/NON",
"content-type": "content-format",
"correlation-id": "token",
"exchange": "uri-host"
},
"amqp->http": {
"routing-key": "URL path",
"delivery-mode": "retry-policy",
"content-type": "Content-Type",
"correlation-id": "X-Request-ID",
"exchange": "Host"
},
"amqp->modbus": {
"routing-key": "register-address",
"delivery-mode": "function-code",
"content-type": "data-format",
"correlation-id": "transaction-id",
"exchange": "unit-id"
},
"amqp->opcua": {
"routing-key": "nodeId",
"delivery-mode": "securityMode",
"content-type": "encoding",
"correlation-id": "requestHandle",
"exchange": "endpointUrl"
},
"modbus->mqtt": {
"register-address": "topic",
"function-code": "qos",
"unit-id": "clientId",
"transaction-id": "messageId",
"quantity": "payload-count"
},
"modbus->coap": {
"register-address": "uri-path",
"function-code": "method",
"unit-id": "uri-host",
"transaction-id": "token",
"quantity": "content-length"
},
"modbus->http": {
"register-address": "URL path",
"function-code": "HTTP method",
"unit-id": "Host",
"transaction-id": "X-Request-ID",
"quantity": "Content-Length"
},
"modbus->amqp": {
"register-address": "routing-key",
"function-code": "delivery-mode",
"unit-id": "app-id",
"transaction-id": "correlation-id",
"quantity": "body-size"
},
"modbus->opcua": {
"register-address": "nodeId",
"function-code": "serviceId",
"unit-id": "sessionId",
"transaction-id": "requestHandle",
"quantity": "arraySize"
},
"opcua->mqtt": {
"nodeId": "topic",
"securityMode": "qos",
"sessionId": "clientId",
"sequenceNumber": "messageId",
"namespaceUri": "namespace"
},
"opcua->coap": {
"nodeId": "uri-path",
"securityMode": "CON/NON",
"sessionId": "token",
"sequenceNumber": "message-id",
"namespaceUri": "uri-host"
},
"opcua->http": {
"nodeId": "URL path",
"securityMode": "TLS config",
"sessionId": "Authorization",
"sequenceNumber": "X-Request-ID",
"namespaceUri": "Host"
},
"opcua->amqp": {
"nodeId": "routing-key",
"securityMode": "delivery-mode",
"sessionId": "app-id",
"sequenceNumber": "message-id",
"namespaceUri": "exchange"
},
"opcua->modbus": {
"nodeId": "register-address",
"securityMode": "N/A",
"sessionId": "unit-id",
"sequenceNumber": "transaction-id",
"namespaceUri": "N/A"
}
};
// QoS mappings between protocols
const qosMappings = {
"mqtt->coap": { "QoS 0": "NON", "QoS 1": "CON", "QoS 2": "CON + Token" },
"mqtt->http": { "QoS 0": "Fire & forget", "QoS 1": "With retry", "QoS 2": "Idempotent + retry" },
"mqtt->amqp": { "QoS 0": "Non-persistent", "QoS 1": "Persistent", "QoS 2": "Transactional" },
"mqtt->modbus": { "QoS 0": "No ACK", "QoS 1": "With ACK", "QoS 2": "With ACK + verify" },
"mqtt->opcua": { "QoS 0": "None", "QoS 1": "Sign", "QoS 2": "Sign & Encrypt" },
"coap->mqtt": { "NON": "QoS 0", "CON": "QoS 1" },
"coap->http": { "NON": "Async", "CON": "Sync" },
"coap->amqp": { "NON": "Non-persistent", "CON": "Persistent" },
"coap->modbus": { "NON": "No ACK", "CON": "With ACK" },
"coap->opcua": { "NON": "None", "CON": "Sign" },
"http->mqtt": { "Async": "QoS 0", "Sync": "QoS 1", "Transactional": "QoS 2" },
"http->coap": { "Async": "NON", "Sync": "CON" },
"http->amqp": { "Async": "Non-persistent", "Sync": "Persistent" },
"http->modbus": { "Async": "No ACK", "Sync": "With ACK" },
"http->opcua": { "Async": "None", "Sync": "Sign" },
"amqp->mqtt": { "Non-persistent": "QoS 0", "Persistent": "QoS 1", "Transactional": "QoS 2" },
"amqp->coap": { "Non-persistent": "NON", "Persistent": "CON" },
"amqp->http": { "Non-persistent": "Async", "Persistent": "Sync" },
"amqp->modbus": { "Non-persistent": "No ACK", "Persistent": "With ACK" },
"amqp->opcua": { "Non-persistent": "None", "Persistent": "Sign" },
"modbus->mqtt": { "No ACK": "QoS 0", "With ACK": "QoS 1" },
"modbus->coap": { "No ACK": "NON", "With ACK": "CON" },
"modbus->http": { "No ACK": "Async", "With ACK": "Sync" },
"modbus->amqp": { "No ACK": "Non-persistent", "With ACK": "Persistent" },
"modbus->opcua": { "No ACK": "None", "With ACK": "Sign" },
"opcua->mqtt": { "None": "QoS 0", "Sign": "QoS 1", "Sign & Encrypt": "QoS 2" },
"opcua->coap": { "None": "NON", "Sign": "CON", "Sign & Encrypt": "CON" },
"opcua->http": { "None": "HTTP", "Sign": "HTTPS", "Sign & Encrypt": "HTTPS + mTLS" },
"opcua->amqp": { "None": "Non-persistent", "Sign": "Persistent", "Sign & Encrypt": "Transactional" },
"opcua->modbus": { "None": "No ACK", "Sign": "With ACK", "Sign & Encrypt": "With ACK" }
};
// State variables
let sourceProtocol = "mqtt";
let targetProtocol = "http";
let messageFormat = "json";
let sampleMessage = JSON.stringify({
"deviceId": "sensor-001",
"temperature": 23.5,
"humidity": 65,
"timestamp": "2024-01-15T10:30:00Z",
"location": {
"lat": 37.7749,
"lon": -122.4194
}
}, null, 2);
let animationStep = -1;
let isAnimating = false;
let translatedMessage = null;
// Create container
const container = d3.create("div")
.style("font-family", "system-ui, -apple-system, sans-serif");
// Main layout - two columns
const mainLayout = container.append("div")
.style("display", "flex")
.style("flex-wrap", "wrap")
.style("gap", "20px");
// Left panel - Configuration
const leftPanel = mainLayout.append("div")
.style("flex", "1")
.style("min-width", "350px")
.style("max-width", "450px");
// Protocol selection header
leftPanel.append("h3")
.style("margin", "0 0 15px 0")
.style("color", colors.navy)
.style("font-size", "16px")
.style("border-bottom", `3px solid ${colors.teal}`)
.style("padding-bottom", "8px")
.text("Protocol Configuration");
// Protocol selectors container
const selectorsBox = leftPanel.append("div")
.style("background", colors.white)
.style("padding", "20px")
.style("border-radius", "8px")
.style("border", `2px solid ${colors.lightGray}`)
.style("margin-bottom", "15px");
// Source protocol selector
function createProtocolSelect(parent, label, id, value) {
const group = parent.append("div")
.style("margin-bottom", "15px");
group.append("label")
.attr("for", id)
.style("display", "block")
.style("font-size", "13px")
.style("font-weight", "bold")
.style("color", colors.navy)
.style("margin-bottom", "6px")
.text(label);
const select = group.append("select")
.attr("id", id)
.style("width", "100%")
.style("padding", "10px 12px")
.style("border-radius", "6px")
.style("border", `1px solid ${colors.gray}`)
.style("font-size", "13px")
.style("cursor", "pointer")
.style("background", colors.white);
Object.entries(protocols).forEach(([key, proto]) => {
select.append("option")
.attr("value", key)
.attr("selected", key === value ? true : null)
.text(`${proto.name} - ${proto.description}`);
});
return select;
}
const sourceSelect = createProtocolSelect(selectorsBox, "Source Protocol", "source-proto", sourceProtocol);
const targetSelect = createProtocolSelect(selectorsBox, "Target Protocol", "target-proto", targetProtocol);
// Translation arrow between selectors
selectorsBox.append("div")
.style("text-align", "center")
.style("font-size", "24px")
.style("color", colors.teal)
.style("margin", "10px 0")
.html("↓ Translation ↓");
// Message format selector
const formatGroup = selectorsBox.append("div")
.style("margin-bottom", "15px");
formatGroup.append("label")
.style("display", "block")
.style("font-size", "13px")
.style("font-weight", "bold")
.style("color", colors.navy)
.style("margin-bottom", "6px")
.text("Message Format");
const formatRow = formatGroup.append("div")
.style("display", "flex")
.style("gap", "10px");
["json", "xml"].forEach(fmt => {
const label = formatRow.append("label")
.style("display", "flex")
.style("align-items", "center")
.style("gap", "5px")
.style("cursor", "pointer")
.style("padding", "8px 15px")
.style("border-radius", "4px")
.style("background", fmt === messageFormat ? colors.teal : colors.lightGray)
.style("color", fmt === messageFormat ? colors.white : colors.navy)
.style("font-size", "12px");
label.append("input")
.attr("type", "radio")
.attr("name", "format")
.attr("value", fmt)
.attr("checked", fmt === messageFormat ? true : null)
.style("display", "none");
label.append("span").text(fmt.toUpperCase());
});
// Message input
const messageBox = leftPanel.append("div")
.style("background", colors.white)
.style("padding", "15px")
.style("border-radius", "8px")
.style("border", `2px solid ${colors.lightGray}`)
.style("margin-bottom", "15px");
messageBox.append("label")
.style("display", "block")
.style("font-size", "13px")
.style("font-weight", "bold")
.style("color", colors.navy)
.style("margin-bottom", "8px")
.text("Sample Message (JSON/XML)");
const messageInput = messageBox.append("textarea")
.attr("id", "message-input")
.style("width", "100%")
.style("height", "180px")
.style("padding", "10px")
.style("border-radius", "6px")
.style("border", `1px solid ${colors.gray}`)
.style("font-family", "monospace")
.style("font-size", "12px")
.style("resize", "vertical")
.style("box-sizing", "border-box")
.property("value", sampleMessage);
// Translate button
const translateBtn = messageBox.append("button")
.style("margin-top", "10px")
.style("width", "100%")
.style("padding", "12px 20px")
.style("background", `linear-gradient(135deg, ${colors.teal} 0%, ${colors.green} 100%)`)
.style("color", colors.white)
.style("border", "none")
.style("border-radius", "6px")
.style("cursor", "pointer")
.style("font-size", "14px")
.style("font-weight", "bold")
.text("Translate Message");
// Right panel - Visualization
const rightPanel = mainLayout.append("div")
.style("flex", "2")
.style("min-width", "500px");
// Translation pipeline header
rightPanel.append("h3")
.style("margin", "0 0 15px 0")
.style("color", colors.navy)
.style("font-size", "16px")
.style("border-bottom", `3px solid ${colors.orange}`)
.style("padding-bottom", "8px")
.text("Translation Pipeline");
// Pipeline visualization
const pipelineSvg = rightPanel.append("svg")
.attr("viewBox", "0 0 750 200")
.attr("width", "100%")
.style("max-height", "200px")
.style("background", colors.white)
.style("border-radius", "8px")
.style("border", `2px solid ${colors.lightGray}`);
// Pipeline stages
const stages = [
{ id: "source", label: "Source", subLabel: "", x: 30, width: 120 },
{ id: "parse", label: "Parse", subLabel: "Decode", x: 175, width: 100 },
{ id: "transform", label: "Transform", subLabel: "Map", x: 300, width: 110 },
{ id: "encode", label: "Encode", subLabel: "Format", x: 435, width: 100 },
{ id: "target", label: "Target", subLabel: "", x: 560, width: 120 }
];
// Draw pipeline
function drawPipeline() {
pipelineSvg.selectAll("*").remove();
const sourceProt = protocols[sourceProtocol];
const targetProt = protocols[targetProtocol];
// Arrow marker definition
const defs = pipelineSvg.append("defs");
defs.append("marker")
.attr("id", "arrow")
.attr("viewBox", "0 0 10 10")
.attr("refX", 9)
.attr("refY", 5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto-start-reverse")
.append("path")
.attr("d", "M 0 0 L 10 5 L 0 10 z")
.attr("fill", colors.gray);
// Gradient for animated packet
const gradient = defs.append("linearGradient")
.attr("id", "packetGradient")
.attr("x1", "0%")
.attr("y1", "0%")
.attr("x2", "100%")
.attr("y2", "0%");
gradient.append("stop")
.attr("offset", "0%")
.attr("stop-color", colors.orange);
gradient.append("stop")
.attr("offset", "100%")
.attr("stop-color", colors.yellow);
// Draw connection lines
stages.forEach((stage, i) => {
if (i < stages.length - 1) {
const nextStage = stages[i + 1];
const lineActive = animationStep >= i;
pipelineSvg.append("line")
.attr("x1", stage.x + stage.width)
.attr("y1", 100)
.attr("x2", nextStage.x)
.attr("y2", 100)
.attr("stroke", lineActive ? colors.teal : colors.lightGray)
.attr("stroke-width", lineActive ? 4 : 3)
.attr("marker-end", "url(#arrow)");
}
});
// Draw stages
stages.forEach((stage, i) => {
let fillColor = colors.lightGray;
let strokeColor = colors.gray;
let textColor = colors.navy;
if (stage.id === "source") {
fillColor = sourceProt.color;
strokeColor = sourceProt.color;
textColor = colors.white;
} else if (stage.id === "target") {
fillColor = targetProt.color;
strokeColor = targetProt.color;
textColor = colors.white;
} else if (animationStep >= i - 1) {
fillColor = colors.teal;
strokeColor = colors.teal;
textColor = colors.white;
}
const g = pipelineSvg.append("g")
.attr("transform", `translate(${stage.x}, 50)`);
// Main box with shadow effect
g.append("rect")
.attr("x", 3)
.attr("y", 3)
.attr("width", stage.width)
.attr("height", 100)
.attr("rx", 8)
.attr("fill", "rgba(0,0,0,0.1)");
g.append("rect")
.attr("width", stage.width)
.attr("height", 100)
.attr("rx", 8)
.attr("fill", fillColor)
.attr("stroke", strokeColor)
.attr("stroke-width", 2);
// Stage label
g.append("text")
.attr("x", stage.width / 2)
.attr("y", 35)
.attr("text-anchor", "middle")
.attr("font-size", "14px")
.attr("font-weight", "bold")
.attr("fill", textColor)
.text(stage.label);
// Sub-label
let subLabel = stage.subLabel;
if (stage.id === "source") subLabel = sourceProt.name;
else if (stage.id === "target") subLabel = targetProt.name;
else if (stage.id === "parse") subLabel = `${sourceProt.parseTime}ms`;
else if (stage.id === "transform") subLabel = "Mapping";
else if (stage.id === "encode") subLabel = `${targetProt.encodeTime}ms`;
g.append("text")
.attr("x", stage.width / 2)
.attr("y", 55)
.attr("text-anchor", "middle")
.attr("font-size", "11px")
.attr("fill", textColor)
.style("opacity", 0.8)
.text(subLabel);
// Format/transport info
if (stage.id === "source" || stage.id === "target") {
const prot = stage.id === "source" ? sourceProt : targetProt;
g.append("text")
.attr("x", stage.width / 2)
.attr("y", 75)
.attr("text-anchor", "middle")
.attr("font-size", "9px")
.attr("fill", textColor)
.style("opacity", 0.7)
.text(`${prot.format} / ${prot.transport}`);
}
});
// Animated data packet
if (animationStep >= 0 && animationStep < 4 && isAnimating) {
const currentStage = stages[animationStep];
const nextStage = stages[animationStep + 1];
const packetX = currentStage.x + currentStage.width + 10;
const packet = pipelineSvg.append("g")
.attr("class", "packet");
packet.append("rect")
.attr("x", packetX)
.attr("y", 88)
.attr("width", 24)
.attr("height", 24)
.attr("rx", 4)
.attr("fill", "url(#packetGradient)")
.attr("stroke", colors.orange)
.attr("stroke-width", 2);
packet.append("text")
.attr("x", packetX + 12)
.attr("y", 104)
.attr("text-anchor", "middle")
.attr("font-size", "10px")
.attr("fill", colors.white)
.attr("font-weight", "bold")
.text("{}");
}
// Status indicator
const statusText = animationStep < 0 ? "Ready" :
animationStep >= 4 ? "Complete!" :
`Processing: ${stages[animationStep + 1].label}`;
pipelineSvg.append("text")
.attr("x", 375)
.attr("y", 180)
.attr("text-anchor", "middle")
.attr("font-size", "12px")
.attr("fill", animationStep >= 4 ? colors.green : colors.gray)
.attr("font-weight", animationStep >= 4 ? "bold" : "normal")
.text(statusText);
}
// Side-by-side comparison section
const comparisonSection = rightPanel.append("div")
.style("margin-top", "15px")
.style("display", "flex")
.style("gap", "15px")
.style("flex-wrap", "wrap");
// Original message box
const originalBox = comparisonSection.append("div")
.style("flex", "1")
.style("min-width", "250px")
.style("background", colors.white)
.style("padding", "15px")
.style("border-radius", "8px")
.style("border", `2px solid ${colors.lightGray}`);
originalBox.append("h4")
.style("margin", "0 0 10px 0")
.style("color", colors.navy)
.style("font-size", "13px")
.style("display", "flex")
.style("align-items", "center")
.style("gap", "8px")
.html(`<span style="width:12px;height:12px;background:${protocols[sourceProtocol].color};border-radius:2px;display:inline-block;"></span>Original Message`);
const originalContent = originalBox.append("pre")
.attr("id", "original-msg")
.style("background", colors.lightGray)
.style("padding", "10px")
.style("border-radius", "4px")
.style("font-size", "10px")
.style("overflow-x", "auto")
.style("margin", "0")
.style("max-height", "150px")
.style("overflow-y", "auto");
// Translated message box
const translatedBox = comparisonSection.append("div")
.style("flex", "1")
.style("min-width", "250px")
.style("background", colors.white)
.style("padding", "15px")
.style("border-radius", "8px")
.style("border", `2px solid ${colors.lightGray}`);
translatedBox.append("h4")
.attr("id", "translated-header")
.style("margin", "0 0 10px 0")
.style("color", colors.navy)
.style("font-size", "13px")
.style("display", "flex")
.style("align-items", "center")
.style("gap", "8px")
.html(`<span style="width:12px;height:12px;background:${protocols[targetProtocol].color};border-radius:2px;display:inline-block;"></span>Translated Message`);
const translatedContent = translatedBox.append("pre")
.attr("id", "translated-msg")
.style("background", colors.lightGray)
.style("padding", "10px")
.style("border-radius", "4px")
.style("font-size", "10px")
.style("overflow-x", "auto")
.style("margin", "0")
.style("max-height", "150px")
.style("overflow-y", "auto")
.text("Click 'Translate' to see result...");
// Mapping panels row
const mappingSection = rightPanel.append("div")
.style("display", "flex")
.style("gap", "15px")
.style("margin-top", "15px")
.style("flex-wrap", "wrap");
// Header mapping box
const headerBox = mappingSection.append("div")
.style("flex", "1")
.style("min-width", "220px")
.style("background", colors.white)
.style("padding", "15px")
.style("border-radius", "8px")
.style("border", `2px solid ${colors.lightGray}`);
headerBox.append("h4")
.style("margin", "0 0 10px 0")
.style("color", colors.navy)
.style("font-size", "14px")
.style("border-bottom", `2px solid ${colors.teal}`)
.style("padding-bottom", "5px")
.text("Header Field Mapping");
const headerContent = headerBox.append("div")
.attr("id", "header-mapping")
.style("max-height", "180px")
.style("overflow-y", "auto");
// QoS mapping box
const qosBox = mappingSection.append("div")
.style("flex", "1")
.style("min-width", "180px")
.style("background", colors.white)
.style("padding", "15px")
.style("border-radius", "8px")
.style("border", `2px solid ${colors.lightGray}`);
qosBox.append("h4")
.style("margin", "0 0 10px 0")
.style("color", colors.navy)
.style("font-size", "14px")
.style("border-bottom", `2px solid ${colors.orange}`)
.style("padding-bottom", "5px")
.text("QoS/Reliability Mapping");
const qosContent = qosBox.append("div")
.attr("id", "qos-mapping");
// Latency estimation box
const latencyBox = mappingSection.append("div")
.style("flex", "1")
.style("min-width", "200px")
.style("background", "linear-gradient(135deg, #1a252f 0%, #2C3E50 100%)")
.style("padding", "15px")
.style("border-radius", "8px");
latencyBox.append("h4")
.style("margin", "0 0 10px 0")
.style("color", colors.white)
.style("font-size", "14px")
.text("Latency Overhead Estimation");
const latencyContent = latencyBox.append("div")
.attr("id", "latency-estimation");
// Generate translated message based on target protocol
function generateTranslatedMessage(parsedMsg) {
const targetProt = protocols[targetProtocol];
// Generate protocol-specific headers
const headers = {};
targetProt.headers.forEach(h => {
if (h === "topic" || h === "uri-path" || h === "routing-key" || h === "register-address" || h === "nodeId") {
headers[h] = `/sensors/${parsedMsg.deviceId || "device"}`;
} else if (h === "Content-Type" || h === "content-type" || h === "content-format") {
headers[h] = messageFormat === "json" ? "application/json" : "application/xml";
} else if (h === "qos" || h === "delivery-mode" || h === "securityMode") {
headers[h] = targetProt.qosLevels[1];
} else if (h === "observe" || h === "subscription") {
headers[h] = true;
} else if (h === "messageId" || h === "transaction-id" || h === "correlation-id" || h === "sequenceNumber") {
headers[h] = Math.floor(Math.random() * 65535);
} else if (h === "token" || h === "sessionId") {
headers[h] = Math.random().toString(36).substring(2, 10);
} else {
headers[h] = "auto-generated";
}
});
return {
protocol: targetProt.name,
headers: headers,
payload: parsedMsg,
metadata: {
timestamp: new Date().toISOString(),
translatedFrom: protocols[sourceProtocol].name,
format: messageFormat
}
};
}
// Update all visualizations
function updateVisualization() {
const sourceProt = protocols[sourceProtocol];
const targetProt = protocols[targetProtocol];
const mappingKey = `${sourceProtocol}->${targetProtocol}`;
// Update original header color
originalBox.select("h4")
.html(`<span style="width:12px;height:12px;background:${sourceProt.color};border-radius:2px;display:inline-block;"></span>Original (${sourceProt.name})`);
translatedBox.select("h4")
.html(`<span style="width:12px;height:12px;background:${targetProt.color};border-radius:2px;display:inline-block;"></span>Translated (${targetProt.name})`);
// Update original content
originalContent.text(sampleMessage);
// Update pipeline
drawPipeline();
// Update header mapping
headerContent.html("");
const mapping = headerMappings[mappingKey] || {};
if (sourceProtocol === targetProtocol) {
headerContent.append("div")
.style("color", colors.gray)
.style("font-style", "italic")
.style("font-size", "12px")
.style("padding", "10px")
.style("text-align", "center")
.text("No translation needed (same protocol)");
} else {
// Create mapping table
const table = headerContent.append("table")
.style("width", "100%")
.style("border-collapse", "collapse")
.style("font-size", "11px");
const thead = table.append("thead");
const headerRow = thead.append("tr");
headerRow.append("th")
.style("padding", "4px")
.style("text-align", "left")
.style("border-bottom", `1px solid ${colors.gray}`)
.style("color", sourceProt.color)
.text(sourceProt.name);
headerRow.append("th")
.style("padding", "4px")
.style("text-align", "center")
.style("border-bottom", `1px solid ${colors.gray}`)
.text("");
headerRow.append("th")
.style("padding", "4px")
.style("text-align", "left")
.style("border-bottom", `1px solid ${colors.gray}`)
.style("color", targetProt.color)
.text(targetProt.name);
const tbody = table.append("tbody");
Object.entries(mapping).forEach(([source, target]) => {
const row = tbody.append("tr");
row.append("td")
.style("padding", "4px 6px")
.style("background", `${sourceProt.color}22`)
.style("border-radius", "3px 0 0 3px")
.text(source);
row.append("td")
.style("padding", "4px")
.style("text-align", "center")
.style("color", colors.gray)
.text("->");
row.append("td")
.style("padding", "4px 6px")
.style("background", `${targetProt.color}22`)
.style("border-radius", "0 3px 3px 0")
.text(target);
});
}
// Update QoS mapping
qosContent.html("");
const qosMap = qosMappings[mappingKey] || {};
if (sourceProtocol === targetProtocol) {
qosContent.append("div")
.style("color", colors.gray)
.style("font-style", "italic")
.style("font-size", "12px")
.style("text-align", "center")
.text("Direct passthrough");
} else {
Object.entries(qosMap).forEach(([source, target]) => {
const row = qosContent.append("div")
.style("margin-bottom", "8px")
.style("font-size", "11px")
.style("padding", "6px")
.style("background", colors.lightGray)
.style("border-radius", "4px");
row.append("div")
.style("color", sourceProt.color)
.style("font-weight", "bold")
.text(source);
row.append("div")
.style("color", colors.gray)
.style("padding-left", "10px")
.style("margin-top", "2px")
.html(`→ <span style="color:${targetProt.color};font-weight:bold;">${target}</span>`);
});
}
// Update latency estimation
latencyContent.html("");
const parseTime = sourceProt.parseTime;
const transformTime = 0.5 + (sourceProtocol !== targetProtocol ? 0.3 : 0);
const encodeTime = targetProt.encodeTime;
const networkDelta = sourceProt.transport !== targetProt.transport ? 2.0 : 0;
const totalLatency = parseTime + transformTime + encodeTime + networkDelta;
const latencyItems = [
{ label: "Parse", value: `${parseTime}ms`, color: colors.teal, width: (parseTime / totalLatency) * 100 },
{ label: "Transform", value: `${transformTime.toFixed(1)}ms`, color: colors.orange, width: (transformTime / totalLatency) * 100 },
{ label: "Encode", value: `${encodeTime}ms`, color: colors.teal, width: (encodeTime / totalLatency) * 100 }
];
if (networkDelta > 0) {
latencyItems.push({ label: "Transport", value: `${networkDelta}ms`, color: colors.purple, width: (networkDelta / totalLatency) * 100 });
}
latencyItems.forEach(item => {
const row = latencyContent.append("div")
.style("margin-bottom", "8px");
const labelRow = row.append("div")
.style("display", "flex")
.style("justify-content", "space-between")
.style("margin-bottom", "3px")
.style("font-size", "11px");
labelRow.append("span")
.style("color", colors.lightGray)
.text(item.label);
labelRow.append("span")
.style("color", item.color)
.style("font-weight", "bold")
.text(item.value);
// Progress bar
row.append("div")
.style("background", "rgba(255,255,255,0.1)")
.style("border-radius", "3px")
.style("height", "6px")
.style("overflow", "hidden")
.append("div")
.style("width", `${item.width}%`)
.style("height", "100%")
.style("background", item.color)
.style("border-radius", "3px");
});
// Total row
const totalRow = latencyContent.append("div")
.style("margin-top", "12px")
.style("padding-top", "10px")
.style("border-top", `1px solid ${colors.gray}`)
.style("display", "flex")
.style("justify-content", "space-between")
.style("font-size", "13px");
totalRow.append("span")
.style("color", colors.white)
.style("font-weight", "bold")
.text("Total Latency");
totalRow.append("span")
.style("color", colors.green)
.style("font-weight", "bold")
.text(`${totalLatency.toFixed(1)}ms`);
// Overhead change indicator
const overheadDiff = targetProt.overhead - sourceProt.overhead;
latencyContent.append("div")
.style("margin-top", "10px")
.style("font-size", "10px")
.style("color", colors.lightGray)
.html(`Header: ${sourceProt.overhead}B → ${targetProt.overhead}B
<span style="color: ${Math.abs(overheadDiff) > 50 ? colors.orange : colors.green}">
(${overheadDiff > 0 ? '+' : ''}${overheadDiff}B)
</span>`);
}
// Animation sequence
async function runTranslation() {
if (isAnimating) return;
isAnimating = true;
animationStep = -1;
translateBtn.style("opacity", "0.6").style("cursor", "wait");
// Validate and parse input
let parsed;
try {
if (messageFormat === "json") {
parsed = JSON.parse(sampleMessage);
} else {
// Simple XML parsing indication
parsed = { xmlContent: sampleMessage };
}
} catch (e) {
translatedContent.text(`Error: Invalid ${messageFormat.toUpperCase()} - ${e.message}`);
isAnimating = false;
translateBtn.style("opacity", "1").style("cursor", "pointer");
return;
}
// Run animation steps
for (let step = 0; step < 5; step++) {
animationStep = step;
drawPipeline();
await new Promise(r => setTimeout(r, 500));
}
// Generate translated output
translatedMessage = generateTranslatedMessage(parsed);
translatedContent.text(JSON.stringify(translatedMessage, null, 2));
isAnimating = false;
translateBtn.style("opacity", "1").style("cursor", "pointer");
}
// Event listeners
sourceSelect.on("change", function() {
sourceProtocol = this.value;
if (sourceProtocol === targetProtocol) {
const protocolKeys = Object.keys(protocols);
const idx = protocolKeys.indexOf(sourceProtocol);
targetProtocol = protocolKeys[(idx + 1) % protocolKeys.length];
targetSelect.property("value", targetProtocol);
}
animationStep = -1;
updateVisualization();
});
targetSelect.on("change", function() {
targetProtocol = this.value;
animationStep = -1;
updateVisualization();
});
messageInput.on("input", function() {
sampleMessage = this.value;
originalContent.text(sampleMessage);
});
translateBtn.on("click", runTranslation);
// Format radio buttons
formatRow.selectAll("label").on("click", function() {
const radio = d3.select(this).select("input");
messageFormat = radio.property("value");
formatRow.selectAll("label")
.style("background", colors.lightGray)
.style("color", colors.navy);
d3.select(this)
.style("background", colors.teal)
.style("color", colors.white);
// Update sample message for format
if (messageFormat === "xml") {
sampleMessage = `<?xml version="1.0"?>
<sensor>
<deviceId>sensor-001</deviceId>
<temperature>23.5</temperature>
<humidity>65</humidity>
<timestamp>2024-01-15T10:30:00Z</timestamp>
<location>
<lat>37.7749</lat>
<lon>-122.4194</lon>
</location>
</sensor>`;
} else {
sampleMessage = JSON.stringify({
"deviceId": "sensor-001",
"temperature": 23.5,
"humidity": 65,
"timestamp": "2024-01-15T10:30:00Z",
"location": {
"lat": 37.7749,
"lon": -122.4194
}
}, null, 2);
}
messageInput.property("value", sampleMessage);
updateVisualization();
});
// Initialize
updateVisualization();
return container.node();
}1314.2 Common Translation Patterns
This is the most common translation pattern for cloud integration:
| Aspect | MQTT | HTTP |
|---|---|---|
| Topic | sensors/temp/room1 |
POST /api/sensors/temp/room1 |
| QoS | QoS 1 | Retry with exponential backoff |
| Retain | retain=true |
Cache-Control: max-age=3600 |
| Payload | Binary/JSON | JSON with Content-Type header |
Use when: Connecting IoT devices to REST APIs, web dashboards, or cloud services.
Bridging legacy industrial equipment to modern IoT platforms:
| Aspect | Modbus | MQTT |
|---|---|---|
| Addressing | Register 40001 | Topic plc/holding/40001 |
| Data Type | 16-bit register | JSON with type metadata |
| Polling | Request-response | Publish on change |
| Security | Unit ID only | TLS + client certificates |
Use when: Modernizing SCADA systems, connecting PLCs to cloud platforms.
Connecting constrained edge devices to enterprise message queues:
| Aspect | CoAP | AMQP |
|---|---|---|
| Transport | UDP | TCP |
| Observe | CoAP observe | AMQP subscription |
| Reliability | CON/NON | Persistent/Transactional |
| Encoding | CBOR | JSON/Protobuf |
Use when: IoT gateways connecting to enterprise ESB or message-oriented middleware.
Not all protocol features can be perfectly translated:
- QoS Downgrade: MQTT QoS 2 (exactly-once) cannot be fully guaranteed over HTTP
- Pub/Sub Emulation: Modbus has no native pub/sub - requires polling or change detection
- Binary Efficiency: HTTP text overhead is ~50x higher than MQTT/CoAP binary
- Observe Patterns: CoAP observe requires special handling for non-CoAP targets
- Security Context: OPC-UA certificate chains may not map to simpler protocols
1314.3 Understanding Protocol Translation
1314.3.1 Why Protocol Translation Matters
IoT ecosystems often include devices and services using different communication protocols. Protocol translation bridges these differences, enabling interoperability across:
| Scenario | Source | Target | Use Case |
|---|---|---|---|
| Cloud Integration | MQTT | HTTP | Sensor data to REST APIs |
| Edge Processing | CoAP | MQTT | Constrained devices to broker |
| Industrial IoT | Modbus | AMQP | Legacy PLCs to message queues |
| Mobile Apps | HTTP | MQTT | User interfaces to IoT backend |
| Enterprise | OPC-UA | AMQP | Industrial data to analytics |
1314.3.2 Translation Pipeline Stages
The source protocol message is decoded according to its specification: - Extract headers and metadata - Validate message structure - Parse payload format (JSON, XML, binary, etc.) - Verify checksums and sequence numbers
Message components are mapped between protocol semantics: - Header field mapping (topic to routing-key, etc.) - QoS/reliability level conversion - Addressing scheme translation - Payload format adaptation if needed - Security context transformation
The translated message is encoded in the target protocol format: - Apply target protocol headers - Set appropriate QoS flags - Serialize payload in target format - Calculate checksums if required - Add security wrappers if needed
1314.3.3 Performance Considerations
Protocol translation introduces overhead:
- Latency: Parsing, transformation, and encoding add 1-5ms typically
- Header Overhead: Different protocols have varying header sizes (2B to 100B+)
- Connection Management: Some translations require protocol bridging (TCP to UDP)
- State Maintenance: Tracking message IDs across protocol boundaries
- Memory: Buffering during translation, especially for large payloads
1314.3.4 Best Practices
- Minimize hops: Direct translation when possible, avoid protocol chains
- Cache mappings: Reuse header transformations for repeated message patterns
- Async processing: Use queues for high-throughput scenarios
- Monitor latency: Track translation overhead metrics continuously
- Handle failures gracefully: Implement retry with exponential backoff
- Preserve semantics: Map QoS levels conservatively (downgrade rather than upgrade)
1314.4 Related Topics
- IoT Interoperability - Standards and approaches
- MQTT Fundamentals - Deep dive into MQTT
- CoAP Fundamentals - RESTful IoT communication
- Protocol Bridging - Gateway architectures
- OPC-UA - Industrial interoperability