org.mule.module.blink1.Blink1Module.java Source code

Java tutorial

Introduction

Here is the source code for org.mule.module.blink1.Blink1Module.java

Source

/*
 * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
 *
 * The software in this package is published under the terms of the CPAL v1.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE.md file.
 * 
 */

package org.mule.module.blink1;

import java.awt.Color;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;

import jwalker.ColorUtil;

import org.apache.commons.lang.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mule.api.MuleRuntimeException;
import org.mule.api.annotations.Category;
import org.mule.api.annotations.Module;
import org.mule.api.annotations.Processor;
import org.mule.api.annotations.lifecycle.Start;
import org.mule.api.annotations.param.Default;
import org.mule.api.annotations.param.Optional;
import org.mule.config.i18n.MessageFactory;
import org.mule.util.IOUtils;
import org.mule.util.StringUtils;

import thingm.blink1.Blink1;

/**
 * A connector for the blink(1) device. The Blink(1) is a small USB LED light that
 * can change color and cycle through color patterns.
 * <p/>
 * {@sample.xml ../../../doc/blink1-connector.xml.sample blink1:config}
 * 
 * @author MuleSoft Inc.
 */
@Module(name = "blink1", schemaVersion = "1.0", friendlyName = "Blink(1)", minMuleVersion = "3.4.0", description = "Blink(1) Connector")
@Category(name = "org.mule.tooling.ui.modules.core.miscellaneous", description = "Miscellaneous")
public class Blink1Module {
    public static final int MAX_PATTERN_POSITION = 11;

    private static final Log LOGGER = LogFactory.getLog(Blink1Module.class);
    private static final int FAILED = -1;
    private static final String PROJECT_ISSUES_MESSAGE = "Please report this issue at https://github.com/mulesoft/blink1-connector/issues";
    private static final Map<String, Color> COLOR_MAP;

    static {
        COLOR_MAP = loadColorMap();
    }

    private static Map<String, Color> loadColorMap() {
        try {
            final Map<String, Color> colorMap = new HashMap<String, Color>();
            for (final Field f : Color.class.getFields()) {
                if (f.getType() == Color.class) {
                    final Color c = (Color) f.get(null);
                    colorMap.put(f.getName().toUpperCase(), c);
                }
            }
            return colorMap;
        } catch (final IllegalAccessException iae) {
            throw new RuntimeException(iae);
        }
    }

    private Blink1 blink1;

    @Start
    public void loadNativeLibrary() throws IOException {
        final String osName = System.getProperty("os.name");
        final String osArch = System.getProperty("os.arch");
        final String nativeLibraryPath = selectNativeLibrary(osName, osArch);
        LOGGER.info("Loading native blink(1) library: " + nativeLibraryPath);

        // extract the blink library file relevant for the OS / architecture
        final File library = File.createTempFile("blink1.", ".lib");
        library.deleteOnExit();

        final FileOutputStream fos = new FileOutputStream(library);
        IOUtils.copy(
                Thread.currentThread().getContextClassLoader().getResourceAsStream("native/" + nativeLibraryPath),
                fos);

        IOUtils.closeQuietly(fos);

        // load the blink library
        try {
            System.load(library.getAbsolutePath());
        } catch (final Throwable t) {
            throw new RuntimeException("Failed to load native library: " + nativeLibraryPath + " for environment: "
                    + osName + " - " + osArch + " " + PROJECT_ISSUES_MESSAGE, t);
        }

        blink1 = new Blink1();
    }

    private String selectNativeLibrary(final String osName, final String osArch) {

        if (StringUtils.startsWithIgnoreCase(osName, "linux")) {
            if (StringUtils.startsWithIgnoreCase(osArch, "arm")) {
                return "linux-arm/libBlink1.so";
            } else {
                return "linux/libBlink1.so";
            }
        } else if (StringUtils.startsWithIgnoreCase(osName, "mac")) {
            return "mac/libBlink1.jnilib";
        } else if (StringUtils.startsWithIgnoreCase(osName, "windows")) {
            return "windows/Blink1.dll";
        } else {
            throw new RuntimeException(
                    "Unsupported environment: " + osName + " - " + osArch + " " + PROJECT_ISSUES_MESSAGE);
        }
    }

    /**
     * Get the status of all the connected blink(1) devices.
     * <p/>
     * {@sample.xml ../../../doc/blink1-connector.xml.sample blink1:get-statuses}
     * 
     * @return a {@link List} of {@link Blink1Status}, possibly empty but never null.
     */
    @Processor
    public List<Blink1Status> getStatuses() {
        final List<Blink1Status> statuses = new ArrayList<Blink1Status>();

        final String[] devicePaths = blink1.getDevicePaths();
        final String[] deviceSerials = blink1.getDeviceSerials();

        for (int i = 0; i < blink1.getCount(); i++) {
            blink1.openById(i);
            final int firmwareVersion = blink1.getFirmwareVersion();
            blink1.close();

            final Blink1Status status = new Blink1Status(i, devicePaths[i], deviceSerials[i], firmwareVersion);
            statuses.add(status);
        }

        return statuses;
    }

    /**
     * Set the color.
     * <p/>
     * {@sample.xml ../../../doc/blink1-connector.xml.sample blink1:set-color}
     * 
     * @param deviceId unique identifier of the device.
     * @param color either a {@link Color} name or an HTML color. The <code>on</code> and
     *            <code>off</code> aliases are recognized as 'WHITE' and
     *            'BLACK'. Named colors supported are:
     *              /**
     * <ul>
     *     <li>'White'</li>
     *     <li>'LightGray'</li>
     *     <li>'Gray'</li>
     *     <li>'DarkGray'</li>
     *     <li>'Black'</li>
     *     <li>'Red'</li>
     *     <li>'Pink'</li>
     *     <li>'Orange'</li>
     *     <li>'Yellow'</li>
     *     <li>'Green'</li>
     *     <li>'Magenta'</li>
     *     <li>'Cyan'</li>
     *     <li>'Blue'</li>
     * </ul>
     */
    @Processor
    public void setColor(final int deviceId, final String color) {
        final Color c = parseColor(color);
        runOnBlink(deviceId, new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return blink1.setRGB(c);
            }
        });
    }

    /**
     * Fade-in the color.
     * <p/>
     * {@sample.xml ../../../doc/blink1-connector.xml.sample blink1:fade-to-color}
     * 
     * @param deviceId unique identifier of the device.
     * @param duration time in milliseconds that the fading lasts.
     * @param color either a {@link Color} name or an HTML color.
     */
    @Processor
    public void fadeToColor(final int deviceId, final int duration, final String color) {
        final Color c = parseColor(color);
        runOnBlink(deviceId, new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return blink1.fadeToRGB(duration, c);
            }
        });
    }

    /**
     * Clear the pattern stored in a blink(1) device.
     * <p/>
     * {@sample.xml ../../../doc/blink1-connector.xml.sample blink1:clear-pattern}
     * 
     * @param deviceId unique identifier of the device.
     */
    @Processor
    public void clearPattern(final int deviceId) {
        final List<PatternEntry> entries = Collections.nCopies(MAX_PATTERN_POSITION + 1,
                new PatternEntry(0, "black"));
        storePattern(deviceId, entries, 0);
    }

    /**
     * Store a pattern.
     * <p/>
     * {@sample.xml ../../../doc/blink1-connector.xml.sample blink1:store-pattern}
     * 
     * @param deviceId unique identifier of the device.
     * @param entries definition of the pattern.
     * @param startPosition position at which the pattern must be stored. Between 0 and
     *            {@link Blink1Module#MAX_PATTERN_POSITION}.
     */
    @Processor
    public void storePattern(final int deviceId, final List<PatternEntry> entries,
            @Optional @Default("0") final int startPosition) {
        Validate.notEmpty(entries, "A least one pattern entry must be provided");
        validateStartPosition(startPosition);

        final int endPosition = startPosition + entries.size() - 1;
        Validate.isTrue(endPosition <= MAX_PATTERN_POSITION,
                "Sequence too long: the end position must be less than or equal to " + MAX_PATTERN_POSITION
                        + " but was: " + endPosition);

        for (int i = 0; i < entries.size(); i++) {
            final PatternEntry entry = entries.get(i);
            final int position = startPosition + i;

            runOnBlink(deviceId, new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    return blink1.writePatternLine(entry.getDuration(), entry.getParsedColor(), position);

                }
            });
        }
    }

    /**
     * Start playing the stored pattern.
     * <p/>
     * {@sample.xml ../../../doc/blink1-connector.xml.sample blink1:start-pattern}
     * 
     * @param deviceId unique identifier of the device.
     * @param position where to start playing the pattern. Between 0 and
     *            {@link Blink1Module#MAX_PATTERN_POSITION}.
     */
    @Processor
    public void startPattern(final int deviceId, @Optional @Default("0") final int position) {
        validateStartPosition(position);

        runOnBlink(deviceId, new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return blink1.play(true, position);
            }
        });
    }

    /**
     * Stop playing the stored pattern.
     * <p/>
     * {@sample.xml ../../../doc/blink1-connector.xml.sample blink1:stop-pattern}
     * 
     * @param deviceId unique identifier of the device.
     */
    @Processor
    public void stopPattern(final int deviceId) {
        runOnBlink(deviceId, new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return blink1.play(false, 0);
            }
        });
    }

    /**
     * Schedule playing the server down pattern.
     * <p/>
     * {@sample.xml ../../../doc/blink1-connector.xml.sample blink1:start-server-down}
     * 
     * @param deviceId unique identifier of the device.
     * @param delay time in milliseconds after which the pattern will play.
     */
    @Processor
    public void startServerDown(final int deviceId, @Optional @Default("0") final int delay) {
        validateDelay(delay);

        runOnBlink(deviceId, new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return blink1.serverdown(true, delay);
            }
        });
    }

    /**
     * Stop playing the server down pattern or cancel its scheduled execution.
     * <p/>
     * {@sample.xml ../../../doc/blink1-connector.xml.sample blink1:stop-server-down}
     * 
     * @param deviceId unique identifier of the device.
     */
    @Processor
    public void stopServerDown(final int deviceId) {
        runOnBlink(deviceId, new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return blink1.serverdown(false, 0);
            }
        });
    }

    private void validateStartPosition(final int startPosition) {
        Validate.isTrue(startPosition >= 0 && startPosition <= 12,
                "Start position must be between 0 and 12 included");
    }

    private void validateDelay(final int delay) {
        Validate.isTrue(delay >= 0, "delay must be a positive integer");
    }

    private void runOnBlink(final int deviceId, final Callable<Integer> action) {
        final int openResult = blink1.openById(deviceId);

        if (openResult == FAILED) {
            throw new MuleRuntimeException(
                    MessageFactory.createStaticMessage("Failed to open blink(1) device ID: " + deviceId));
        }

        try {
            final int actionResult = action.call();
            if (actionResult == FAILED) {
                throw new MuleRuntimeException(MessageFactory
                        .createStaticMessage("Failed to execute action on blink(1) device ID: " + deviceId));
            }
        } catch (final Exception e) {
            throw new MuleRuntimeException(MessageFactory
                    .createStaticMessage("Failed to execute action on blink(1) device ID: " + deviceId), e);
        } finally {
            blink1.close();
        }
    }

    public static Color parseColor(final String colorString) {
        if (StringUtils.equalsIgnoreCase(colorString, "on")) {
            return Color.WHITE;
        } else if (StringUtils.equalsIgnoreCase(colorString, "off")) {
            return Color.BLACK;
        } else if (StringUtils.startsWith(colorString, "#")) {
            return ColorUtil.decodeHtmlColorString(colorString);
        }

        final Color color = COLOR_MAP.get(StringUtils.upperCase(colorString));
        if (color == null) {
            throw new IllegalArgumentException("Invalid color: " + colorString);
        } else {
            return color;
        }
    }
}