com.yarg.animatronics.datamodel.PwmMotor.java Source code

Java tutorial

Introduction

Here is the source code for com.yarg.animatronics.datamodel.PwmMotor.java

Source

package com.yarg.animatronics.datamodel;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

public abstract class PwmMotor {

    private ArrayList<AnimationKey> animationKeys = new ArrayList<>();
    private int pwmChannel = 1;

    /**
     * Maximum tick value to achieve maximum rotation.
     * @return Maximum tick value.
     */
    public abstract int getMaxTick();

    /**
     * Minimum tick value to achieve minimum rotation.
     * @return Minimum tick value.
     */
    public abstract int getMinTick();

    /**
     * Maximum rotation angle for the motor.
     * @return Maximum rotation angle for the motor.
     */
    public abstract double getMaxAngle();

    /**
     * Minimum rotation angle for the motor.
     * @return Minimum rotation angle for the motor.
     */
    public abstract double getMinAngle();

    /**
     * Name or ID of the motor. Used when referencing the motor for consumers."
     * @return Name or ID of the motor.
     */
    public abstract String getMotorId();

    /**
     * Calculate the number of ticks per millisecond based on the specified signaling frequency.
     * @param signalFrequencyHz The signal frequency used with the motor. Comes from PWM board.
     * @return Number of ticks per second for the subclass motor.
     */
    public static int getTicksPerMillisecond(int signalFrequencyHz) {

        int millisecondRefresh = 1000 / signalFrequencyHz;
        int ticksPerMillisecond = 4096 / millisecondRefresh;
        return ticksPerMillisecond;
    }

    /**
     * Get the channel on the PWM board that this motor is connected to.
     * @return The PWM channel for this motor.
     */
    public int getPwmChannel() {
        return pwmChannel;
    }

    /**
     * Set the channel on the PWM board that this motor is connected to.
     * @param pwmChannel PWM channel for this motor.
     */
    public void setPwmChannel(int pwmChannel) {
        this.pwmChannel = pwmChannel;
    }

    /**
     * Get the number of animation keys set for this motor.
     * @return Number of animation keys set for this motor.
     */
    public int getNumberOfKeys() {
        return animationKeys.size();
    }

    /**
     * Get the first animation key index.
     * @return First animation key index, or -1 if there aren't any animation keys set.
     */
    public int getFirstAnimationKeyIndex() {
        if (animationKeys.size() == 0) {
            return -1;
        }

        return 0;
    }

    /**
     * Get the last animation key index.
     * @return Last animation key index, or -1 if there aren't any animation keys set.
     */
    public int getLastAnimationKeyIndex() {
        return animationKeys.size() - 1;
    }

    /**
     * Get the tick value at the animation index specified.
     * @param index Animation index to retrieve tick value for.
     * @return Tick value.
     */
    public int getTickAtIndex(int index) {
        return animationKeys.get(index).getTick();
    }

    /**
     * Set the tick value at the animation index specified. Clamped to the max and min tick values defined for the
     * motor.
     * @param index Index to set the tick value for.
     * @param tick Tick value to set at the index specified.
     */
    public void setTickAtIndex(int index, int tick) {

        if (tick > getMaxTick()) {
            tick = getMaxTick();
        } else if (tick < getMinTick()) {
            tick = getMinTick();
        }

        animationKeys.get(index).setTick(tick);
    }

    /**
     * Get the time in milliseconds of the animation index specified.
     * @param index Animation index to retrieve tick value for.
     * @return Time in milliseconds from the start of the animation sequence.
     */
    public long getTimeAtIndex(int index) {
        return animationKeys.get(index).getTime();
    }

    /**
     * Set the time of the animation index specified. The time specified must be greater than the previous animation
     * key's time and less than the next animation key's time.
     * @param index Animation index to set the time for.
     * @param time Time, in milliseconds, from the start of the animation (0) to set this animtion key time to.
     */
    public void setTimeAtIndex(int index, long time) {

        if (index > 0 && getTimeAtIndex(index - 1) >= time) {
            throw new IllegalArgumentException("Time specified must be greater than the time of the previous key.");
        } else if (index < (animationKeys.size() - 1) && getTimeAtIndex(index + 1) <= time) {
            throw new IllegalArgumentException("Time specified must be less than the time of the next key.");
        }

        animationKeys.get(index).setTime(time);
    }

    /**
     * Set the rotation angle for the animation key at specified index. Clamped to the max and min angle values defined
     * for the motor.
     * @param index Animation key index to set rotation for.
     * @param angle Angle to set.
     */
    public void setAngleAtIndex(int index, double angle) {

        if (angle > getMaxAngle()) {
            angle = getMaxAngle();
        } else if (angle < getMinAngle()) {
            angle = getMinAngle();
        }

        int ticks = convertAngleToTicks(angle);
        setTickAtIndex(index, ticks);
    }

    /**
     * Get the angle of rotation at the specified animation key index.
     * @param index Animation key index to get rotation for.
     * @return Angle of rotation set at the specified animation key index.
     */
    public double getAngleAtIndex(int index) {

        int ticks = getTickAtIndex(index);
        return convertTicksToAngle(ticks);
    }

    /**
     * Add a new animation key to the end of the animation sequence. Animation key created will
     * have the same tick value as the previous key and a time one second after the previous key.
     * @return Index of the newly created animation key.
     */
    public int addAnimationKey() {

        int tick = 0;
        long time = 0L;

        if (animationKeys.size() > 0) {
            tick = animationKeys.get(animationKeys.size() - 1).getTick();
            time = animationKeys.get(animationKeys.size() - 1).getTime() + 1000L;
        }

        AnimationKey newKey = new AnimationKey(tick, time);
        animationKeys.add(newKey);
        return (animationKeys.size() - 1);
    }

    /**
     * Add a new animation key at the specified index. If index is between two keys the
     * key created will have a tick and time values half way between the nearest neighbors.
     * If the index is greater than the size of the animation key sequence a new key will be
     * added on the end. If the index is less than or equal to 0 index 0 will be returned and
     * no new key will be added.
     * @param index Index to create animation key at.
     * @return Index of the key created.
     * @throws IllegalArgumentException.
     */
    public int addAnimationKeyAtIndex(int index) {

        if (index < 0) {
            throw new IllegalArgumentException("Index must be a positive value.");
        }

        if (index >= animationKeys.size()) {
            return addAnimationKey();
        } else if (index <= 0) {
            return 0;
        }

        int tick = 0;
        long time = 0L;

        // Calculate tick between nearest neighbors.
        // Calculate time between nearest neighbors.
        int nextTick = animationKeys.get(index).getTick();
        int previousTick = animationKeys.get(index - 1).getTick();
        tick = (nextTick - previousTick) / 2 + previousTick;

        long nextTime = animationKeys.get(index).getTime();
        long previousTime = animationKeys.get(index - 1).getTime();
        time = (nextTime - previousTime) / 2 + previousTime;

        if (time <= previousTime || time >= nextTime) {
            throw new IllegalArgumentException("Unable to subdivide time any further at the index specified.");
        }

        AnimationKey newKey = new AnimationKey(tick, time);
        animationKeys.add(index, newKey);
        return index;
    }

    /**
     * Remove the animation key at the specified index.
     * @param index
     */
    public void removeKeyAtIndex(int index) {
        animationKeys.remove(index);
    }

    /**
     * Convert angle to ticks for the motor defined in the subclass.
     * @param angle Angle to convert.
     * @return Angle converted to ticks.
     */
    protected int convertAngleToTicks(double angle) {

        double angleDelta = getMaxAngle() - getMinAngle();
        int tickDelta = getMaxTick() - getMinTick();
        double fromMinAngleDelta = angle - getMinAngle();
        int ticksConverted = (int) (tickDelta * fromMinAngleDelta / angleDelta + getMinTick());
        return ticksConverted;
    }

    /**
     * Convert ticks to angle for the motor defined in the subclass.
     * @param ticks Ticks to convert.
     * @return Ticks converted to degrees.
     */
    protected double convertTicksToAngle(int ticks) {

        int fromMinTickDelta = ticks - getMinTick();
        int tickDelta = getMaxTick() - getMinTick();
        double angleDelta = getMaxAngle() - getMinAngle();
        double angleConverted = fromMinTickDelta * angleDelta / tickDelta + getMinAngle();
        return angleConverted;
    }

    /**
     * Return a copy of the animation keys list for this motor.
     * @return Copy of the animation keys list for this motor.
     */
    protected List<AnimationKey> getAnimationKeys() {
        ArrayList<AnimationKey> copyOfAnimationKeys = new ArrayList<>();
        copyOfAnimationKeys.addAll(animationKeys);
        return copyOfAnimationKeys;
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder(33, 19).append(animationKeys).append(pwmChannel).append(getMaxAngle())
                .append(getMinAngle()).append(getMaxTick()).append(getMinTick()).append(getMotorId()).toHashCode();
    }

    @Override
    public boolean equals(Object obj) {

        if (this == obj) {
            return true;
        } else if (!(obj instanceof PwmMotor)) {
            return false;
        }

        PwmMotor compareObj = (PwmMotor) obj;
        return new EqualsBuilder().append(getAnimationKeys(), compareObj.getAnimationKeys())
                .append(getPwmChannel(), compareObj.getPwmChannel()).append(getMaxAngle(), compareObj.getMaxAngle())
                .append(getMinAngle(), compareObj.getMinAngle()).append(getMaxTick(), compareObj.getMaxTick())
                .append(getMinTick(), compareObj.getMinTick()).append(getMotorId(), compareObj.getMotorId())
                .isEquals();
    }

    @Override
    public String toString() {

        return new ToStringBuilder(this).append("Animation Keys", getAnimationKeys())
                .append("Pwm Channel", getPwmChannel()).append("Max Angle", getMaxAngle())
                .append("Min Angle", getMinAngle()).append("Max Tick", getMaxTick())
                .append("Min Tick", getMinTick()).append("Motor Id", getMotorId()).toString();
    }

}