Monday, November 26, 2012

NXTLegoFX: JavaFX based application to play with a Lego Mindstorms NXT. Part I

Hi there!

This is the first of a series of posts related to a new application in JavaFX I'm developing with the initial intention of interacting with a Lego® Mindstorms® NXT.

The first purpose of this app is, as a preliminary proof of concept, to show how easy is to integrate different software and hardware technologies. For that I'll start by introducing you briefly to the following topics:
  • Lego Mindstorms NXT: computational module, with graphical programming environment, initially intended for kids to play with robotics.
  • leJOS, firmware to replace the NXT one, includes a tiny Java Virtual Machine, and allows you to programm the robot with Java.
  • Glassfish, application server to run Java apps and RESTful web services, implemented using HTTP and the principles of REST.
  • JavaFX, the new Java platform for creating and delivering rich Internet applications.
If you're already an expert in any of these items, please, feel free to skip to the last part of the post. If you're not, there're plenty of blogs out there covering deeply any of them, as this post pretends to be just a glance.

Please notice I don't consider myself in any way an expert in any of the topics I'm covering here. Quite on the contrary, this is an exercise of traversal and shallow engineering.

Before I begin, it's fair I mention the guy whose idea I've taken to start with all of this: Sébastien Stormacq @Sebtso. His entry blog Put a Java Brain in your Lego Robot, from 2010, opened my eyes, with his code I didn't need to start from the scratch. Also I want to thank the team members of leJOS community. They keep vey much alive this fantastic project.

1. The schematics

For starters, let's see a simple overview of the intended project and the technologies involved.


2. Lego Mindstorms

As I said before, there are plenty of blogs out there describing in full detail what it is or what you can do with it, so just for the very beginners here it's in short the main specs.

Lego Mindstorms NXT is a programmable robotics kit released by Lego in 2006. The NXT 2.0 was released in 2009. The kit includes 3 servo motors and 4 sensors (ultrasonic, 2 touch and color), 7 connection cables, a USB interface cable, and the NXT Intelligent Brick, which is the brain of a Mindstorms machine.
The brick is the computational module:
  • Based on a 32-bit Atmel AT91SAM7S256 ARM7TDMI microcontroller (ARMv4) with 64 kB RAM, 256 kB flash and 48 MHz.
  • Attached to it a 8-bit Atmel AVR ATmega48 controller via I2C interface, 512 Bytes RAM, used to collect data from sensors and control motors.
  • With one USB 2.0 port, Bluetooth support and a 100x64 pixels black and white display.
Let me say here that, relatively speaking, the brick architecture could be compared to a quite underrated Raspberry Pi (ARMv6 ARM1176JZF-S 32-bit, 512 MB RAM, 700 MHz) with and Arduino (ATMega328, 8-bit, 2 kB RAM) attached.

On the software side, bundled with the kit comes NXT-G, a graphical programming environment that enables the creation and downloading of programs to the NXT. It is powered by LabVIEW from National Instruments. These programs make use of the standard firmware provided by Lego.


This is intended for kids to start programming, very much like MIT's Scratch. It's open to new blocks related to sensors from third-party providers like HiTechnic or Dexter Industries.

While NXT-G provides a very user friendly approach to programming NXT robots, for more advanced programming with a text-based language, there is plenty of unofficial languages for the NXT out there. Three of the most popular of them are NXC/NBC, RobotC and LeJOS NXJ.

3. LeJOS NXJ

Well, why I've choosen LeJOS NXJ in the first place, a Java based language, instead of the others C based languages it should be clear to a regular reader of this blog.

You can find a complete description of leJOS, the firmware replacement for the official one, and leJOS NXT, the language, here. You can find a full tutorial here.  

Download the latest version (at the time of this post, 0.9.1beta-3) here. Choose Windows (*_win32_setup.exe) or Linux and Mac OS X version (*.tar.gz).

In short, the firmware includes a tiny Java virtual machine, which allows Lego Mindstorms robots to be programmed with Java. So, first of all, you have to flash this firmware. 
Note you'll lose every file stored in the NXT. This process is reversible, and you can restore Lego official firmware afterwards.

To flash the NXT, please follow the detailed Getting Started instructions here according to your operative system. At the end, you'll have your NXT with a brand new leJOS logo and a main menu, quite similar to the official one. Here you'll find detailed instructions of how to use it. 

If you want to use bluetooth for communication, enter in the submenu, select Power On, Visibility On, and enter a 4 digit pin (1234 by default). In your PC, search the device and connect to it. In Windows (my case) this is quite simple and it works smoothly. But in Mac OS X, on the contrary, it seems bluetooth is not working, from what I've heard. Edited: Please, check this Comment from Philipp Dörfler below for instructions to use bluetooth with your NXT in Mac OS.


On the other side, on your PC you'll find in the installation path a bin folder with a bunch of Swing based PC GUI tools, which are batch files like nxjbrowse, to list the files in your NXT, the nxjflashg, to flash the NXT or nxjcontrol, that gives you full control of your NXT:


Programming the NXT

You can either write Java programs that run on the NXT or run on the PC and control the NXT remotely. You can use any Java IDE or just command line tools.

In the installation path you'll find the samples.zip file, which contains samples to download and execute on the NXT (samples folder) or to run them on your PC (pcsamples folder). Ant build scripts are provided. 

Running samples on the NXT

The samples require classes.jar in the classpath. When you run the project from the IDE, a dialog ask you to enter the name of the sample you want to download and run. The view sample, for instance, shows a menu in the NXT LCD and you can read sensors or control motors with the buttons.

Here you have a sneak peek of the code of this program:

package org.lejos.sample.view;
import lejos.nxt.Battery;
import lejos.nxt.Button;
import lejos.nxt.LCD;
import lejos.util.TextMenu;

public class View {
 
    public static void main (String[] aArg) throws Exception
    {
        String[] viewItems = {"System", "Sensors", "Motors", "Exit"};
        TextMenu main = new TextMenu(viewItems, 1, "View Example");
        ...
        int selection;

        for(;;)
        {
            LCD.clear();
            selection = main.select();
            if (selection == 0) // System Info
            {
                LCD.clear();
                LCD.drawString(sys, 0, 0);
                LCD.drawString(batt, 0, 2);
                LCD.drawInt(Battery.getVoltageMilliVolt(), 4, 10, 2);
                LCD.drawString(tot, 0, 3);
                LCD.drawInt((int)(Runtime.getRuntime().totalMemory()), 5, 10, 3);
                LCD.drawString(free, 0, 4);
                LCD.drawInt((int)(Runtime.getRuntime().freeMemory()), 5, 10,4);
                LCD.refresh();

                Button.ESCAPE.waitForPressAndRelease();
            }
            ...
        }
    }
}
As you can see, it's pretty straightforward.

Running on your PC with remote control of the NXT 

In this case, you must add the jar files from lib/pc, pccomm.jar and pctools.jar, and from lib/pc/3rdparty bluecove.jar. But you must not add classes.jar

Here it's one of the samples:

package org.lejos.pcsample.tachocount;
import lejos.nxt.Motor;
import lejos.nxt.Sound;
import lejos.nxt.remote.NXTCommand;
import lejos.pc.comm.NXTComm;
import lejos.pc.comm.NXTCommLogListener;
import lejos.pc.comm.NXTCommandConnector;
import lejos.pc.comm.NXTConnector;

/**
 * @author Lawrie Griffiths and Brian Bagnall
 */
public class TachoCount {
 
    public static void main(String [] args) throws Exception {
        NXTConnector conn = new NXTConnector();
        conn.addLogListener(new NXTCommLogListener() {
            public void logEvent(String message) {
                System.out.println(message);    
            }
            public void logEvent(Throwable throwable) {
                System.err.println(throwable.getMessage());   
            }   
        });
        if(!conn.connectTo("btspp://NXT", NXTComm.LCP)) {
            System.err.println("Failed to connect");
            System.exit(1);
        }
        NXTCommandConnector.setNXTCommand(new NXTCommand(conn.getNXTComm()));

        System.out.println("Tachometer A: " + Motor.A.getTachoCount());
        System.out.println("Tachometer C: " + Motor.C.getTachoCount());
        Motor.A.rotate(5000);
        Motor.C.rotate(-5000);
        System.out.println("Tachometer A: " + Motor.A.getTachoCount());
        System.out.println("Tachometer C: " + Motor.C.getTachoCount());
        conn.close();
    } 
}

Notice that the NXTConnector object uses Lego Mindstorms NXT Communications Protocol LCP to establish communication between PC and the NXT either via bluetooth or via USB. As valid URL for your specific NXT, you can set "btspp://<NXT name>", "btspp://<NXT address>", "usb://<NXT name>" or "usb://<NXT address>". To coonect to any device just use "btspp://" or "usb://".

Look at the other samples, to find out the rest of the possibilities.

4. The Glassfish server

With the ability to communicate to the NXT from a PC, a further step is the installation of a server on the PC to allow the connection of several devices simultaneously to the same NXT(s). 

So I'll try to develop an application, deployed in a Glassfish application server, that will respond to REST requests from these devices, by establishing a synchronized connection with the NXT.

The LegoNXT class

Continuing the previous work of Sébastien Stormacq, based on a singleton object to guarantee one only access to the NXT, regardless the number of users trying to access to it, I'll add a blocking key, so only one request is processed at a time. We must keep in mind that we are targeting a very slow processor. I also add another key to know if the NXT is connected or not.

Here you can see part of the code:

public class LegoNXT {

    private static LegoNXT singleton = null;

    private static boolean legoBlocked=false;
    private static boolean legoConnected=false;
    
    public static LegoNXT getSingleton() {

        final Object lock = LegoNXT.class;

        if (singleton == null) {
            synchronized(lock) {
                singleton = new LegoNXT();                
            }
        }
        return singleton;
    }

    private LegoNXT() {}
    
    public void connect(String NXTName, String Protocol) {
        final String connectString = Protocol.concat("://").concat(NXTName); 
        legoBlocked=true;
        legoConnected=false;
        try {
            Logger.getLogger(getClass().getName()).log(Level.INFO, "Initializing communication with NXT");
            NXTConnector cmd = new NXTConnector();
            cmd.addLogListener(new NXTCommLogListener() {
                @Override public void logEvent(String message) {
                    Logger.getLogger(getClass().getName()).log(Level.INFO, message);
                }
                @Override public void logEvent(Throwable throwable) {
                    Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Exception while talking to NXT brick", throwable);
                }
            });
            
            if(!cmd.connectTo(connectString, NXTComm.LCP)) {
                Logger.getLogger(getClass().getName()).log(Level.WARNING, "Failed to connect");
                legoBlocked=false;
                return;
            }

            NXTCommandConnector.setNXTCommand(new NXTCommand(cmd.getNXTComm()));
            Logger.getLogger(getClass().getName()).log(Level.FINE, "Done");
            legoConnected=true;

        } catch (Exception e) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Exception while initializing connection", e);
            legoBlocked=false;
        }
    }

    public void disconnect() {
        try {
            NXTCommandConnector.close();
        } catch (IOException ex) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
        }
        legoConnected=false;
        legoBlocked=false;
    }
    
    public boolean isLegoBlocked() { return legoBlocked; }
    public boolean isLegoConnected(){ return legoConnected; }

    ...
    /* NXT methods */

The RESTful web services

The first RESTful web service will search for any NXT device connected to the server.

@Path("search")
public class SearchResource {
    @GET
    @Produces("application/json")
    public String getJson() {
        NXTConnectionManager man= new NXTConnectionManager();
        NXTInfo[] search = man.search();        
        JSONObject json = null;
        try{            
            JSONArray arr=new JSONArray();        
            for(NXTInfo info:search){
                JSONObject element = new JSONObject();
                element.put(CData.JSON_RESPONSE_DEVICENAME, info.name);
                element.put(CData.JSON_RESPONSE_DEVICEADDRESS, info.deviceAddress);
                if(info.protocol==NXTCommFactory.USB){
                    element.put(CData.JSON_RESPONSE_PROTOCOL, "USB");
                } else if(info.protocol==NXTCommFactory.BLUETOOTH){
                    element.put(CData.JSON_RESPONSE_PROTOCOL, "BLUETOOTH");
                }
                arr.put(element);
            }
            json = new JSONObject();
            json.put(CData.JSON_RESPONSE_DEVICES, arr);
        } catch(JSONException jse){
            return "{}";
        }
        return json.toString();
    }
}

So when you start the Glassfish server, from any browser you can search for NXT devices connected, and it will answer with a JSON array. In my case, with just one device:


The second service, once you've selected the NXT to connect to, will process a request to ask the NXT for several of its system properties:

@Path("device")
public class DeviceResource {

    private LegoNXT nxt = null;

    @GET
    @Produces("application/json")
    public String getJson(@QueryParam("NXTName") @DefaultValue("NXT") String NXTName,
                          @QueryParam("Protocol") @DefaultValue("usb") String Protocol) {
        String result = null;
        if (nxt == null) {
            nxt = LegoNXT.getSingleton();
        }
        int cont=0;
        while(nxt.isLegoBlocked()){
            try{
                Thread.sleep(100);
                cont+=1;
            } catch(InterruptedException ie) {}
            if(cont>CData.TIME_OUT){ 
                String msg = "Device was blocked";
                Logger.getLogger(getClass().getName()).log(Level.WARNING, msg);
                result = "{ \"status\" : \"" + msg + "\" }";
                nxt = null; 
                return result;
            }
        }
        
        try {
            nxt.connect(NXTName,Protocol);
            result = new JSONStringer().object()
                    .key(CData.JSON_RESPONSE_DEVICENAME)
                    .value(nxt.getDeviceName())
                    .key(CData.JSON_RESPONSE_BATTERYLEVEL)
                    .value((double)nxt.getDeviceBateryLevel())
                    .key(CData.JSON_RESPONSE_BLUETOOTHADDRESS)
                    .value(nxt.getDeviceBluetoothAddress())
                    .key(CData.JSON_RESPONSE_FIRMWAREVERSION)
                    .value(nxt.getDeviceFirmwareVersion())
                    .key(CData.JSON_RESPONSE_VOLUME)
                    .value(nxt.getVolume())
                    .key(CData.JSON_RESPONSE_STATUS)
                    .value("OK")
                    .key(CData.JSON_RESPONSE_TIME)
                    .value(((double)cont)/10d)
                    .endObject().toString();
            nxt.disconnect();
        } catch (Exception ex) {
            String msg = "Can not get device information";
            Logger.getLogger(getClass().getName()).log(Level.WARNING, msg, ex);
            result = "{ \"status\" : \"" + msg + "\" }";
            nxt = null;
        }
        return result;
    }
}

Several other RESTful web services are also added to the server in this way. 

EDITED on the 1st of February of 2013: By request I've uploaded the server code to my GitHub repo, so if you're interested, grab the code here and give it a try!

5. The client requests

Those RESTful web services must be called from the client side, and the returned json string must be processed to extract the contained information. For that I'll use the GSON open source Java library, which allows deserialization of collections or nested classes. So I just need one POJO class with the properties expected to be read from the json string for one device, EntSearch, and one POJO class with an arraylist of the first one, EntDeviceSearch, to collect all NXT devices found and their properties:

public class EntSearch {
    private String devicename;    
    private String deviceaddress;    
    private String deviceprotocol;    
    private int batterylevel;
    private String firmwareversion;    
    private String status;    
    private int volume;    
    private double time;

    /* getters and setters */
}
public class EntDeviceSearch {
    private ArrayList<entsearch> devices = null;

    /* getters and setters */
}

So the request to the server to search for NXT devices, and the deserialization of the response, can be done very easily:
 
     public void search() {
        URL theJsonUrl=null;
        try {
            theJsonUrl = new URL("http://localhost:8080/WebLego/NXT/search");            
        } catch (MalformedURLException ex) {
            System.out.println("URL Error: " + ex.getMessage());
            return;
        }

        String jSonTxt="";
        try {
            InputStream in=theJsonUrl.openStream();
            jSonTxt=IOUtils.toString(in);
        } catch (IOException ex) {
            System.out.println("URL Error: " +theJsonUrl+" "+ ex.getMessage());
            return;
        }

        EntDeviceSearch devices=null;
        try{
            devices=new Gson().fromJson(jSonTxt,EntDeviceSearch.class);
        } catch(JsonSyntaxException jse){
            System.out.println("GSON Error: "+jse.getMessage());
            return;
        }

        if(devices!=null){
            System.out.println("Found Devices: "+devices.getDevices().size());
            for(EntSearch ent: devices.getDevices()){
                ... // process each entry accordingly
            }
        }
        else{
            System.out.println("Search Error");     
        }       
    }
 
6. The JavaFX UI

Finally we reach the User Interface part. Now we only need to desing a fancy scene and connect it to the server via the RESTful web services. As this is a proof of concept, well be adding just a few controls to the scene, in this first version.

I'll use a Tab Pane with two tabs. The first one, labeled Communication, will have a TableView to list all NXT devices found, just by calling the search RESTful web service. Once a row is selected, the other service (device) is called to fetch the data from the picked device.

You should know by now that there're quite fancy custom JavaFX controls in jfxtras.org. Just drop the last available jar (at the time of this writting, jfxtras-labs-2.2-r5-SNAPSHOT.jar) to your project. To show the data values, a Battery, several Leds and an Indicator from JFXtras project will be used.

This is how it looks like the Communication tab, for now:



Values from the NXT such as battery level (in mV), speaker volume level and connection status are read with the device REST service. To set the volume, a new REST service is used.

I should explain here the model behind this application, as it connects the UI part (FXML, the controller, the controls) with the REST requests from the client to the server part. There're a few tricky things here, as the controls values (JavaFX thread) are bound to the readings of the REST responses (standard Java thread).

But due to the clearly exceded length of this post, I must stop here, and leave it to a second part... So let me just add a short video showing what we've got of the NXTLegoFX application so far:



See you in the (hopefully coming soon) next part of this post! Love to hear any comments from you in the meantime.

Saturday, September 22, 2012

A multi-touching experience with a WeTab

Hi there!

This time my post won't be strictly code related. Instead I'll share with you my recent experience with a WeTab, a tablet computer I have for a couple of years, trying to test a few JavaFX multitouch enabled samples from the JFXtras project, from Carl Dea (@carldea) and Gerrit Grunwald (@hansolo_).

First of all, let me introduce you to the WeTab tablet, for those of you who haven't heard of it yet. Instead of boring you with technical data, here you can find the specs, and here you have a very good review of the WeTab. But in short, it includes a (big) 11.6" TN-panel touch screen (1366×768 resolution) powered with Intel GMA 3150 graphics and PCIe x1 Broadcom CrystalHD BCM70015, a 1.66 GHz Intel Atom N450 processor with fan, 1 GB RAM memory, 16 or 32 GB  flash memory, WiFi and optional 3G, 2 USB ports, and a total weight of the device of 1.002 kg.


I knew of it back in 2010, thanks to my partner Ricardo Vela (@rikivela), when we were developing client and server Java based projects, and we felt the necessity to go wireless with our client applications. So we searched for a tablet where we could run java and he found this linux based tablet with MeeGo technology. It was only sold in Germany by then, so we have to buy it through Amazon.de store.
 
So at the beginning of 2011 I had in my hands this heavy tablet. It took me a while to refresh my rusty linux knowledge, but I was able to download and install the last JRE linux version, and test that my application ran on it really nice and smooth. I was so excited I took a (very bad) picture of it:



Even it had its plastic cover on it! I realized I have to fix the screen size of my app, but apart from that, you know what they say: write once, run everywhere.

Then we had the chance to make an iOS version for iPhone and iPad tablet of the same project, and we found the pros and cons of it: The WeTab screen had nothing to do with the Retina Display of iOS devices... but we have to re-write all the client app to Objetive-C. Anyway, this is a story for another day.

For several reasons I had my WeTab parked somewhere, until recently, when Carl Dea tweeted about finding a linux tablet to run JavaFX. I offered myself, and then... the story begins...

Java, JavaFX and MeeGo

If you want to test Java and JavaFX based applications you have to install the last JRE in the tablet. There're plenty of blogs out there to show you how, but here it's what I do:

First of all, go to the WeTab Market (first icon you'll find on the desktop), Tools & Utilities section, and download the Firefox Internet browser. It will install itself inmediately. Open it and go to Oracle site and download jre-7u7-linux-i586.rpm. It will be stored by default in /home/<user>/Downloads.

Download also the RootShell app, so you can open a terminal window and enter the command line world.



Write and insert password when asked:
#cd Downloads
#sudo rpm -ivh jre-7u7-linux-i586.rpm

Then the whole package will be installed in /usr/java/jre1.7.0_07. Also the links /usr/java/default and /usr/java/latest will be added. Ignore a few errors shown at the end of the process.

To add the JRE to the path, edit the file:
#sudo gedit java.sh

add the following:
#!/bin/sh
export PATH=/usr/java/default/bin:$PATH
export JAVA_HOME=/usr/java/default


save and close it, and install it:
#sudo install -m755 java.sh /etc/profile.d

Close the console, and you're ready to try any Java or JavaFX application.

SlideLockSample. First try

In my desktop I create a sample class called SlideLockSample1, which I compile and build and then I copy the jar file to the WeTab, with an USB flash drive, for instance. This is part of the start method:

    @Override
    public void start(Stage primaryStage) {
        final Label lbl=new Label("Locked!");
        lbl.styleProperty().set("-fx-text-fill: red;-fx-font: 1.5em \"Arial Bold\";");
                    
        SlideLock slideLock = new SlideLock("slide to JavaFX");
        slideLock.setBackgroundVisible(true);
        slideLock.lockedProperty().addListener(new ChangeListener< boolean >() {

            @Override
            public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) {
                if (!newValue) {
                    lbl.styleProperty().set("-fx-text-fill: blue");
                    lbl.textProperty().set("Unlocked!!!!");
                } else {
                    lbl.styleProperty().set("-fx-text-fill: red");
                    lbl.textProperty().set("Locked!");
                }
            }
        });
    }


Here you have the code and the jar from JFXtras project, with the very last commits.

And here you have a small video taken with my iPhone showing how I slide the control with my finger, after starting the project with java -jar SlideLockSample1.jar in the console.

I was excited to see the touch event working... until I realized it was in fact a simple mouse event. To test that I commented first the touch events in addHandlers() method in SlideLockSkin class:

private void addHandlers() {
        // MouseEvents
        setOnMousePressed(mouseHandler);
        setOnMouseDragged(mouseHandler);
        setOnMouseReleased(mouseHandler);
        // TouchEvents
        setOnTouchPressed(touchHandler);
        setOnTouchMoved(touchHandler);
        setOnTouchReleased(touchHandler);
    }

And ran the jar again, working as it did the first time. So then I uncommented touch events and commented mouse events. Guess what? It didn't work.

So my multitouch tablet was only handling single touch events as pure mouse events. I found googling that with the last touchscreen firmware (which I had) the multitouch was supported <just> in the browser, the maps and the Picture Viewer app. I tested this one, and it really had zoom, rotation, scrolling  gestures working. You can find it in the WeTab Market, Entertainment and Multimedia section.

What about other applications? Nothing at all.

Quite dissapointed I looked for other options. I found a really good WeTab blog here, and following his post about multitouch I took the plunge and installed Ubuntu in my WeTab, now with instructions from this other post.

Partitioning the WeTab

As I wanted to keep the WeTab OS, I had to partition the 32 GB disk. For that I used GParted software, which I could download from here.  

Shortly, from Windows OS, you have to unzip the content to the USB flash drive, open a command line window with administrator privileges. Then let's say USB drive is H:, type H:cd utils/win32, and run makeboot.bat to install a hidden file and make the flash drive bootable.

Then comes the tricky part... Plug in the flash drive in the WeTab, turn it on and as soon as you see the blue led in the top left corner light up, press both the power-button and the quicktouch orange button (top left corner) together for approximately 1 second, and release them. GParted Live should boot up from the USB flash drive.

Then use the quicktouch orange button on the upper left corner of the WeTab to browse through the Boot menu for Gparted. One short touch switches between options. Select "other modes of Gparted",
hold down a second on the quicktouch button, it selects the entry. Then choose "Run from RAM or memory". It takes a while to load, but at the end it shows a graphical application with all the partitions. Select the sda3 partition (the biggest one) and then Resize. I resized mine to allow 8 GB for Ubuntu. Select the unasigned space and create a new partition with ext3 format (sda4).

Installing Ubuntu

Download last Ubuntu version, 12.04, from here. To create a bootable USB drive from Windows, also download and install this. Then open it, locate the ubuntu image, select the flash drive and click on Create.

Now you have to plug in the flash drive in the WeTab, turn it on, press both power and quicktouch buttons for a second, and when the installer boots up, follow the process to install Ubuntu. At the end of the process, you should modify the WeTab booting options to select WeTab OS or Ubuntu.

Finally, in order to allow multitouch events, following Etheros blog, I opened a console and typed:
sudo add-apt-repository ppa:utouch-team/utouch

Guess what? The repository was gone... You can check it here, the page is not found.

That's one of the thing I fear from Linux... you're always depending on some team's work, which suddenly may disappear.

Belive me I tried other options from the repository, but nothing worked, although I must say probably I missed something as clearly I am not a Linux expert.

Anyway, I installed Java, pretty much as described before, and it happened the same, the touch event was treated like a mouse event. No multitouch at all, as expected.

So I took another approach...

Installing Windows 8

A friend of mine told me about the native support of Windows 7 and Windows 8 for touchscreens, so I decided to give it a try, as the WeTab is x86 based.

So I downloaded Windows 8 Release Preview from here, and unzipped it to the USB flash drive.  Plugged in the flash drive in the WeTab, turned it on, pressed both power and quicktouch buttons for a second, and followed the process to install Windows 8 (in my case, in the sda4 partition, overwritting Ubuntu). At the end of the process the new interface of Windows was ready, with multitouch gestures support.

I won't go any further with that... Just two things: I ran out space! The installation took the 8 GB and left 90 MB free!! And there were no way to boot from WeTab anymore...

Well, I had to start all over again... Booting with GParted Live, I deleted sda4 partition, resized sda3 to have at least 16 GB of free space, created a new NTFS partition and installed again Windows 8.

To allow booting from WeTab OS, I found the EasyBCD software here, which allows to create a bootable menu from Windows. Just add a new Entry, select Bios Extender and install PLOP.

Java and JavaFx, second try

Downloading and installing JRE as usually in Windows, I was finally ready to test JavaFX multitouch gestures!

To end this long post, let me just show you two samples I've tested from JFXtras project.  Also you may have a look at this tutorial from JavaFX documentation.

This is the SlideLock Sample video I've taken with my iPhone, so excuse me for its bad quality:

You can see real touch events in action. Really smooth. Here you may find the surce code for the SlideLockSample2. Great job Carl!!

And this is the Gauge Radial Sample video:


Now, you can see rotate gestures in action. For the code, just look at Gerrit's blog here. As always, great work, Gerrit. 

Well, sure you've noticed in the video the slow performance of the radial... Don't blame Gerrit... As he pointed out, the issue here is hardware vs software acceleration.

If you look at JavaFX 2.2 system requirements here you'll read that for JavaFX applications to take advantage of the new hardware acceleration pipeline provided by JavaFX, your system must feature one of a wide range of GPUs currently available in the market. For Intel graphics cards, as mine, you need at least GMA 4500MHD and GMA HD. 

But the WeTab card is GMA 3150, so that's why if the system does not support hardware acceleration, then JavaFX uses the Java2D software pipeline. Here the Atom N450 does its best...

Conclusions

First of all, excuse me for this long post. I've tried to talk about many things... You can always skip most of them and go to a part of your interest.

Second, you can see there's plenty of room to experiment here... Please, feel free to share any experience!

Finally, about multitouch gestures, although they look astonishing, there's lot to do in many devices and OSes. In this early stages, you should find the proper combination of hardware and software. A bad choice can ruin your work. That's where tablets like the WeTab have their pros, easily you can make a light demo of your projects. 

And it's for another discussion what happens with Java Embedded and JavaFX for more limited architectures like ARM platforms, with devices like Raspberry Pi.


Friday, September 14, 2012

A JavaFX weekly scheduler

Hi there!

This post pretends to be a detailed tutorial of how you can use custom JavaFX controls to make a nice UI, taking advantage of the JavaFX Scene Builder

As custom control I'll use a slightly modified DoubleSlider control, which originally was developed by Altuğ Uzunali by modifying the Slider Control in OpenJFX project. You can see his blog here, and download his code here. Nice work, Altuğ!

As UI I'll try to develop a weekly scheduler for working or bussines hours, using one double slider to set beginning and ending hours for each day of the week.

Here you can see in advance the result. If you find it interesting and want to learn how can you do it yourself, please, keep reading!



Step 1. Create the project

For starters, let's make a new project in NetBeans 7.2, selecting the JavaFX FXML Application type. Let's call the project WeeklySchedulerFX. Also, the FXML file will be called scheduler:

At end of the wizard three files will be created:

Step 2. Create the UI

By double clicking in scheduler.fxml the JavaFX Scene Builder will be open. Now we have to delete what it contains by default (a button and a label) and keep the AnchorPane, and start designing our own scene, by first resizing it to 600x450 dragging it or in the Layout panel, setting Pref width to 600 and Pref height to 450.

Now, let's add the label for the title. It has the following settings: 

Properties panel: 
  • Text: Weekly Scheduler  - Working Hours
  • Alignment: CENTER
  • Effect: DropShadow, spread 0.20, color #956e07,
  • Font: System 16px (Bold)
 Layout panel:
  • AnchorPane Constraints: Top 30, Right 20, Left 20.

Now, we need 7 rows for the doubleSlider controls, and a place to locate the labels (day and range of hours selected), so let's use a GridPane with 7 rows and 2 columns. In the Properties panel, first of all, we set the fx:id for this control, so we can use it in our code later.

Properties panel: 
  • fx:id: grid
  • Alignment: CENTER
  • Vgap: 2
 Layout panel:
  • AnchorPane Constraints: Top 70, Right 20, Bottom 20, Left 20.
  • add Row[3], Row [4], Row [5] and Row [6]
  • Column [0], Pref Width=100, Hgrow NEVER
  • Column [1], Pref Width=460, Max Width: USE_COMPUTED_SIZE, Hgrow ALWAYS

This is what we have for now:

Although the GridPane is a container and we could just add the controls to the specified row and column, we can add to each cell another container, so we can insert later on the custom control or other controls from code, just looping through the gridPane children. 

Let's add a VBox to each cell, and set for all of them Alignment: CENTER. For the first column, also we set Spacing: 5 and Padding 5-5-5-5. For the second column, set Padding: 10-10-10-10.

Now, in the first column, we can add a label with the day of the week. For the first one:

Properties panel: 
  • Text: MONDAY
  • Effect: DropShadow
and so on. 

That's all for the Java Scenic Builder. We can close it and move on to the code part. You should have something like this:

Step 3. DoubleSlider custom control

First of all, we download Altuğ Uzunali's code, and in our project we create a package called doubleSlider and insert DoubleSlider.java, DoubleSliderBehavior.java, DoubleSliderSkin.java and double_slider.css files there. Make sure you change the package declaration to weeklyschedulerfx.doubleSlider  in the java files, and set the -fx-skin property:

.double-slider
{
    -fx-skin: "weeklyschedulefx.doubleSlider.DoubleSliderSkin";
}

in the css one. 

Now we are going to make a few changes in his code. (For shortness sake I won't comment here every change I make, but there are very few; please have a look at the code you can download below)

In order to display time formats instead of numbers in the ticks of the slider, in DoubleSliderSkin class, in the setShowTickMarks method, insert this code just after the tickLine.setMinorTickCount line:


if (doubleSlider.getLabelFormatter() != null){
    tickLine.setTickLabelFormatter(new StringConverter(){

        @Override
        public String toString(Number object) {
            return getSkinnable().getLabelFormatter().toString((Double)object);
        }

        @Override
        public Number fromString(String string) {
            return getSkinnable().getLabelFormatter().fromString(string);
        }
    });
}


Let's add now a node to the control in order to highlight the range of hours between the two thumbs. In DoubleSliderSkin class, declare it:


private StackPane range;


Initialize it (in initialize method) and add it with the other stackpanes (both in initialize and setShowTickMarks methods) to the scene: 

range = new StackPane();
range.getStyleClass().setAll("range");

getChildren().clear();
getChildren().addAll(track, thumb1, thumb2, range);


Now we have to set the range layout. Every time the thumb's layout change the method positionThumb is called. So there insert the following code:

if(horizontal){
    range.setLayoutX(lx1+thumbWidth/2);
    range.setLayoutY(track.getLayoutY());
    range.setPrefWidth(lx2-lx1);
    range.resize(lx2-lx1, track.getHeight());
} else{
    range.setLayoutX(track.getLayoutX());
    range.setLayoutY(ly2+3*thumbHeight/4);
    range.setPrefHeight(Math.abs(ly2-ly1+thumbHeight/2));
    range.resize(track.getWidth(),Math.abs(ly2-ly1+thumbHeight/2));
}

In the layoutChildren method call positionThumb after the track is resized and relocated, not before:

// layout track
track.resizeRelocate(trackStart - trackRadius, trackTop , trackLength + trackRadius + trackRadius, trackHeight);
// layout thumbs and range
positionThumb();

Finally we add the styling properties to the css file (a few more are in the file below):

.double-slider Text {
    -fx-text-fill: #303030;
}

.double-slider .range {
    -fx-base: #956e07;
    -fx-background-color: -fx-base,
        derive(-fx-base,-22%),
        linear-gradient(to bottom, derive(-fx-base,-15.5%), derive(-fx-base,34%) 30%, derive(-fx-base,68%));
    -fx-background-insets: 1 0 -1 0, 0, 1;
    -fx-padding: 0.208333em; /* 2.5 */
}

.double-slider .axis {
    -fx-tick-label-fill: #303030;
}

And that's it!

Step 4. Application setup

Back to our application, we need to add to the Stylesheets the custom control one, and set the stage title:


Scene scene = new Scene(root);
scene.getStylesheets().addAll(DoubleSlider.class.getResource("double_slider.css").toExternalForm());

stage.setScene(scene);
stage.setTitle("JavaFX Tutorial");

Step 5. The Controller

First of all, we add the GridPane container defined in the fxml file:

@FXML
private GridPane grid;

In the initialize method now we loop through the grid children. In the first seven nodes, related to the first column, we add to each VBox a new label which will show the selected range in the format [00:00 - 24:00]. 

final Label[] rangeLabel=new Label[7];
int cont=0;
for(Node n:grid.getChildren()){
    if(n instanceof VBox){
        VBox cell=(VBox)n;
        if(cont<7){                   
            rangeLabel[cont]=new Label();
            rangeLabel[cont].textProperty().set("[00:00 - 00:00]");
            cell.getChildren().add(rangeLabel[cont]);                    
        } 
        ...

In the last seven nodes, we add the doubleSlider controls. We want to show a range of hours and half hours, so we have 2*24+ 1 tick marks, and that's why we set the min value to 0 and the max value to 48. 

Also, the unit distance between major tick marks is set to 2 (hours), and the number of minor ticks between any two major ticks is specified as 1 (half hour). The setSnapToTicks method is set to true to keep the slider's value aligned with the tick marks.
 
...
else {
    final int day=cont-7;

    final DoubleSlider doubleSlider1 = new DoubleSlider();
    doubleSlider1.setPrefWidth(300);                
    doubleSlider1.setShowTickMarks(true);
    doubleSlider1.setShowTickLabels(true);
    doubleSlider1.setMajorTickUnit(2);
    doubleSlider1.setMinorTickCount(1);
    doubleSlider1.setSnapToTicks(true);
    doubleSlider1.setMin(0);
    doubleSlider1.setMax(48);
    doubleSlider1.setLabelFormatter(new StringConverter(){              
        @Override
        public String toString(Double object) {
            if(object==null){
                return null;
            }
            return toTime(object.doubleValue());
        }

        @Override
        public Double fromString(String string) {
            return (string!=null?new Double(string):new Double(0));
        }
    });
    doubleSlider1.value1Property().addListener(new ChangeListener() {
        @Override
        public void changed(ObservableValue arg0,
                        Number arg1, Number arg2) {
            rangeLabel[day].textProperty().set("["+toTime(arg2.intValue())+" - "+toTime(doubleSlider1.getValue2())+"]");
        }
    });
    doubleSlider1.value2Property().addListener(new ChangeListener() {
        @Override
        public void changed(ObservableValue arg0,
                        Number arg1, Number arg2) {
            rangeLabel[day].textProperty().set("["+toTime(doubleSlider1.getValue1())+" - "+toTime(arg2.intValue())+"]");
        }
    });
}

You can see that we set the labelFormatter to map the double value of the thumb position to a time string. For this, a simple string formatter is used:
 
private String toTime(double value){
    return String.format("%02d:%02d", (int)(value/2), (int)(30*(value%2)));
}

Also, we add a valueProperty() listener for each thumb so we can reflect their position changes in the proper rangeLabel.

Finally, we decorate the cells, taking here the advices of  Jasper Potts in his the excellent post Styling FX Buttons with CSS:
 
cell.setStyle("-fx-background-color: #ecebe9,rgba(0,0,0,0.05),linear-gradient(#dcca8a, #c7a740),"
        + "linear-gradient(#f9f2d6 0%, #f4e5bc 20%, #e6c75d 80%, #e2c045 100%),"
        + "linear-gradient(#f6ebbe, #e6c34d);"
        + "-fx-background-radius: 4;-fx-background-insets: 0,2 2 1 2,2,3,4;");

End

And this is pretty much everything you need to do to have a cool control and a really nice UI up and running in a few hours! Even minutes if you grab the code here!

Now it's up to you to make any changes you may have missed here.

Please let me know if you have any comment or suggestion. They would be much appreciated!