feat: update logging, add high performance example
This commit is contained in:
@@ -109,7 +109,7 @@ void ApiServer::setupWebSocket() {
|
|||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
DeserializationError err = deserializeJson(doc, (const char*)data, len);
|
DeserializationError err = deserializeJson(doc, (const char*)data, len);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
LOG_INFO("API", "Received event: " + String(doc["event"].as<String>()));
|
LOG_DEBUG("API", "Received event: " + String(doc["event"].as<String>()));
|
||||||
String eventName = doc["event"].as<String>();
|
String eventName = doc["event"].as<String>();
|
||||||
String payloadStr;
|
String payloadStr;
|
||||||
if (doc["payload"].is<const char*>()) {
|
if (doc["payload"].is<const char*>()) {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ ClusterManager::ClusterManager(NodeContext& ctx, TaskManager& taskMgr) : ctx(ctx
|
|||||||
IPAddress ip = WiFi.localIP();
|
IPAddress ip = WiFi.localIP();
|
||||||
IPAddress mask = WiFi.subnetMask();
|
IPAddress mask = WiFi.subnetMask();
|
||||||
IPAddress bcast(ip[0] | ~mask[0], ip[1] | ~mask[1], ip[2] | ~mask[2], ip[3] | ~mask[3]);
|
IPAddress bcast(ip[0] | ~mask[0], ip[1] | ~mask[1], ip[2] | ~mask[2], ip[3] | ~mask[3]);
|
||||||
LOG_INFO("Cluster", String("Broadcasting CLUSTER_EVENT to ") + bcast.toString() + " len=" + String(jsonStr->length()));
|
LOG_DEBUG("Cluster", String("Broadcasting CLUSTER_EVENT to ") + bcast.toString() + " len=" + String(jsonStr->length()));
|
||||||
this->ctx.udp->beginPacket(bcast, this->ctx.config.udp_port);
|
this->ctx.udp->beginPacket(bcast, this->ctx.config.udp_port);
|
||||||
String msg = String(ClusterProtocol::CLUSTER_EVENT_MSG) + ":" + *jsonStr;
|
String msg = String(ClusterProtocol::CLUSTER_EVENT_MSG) + ":" + *jsonStr;
|
||||||
this->ctx.udp->write(msg.c_str());
|
this->ctx.udp->write(msg.c_str());
|
||||||
@@ -204,7 +204,7 @@ void ClusterManager::onNodeInfo(const char* msg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG_DEBUG("Cluster", String("Failed to parse NODE_INFO JSON from ") + senderIP.toString());
|
LOG_WARN("Cluster", String("Failed to parse NODE_INFO JSON from ") + senderIP.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,11 +216,11 @@ void ClusterManager::onClusterEvent(const char* msg) {
|
|||||||
LOG_DEBUG("Cluster", "CLUSTER_EVENT received with empty payload");
|
LOG_DEBUG("Cluster", "CLUSTER_EVENT received with empty payload");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOG_INFO("Cluster", String("CLUSTER_EVENT raw from ") + ctx.udp->remoteIP().toString() + " len=" + String(strlen(jsonStart)));
|
LOG_DEBUG("Cluster", String("CLUSTER_EVENT raw from ") + ctx.udp->remoteIP().toString() + " len=" + String(strlen(jsonStart)));
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
DeserializationError err = deserializeJson(doc, jsonStart);
|
DeserializationError err = deserializeJson(doc, jsonStart);
|
||||||
if (err) {
|
if (err) {
|
||||||
LOG_DEBUG("Cluster", String("Failed to parse CLUSTER_EVENT JSON from ") + ctx.udp->remoteIP().toString());
|
LOG_ERROR("Cluster", String("Failed to parse CLUSTER_EVENT JSON from ") + ctx.udp->remoteIP().toString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Robust extraction of event and data
|
// Robust extraction of event and data
|
||||||
@@ -249,7 +249,7 @@ void ClusterManager::onClusterEvent(const char* msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string eventKey(eventStr.c_str());
|
std::string eventKey(eventStr.c_str());
|
||||||
LOG_INFO("Cluster", String("Firing event '") + eventStr + "' with dataLen=" + String(data.length()));
|
LOG_DEBUG("Cluster", String("Firing event '") + eventStr + "' with dataLen=" + String(data.length()));
|
||||||
ctx.fire(eventKey, &data);
|
ctx.fire(eventKey, &data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
14
test/README
14
test/README
@@ -48,3 +48,17 @@ Example:
|
|||||||
node test/ws-cluster-broadcast-color.js ws://10.0.1.53/ws
|
node test/ws-cluster-broadcast-color.js ws://10.0.1.53/ws
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 4. WS Cluster Broadcast Rainbow (`test/ws-cluster-broadcast-rainbow.js`)
|
||||||
|
|
||||||
|
Broadcasts a smooth rainbow color transition over WebSocket using `cluster/broadcast` and the `api/neopattern/color` event. Update rate defaults to `UPDATE_RATE` in the script (e.g., 100 ms).
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```
|
||||||
|
node test/ws-cluster-broadcast-rainbow.js ws://<device-ip>/ws
|
||||||
|
```
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
node test/ws-cluster-broadcast-rainbow.js ws://10.0.1.53/ws
|
||||||
|
```
|
||||||
|
Note: Very fast update intervals (e.g., 10 ms) may saturate links or the device.
|
||||||
|
|
||||||
|
|||||||
71
test/ws-cluster-broadcast-rainbow.js
Normal file
71
test/ws-cluster-broadcast-rainbow.js
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
// WebSocket client to broadcast smooth rainbow color changes across the cluster
|
||||||
|
// Usage: node ws-cluster-broadcast-rainbow.js ws://<device-ip>/ws
|
||||||
|
|
||||||
|
const WebSocket = require('ws');
|
||||||
|
|
||||||
|
const url = process.argv[2] || 'ws://127.0.0.1/ws';
|
||||||
|
const ws = new WebSocket(url);
|
||||||
|
|
||||||
|
function hsvToRgb(h, s, v) {
|
||||||
|
const c = v * s;
|
||||||
|
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
||||||
|
const m = v - c;
|
||||||
|
let r = 0, g = 0, b = 0;
|
||||||
|
if (h < 60) { r = c; g = x; b = 0; }
|
||||||
|
else if (h < 120) { r = x; g = c; b = 0; }
|
||||||
|
else if (h < 180) { r = 0; g = c; b = x; }
|
||||||
|
else if (h < 240) { r = 0; g = x; b = c; }
|
||||||
|
else if (h < 300) { r = x; g = 0; b = c; }
|
||||||
|
else { r = c; g = 0; b = x; }
|
||||||
|
const R = Math.round((r + m) * 255);
|
||||||
|
const G = Math.round((g + m) * 255);
|
||||||
|
const B = Math.round((b + m) * 255);
|
||||||
|
return { r: R, g: G, b: B };
|
||||||
|
}
|
||||||
|
|
||||||
|
function toHex({ r, g, b }) {
|
||||||
|
const h = (n) => n.toString(16).padStart(2, '0').toUpperCase();
|
||||||
|
return `#${h(r)}${h(g)}${h(b)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let hue = 0;
|
||||||
|
const SAT = 1.0; // full saturation
|
||||||
|
const VAL = 1.0; // full value
|
||||||
|
const BRIGHTNESS = 128;
|
||||||
|
const UPDATE_RATE = 100; // 100 ms
|
||||||
|
|
||||||
|
let timer = null;
|
||||||
|
|
||||||
|
ws.on('open', () => {
|
||||||
|
console.log('Connected to', url);
|
||||||
|
// UPDATE_RATE ms updates (10 Hz). Be aware this can saturate slow links.
|
||||||
|
timer = setInterval(() => {
|
||||||
|
const rgb = hsvToRgb(hue, SAT, VAL);
|
||||||
|
const color = toHex(rgb);
|
||||||
|
const envelope = {
|
||||||
|
event: 'api/neopattern/color',
|
||||||
|
data: { color, brightness: BRIGHTNESS }
|
||||||
|
};
|
||||||
|
const msg = { event: 'cluster/broadcast', payload: envelope };
|
||||||
|
try {
|
||||||
|
ws.send(JSON.stringify(msg));
|
||||||
|
} catch (_) {}
|
||||||
|
hue = (hue + 2) % 360; // advance hue (adjust for speed)
|
||||||
|
}, UPDATE_RATE);
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.on('message', (data) => {
|
||||||
|
// Optionally throttle logs: comment out for quieter output
|
||||||
|
// console.log('WS:', data.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.on('error', (err) => {
|
||||||
|
console.error('WebSocket error:', err.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.on('close', () => {
|
||||||
|
if (timer) clearInterval(timer);
|
||||||
|
console.log('WebSocket closed');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user