How to send a binary file via TCP or Websockets

Hallo nice people, thank you for reading.

I need to send a WebP-File as a response to a TCP-Request and don't know how.

At the moment I have a Windows program setting up a TCP-Server that does exactly this, but I want to take this functionality out of this program into Node-RED.

So I need a TCP-Server in Node-Red. Now I can't find any example on how to send a file.

Should I use websockets? And how is this done then? I am fairly new to Node-RED and have no clue with TCP and such! That said: Thank you very much for your help. I appreciate that very much!

Sending a file is essentially the same as sending a number or a string (its just 1s and 0s)

Assuming some external application is sending a TCP packet - what in that packet tells you to send a file?

Assuming the mere presence of a TCP packet (i.e. "something"/"anything" is received means "send a very specific file") then it is pretty much:

TCP-in --> file read --> TCP-out


Additional question.

Do you have control over the the application making the TCP request? If yes, then using HTTP or MQTT will be far far far easier.


Additional help and pointers.

There are built in examples for TCP nodes CTRL-I -> examples

I recommend watching this playlist: Node-RED Essentials. The videos are done by the developers of node-red. They're nice & short and to the point. You will understand a whole lot more in about 1 hour. A small investment for a lot of gain.

Do remember though that MQTT have a maximum message size.

To do it using raw websockets is possible as long as the network connection between client and server is reasonably stable and robust. But it will require some work on both ends to get it to work. You would probably have to chunk up the data and you would need to know how to deal with buffers, websockets support buffer transfers though.

Node-RED is already a TCP server so if you are using TCP->TCP connections already, you should be able to switch the endpoint to Node-RED fairly easily.

Also, have you looked to see if there is an FTPS/SFTP/SCP server node listed in the flows library?

HTTP(s) file transfer is also pretty straight-forwards.

Thank you so much for your replies! I appreciate it very much.

I have been trying the whole day but without any success. I really should watch the tutorials, I suppose, and I will, but since I am already so close I would appreciate it very much if you could give me a hint.

Let me describe what i have, please.

On a remote server a piece of PHP-code until now sends a JSON-String to my program, Unreal Engine, to trigger it to create an image on the fly.

$result = socket_connect($socket, gethostbyname($useHost), $usePort) or complete(FALSE, 'socket_connect() failed (' . socket_strerror(socket_last_error($socket)) . ')');
failed (' . socket_strerror(socket_last_error($socket)) . ')');

$sendData = json_decode($json, true);
$sendData['session_id'] = session_id(); // Add the session ID to the array
$json = json_encode($sendData);

socket_write($socket, $json, strlen($json));

socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 20, 'usec' => 0]);
while ($out = socket_read($socket, 4096)) {
    $img_data .= $out;
}

socket_close($socket);

if (!$img_data) {  complete(FALSE, 'socket_read() - response is empty'); }

$result = file_put_contents(__DIR__ . '/../../html' . $img_path, $img_data);
die($result);

Now I succeeded responding to the request with a very simple flow:

This works. So I am quite close.

But here I am stuck. I need to forward the request to port 9000, then wait until the image is created (that works fine) and then return the original message but with the newly created file as payload, right? Something like this:

image

And this is what I don't get. The TCP-out node, when set to "Reply to TCP" does not have any information to WHICH tcp to reply to. So it is of no help, right? How can it know on which connection to reply? What am I missing here?

Thank you so much!

2 questions (1 I already asked)

  1. Are you in control of the data sending? Ie could you change it to use mqtt or http instead of raw TCP?? If the answer is yes, you can make this so much easier.

  2. What are you doing inside of that function? In order for the TCP node to know what to reply to, you must return the original message.

Hello and thank you so much for your help!

Yes, I have some influence on how the requests are coming in. Until now we have six computers with Unreal Engine creating images on demand, each has its own port.

However I am very new to my company here and therefore have to be a lttle bit careful on changing things and politics, since everything has a long history of workload and performed okay until now. Actually I already revealed some potential in getting things faster here, thus I am very happy to answer your questions that might point me even further into the right directions.

We have OpenVPN running here and the PCs are - as said - adressed with TCP Ports opened at the hardware router. Actually I would like to open the VPN completely for the public while also completely taking it out of the company network, so in future the images could be directly downloaded from the vpn, then avoiding the double sending first to the server, then to the client.

Until then I have to stick to the request-request-response-response-solution. You say I should consider using HTTP, then i would have to find a mechanism that distributes the incoming requests to the appropriate PC. That would be of no problem, since the requests contain the right address in theit payload. However I am a bit unsure which impact that would have in terms of speed and security and so on. Also I don't know how to access HTTP-Servers created on Node-RED level behind the router can be accessed from outside, at least at the moment.

For now, maybe can you help me just putting into the paylod of the initially incomping request, to be able to respond to it?

Thank you so much!

Martin

Sure, No bother, but first, you have to answer:

i.e. show me the content (as text/code) of the function node named "CreateFileNameForRequest"

You are using a VPN and raw TCP - I'm afraid you may be kidding yourselves about security. I am not an expert but the risks of trusting VPNs is well documented.

you mention "distributes the incoming requests to the appropriate PC" - if you were using MQTT (a lightweight, publish-subscribe, machine-to-machine (M2M) network protocol) you would likely see speed improvements as well as security (MQTT can be secured and is used safely accross the internet!)

Without knowing the actual configuration it is hard to be certain but this is almost certainly highly insecure. A VPN connects two remote networks together. So the combination is only as secure as the least secure end - which you would have to assume is the public end.

This is a very simple problem, generally resolved either by your own proxy server or a cloud-based proxy such as Cloudflare Zero Trust.

Hello nice people! Thank you so much for your answers!

[OFFTOPIC Start: Security issues]

Even somewhat OT, I am happy to hear your opinion about the VPN and security issues. The VPN once had been set up to make ONLY the render-stations accessible to request them to create images on demand. But then it was more and more integrated into the companies network. So actually I suppose at the moment it is more vulnerable than it was meant to be.

However there has to be some way to serve the requests on demand. Thus we have to have a publicly available mechanism.

My idea is to physically disconnect the whole VPN from the companies network again and then close all ports on the router but one. Then use this single open port as an endpoint from outside. Actually this is the reason why I came across Node-RED: I was looking for a solution to create a dispatcher sitting behind that single port to distribute the requests to the render nodes.

Now WHAT is running behind this single open port, if it is a HTTP-Server provided by Node-RED or a single TCP-Server or whatever, that is part of further observations. Until now, as said, the WebPage connects directly to the PCs in the VPN via sockets. The PHP solution is running on the companies internet provider, no virtual server, absolutely standard configuration over there. The companies PHP-solution is made inhouse, under full control of the company. I already talked to the developers of the WebFrontend and asked them if one could change the request protocol to HTTP, and got the answer, that this was of no good, because then there has to be a timeout mechanism implemented. I don't get that really, but maybe I don't have the skills to grasp that.

To speed the whole delivery process even more, I was thinking to make the VPN a real Client-VPN. Having this, the images requested by the visitors of the WebPage would be delivered directly out of the VPN. But that is something I have to understand even more. The company is open for such solution, if it is reasonably secure. At least, the VPN would only consist of some PCs running Windows and some publicly available images. Nothing else.

In any case, in the future, there shall be a single Node-RED-Dispatcher that forwards the requests, but not directly to the rendering program (Unreal Engine) anymore, but to other Node-RED-flows running on the single PCs. So here Node-RED is running on the single computers, communicating with the rendering program Unreal Engine. Node-RED is working as a watchdog at this point, buffering messages, checking if Unreal Engine (prone to crashes) is still running, further making it possible to kill such hanging process, restart it, even restart the PC and so on.

So this is what I am working on right now: the watchdog, not the central dispatcher. And for now, the watchdog will be the TCP-Server for the WebFrontend, replacing the render software being the server itself. Okay, all of that was OT now, but I'd be very happy to hear your thoughts on that.

[/OFFTOPIC End]

With regards to the things described above, what I am trying now, in a single sentence: Create a TCP-Server in Node-RED, take the TCP-requests from the WebFrontend, buffer them to have the time for each WebP-image for each request being created, then attach the resulting WebP to the initially incoming message to send it back as r the response to the frontend.

As a new user I am not allowed to upload attachments here, so I hope you don't mind when I paste the flows in here. To answer your questions what I exactly am doing in the render nodes. Sorry.

Thank you so much! I appreciate your help really very, very much!

[
    {
        "id": "b292f534cec477dc",
        "type": "tcp request",
        "z": "f6c3cbfe3424217b",
        "name": "TCP-Server in Render-program, Port 9000",
        "server": "localhost",
        "port": "9000",
        "out": "sit",
        "ret": "string",
        "splitc": " ",
        "newline": "",
        "trim": false,
        "tls": "",
        "x": 1490,
        "y": 680,
        "wires": [
            [
                "1b3ab7efb687435d",
                "45dd5140290be219"
            ]
        ]
    },
    {
        "id": "40e6d4cfa60f5bd7",
        "type": "function",
        "z": "f6c3cbfe3424217b",
        "name": "AddToQueue",
        "func": "// Get the existing queue from flow context or initialize a new empty array\nvar queue = flow.get('queue') || [];\n\n// Add the full msg object to the queue\nmsg.custom_id = generateNewMsgId(); // Assign a new unique ID\nmsg.command = \"request\"; // Assign a new unique ID\n\nqueue.push(msg);\nnode.warn(\"Adding msg with id \" + msg.custom_id + \" payload: \" + JSON.stringify(msg.payload.params) + \", current queue length after pushing: \" + queue.length);\n\n// Store the updated queue back into flow context\nflow.set('queue', queue);\n\n// Log the current length of the queue to the console\n\n// Check if Unreal is busy or free to receive the next message\nvar busy = flow.get('busy') || false;\n\nif (!busy && queue.length > 0) {\n    // If Unreal is not busy, send the first message from the queue\n\n    var nextMsg = queue.shift(); // Take the first message off the queue\n\n//    node.warn(\"Unreal not busy, shifting, current queue length after shifting: \" + queue.length);\n    flow.set('queue', queue); // Update the queue in context\n    flow.set('busy', true); // Mark Unreal as busy\n\n\n\n    return nextMsg; // Return the message to be sent to the next node (TCP)\n}\n//node.warn(\"Unreal is busy, doing nothing, so current queue length is: \" + queue.length);\n\n\nreturn null; // Unreal is busy, don't send anything yet\n\nfunction generateNewMsgId() {\n    return Math.random().toString(36).substring(2, 10) + Date.now().toString(36); // Generates a longer unique ID\n}",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "// Code added here will be run once\n// whenever the node is started.\nflow.set('queue', []); // Reset the queue to an empty array\nflow.set('busy', false); // Ensure the busy flag is also reset",
        "finalize": "",
        "libs": [],
        "x": 810,
        "y": 680,
        "wires": [
            [
                "42c1b2143a841d6e"
            ]
        ]
    },
    {
        "id": "1b3ab7efb687435d",
        "type": "function",
        "z": "f6c3cbfe3424217b",
        "name": "Acknowledge Unreal ready state",
        "func": "// Reset the busy flag, indicating Unreal is ready for the next message\nflow.set('busy', false);\n\n// Get the queue\nvar queue = flow.get('queue') || [];\n\n// Log the current length of the queue to the console\n\n// If there are messages in the queue, send the next one\nif (queue.length > 0) {\n    var nextMsg = queue.shift(); // Remove the first message from the queue\n    node.warn(\"Sending next msg with id \" + msg.custom_id + \" payload: \" + JSON.stringify(msg.payload.params) + \", current queue length after sending: \" + queue.length);    \n    flow.set('queue', queue); // Update the queue in context\n    flow.set('busy', true); // Mark Unreal as busy again\n//    node.warn(\"Current queue length after acknowledgment: \" + queue.length);\n    return nextMsg; // Return the next message to be sent to the TCP Out Node\n}\nelse\n    node.error(\"LAST MESSAGE IN QUEUE SENT - QUEUE EMPTY\");\n\n\nreturn null; // No more messages in the queue",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1210,
        "y": 580,
        "wires": [
            [
                "42c1b2143a841d6e"
            ]
        ]
    },
    {
        "id": "0b68902a7ecfa77d",
        "type": "json",
        "z": "f6c3cbfe3424217b",
        "name": "",
        "property": "payload",
        "action": "str",
        "pretty": true,
        "x": 1250,
        "y": 680,
        "wires": [
            [
                "b292f534cec477dc"
            ]
        ]
    },
    {
        "id": "b05c9039451b6f3e",
        "type": "tcp in",
        "z": "f6c3cbfe3424217b",
        "name": "",
        "server": "server",
        "host": "",
        "port": "62483",
        "datamode": "stream",
        "datatype": "buffer",
        "newline": "",
        "topic": "",
        "trim": false,
        "base64": false,
        "tls": "",
        "x": 640,
        "y": 680,
        "wires": [
            [
                "40e6d4cfa60f5bd7"
            ]
        ]
    },
    {
        "id": "45dd5140290be219",
        "type": "debug",
        "z": "f6c3cbfe3424217b",
        "name": "Response from rendering program",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1840,
        "y": 680,
        "wires": []
    },
    {
        "id": "42c1b2143a841d6e",
        "type": "function",
        "z": "f6c3cbfe3424217b",
        "name": "CreateFileNameForRequest",
        "func": "\nlet data = JSON.parse(msg.payload);\n\ndata.props.sort((a, b) => a - b);\n\nlet filename = `${data.custom}-${data.project}-${data.unit}-${data.room}` +\n               `-${data.cam}-${data.light}-${data.palette}-` +\n               `${data.props.join('-')}`;\n\ndata.filename = filename;\n\nlet custom_id = generateNewMsgId();\ndata.custom_id = custom_id;\n\nlet command = \"request\";\ndata.command = command;\n\n\nmsg.payload = data;\n\nnode.warn(`Modified payload: ${JSON.stringify(msg.payload)}`);\n\nreturn msg;\n\nfunction generateNewMsgId() {\n    return Math.random().toString(36).substring(2, 10) + Date.now().toString(36); // Generates a longer unique ID\n}\n",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1060,
        "y": 680,
        "wires": [
            [
                "0b68902a7ecfa77d"
            ]
        ]
    }
]

After having seen all beginners tutorials and hours of testing, I finally made it work. Maybe this is of use for another beginner...

My flow simulates a client sending requests to a TCP-Server A, which buffers the requests to send them one by one to another TCP-Server B, which processes them, then responds back to TCP-Server A, which then in turn responds to the client.

Thanks a lot for your efforts!

Still I can't attach files here, thus as JSON

[
    {
        "id": "e4027f0dc4b50d08",
        "type": "comment",
        "z": "7e7c311a0f8dcf81",
        "name": "Emulating Client <-> TCP-Server <-> TCP-Server communication",
        "info": "# Emulating Client <-> TCP-Server <-> TCP-Server communication\nThis flow emulates a client sending messages which will be buffered on a TCP-Server, sent one by one to another TCP-Server, that processes the requests.",
        "x": 710,
        "y": 80,
        "wires": []
    },
    {
        "id": "145298825c41ffb1",
        "type": "group",
        "z": "7e7c311a0f8dcf81",
        "name": "Init",
        "style": {
            "fill": "#d1d1d1",
            "label": true,
            "color": "#000000"
        },
        "nodes": [
            "c8690eda7fbf3016",
            "d1175fcf1b66c758"
        ],
        "x": 14,
        "y": 39,
        "w": 432,
        "h": 82
    },
    {
        "id": "c8690eda7fbf3016",
        "type": "function",
        "z": "7e7c311a0f8dcf81",
        "g": "145298825c41ffb1",
        "name": "INIT",
        "func": "flow.set(\"queue\", []);\nflow.set(\"flowcounter\", 0);\n\nnode.warn(\"QUEUE LENGTH:\" + flow.get(\"queue\").length + \" | FLOWCOUNTER: \"+ flow.get(\"flowcounter\"));\nflow.set(\"busy\",false);\n",
        "outputs": 0,
        "timeout": 0,
        "noerr": 0,
        "initialize": "flow.set(\"queue\", []);\nflow.set(\"flowcounter\", 0);\n\nnode.warn(\"QUEUE LENGTH:\" + flow.get(\"queue\").length + \" | FLOWCOUNTER: \" + flow.get(\"flowcounter\"));\nflow.set(\"busy\", false);\n",
        "finalize": "flow.set(\"queue\", []);\nflow.set(\"flowcounter\", 0);\n\nnode.warn(\"QUEUE LENGTH:\" + flow.get(\"queue\").length + \" | FLOWCOUNTER: \" + flow.get(\"flowcounter\"));\nflow.set(\"busy\", false);\n",
        "libs": [],
        "x": 370,
        "y": 80,
        "wires": []
    },
    {
        "id": "d1175fcf1b66c758",
        "type": "inject",
        "z": "7e7c311a0f8dcf81",
        "g": "145298825c41ffb1",
        "name": "RESET Queue and counter",
        "props": [],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "x": 180,
        "y": 80,
        "wires": [
            [
                "c8690eda7fbf3016"
            ]
        ]
    },
    {
        "id": "b75b905e614281d1",
        "type": "group",
        "z": "7e7c311a0f8dcf81",
        "name": "EMULATOR Client (or whatever sends TCP-Requests)",
        "style": {
            "fill": "#c8e7a7",
            "label": true,
            "color": "#000000"
        },
        "nodes": [
            "b89748ba450ac707",
            "adfd8738a2d7cae8",
            "0bf3b0e9c8dda88d",
            "bed3f8ec500ce25b",
            "7c5c08a91e8ade63",
            "c19a519b27b7f1d2",
            "75cf2e5b421eb801",
            "22c2c6698bc32648",
            "045e6c2224c07783",
            "62d0441961851971"
        ],
        "x": 14,
        "y": 139,
        "w": 1412,
        "h": 122
    },
    {
        "id": "b89748ba450ac707",
        "type": "inject",
        "z": "7e7c311a0f8dcf81",
        "g": "b75b905e614281d1",
        "name": "",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "{\"type\":\"request\",\"state\":\"unprocessed\"}",
        "payloadType": "json",
        "x": 110,
        "y": 220,
        "wires": [
            [
                "bed3f8ec500ce25b"
            ]
        ]
    },
    {
        "id": "adfd8738a2d7cae8",
        "type": "debug",
        "z": "7e7c311a0f8dcf81",
        "g": "b75b905e614281d1",
        "name": "Server request",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 620,
        "y": 180,
        "wires": []
    },
    {
        "id": "0bf3b0e9c8dda88d",
        "type": "function",
        "z": "7e7c311a0f8dcf81",
        "g": "b75b905e614281d1",
        "name": "AddCounter",
        "func": "let counter = flow.get(\"flowcounter\");\nmsg.payload.flowcounter = counter;\ncounter = counter + 1;\nflow.set(\"flowcounter\",counter);\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 430,
        "y": 220,
        "wires": [
            [
                "adfd8738a2d7cae8",
                "75cf2e5b421eb801"
            ]
        ]
    },
    {
        "id": "bed3f8ec500ce25b",
        "type": "function",
        "z": "7e7c311a0f8dcf81",
        "g": "b75b905e614281d1",
        "name": "CreateGUID",
        "func": "function generateGUID() {\n    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n        let r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);\n        return v.toString(16);\n    });\n}\n\n// Ensure msg.payload is an object (if it’s not already)\nif (typeof msg.payload !== 'object' || msg.payload === null) {\n    msg.payload = {};  // Initialize as an empty object if needed\n}\n\n// Generate a GUID and add it to msg.payload\nmsg.payload.guid = generateGUID();\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 270,
        "y": 220,
        "wires": [
            [
                "0bf3b0e9c8dda88d"
            ]
        ]
    },
    {
        "id": "7c5c08a91e8ade63",
        "type": "debug",
        "z": "7e7c311a0f8dcf81",
        "g": "b75b905e614281d1",
        "name": "FINALLY",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1320,
        "y": 220,
        "wires": []
    },
    {
        "id": "c19a519b27b7f1d2",
        "type": "complete",
        "z": "7e7c311a0f8dcf81",
        "g": "b75b905e614281d1",
        "name": "",
        "scope": [
            "daef06209d72aad1"
        ],
        "uncaught": false,
        "x": 1050,
        "y": 220,
        "wires": [
            [
                "045e6c2224c07783"
            ]
        ]
    },
    {
        "id": "75cf2e5b421eb801",
        "type": "json",
        "z": "7e7c311a0f8dcf81",
        "g": "b75b905e614281d1",
        "name": "toString",
        "property": "payload",
        "action": "str",
        "pretty": false,
        "x": 600,
        "y": 220,
        "wires": [
            [
                "22c2c6698bc32648"
            ]
        ]
    },
    {
        "id": "22c2c6698bc32648",
        "type": "tcp out",
        "z": "7e7c311a0f8dcf81",
        "g": "b75b905e614281d1",
        "name": "",
        "host": "127.0.0.1",
        "port": "62000",
        "beserver": "client",
        "base64": false,
        "end": true,
        "tls": "",
        "x": 820,
        "y": 220,
        "wires": []
    },
    {
        "id": "045e6c2224c07783",
        "type": "json",
        "z": "7e7c311a0f8dcf81",
        "g": "b75b905e614281d1",
        "name": "",
        "property": "payload",
        "action": "",
        "pretty": false,
        "x": 1190,
        "y": 220,
        "wires": [
            [
                "7c5c08a91e8ade63"
            ]
        ]
    },
    {
        "id": "62d0441961851971",
        "type": "comment",
        "z": "7e7c311a0f8dcf81",
        "g": "b75b905e614281d1",
        "name": "Inject messages here. Fast!",
        "info": "",
        "x": 160,
        "y": 180,
        "wires": []
    },
    {
        "id": "2885526795da429b",
        "type": "group",
        "z": "7e7c311a0f8dcf81",
        "name": "TCP-Server: Watchdog with Message Buffer",
        "style": {
            "fill": "#ffefbf",
            "label": true,
            "color": "#000000"
        },
        "nodes": [
            "ac777e3562cebd25",
            "95a07b23231f73c8",
            "419ca868a9a718a5",
            "daef06209d72aad1",
            "4115a1eef647ae8c",
            "421bd258294fc1a5",
            "1b3ab7efb687435d",
            "9ebded45a59bb4e3",
            "71056ad2ce2bc309",
            "469c639ce9a8e9b0",
            "89b15622b8ceb350",
            "5a3aaa8423c8fe72"
        ],
        "x": 14,
        "y": 279,
        "w": 1412,
        "h": 262
    },
    {
        "id": "ac777e3562cebd25",
        "type": "tcp in",
        "z": "7e7c311a0f8dcf81",
        "g": "2885526795da429b",
        "name": "Watchdog In @ TCP 62000",
        "server": "server",
        "host": "",
        "port": "62000",
        "datamode": "single",
        "datatype": "utf8",
        "newline": "",
        "topic": "Watchdog In",
        "trim": false,
        "base64": false,
        "tls": "",
        "x": 150,
        "y": 440,
        "wires": [
            [
                "9ebded45a59bb4e3",
                "71056ad2ce2bc309"
            ]
        ]
    },
    {
        "id": "95a07b23231f73c8",
        "type": "tcp request",
        "z": "7e7c311a0f8dcf81",
        "g": "2885526795da429b",
        "name": "Request Unreal @ TCP 63000",
        "server": "127.0.0.1",
        "port": "63000",
        "out": "time",
        "ret": "buffer",
        "splitc": "100",
        "newline": "",
        "trim": false,
        "tls": "",
        "x": 850,
        "y": 440,
        "wires": [
            [
                "89b15622b8ceb350",
                "daef06209d72aad1",
                "1b3ab7efb687435d"
            ]
        ],
        "info": "Request to Unreal"
    },
    {
        "id": "419ca868a9a718a5",
        "type": "debug",
        "z": "7e7c311a0f8dcf81",
        "g": "2885526795da429b",
        "name": "Watchdog 62000 Out",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1280,
        "y": 480,
        "wires": []
    },
    {
        "id": "daef06209d72aad1",
        "type": "tcp out",
        "z": "7e7c311a0f8dcf81",
        "g": "2885526795da429b",
        "name": "TCPWatchDogDone",
        "host": "",
        "port": "",
        "beserver": "reply",
        "base64": false,
        "end": false,
        "tls": "",
        "x": 1160,
        "y": 420,
        "wires": []
    },
    {
        "id": "4115a1eef647ae8c",
        "type": "debug",
        "z": "7e7c311a0f8dcf81",
        "g": "2885526795da429b",
        "name": "Watchdog 62000 from Queue",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 680,
        "y": 500,
        "wires": []
    },
    {
        "id": "421bd258294fc1a5",
        "type": "function",
        "z": "7e7c311a0f8dcf81",
        "g": "2885526795da429b",
        "name": "AddToQueue",
        "func": "var queue = flow.get('queue') || [];\nvar busy = flow.get('busy') || false;\n\nqueue.push(msg);\nflow.set('queue', queue);\n\nnode.log(\"queue busy: \" + busy + \" | Adding msg with id \" + msg.payload.guid + \" counter: \" + msg.payload.flowcounter + \", queue length after push: \" + queue.length);\n\n\nvar busy = flow.get('busy') || false;\n\nif (!busy && queue.length > 0) {\n    // If Unreal is not busy, send the first message from the queue\n\n    var nextMsg = queue.shift(); // Take the first message off the queue\n\n//    node.warn(\"Unreal not busy, shifting, current queue length after shifting: \" + queue.length);\n    flow.set('queue', queue); // Update the queue in context\n    flow.set('busy', true); // Mark Unreal as busy\n\n\n\n    return nextMsg; // Return the message to be sent to the next node (TCP)\n}\n//node.warn(\"Unreal is busy, doing nothing, so current queue length is: \" + queue.length);\n\n\nreturn null; // Unreal is busy, don't send anything yet\n",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 470,
        "y": 440,
        "wires": [
            [
                "4115a1eef647ae8c",
                "469c639ce9a8e9b0"
            ]
        ]
    },
    {
        "id": "1b3ab7efb687435d",
        "type": "function",
        "z": "7e7c311a0f8dcf81",
        "g": "2885526795da429b",
        "name": "Acknowledge ready state",
        "func": "\nvar queue = flow.get('queue') || [];\nnode.log(\"Acknowleding reponse from UE | Queue busy before check: \" + flow.get('busy') + \" | Queue length before check: \" + queue.length);\n\n// Get the queue\nflow.set('busy', false);\n\n// Log the current length of the queue to the console\n\n// If there are messages in the queue, send the next one\nif (queue.length > 0) {\n    var nextMsg = queue.shift(); // Remove the first message from the queue\n    node.log(\"Sending next msg with guid \" + nextMsg.payload.guid + \" counter: \" + nextMsg.payload.flowcounter + \" | queue length after sending: \" + queue.length);   \n    flow.set('queue', queue); // Update the queue in context\n    flow.set('busy', true); // Mark Unreal as busy again\n//    node.warn(\"Current queue length after acknowledgment: \" + queue.length);\n    return nextMsg; // Return the next message to be sent to the TCP Out Node\n}\nelse\n    node.error(\"LAST MESSAGE IN QUEUE SENT - QUEUE EMPTY\");\n\n\nreturn null; // No more messages in the queue",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 770,
        "y": 360,
        "wires": [
            [
                "469c639ce9a8e9b0"
            ]
        ]
    },
    {
        "id": "9ebded45a59bb4e3",
        "type": "debug",
        "z": "7e7c311a0f8dcf81",
        "g": "2885526795da429b",
        "name": "Watchdog 62000 In",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 370,
        "y": 500,
        "wires": []
    },
    {
        "id": "71056ad2ce2bc309",
        "type": "json",
        "z": "7e7c311a0f8dcf81",
        "g": "2885526795da429b",
        "name": "",
        "property": "payload",
        "action": "",
        "pretty": false,
        "x": 330,
        "y": 440,
        "wires": [
            [
                "421bd258294fc1a5"
            ]
        ]
    },
    {
        "id": "469c639ce9a8e9b0",
        "type": "json",
        "z": "7e7c311a0f8dcf81",
        "g": "2885526795da429b",
        "name": "",
        "property": "payload",
        "action": "",
        "pretty": false,
        "x": 650,
        "y": 440,
        "wires": [
            [
                "95a07b23231f73c8"
            ]
        ]
    },
    {
        "id": "89b15622b8ceb350",
        "type": "json",
        "z": "7e7c311a0f8dcf81",
        "g": "2885526795da429b",
        "name": "",
        "property": "payload",
        "action": "",
        "pretty": false,
        "x": 1110,
        "y": 480,
        "wires": [
            [
                "419ca868a9a718a5"
            ]
        ]
    },
    {
        "id": "5a3aaa8423c8fe72",
        "type": "comment",
        "z": "7e7c311a0f8dcf81",
        "g": "2885526795da429b",
        "name": "After a message is done, check if more messages are waiting.",
        "info": "",
        "x": 880,
        "y": 320,
        "wires": []
    },
    {
        "id": "a2cddeeeb6df5b61",
        "type": "group",
        "z": "7e7c311a0f8dcf81",
        "name": "TCP-Server processing the requests one by one.",
        "style": {
            "label": true,
            "color": "#000000",
            "fill": "#bfdbef"
        },
        "nodes": [
            "8eedbd8a913e3510",
            "402456a02fe1a44f",
            "965b1a7cef34608b",
            "f5c99c38afb98aff",
            "249b754e2c1ea840",
            "ba3e2ece5869de2d",
            "a2efcb6aaf8b2059",
            "3d6ef7bc87a4e063",
            "e6cedb24e5d21c7c"
        ],
        "x": 14,
        "y": 579,
        "w": 1272,
        "h": 162
    },
    {
        "id": "8eedbd8a913e3510",
        "type": "tcp in",
        "z": "7e7c311a0f8dcf81",
        "g": "a2cddeeeb6df5b61",
        "name": "UNREAL Server TCP 63000",
        "server": "server",
        "host": "",
        "port": "63000",
        "datamode": "stream",
        "datatype": "buffer",
        "newline": "",
        "topic": "",
        "trim": false,
        "base64": false,
        "tls": "",
        "x": 160,
        "y": 660,
        "wires": [
            [
                "ba3e2ece5869de2d"
            ]
        ]
    },
    {
        "id": "402456a02fe1a44f",
        "type": "debug",
        "z": "7e7c311a0f8dcf81",
        "g": "a2cddeeeb6df5b61",
        "name": "UE 63000 In",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 510,
        "y": 700,
        "wires": []
    },
    {
        "id": "965b1a7cef34608b",
        "type": "tcp out",
        "z": "7e7c311a0f8dcf81",
        "g": "a2cddeeeb6df5b61",
        "name": "UNREAL Out @ TCP 63000",
        "host": "",
        "port": "",
        "beserver": "reply",
        "base64": false,
        "end": false,
        "tls": "",
        "x": 1140,
        "y": 660,
        "wires": []
    },
    {
        "id": "f5c99c38afb98aff",
        "type": "delay",
        "z": "7e7c311a0f8dcf81",
        "g": "a2cddeeeb6df5b61",
        "name": "Simulate workload time",
        "pauseType": "delay",
        "timeout": "3",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 750,
        "y": 660,
        "wires": [
            [
                "3d6ef7bc87a4e063"
            ]
        ]
    },
    {
        "id": "249b754e2c1ea840",
        "type": "debug",
        "z": "7e7c311a0f8dcf81",
        "g": "a2cddeeeb6df5b61",
        "name": "UE 63000 out",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1100,
        "y": 620,
        "wires": []
    },
    {
        "id": "ba3e2ece5869de2d",
        "type": "json",
        "z": "7e7c311a0f8dcf81",
        "g": "a2cddeeeb6df5b61",
        "name": "",
        "property": "payload",
        "action": "",
        "pretty": false,
        "x": 350,
        "y": 660,
        "wires": [
            [
                "402456a02fe1a44f",
                "a2efcb6aaf8b2059"
            ]
        ]
    },
    {
        "id": "a2efcb6aaf8b2059",
        "type": "function",
        "z": "7e7c311a0f8dcf81",
        "g": "a2cddeeeb6df5b61",
        "name": "set state processed",
        "func": "msg.payload.state = \"processed\";\nreturn msg;\n\n",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 530,
        "y": 660,
        "wires": [
            [
                "f5c99c38afb98aff"
            ]
        ]
    },
    {
        "id": "3d6ef7bc87a4e063",
        "type": "json",
        "z": "7e7c311a0f8dcf81",
        "g": "a2cddeeeb6df5b61",
        "name": "",
        "property": "payload",
        "action": "",
        "pretty": false,
        "x": 930,
        "y": 660,
        "wires": [
            [
                "965b1a7cef34608b",
                "249b754e2c1ea840"
            ]
        ]
    },
    {
        "id": "e6cedb24e5d21c7c",
        "type": "comment",
        "z": "7e7c311a0f8dcf81",
        "g": "a2cddeeeb6df5b61",
        "name": "Set the message as being processed",
        "info": "Set the message state to \"processed\"",
        "x": 580,
        "y": 620,
        "wires": []
    }
]

THANK YOU, DEVELOPERS!

Just wanted to say, now that I am starting to understand how Node-RED works, I am so impressed by this tool! It is really outstanding smart and nice and well done. Incredible! Thus: Thank you very much!

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.