// -*- mode: java; c-basic-offset: 2; -*- // Copyright 2016 MIT, All rights reserved // Released under the Apache License, Version 2.0 // http://www.apache.org/licenses/LICENSE-2.0 package com.google.appinventor.components.runtime; import com.google.appinventor.components.annotations.DesignerComponent; import com.google.appinventor.components.annotations.DesignerProperty; import com.google.appinventor.components.annotations.PropertyCategory; import com.google.appinventor.components.annotations.SimpleEvent; import com.google.appinventor.components.annotations.SimpleFunction; import com.google.appinventor.components.annotations.SimpleObject; import com.google.appinventor.components.annotations.SimpleProperty; import com.google.appinventor.components.common.ComponentCategory; import com.google.appinventor.components.common.PropertyTypeConstants; import com.google.appinventor.components.common.YaVersion; import com.google.appinventor.components.runtime.util.ErrorMessages; import android.os.Handler; /** * A component that provides a high-level interface to a gyro sensor on a LEGO * MINDSTORMS EV3 robot. * * @author jerry73204@gmail.com (jerry73204) * @author spaded06543@gmail.com (Alvin Chang) */ @DesignerComponent(version = YaVersion.EV3_GYROSENSOR_COMPONENT_VERSION, description = "A component that provides a high-level interface to a gyro sensor on a " + "LEGO MINDSTORMS EV3 robot.", category = ComponentCategory.LEGOMINDSTORMS, nonVisible = true, iconName = "images/legoMindstormsEv3.png") @SimpleObject public class Ev3GyroSensor extends LegoMindstormsEv3Sensor implements Deleteable { private static final int DELAY_MILLISECONDS = 50; private static final int SENSOR_TYPE = 32; private static final int SENSOR_MODE_ANGLE = 0; private static final int SENSOR_MODE_RATE = 1; private static final String SENSOR_MODE_ANGLE_STRING = "angle"; private static final String SENSOR_MODE_RATE_STRING = "rate"; private static final String DEFAULT_SENSOR_MODE_STRING = SENSOR_MODE_ANGLE_STRING; private Handler eventHandler; private final Runnable sensorValueChecker; private int mode = SENSOR_MODE_ANGLE; private String modeString = SENSOR_MODE_ANGLE_STRING; private double previousValue = -1.0; private boolean sensorValueChangedEventEnabled = false; /** * Creates a new Ev3GyroSensor component. */ public Ev3GyroSensor(ComponentContainer container) { super(container, "Ev3GyroSensor"); eventHandler = new Handler(); sensorValueChecker = new Runnable() { public void run() { String functionName = ""; if (bluetooth != null && bluetooth.IsConnected()) { double currentValue = getSensorValue(functionName); if (previousValue < 0.0) { previousValue = currentValue; eventHandler.postDelayed(this, DELAY_MILLISECONDS); return; } // trigger events according to the conditions if (mode == SENSOR_MODE_RATE && Math.abs(currentValue) >= 1.0) SensorValueChanged(currentValue); else if (mode == SENSOR_MODE_ANGLE && Math.abs(currentValue - previousValue) >= 1.0) SensorValueChanged(currentValue); previousValue = currentValue; } eventHandler.postDelayed(this, DELAY_MILLISECONDS); } }; eventHandler.post(sensorValueChecker); Mode(DEFAULT_SENSOR_MODE_STRING); SensorValueChangedEventEnabled(false); } /** * Returns the current angle or rotation speed based on current mode, * or -1 if the value cannot be read from sensor. */ @SimpleFunction(description = "Returns the current angle or rotation speed based on current mode, " + "or -1 if the value cannot be read from sensor.") public double GetSensorValue() { String functionName = ""; return getSensorValue(functionName); } /** * Specifies the mode of the sensor. */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_LEGO_EV3_GYRO_SENSOR_MODE, defaultValue = DEFAULT_SENSOR_MODE_STRING) @SimpleProperty public void Mode(String modeName) { String functionName = "Mode"; try { setMode(modeName); } catch(IllegalArgumentException e) { form.dispatchErrorOccurredEvent(this, functionName, ErrorMessages.ERROR_EV3_ILLEGAL_ARGUMENT, functionName); } } /** * Returns the mode of the sensor. */ @SimpleProperty(description = "The sensor mode can be a text constant of either \"rate\" or \"angle\", " + "which correspond to SetAngleMode or SetRateMode respectively.", category = PropertyCategory.BEHAVIOR) public String Mode() { return modeString; } /** * Make the sensor read the angle. */ @SimpleFunction(description = "Measures the orientation of the sensor.") public void SetAngleMode() { setMode(SENSOR_MODE_ANGLE_STRING); } /** * Make the sensor read the rotation rate. */ @SimpleFunction(description = "Measures the angular velocity of the sensor.") public void SetRateMode() { setMode(SENSOR_MODE_RATE_STRING); } /** * Returns whether the SensorValueChanged event should fire when the sensor value changed. */ @SimpleProperty(description = "Whether the SensorValueChanged event should fire when the sensor value changed.", category = PropertyCategory.BEHAVIOR) public boolean SensorValueChangedEventEnabled() { return sensorValueChangedEventEnabled; } /** * Returns whether the SensorValueChanged event should fire when the sensor value changed. */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN, defaultValue = "False") @SimpleProperty public void SensorValueChangedEventEnabled(boolean enabled) { sensorValueChangedEventEnabled = enabled; } /** * Called then the sensor value changed. */ @SimpleEvent(description = "Called then the sensor value changed.") public void SensorValueChanged(double sensorValue) { EventDispatcher.dispatchEvent(this, "SensorValueChanged", sensorValue); } private double getSensorValue(String functionName) { return readInputSI(functionName, 0, sensorPortNumber, SENSOR_TYPE, mode); } private void setMode(String newModeString) { if (SENSOR_MODE_ANGLE_STRING.equals(newModeString)) mode = SENSOR_MODE_ANGLE; else if (SENSOR_MODE_RATE_STRING.equals(newModeString)) mode = SENSOR_MODE_RATE; else throw new IllegalArgumentException(); this.modeString = newModeString; } // Deleteable implementation @Override public void onDelete() { super.onDelete(); } }