Knowledge Base

If you experience issues with your Dolby IMS2000 Cinema Server connecting to or executing commands on the JNIOR you may need to follow one of the procedures below. There are two issues that I have seen.

Dolby has resolved the issue for the IMS3000.

A Dolby may not even try to connect to a JNIOR when it should.

Make sure you have rebooted the Dolby IMS after adding the JNIOR as a device.

To determine if this is the issue:

  1. Go to the DCP for the JNIOR
  2. Click on the Console tab
  3. Click ‘Start Session’
  4. Log in when prompted
  5. enter the netstat command
  6. There should be a connection to 9200 with the Dolby Server IP Address for the Remote IP

The Dolby is connected but nothing happens on the JNIOR when the Dolby sends the command.

When Dolby tries to connect to the JNIOR it may not be able to log in to the JNIOR due to a bad password. The highlighted bytes below are corrupted.

The bytes should represent the password like this…

The previous images are screenshots for the WireShark application when analyzing a network capture. This capture is included in a JNIOR Support Tool Snapshot. I have been told by Dolby that this is likely due to the browser auto-fill.

To determine if this is the issue you should check the protocol.log on the JNIOR

To do that:

  1. Go to the DCP for the JNIOR
  2. Click on the Console tab
  3. Click ‘Start Session’
  4. Log in when prompted
  5. Enter the cat protocol.log command

If you see the failed login then do the following:

  1. Go to the JNIOR Device Configuration on the Dolby IMS
  2. Remove the JNIOR device
  3. Reboot the Dolby IMS
  4. Add the JNIOR as a device but DO NOT enter the password
  5. Reboot the Dolby IMS

You can now re-test the JNIOR connection to see if this is resolved.

If it is not resolved then make sure your browser is not using auto-fill for the site. The procedure differs per browser. Follow one of the links below for your browser.

If it is not resolved you can try to analyze the network capture or contact INTEG for additional support.

I hope this article helped you out. Please contact us to let us know!

The following information describes how to use the JNIOR to capture data on the Ethernet network and the JNIOR Serial ports. Capturing the data can be a great troubleshooting technique for communicating with various devices.

The Netstat command on the JNIOR Series 4 has multiple functions and some of them are very helpful when trying to troubleshoot a network connection via the Ethernet network.

The standard Netstat command will list all the TCP ports that are listening on the JNIOR and indicate any connections made.

Typing help netstat will show all the different options you can use with netstat.

The -C, -R, and -F options are great for getting, resetting, and filtering a file of the packets being sent on the TCP ports. This file can opened in wireshark so you can better view the information being sent back and forth on each port.

There is a chance you will see the following PHP error. For this to be seen you must have a Series 4 JNIOR that had JANOS version 1.6 or older and you just updated to a new JANOS version

Why did this happen?

The older JNIORs had a different web page than we have now. The main web page used to be served out of the flash/www folder. Now the DCP, the newer web page, is served out of the flash/www.zip file. If the flash/www/index.php file is still found then the web server will process it and serve it instead of the index.php in the www.zip file.

How do we fix it?

To fix this we need to remove the old flash/www/index.php file. This will allow JANOS to look in the flash/www.zip file. This should be a step in the All-In-One update project. You can do it manually with a Telnet connection like this…

This post explains how to send and receive messages from a System Message Pump. There is a previous post showing how to create a System Message Pump that you can access here. Please look over that post first as this one uses code from and references that post. 

After creating a Message Pump program from the previous System Message Pump sample, the next step is to create and receive messages between two different programs. To start, we can create a new program that will interact with the previous System Message Pump program. We’ll call this program our PostMessage program. This sample program only has two messages it can send, but could be edited to add more. These messages will also only pulse output relays but can be edited to do a lot of other actions as well.

import com.integpg.system.JANOS;
import com.integpg.system.SystemMsg;
import com.integpg.system.MessagePump;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Json;

public class CommunicationPost2 {

    //This funciton decides from the user input it gets whether or not the program should post another message or close the program.
    //The only message you can create is message 1300
    public static int getresponse(MessagePump closesPump) throws IOException {

        String type = "";
        BufferedReader reading = new BufferedReader(new InputStreamReader(System.in));
        int testtype = 0;
        boolean running = true;
        boolean typecheck = false;

        while (running == true) {

            System.out.println("\nPlease enter a System Pump command. Type 'create' to make a command. Type 'finished' when done.\n");
            String choice = reading.readLine();
            System.out.println("|" + choice + "|");

            switch (choice) {

                case "finished":
                    System.out.println("Finishing program...\n");
                    System.out.println();
                    closesPump.close();
                    System.out.println();
                    System.exit(0);

                case "create":
                    System.out.println();
                    System.out.println("\nPlease enter the type number\n");
                    System.out.println();
                    while (typecheck == false) {
                            type = reading.readLine();
                            try{
                            testtype = Integer.parseInt(type);
                            typecheck = true;
                            }
                            catch (NumberFormatException e) {
                                System.out.println("\nIncorrect input. Try again\n");
                            }
                    }
                    return testtype;

                default:
                    System.out.println("\nBad input. Try again.\n");
            }
        }
        return testtype;
    }

    //adds a value to the Json object that we be sent as part of the message to the other program
    public static Json addToJson(Json gettingJson, String value, Object jsonvalue) {

        gettingJson.put(value, jsonvalue);
        return gettingJson;

    }

    //creates the message that will be sent to the other program to complete
    public static SystemMsg createMessage(int messagetype, byte[] messageinfo) {

        SystemMsg newMsg = new SystemMsg();
        newMsg.type = messagetype;
        newMsg.msg = messageinfo;
        System.out.println(newMsg);
        return newMsg;

    }

    //completes the message 1301 if it was sent from the other program
    public static void completeMessage1301(String recievedJsonInfo) {

        Json holdinginfo = new Json(recievedJsonInfo);
        String[] arrayofkeys = holdinginfo.keyarray();
        int togglecheck = holdinginfo.getInt(arrayofkeys[0]);
        int channelscheck = holdinginfo.getInt(arrayofkeys[1]);
        int durationcheck = holdinginfo.getInt(arrayofkeys[2]);
        System.out.println(arrayofkeys[0] + ": " + togglecheck + ", " + arrayofkeys[1] + ": " + channelscheck + ", " + arrayofkeys[2] + ": " + durationcheck);
        try {
            JANOS.setOutputPulsed(togglecheck, channelscheck, durationcheck);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    public static Json getInfoToJson() throws IOException {

        Json newJson = new Json();
        String tempduration = "";
        String tempchannels = "";
        String temp_O_or_C = "";
        boolean successcheck1 = false;
        boolean successcheck2 = false;
        boolean successcheck3 = false;
        BufferedReader reading = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("\nThis is for toggling pulse outputs.\n");
        System.out.println("\nPlease enter if which channels you are pulsing: ' 1 - 255'\n");

        while (successcheck1 == false) {
            try {
                temp_O_or_C = reading.readLine();
                successcheck1 = true;
            } catch (IOException | NumberFormatException e) {
                System.out.println("\nIncorrect format, try again.\n");
            }
            if (successcheck1 == true) {
                int O_or_C = Integer.parseInt(temp_O_or_C);
                newJson = addToJson(newJson, "Pulse", O_or_C);
            }
        }

        System.out.println("\nPlease enter what channels: '1 - 255'\n");
        while (successcheck2 == false) {
            try {
                tempchannels = reading.readLine();
                successcheck2 = true;
            } catch (IOException | NumberFormatException e) {
                System.out.println("\nIncorrect format, try again.\n");

            }
            if (successcheck2 == true) {
                int channels = Integer.parseInt(tempchannels);
                newJson = addToJson(newJson, "Channels", channels);
            }
        }

        System.out.println("\nPlease enter duration in milliseconds:\n");
        while (successcheck3 == false) {
            try {
                tempduration = reading.readLine();
                successcheck3 = true;
            } catch (IOException | NumberFormatException e) {
                System.out.println("\nIncorrect format, try again.\n");
            }
            if (successcheck3 == true) {
                int duration = Integer.parseInt(tempduration);
                newJson = addToJson(newJson, "Duration", duration);
            }
        }

        return newJson;

    }

    public static void main(String[] args) throws IOException, InterruptedException {

        MessagePump MESSAGE_PUMP = new MessagePump();

        MESSAGE_PUMP.open();

        while (true) {

            boolean checkmsg = false;

            int fixedtype = getresponse(MESSAGE_PUMP);

            if (fixedtype == 1300) {

                Json firstJson = getInfoToJson();
                SystemMsg systemMsg = createMessage(fixedtype, firstJson.toString().getBytes());
                MESSAGE_PUMP.postMessage(systemMsg);
                System.out.println();
            }

            while (checkmsg == false) {

                SystemMsg msgRecieved = MESSAGE_PUMP.getMessage(1301);

                if (msgRecieved.type == 1301) {
                    System.out.println("Message 1301 recieved, pulsing output 1.");
                    String jsoninfo2 = new String(msgRecieved.msg);
                    completeMessage1301(jsoninfo2);
                    checkmsg = true;
                }

            }

        }

    }

}

This PostMessage program will create a message that, after getting inputs from the user, will be sent to the previously created System Message Pump program. The program starts by asking the user if they wish to create a message or close the program, then calling the “getresponse” function to initiate whichever the user chooses.

    public static int getresponse(MessagePump closesPump) throws IOException {

        String type = "";
        BufferedReader reading = new BufferedReader(new InputStreamReader(System.in));
        int testtype = 0;
        boolean running = true;
        boolean typecheck = false;

        while (running == true) {

            System.out.println("\nPlease enter a System Pump command. Type 'create' to make a command. Type 'finished' when done.\n");
            String choice = reading.readLine();
            System.out.println("|" + choice + "|");

            switch (choice) {

                case "finished":
                    System.out.println("Finishing program...\n");
                    System.out.println();
                    closesPump.close();
                    System.out.println();
                    System.exit(0);

                case "create":
                    System.out.println();
                    System.out.println("\nPlease enter the type number\n");
                    System.out.println();
                    while (typecheck == false) {
                            type = reading.readLine();
                            try{
                            testtype = Integer.parseInt(type);
                            typecheck = true;
                            }
                            catch (NumberFormatException e) {
                                System.out.println("\nIncorrect input. Try again\n");
                            }
                    }
                    return testtype;

                default:
                    System.out.println("\nBad input. Try again.\n");
            }
        }
        return testtype;
    }

If the user decides to create a message, it will call a function called “getInfoToJson”. This will gather information to form a Json object. The first two values are from 1 – 255 to represent the 8 outputs in bit values, and the last value is time in milliseconds. The “getInfoToJson” function will call the “addToJson” function to create the Json object “getInfoToJson” will return. 

public static Json getInfoToJson() throws IOException {

        Json newJson = new Json();
        String tempduration = "";
        String tempchannels = "";
        String temp_O_or_C = "";
        boolean successcheck1 = false;
        boolean successcheck2 = false;
        boolean successcheck3 = false;
        BufferedReader reading = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("\nThis is for toggling pulse outputs.\n");
        System.out.println("\nPlease enter if which channels you are pulsing: ' 1 - 255'\n");

        while (successcheck1 == false) {
            try {
                temp_O_or_C = reading.readLine();
                successcheck1 = true;
            } catch (IOException | NumberFormatException e) {
                System.out.println("\nIncorrect format, try again.\n");
            }
            if (successcheck1 == true) {
                int O_or_C = Integer.parseInt(temp_O_or_C);
                newJson = addToJson(newJson, "Pulse", O_or_C);
            }
        }

        System.out.println("\nPlease enter what channels: '1 - 255'\n");
        while (successcheck2 == false) {
            try {
                tempchannels = reading.readLine();
                successcheck2 = true;
            } catch (IOException | NumberFormatException e) {
                System.out.println("\nIncorrect format, try again.\n");

            }
            if (successcheck2 == true) {
                int channels = Integer.parseInt(tempchannels);
                newJson = addToJson(newJson, "Channels", channels);
            }
        }

        System.out.println("\nPlease enter duration in milliseconds:\n");
        while (successcheck3 == false) {
            try {
                tempduration = reading.readLine();
                successcheck3 = true;
            } catch (IOException | NumberFormatException e) {
                System.out.println("\nIncorrect format, try again.\n");
            }
            if (successcheck3 == true) {
                int duration = Integer.parseInt(tempduration);
                newJson = addToJson(newJson, "Duration", duration);
            }
        }

        return newJson;

    }

Then it will call the “createMessage” function to get the ID and information of the message that we are going to send.

public static SystemMsg createMessage(int messagetype, byte[] messageinfo) {

        SystemMsg newMsg = new SystemMsg();
        newMsg.type = messagetype;
        newMsg.msg = messageinfo;
        System.out.println(newMsg);
        return newMsg;

    }

After this message is sent, it will be received from an edited version of the System Message Pump program and then send a certain message. The code for the edited Engine Pump file of the System Message Pump program is below.

import com.integpg.system.JANOS;
import com.integpg.system.MessagePump;
import com.integpg.system.SystemMsg;
import java.io.IOException;
import java.util.Json;
import java.util.Vector;

public class MessagePumpEngine implements Runnable {

    private static final MessagePumpEngine THIS = new MessagePumpEngine();
    private static final MessagePump MESSAGE_PUMP = new MessagePump();
    private static final Vector LISTENERS = new Vector<>();

    private static Thread _thread;

    /**
     * Singleton constructor
     */
    private MessagePumpEngine() {
    }

    /**
     * adds a listener that will be alerted of all received messages. The
     * listener will be responsible for determining if the message has meaning
     * to them
     *
     * @param listener
     */
    public static void addListener(MessagePumpListener listener) {
        synchronized (LISTENERS) {
            LISTENERS.addElement(listener);
        }
    }

    /**
     * starts our message pump engine.
     */
    static void start() {
        if (null == _thread) {
            _thread = new Thread(THIS);
            _thread.setName("message-pump-engine");
            _thread.setDaemon(true);
            _thread.start();
        }
    }
    
    //completes the message 1300 after being sent from the other application
    
    public static void completeMessage1300 (String recievedJsonInfo) {
        
        Json holdinginfo = new Json(recievedJsonInfo);
        String[] arrayofkeys = holdinginfo.keyarray();
        int togglecheck = holdinginfo.getInt(arrayofkeys[0]);
        int channelscheck = holdinginfo.getInt(arrayofkeys[1]);
        int durationcheck = holdinginfo.getInt(arrayofkeys[2]);
        System.out.println(arrayofkeys[0] + ": " + togglecheck + ", " + arrayofkeys[1] + ": " + channelscheck + ", " + arrayofkeys[2] + ": " + durationcheck);
        try {
            JANOS.setOutputPulsed(togglecheck, channelscheck, durationcheck);
        } 
        catch (IOException ex) {
            ex.printStackTrace();
        }
    }
    
    //creates the message that will be sent to the other program to complete
    
    public static SystemMsg createMessage (int messageType, byte [] messageInfo) {
        
        SystemMsg returnMsg = new SystemMsg();
        returnMsg.type = messageType;
        returnMsg.msg = messageInfo;
        
        System.out.println(returnMsg);
        return returnMsg;
        
    }
    
    //adds a value to the Json object that we be sent as part of the message to the other program
    
    public static Json addToJson(Json Jsonadd, String valuename, Object jsonvalue) {
    
        Jsonadd.put(valuename, jsonvalue);
        return Jsonadd;
        
    }
    
    @Override
    public void run() {
        System.out.println("open the message pump and start monitoring.\r\n");
        MESSAGE_PUMP.open();

        for (;;) {
            // read all messages from the message pump
            SystemMsg systemMsg = MESSAGE_PUMP.getMessage();

            // we must repost as fast as we can
            MESSAGE_PUMP.postMessage(systemMsg);

            // notify all of our listeners

            if (systemMsg.type == 1300) {

                System.out.println();
                System.out.println("Recieved command 1300, pulsing output. Sending another command.\n");
                String jsoninfo = new String(systemMsg.msg);
                completeMessage1300(jsoninfo);
                Json thirdjson = new Json();
                addToJson(thirdjson, "setclose", 1); 
                addToJson(thirdjson, "settingchannel", 1);
                addToJson(thirdjson, "timeset", 5000);
                SystemMsg returnMsg = createMessage(1301, thirdjson.toString().getBytes());
                MESSAGE_PUMP.postMessage(returnMsg);                

            }

        }

    }

    /**
     * Exposes the postMesssage method of the System MessagePump
     *
     * @param msg the message the will be sent to the system message pump
     */
    public static void postMessage(SystemMsg msg) {
        MESSAGE_PUMP.postMessage(msg);
    }

}

When this file of code replaces the engine pump file in the System Message Pump program, there is a loop that will be listening for the message sent from our previous program with the message type of 1300. After it receives that message, it will create a string from all of the message’s info, and then call the “completeMessage1301” function to pulse the correct outputs. Once the program does that, it will then use the “createMessage” function to create another message for the PostMessage program to read. There is another loop for the listeners to read that message when its sent as well.

public void run() {
        System.out.println("open the message pump and start monitoring.\r\n");
        MESSAGE_PUMP.open();

        for (;;) {
            // read all messages from the message pump
            SystemMsg systemMsg = MESSAGE_PUMP.getMessage();

            // we must repost as fast as we can
            MESSAGE_PUMP.postMessage(systemMsg);
            

            if (systemMsg.type == 1300) {

                System.out.println();
                System.out.println("Recieved command 1300, pulsing output. Sending another command.\n");
                String jsoninfo = new String(systemMsg.msg);
                completeMessage1300(jsoninfo);
                Json thirdjson = new Json();
                addToJson(thirdjson, "setclose", 1); 
                addToJson(thirdjson, "settingchannel", 1);
                addToJson(thirdjson, "timeset", 5000);
                SystemMsg returnMsg = createMessage(1301, thirdjson.toString().getBytes());
                MESSAGE_PUMP.postMessage(returnMsg);

                    }
                }

            }

        }

    }

This will have us go back to the PostMessage program to receive the message the Engine Pump file sent back. The PostMessage program calls the completeMessage1301 function to grab the values from the message and pulse the correct outputs. After that, the program will loop back to the beginning and re-prompt the user if they want to create or finish the program, and repeat this process until the user decides to finish the program.

 while (checkmsg == false) {

                SystemMsg msgRecieved = MESSAGE_PUMP.getMessage(1301);

                if (msgRecieved.type == 1301) {
                    System.out.println("Message 1301 recieved, pulsing output 1.");
                    String jsoninfo2 = new String(msgRecieved.msg);
                    completeMessage1301(jsoninfo2);
                    checkmsg = true;
                }

            }

The JNIOR Support Tool will allow foreign characters in the filenames for macro and device files. Here is a quick video showing how to delete them.

1. Create a console session.
2. login
3. type rm mac
4. Then press tab

The JNIOR will fill in the name of the macro file.  In your case, press tab until you get the file with the offending name.

5. Then use the left arrow key to go back and insert a quote at the beginning of the file name

6. Press enter

You can then go back to the folders tab to see that the file is gone.

Repeat the same steps for the “macro file not found.log” file.

If the JNIOR series 4, models 410, 412 or 414, is not working with the GDC then check to make sure MODBUS is enabled. The built-in library for the JNIOR on the GDC server uses MODBUS.

The series 3 enabled the MODBUS server by default. The MODBUS server is an optional application that needs to be enabled on the series 4. You can look at the following links for more information

The iBoot device from dataprobe is a Web Enabled Power Switch. We can command the devices by issuing HTTP Requests. To do this we will add an iBoot device to the Devices file and add “Power On” and “Power Off” actions to the Macro file. We then associate the actions with macros. We might want separate macros for power on, power off and power cycle.

To create the iBoot device in the devices file go to the Devices tab in the JNIOR Support Tool. Then in the lower left click ‘Add’. Then fill out the information as follows. This device can be added to an existing devices file or a new one. We are going to use the HTTP Request device type.

Then click over to the Macro tab and click on Link Devices. This will link the devices file so that the configured devices are available to create macro actions.

Now click ‘Add’ below the Macro Action View. You can rename the action. Click in the Device column for the new Action and scroll to the bottom to select your linked device. Select the iBoot device. Now click on Action and select “GET”. In the data field enter the resource portion of the URL””. It should look like this… Remember to enter the username and password if needed.

You can now add the action to any macro by selecting both the macro you wish to add it to and the new iBoot action.s Then use the <- button between the two views like this…

He is an example that will make a HttpGet request and logging the response. The response text is printed to the screen and captured to a file. An human readable format of the bytes are also logged to a file

package httprequesttest;

import com.integpg.system.JANOS;
import java.io.FileOutputStream;
import java.io.IOException;

public class HttpRequestTest {

    public static void main(String[] args) {
        System.out.println("args.length: " + args.length);

        if (0 < args.length) {
            try {
                //
                // get the server hostname from the command line arguments
                String hostname = args[0];
                String serviceName = args[1];
                String paramsString = "";
                if (3 == args.length) paramsString = args[2];

                //
                // define our get job status request
                String urlString = String.format("http://%s/%s?%s", hostname, serviceName, paramsString);
                System.out.println("Testing call to " + urlString);

                //
                // define our http object and perform the request
                HttpURL url = new HttpURL(urlString);
                HttpRequest httpRequest = new HttpRequest(url);
                HttpResponse httpResponse = httpRequest.sendRequest();
                httpResponse.processResponse();
                String response = httpResponse.getData();

                //
                // print the response and toss the response in a file
                System.out.println("httpResponse: " + response);
                writeAllBytes("response.txt", response.getBytes());
                writeAllBytes("response.bin", hexDump(response.getBytes()).getBytes());
            } catch (IOException ex) {
                ex.printStackTrace();
            }

        } else {
            System.out.println("a url must be supplied");
        }
    }


/**
 * HELPER FUNCTIONS
 */
    public static void writeAllBytes(String filename, byte[] bytes) throws IOException {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(filename);
            fos.write(bytes);
            fos.flush();
        } catch (Exception ex) {
            JANOS.syslog("writeAllBytes to " + filename + " threw " + ex.getMessage(), JANOS.SYSLOG_ERROR);
        } finally {
            try {
                if (null != fos) fos.close();
            } catch (IOException ex1) {
                ex1.printStackTrace();
            }
        }
    }

    public static final char[] LowerHexArray = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };



    public static String hexDump(byte[] bytes) {
        return hexDump(bytes, 0, bytes.length);
    }



    public static String hexDump(byte[] bytes, int offset, int length) {
        StringBuilder sb = new StringBuilder((length / 16 + 1) * 83);

        char[] line = new char[81];
        line[0] = '0';
        line[1] = 'x';
        line[line.length - 2] = '\r';
        line[line.length - 1] = '\n';

        int endAddress = offset + length;
        for (int lineAddress = offset - (offset % 16); lineAddress < endAddress; lineAddress += 16) {
            int charPos = 2;
            line[charPos++] = LowerHexArray[(lineAddress >> 12 & 0x0F)];
            line[charPos++] = LowerHexArray[(lineAddress >> 8 & 0x0F)];
            line[charPos++] = LowerHexArray[(lineAddress >> 4 & 0x0F)];
            line[charPos++] = LowerHexArray[lineAddress & 0x0F];
            line[charPos++] = ' ';
            line[charPos++] = ' ';
            line[charPos++] = ' ';

            for (int linePos = 0; linePos < 16; linePos++) {
                int address = lineAddress + linePos;
                if (address < offset) {
                    line[charPos++] = ' ';
                    line[charPos++] = ' ';
                    line[charPos++] = ' ';
                } else if (address < endAddress) {
                    int c = bytes[address];
                    line[charPos++] = LowerHexArray[(c >> 4 & 0x0F)];
                    line[charPos++] = LowerHexArray[c & 0x0F];
                    line[charPos++] = ' ';
                } else {
                    line[charPos++] = ' ';
                    line[charPos++] = ' ';
                    line[charPos++] = ' ';
                }

                if (7 == linePos) {
                    // add 2 spaces between each group of 8 bytes
                    line[charPos++] = ' ';
                    line[charPos++] = ' ';
                }
            }
            // add 3 spaces between the second group of 8 bytes and the ASCII output
            line[charPos++] = ' ';
            line[charPos++] = ' ';
            line[charPos++] = ' ';

            for (int linePos = 0; linePos < 16; linePos++) {
                int address = lineAddress + linePos;

                if (address < offset) line[charPos++] = ' ';
                else if (address < endAddress) {
                    char c = (char) bytes[address];
                    if (0x20 > c || 0x80 <= c) line[charPos++] = '.';
                    else line[charPos++] = c;
                } else line[charPos++] = ' ';
                if (7 == linePos) line[charPos++] = ' ';
            }

            sb.append(line, 0, line.length);
        }

        return sb.toString();
    }
}

HttpRequest.java

package httprequesttest;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.Hashtable;

public class HttpRequest {

    private final HttpURL _url;
    private Socket _socket;
    private String _type = "GET";
    private byte[] _data;
    private final Hashtable<String, String> _userHeaders = new Hashtable<String, String>();



    public HttpRequest(HttpURL url) {
        _url = url;
    }



    public String getRequestMethod() {
        return _type;
    }



    public void setRequestMethod(String type) {
        _type = type;
    }



    public void addHeader(String name, String value) {
        _userHeaders.put(name, value);
    }



    public void setData(byte[] data) {
        _data = data;
    }



    public HttpResponse sendRequest() throws IOException {
        InetAddress resolvedAddress = InetAddress.getByName(_url.getHost());
        _socket = new Socket(resolvedAddress.getHostAddress(), _url.getPort());
        _socket.setSoTimeout(15000);

        if (-1 != _url.toString().toLowerCase().indexOf("https")) {
            System.out.println("set secure");
            _socket.setSecure(true);
        }

//        System.out.print("Request:\r\n");
        sendHeaders();
        if (null != _data) {
            sendData();
        }

        return new HttpResponse(this);
    }



    private void sendHeaders() throws IOException {
        OutputStream out = getOutputStream();
        String headers
                = _type + " /" + _url.getFile() + _url.getQuery() + " HTTP/1.1\r\n"
                + "Host: " + _url.getHost() + "\r\n"
                + "Connection: close\r\n"
                + "Pragma: no-cache\r\n"
                + "Cache-Control: no-cache\r\n";

        if (null != _data) {
            headers += "Content-Length: " + _data.length + "\r\n";
        } else {
            headers += "Content-Length: 0\r\n";
        }

        if (0 < _userHeaders.size()) {
            Enumeration headerEnumeration = _userHeaders.keys();
            while (headerEnumeration.hasMoreElements()) {
                String name = (String) headerEnumeration.nextElement();
                String value = _userHeaders.get(name);
                headers += name + ": " + value + "\r\n";
            }
        }

        out.write((headers + "\r\n").getBytes());
        out.flush();
    }



    private void sendData() throws IOException {
        if (null != _data) {
            OutputStream out = getOutputStream();
            out.write(_data);
            out.flush();
        }
    }



    public OutputStream getOutputStream() throws SocketException, IOException {
        if (_socket == null) throw new SocketException("Socket not yet connected");
        return _socket.getOutputStream();
    }



    public InputStream getInputStream() throws SocketException, IOException {
        if (_socket == null) throw new SocketException("Socket not yet connected");
        return _socket.getInputStream();
    }



    public void close() throws IOException {
        if (null != _socket) {
            System.out.println("close HTTPRequest");
            if (null != getInputStream()) getInputStream().close();
            if (null != getOutputStream()) getOutputStream().close();
            _socket.close();
        }
    }

}

We can command the Nagra myCinema player to start playing its content. To do this we will add a myCinema device to the Devices file and a “Play” action to the Macro file. We then associate the “Play” Action with a macro. It’s that easy!

To create the myCinema device in the devices file go to the Devices tab in the JNIOR Support Tool. Then in the lower left click ‘Add’. Then fill out the information as follows. This device can be added to an existing devices file or a new one. We are going to use a RAW ETHERNET device so that we can send any command we want. The command is a simple text string.

Then click over to the Macro tab and click on Link Devices. This will link the devices file so that the configured devices are available to create macro actions. Now click ‘Add’ below the Macro Action View. You can rename the action. Click in the Device column for the new Action and scroll to the bottom to select your linked device. Select the myCinema device. Now click on Action and select Send. In the data field enter “play”. It should look like this…

You can now add the action to any macro by selecting both the macro you wish to add it to and the new myCinema action. Then use the <- button between the two views like this…

The Cinema application for the JNIOR enhances the JNIOR capabilities. While the JNIOR can be used in its most basic form, the Cinema application provides the ability to execute macros, sequence of actions.

The Cinema application does not ship preinstalled. You MUST obtain the application from our website. There is a download on the website that will be opened in the JNIOR Support Tool and published to the JNIOR. This is called an Update Project.

Here are links to latest versions of the JNIOR Support Tool and the Cinema application.

Name Version Release Date Size MD5
JNIOR Support Tool v7.9 May 16 2019 13 MB 2c22f26f4e87d724e0f7c918095eb8c0
Cinema.jar - Update Project v3.6 Aug 14 2019 335 KB d96d4ae9b9adc4f0b8cdaf9bd87518f3

When you open the Cinema Update Project in the Support Tool you will see the following.

Click Publish and select the JNIOR you want to update. Once the update is complete the JNIOR will have rebooted and the application will be ready to configure. Configuration is largely dependent on what you are trying to do.

Within the Support Tool you can configure devices and macros. Devices are outbound connections from the Cinema application. For example, a projector, sound processor, or lighting system. Those devices can be serial or Ethernet. We have not implemented very many devices but the ones that are implemented satisfy a very large majority of the installations. If you need a device that is not implemented, you can use the “Raw Serial” or “Raw Ethernet” device. This will allow you to send commands that you define to those devices. Even if you pick a device that we have implemented, you may need to add a new command. You can use the Send action and define the bytes that need to be sent.

You can use the Cinema application to execute DMX scripts. Those scripts can be on the same JNIOR or on another JNIOR. Both situations require making a TCP or Raw Ethernet connection to port 10000.

To execute a script once you will send script(SCRIPTNAME)\r\n

To execute a script a certain number of times you will send the same command but with a repeat parameter. For example, to execute 3 times we would send script(SCRIPTNAME -r 3)\r\n

To execute a script and have it repeat forever, until you abort it or reboot the JNIOR, you will send the -f parameter. For example, script(SCRIPTNAME -f)\r\n

Create a DMX device to use in the Macro

Create a Macro to call a DMX script with the Device we just created

As of Cinema version 2.9 you can use the scheduling in Cinema.jar to schedule macros daily, weekly or monthly.

When Cinema.jar is installed and executed for the first time it will create 3 default keys. They will look like this in the registry.

The configuration is a multi-part registry key. There are three parts. The type, the time of day, and the macro to execute.

Here are examples of the different scheduling types

Daily

You can define a macro to execute at a specific time every day. To do this we use the daily type. An example of executing a macro daily at 3:30pm with the name test would be:

daily, 15:30, test

Weekly

You can define a macro to execute on certain days of the week. To do this we use the 2 character day abbreviations in place of the type field. The 2 character day abbreviations are Su, Mo, Tu, We, Th, Fr and Sa. An example of executing a macro every day of the week at 3:30pm with the name test would be:

sumotuwethfrsa, 15:30, test

Monthly

Macros can be defined to execute on a certain date of the month, every month. To do this we use the monthly type followed the day of the month inside the parenthesis. An example of executing a macro with the name test would be:

montly(15), 15:30, test

You can also select to only execute the macro on certain months. Do do this we use 3 character month abbreviations. Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov and Dec. Only one month can be selected at a time.

sep(15), 15:30, test

Here is a registry example

There are many reasons to upgrade a series 3 JNIOR to a series 4. While we believe the experience and reliability are much better with the newer hardware and software we understand that in most cases the “if it isn’t broke, don’t fix it” rule applies.

When you are ready, use the following steps to help the transition go smoothly.

Can I just perform a backup from the series 3 and restore onto the series 4?

You can perform a backup and restore. BUT, this will copy over the cinema.jnior application. This file will not run on the series 4. There is a specific cinema file, cinema.JAR, that will run on the series 4.

If the cinema.jnior file gets copied over there will only be one small issue. A restore will set that file to run on boot. Every time the JNIOR boots it will throw an error regarding the cinema.jnior file being there and being the incorrect format.

The procedure

1. Update the Series 4 Operating System

Make sure the new series 4 JNIOR’s Operating System and Cinema application are up to date. You can find those applications on the All Downloads page.

The Cinema update will ensure that the Cinema application is present that will run on the Series 4. The version of the application that is loaded on the Series 3 will NOT run on the Series 4.

2. Take a Snapshot of the series 3 JNIOR to wish to replace

3. Extract the Snapshot

Open the Snapshot zip file and extract the macro file, devices file and flash/jnior.ini file.

4. Upload the macro and devices files to the destination series 4 JNIOR

5. Upload the configuration

The configuration is stored in the flash/jnior.ini file. Before you upload it we will need to remove two sections from the file. We will remove the [ipConfig] section so that the IP Address remains and the [run] section so that cinema.jnior does not try to start on boot.

6. Enable MODBUS Server (If you are using a GDC Cinema Server)

The MODBUS server ran by default in the series 3 operating system. It is a separate application on the Series 4 that must be enabled to provide MODBUS connectivity.

You can enable MODBUS via the DCP. http://JNIOR_IP_ADDRESS in your browser.

6. Reboot the JNIOR again.

You new Series 4 should be running the Cinema application with the configuration and settings from your previous series 3 JNIOR.

MODBUS client is not able to connect to the JNIOR

The MODBUS server running on the JNIOR is a separate application from the Operating System. It does not run by default when the JNIOR is shipped. It is installed by default. It must be activated by the end user before the the MODBUS connection can be successful.

To activate it go to the DCP, click on the Configuration tab. Half way down the left side click on Applications. You will see a list of applications that are loaded on the JNIOR. Check the box next to Modbus Server. This will tell the JNIOR to run the application when the JNIOR boots up. You will need to reboot the JNIOR at this point to get the MODBUS Server to start.

To verify that the MODBUS server is running we can list the listening network sockets using the netstat command.

Lastly, you can change a few configuration parameters for the MODBUS server under the registry tab.

Port 502 is the default MODBUS port. Most likely this will NOT need to be changed. The Timeout setting is the number of milliseconds to wait for a MODBUS client to poll the server. If a request is not made within this amount of time then the JNIOR will close the connection. If any configuration needs to be changed it will probably be the Login setting. Not very many MODBUS clients implement a login. If they dont log in then you will see the following in the Modbus Server log…

Setting the Login key to false will allow this device to connect successfully.

The problem: You get the following screen when trying to go to the DCP or any web page on the JNIOR.

In this image we tried to go to the IP Address of the JNIOR.  This should present us with the DCP web page.  In this case we are presented with the “Page not found” response page.

This means that the file cannot be found in the filesystem for this page resource.

To troubleshoot this we need to look at the filesystem.  Since the DCP is not available we need to use FTP or a telnet session.

Using FTP

Open Windows Explorer. In the address bar type ftp://IP ADDRESS. You might see the message that Windows Explorer cannot access your folder.

Most likely this is because you need to provide credentials. To do that you need to right click in the white-space in the window and select Login As…

Once you log in navigate to the flash directory.

If the the DCP or default web page is missing then all of the following must be met:

  • www.zip is missing
  • public.zip is missing
  • www/ directory is missing or there is not an index.php file in the www/ directory
  • public/ directory is missing or there is not an index.php file in the public/ directory

Using Telnet

We can use Telnet to look at the filesystem as well. To do this, open your favorite Telnet application. Make a connection to the JNIOR. Log in. You will then use either the dir command or the ls command. Both commands are the same and will list the directory contents.

Use the command of your choice, whichever command is easier to remember, and add “flash” as a parameter. This wil cause the command to list the contents of the flash directory. If the the DCP or default web page is missing then all of the following must be met:

  • www.zip is missing
  • public.zip is missing
  • www/ directory is missing or there is not an index.php file in the www/ directory
  • public/ directory is missing or there is not an index.php file in the public/ directory

Security is a good thing. It is also hard. The IpConfig/Allow registry key will help you filter the connections permitted by the JNIOR.

The documentation for the use of the IpConfig/Allow key is as follows…

The IpConfig/Allow Registry key when present defines specific IP addresses and IP address subnets that are to be granted access to the JNIOR. All others are ignored. If undefined then all routable clients have access. For example, to restrict access only to your IP address (say 50.197.34.73) and your entire local subnet (10.0.0.x with mask 255.255.255.0) you would define the following:

reg IpConfig/Allow = “10.0.0.0/24, 50.197.34.73” 

In this case you, using that specific address, and everyone on the local subnet would be able to see the JNIOR and access its functions while everyone else is blocked.

This therefore is a list of IP address/subnet specifications of the form:

nnn.nnn.nnn.nnn/dd 

Here the 4 octet IP address for IPv4 is specified and ‘/dd’ if present indicates the number of bits from the left that must match. If the ‘/dd’ parameter is omitted it is the same as specifying ‘/32’ and as if you used a subnet mask of 255.255.255.255. In other words that IP address must match in its entirety.

Changes to this configuration setting take effect on reboot.

The IpConfig/Allow registry key can be powerful to help secure your JNIOR.  It can successfully thwart unauthorized access and prevent DOS attacks.

The danger comes in when the registry key is mis-configured. It can be a typo or not fully configured, something simple, but when this happens it can prevent legitimate attempts to access the unit.

If this happens a USB-to-Serial cable is needed to access the unit via the RS-232 port. Make sure to use the correct serial settings of 115200, 8 data bits, 1 stop bit and no parity. Once connected you can issue the reg command.

Above you can see that the IpConfig/Allow key is set for 10.0.0.0/24. This states that the first 24 bits of the address must match for a network connection to be accepted. If, for some reason, this was mistyped then legitimate connections would not be allowed. This would basically render the network port useless. The user might not have noticed what the error was.

Using the Serial to USB cable is the only way to access the unit. The key can then either be fixed or removed to regain access over the network.

It is always a good idea to take backups of devices. Personal pictures, work documents, and even the JNIOR. There is another aspect of taking a backup of the JNIOR that can prove very useful. That is that a backup can become an update project. The update project can then be used to restore the same unit to a point in time or to clone a JNIOR. This means that you can take a backup and then make one or more other JNIORs appear and behave exactly the same.

Where can I find the backup feature?

The Backup feature is under the Snapshot tab.

How is a Backup different than a Snapshot?

The backup differs from a snapshot in a two ways. The most important difference is that an update project will be created. The other difference is that log files are not captured. A snapshot is used to capture log files to submit to INTEG to help debug any issue you may be having.

The Backup procedure

Click ‘Take Backup’ in the Snapshot Tab

You will be shown a prompt that lets you know what is about to happen and to make sure you want to continue.

Clicking ‘Yes’ will provide you with a dialog allowing you to select which unit you want to backup.

When the backup has finished you will be prompted for a name. Give the backup a meaningful name. That name might be the unit name followed by a date string or it may be a name describing the job function that the JNIOR is performing.

Lastly you will be prompted to open the Backup or Update Project in the Update Tab.

If you click ‘No’, you can open the Update Project at any future point in time. Just go to the Update tab and click ‘open project’ and select the backup.

The following is the backup update project that was created.

That, is the backup procedure. Congratulations, you can now use this backup to restore this unit back to this point in time or use it to configure other JNIORs to perform the same task.

When we want to reboot the JNIOR from our Java application we call the command line reboot command.  To call to the command line we use the ConsoleProcess class.  Since there will not be any user to confirm the reboot we need to use the -f command to force the reboot.

        try {
ConsoleProcess consoleProcess = new ConsoleProcess("reboot -f");
consoleProcess.waitPrompt();
} catch (Exception ex) {
throw new IOException("Error rebooting").initCause(ex);
}

Sometimes you need to find out what the IP Address of the JNIOR is that is sitting on your network.  Sometimes someone else set it up and are no longer working with you or maybe you programmed it but years have passed and you haven’t needed to access the JNIOR because it is performing its job and no attention has been needed.

There are a couple of different ways to find out the IP Address.

By far the easiest way to find the JNIOR IP Address is the use the JNIOR Support Tool.  The JNIOR Support Tool only works on Windows systems.  http://jnior.com/category/news/support-tool/ will have links to the latest, as well as previous, versions of the JNIOR Support Tool.  Once loaded and started you will be presented with the Beacon tab.  The Beacon tab will show all the JNIORs that are present on your LOCAL network that have reported.  When the Support Tool launches a broadcast is sent out asking for all the JNIORs to report their information.  Some network configurations can either help or hinder this operation.  In most cases it works as intended.  Here is a picture of the Beacon screen.  If you have more than one JNIOR shown you can use the Serial Number of the JNIOR to identify which row corresponds to your unit or you can use the identify feature to pulsate the amber LED next to the power indicator.  To perform that function you can right click on a row and select either Query | Identify or Configure | Identify from the menu.

The other way to determine the IP Address of a unit is to directly connect a serial cable to the RS-232 port.  Computers do not come with serial ports anymore so a USB to Serial adapter will be needed.  You can find them at Amazon.  Once the cable is connected, select your favorite terminal application or use the Command Line application included with the Support Tool.  The serial settings must be 115200, 8, 1, none for the baud rate, data bits, stop bits and parity respectively.  Now that the connection is set up you can press enter and the login prompt will be presented.  The default credentials are username: jnior and password : jnior.  You can now enter ipconfig to see the current network setup.

 

 

 

 

 

 

 

 

Lastly, for those who know Morse code, you can unplug the network cable and the status LED will flash Morse code representing the value of the last octet.  You must know the first 3 octets of your network.

    0 --- --- --- --- ---
    1 - --- --- --- ---
    2 - - --- --- ---
    3 - - - --- ---
    4 - - - - ---
    5 - - - - -
    6 --- - - - -
    7 --- --- - - -
    8 --- --- --- - -
    9 --- --- --- --- -