Skip to menu

Robotics with Object Pascal

Rover written with RUST

IPC method

2025.11.10 23:35

me Views:31

For communication between applications on the same device (Inter-Process Communication or IPC), Shared Memory is the theoretical fastest method, but Unix Domain Sockets are often the most practical recommendation for your specific case.

Here is the breakdown of speed and overhead:

 

1. The Fastest: Shared Memory (RAM)

 

  • Speed: ???? Fastest

  • How it works: You designate a block of RAM that both your Rust app and Pascal app can read/write to directly.

  • Why it's fast: It is "Zero-Copy." The data doesn't move; one program writes it, and the other instantly sees it without the operating system having to copy bytes around.

  • Your Situation: You are already using this! Writing to /dev/shm/ creates files in RAM.

    • The bottleneck: The slowness you might feel isn't the "method" (writing to file), it is the Text Conversion.

    • Fix: Converting float numbers to text (e.g., "-123.456") is computationally expensive. If you write the raw binary bytes of the integers/floats to /dev/shm/dots.bin, it will be 10x-50x faster than writing text files.

 

2. The Best Candidate: Unix Domain Sockets (UDS)

 

  • Speed: ???? Very Fast (Faster than UDP/TCP)

  • How it works: It looks like a network socket but stays entirely inside the Linux kernel. It creates a special file (e.g., /tmp/vo_socket).

  • Why it's better than UDP:

    • Zero Network Overhead: It bypasses the entire network stack (no IP headers, no checksums, no routing lookups).

    • Reliable: Unlike UDP, it guarantees data arrives in order and isn't dropped, but it's still extremely lightweight.

  • Recommendation: This is the standard way to connect local apps (e.g., how Docker communicates with its daemon).

 

3. The Candidate: UDP (Loopback)

 

  • Speed: ???? Fast, but has overhead

  • How it works: Sends network packets to 127.0.0.1.

  • Verdict: It is slower than Unix Sockets because the OS still has to pretend it's sending a network packet. It calculates checksums and headers even though it's just going to itself.

  • When to use: Only use UDP if you plan to move your viewer to a different computer later (e.g., viewing the robot's data from your laptop over WiFi).

 

Summary Recommendation

 

  1. If you stay on the same device: Keep using /dev/shm/ (Shared Memory), but switch to Binary.

    • Instead of writing text: "-500 120 2500" (13 bytes + conversion cost)

    • Write raw bytes: [0xC4, 0x7A, 0x00, 0x00...] (12 bytes, zero conversion cost).

    • Pascal can easily read raw binary structures.

  2. If you want a "stream" (like video): Use Unix Domain Sockets. It handles the flow control for you so you don't have to worry about reading a file at the exact same moment it's being written.

 

 

Hex.png

 

I am writing them as 32-bit Signed Integers (4 bytes each) in Little Endian format.

Here is the explanation of the data in your dots_49.bin file based on your screenshot (Hex.png):

 

The Data Structure

 

Every 12 bytes represents one 3D point:

  1. X (4 bytes)

  2. Y (4 bytes)

  3. Z (4 bytes)

 

Decoding Your First Point (Offset 00-0B)

 

1. X Coordinate (Bytes 00-03): EA FE FF FF

  • Since this is Little Endian, you read the bytes backwards: FF FF FE EA

  • In hexadecimal, 0xFFFF FEEA represents a negative number.

  • Decimal Value: -278

2. Y Coordinate (Bytes 04-07): 65 03 00 00

  • Read backwards: 00 00 03 65

  • Decimal Value: 869 ()

3. Z Coordinate (Bytes 08-0B): 54 FA FF FF

  • Read backwards: FF FF FA 54

  • Decimal Value: -1452

So, your first point is at (x: -278, y: 869, z: -1452) millimeters.

This makes sense because we applied your offsets:

  • X: Original was likely around 722. We applied OFFSET_X = -1000. Result: -278.

  • Y: Original was likely around -131. We applied OFFSET_Y = 1000. Result: 869.

 

How to read this in Pascal

type
  TPoint3D = record
    x: Integer; // 4 bytes (Int32)
    y: Integer; // 4 bytes (Int32)
    z: Integer; // 4 bytes (Int32)
  end;

 

Here is the complete Object Pascal (Free Pascal / Lazarus) code to read the binary files generated by your Rust program.

This code does the following:

  1. Defines a packed record that exactly matches the 12-byte raw format.

  2. Loops through /dev/shm/dots_00.bin to dots_49.bin.

  3. Reads the raw binary data directly into a dynamic array (extremely fast).

  4. Prints the first few coordinates to the console so you can verify they match your Rust output.

 

How to use

 

  1. Open Lazarus.

  2. File -> New -> Simple Program (or Console Application).

  3. Paste this code.

  4. Run it (F9).

 

ReadVoDots.lpr

program ReadVoDots;

{$mode objfpc}{$H+}

uses
  Classes, SysUtils;

type
  // This record exactly matches the 12 bytes per point written by Rust.
  // "packed" ensures the compiler doesn't add gaps/padding between fields.
  TPoint3D = packed record
    X: Int32; // 4 bytes
    Y: Int32; // 4 bytes
    Z: Int32; // 4 bytes
  end;

// A dynamic array to hold the loaded cloud
type
  TPointCloud = array of TPoint3D;

// --- Function to Read a Single Binary File ---
procedure LoadPointCloud(const Filename: string; out Cloud: TPointCloud);
var
  Stream: TFileStream;
  FileSize: Int64;
  NumPoints: Integer;
  i: Integer;
begin
  if not FileExists(Filename) then
  begin
    // Silent exit or log error if file isn't ready yet
    // WriteLn('File not found: ', Filename);
    Exit;
  end;

  Stream := TFileStream.Create(Filename, fmOpenRead or fmShareDenyNone);
  try
    FileSize := Stream.Size;
    
    // Calculate how many points are in the file
    NumPoints := FileSize div SizeOf(TPoint3D);
    
    // Allocate memory for all points at once
    SetLength(Cloud, NumPoints);

    if NumPoints > 0 then
    begin
      // BLAST SPEED: Read the entire file directly into the array memory.
      // No loop, no parsing, just raw memory copy.
      Stream.ReadBuffer(Cloud[0], FileSize);
      
      WriteLn(Format('Loaded %s: %d points', [ExtractFileName(Filename), NumPoints]));

      // --- VERIFICATION ---
      // Print the first 3 points to check if values look sane (integers like -500, 120, etc)
      for i := 0 to 2 do
      begin
        if i < NumPoints then
          WriteLn(Format('   [%d] X: %d   Y: %d   Z: %d', [i, Cloud[i].X, Cloud[i].Y, Cloud[i].Z]));
      end;
    end;

  finally
    Stream.Free;
  end;
end;

var
  i: Integer;
  CurrentFile: string;
  MyCloud: TPointCloud;
begin
  WriteLn('--- Starting Point Cloud Reader ---');

  // Loop through the sequence generated by Rust (00 to 49)
  for i := 0 to 49 do
  begin
    // Format the filename exactly as Rust saves it: dots_00.bin, dots_01.bin...
    CurrentFile := Format('/dev/shm/dots_%0.2d.bin', [i]);
    
    LoadPointCloud(CurrentFile, MyCloud);
    
    // Here "MyCloud" contains your data. 
    // You can now pass "MyCloud" to your OpenGL renderer or processing logic.
  end;

  WriteLn('--- Done ---');
  // ReadLn; // Uncomment if you run from terminal and want it to pause
end.

 

Why this works

 

  • Int32: Matches Rust's i32 perfectly.

  • packed record: Ensures the memory layout is identical (X then Y then Z, no spaces).

  • Stream.ReadBuffer(Cloud[0], ...): This is the fastest possible way to read data in Pascal. It dumps the file content directly into RAM in one operation.