From ae8432ffe5cea8421b04068d0fe62d8e15175505 Mon Sep 17 00:00:00 2001 From: Christopher Baines Date: Wed, 14 Sep 2011 10:42:54 +0100 Subject: New package management, needs more work, not enough is public. Split programs from the gui, these are included as examples. --- .../src/uk/ac/open/punchingbag/PunchingBag.java | 786 +++++++++++++++++++++ 1 file changed, 786 insertions(+) create mode 100644 PunchingBag/src/uk/ac/open/punchingbag/PunchingBag.java (limited to 'PunchingBag/src/uk/ac/open/punchingbag/PunchingBag.java') diff --git a/PunchingBag/src/uk/ac/open/punchingbag/PunchingBag.java b/PunchingBag/src/uk/ac/open/punchingbag/PunchingBag.java new file mode 100644 index 0000000..31b0575 --- /dev/null +++ b/PunchingBag/src/uk/ac/open/punchingbag/PunchingBag.java @@ -0,0 +1,786 @@ +package uk.ac.open.punchingbag; + +import gnu.io.CommPortIdentifier; + +import javax.swing.event.EventListenerList; + +import java.awt.Rectangle; +import java.awt.geom.Area; + +import java.io.IOException; +import java.math.BigInteger; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; + +// TODO: Loads of threads in this class, make sure everything is thread safe. + +public class PunchingBag implements Runnable { + boolean ledsChanged = false; + final public int ledHeight = 20; + final public int ledWidth = 9; + private byte[] rawLeds = new byte[6 * 8]; + private Colour[][] leds = new Colour[ledWidth][ledHeight]; // NEVER ACCESS + // DIRECTLY (Use + // SetLedInternal) + private byte[] ledGridIntensities = new byte[6]; + final public int buttonHeight = 19; + final public int buttonWidth = 8; + private boolean[][] buttons = new boolean[buttonWidth][buttonHeight]; + boolean buttonsChanged = false; + + int bottomAccelX = 0; + int bottomAccelY = 0; + int topAccelX = 0; + int topAccelY = 0; + boolean acccelChanged = false; + + private long lastRefresh = 0; + + private ArrayList runningEffects = new ArrayList(); + + Arduino buttonArduino = new Arduino(); + Arduino ledArduino = new Arduino(); + + boolean debugTimings = false; + boolean debugSerial = false; + + static private PunchingBag bag = new PunchingBag(); + + public static PunchingBag getBag() { + return bag; + } + + public enum Colour { + None, Red, Yellow, Green + }; + + public enum Direction { + Right, Left, Up, Down + }; + + // Is there a better class for this? One that it is not a swing class. + private EventListenerList buttonListenerList = new EventListenerList(); + private EventListenerList ledListenerList = new EventListenerList(); + private EventListenerList accelListenerList = new EventListenerList(); + + private PunchingBag() { + Thread bagControlThread = new Thread(this); + bagControlThread.setPriority(Thread.MAX_PRIORITY); + bagControlThread.start(); + } + + /** + * Adds an ActionListener to the button. + * + * @param l + * the ActionListener to be added + */ + public void addButtonListener(ButtonListener l) { + buttonListenerList.add(ButtonListener.class, l); + } + + public void addLEDChangeListener(LEDListener l) { + ledListenerList.add(LEDListener.class, l); + } + + public void addAccelChangeListener(AccelListener l) { + accelListenerList.add(AccelListener.class, l); + } + + public Colour getLED(int x, int y) { + return leds[x][y]; + } + + private boolean setLEDInternal(int x, int y, Colour colour) { + if (x >= 0 && x < ledWidth && y >= 0 && y < ledHeight) { + if (leds[x][y] != colour) { + leds[x][y] = colour; + ledsChanged = true; + + } + return true; + } else { + return false; + } + } + + public void setLED(int x, int y, Colour colour) { + runningEffects.add(new Point(x, y, colour)); + } + + public void circleExpand(int x, int y, int intensity) { + runningEffects.add(new CircleExpand(x, y, 100)); + } + + void squareExpand(int x, int y, int intensity, Colour colour) { + runningEffects.add(new SquareExpand(x, y, intensity, colour)); + } + + public void fillRect(int x, int y, int width, int height, Colour colour, long time) { + runningEffects.add(new FillRect(x, y, width, height, colour, time)); + } + + void rect(int x, int y, int width, int height, Colour colour) { + runningEffects.add(new Rect(x, y, width, height, colour)); + } + + void noise(Rectangle rect, long time) { + runningEffects.add(new Noise(rect, System.currentTimeMillis() + time)); + } + + abstract class Effect { + long lastRefresh = 0; + boolean stop = false; + + // int refreshRate = 60; // Times per second to refresh + + public void stop() { + stop = true; + } + + abstract public void draw(); + } + + class Point extends Effect { + final int x; + final int y; + final Colour colour; + + public Point(int x, int y, Colour colour) { + this.x = x; + this.y = y; + this.colour = colour; + } + + public void draw() { + setLEDInternal(x, y, colour); + } + } + + class FillRect extends Effect { + final int x; + final int y; + final int width; + final int height; + final Colour colour; + final long time; + + public FillRect(int x, int y, int width, int height, Colour colour, long time) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.colour = colour; + this.time = System.currentTimeMillis() + time; + } + + public void draw() { + fillRectInternal(x, y, width, height, colour); + if (System.currentTimeMillis() >= time) { + stop(); + } + } + } + + class Rect extends Effect { + final int x; + final int y; + final int width; + final int height; + final Colour colour; + + public Rect(int x, int y, int width, int height, Colour colour) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.colour = colour; + } + + public void draw() { + drawRectInternal(x, y, width, height, colour); + } + } + + class Text extends Effect { + final String string; + final Area area; + final Direction direction; + final int speed; + + public Text(String string, Area area, Direction direction, int speed) { + this.string = string; + this.area = area; + this.direction = direction; + this.speed = speed; + + } + + public void draw() { + byte[] stringBytes = string.getBytes(); + + } + } + + class CircleExpand extends Effect { + final double x; + final double y; + int intensity; + boolean drawnSomething = true; + + double currentRadius = 0.5; + + public CircleExpand(int x, int y, int intensity) { + this.x = x + 0.5; + this.y = y + 0.5; + this.intensity = intensity; + } + + public void draw() { + Colour colour; + + if (intensity >= 10) { + colour = Colour.Red; + } else if (intensity >= 5) { + colour = Colour.Yellow; + } else { + colour = Colour.Green; + } + + intensity -= 5; + + currentRadius += 0.4 + (currentRadius / 20); + // currentRadius += 0.01; + // longhand: currentRadius = currentRadius + 0.1 + (currentRadius / + // 10); + + if (!drawCircle(x, y, currentRadius, colour)) + stop = true; + } + + } + + class SquareExpand extends Effect { + final int x; + final int y; + final Colour colour; + int intensity; + boolean drawnSomething = true; + + int heightWidth = 2; + + public SquareExpand(int x, int y, int intensity, Colour colour) { + this.x = x; + this.y = y; + this.intensity = intensity; + this.colour = colour; + + } + + public void draw() { + heightWidth += 0.1 + 0.1 + (heightWidth / 20); + if (!drawRectCenter(x, y, heightWidth, heightWidth, colour)) + stop = true; + + } + + } + + class Noise extends Effect { + final Rectangle area; + final long endTime; + + public Noise(Rectangle area, long endTime) { + this.area = area; + this.endTime = endTime; + } + + @Override + public void draw() { + if (endTime >= System.currentTimeMillis()) { + for (int y = area.y; y < (area.y + area.height); y++) { + for (int x = area.x; x < (area.x + area.width); x++) { + double random = Math.random(); + if (random < 0.25) { + setLEDInternal(x, y, Colour.Red); + } else if (random < 0.5) { + setLEDInternal(x, y, Colour.Yellow); + } else if (random < 0.75) { + setLEDInternal(x, y, Colour.Green); + } else { + setLEDInternal(x, y, Colour.None); + } + } + } + } else { + stop(); + } + } + } + + public boolean drawRectCenter(int x, int y, int height, int width, Colour colour) { + if (height < 0) { + height = 0; + } + if (width < 0) { + width = 0; + } + if (height == 0 && width == 0) { + return setLEDInternal(x, y, colour); + } + + boolean doneSomething = false; + int widthEx = 0; + int heightEx = 0; + + do { + if (setLEDInternal((x - (height / 2)) + widthEx, y - (height / 2), colour)) { + doneSomething = true; + } + if (setLEDInternal(x - (height / 2), (y - (height / 2)) + heightEx, colour)) { + doneSomething = true; + } + if (setLEDInternal((x - (height / 2)) + widthEx, y + (height / 2), colour)) { + doneSomething = true; + } + if (setLEDInternal(x + (height / 2), (y - (height / 2)) + heightEx, colour)) { + doneSomething = true; + } + if (heightEx < height) { + heightEx++; + } + if (widthEx < width) { + widthEx++; + } + + } while (height >= 0 && width >= 0); + + return doneSomething; + } + + public boolean drawRectInternal(int x, int y, int width, int height, Colour colour) { + if (height < 0) { + height = 0; + } + if (width < 0) { + width = 0; + } + if (height == 0 && width == 0) { + return setLEDInternal(x, y, colour); + } + + boolean doneSomething = false; + int heightEx = 0; + int widthEx = 0; + boolean finished = true; + do { + if (setLEDInternal(x + widthEx, y, colour)) { + doneSomething = true; + } + if (setLEDInternal(x, y + heightEx, colour)) { + doneSomething = true; + } + if (setLEDInternal(x + widthEx, y + height, colour)) { + doneSomething = true; + } + if (setLEDInternal(x + width, y + heightEx, colour)) { + doneSomething = true; + } + if (heightEx < height) { + finished = false; + heightEx++; + } + if (widthEx < width) { + finished = false; + widthEx++; + } + } while (!finished); + + return doneSomething; + } + + public boolean drawEllipse(int x, int y, int radx, int rady, Colour colour) { + if (radx == 0 && rady == 0) { + return setLEDInternal(x, y, colour); + } + + boolean doneSomething = false; + + int dx = 0, dy = rady; /* first quadrant from top left to bottom right */ + int a2 = radx * radx, b2 = rady * rady; + int err = b2 - (2 * rady - 1) * a2, e2; /* error value in the first step */ + + do { + if (setLEDInternal(x + dx, y + dy, colour)) {/* I. Quadrant */ + doneSomething = true; + } + if (setLEDInternal(x - dx, y + dy, colour)) {/* II. Quadrant */ + doneSomething = true; + } + if (setLEDInternal(x - dx, y - dy, colour)) {/* III. Quadrant */ + doneSomething = true; + } + if (setLEDInternal(x + dx, y - dy, colour)) {/* IV. Quadrant */ + doneSomething = true; + } + + e2 = 2 * err; + if (e2 < (2 * dx + 1) * b2) { + dx++; + err += (2 * dx + 1) * b2; + } + if (e2 > -(2 * dy - 1) * a2) { + dy--; + err -= (2 * dy - 1) * a2; + } + } while (dy >= 0); + + while (dx++ < radx) { /* correction for flat ellipses (b=1) */ + if (setLEDInternal(x + dx, y, colour)) { + doneSomething = true; + } + if (setLEDInternal(x - dx, y, colour)) { + doneSomething = true; + } + } + return doneSomething; + } + + public boolean drawCircle(double x, double y, double radius, Colour colour) { + boolean drawnSomething = false; + int px; + int py; + for (double i = 0; i < 360; i++) { + px = (int) Math.round(x + (radius * Math.cos(i))); + py = (int) Math.round(y + (radius * Math.sin(i))); + + if (x >= 0 && x <= 8 && y >= 0 && y <= 19) { + if (setLEDInternal(px, py, colour)) { + drawnSomething = true; + } + } + } + return drawnSomething; + } + + public boolean fillRectInternal(int x, int y, int height, int width, Colour colour) { + boolean doneSomething = false; + for (int px = x; px < (x + height); px++) { + + for (int py = y; py < (y + width); py++) { + if (setLEDInternal(px, py, colour)) { + doneSomething = true; + } + } + } + + return doneSomething; + + } + + private void calculateRawLeds() { + // First clear everything + Arrays.fill(rawLeds, (byte) 0); + // First loop through the 5 easy arrays + for (int grid = 0; grid < 5; grid++) { + for (int x = 0; x < 8; x++) { + for (int y = 0; y < 8; y++) { + if ((y % 2) == 0) { + if (leds[1 + x][(grid * 4) + (y / 2)] == Colour.Green || leds[1 + x][(grid * 4) + (y / 2)] == Colour.Yellow) { + rawLeds[(grid * 8) + x] = (byte) (rawLeds[(grid * 8) + x] | (1 << (7 - y))); + } + } else { + if (leds[1 + x][(grid * 4) + (y / 2)] == Colour.Red || leds[1 + x][(grid * 4) + (y / 2)] == Colour.Yellow) { + rawLeds[(grid * 8) + x] = (byte) (rawLeds[(grid * 8) + x] | (1 << (7 - y))); + } + } + } + } + } + + /* + * for (int y = 0; y <= 18; y++) { for (int x = 0; x <= 6; x++) { if ((y + * % 2) == 0) { if (leds[x][y] == Colour.Green || leds[x][y] == + * Colour.Yellow) { rawLeds[(int) Math.floor(y / 4)] = (byte) + * (rawLeds[(int) Math .floor(y / 4)] | (1 << (7 - x))); } } else { if + * (leds[x][y] == Colour.Red || leds[x][y] == Colour.Yellow) { + * rawLeds[(int) Math.floor(y / 4)] = (byte) (rawLeds[(int) Math + * .floor(y / 4)] | (1 << (7 - x))); } } } } + */ + /* + * int x = 7; // TODO: Complete and test, or rethink the following? for + * (int startY = 0; startY <= 4; startY++) { for (int y = 0; y <= 3; + * y++) { if (leds[x][startY + (y * 4)] == Colour.Red || leds[x][startY + * + (y * 4)] == Colour.Yellow) { rawLeds[(5 * 8) + startY] = (byte) + * (rawLeds[(int) Math .floor(y / 4)] | (1 << (7 - x))); } if + * (leds[x][y] == Colour.Green || leds[x][y] == Colour.Yellow) { + * rawLeds[(int) Math.floor(y / 4)] = (byte) (rawLeds[(int) Math + * .floor(y / 4)] | (1 << (7 - x))); } } } + */ + } + + /* Clears the led grid */ + private void clearLEDGrid() { + for (int x = 0; x < ledWidth; x++) { + for (int y = 0; y < ledHeight; y++) { + leds[x][y] = Colour.None; + } + } + } + + private void clearButtonGrid() { + for (int x = 0; x < buttonWidth; x++) { + for (int y = 0; y < buttonHeight; y++) { + buttons[x][y] = false; + } + } + } + + private void clearEffects() { + for (Iterator iter = runningEffects.iterator(); iter.hasNext();) { + ((Effect) iter.next()).stop(); + } + } + + public void clearLEDs() { + clearLEDGrid(); + clearEffects(); + } + + public void setLEDGridIntensity(byte grid, byte intensity) { + ledGridIntensities[grid] = intensity; + } + + public void setLEDIntensities(byte intensity) { + Arrays.fill(ledGridIntensities, intensity); + } + + public void buttonPressed(int x, int y) { + buttons[x][y] = true; + buttonsChanged = true; + } + + public boolean connectToArduinos() { + try { + ledArduino.connect("COM6"); + buttonArduino.connect("COM7"); + } catch (Exception e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + HashSet serialPorts = Arduino.getAvailableSerialPorts(); + for (Iterator iter = serialPorts.iterator(); iter.hasNext();) { + CommPortIdentifier comPort = (CommPortIdentifier) iter.next(); + // HACK FOR WINDOWS TO IDENIFY PORTS LIKELY TO CONTAIN ARDUINO + // (COM10 for instance). TODO: FIX + if (comPort.getName().length() == 5) { + System.out.println(comPort.getName()); + } + try { + + System.out.println(comPort.getName()); + } catch (Exception e) { + e.printStackTrace(); + } + } + return true; + } + + private void readAccelData(String data) { + //System.out.println("Data: " + data); + String[] nums = data.split(" "); + for (int x = 1; x < 5; x++) { + // Regex expression to strip newline at end (normally just 4th) + int num = Integer.valueOf(nums[x].replaceAll("[^0-9]", "")); + if (x == 1 && num != topAccelX) { + topAccelX = num; + acccelChanged = true; + } else if (x == 2 && num != topAccelY) { + topAccelY = num; + acccelChanged = true; + } else if (x == 3 && num != bottomAccelX) { + bottomAccelX = num; + acccelChanged = true; + } else if (x == 4 && num != bottomAccelY) { + bottomAccelY = num; + acccelChanged = true; + } + } + } + + private void readButtonData(String data) { + if (data.replaceAll("[^0-9]", "").length() > 0) { + //System.out.print(data); + int num = Integer.valueOf(data.replaceAll("[^0-9]", "")); + int x = ((num - 1) % 8); + int y = (num - 1) / 8; + //System.out.println("X: " + x + " Y: " + y); + if (!(x < 0)) { + buttons[x][y] = true; + buttonsChanged = true; + } + } + } + + private void printByte(byte b) { + // System.out.println("Byte: " + b); + String str; + for (int j = 0; j < 8; j++) { + byte val = (byte) (b & (1 << (7 - j))); + // System.out.println("Val: " + val + " " + (1 << (7-j))); + if (val > 0) { + str = "1"; + } else { + str = "0"; + } + System.out.print(str); + } + } + + private void printlnByte(byte b) { + printByte(b); + System.out.println(""); + } + + public void run() { + long timeToSleep = 10; + + while (true) { + if (debugTimings) { + System.out.println("Time since last refresh: " + (System.currentTimeMillis() - lastRefresh)); + if ((System.currentTimeMillis() - lastRefresh) != 0) + System.out.println("FPS: " + (1000 / (System.currentTimeMillis() - lastRefresh))); + } + + if ((System.currentTimeMillis() - lastRefresh) > (1000 / 60)) { + if (timeToSleep > 0) { + timeToSleep--; + } + } else { + timeToSleep++; + } + if (debugTimings) + System.out.println("Sleeping: " + timeToSleep); + lastRefresh = System.currentTimeMillis(); + + // System.out.println("R"); + synchronized (leds) { + clearLEDGrid(); + // clearButtonGrid(); + long beginTimeForEffects = System.currentTimeMillis(); + synchronized (runningEffects) { // Should prevent + // ConcurrentModificationException's + // (havent managed to produce one + // though + for (Iterator iter = runningEffects.iterator(); iter.hasNext();) { + Effect ef = (Effect) iter.next(); + if (ef.stop) { + iter.remove(); + } else {// if ((ef.lastRefresh + (1000 / + // ef.refreshRate)) <= + // Systems.currentTimeMillis()) { + ef.draw(); + ef.lastRefresh = System.currentTimeMillis(); + } + } + } + // System.out.println("Effects: " + // + (System.currentTimeMillis() - beginTimeForEffects)); + } + + long beginTimeButtonIn = System.currentTimeMillis(); + if (buttonArduino.in != null) { + try { + int read; + while ((read = buttonArduino.read()) != -1) { + // System.out.print((char) read); + if ((char) read == 'A') { + String str = ""; + // StringBuilder sb = new StringBuilder(20); + while ((read = buttonArduino.read()) != '\n') { + str = str + String.valueOf((char) read); + // sb.append((char) read); + } + // System.out.println(""); + readAccelData(str); + // String[] nums = sbString.trim().split(" "); + // System.out.println(nums); + // for (int x=0; x<4; x++) { + // System.out.println(nums[x]); + // } + + } else if ((char) read == 'B') { + String str = ""; + // StringBuilder sb = new StringBuilder(20); + while ((read = buttonArduino.read()) != '\n') { + str = str + String.valueOf((char) read); + // sb.append((char) read); + } + // System.out.println(""); + readButtonData(str); + + } else { + // System.out.println((char) read); + } + // System.out.print("|"); + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + if (debugTimings) + System.out.println("Button: " + (System.currentTimeMillis() - beginTimeButtonIn)); + + calculateRawLeds(); + String str; + for (int i = 0; i < (6 * 8); i++) { + // printByte(rawLeds[i]); + } + // System.out.println(""); + + // Arrays.fill(rawLeds, (byte) -42); + if (ledArduino.out != null) { + // calculateRawLeds(); + try { + ledArduino.write(((byte) 108)); + ledArduino.write(rawLeds); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + + if (ledsChanged) { + LEDListener[] LEDListeners = ledListenerList.getListeners(LEDListener.class); + for (int i = 0; i < ledListenerList.getListenerCount(); i++) { + LEDListeners[i].LEDchanged(); + } + } + + if (buttonsChanged) { + ButtonListener[] buttonListeners = buttonListenerList.getListeners(ButtonListener.class); + for (int i = 0; i < buttonListenerList.getListenerCount(); i++) { + for (int x = 0; x < buttonWidth; x++) { + for (int y = 0; y < buttonHeight; y++) { + if (buttons[x][y]) { + buttonListeners[i].buttonPressed(x, y); + buttonListeners[i].contact(new Contact(System.currentTimeMillis(), x, y, bottomAccelX)); + } + } + } + } + } + + clearButtonGrid(); + + try { + Thread.sleep(timeToSleep); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } +} -- cgit v1.2.3