Java tutorial
/* * 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; } } }