{"id":3737,"date":"2017-11-16T05:05:55","date_gmt":"2017-11-16T04:05:55","guid":{"rendered":"https:\/\/andyland.info\/wordpress\/?p=3737"},"modified":"2017-11-15T10:26:44","modified_gmt":"2017-11-15T09:26:44","slug":"grandma-version-1-command-line-interface-java","status":"publish","type":"post","link":"https:\/\/andyland.info\/wordpress\/grandma-version-1-command-line-interface-java\/","title":{"rendered":"GrandMA Version 1 Command Line Interface implemented in Java"},"content":{"rendered":"<p>Due to &#8230; massive &#8230; request I took the chance and did a major overhaul of my post about the GrandMA remote command line from\u00a0<a href=\"https:\/\/andyland.info\/wordpress\/?p=817\">a few years ago<\/a>. Back in the days I pulled together a quick &#8216;n dirty version which somehow worked and caught some interest but never really became more than a proof of concept.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-3811 alignleft\" src=\"https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/gma_remoteCMD_dark.jpg\" alt=\"\" width=\"600\" height=\"278\" srcset=\"https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/gma_remoteCMD_dark.jpg 600w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/gma_remoteCMD_dark-300x139.jpg 300w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/gma_remoteCMD_dark-250x116.jpg 250w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/gma_remoteCMD_dark-150x70.jpg 150w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p><strong>tl;dr:<\/strong> This program provides a remote command line to a GrandMA Version 1 lighting desk (console or onPC). Since it&#8217;s implemented in Java you can basically run it from every computer that you have at hand ( yes: even a Raspberry PI\u00a0).\u00a0Having a command line proved to be quite handy especially when you are setting up a show from scratch. It is simply faster to issue the command &#8220;fader 1.1 thru 5.10 at 0&#8221; than to manually set 90 faders to zero (or -god forbid- use a mouse to do this on the onPC-version without a touchscreen).<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>I&#8217;m a little late on that topic: Meanwhile ( meaning: a few years ago )\u00a0the GrandMA Version 2 has been released and\u00a0it incorporates a native Telnet interface. This somehow makes my version obsolete but &#8230; people asked for it and I owe it to myself, I think.<\/p>\n<p><!--more--><\/p>\n<p>&nbsp;<\/p>\n<p>This\u00a0was initially planned\u00a0to be\u00a0the first &#8216;official&#8217; release to benefit from the GUI-Template which I am working on for quite some time now but I realized that it carries\u00a0 way too much overhead for a simple Demonstration like this.\u00a0That&#8217;s why\u00a0I quickly put together a rather minimalistic version which only\u00a0incorporates the basic features. You can easily expand from here.\u00a0 ( Don&#8217;t worry: I&#8217;ll post a VERY thorough documentation of my GUI-template soon =). )<\/p>\n<p>&nbsp;<\/p>\n<p><img decoding=\"async\" class=\"size-large wp-image-3739 alignleft\" src=\"https:\/\/www.andyland.info\/grafiken\/grandmacmd\/gma_006.jpg\" alt=\"\" \/><\/p>\n<p>Let&#8217;s first take another look at the old version. It connects to a GrandMA console relying on a hardcoded username (&#8216;andy&#8217;) and an empty password. Back then I didn&#8217;t have the time, need\u00a0and energy to care about how these things are implemented.<\/p>\n<p>In addition to the pure commandline a small set of functions (2 faders, some buttons) is implemented.<\/p>\n<p>It only works on Windows. This is a limitation which makes it somehow senseless to have a remote command line. Because&#8230;. well &#8230; I might call it common sense.<\/p>\n<p>Reading <a href=\"https:\/\/andyland.info\/wordpress\/?p=817\">the old post<\/a> and looking at the old source-code I realised that there were some imprecise statements. Some would say that the main parts were just plain wrong so it&#8217;s reasonable enough to do fix this within a new post.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>Here is a quick recap of the original idea: The GrandMA lighting desk provides a local command-line Interface which basically gives you access to everything you would otherwise\u00a0achieve via a combination of\u00a0mouseklicks. There&#8217;s also the possibility to run multiple consoles within a network and you could use another console&#8217;s interface\u00a0to\u00a0remotely\u00a0access a command line. In addition there was a software for the PocketPC-platform which -among other things- allowed access to the console&#8217;s command-line via network. There&#8217;s probably also some IOS-Version floating around but it seems to be outdated.<\/p>\n<p>To make things a little more <del>weird<\/del> complex there even was a PocketPC-emulator. This means instead of simply running an instance of GrandMA onPC on your Windows-computer and connecting this to another GrandMA console you could start an emulated version of the PocketPC-software which not only had a reduced set of buttons but also successfully emulated the actual screen size of the PocketPC&#8217;s display. Trust me: THAT thing is a true PITA to use.\u00a0As I mentioned in the <a href=\"https:\/\/andyland.info\/wordpress\/?p=817\">original post<\/a> there a some things I will probably never understand.<\/p>\n<p>But who am I to complain: Using the PocketPC emulator on a Windows-PC to remotely access the command-line\u00a0on another PC\u00a0and analysing the data with Wireshark sets the base for my implementation of a remote command-line.<\/p>\n<p>Here we go: Situated on the left is my terribly outdated and faulty laptop which I revived because it still had an installation of the PocketPC emulator. Realise how the emulator perfectly ( and senselessly ) maps the original screen size of the PocketPC. \u00a0An instance of Wireshark is also running in the background. The centre Screen shows\u00a0is my Linux-desktop running eclipse and the implementation of the remote CMD. On the right is my girlfriend&#8217;s laptop running the latest version of GrandMA onPC version 1 \u00a0(she doesn&#8217;t know it but she always has the latest version on her Laptop &#8211; god knows how).<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-3738\" src=\"https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/1010817-1024x576.jpg\" alt=\"\" width=\"800\" height=\"450\" srcset=\"https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/1010817.jpg 1024w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/1010817-300x169.jpg 300w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/1010817-768x432.jpg 768w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/1010817-250x141.jpg 250w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/1010817-150x84.jpg 150w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>In order to be able to remotely use a commandline we first need to configure a user-account\u00a0on the target console. This is done via &#8220;Tools | Setup User &amp; Profiles&#8221;. I didn&#8217;t manage to\u00a0successfully login from remote\u00a0using the built-in Administrator-account yet. Either way it&#8217;s always a good idea to have dedicated accounts for\u00a0these kind of things. (And for those of you unfamiliar with this topic: yes, once logged in, everybody can see the plaintext passwords of\u00a0other users. You surely wouldn&#8217;t to this nowadays but\u00a0that&#8217;s not\u00a0an issue in this scenario.\u00a0)<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-3816\" src=\"https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/gmaUser-1024x680.jpg\" alt=\"\" width=\"800\" height=\"531\" srcset=\"https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/gmaUser-1024x680.jpg 1024w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/gmaUser-300x199.jpg 300w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/gmaUser-768x510.jpg 768w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/gmaUser-226x150.jpg 226w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/gmaUser-150x100.jpg 150w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/gmaUser.jpg 1536w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>To implement our own remote command line we need to make\u00a0three TCP connections to the console on port 7001, 7002 and 7003:<\/p>\n<p>The first steps consist of opening a TCP Connection to port 7003 und 7001 of the remote console.\u00a0An initial handshake-signature is sent to port 7003. This signature seems to differ everytime but I am using a constant signature for a few years now and until now\u00a0there were no signs of\u00a0this being a problem.<\/p>\n<p style=\"text-align: center;\">\u00a0<strong>Byte[]{0xDC, 0x05, 0x00, 0x00}<\/strong><\/p>\n<p>The next step is sending the username (and a password, if necessary) to the console. Therefore a single packet is sent to the console to port 7001 containing only 4 Bytes of data:<\/p>\n<p style=\"text-align: center;\"><strong>Byte[]{LENGTHOFUSERNAME, 0x00, 0x00, 0x00}<\/strong>.<\/p>\n<p>The actual username is directly sent afterwards within one single TCP-packet. Keep an eye on sending a username of the correct length as specified in the packet before. If you mess this up you can instantly crash the console via remote (tested with GrandMA onPC v6.808). If you REALLY want to mess with other people&#8217;s setup &#8230; this is a good place to activate\u00a0your 1337 3V!L H4XX0|2 M0|)3.<\/p>\n<p>To complete the sending of the username the code for &lt;Enter&gt; has to be sent. That&#8217;s another 4-byte TCP-packet:<\/p>\n<p style=\"text-align: center;\"><strong>Byte[]{0x01, 0x00, 0x0D, 0x00}<\/strong>.<\/p>\n<p>It is now necessary to connect to port 7002 at the remote console. This port is used to transmit graphics-data: a blinking cursor, for example. It is not necessary to send any data\u00a0via this connection, just the connection itself is needed in order for the login to proceed. A few more details on this can be found in the <a href=\"https:\/\/andyland.info\/wordpress\/?p=817\">original article<\/a>.<\/p>\n<p>If you need to send a password then this is done now. All the characters of the password are sent seperately within another stack of 4-byte TCP-packets. The structure of each packet is as follows:<\/p>\n<p style=\"text-align: center;\"><strong>Byte[]{0x01, 0x01, PWDCHAR, 0x00}<\/strong>.<\/p>\n<p>If. for example, your Password is &#8220;XXXXXX&#8221; then you will probably send six consecutive packets with data &#8220;0x01, 0x01, 0x58, 0x00&#8221; to the console to port 7001 (0x58 is the corresponding HEX-value for &#8216;X&#8217; based on the ASCII table). Sending\u00a0the password has to be finished with another remote &lt;Enter&gt;:<\/p>\n<p style=\"text-align: center;\"><strong>Byte[]{0x01, 0x00, 0x0D, 0x00}<\/strong>.<\/p>\n<p>And that&#8217;s basically all there is to it. Your login should now be complete. You can test this in two ways: You can\u00a0 send a simple command like &#8220;fader 1.1 at 100&#8221;. This will move the first fader of the first faderpage to 100 (full on). You can also check within the network-settings of the remote console if\u00a0the user is logged in. That&#8217;s &#8220;Tools | MA Network Configuration | Remotes&#8221;:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-3815\" src=\"https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/gmaLoggedin_02-1024x680.jpg\" alt=\"\" width=\"800\" height=\"531\" srcset=\"https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/gmaLoggedin_02-1024x680.jpg 1024w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/gmaLoggedin_02-300x199.jpg 300w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/gmaLoggedin_02-768x510.jpg 768w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/gmaLoggedin_02-226x150.jpg 226w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/gmaLoggedin_02-150x100.jpg 150w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/gmaLoggedin_02.jpg 1536w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>Here&#8217;s the complete code for the application. Core elements of it are quite thoroughly commented, I think. If you\u00a0are facing\u00a0trouble using the code\u00a0feel free to contact me and I will send you a compiled version of the program. If you are using these pieces of information within your own application please let me know &#8211; just because I&#8217;m curious and it would be a nice gesture.<\/p>\n<pre>package Application;\r\n\r\nimport java.awt.Color;\r\nimport java.awt.Container;\r\nimport java.awt.GridBagConstraints;\r\nimport java.awt.GridBagLayout;\r\nimport java.awt.Insets;\r\nimport java.awt.event.ActionEvent;\r\nimport java.awt.event.ActionListener;\r\nimport java.io.DataInputStream;\r\nimport java.io.DataOutputStream;\r\nimport java.net.Socket;\r\n\r\nimport javax.swing.JButton;\r\nimport javax.swing.JFrame;\r\nimport javax.swing.JLabel;\r\nimport javax.swing.JTextField;\r\n\r\n\r\n\r\n\/**\r\n\u00a0* GrandMA Version 1 remote command line interface.\r\n\u00a0* This is an example of how to implement a remote command line interface for GrandMA version 1 Lighting desks.\r\n\u00a0* \r\n\u00a0*\u00a0 \r\n\u00a0* There's probably enough room left for future improvement.\r\n\u00a0* Feel free to contact me if anything is left unclear.\r\n\u00a0* \r\n\u00a0* @author Andy Fischer; info@doktor-andy.de\r\n\u00a0*\r\n\u00a0*\/\r\npublic class Application {\r\n\u00a0\r\n\u00a0static JFrame frmMain;\r\n\u00a0static JLabel lblTarget;\r\n\u00a0static JLabel lblUsername;\r\n\u00a0static JLabel lblPassword;\r\n\u00a0static JTextField txtTarget;\r\n\u00a0static JTextField txtUsername;\r\n\u00a0static JTextField txtPassword;\r\n\u00a0static JTextField txtCMD;\r\n\u00a0static JButton btnLogin;\r\n\u00a0static JButton btnLogoff;\r\n\u00a0static JButton btnSend;\r\n\u00a0\r\n\u00a0static Socket sckHandshake = null;\u00a0\/\/Port 7003\r\n\u00a0static DataOutputStream osHandshake = null;\r\n\u00a0static DataInputStream isHandshake = null;\r\n\u00a0\r\n\u00a0static Socket sckCommand = null;\u00a0\/\/Port 7001\r\n\u00a0static DataOutputStream osCommand = null;\r\n\u00a0static DataInputStream isCommand = null;\r\n\u00a0\r\n\u00a0static Socket sckGFX = null;\u00a0\/\/Port 7002\r\n\u00a0\r\n\u00a0private static final byte[] CMD_HANDSHAKE = new byte[]{(byte)0xDC, 0x05,0x00,0x00};\r\n\u00a0private static final byte[] CMD_ENTER = new byte[]{0x01, 0x00, 0x0d, 0x00};\r\n\u00a0\r\n\u00a0public static void main(String[] args){\r\n\u00a0\u00a0initGUI();\r\n\u00a0\u00a0initLabels();\r\n\u00a0\u00a0initTextFields();\r\n\u00a0\u00a0initButtons();\r\n\u00a0\u00a0initListeners();\r\n\u00a0\u00a0placeElements();\r\n\u00a0\u00a0\u00a0\u00a0\r\n\u00a0\u00a0frmMain.setVisible( true );\r\n\u00a0}\r\n\r\n\u00a0\r\n\u00a0private static void initGUI(){\r\n\u00a0\u00a0frmMain = new JFrame(\"GrandMA Remote CMD\");\r\n\u00a0\u00a0frmMain.setSize(600, 250);\r\n\u00a0\u00a0frmMain.setLocationRelativeTo( null );\r\n\u00a0\u00a0frmMain.setResizable( false );\r\n\u00a0\u00a0frmMain.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\r\n\u00a0\u00a0frmMain.setLayout( new GridBagLayout() );\r\n\u00a0\u00a0initRaster_10x8(frmMain, false);\r\n\u00a0}\r\n\u00a0\r\n\u00a0private static void initLabels(){\r\n\u00a0\u00a0lblTarget = new JLabel(\"Target\");\r\n\u00a0\u00a0lblTarget.setPreferredSize(lblTarget.getSize());\u00a0\/\/This stops the element from automatically resizing because of its inner text.\r\n\u00a0\u00a0\r\n\u00a0\u00a0lblUsername = new JLabel(\"Username\");\r\n\u00a0\u00a0lblUsername.setPreferredSize(lblUsername.getSize());\r\n\u00a0\u00a0\r\n\u00a0\u00a0lblPassword = new JLabel(\"Password\");\r\n\u00a0\u00a0lblPassword.setPreferredSize(lblPassword.getSize());\r\n\u00a0}\r\n\u00a0\r\n\u00a0private static void initTextFields(){\r\n\u00a0\u00a0txtTarget = new JTextField();\r\n\u00a0\u00a0txtTarget.setPreferredSize(txtTarget.getSize());\r\n\u00a0\u00a0txtTarget.setText(\"192.168.1.23\");\r\n\u00a0\u00a0\r\n\u00a0\u00a0txtUsername = new JTextField();\r\n\u00a0\u00a0txtUsername.setPreferredSize(txtUsername.getSize());\r\n\u00a0\u00a0txtUsername.setText(\"andy\");\r\n\u00a0\u00a0\r\n\u00a0\u00a0txtPassword = new JTextField();\r\n\u00a0\u00a0txtPassword.setPreferredSize(txtPassword.getSize());\r\n\u00a0\u00a0\r\n\u00a0\u00a0txtCMD = new JTextField();\r\n\u00a0\u00a0txtCMD.setPreferredSize(txtCMD.getSize());\r\n\u00a0\u00a0txtCMD.setText(\"fader 1.1 at 100\");\r\n\u00a0}\r\n\u00a0\r\n\u00a0private static void initButtons(){\r\n\u00a0\u00a0btnLogin = new JButton(\"Login\");\r\n\u00a0\u00a0btnLogin.setPreferredSize(btnLogin.getSize());\r\n\u00a0\u00a0\r\n\u00a0\u00a0btnLogoff = new JButton(\"Logoff\");\r\n\u00a0\u00a0btnLogoff.setPreferredSize(btnLogoff.getSize());\r\n\u00a0\u00a0\r\n\u00a0\u00a0btnSend = new JButton(\"Send\");\r\n\u00a0\u00a0btnSend.setPreferredSize(btnSend.getSize());\r\n\u00a0}\r\n\u00a0\r\n\u00a0private static void initListeners(){\r\n\u00a0\u00a0\r\n\u00a0\u00a0btnLogin.addActionListener(new ActionListener() { \r\n\u00a0\u00a0\u00a0public void actionPerformed(ActionEvent e) {\u00a0\u00a0\u00a0\u00a0\r\n\u00a0\u00a0\u00a0\u00a0logon(txtTarget.getText(), txtUsername.getText(), txtPassword.getText());\r\n\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0});\r\n\u00a0\u00a0\r\n\u00a0\u00a0btnLogoff.addActionListener(new ActionListener() { \r\n\u00a0\u00a0\u00a0public void actionPerformed(ActionEvent e) {\u00a0\u00a0\u00a0\u00a0\r\n\u00a0\u00a0\u00a0\u00a0logoff();\r\n\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0});\r\n\u00a0\u00a0\r\n\u00a0\u00a0btnSend.addActionListener(new ActionListener() { \r\n\u00a0\u00a0\u00a0public void actionPerformed(ActionEvent e) {\u00a0\u00a0\u00a0\u00a0\r\n\u00a0\u00a0\u00a0\u00a0sendCommand( txtCMD.getText() );\r\n\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0});\r\n\u00a0}\r\n\u00a0\r\n\u00a0\/**\r\n\u00a0 * \r\n\u00a0 * @param pTargetAddress\r\n\u00a0 * @param pUsername\r\n\u00a0 * @param pPassword\r\n\u00a0 * @return\r\n\u00a0 *\/\r\n\u00a0private static boolean logon(String pTargetAddress, String pUsername, String pPassword) {\r\n\u00a0\u00a0\/\/ https:\/\/www.javaworld.com\/article\/2077322\/core-java\/core-java-sockets-programming-in-java-a-tutorial.html\r\n\u00a0\u00a0boolean bReturn = false;\r\n\u00a0\u00a0try {\r\n\u00a0\u00a0\u00a0sckHandshake = new Socket(pTargetAddress, 7003);\r\n\u00a0\u00a0\u00a0osHandshake = new DataOutputStream(sckHandshake.getOutputStream());\r\n\u00a0\u00a0\u00a0isHandshake = new DataInputStream(sckHandshake.getInputStream());\r\n\r\n\u00a0\u00a0\u00a0sckCommand = new Socket(pTargetAddress, 7001);\r\n\u00a0\u00a0\u00a0osCommand = new DataOutputStream(sckCommand.getOutputStream());\r\n\u00a0\u00a0\u00a0isCommand = new DataInputStream(sckCommand.getInputStream());\r\n\r\n\u00a0\u00a0\u00a0if (sckHandshake != null &amp;&amp; osHandshake != null\u00a0&amp;&amp; isHandshake != null) {\r\n\u00a0\u00a0\u00a0\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\/\/----A handshake is sent. I am not quite sure if it's okay to send ~something~.\r\n\u00a0\u00a0\u00a0\u00a0\/\/----Looking at my wireshark-log the handshake is different every time.\r\n\u00a0\u00a0\u00a0\u00a0\/\/----However, this is working stable for a few years on different computers now so I am\r\n\u00a0\u00a0\u00a0\u00a0\/\/----pretty confident.\r\n\u00a0\u00a0\u00a0\u00a0osHandshake.write(CMD_HANDSHAKE);\r\n\r\n\u00a0\u00a0\u00a0\u00a0\/\/----Creating a byte-Array containing the chars of the uername\r\n\u00a0\u00a0\u00a0\u00a0byte[] byUsername = new byte[pUsername.length()];\r\n\u00a0\u00a0\u00a0\u00a0for (int i = 0; i &lt; pUsername.length(); i++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0char c = pUsername.charAt(i);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0System.out.println(\"HEX:\" + String.format(\"%04x\", (int) c));\r\n\u00a0\u00a0\u00a0\u00a0\u00a0byUsername[i] = (byte) c;\r\n\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\/\/----First we send a packet with 4 bytes of data containing the length of the username which will \r\n\u00a0\u00a0\u00a0\u00a0\/\/----then be sent next. If you mess this up you can instantly crash(!) the console \r\n\u00a0\u00a0\u00a0\u00a0\/\/----via remote. Tested with onPC v6.808.\r\n\u00a0\u00a0\u00a0\u00a0osCommand.write(new byte[] { (byte) pUsername.length(), 0x00, 0x00, 0x00 });\r\n\u00a0\u00a0\u00a0\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\/\/----Sending the byte-array constructed we from the username\r\n\u00a0\u00a0\u00a0\u00a0osCommand.write(byUsername);\r\n\u00a0\u00a0\u00a0\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\/\/----Sending the ENTER-string\r\n\u00a0\u00a0\u00a0\u00a0osCommand.write(CMD_ENTER);\r\n\r\n\u00a0\u00a0\u00a0\u00a0\/\/----Now a connection to port 7002 has to be made. Nothing to send- just the connection\r\n\u00a0\u00a0\u00a0\u00a0\/\/----Otherwise the login will hang. One thing his connection transmits is the blinking cursor from the console.\r\n\u00a0\u00a0\u00a0\u00a0sckGFX = new Socket(pTargetAddress, 7002);\r\n\u00a0\u00a0\u00a0\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\/\/---We are sending the chars of the password. Every character is encapsulated into \r\n\u00a0\u00a0\u00a0\u00a0\/\/---a single network packet containing 4 bytes of data: 0x01, 0x01, %CHAR%, 0x00\r\n\u00a0\u00a0\u00a0\u00a0for (int i = 0; i &lt; pPassword.length(); i++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0char c = pPassword.charAt(i);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0osCommand.write(new byte[] { 0x01, 0x01, (byte) c, 0x00 });\r\n\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\/\/----Sending ENTER\r\n\u00a0\u00a0\u00a0\u00a0osCommand.write(CMD_ENTER);\r\n\u00a0\u00a0\u00a0\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\/\/---This is not reliable. It only shows that the logon()-method finished successfully.\r\n\u00a0\u00a0\u00a0\u00a0\/\/---There could be issues on the GrandMA-side of the connection leading to you not being logged in.\r\n\u00a0\u00a0\u00a0\u00a0\/\/---However, you are probably logged in by now and have at least some basic example where you can \r\n\u00a0\u00a0\u00a0\u00a0\/\/---continue from.\r\n\u00a0\u00a0\u00a0\u00a0bReturn = true;\r\n\u00a0\u00a0\u00a0\u00a0System.out.println(\"logon(): Connected\");\r\n\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0} catch (Exception e) {\r\n\u00a0\u00a0\u00a0System.out.println(\"logon() Exception:\" + e.getMessage());\r\n\u00a0\u00a0}\r\n\u00a0\u00a0return bReturn;\r\n\u00a0}\r\n\u00a0\r\n\u00a0\/**\r\n\u00a0 * Close everything\r\n\u00a0 *\/\r\n\u00a0private static void logoff(){\r\n\u00a0\u00a0try{\r\n\u00a0\u00a0\u00a0osHandshake.close();\r\n\u00a0\u00a0\u00a0isHandshake.close();\r\n\u00a0\u00a0\u00a0sckHandshake.close();\r\n\u00a0\u00a0\u00a0\r\n\u00a0\u00a0\u00a0osCommand.close();\r\n\u00a0\u00a0\u00a0isCommand.close();\r\n\u00a0\u00a0\u00a0sckCommand.close();\r\n\u00a0\u00a0\u00a0\r\n\u00a0\u00a0\u00a0sckGFX.close();\u00a0\u00a0\u00a0\r\n\u00a0\u00a0}catch(Exception e){\r\n\u00a0\u00a0\u00a0System.out.println(\"logoff() Exception:\" + e.getMessage());\r\n\u00a0\u00a0}\r\n\u00a0}\r\n\u00a0\r\n\u00a0\/**\r\n\u00a0 * Sending the command to the remote console.\r\n\u00a0 * Every character is encapsulated into a packet with a length of 4 bytes of data. \u00a0\r\n\u00a0 * @param pCommand\r\n\u00a0 *\/\r\n\u00a0private static void sendCommand( String pCommand ){\r\n\u00a0\u00a0try{\r\n\u00a0\u00a0\u00a0for(int j=0; j&lt;pCommand.length(); j++){\r\n\u00a0\u00a0\u00a0\u00a0char c = pCommand.charAt(j);\r\n\u00a0\u00a0\u00a0\u00a0\/\/System.out.println(\"HEX:\" + String.format(\"%04x\", (int)c) );\r\n\u00a0\u00a0\u00a0\u00a0osCommand.write(new byte[]{0x01, 0x01, (byte)c, 0x00});\r\n\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0osCommand.write( CMD_ENTER );\r\n\u00a0\u00a0}catch(Exception e){\r\n\u00a0\u00a0\u00a0System.out.println(\"sendCommand() Exception:\" + e.getMessage());\r\n\u00a0\u00a0}\r\n\u00a0}\r\n\u00a0\r\n\u00a0private static void placeElements(){\r\n\u00a0\u00a0GridBagConstraints gbc = new GridBagConstraints();\r\n\u00a0\u00a0\r\n\u00a0\u00a0gbc.ipady = 0;\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/make this component tall\r\n\u00a0\u00a0gbc.fill = GridBagConstraints.BOTH;\r\n\u00a0\u00a0gbc.insets = new Insets(0, 10, 0, 10);\r\n\u00a0\u00a0gbc.gridwidth = 2; gbc.gridheight = 1;\r\n\u00a0\u00a0\r\n\u00a0\u00a0gbc.gridx = 0; gbc.gridy = 0;\r\n\u00a0\u00a0frmMain.add(lblTarget, gbc);\r\n\u00a0\u00a0\r\n\u00a0\u00a0gbc.gridx = 2; gbc.gridy = 0;\r\n\u00a0\u00a0frmMain.add(txtTarget, gbc);\r\n\u00a0\u00a0\r\n\u00a0\u00a0gbc.gridx = 0; gbc.gridy = 1;\r\n\u00a0\u00a0frmMain.add(lblUsername, gbc);\r\n\u00a0\u00a0\r\n\u00a0\u00a0gbc.gridx = 2; gbc.gridy = 1;\r\n\u00a0\u00a0frmMain.add(txtUsername, gbc);\r\n\u00a0\u00a0\r\n\u00a0\u00a0gbc.gridx = 0; gbc.gridy = 2;\r\n\u00a0\u00a0frmMain.add(lblPassword, gbc);\r\n\u00a0\u00a0\r\n\u00a0\u00a0gbc.gridx = 2; gbc.gridy = 2;\r\n\u00a0\u00a0frmMain.add(txtPassword, gbc);\r\n\u00a0\u00a0\r\n\u00a0\u00a0gbc.gridx = 0; gbc.gridy = 3;\r\n\u00a0\u00a0frmMain.add(btnLogin, gbc);\r\n\u00a0\u00a0\r\n\u00a0\u00a0gbc.gridx = 2; gbc.gridy = 3;\r\n\u00a0\u00a0frmMain.add(btnLogoff, gbc);\r\n\u00a0\u00a0\r\n\u00a0\u00a0gbc.gridwidth = 8;\r\n\u00a0\u00a0gbc.gridx = 0; gbc.gridy = 6;\r\n\u00a0\u00a0frmMain.add(txtCMD, gbc);\r\n\u00a0\u00a0\r\n\u00a0\u00a0gbc.gridwidth = 2;\r\n\u00a0\u00a0gbc.gridx = 8; gbc.gridy = 6;\r\n\u00a0\u00a0frmMain.add(btnSend, gbc);\r\n\u00a0}\r\n\u00a0\r\n\u00a0\/**\r\n\u00a0 * This is probably looking weird but I figured out this creates a sophisticating solution to \r\n\u00a0 * manually adding elements to a container. Even if you only add parts of the elements everything\r\n\u00a0 * looks like it's supposed to. Furthermore you still have the benefits of a GridbagLayout. \r\n\u00a0 * @param pContainer\r\n\u00a0 * @param pRasterVisible\r\n\u00a0 *\/\r\n\u00a0private static void initRaster_10x8(Container pContainer, boolean pRasterVisible){\r\n\u00a0\u00a0\r\n\u00a0\u00a0JLabel[] lblBlanko = new JLabel[18];\r\n\u00a0\u00a0for(int i=0; i&lt;lblBlanko.length; i++){\r\n\u00a0\u00a0\u00a0lblBlanko[i] = new JLabel(\"\");\r\n\u00a0\u00a0\u00a0lblBlanko[i].setBackground( Color.GREEN );\u00a0\u00a0\r\n\u00a0\u00a0\u00a0lblBlanko[i].setOpaque( pRasterVisible );\r\n\u00a0\u00a0\u00a0lblBlanko[i].setPreferredSize(lblBlanko[i].getSize());\r\n\u00a0\u00a0}\r\n\u00a0\u00a0\r\n\u00a0\u00a0\/\/Die BlankoLabels platzieren\r\n\u00a0\u00a0GridBagConstraints gbcBlanko = new GridBagConstraints();\r\n\u00a0\u00a0gbcBlanko.insets = new Insets(01, 01, 01, 01);\r\n\u00a0\u00a0gbcBlanko.weightx = 1.0;\r\n\u00a0\u00a0gbcBlanko.weighty = 1.0;\r\n\u00a0\u00a0\r\n\u00a0\u00a0\/\/----Horizontal\r\n\u00a0\u00a0for(int i=0; i&lt;10; i++){\r\n\u00a0\u00a0\u00a0gbcBlanko.gridy=0;\r\n\u00a0\u00a0\u00a0gbcBlanko.gridx=i; \r\n\u00a0\u00a0\u00a0gbcBlanko.fill = GridBagConstraints.BOTH;\r\n\u00a0\u00a0\u00a0pContainer.add(lblBlanko[i], gbcBlanko);\r\n\u00a0\u00a0}\r\n\u00a0\u00a0\r\n\u00a0\u00a0\/\/----Vertikal\r\n\u00a0\u00a0for(int i=1; i&lt;9; i++){\r\n\u00a0\u00a0\u00a0gbcBlanko.gridx=0;\r\n\u00a0\u00a0\u00a0gbcBlanko.gridy=i; \r\n\u00a0\u00a0\u00a0pContainer.add(lblBlanko[i+9], gbcBlanko);\r\n\u00a0\u00a0}\r\n\u00a0}\r\n}\/\/Class\r\n\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Due to &#8230; massive &#8230; request I took the chance and did a major overhaul of my post about the GrandMA remote command line from\u00a0a few years ago. Back in the days I pulled together a quick &#8216;n dirty version which somehow worked and caught some interest but never really \u2026 <a class=\"continue-reading-link\" href=\"https:\/\/andyland.info\/wordpress\/grandma-version-1-command-line-interface-java\/\"> Continue reading <span class=\"meta-nav\">&rarr; <\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_crdt_document":"","footnotes":"","_links_to":"","_links_to_target":""},"categories":[148,20,5,24],"tags":[126,50,122,293,58,294],"class_list":["post-3737","post","type-post","status-publish","format-standard","hentry","category-grandma-onpc","category-licht","category-projekte","category-raspberry-pi","tag-dmx","tag-grandma-onpc","tag-java","tag-ma-lighting","tag-raspberry-pi-2","tag-remote-cmd","odd"],"_links":{"self":[{"href":"https:\/\/andyland.info\/wordpress\/wp-json\/wp\/v2\/posts\/3737","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/andyland.info\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/andyland.info\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/andyland.info\/wordpress\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/andyland.info\/wordpress\/wp-json\/wp\/v2\/comments?post=3737"}],"version-history":[{"count":0,"href":"https:\/\/andyland.info\/wordpress\/wp-json\/wp\/v2\/posts\/3737\/revisions"}],"wp:attachment":[{"href":"https:\/\/andyland.info\/wordpress\/wp-json\/wp\/v2\/media?parent=3737"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/andyland.info\/wordpress\/wp-json\/wp\/v2\/categories?post=3737"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/andyland.info\/wordpress\/wp-json\/wp\/v2\/tags?post=3737"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}