diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 7f68460d..00000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/java/com/schneewittchen/rosandroid/widgets/button/ButtonData.java b/app/src/main/java/com/schneewittchen/rosandroid/widgets/button/ButtonData.java index 04894eaa..1428f513 100644 --- a/app/src/main/java/com/schneewittchen/rosandroid/widgets/button/ButtonData.java +++ b/app/src/main/java/com/schneewittchen/rosandroid/widgets/button/ButtonData.java @@ -1,5 +1,7 @@ package com.schneewittchen.rosandroid.widgets.button; +import android.util.Log; + import com.schneewittchen.rosandroid.model.entities.widgets.BaseEntity; import com.schneewittchen.rosandroid.model.repositories.rosRepo.node.BaseData; @@ -28,6 +30,7 @@ public ButtonData(boolean pressed) { @Override public Message toRosMessage(Publisher publisher, BaseEntity widget) { + Log.d("name", widget.name); std_msgs.Bool message = (Bool) publisher.newMessage(); message.setData(pressed); return message; diff --git a/app/src/main/java/com/schneewittchen/rosandroid/widgets/button/ButtonView.java b/app/src/main/java/com/schneewittchen/rosandroid/widgets/button/ButtonView.java index 1fb54780..626d93f9 100644 --- a/app/src/main/java/com/schneewittchen/rosandroid/widgets/button/ButtonView.java +++ b/app/src/main/java/com/schneewittchen/rosandroid/widgets/button/ButtonView.java @@ -11,6 +11,8 @@ import android.util.AttributeSet; import android.view.MotionEvent; + + import com.schneewittchen.rosandroid.R; import com.schneewittchen.rosandroid.ui.views.widgets.PublisherWidgetView; diff --git a/app/src/main/java/com/schneewittchen/rosandroid/widgets/directional/DirectionalData.java b/app/src/main/java/com/schneewittchen/rosandroid/widgets/directional/DirectionalData.java new file mode 100644 index 00000000..e886743a --- /dev/null +++ b/app/src/main/java/com/schneewittchen/rosandroid/widgets/directional/DirectionalData.java @@ -0,0 +1,81 @@ +package com.schneewittchen.rosandroid.widgets.directional; + +import com.schneewittchen.rosandroid.model.entities.widgets.BaseEntity; +import com.schneewittchen.rosandroid.model.repositories.rosRepo.node.BaseData; +import com.schneewittchen.rosandroid.widgets.joystick.JoystickEntity; + +import org.ros.internal.message.Message; +import org.ros.node.topic.Publisher; + +import geometry_msgs.Twist; +import geometry_msgs.Vector3; + +import android.util.Log; + + +/** + * TODO: Description + * + * @author Nico Studt + * @version 1.0.0 + * @created on 17.03.20 + * @updated on 17.03.20 + * @modified by + */ +public class DirectionalData extends BaseData { + + public static final String TAG = DirectionalData.class.getSimpleName(); + + private double getSpeed(String sense, double value) { + if (sense.equals("Positive") && value >= 0) { + return value; + } else if (sense.equals("Positive") && value < 0) { + return -value; + } else if (value < 0) { + return value; + } else { + return -value; + } + } + + @Override + public Message toRosMessage(Publisher publisher, BaseEntity widget) { + + DirectionalEntity dirWidget = (DirectionalEntity) widget; + + Log.d("name", dirWidget.name); + Log.d("topic", dirWidget.topic.name); + Log.d("position", Integer.toString(dirWidget.posX)); + Log.d("axis", dirWidget.axis); + Log.d("dir", dirWidget.direction); + Log.d("sense", dirWidget.sense); + + geometry_msgs.Twist message = (Twist) publisher.newMessage(); + Vector3 dirVector; + + double speed = 0.3; + if (dirWidget.direction.equals(("Linear"))) { + dirVector = message.getLinear(); + } else { + dirVector = message.getAngular(); + } + if (dirWidget.speed > 0) { + speed = dirWidget.speed; + } + String sense = dirWidget.sense; + + switch (dirWidget.axis) { + case "X": + dirVector.setX(getSpeed(sense, speed)); + break; + case "Y": + dirVector.setY(getSpeed(sense, speed)); + break; + case "Z": + dirVector.setZ(getSpeed(sense, speed)); + break; + } + + return message; + } +} diff --git a/app/src/main/java/com/schneewittchen/rosandroid/widgets/directional/DirectionalDetailVH.java b/app/src/main/java/com/schneewittchen/rosandroid/widgets/directional/DirectionalDetailVH.java new file mode 100644 index 00000000..58e3d9fb --- /dev/null +++ b/app/src/main/java/com/schneewittchen/rosandroid/widgets/directional/DirectionalDetailVH.java @@ -0,0 +1,111 @@ +package com.schneewittchen.rosandroid.widgets.directional; + +import android.util.Log; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.Spinner; +import android.widget.TextView; + +import com.schneewittchen.rosandroid.R; +import com.schneewittchen.rosandroid.model.entities.widgets.BaseEntity; +import com.schneewittchen.rosandroid.ui.views.details.PublisherWidgetViewHolder; +import com.schneewittchen.rosandroid.utility.Utils; +import com.schneewittchen.rosandroid.widgets.button.ButtonEntity; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +import geometry_msgs.Twist; +import std_msgs.Bool; + + +/** + * TODO: Description + * + * @author Nico Studt + * @version 1.0.2 + * @created on 13.02.20 + * @updated on 20.05.20 + * @modified by Nico Studt + * @updated on 05.11.2020 + * @modified by Nico Studt + */ +public class DirectionalDetailVH extends PublisherWidgetViewHolder { + + private EditText textText; + private EditText speedText; + private Spinner rotationSpinner; + private Spinner axisSpinner; + private Spinner senseSpinner; + private Spinner directionSpinner; + private ArrayAdapter rotationAdapter; + private ArrayAdapter axisAdapter; + private ArrayAdapter senseAdapter; + private ArrayAdapter directionAdapter; + + + @Override + public void initView(View view) { + textText = view.findViewById(R.id.btnTextTypeText); + speedText = view.findViewById(R.id.speedTextTypeText); + + rotationSpinner = view.findViewById(R.id.btnTextRotation); + axisSpinner = view.findViewById(R.id.btnTextAxis); + directionSpinner = view.findViewById(R.id.btnTextDirection); + senseSpinner = view.findViewById(R.id.btnTextSense); + + // Init spinners + rotationAdapter = ArrayAdapter.createFromResource(view.getContext(), + R.array.button_rotation, android.R.layout.simple_spinner_dropdown_item); + rotationSpinner.setAdapter(rotationAdapter); + + axisAdapter = ArrayAdapter.createFromResource(view.getContext(), + R.array.directional_twist_axis, android.R.layout.simple_spinner_dropdown_item); + axisSpinner.setAdapter(axisAdapter); + + directionAdapter = ArrayAdapter.createFromResource(view.getContext(), + R.array.directional_twist_dir, android.R.layout.simple_spinner_dropdown_item); + directionSpinner.setAdapter(directionAdapter); + + senseAdapter = ArrayAdapter.createFromResource(view.getContext(), + R.array.directional_twist_sense, android.R.layout.simple_spinner_dropdown_item); + senseSpinner.setAdapter(senseAdapter); + } + + @Override + protected void bindEntity(BaseEntity entity) { + DirectionalEntity buttonEntity = (DirectionalEntity) entity; + + textText.setText(buttonEntity.text); + speedText.setText(String.valueOf(buttonEntity.speed)); + + String degrees = Utils.numberToDegrees(buttonEntity.rotation); + + rotationSpinner.setSelection(rotationAdapter.getPosition(degrees)); + axisSpinner.setSelection(axisAdapter.getPosition(buttonEntity.axis)); + directionSpinner.setSelection(directionAdapter.getPosition(buttonEntity.direction)); + senseSpinner.setSelection(senseAdapter.getPosition(buttonEntity.sense)); + } + + @Override + protected void updateEntity(BaseEntity entity) { + DirectionalEntity buttonEntity = (DirectionalEntity)entity; + + buttonEntity.text = textText.getText().toString(); + if (speedText.getText().length() > 0 ) { + buttonEntity.speed = Double.parseDouble(speedText.getText().toString()); + } + String degrees = rotationSpinner.getSelectedItem().toString(); + buttonEntity.rotation = Utils.degreesToNumber(degrees); + buttonEntity.axis = axisSpinner.getSelectedItem().toString(); + buttonEntity.direction = directionSpinner.getSelectedItem().toString(); + buttonEntity.sense = senseSpinner.getSelectedItem().toString(); + } + + @Override + public List getTopicTypes() { + return Collections.singletonList(Bool._TYPE); + } +} diff --git a/app/src/main/java/com/schneewittchen/rosandroid/widgets/directional/DirectionalEntity.java b/app/src/main/java/com/schneewittchen/rosandroid/widgets/directional/DirectionalEntity.java new file mode 100644 index 00000000..ec0bdeb0 --- /dev/null +++ b/app/src/main/java/com/schneewittchen/rosandroid/widgets/directional/DirectionalEntity.java @@ -0,0 +1,41 @@ +package com.schneewittchen.rosandroid.widgets.directional; + +import com.schneewittchen.rosandroid.model.entities.widgets.PublisherWidgetEntity; +import com.schneewittchen.rosandroid.model.repositories.rosRepo.message.Topic; + +import geometry_msgs.Twist; + + +/** + * TODO: Description + * + * @author Nico Studt + * @version 1.1.1 + * @created on 31.01.20 + * @updated on 10.05.20 + * @modified by Nico Studt + */ +public class DirectionalEntity extends PublisherWidgetEntity { + + public String text; + public int rotation; + public String axis; + public String direction; + public String sense; + public double speed; + + public DirectionalEntity() { + this.width = 2; + this.height = 2; + this.topic = new Topic("cmd_vel", Twist._TYPE); + this.publishRate = 20f; + this.text = "Move on"; + this.rotation = 0; + this.immediatePublish = true; + this.axis = "X"; + this.direction = "Linear"; + this.sense = "Positive"; + this.speed = 0.3; + } + +} diff --git a/app/src/main/java/com/schneewittchen/rosandroid/widgets/directional/DirectionalView.java b/app/src/main/java/com/schneewittchen/rosandroid/widgets/directional/DirectionalView.java new file mode 100644 index 00000000..f13bca56 --- /dev/null +++ b/app/src/main/java/com/schneewittchen/rosandroid/widgets/directional/DirectionalView.java @@ -0,0 +1,140 @@ +package com.schneewittchen.rosandroid.widgets.directional; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; + +import androidx.annotation.Nullable; + +import com.schneewittchen.rosandroid.R; +import com.schneewittchen.rosandroid.model.entities.widgets.BaseEntity; +import com.schneewittchen.rosandroid.ui.views.widgets.PublisherWidgetView; +import com.schneewittchen.rosandroid.widgets.button.ButtonData; + +import android.text.StaticLayout; +import android.text.TextPaint; +import android.graphics.Color; +import android.graphics.Rect; +import android.text.Layout; + +/** + * TODO: Description + * + * @author Nico Studt + * @version 1.1.0 + * @created on 18.10.19 + */ +public class DirectionalView extends PublisherWidgetView { + + public class MoveThread extends Thread { + + @Override + public void run() { + while (!Thread.currentThread().isInterrupted()) { + try { + Thread.sleep(1); + publishViewData(new DirectionalData()); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + ex.printStackTrace(); + } + } + } + } + + public static final String TAG = DirectionalView.class.getSimpleName(); + + Paint buttonPaint; + TextPaint textPaint; + StaticLayout staticLayout; + MoveThread t; + + public DirectionalView(Context context) { + super(context); + init(); + } + + public DirectionalView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(); + } + + + private void init(){ + buttonPaint = new Paint(); + buttonPaint.setColor(getResources().getColor(R.color.colorPrimary)); + buttonPaint.setStyle(Paint.Style.FILL_AND_STROKE); + + textPaint = new TextPaint(); + textPaint.setColor(Color.BLACK); + textPaint.setStyle(Paint.Style.FILL_AND_STROKE); + textPaint.setTextSize(26 * getResources().getDisplayMetrics().density); + t = new MoveThread(); + } + + private void changeState(boolean pressed) { + invalidate(); + } + + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (this.editMode) { + return super.onTouchEvent(event); + } + switch (event.getActionMasked()) { + case MotionEvent.ACTION_OUTSIDE: + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_BUTTON_RELEASE: + case MotionEvent.ACTION_UP: + buttonPaint.setColor(getResources().getColor(R.color.colorPrimary)); + changeState(false); + t.interrupt(); + t = new MoveThread(); + break; + case MotionEvent.ACTION_DOWN: + buttonPaint.setColor(getResources().getColor(R.color.color_attention)); + changeState(true); + t.start(); + break; + default: + return false; + } + + return true; + } + + + @Override + public void onDraw(Canvas canvas) { + super.onDraw(canvas); + + float width = getWidth(); + float height = getHeight(); + float textLayoutWidth = width; + + DirectionalEntity entity = (DirectionalEntity) widgetEntity; + + if (entity.rotation == 90 || entity.rotation == 270) { + textLayoutWidth = height; + } + + canvas.drawRect(new Rect(0, 0, (int) width, (int) height), buttonPaint); + + staticLayout = new StaticLayout(entity.text, + textPaint, + (int) textLayoutWidth, + Layout.Alignment.ALIGN_CENTER, + 1.0f, + 0, + false); + canvas.save(); + canvas.rotate(entity.rotation, width / 2, height / 2); + canvas.translate(((width / 2) - staticLayout.getWidth() / 2), height / 2 - staticLayout.getHeight() / 2); + staticLayout.draw(canvas); + canvas.restore(); + } +} diff --git a/app/src/main/res/layout/widget_detail_directional.xml b/app/src/main/res/layout/widget_detail_directional.xml new file mode 100644 index 00000000..c7b93056 --- /dev/null +++ b/app/src/main/res/layout/widget_detail_directional.xml @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/widgets.xml b/app/src/main/res/values/widgets.xml index 93278309..1ce7921e 100644 --- a/app/src/main/res/values/widgets.xml +++ b/app/src/main/res/values/widgets.xml @@ -8,7 +8,7 @@ Debug Gps Joystick - Label + Directional Logger RqtPlot Viz2D @@ -99,7 +99,6 @@ You can zoom in and out by pinching 2 or more fingers together or apart. - The Joystick node is designed for teleoperational control of robotic systems. @@ -124,6 +123,31 @@ + + + The Directional node is designed for teleoperational control of robotic systems. You can create + a button which allows the robot to move or turn. + It will communicate to the topic specified in the details. The inherited message is thereby + the geometry_msgs/Twist. In the details section of the directional, you can specify the axis + of the speed and the kind of speed (linear / angular). + + + + Linear + Angular + + + + X + Y + Z + + + + Positive + Negative + + A Label which does not interact with ROS but helps to organize your screen.