|
//----------------------------------------------------------------------------- |
|
// NodeRED Integration for Torque Script |
|
// Created by: skylord123 <github.com/skylord123> |
|
// |
|
// This script provides functionality for integrating with a Node-RED instance |
|
// over a TCP connection from within the Torque Game Engine. It allows for |
|
// sending messages to and receiving messages from Node-RED, facilitating |
|
// communication between the game and external systems or devices controlled |
|
// by Node-RED. |
|
// |
|
// Usage: |
|
// - The `getNodeRED()` global function retrieves the singleton NodeRED object, |
|
// creating it if it doesn't exist. This object manages the TCP connection and |
|
// communication. |
|
// |
|
// - Call `NodeRED::connect(%host)` to establish a connection with the Node-RED |
|
// instance. The `%host` parameter should specify the host and port (e.g., |
|
// "localhost:1880"). |
|
// |
|
// - Use `NodeRED::send(%msg)` to send messages to the connected Node-RED instance. |
|
// Ensure you are connected before attempting to send. |
|
// |
|
// - Register callback functions using `NodeRED::addReceiveCallback(%func)` to |
|
// handle messages received from Node-RED. Each callback function must be |
|
// capable of accepting a single argument: the message string received. |
|
// |
|
// - Disconnect from Node-RED using `NodeRED::disconnect()`. |
|
// |
|
// Debugging: |
|
// - Set the debug level with `NodeRED::setDebugLevel(%level)`, where `%level` |
|
// controls the verbosity of debug output. Level 0 suppresses all messages, |
|
// level 1 shows connection state changes, and level 2 logs all sent and |
|
// received messages. |
|
// |
|
// Example sending and receiving: |
|
// ``` |
|
// getNodeRED().connect("localhost:1881"); |
|
// if(!getNodeRED().hasReceiveCallback("socket_data_in")) { |
|
// getNodeRED().addReceiveCallback("socket_data_in"); |
|
// } |
|
// getNodeRED().send("player_message|Skylord|Hey Buddy!"); |
|
// |
|
// function socket_data_in(%msg) { |
|
// error("Received: " @ %msg); |
|
// } |
|
// ``` |
|
// |
|
// This script enhances the game's capability to interact with hardware, |
|
// services, and other systems by leveraging the powerful flow-based programming |
|
// environment of Node-RED. |
|
//----------------------------------------------------------------------------- |
|
|
|
|
|
// Define a global function to retrieve the NodeRED object, creating it if it doesn't exist. |
|
function getNodeRED() { |
|
if (!isObject(NodeRED)) { |
|
%nodeRed = new ScriptObject(NodeRED) { |
|
className = "NodeRED"; |
|
}; |
|
} |
|
return NodeRED; |
|
} |
|
|
|
// Class definition for NodeRED |
|
function NodeRED::onAdd(%this) { |
|
// Initialize variables |
|
%this.connection = ""; // TCPObject will be assigned here when connecting |
|
%this.receiveCallbacks = ""; // Store callbacks |
|
%this.receiveCallbacksLength = 0; |
|
%this.debugLevel = 1; |
|
%this.host = "localhost:1881"; |
|
} |
|
|
|
function NodeRED::setHost(%this, %host) { |
|
if (%this.host $= %host) { |
|
return false; |
|
} |
|
%this.host = %host; |
|
return true; |
|
} |
|
|
|
function NodeRED::isConnected(%this) { |
|
return isObject(%this.connection) && %this.connection.connected; |
|
} |
|
|
|
function NodeRED::connect(%this, %host) { |
|
if (%this.isConnected()) { |
|
return false; |
|
} |
|
|
|
if (strLen(%host)) { |
|
%this.setHost(%host); |
|
} |
|
|
|
if (!strLen(%this.host)) { |
|
%this._debug("Missing host", 1); |
|
return false; |
|
} |
|
|
|
%this._debug("Connecting..", 1); |
|
|
|
if (!isObject(%this.connection)) { |
|
%this.connection = new TCPObject(NodeRedTCP) { |
|
class = "NodeRedTCP"; |
|
nodeRed = %this; // Link back to NodeRED object |
|
}; |
|
} |
|
%this.connection.connecting = true; |
|
%this.connection.connectionAttempts++; |
|
%this.connection.connect(%this.host); |
|
return true; |
|
} |
|
|
|
function NodeRED::disconnect(%this) { |
|
if (%this.isConnected()) { |
|
%this.connection.disconnect(); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
function NodeRED::send(%this, %msg) { |
|
if (%this.isConnected()) { |
|
%this._debug("SENDING -> " @ %msg, 2); |
|
%this.connection.send(%msg @ "\n"); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
function NodeRED::_debug(%this, %msg, %level) { |
|
if (%this.debugLevel >= %level) { |
|
error("[Node-RED" @ (%this.host !$= "" ? " " @ %this.host : "") @ "] " @ %msg); |
|
} |
|
} |
|
|
|
function NodeRED::setDebugLevel(%this, %level) { |
|
%this.debugLevel = %level; |
|
} |
|
|
|
function NodeRED::addReceiveCallback(%this, %func) { |
|
%this.receiveCallbacksLength++; |
|
%this.receiveCallbacks[%this.receiveCallbacksLength] = %func; |
|
} |
|
|
|
function NodeRED::hasReceiveCallback(%this, %func) { |
|
for (%i = 1; %i <= %this.receiveCallbacksLength; %i++) { |
|
if (%this.receiveCallbacks[%i] $= %func) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
function NodeRedTCP::_retryConnect(%this) { |
|
if (%this.connected) return false; |
|
%connectionDelay = %this.connectionAttempts < 5 ? 1000 : 5000; |
|
%this.nodeRed._debug(%this.state @ " - retrying connection in " @ %connectionDelay @ "ms (attempts=" @ %this.connectionAttempts @ ")", 1); |
|
%this.nodeRed.schedule(%connectionDelay, "connect", ""); |
|
return true; |
|
} |
|
|
|
function NodeRedTCP::onAdd(%this) { |
|
%this.connected = false; |
|
%this.connecting = false; |
|
%this.connectionAttempts = 0; |
|
} |
|
|
|
function NodeRedTCP::onDNSFailed(%this) { |
|
%this.state = "DNS resolution failed"; |
|
%this.connecting = false; |
|
%this.connected = false; |
|
if(!%this._retryConnect()){ |
|
%this.nodeRed._debug("DNS resolution failed for " @ %this.nodeRed.host, 1); |
|
} |
|
} |
|
|
|
function NodeRedTCP::onConnected(%this) { |
|
%this.state = "Connected"; |
|
%this.connected = true; |
|
%this.connecting = false; |
|
%this.connectionAttempts = 0; |
|
%this.nodeRed._debug("Connected", 1); |
|
} |
|
|
|
function NodeRedTCP::onDisconnect(%this) { |
|
%this.state = "Disconnected"; |
|
%this.connected = false; |
|
%this.connecting = false; |
|
if(!%this._retryConnect()){ |
|
%this.nodeRed._debug(%this.state, 1); |
|
} |
|
} |
|
|
|
function NodeRedTCP::onConnectFailed(%this) { |
|
%this.state = "Connection failed"; |
|
%this.connecting = false; |
|
%this.connected = false; |
|
if(!%this._retryConnect()){ |
|
%this.nodeRed._debug(%this.state, 1); |
|
} |
|
} |
|
|
|
function NodeRedTCP::onLine(%this, %line) { |
|
%this.nodeRed._debug("RECEIVED <- " @ %line, 2); |
|
for (%i = 1; %i <= %this.nodeRed.receiveCallbacksLength; %i++) { |
|
call(%this.nodeRed.receiveCallbacks[%i], %line); |
|
} |
|
} |