Fest slows down while testing with swingx jxtreetable - java

I am not sure how to explain this. But I'll try.. Fest slows down to crawl while working with JXTreeTable of swingx. It doesn't slow down initially. It works fine for a while, but after a while when the same actions are repeated it slows down badly.
I have raised a bug for this in github. Please tell me if this is something that I am doing wrong instead. I am not able to reproduce the problem when I tried to create an SSCCE.
Anyway, here's a video of it slowing down.
http://screencast.com/t/liNttCw2In0w
At times 0.39s to 0.40 a set of operations are performed. These are done when there is one row in the JXTreeTable.
At time 0.49 to end of recording the same operation is repeated but there are now 3 rows in the table, it takes very long for the mouse to click.
I have attached a screenshot taken at the time when fest slows down, which attempts to explain it more
This is the code that does the work:
Step 1) Selecting a node from the tree is done as below:
JTreeFixture folioTreeFixture = importShareholders.panel("treePanel").tree("folioTree");
folioTreeFixture.separator("~");
folioTreeFixture.selectPath(new StringWrapper("Shareholders", true)+"~"+
(ShareType.isEquity(shareType) ? new StringWrapper("Equity Folios", true) : new StringWrapper("Preference Folios", true))+"~"+
new FolioTreeRep(folio.getName(),folioNo, shareType).toString());
Step 2) Searching and selecting a row from the JXTreeTable
int selectRow=-1;
JTableFixture table=importShareholders.table("historyTable");
for(int i=0;i<table.rowCount();i++){
String certificateNumber = table.cell(TableCell.row(i).column(ShareholderHistoryTable.columnIndex(ShareholderHistoryTable.CERT_NO))).value();
String remarks=table.cell(TableCell.row(i).column(ShareholderHistoryTable.columnIndex(ShareholderHistoryTable.REMARKS))).value();
if(StringUtils.isEmpty(remarks) && StringUtils.isNotEmpty(certificateNumber) && Integer.parseInt(certificateNumber)==certNo){
selectRow=i;
break;
}
}
if(selectRow==-1){
fail("Couldn't find certificate number to transfer");
}
Step 3) Showing the pop up menu and clicking the row
table.showPopupMenuAt(TableCell.row(selectRow).column(0)).menuItem("btnTransfer").click();
I am not sure why its slowing down. Please let me know if there is any more info I can help with. Would be grateful for some help in solving the problem
I have profiled the application and I dont find anything untoward happening. I dont have a lot of experience profiling applications. I would be grateful if someone could have a second look at this. I profiled it with yourkit and have uploaded the snapshot dump here:
https://www.dropbox.com/s/dh976v01q9c3sgj/ImportShareholderData.shouldTransferAndSplit-2013-06-14-shutdown.snapshot.zip
Any help will be greatly appreciated..
EDIT:
I think I forgot to mention the same thing works when I do it manually. It only slows down with fest. That leads me to believe that there is an issue with fest maybe?
Sorry about that.
EDIT 2:
As request by Marcin (sorry for the delay Marcin).. Here's the code when the first row is getting split
public List<Integer> splitRowEqually(ShareType shareType, String date, int folioNo, int certNo, int... certnos) throws NoSuchFieldException, TorqueException {
//select a tree node
selectFolioInTree(shareType, folioNo);
Pause.pause(new Condition("Wait until tab is created") {
#Override
public boolean test() {
return importShareholders.tabbedPane().tabTitles().length>0;
}
});
//select a row on the table to split
int row=selectRowWithCertNunber(certNo);
List<Integer> rowsIndexes=new ArrayList<Integer>();
JTableFixture table = importShareholders.table();
//show popup menu on that row and select split
table.showPopupMenuAt(row(row).column(columnIndex(TRANS_TYPE))).menuItem("btnSplit").click();
DialogFixture splitDialog=FinderUtilities.getDialogWithTitle("Split Share Certificate");
splitDialog.textBox("tfDateOfSplit").setText(date);
int noOfShares= Integer.parseInt(table.cell(row(row).column(columnIndex(NO_OF_SHARES))).value());
int distFrom= Integer.parseInt(table.cell(row(row).column(columnIndex(DIST_NO_FROM))).value());
int distTo= Integer.parseInt(table.cell(row(row).column(columnIndex(DIST_NO_TO))).value());
//split the row into the number of times decided by the certnos array
int noOfSharesInEachSplit=noOfShares/certnos.length;
for(int i=0;i<certnos.length;i++){
int distToInSplit = distFrom + noOfSharesInEachSplit-1;
enterSplitRowDetails(splitDialog, certnos[i], distFrom, distToInSplit<=distTo ? distToInSplit : distTo);
distFrom=distToInSplit+1;
rowsIndexes.add(row++);
}
splitDialog.button("btnSplit").click();
return rowsIndexes;
}
//selects a node from the left hand side tree
public void selectFolioInTree(final ShareType shareType,final int folioNo) throws TorqueException {
JTreeFixture folioTreeFixture = importShareholders.panel("treePanel").tree("folioTree");
folioTreeFixture.separator("~");
// I use these wrapper classes - StringWrapper and FolioTreeRep, so that I can get a html
// string for the tree node like <html><b>Shareholder</b></html>
String treePath = new StringWrapper("Shareholders", true) + "~" +
(ShareType.isEquity(shareType) ? new StringWrapper("Equity Folios", true) : new StringWrapper("Preference Folios", true)) + "~" +
new FolioTreeRep(mapOfFolioNames.get(folioNo), folioNo, shareType).toString();
folioTreeFixture.clickPath(treePath);
}
//search the table for a row that contains the cert no provided in the Certificate Number column.
private int selectRowWithCertNunber(int certNo) throws NoSuchFieldException {
int selectRow=-1;
JTableFixture table=importShareholders.table("historyTable");
for(int i=0;i<table.rowCount();i++){
String certificateNumber = table.cell(row(i).column(columnIndex(CERT_NO))).value();
String remarks=table.cell(row(i).column(columnIndex(REMARKS))).value();
if(StringUtils.isEmpty(remarks) && StringUtils.isNotEmpty(certificateNumber)
&& Integer.parseInt(certificateNumber)==certNo){
selectRow=i;
break;
}
}
if(selectRow==-1){
fail("Couldn't find certificate number to transfer");
}
return selectRow;
}
// enter details on the table in the SplitDialog
private void enterSplitRowDetails(DialogFixture splitDialog, int cert, int distFrom, int distTo) {
splitDialog.button("btnAdd").click();
int row = splitDialog.table().rowCount();
splitDialog.table().enterValue(row(row - 1).column(0), String.valueOf(cert));
splitDialog.table().enterValue(row(row - 1).column(1), String.valueOf(distFrom));
splitDialog.table().enterValue(row(row - 1).column(2), String.valueOf(distTo));
}

Emm... It is quite interesting question;
I suppose the question contains less really required details especially the robot integration and IO solutions details so I cannot just give you a proper answer...
Anyway, I'll try to analyze the problem in voice a little bit in my way...
First. According to your screenshot comments, I can notice that all "30s pauses or so" occur on some, as I can get it, stream reading process "select/search" (your app gets some data to output etc). So maybe it is much deeper than you think because it is probably thread problem;
I couldn't find the GuiQuery/GuiTask/GuiActionRunne classes usage in your code snippets so I may suggest the "synch problem" may take place in the mentioned case...
Second. OK... If it is still the thread problem I may suggest the robot and IO solutions are both in some ONE thread (the Main thread or something) because, according to your tips as "At times 0.39s to 0.40 a set of operations are performed. These are done when there is one row in the JXTreeTable." ... GUI is waiting for some process to be completed...
Third.
And again... According to this issue as
"It is recommended to turn on an automated check to verify that all
Swing components updates are done in Swing’s EDT (Event Dispatcher
Thread). For those unfamiliar with the EDT, it is responsible for
handling and updating all Swing widgets in a separate thread, causing
that the application never loses responsiveness to user gestures (just
in short, more about the EDT here). To do that, we add the following
hook to the test:"
import org.fest.swing.edt.FailOnThreadViolationRepaintManager;
import org.junit.BeforeClass;
...
#BeforeClass
public static void setUpOnce() {
FailOnThreadViolationRepaintManager.install();
}
Next step is to launch the frame or dialog. As JUnit runs in its own
thread, we must launch the frame or dialog through Fest, to ensure,
again, that EDT is properly used:
import org.fest.swing.edt.GuiActionRunner;
import org.fest.swing.edt.GuiQuery;
import org.fest.swing.fixture.FrameFixture;
import org.junit.Before;
...
private FrameFixture testFrame;
private AllTypesFrame frame;
...
#Before
public void setUp() {
frame = GuiActionRunner.execute(new GuiQuery<AllTypesFrame>() {
protected AllTypesFrame executeInEDT() {
return new AllTypesFrame();
}
});
testFrame = new FrameFixture(frame);
testFrame.show();
}
... makes me think it is maybe the "thread-problem" which is described in the First and Second tips...
so, as a conclusion, I can say that maybe you have to multi-thread your test a little more because it is obviously some kind of synch problem...
P.S.
#sethu, before you start your debugging I want to point a little...
I still suspect threads conflict is taking place here (see my previous tips) because, as I may notice, your code snippets are showing static expressions usage to invoke methods like Pause.pause(...) or FinderUtilities.getDialogWithTitle(...) etc I cannot see the whole project architecture so it is hard to analyze according the represented bits but it is pretty clear the "manual testing" goes fine because action listeners react in real time but fest testing does the annoying delays because it uses some "timer" to countdown until a click emulation occurs etc and of course it is a background process which needs a separate thread... Watch debugging carefully maybe somewhere in your code UI thread and fest thread do conflict (see static methods, thread.sleep etc) the points where fest thread could block (override) the UI's one... :S By the way what method Pause.pause(...) does?
P.P.S.
If you have some additional information please comment my answer
Report if my answer helps you

I do not know what are your robot settings but you can at least try to set idleTimeout and other timeouts for the robot you use. The default timeout is 10 sec (look in org.fest.swing.core.Settings). After I decrease it (first 1000ms, next 100ms) I noticed that robot works faster.
robot().settings().idleTimeout(YOUR_TIMEOUT)
Here is my test setup and one test method. Hope is clear.
Here you have my before/after
private static int testMethodCounter = 0;
private static EmergencyAbortListener mEmergencyAbortListener;
private FrameFixture workbenchFrame;
private Robot robot2;
private static final int myIdleTimeout = 100;
#Before
public void setUp() throws Exception {
// my workaround to be able to start the app once and reuse for all tests
if (testMethodCounter == 0) {
robot2 = BasicRobot.robotWithNewAwtHierarchy();
GuiActionRunner.execute(new GuiTask() {
#Override
protected void executeInEDT() throws Throwable {
ApplicationLauncher.application(ProgramRun.class).start();
}
});
} else {
// the second test method see all before created gui components
robot2 = BasicRobot.robotWithCurrentAwtHierarchy();
}
testMethodCounter++;
robot2.settings().idleTimeout(myIdleTimeout);
workbenchFrame = WindowFinder.findFrame(FrameNames.WORKBENCH.getName()).withTimeout(10000)
.using(robot2);
}
#After
public void tearDown() {
// current window will not be closed
robot2.cleanUpWithoutDisposingWindows();
}
#Test
public void someSmokeTest() throws Exception {
Pause.pause(1000);
// perform some test specific gui actions
// here is very important moment, I need new robot because
// workbenchFrame.button(ButtonNames.SOME_BUTTON_NAME).click(); creates new dialog
// which will be avilable in AWT stack after creation
robot2.cleanUpWithoutDisposingWindows();
robot2 = BasicRobot.robotWithCurrentAwtHierarchy();
// the new Robot needs timeout setup
// without this I have long breaks between gui events
robot2.settings().idleTimeout(myIdleTimeout);
workbenchFrame.button(ButtonNames.SOME_BUTTON_NAME).click();
DialogFixture dialog = WindowFinder.findDialog("dialog2")
.withTimeout(5000).using(robot2);
// some actions on the dialog
// once again next new dialog
workbenchFrame.menuItem(MenuItemNames.NAME).click();
robot2.cleanUpWithoutDisposingWindows();
robot2 = BasicRobot.robotWithCurrentAwtHierarchy();
// and idleTimeout setup once again, new Robot needs new setup
robot2.settings().idleTimeout(myIdleTimeout);
// next actions + assertion
}

Related

Updating a jLabel

I have a simple GUI that has a jTextField that waits for the user to put in something. After a button is clicked, the program:
reads the input, saves it in a String variable;
opens a new GUI (that is in a separate class file), which contains an empty jLabel, and passes the String variable to it, changing the jLabel text to it.
The problem is that no matter how hard I try to reconfigure the code, adding things like repaint(), revalidate(), etc., the jLabel in the second GUI stays empty. Using a System.out.println(jLabel.getText()) reveals that the text value is indeed changed, but not displayed. How do I "refresh" this jLabel, so it'd show what I want it to? I'm aware I could add an event, though I don't want the user to click anything to refresh the GUI, the values should be there as it's initiated. I've read trough several similar posts, but found that the solutions don't work for me.
The code of first GUI's button click event:
private void sbuttonActionPerformed(java.awt.event.ActionEvent evt) {
errortext.setText("");
Search = sfield.getText();
Transl = hashes.find(Search);
if (Transl.equals("0")) errortext.setText("Word not found in database.");
else {
ws.run(Search, Transl); // <- this opens the second GUI, with two String parameters I want to display in the second GUI;
}
}
The code of the second GUI (activeword and translation are the jLabels that are giving me trouble.):
public void run(String Search, String Transl) {
WordScreen init = new WordScreen(); //initialise the second GUI;
init.setVisible(true);
activeword.setText(Search);
translation.setText(Transl);
}
Any reply is very welcome! Please ask me for more information about the code if necessary, I will make sure to reply as soon as possible!
Best solution: change WordScreen's constructor to accept the two Strings of interest:
From this:
public void run(String Search, String Transl) {
WordScreen init = new WordScreen(); //initialise the second GUI;
init.setVisible(true);
activeword.setText(Search);
translation.setText(Transl);
}
to this:
public void run(String search, String transl) {
WordScreen init = new WordScreen(search, transl);
init.setVisible(true);
}
Then in the WordScreen constructor use those Strings where needed:
public WordScreen(String search, String transl) {
JLabel someLabel = new JLabel(search);
JLabel otherLabel = new JLabel(transl);
// put them where needed
}
Note that I cannot create a comprehensive answer without your posting a decent MRE
As an aside, you will want to learn and use Java naming conventions. Variable names should all begin with a lower letter while class names with an upper case letter. Learning this and following this will allow us to better understand your code, and would allow you to better understand the code of others.

Java - Swing form appear empty when get called

I got a mouse click event that call and display a new JFrame form when click. But when I click, the form show up with no element inside it. There are 2 events of that type in my project and I handle them both with the same mechanism. The first one works perfectly, but the second one got the problem. I also use pack() but the problem is still there. Could any one show me how to work this out? Thanks a lot! My project is in Vietnamese, so if any one wants to mention any element in the code or the UI, just writing it with no sign is good enough!
Here are the event handlers:
//The first event
private void tblClientResultMouseClicked(java.awt.event.MouseEvent evt) {
// TODO add your handling code here:
DefaultTableModel dm = (DefaultTableModel) tblClientResult.getModel();
int row = tblClientResult.getSelectedRow();
int col = tblClientResult.getSelectedColumn();
if (col == 7) {
SearchTruyenFrm searchTruyenFrm = new SearchTruyenFrm(listKH.get(row));
searchTruyenFrm.setVisible(true);
}
}
//The second one
private void tblTruyenResultMouseClicked(java.awt.event.MouseEvent evt) {
// TODO add your handling code here:
DefaultTableModel dm = (DefaultTableModel) tblTruyenResult.getModel();
int row = tblTruyenResult.getSelectedRow();
int col = tblTruyenResult.getSelectedColumn();
if (col == 6) {
MuonTruyen muonTruyen = new MuonTruyen();
muonTruyen.setTruyen(listTruyen.get(row));
muonTruyen.setPhieuMuon(phieuMuon);
//Trouble here (?)
ThueTruyenInfoFrm infoFrm = new ThueTruyenInfoFrm(listTruyen.get(row));
infoFrm.setVisible(true);
while (infoFrm.isVisible()) {
}
muonTruyen.setDieuKien(infoFrm.getTxtDieuKien().getText());
muonTruyen.setGiaMuon(Float.parseFloat(infoFrm.getTxtGiaThue().getText()));
muonTruyen.setTienPhat(0);
muonTruyen.setPaid(false);
}
}
Inside the constructor:
public ThueTruyenInfoFrm(Truyen selected) {
initComponents();
txtTenTr.setText(selected.getTen());
txtTacGia.setText(selected.getTacGia());
pack();
}
How it happens:
Desired outcome:
Welcome to the wonderful world of "Honey, I've blocked the Event Dispatching Thread (and now nothing works)"
Mouse events (like all GUI based events) are delivered within the context of the EDT, so doing something like...
private void tblTruyenResultMouseClicked(java.awt.event.MouseEvent evt) {
//....
while (infoFrm.isVisible()) {
}
}
will block the EDT and prevent any further processing of events, including paint events, basically hanging your program.
I suggest you start by having a read of The Event Dispatch Thread to get a nutter understand of the overall issue.
To solve your problem, you will want to make use of a modal dialog, which will wait at the point the dialog is made visible and continue executing after it's closed.
Have a look at How to make dialogs.
This is an important lesson, as you should never start with a top level container (like JFrame), but instead, base all you GUIs on something like a JPanel instead, this gives you greater freedom in deciding on when and how those components are displayed.
Form editors won't teach you techniques which produce re-usable or self contained code and I would highly recommend that you consider spending sometime coding them by hand.

How to handle event in a program running in a cycle (loop)?

I´ve been dealing with this for some days now I´ve serach over the internet and tried everything that came up to my mind but nothing works. My problem is that I am making a pexeso game, we have some additionaly library directly from school or something like that which allows us to draw some pictures instead of comad line only... (We didn´t have graphics yet) So my problem is that my game is running in cycle and waiting for my click som I am checking the condition if click was made. And if I want to click I have to have just a method public void vyberSuradnice(int, int) declared in my code.
So the problem is that my game is runinng in cycle and checking if click was made. But when the click was made the method vyberSuradnice is executed and there I am setting the value off atrrbiute - cakatNaKlik on false, so click was made and one step of game can be made.. But since the function is running in cycle, then even If I clicked and in the method vyberSuradnice the value of attribute is changed, my function which is running in cycle isn´t respond to that change, so the game isn´t going on.
this is the method for clicking
public void vyberSuradnice(int paSuradnicaX, int paSuradnicaY) {
this.riadokOdkry = (paSuradnicaY ) / 25;
this.stlpecOdkry = (paSuradnicaX - 10) / 25;
if (this.riadokOdkry > this.aPocetRiadkov || this.stlpecOdkry > this.aPocetStlpcov) {
System.out.println("Klikli ste mimo hracieho pola ");
} else {
this.cakatNaKlik = false;
}
}
This the part of code where I am waiting for cakatNaklik - false value
while (uhadol) {
if (!this.cakatNaKlik) {
if (this.pocetUhadnutych >= (this.aPocetRiadkov * this.aPocetStlpcov) / 2) {
uhadol = false;
}
this.hraciaPlocha[this.riadokOdkry][this.stlpecOdkry].setUhadnute(true);
But even if the value is changed in method vyberSuradnice this condition is not triggered. But when I make something like this :
while (uhadol) {
System.out.print(this.cakatNaKlik);
if (!this.cakatNaKlik) {
if (this.pocetUhadnutych >= (this.aPocetRiadkov * this.aPocetStlpcov) / 2) {
uhadol = false;
}
this.hraciaPlocha[this.riadokOdkry][this.stlpecOdkry].setUhadnute(true);
the game is working like by writing the variable refresh it or something... but I am getting neverending print of true true or false on command line and this is something I can´t afford to have...
I know this may can be dan by threads but it´s something I can´t allowed to make and I am basically trying to do two things at once.
Is there any other to do this that the variable will be refreshed even without that println and the code will work ?
Thanks very much for every help
Thanks everybody for help, I finally solved it. I just tried tu put a sleep before the if condition. It only need to sleep for even a one miliseconds and it´s seems that the condition is refreshed so it works.
Try this:
Implement an ActionListener interface or extend a class that implements an ActionListener interface. For example:
public class MyClass implements ActionListener {
}
Register an instance of the event handler class as a listener on one or more components. For example:
someComponent.addActionListener(instanceOfMyClass);
instanceOfClass = this; //(if it is handled in the same Class)
Include code that implements the methods in listener interface. For example:
public void actionPerformed(ActionEvent e) {
...//code that reacts to the action...
}
Reference:
https://docs.oracle.com/javase/tutorial/uiswing/events/actionlistener.html

I don't understand this java behaviour

I had a question about making a JButton flash colors ,like you would see when an answer was given in millionaire tv show. I got the answer there on how to do it properly but I also managed to "do it" in this way which raised some questions I couldn't answer totally.
As you see in the code bellow I am calling a
JOptionPane.showMessageDialog() after the JButton.setBackground and right before i stall the code using
do {
}while(time+i*100>System.currentTimeMillis());
Ignore the robot.keypress for the time. Lets get to the point. If i didn't use the JOptionPane before the code stalling , the ui would seem frozen and the button wouldn't repaint. But calling the JOptionPane.ShowMessageDialog() gives "time" to the button to repaint. Then the code is stalled normally and I achieve the sequential color repaint. I used the robot.keypress to close the Pane and achieve the effect desired.
My Questions: First, what happens when the JOptionPane is created that allows the button to repaint ? And secondly why the robot works only before the JOptionPane is called? I tried calling after the Pane was called like one would assume it should happen , but it wouldn't work in that case.
Extra: This didn't work in a mac it seems to only work for windows. Not quite sure.
public static void paintbutton(int bnr,boolean corr) {
long time;
try {
Robot robot = new Robot();
for (int i=5;i>1;i--){
b[bnr-1].setBackground(null);
// Simulate a key press
robot.keyPress(KeyEvent.VK_SPACE);
robot.keyRelease(KeyEvent.VK_SPACE);
JOptionPane.showMessageDialog(null,"hi");
time = System.currentTimeMillis();
do {
}while(time+i*100>System.currentTimeMillis());
b[bnr-1].setBackground(i==1?(corr?Color.green:Color.red):Color.yellow);
// Simulate a key press
robot.keyPress(KeyEvent.VK_SPACE);
robot.keyRelease(KeyEvent.VK_SPACE);
JOptionPane.showMessageDialog(null,"hi");
time = System.currentTimeMillis();
do {
}while(time+i*100>System.currentTimeMillis());
}
} catch (AWTException e) {
System.err.println("error");
}
}
To avoid confusion as to the nature of this question!
The code in the state below doesn't work and I know it shouldn't. I am curious on how adding JOptionPane solves that.
public static void paintbutton(int bnr,boolean corr) {
long time;
for (int i=5;i>1;i--){
b[bnr-1].setBackground(null);
time = System.currentTimeMillis();
do {
}while(time+i*100>System.currentTimeMillis());
b[bnr-1].setBackground(i==1?(corr?Color.green:Color.red):Color.yellow);
time = System.currentTimeMillis();
do {
}while(time+i*100>System.currentTimeMillis());
}
}

LeJOS NXT Programming Sonic Sensor

I am trying to make a NXT Robot that has attached the Ultrasonic Sensor. It has to drive until the distance is 15, and then the engines have to stop. After it stops it has to turn, but it doesn't work.
import lejos.nxt.*;
public class test {
public static void main(String [] args) throws InterruptedException {
UltrasonicSensor ultra = new UltrasonicSensor(SensorPort.S1);
for (int i = 0; i < 5; i++) {
try {
Motor.B.rotate(-1500 , true);
Motor.C.rotate(-1500 , true);
} catch (Exception E){}
while ( ultra.getDistance() < 15 ) {
Motor.B.backward();
Motor.C.backward();
}
LCD.clear();
LCD.drawString("Distance : "+ultra.getDistance(), 0, 0);
}
Button.waitForAnyPress();
}
}
My old code, which also didn't work:
import lejos.nxt.*;
public class test {
public static void main(String [] args) throws InterruptedException {
UltrasonicSensor ultra = new UltrasonicSensor(SensorPort.S1);
try {
Motor.B.rotate(-720);
Motor.C.rotate(-720);
} catch (Exception E){}
for (int i = 0; i < 5; i++)
{
LCD.drawString("Distance : "+ultra.getDistance(), 0, i);
Thread.sleep(2000);
int maxDistance = ultra.getDistance();
if (maxDistance < 15){
Motor.B.stop();
Motor.C.stop();
}
}
Button.waitForAnyPress();
}
}
Assumptions
Okay, from the looks of things, your code is probably not doing what you want. (In the future, when writing a question on Stack Overflow, please clarify in detail what the expected behavior is, as well as what erroneous behavior you're seeing. Those are usually the first two questions we would ask of you, anyway.)
First of all, you're going to want to ensure that your NXT kit has been set up properly, with your two motors on B and C, and your sensor on S1. If this is so, continue reading.
Code Interpretation
The motor commands:
try {
Motor.B.rotate(-1500, true);
Motor.C.rotate(-1500, true);
} catch (Exception E) {}
look like they're valid motor commands... but wait! You're using a two-wheeled robot, with the motors connected to two wheels that point in opposite directions? But you're using the same distance and direction for your motor's limit angle! If your wheels oppose each other, then this will do nothing but make the robot spin in a circle.
NOTE: Since your motors are configured properly, as written in your comments, ignore this part.
If you change the direction of one of the motors by changing the positive to a negative, then you'll have them both working in unison to move your robot forward (or backwards, if you change the wrong one!)
Also, keep in mind that passing true as the second argument in
Motor.B.rotate(-1500, true);
Motor.C.rotate(-1500, true);
makes this function in a very specific fashion, according to the Javadoc (emphasis mine):
If immediateReturn is true, method returns immediately and the motor stops by itself.
If any motor method is called before the limit is reached, the rotation is canceled.
The first sentence means that this does what we want it to: It tells our motor to find the right limit angle by itself, but don't make our program wait for it. However, the second sentence means that if any other motor commands are called, it will stop moving to the given limit angle. Yeah, that's right. Those next few lines make us stop moving the motors and do what they say instead.
Now, this code is problematic for two reasons:
while (ultra.getDistance() < 30) {
Motor.B.backward();
Motor.C.backward();
}
First, these commands will IMMEDIATELY stop our previous two motor commands from executing, which basically means the motors will jump straight to going "backwards" and looping until the distance sensor reads greater than or equal to 30. This is actually what we want, but we need a bit more...
Second, after your sensor reads the distance greater than 30, your motors are never told to stop! So even when your program is showing you the distance, and waiting for your button to be pressed, it'll still be moving!
A Solution
Okay, there's a few things that need to change:
Your initial motor command is being blocked out by the later commands that tell it to move to the correct position.
Your motors don't stop when they're supposed to.
Below is your code, edited to address each of these issues. I've included notes where I've made changes to show you what I've changed.
import lejos.nxt.*;
public class test {
public static void main(String [] args) throws InterruptedException {
UltrasonicSensor ultra = new UltrasonicSensor(SensorPort.S1);
for (int i = 0; i < 5; i++) {
// No initial motor movement (because it did nothing anyway)
// We change this to approach from either direction.
while (ultra.getDistance() != 30) {
// Check whether it's behind or ahead of it.
// Assuming that B- and C- increase distance, and B+ and C+ decrease it (depends on robot configuration).
// This is called a feedback loop, by the way.
if (ultra.getDistance() < 30) { // Move forward (distance+)
Motor.B.backward();
Motor.C.backward();
} else { // Move backward (distance-)
Motor.B.forward();
Motor.C.forward();
}
}
// We only get here when the distance is right, so stop the motors.
Motor.B.stop();
Motor.C.stop();
LCD.clear();
LCD.drawString("Distance : "+ultra.getDistance(), 0, 0);
}
Button.waitForAnyPress();
}
}
Now, this code isn't perfect; it may have a tendency to oscillate between forward and backward on slippery surfaces (which may turn it slightly to the left or right due to differences in applied torque), or if the sensor misses the correct position and the robot overshoots it.
This code also doesn't wait until the robot stabilizes at the given position, just until the sensor first reports the correct one. Again, this may result in sliding around a bit if the wheels don't have decent traction, the motors are set to smooth acceleration, or if the motors run at too high of a speed.
To correct these flaws, you'd need a more advanced type of feedback loop which accounts for acceleration and slip, and you'd need to wait until the robot stabilizes at the correct position for a short period of time before stopping the motors.
However, this should get you moving in the right direction, so to speak.
EDIT Corrected drive motor directionality, as specified in the comments.

Categories

Resources