Creating Serial COMM-Port Simulator using Socat


Developing an application that integrates with devices like Printer, Cash Acceptor, or any other device requires us to carry the devices anywhere and anytime when we are going to work, especially when we are working in a hybrid WFH-WHO office.

In this post, we will talk about Creating a Serial COMM-Port Simulator using Socat. Socat stands for SOcket CAT, its utility to transfer data between two channels with both directions.  

There are many different types of channels socat can connect, including:

  • Files

  • Pipes

  • Devices (serial line, pseudo-terminal, etc)

  • Sockets (UNIX, IP4, IP6 - raw, UDP, TCP)

  • SSL sockets

  • Proxy CONNECT connections

  • File descriptors (stdin, etc)

  • The GNU line editor (readline)

  • Programs

  • Combinations of two of these

For more information about Socat you can find it on its documentations (http://www.dest-unreach.org/socat/


We are going to create a Printer Simulator using Serial comm-port forwarded to TCP Servers’s Simulator using Socat and Java:


First of all, prepare Socat command to be executed by PrinterSimulator


socat -d -d pty,raw,echo=0,link=/tmp/ttyPrinter tcp=127.0.0.1:3006,retry=5


Explanation

  1. socat: the socat command

  2. -d -d : Print fatal, error, warning, notice logs

  3. pty: Generates a pseudo terminal (pty) and uses its master side

  4. raw: Sets raw mode, thus passing input and output almost unprocessed

  5. echo=0: disable local echo 

  6. link=/tmp/ttyPrinter : Generates a symbolic link that points to the actual pseudo terminal (pty). Without this, socat will generate pty with sequence name like: /dev/pts/1

  7.  tcp=172.0.0.1:3006 : forward data to this (simulator) address 

  8. retry=5: Number of retries before the connection or listen attempt is aborted. Default is 0, which means just one attempt.


After specifying the socat command, we are going to create TCP Server on PrinterSimulator. This is going to be a simple SocketServer that will always return success.


public class PrinterSimulatorTcp {
	
	public static void main(String[] args) {
		PrinterSimulatorTcp instance = new PrinterSimulatorTcp();
		instance.startSimulator();
		
		new Thread( () -> {
			MainSimulator.executeShellCommand("socat -dd pty,raw,echo=0,link=/tmp/ttyPrinter tcp:127.0.0.1:3006,retry=5");
		}).start();
	}
	
	public void startSimulator() {
		System.out.println("STARTING TCP 3006");
		new Thread( () -> {
			try (ServerSocket server = new ServerSocket(3006))
           {
           	try (
       				Socket socket = server.accept();
               		InputStream in = socket.getInputStream();
               		OutputStream out = socket.getOutputStream()
               ) {
					while (true) {
               		ByteArrayOutputStream baos = new ByteArrayOutputStream();
						byte[] read = new byte[1024];
						int len = in.read(read);
						System.out.println("INCOMMING MESSAGE");
						baos.write(read, 0, len);
						byte[] bytes = baos.toByteArray();
						StringBuilder sb = new StringBuilder();
						for (byte b : bytes) {
							sb.append(String.format("%02X ", b));
						}
	                    System.out.println(sb);
	                    if (sb.toString().startsWith("10"))						
	                    	out.write(new byte[]{0x12});
					}
               }
           }
           catch (IOException ex)
           {
           	ex.printStackTrace();
           }
		}).start();
	}
	
	public static String executeShellCommand(String command)
	{
		StringBuffer output = new StringBuffer();
		Process p;
		try {
			p = Runtime.getRuntime().exec(new String[]{"bash", "-c", command});
			p.waitFor();
			BufferedReader reader =  new BufferedReader(new InputStreamReader(p.getInputStream()));
			String line = "";			
			while ((line = reader.readLine())!= null)
			{
				output.append(line + "\n");
			}
			
			System.out.println(output.toString());
			return output.toString();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}

Explanation:
  1. we have three methods called executeShellCommand and startSimulator and main method
  2. startSimulator will start tcp server on port 3006 and waiting for incomming message with method in.read(read) after message received, it will check the content start with "10" (hex string), it will response "12" (hex)
  3. executeShellCommand will execute socat command, socat will forward incomming command in the serial port /tmp/ttyPrinter to 127.0.0.1:3006
  4. response from our simulator will be forwared back to the serial port.
  5. main method used to execute socat command, and tcp server on different thread.
To test PrinterSimulatorTcp, you can use this command:

echo -en '\x10\x02\x02\x02' > /tmp/ttyPrinter

above command will sending data "10 02 02 02" to serial COMM-Port /tmp/ttyPrinter

Results:


Thats all about Creating a Serial COMM-Port Simulator using Socat. hope this help.

Comments

Popular posts from this blog

Creating ISO8583 Server using Spring Integration

SFTP Client using SSHJ

Create ZIP Stream Before Uploading Files to SFTP