Lok24
16.12.2016, 15:51

Editiert von
Lok24
16.12.2016, 15:57

Hilfe bei JAVA für leJOS gesucht

Hallo,

ich habe jetzt leJOS halbwegs am Laufen, hapern tut es ganz stark mit dem Verständnis von OOP und Java.

Wer hat Lust und Kenntnisse, mir auf die Sprünge zu helfen?

Problemstellung:
Ein Motor und sein Sensor bilden eine "Unit", die ich steuern möchte.
Im "Hauptprogramm" wird alles definiert, danach möchte ich mit der Methode "speed" die Geschwindigkeit steuern.
d.h. das Objekt "Unit1" der Klasse "UNIT_CS" soll dauerhaft existieren und mit dem Motor und einem Sensor fest verknüpft sein

Hauptprogramm:
---------------------

EV3LargeRegulatedMotor m11 = new EV3LargeRegulatedMotor(MotorPort.A);
EV3TouchSensor s11 = new EV3TouchSensor(SensorPort.S1);

Unit_CS Unit1= new Unit_CS(m11,s11);
Unit1.speed(100);
Unit1.speed(200);
Unit1.speed(0);

Unit_CS:
----------------------

** HIER FEHLT IRGENDWAS ENTSCHEIDENDES ....... mit den
** Parametern (RegulatedMotor m,SensorModes s)
** sodass "speed" auf m zugreifen kann ......

public void speed(int v) {
m.forward();
m.setSpeed(v);
}

Wer kann helfen?

Grüße

Werner



daniel.vergien
16.12.2016, 16:05

Als Antwort auf den Beitrag von Lok24

Re: Hilfe bei JAVA für leJOS gesucht

Lok24 hat geschrieben:



Hauptprogramm:
---------------------

EV3LargeRegulatedMotor m11 = new EV3LargeRegulatedMotor(MotorPort.A);
EV3TouchSensor s11 = new EV3TouchSensor(SensorPort.S1);

Unit_CS Unit1= new Unit_CS(m11,s11);


Unit_CS:
----------------------

** HIER FEHLT IRGENDWAS ENTSCHEIDENDES ....... mit den
** Parametern (RegulatedMotor m,SensorModes s)
** sodass "speed" auf m zugreifen kann ......

public Unit_CS(EV3LargeRegulatedMotor m, EV3TouchSensor s) {
/* Im Konstruktor die Referenz auf den Motor ablegen */
this.m = m1;
}

/* Hier wird die Referenz auf das Motor Objekt gespeichert */
private EV3LargeRegulatedMotor m;

public void speed(int v) {
m.forward();
m.setSpeed(v);
}



Hallo,

ich habs mal direkt im Zitat geändert. Wenn Du nicht weiter kommst, schick mir einfach die Dateien.

Bis denn

Daniel



Lok24
16.12.2016, 16:27

Als Antwort auf den Beitrag von daniel.vergien

Re: Hilfe bei JAVA für leJOS gesucht

Hallo Daniel,

recht vielen Dank für die schnelle Hilfe.

daniel.vergien hat geschrieben:

public Unit_CS(EV3LargeRegulatedMotor m, EV3TouchSensor s) {
/* Im Konstruktor die Referenz auf den Motor ablegen */
this.m = m1;
}
Aha! Sowas ähnliches hatte ich ohne Wirkung eingebaut und verworfen, auch weil in Tutorials immer von "main" im Zusammenhang mit dem Konstruktor die Rede ist. Der Konstruktor ist es, weil es heißt wie die Klasse, nicht wahr?

daniel.vergien hat geschrieben:
/* Hier wird die Referenz auf das Motor Objekt gespeichert */
private EV3LargeRegulatedMotor m;
Ah! Eine Klassen-Variable, wenn ich es recht verstanden habe.

Egal, es tut was ich möchte.
Ich fürchte es werden weitere Fragen folgen...

Grüße und schönes Wochenende

Werner



daniel.vergien
16.12.2016, 16:39

Als Antwort auf den Beitrag von Lok24

Re: Hilfe bei JAVA für leJOS gesucht

Lok24 hat geschrieben:



daniel.vergien hat geschrieben:
public Unit_CS(EV3LargeRegulatedMotor m, EV3TouchSensor s) {
/* Im Konstruktor die Referenz auf den Motor ablegen */
this.m = m1;
}
Aha! Sowas ähnliches hatte ich ohne Wirkung eingebaut und verworfen, auch weil in Tutorials immer von "main" im Zusammenhang mit dem Konstruktor die Rede ist. Der Konstruktor ist es, weil es heißt wie die Klasse, nicht wahr?

Genau, die Konstruktoren haben per Definition den Namen wie die Klasse und keinen Rückgabewert, auch nicht "void". Einer der Konstruktoren wird aufgerufen, wenn Du eine Instanz der Klasse bildest (ein Objekt).
Lok24 hat geschrieben:

daniel.vergien hat geschrieben:
/* Hier wird die Referenz auf das Motor Objekt gespeichert */
private EV3LargeRegulatedMotor m;
Ah! Eine Klassen-Variable, wenn ich es recht verstanden habe.

Genau, oder ein Feld, Property, je nach dem welchen Text Du liest.

Falls Du noch Fragen hast, frag. Und wenn es hier zu off-topic wird, gerne auch per PM.

Bis denn

Daniel



Lok24
18.12.2016, 15:44

Als Antwort auf den Beitrag von Lok24

Re: Hilfe bei JAVA für leJOS gesucht

Hallo,

es wird besser, aber noch nicht gut

Eine Unit besteht im Modell immer aus einem konkreten Motor und einem konkreten Sensor.
Deswegen werden die in einem Array "Unit[]" definiert.

(Man könnte die Objekte auch im Konstruktor von Unit erzeugen, dann müsste man aber Art und Port als string übergeben, also quasi
U[4] = new Unit("L", "A"). Vertagt auf später.

In der Klasse "Unit" werden jedoch die 'Parents' (RegulatedMotor, SensorModes ) genutzt.
Ist das noch halbwegs sauber programmiert, oder geht das nur zufällig?

Die Methode "move" bekommt die Geschwindigkeit übergeben, der Motor dreht, bis der Sensor auslöst.
Überraschenderweise: das geht auch

Jetzt sollen die aber gleichzeitig laufen!
Also geändert in
"public Unit(RegulatedMotor m, SensorModes s ) extends thread"
Erfolg: kein Fehler, aber da läuft auch nix parallel.
Ich vermute die Idee mit den while-Schleifen ist da kontraproduktiv...
Wie macht man sowas "richtig"?

Grüße

Werner
--------------------------------------
public static void main(String[] args)

Unit[] U = new Unit[2];

EV3LargeRegulatedMotor m11 = new EV3
LargeRegulatedMotor(MotorPort.A);
EV3ColorSensor s12 = new EV3ColorSensor(SensorPort.S2);
U[1]= new Unit(m11,s12);

EV3MediumRegulatedMotor m12 = new EV3
MediumRegulatedMotor(MotorPort.B);
EV3TouchSensor s11 = new EV3TouchSensor(SensorPort.S1);
U[2]= new Unit(m12,s11);

U[2].move(100);
U[2].stop();
U[1].move(700);
U[1].stop();
---------------------------------------

public class Unit {

private RegulatedMotor m;
private SensorModes s;
private float[] sample;

public Unit(RegulatedMotor m, SensorModes s ) {
this.m = m;
this.s = s;
}

public void move(int v) {
if (s.getClass().getName().indexOf("Color") > 0){
SampleProvider sp = s.getMode("ColorID");
sample = new float[sp.sampleSize()];
}
if (s.getClass().getName().indexOf( "Touch") > 0){
SampleProvider sp = s.getMode("Touch");
sample = new float[sp.sampleSize()];
}
m.backward();
m.setSpeed(v);

while (true) {
s.fetchSample(sample, 0);
System.out.println(sample[0]);
Delay.msDelay(100);
if (sample[0] == 3) break;
if (sample[0] == 1) break;
}

//m.setSpeed(0);
//m.waitComplete();
//Sound.beep();
//Delay.msDelay(1000);

}


public void stop() {
m.stop();
m.flt();

}

}



daniel.vergien
18.12.2016, 20:27

Als Antwort auf den Beitrag von Lok24

Re: Hilfe bei JAVA für leJOS gesucht

Hallo Werner,

einen Thread started man in Java gewöhnlich über zwei verschiedene Wege:

1. Mit Runable

Du lässt deine Klasse das Interface "Runable" implementieren, also "public Unit(RegulatedMotor m, SensorModes s ) implements Runable"
Der Code der im Thread laufen soll kommt dann in die Methode "public void run()". Gestartet wird das ganze dann mit "Thread t = new Thread(new Unit(...)); t.start;

2. Direkt mit Thread
"public Unit(RegulatedMotor m, SensorModes s ) extends Thread" (mit großem T). Der Code der laufen soll kommt auch in "pulbic void run()". Gestarted wirt das dann mit "Unit u = new Unit(...); u.start;".

Der erste Weg hat den Vorteil, das eine Klasse mehrere Interfaces implantieren kann, aber nur von einer Klasse erben kann. Wenn deine Klassenhierachie etwas komplexer wird ist das oft ein entscheidender Vorteil.

Bis denn

Daniel



Lok24
20.12.2016, 10:13

Als Antwort auf den Beitrag von daniel.vergien

Editiert von
Lok24
20.12.2016, 10:23

Re: Hilfe bei JAVA für leJOS gesucht

Hallo Daniel,

daniel.vergien hat geschrieben:

einen Thread started man in Java gewöhnlich über zwei verschiedene Wege:
1. Mit Runable
2. Direkt mit Thread


Der entscheidende Unterschied scheint mir zu sein, dass man bei "Thread" außer "run" keine weiteren Methoden in der Klasse definieren kann?

daniel.vergien hat geschrieben:
Der erste Weg hat den Vorteil, das eine Klasse mehrere Interfaces implantieren kann, aber nur von einer Klasse erben kann. Wenn deine Klassenhierachie etwas komplexer wird ist das oft ein entscheidender Vorteil.


Ein schöner Satz! Das einzige Wort da ich komplett verstanden habe ist "Vorteil
Nein, es wird nicht komplizierter, was ich hier gepostet habe ist - Stand heute - das ganze Programm.

Ich hab's jetzt mit "runable" implementiert, tut aber nur manchmal was es soll, die Threads stoppen nicht (das war ja mein Problem und die Fragestellung)

Ursache hierfür ist anscheinend die leJOS-method "motor.stop()", die offenbar den Motor nur bremst. "motor.stop(true)" macht was völlig anderes. Irgendwie habe ich noch keine gute Dokumentation gefunden.
Da habe ich jetzt fast einen Tag mit zugebracht...

Jedenfalls: erneut Danke, Du hilfst mir da wirklich sehr weiter.

Grüße

Werner



daniel.vergien
20.12.2016, 11:42

Als Antwort auf den Beitrag von Lok24

Re: Hilfe bei JAVA für leJOS gesucht

Lok24 hat geschrieben:



Der entscheidende Unterschied scheint mir zu sein, dass man bei "Thread" außer "run" keine weiteren Methoden in der Klasse definieren kann?

Nein, der entscheidende Unterschied ist schon der mit der Vererbung . Du kannst auch in einer Klasse die von Thread ableitet so viele Methoden definieren wie Du brauchst.

Lok24 hat geschrieben:

Ich hab's jetzt mit "runable" implementiert, tut aber nur manchmal was es soll, die Threads stoppen nicht (das war ja mein Problem und die Fragestellung)


Was meinst Du mit "die Threads stoppen nicht"? Das sollen sie eigentlich auch nicht, da wenn ein Thread gestoppt ist, kann er nicht mehr gestartet werden.
Du brauchst wahrscheinlich eine endlos Schleife in der run Methode, die die zwei Zustände "Fahre und Stoppe" und "Warte auf Fahrbefehl" abarbeitet. Den Fahrbefehl sendest Du dann per "public void move(int v)", in der Schleife erkennst Du das "v > 0 == true" und wechslst in den "Fahre und Stoppe" Zustand. Wenn die Unit gestoppt ist, setzt Du v=0.

Ich hoffe ich habe das Problem richtig verstanden, und meine Ausführung hilft.

Bis denn

Daniel



Lok24
21.12.2016, 09:50

Als Antwort auf den Beitrag von daniel.vergien

Re: Hilfe bei JAVA für leJOS gesucht

Halo Daniel,

daniel.vergien hat geschrieben:

Nein, der entscheidende Unterschied ist schon der mit der Vererbung . Du kannst auch in einer Klasse die von Thread ableitet so viele Methoden definieren wie Du brauchst.

Natürlich Ich hätte vielleicht zum Testen nicht einen in Java reservierten Namen ("stop") nehmen sollen oder die Fehlermeldung richtig lesen. Egal, jetzt ist es eben "runable", auch schön.

daniel.vergien hat geschrieben:
Was meinst Du mit "die Threads stoppen nicht"? Das sollen sie eigentlich auch nicht, da wenn ein Thread gestoppt ist, kann er nicht mehr gestartet werden.
Wir reden offenbar von verschiedenen Vorgehensweisen.
Ich hatte einen Thread geschrieben

public class Unit extends Thread {
public void run();
m.SetSpeed(100);
Wait 1 sec;
m.stop()

Dann kommt ja kein Befehl mehr, der Thread stirbt.
Tat er aber irgend wie nicht?
Für die nächste Bewegung hätte ich einen neuen Thread aufgemacht.

Du gehst davon aus, dass der Thread lebenslang läuft und von außen gesteuert wird, das ist völlig anders.
Aber viel besser, weil ich natürlich immer mal nachfragen kann, was der Sensor/Motor gerade so macht.

daniel.vergien hat geschrieben:
Ich hoffe ich habe das Problem richtig verstanden, und meine Ausführung hilft.
Ja und Ja.

unten noch die Screenshots der beiden Programmteile main und run.
Naja, ehrlich gesagt ist das praktisch das ganze Programm
(Viel) mehr soll es gar nicht tun.

Gestartet werden zwei Units, wenn beide ihre Endposition erreicht haben
werden sie erneut gestartet. Muss noch ein paar Kommentare einstreuen.

Grüße

Werner

[image]


[image]



daniel.vergien
21.12.2016, 10:25

Als Antwort auf den Beitrag von Lok24

Re: Hilfe bei JAVA für leJOS gesucht

Hallo Werner,

funktioniert es den jetzt?

Sonst hab ich noch ein Paar Anmerkungen zu deinem Code:
* String Vergleiche mit "==" sind gefährlich. Es ist nicht garantiert das zwei String Objekte mit gleichen Inhalt das gleiche Objekt sind. Besser: status.equals("ON"
* Einrückung: da Du scheinbar eclipse nutzt: Strg+Shift+F oder in den "Preferences" nach "Save Actions" suchen und es dort gleich einstellen.
* Methoden, und Variablen fangen min einem kleinen Buchstaben an (EndThread() -> endThread()) und "CamleCase" ist die Konvention (getstatus() -> getStatus())
* Jeden if/else/for Block Klammern, das hilft gegen fehler, wenn man dann doch mal ein zweites Statement hinzufügt

Die letzten drei Punkte haben natürlich keinen Einfluss auf das Programm, aber da man Quellcode öfters liest als schreibt, ist es hilfreich sich an diese Konventionen zu halten. Der erste Punkt kann schwer zu findende Fehler produzieren.

Wenn Du es mit der Code-Qualität noch weiter treiben möchtest:
* enum type Direction mit LEFT und RIGHT definierten, sowie für Status mit ON, OFF. Das garantiert das man nicht aus versehen status = "LEFT" schreibt.

Lok24 hat geschrieben:

Muss noch ein paar Kommentare einstreuen.

Aber bitte nur da, wo Du etwas machst was nicht aus dem Code deutlich wird. "// Setze status auf OFF" und in der nächsten Zeile "status="OFF"" hilft niemanden...

So genug Code-Review für privaten Kram...

Bis denn

Daniel



Lok24
21.12.2016, 12:24

Als Antwort auf den Beitrag von daniel.vergien

Re: Hilfe bei JAVA für leJOS gesucht

Hallo Daniel,

daniel.vergien hat geschrieben:

funktioniert es den jetzt?
Aber wie!

Und Du legst den Finger genau auf den wunden Punkt:
Jetzt gilt es, das Programm besser zu strukturieren, sprechende Namen einzubauen etc. Erst dann wird es erweitert.

daniel.vergien hat geschrieben:
* String Vergleiche mit "==" sind gefährlich. Es ist nicht garantiert das zwei String Objekte mit gleichen Inhalt das gleiche Objekt sind. Besser: status.equals("ON"

- if (status == "ON")
"ON" ist keine Objekt, sondern eine Zeichenkette?
Status ist eine Klassenvariable?
Wo könnte da das Problem herkommen?

daniel.vergien hat geschrieben:
* Einrückung: da Du scheinbar eclipse nutzt: Strg+Shift+F oder in den "Preferences" nach "Save Actions" suchen und es dort gleich einstellen.

Ja. Eclipse Helios.
..test...save...
Ah, très chic. Danke!
(aber ich hatte alles schön eingerückt, nicht wahr?)

daniel.vergien hat geschrieben:
* Methoden, und Variablen fangen min einem kleinen Buchstaben an (EndThread() -> endThread()) und "CamleCase" ist die Konvention (getstatus() -> getStatus())
* Jeden if/else/for Block Klammern, das hilft gegen fehler, wenn man dann doch mal ein zweites Statement hinzufügt


Grmmpf.
Ich habe Jahrzehnte (auch beruflich) programmiert,
kann mich aber irgendwie nicht mit Sprachen anfreunden, die case-sensitive sind (auch nicht mit Betriebssystemen die case-sensitiv sind), Klammern brauchen, die auf meiner Tastatur nur schwer erreichbar sind oder ein Zeilenende ; nötig machen.

daniel.vergien hat geschrieben:
.... schreibt, ist es hilfreich sich an diese Konventionen zu halten.
Das will ich gerne tun.

daniel.vergien hat geschrieben:
Wenn Du es mit der Code-Qualität noch weiter treiben möchtest:
* enum type Direction mit LEFT und RIGHT definierten, sowie für Status mit ON, OFF. Das garantiert das man nicht aus versehen status = "LEFT" schreibt.
Genau das wäre meine nächste Frage gewesen.

Ich schreibe dann status =dir.LEFT, =stat.LEFT oder =dir.LFET sollten schon beim Eintippen als falsch erkannt werden.
Aber wie mache ich die projektweit zugänglich, wenn doch globale Variablen eher verpönt sind?

Lok24 hat geschrieben:
Aber bitte nur da, wo Du etwas machst was nicht aus dem Code deutlich wird. "// Setze status auf OFF" und in der nächsten Zeile "status="OFF"" hilft niemanden...



Stay tuned!

Grüße

Werner



daniel.vergien
21.12.2016, 15:42

Als Antwort auf den Beitrag von Lok24

Re: Hilfe bei JAVA für leJOS gesucht

Lok24 hat geschrieben:

Aber wie!

Schön zu höhren.

Lok24 hat geschrieben:

daniel.vergien hat geschrieben:
* String Vergleiche mit "==" sind gefährlich. Es ist nicht garantiert das zwei String Objekte mit gleichen Inhalt das gleiche Objekt sind. Besser: status.equals("ON"

- if (status == "ON")
"ON" ist keine Objekt, sondern eine Zeichenkette?
Status ist eine Klassenvariable?
Wo könnte da das Problem herkommen?


Der Operator "==" vergleicht die Identität von Objekten. "ON" ist ein Objekt vom Typ String. Meistens funktioniert der Vergleich "String a = "ON"; String b = "ON"; if(a==B)...", aber nur weil die jvm für alle Strings das gleiche Objekt nutzt. Das ist aber nicht garantiert. Gerade bei langen Strings geht das auch mal schief, und dann wundert man sich.

Lok24 hat geschrieben:

(aber ich hatte alles schön eingerückt, nicht wahr?)

Die letzte Methode nicht...

Lok24 hat geschrieben:

Grmmpf.
Ich habe Jahrzehnte (auch beruflich) programmiert,
kann mich aber irgendwie nicht mit Sprachen anfreunden, die case-sensitive sind (auch nicht mit Betriebssystemen die case-sensitiv sind), Klammern brauchen, die auf meiner Tastatur nur schwer erreichbar sind oder ein Zeilenende ; nötig machen.

Das ist Gewöhnung. Ich werde wahnsinnig wenn es auf die Einrückung ankommt (YAML, oder Python).

Lok24 hat geschrieben:

daniel.vergien hat geschrieben:
Wenn Du es mit der Code-Qualität noch weiter treiben möchtest:
* enum type Direction mit LEFT und RIGHT definierten, sowie für Status mit ON, OFF. Das garantiert das man nicht aus versehen status = "LEFT" schreibt.
Genau das wäre meine nächste Frage gewesen.

Ich schreibe dann status =dir.LEFT, =stat.LEFT oder =dir.LFET sollten schon beim Eintippen als falsch erkannt werden.
Aber wie mache ich die projektweit zugänglich, wenn doch globale Variablen eher verpönt sind?


Du definierst eine Enum, z.B.:

public enum Direction
{
LEFT, RIGHT
}


Und nutzt des dann so:


// Feld anlegen
private Direction direction;

// Zuweisung
direction = Direction.LEFT;


Da Meckert dann der Compiler wenn Du falsche Typen nutzen möchtest. Bei Konstanten würde er es leider nicht tun:


public final static int DIRECTION_LEFT = 1;
public final static int DIRECTION_RIGHT = 2;
public final static int STATE_ON = 3;
public final static int STATE_OFF = 4;


int state = STATE_ON; // ok
state = DIRECTION_LEFT; // der Compiler meckert nicht, ist aber höchst wahrscheinlich Grütze
int direction = DIRECTION_LEFT;
direction = 33; // der Compiler meckert nicht, ist aber höchst wahrscheinlich Grütze


Bis denn

Daniel

P.S.: um mal zum eigentlichen Thema zurück zu kommen: was baust Du eigentlich?



Lok24
22.12.2016, 09:45

Als Antwort auf den Beitrag von daniel.vergien

Re: Hilfe bei JAVA für leJOS gesucht

Hallo Daniel,

so, es geht weiter, ich packe jetzt das Thread-Erzeugen und die Sensoren-Prüfung in den Konstruktor von UNIT, das wird ja noch schöner.

Ich habe mich jetzt für KONSTANTEN entschieden, das schreibt und liest sich gut.

public static final String LEFT = "LEFT";
public static final String RIGHT = "RIGHT";
public static final String ON = "ON";
public static final String OFF = "OFF";

U[1].moveUnit(0, 300, LEFT);
U[2].moveUnit(5, 600, RIGHT);

while (U[1].getStatus() == OFF | U[2].getStatus() == OFF) {
}

Konkret baue ich nichts, sondern experimentiere ein wenig für ein konkretes Projekt, das aber ähnliche Aufgaben hat wie viele andere:

Ein Objekt soll bewegt werden, (ein) Sensor(en) soll(en) prüfen wo es ist, und das für mehrere Objekte parallel.
Das sind klassische Roboteranforderungen, die man immer wieder brauchen kann.

Und in labVIEW ist das Wiederzusammenführen von Threads nicht eben trivial, mehrdimensionale Arrays gibt es nicht.
Und auch die Parametrisierung ist nicht übersichtlich, hier mal das Setzen der Parameter und Arrays für meine 12V-Anlage.

[image]



Hier der Effekt:
http://www.1000steine.de/...amp;id=361608#id361608

Und nein, ich werde nicht an einen Motor direkt eine Geschwindigkeit dranschreiben. Ich werde mein Programm so schreiben, dass man v mit einem(!) Faktor programmweit ändern kann, das bedeutet alle UNITs laufen proportional langsam (Test), außerdem wird das Programm vermutlich selbstlernend werden.

Grüße

Werner



Gesamter Thread: