Setters not doing what they are supposed to do in java - java

I have a TV class that has only 99 channel and 20 as the highest volume but my volume and channel setters are not working properly also when i add one to the volume instead of add 1
to 10 which is my default volume it only add one to the given volume digit
my tv class
public class TV
{
private String flag;
private int ch;
private int vol = 10;
public TV(String onOffSwitch, int channel, int volume)
{
flag = onOffSwitch;
ch = channel;
vol = volume;
}
public void setFlag(String onOffSwitch)
{
onOffSwitch = "Off";
}// End of setFlag
public String getFlag()
{
return flag;
}// End of getFlag
public void setCh (int newChannel)
{
if (newChannel >= 99)
{
ch = 99;
}else
{
ch = newChannel;
}
if(newChannel < 0)
{
ch = 0;
}
}//end of setCh
public int getCh ()
{
return ch;
}// End of getCh
public void setVol(int newVolume)
{
if(newVolume >= 20)
{
vol = 20;
}
if(newVolume < 0)
{
vol=0;
}
}// End of SetVolume
public void incrementVolume()
{
vol++;
}
public int getVol()
{
return vol;
}// ENd of getVolume
public String toString()
{
return String.format("%s :%s\n%s:%d\n%s :%d","TV is switched", flag,"TV channel",ch,"TV volume",vol);
}
}// End of TV class
my test Drive class
import java.util.Scanner;
public class TvTest
{
public static void main (String[] args)
{
String tvStatus;
int chan;
int volu;
Scanner input = new Scanner(System.in);
TV tv2 = new TV("off",105,10);
System.out.print(tv2);
System.out.println();
System.out.print("Turn TV On or Off ?");
tvStatus = input.nextLine();
System.out.print("Channel : ");
chan = input.nextInt();
System.out.print("Volume : ");
volu = input.nextInt();
TV tv1 = new TV(tvStatus,chan,volu);
tv1.incrementVolume();
System.out.println(tv1);
}
}
and the test output
TV is switched :off
TV channel:105
TV volume :10
Turn TV On or Off ?on
Channel : 105
Volume : 1
TV is switched :on
TV channel:105
TV volume :2
why isnt my setters working ???

Your constructor should be using your setters:
public TV(String onOffSwitch, int channel, int volume)
{
this.setFlag(onOffSwitch);
this.setCh(channel);
this.setVol(volume);
}
setFlag should be setting flag to the value passed in.
public void setFlag(String onOffSwitch)
{
this.flag = onOffSwitch;
}// End of setFlag

public void setFlag(String onOffSwitch)
{
onOffSwitch = "Off";
}// End of setFlag
onOffSwitch variable is not a field. That's the name in the parameter list, should be flag instead.

public void setVol(int newVolume)
{
if(newVolume >= 20)
{
vol = 20;
}
if(newVolume < 0)
{
vol=0;
}
}// End of SetVolume
That setter does not do anything unless the new value is out of range.

The problem isn't with your setters/getters (although they read a bit weird), the problem is, you're ignore them in your constructor...
public TV(String onOffSwitch, int channel, int volume)
{
flag = onOffSwitch;
ch = channel;
vol = volume;
}
Try using something like
public TV(String onOffSwitch, int channel, int volume)
{
setFlag(flag);
setCh(channel);
setVol(volume);
}
Instead...
The variable onOffSwitch is also undefined, so you example won't compile ;). It should read something like...
public void setFlag(String onOffSwitch)
{
flag = onOffSwitch;
}// End of setFlag
Mind you, I'd probably use a boolean value and have turnOn and turnOff methods, but that's just me...

I've been drinking all day so keep this in mind but I'm guess you were too since you couldn't finish this basic assignment. : p
First off, never, ever, I mean ever use a String to represent a two state value. Booleans are nice, so use them.
class Televsion {
power = false; # off
power = true; # on
}
Secondly, your variable names suck. Not to be mean, there is quite a bit of miss matched between them that needless complexes the program. Also don't use abbreviation or short hand variable names. I can understand if this was maybe 20 or 10 years ago, but modern editors tend have an auto completion features that help fill out the code in one or two key strokes.
It looks like you haven't learned about the keyword "this". When you use "this" inside a class, it specificity calls a specific variable or method from the class. The main takeaway is that it lets you avoid any name collisions from arguments and local variables.
class Televsion {
private boolean power;
private int channel;
private int volume;
public Televsion( boolean power, int channel, int volume ) {
this.power = power;
this.channel= channel;
this.volume = volume;
}
}
This way it's a little more saner for the reader ;P
Testing code in a main method is for newbs and loser. JUnit is virtually built into Java now, so why not use it, or you can use TestNG ^.^
Building out a Test suite can make it easier to design and rework code.
You also might want to take a look at Hamcrest as well.
public TelevsionTest {
private Televsion televsion;
#Before
public void boilerplateSetUpCode() {
// The before annonation has it's roots in apsectJ, I beleve.
// In this context it makes this method run before every test.
televsion = new Televsion();
}
#Test
public void testSwitchOn() {
televsion.switchOn();
assertTrue( televsion.getPowerState() == true );
# JUnits assertions are weak, that's why I like hamcrest.
}
}
Try to code for re-usability and maintainability. This can makes life easier in the long run, but a challenge in the shot term. What if you get cable or satellite, will this affect the channel numbers?
Here is a five minute hack on how I might rework the problem.
public class Televsion {
private Logger4j logger;
private ChannelSource channelSource; // Naming is a little weak
private int volume;
private boolean power;
public Tv() {
channelSource= channelSource.LOCAL;
volume = 0;
power = false;
}
public turnOn(){ power = true; }
public void turnOff() { power = false; }
public void muteVolume() { volume = 0; }
public void setVolume( volume ) {
if ( volume > -1 && volume < 100 ) {
this.volume = volume;
} else {
logger.dubug( String.format("Arg: %d", volume) );
// This is anti-pattern of how not to word a debugging statement.
// Plus, I'm lazy...
}
}
public void changeChannelSource( ChannelSource channelSource ) {
this.channelSource = channelSource;
}
public void changeChannelNumber( int channelNumber ) {
channelSource.changeChannelNumber( channelNumber );
}
// I like singletons
public enum ChannelSource {
LOCAL(0), ALT(0), CABLE(0);
private int channelNumber;
private ChannelSource( int channelNumber ) {
this.channelNumber = channelNumber ;
}
public int getChannelNumber() { return channelNumber; }
public void setChannelNumber( channelNumber ) {
// Lazy hack
if ( this.getName() == "LOCAL" ) {
if ( channelNumber > -1 && channelNumber < 100 ) {
this.channelNumber = channelNumber;
} else {
logger.debug(
String.format("Unable to change %s, channel to %d", this.getName(), channelNumber)
);
}
}
}
}
}
Hopefully, if you start improving your coding standards and start learning good programming techniques then you might enjoy coding enough to make an actual Stack Exchange account :)
This is a simple list of things you should learn to make your Java teacher happy.
- Unit Testing
+ Might as well let a computer grade the code then a human, free A's
- Data Structures
+ Just using collections good enough.
+ Side rant, If use this style of matrices, Object[][] I will haunt you down.
- It's inefficient, plus hard for people maintaining this shitty code
- Jagged arrays can work just as well List< List<Object> > or you can even just use a single array, [] and sprinkle in some magic.
- Annotations
+ Helps takes the Java out of Java
- Read Effective Java
+ It's not a difficult book, will make life easier.
- Know a little about concurrency
+ If you use the synchronized keyword, then you know something is wrong.
+ Take a look into concurrent utils API
+ Read Java Concurrency in Practice
- Spring
+ If you want to get paid for knowing Java
I'm sure there are plenty of other things I'm leaving out but that's should be enough for anyone starting out learning Java.

Related

In Java Is it bad practice to pass a method as an argument in another method?

I am in the midst of learning about methods in Java and was curious to know if passing a method as an argument within another method is bad practice. Although I know there are many ways to achieve the same solution there are times in which code written in one way is more efficient than others.
For instance in the code exercise it was asked to create a method with two parameters(name and position)
and create a second method with one parameter(score). Then call both of these methods and display the results using the following score data (1500, 900, 400, 50).
My solution was written as follows:
public class Main {
public static void main(String[] args) {
displayHighScorePosition("Fred",calculateHighScorePosition(1500));
displayHighScorePosition("Ted", calculateHighScorePosition(900));
displayHighScorePosition("Jen", calculateHighScorePosition(400));
displayHighScorePosition("John",calculateHighScorePosition(50));
}
public static void displayHighScorePosition(String name, int highScorePosition) {
System.out.println(name + " managed to get into position " + highScorePosition + " on the high score table");
}
public static int calculateHighScorePosition(int score) {
int position = 4;
if(score >= 1000) {
position = 1;
} else if (score >= 500) {
position = 2;
} else if (score >= 100) {
position = 3;
}
return position;
}
}
This displayed the same output as what was expected by the exercise, but the code written in the exercise was
public class Main {
public static void main(String[] args) {
int highScorePosition = calculateHighScorePosition(1500);
displayHighScorePosition("Fred", highScorePosition);
int highScorePosition = calculateHighScorePosition(900);
displayHighScorePosition("Ted", highScorePosition);
int highScorePosition = calculateHighScorePosition(400);
displayHighScorePosition("Jen", highScorePosition);
int highScorePosition = calculateHighScorePosition(50);
displayHighScorePosition("John", highScorePosition);
}
public static void displayHighScorePosition(String name, int highScorePosition) {
System.out.println(name + " managed to get into position " + highScorePosition + " on the high score table");
}
public static int calculateHighScorePosition(int score) {
int position = 4;
if(score >= 1000) {
position = 1;
} else if (score >= 500) {
position = 2;
} else if (score >= 100) {
position = 3;
}
return position;
}
}
Does it make a difference? i.e. is one a better way to write or will I run into issues down the road when the code becomes much more complex?
You're not passing another method as a parameter, you're passing the return value of a method as a parameter, which is a totally different concept.
Anyway, the second example (apart from being syntactically wrong since you keep re-declaring a variable instead of just re-assigning a value to it) is just more verbose for no apparent gain, in this case.
It would make sense if you then used any of those declared variables someplace else down the method.

Level Calculation with Java

I need to calculate a Level system for a friend of mine. The problem is that I have no clue how I should do that. He gave me a end point how much experience (31536000) is needed for the max level (44)
The level should increase in difficulty as higher you are (like degresive progression or logarithmic) I just have no clue how to calculate it backwards or even forward.
I am not that experience with Java since this is ages for me.
I need every experience needed for each level from 1 to 44 and you reach level 44 by earning 31536000 experience.
I have test some stuff but I am really not good at that and I know this is a little bit to high for me.
public class level{
public static final double base_exp = 1;
public static final double exp_increase_per_level = 0.40;
public static final int max_level = 44;
public static int level (double exp){
int i = 1;
double test_exp = base_exp;
while (i<max_level) {
if (test_exp > exp){
return i;
}
test_exp = test_exp + test_exp * exp_increase_per_level;
i++;
}
return max_level;
}
public static double level_to_exp (int level) {
if(level == 1)
return base_exp;
else {
double prev = level_to_exp(level-1);
return prev + prev * exp_increase_per_level;
}
}
public static double level_to_total_exp (int level) {
if(level == 1)
return base_exp;
else {
return level_to_total_exp(level-1) + level_to_exp(level-1) * (1 + exp_increase_per_level);
}
}
public static void main(String []args){
System.out.println("to level 1,"+level_to_exp(1)+"exp");
System.out.println("to level 2,"+level_to_exp(2)+"exp");
System.out.println("to level 44,"+level_to_exp(44)+"exp");
System.out.println("Total exp to level 44" +level_to_total_exp(44)+" acuumulated");
}
}
This is just basic maths. Your model appears to be:
score = base^level
So to get the level from the score:
log(score) = log(base^level) = level * log(base)
level = log(score) / log(base)
To calculate base, just substitute in level=44 and score=whatever, and rearrange.

Unfair Candy Distribution

I am completing an assignment and am having a hard time figuring out the logic need to make the program work. I do not want a direct answer but could someone point me in the right direction?
Assignment:
Define a class named UnfairCandyDistributor. An UnfairCandyDistributor object represents a mean big brother who is going to divide a set of candies between himself and his hungry little brother. This will be done unfairly: for every candy given to the sibling, the big brother takes for himself a number of additional candies equal to the younger sibling's total. Each UnfairCandyDistributor object should have the same method:
public void nextCandy()
Each time nextCandy is called, the method prints a message about who gets a candy. Each call to nextCandy produces a single line of output. This time the output is the following:
public class TestCandy2 {
public static void main(String[] args) {
UnfairCandyDistributor mean = new UnfairCandyDistributor();
mean.nextCandy(); // 1 for you.
mean.nextCandy(); // 1 for me.
mean.nextCandy(); // 2 for you.
mean.nextCandy(); // 1 for me.
mean.nextCandy(); // 2 for me.
mean.nextCandy(); // 3 for you.
mean.nextCandy(); // 1 for me.
mean.nextCandy(); // 2 for me.
mean.nextCandy(); // 3 for me.
Here The class I have made so far:
public class UnfairCandyDistributor {
private int you;
private int me;
private int extra;
public void nextCandy()
{
if (you != me || extra != you - 1)
{
me++;
System.out.println(me + " for me");
}
else
{
you++;
System.out.println(you + " for you");
extra = 0;
}
}
}
Can't you just add a boolean variable that tells you who to dole out candy to?
public class UnfairCandyDistributor {
private int you = 0;
private int me = 0;
private boolean candyToMe = false;
public void nextCandy()
{
if (candyToMe)
{
for (int i=0; i<you; i++) {
System.out.println("one for me");
me++;
}
else
{
System.out.println("one for you");
you++;
}
candyToMe = !candyToMe;
}
}
or
public class UnfairCandyDistributor {
private int you = 0;
private int me = 0;
private boolean candyToMe = false;
public void nextCandy()
{
if (candyToMe)
{
System.out.println(you + " for me");
me += you;
}
else
{
System.out.println("1 for you");
you++;
}
candyToMe = !candyToMe;
}
}
..depending on whether you want the candies doled out one at a time or in hand-fulls where appropriate.

(Java) Calling methods in constructor?

***I tried searching but I just can't understand what comes up. Sorry.
I am working on a server/client project for school. I am at my wit's end and I am about to give up. I do not have notes from class so I need some help. First I will post the directions I was given, then my current code, then my problem.
SERVER DIRECTIONS:
The Triangle class must have the following instance variables: side_a, side_b, side_c. Create a static variable to keep track of the number of Triangle objects created. Also create a static variable to hold the total of the perimeters of all Triangle objects created.
A three parameter constructor of the Triangle class should assign a trio of input values to the instance variables of the object. If not a valid triangle set all of the sides to 1. Create a separate private method called isValid(). The sum of any two sides of a triangle must be greater than the third in order to represent a valid triangle. No side may be 0 or negative. The constructor should also add 1 to the count and also call a method to calculate and then add the perimeter for that object to an accumulator.
The Triangle class must have the following methods as stated above:
public boolean is_right() public boolean is_isoc()
public boolean is_equil() public boolean is_scal()
public String toString() – returns the values for the 3 sides of the Triangle
You should also add a method to the Triangle class called calc_perim. This method will use the sides of the Triangle object to calculate the perimeter for that object.
addTotalPerim. This method will call calc_perim and add the perimeter for that object to an accumulator.
reduceTotalPerim. This method should subtract the perimeter for that object from the accumulator.
SERVER CODE:
public class Triangle {
private int side_a, side_b, side_c;
private static int count;
**//PROBLEM 1: Java tells me 'perim' is not used.**
private static int perim;
private boolean valid;
public Triangle(int s1, int s2, int s3)
{
side_a = s1; side_b = s2; side_c = s3;
**//PROBLEM 2: Java tells me 'v' is not used.**
boolean v = isValid();
if (v = false)
{side_a = 1; side_b = 1; side_c = 1;}
Triangle.count++;
calc_perim(s1,s2,s3);
addTotalPerim();
reduceTotalPerim();
}
private int calc_perim()
{
int perimeterCalc = side_a + side_b + side_c;
return perimeterCalc;
}
private void addTotalPerim()
{
Triangle.perim += calc_perim();
}
private void reduceTotalPerim()
{
Triangle.perim -= calc_perim();
}
private boolean isValid()
{
boolean valid1;
if (side_a < 1)
{ valid1 = false;}
else if (side_b < 1)
{ valid1 = false;}
else if (side_c < 1)
{ valid1 = false;}
else if ((side_a + side_b) < side_c || (side_a + side_b) == side_c)
{ valid1 = false;}
else
{ valid1 = true;}
return valid1;
}
public boolean is_right()
{
boolean right;
if (((side_a * side_a) + (side_b * side_b)) == (side_c * side_c))
right = true;
else
right = false;
return right;
}
public boolean is_isoc()
{
boolean isoc;
if (side_a == side_b)
isoc = true;
else if (side_a == side_c)
isoc = true;
else if (side_b == side_c)
isoc = true;
else
isoc = false;
return isoc;
}
public boolean is_equil()
{
boolean equil;
if (side_a == side_b && side_a == side_c)
equil = true;
else
equil = false;
return equil;
}
public boolean is_scal()
{
boolean scal;
if (side_a == side_b || side_a == side_c || side_b == side_c)
scal = false;
else
scal = true;
return scal;
}
public String toString()
{
return "Side 1: " + side_a + " Side 2: " + side_b + " Side 3: " + side_c;
}
}
Sorry about formatting but this site has a terrible way of formatting code, unless I'm misunderstanding something...
SERVER PROBLEMS:
What is the correct way to add/subtract the perimeter obtained via method calc_perim to varible perim? The directions say to call the calc_perim method in the constructor but I can't figure out how, so I just made it do its calculations on its own.
In the constructor, after calling method isValid(), why am I told by Java that variable 'v' is not used? Did I call isValid() incorrectly? >>>>> How do I call a method in the constructor? <<<<<
Other than that major issue, the server class works fine.
Suggestions:
Within Triangle class, initialize the static variables.
private static int count = 0;
private static int perim = 0;
Within Triangle constructor, change,
if (v = false)
to
if (v == false)
Change calc_perim as:
private void addTotalPerim()
{
perim += calc_perim();
}
private void reduceTotalPerim()
{
perim -= calc_perim();
}
Why you call reduceTotalPerim() after addTotalPerim(), didn't get this clear.
isValid function should check all combinations like a+b>c, b+c>a, c+a>b, if any one fails should be invalid
The "is not used" message from the Java compiler is technically a warning, not an error, so you could run your program even with the message still in effect if you really wanted to. But your instincts are correct- it's a bad idea to ignore those messages.
In this case, your code has a serious problem. You're calling v = false, which means that you're assigning a value of false to v. Change it to v == false. By using ==, you're doing a comparison, which is what you really want.

Thread missing updates from other threads

I have created three threads in a java program. One is the main program, the others are two classes that extend Thread. The main thread represent a controller for a machine. Another thread is the actuators and the third is the sensors. The controller sets variables in its own class which is read by the actuator thread. The actuator performs certain actions according to the instructions and update its own internal variables. These are in turn read by the sensor thread which reads the actuator variables (representing real world actions) and sets its own internal variables which in turn is read by the controller and we have come full circle. The controller then sets variables according to the new sensed world etc.
The actuators are in a eternal loop sleeping 100 ms in each loop.
The sensors are also in an eternal loop sleeping 20ms per loop.
The system almost works. The main loop will miss the updates from the sensor unless I add a sleep to it as well. My question is now why that is? Even sleep(0) makes the system work. I've placed the statement inside the performJob(Job input) while loop. How does the java main thread without a sleep call act differently than the same thread with?
Concurrency is a fairly new subject to me.
This is the code I am using:
Main:
public class Main {
public static void main(String[] args) {
Controller Press = new Controller();
Press.processUnits(1); // no reset code at the moment, only use 1 at a time
Press.shutdownThreads();
}
}
Controller:
import java.util.LinkedList;
public class Controller extends Thread {
// Constants
static final int STATE_WAITING = 0;
static final int STATE_MOVE_ARMS = 1;
static final int STATE_MOVE_PRESS = 2;
static final int LOADINGARM = 2;
static final int UNLOADINGARM = 1;
static final int NOARM = 0;
static final boolean EXTEND = true;
static final boolean RETRACT = false;
private enum Jobs {
EXTENDRETRACT, ROTATE, MAGNETONOFF, PRESSLOWERRAISE
}
// Class variables
private int currentState;
// Component instructions
private int armChoice = 0;
private boolean bool = false; // on, up, extend / off, down, retract
private boolean[] rotInstr = {false, false}; // {rotate?, left true/right false}
private boolean errorHasOccurred = false;
private boolean pressDir = false;
private Sensors sense = null;
private Actuators act = null;
private LinkedList<Job> queue = null;
// Constructor
public Controller() {
act = new Actuators(0.0f, this);
sense = new Sensors();
act.start();
sense.start();
currentState = STATE_WAITING;
queue = new LinkedList<Job>();
}
// Methods
int[] getArmInstructions() {
int extret = (bool) ? 1 : 0;
int[] ret = {armChoice, extret};
return ret;
}
boolean[] getRotation() {
return rotInstr;
}
int getControllerState() {
return currentState;
}
boolean getPressDirection() {
return pressDir;
}
public boolean processUnits(int nAmount) {
if (run(nAmount)) {
System.out.println("Controller: All units have been processed successfully.");
return true;
} else { // procUnits returning false means something went wrong
System.out.println("Controller: An error has occured. The process cannot complete.");
return false;
}
}
/*
* This is the main run routine. Here the controller analyses its internal state and its sensors
* to determine what is happening. To control the arms and press, it sets variables, these symbolize
* the instructions that are sent to the actuators. The actuators run in a separate thread which constantly
* reads instructions from the controller and act accordingly. The sensors and actuators are dumb, they
* will only do what they are told, and if they malfunction it is up to the controller to detect dangers or
* malfunctions and either abort or correct.
*/
private boolean run(int nUnits) {
/*
Here depending on internal state, different actions will take place. The process uses a queue of jobs
to keep track of what to do, it reads a job, sets the variables and then waits until that job has finished
according to the sensors, it will listen to all the sensors to make sure the correct sequence of events is
taking place. The controller reads the sensor information from the sensor thread which runs on its own
similar to the controller.
In state waiting the controller is waiting for input, when given the thread cues up the appropriate sequence of jobs
and changes its internal state to state move arms
In Move arms, the controller is actively updating its variables as its jobs are performed,
In this state the jobs are, extend, retract and rotate, pickup and drop.
From this state it is possible to go to move press state once certain conditions apply
In Move Press state, the controller through its variables control the press, the arms must be out of the way!
In this state the jobs are press goes up or down. Pressing is taking place at the topmost position, middle position is for drop off
and lower is for pickup. When the press is considered done, the state reverts to move arms,
*/
//PSEUDO CODE:
//This routine being called means that there are units to be processed
//Make sure the controller is not in a blocking state, that is, it shut down previously due to errors
//dangers or malfunctions
//If all ok - ASSUMED SO AS COMPONENTS ARE FAULT FREE IN THIS VERSION
if (!errorHasOccurred) {
//retract arms
currentState = STATE_MOVE_ARMS;
queue.add(new Job(Jobs.EXTENDRETRACT, LOADINGARM, RETRACT));
queue.add(new Job(Jobs.EXTENDRETRACT, UNLOADINGARM, RETRACT));
performWork();
//System.out.println("Jobs added");
//while there are still units to process
for (;nUnits != 0; nUnits--) {
//move the press to lower position, for unloading
currentState = STATE_MOVE_PRESS;
//rotate to pickup area and pickup the metal, also pickup processed
currentState = STATE_MOVE_ARMS;
queue.add(new Job(Jobs.EXTENDRETRACT, LOADINGARM, EXTEND));
queue.add(new Job(Jobs.EXTENDRETRACT, UNLOADINGARM, EXTEND));
performWork();
//retract and rotate
queue.add(new Job(Jobs.EXTENDRETRACT, LOADINGARM, RETRACT));
queue.add(new Job(Jobs.EXTENDRETRACT, UNLOADINGARM, RETRACT));
performWork();
//state change, press moves to middle position
currentState = STATE_MOVE_PRESS;
//state change back, put the metal on the press, drop processed and pull arms back
currentState = STATE_MOVE_ARMS;
queue.add(new Job(Jobs.EXTENDRETRACT, LOADINGARM, EXTEND));
queue.add(new Job(Jobs.EXTENDRETRACT, UNLOADINGARM, EXTEND));
queue.add(new Job(Jobs.EXTENDRETRACT, LOADINGARM, RETRACT));
queue.add(new Job(Jobs.EXTENDRETRACT, UNLOADINGARM, RETRACT));
performWork();
//state change, press the metal in upper position
currentState = STATE_MOVE_PRESS;
//repeat until done
}
//unload final piece
//move the press to lower position for unload
//rotate and pickup processed piece
//drop it off at unloading and wait for more orders
currentState = STATE_WAITING;
}
return true;
}
private boolean performWork() {
while (!queue.isEmpty()) {
performJob(queue.removeFirst());
}
return true;
}
private boolean performJob(Job input) {
//The purpose of this function is to update the variables and wait until they are completed
// read in the job and check appropriate sensors
boolean[] data = sense.getSensorData(); // LExt, LRet, UlExt, UlRet
printBools(data);
int Instruction = input.Instruction;
boolean skipVars = false;
if (input.Job == Jobs.EXTENDRETRACT) {
if (currentState != STATE_MOVE_ARMS){
System.out.println("Wrong state in performJob. State is "+currentState+" expected "+STATE_MOVE_ARMS);
return false;
}
if ((Instruction == LOADINGARM) && (input.Bool)) skipVars = data[0];
if ((Instruction == LOADINGARM) && (!input.Bool)) skipVars = data[1];
if ((Instruction == UNLOADINGARM) && (input.Bool)) skipVars = data[2];
if ((Instruction == UNLOADINGARM) && (!input.Bool)) skipVars = data[3];
}
if (!skipVars) {
// if sensors not at intended values, update correct variables
System.out.println("Controller: Did not skip vars");
switch (input.Job) {
case EXTENDRETRACT:
armChoice = (Instruction == LOADINGARM) ? LOADINGARM : UNLOADINGARM;
bool = input.Bool;
break;
case ROTATE:
break;
case MAGNETONOFF:
break;
case PRESSLOWERRAISE:
break;
default:
System.out.println("Default called in performJob()");
break;
}
}
// listen to sensors until completed
boolean done = false;
System.out.println("Waiting for sensor data.");
//System.out.print("Instruction is "+Instruction+" and bool is "); if (input.Bool) System.out.print("true\n"); else System.out.print("false\n");
while (!done) {
data = sense.getSensorData();
// REMOVING THIS TRY STATEMENT BREAKS THE PROGRAM
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
System.out.println("Main thread couldn't sleep.");
}
// Check appropriate sensors
if (input.Job == Jobs.EXTENDRETRACT) {
if ((Instruction == LOADINGARM) && (input.Bool)) done = data[0];
if ((Instruction == LOADINGARM) && (!input.Bool)) done = data[1];
if ((Instruction == UNLOADINGARM) && (input.Bool)) done = data[2];
if ((Instruction == UNLOADINGARM) && (!input.Bool)) done = data[3];
}
}
// reset all variables
armChoice = 0;
bool = false;
// when done return
System.out.println("Finished "+input.Job);
return true;
}
public void shutdownThreads() {
sense.shutDown();
act.shutDown();
}
private class Job {
// Class variables
Jobs Job;
int Instruction;
boolean Bool; // used for directions, up/down, left/right, extend/retract
// Constructor
Job(Jobs newJob, int newInstruction, boolean dir) {
Job = newJob;
Instruction = newInstruction;
Bool = dir;
}
}
private void printBools(boolean[] input) {
System.out.println();
for (int i = 0; i < input.length; i++) {
if (input[i]) System.out.print("true "); else System.out.print("false ");
}
System.out.println();
}
}
Actuators:
public class Actuators extends Thread {
// Constants
private final int ARM = 0, ROTATE = 0; // array indexes - which arm, rotate yes/no?
private final int DIR = 1, ROTDIR = 1; // array indexes - which direction ext/ret, rotate direction
private final int EXT = 1;//, RET = 0;
private final double ARM_SPEED = 5.0;
// Class variables
private Controller Owner = null;
private boolean run = true;
// Constructor
Actuators(float nPos, Controller Owner) {
Reality.changeAngle(nPos);
this.Owner = Owner;
}
// Methods
private void rotate(boolean dir) {
float nAngle = dir ? 0.1f : -0.1f;
Reality.changeAngle(nAngle);
}
public void run() {
while (run) {
try {
sleep(100);
} catch (InterruptedException e) {
System.out.println("Actuators couldn't sleep");
}
// read variables in controller
int nState = Owner.getControllerState();
if (nState == Controller.STATE_MOVE_ARMS) {
boolean[] rot = Owner.getRotation();
if (rot[ROTATE]) { // Rotation?
rotate(rot[ROTDIR]);
} else { // or arm extensions
int[] instr = Owner.getArmInstructions();
if (instr[ARM] != Controller.NOARM) { // 0 = no arm movement
//System.out.println("Actuator arm is "+instr[ARM]);
double dir = (instr[DIR] == EXT) ? ARM_SPEED : -ARM_SPEED; // 1 = extend, 0 = retract
Reality.changeArmLength(instr[ARM], dir);
}
}
}
}
}
void shutDown() {
run = false;
}
}
Reality is a class composed of static fields and methods, written to by the actuators and read by sensors.
public class Reality {
// Constants
static private final double EXTEND_LIMIT = 100.0;
static private final double RETRACT_LIMIT = 0.0;
// Variables
private static float ArmsAngle = 0.0f;
// Read by Sensor
static double LoadingArmPos = 0.0;
static double UnloadingArmPos = 0.0;
// Methods
static void changeAngle(float newAngle) {
ArmsAngle = ArmsAngle + newAngle;
if ((ArmsAngle < 0.0f) || (ArmsAngle > 90.0f))
System.out.println("Reality: Unallowed Angle");
}
static void changeArmLength(int nArm, double dPos) { // true = extend, false = retract
switch (nArm) {
case Controller.LOADINGARM:
LoadingArmPos += dPos;
checkArmPos(LoadingArmPos);
break;
case Controller.UNLOADINGARM:
UnloadingArmPos += dPos;
checkArmPos(UnloadingArmPos);
break;
default:
System.out.println("Arm other than 2 (load) or 1 (unload) in changeArmLength in Reality");
break;
}
}
static float senseAngle() {
return ArmsAngle;
}
static private boolean checkArmPos(double dPos) {
// Allowed positions are 100.0 to 0.0
if ((dPos > EXTEND_LIMIT) || (dPos < RETRACT_LIMIT)) {
System.out.println("Arm position impossible in reality. Is "+dPos);
return true;
} else {
return false;
}
}
}
Finally the sensors:
public class Sensors extends Thread {
// Constants
private final double EXTENDED = 100.0;
private final double RETRACTED = 0.0;
private final double MARGIN = 0.1;
// Class Variables
private boolean run = true;
// Read by Controller
private boolean LoadingExtended = true;
private boolean LoadingRetracted = true;
private boolean UnloadingExtended = true;
private boolean UnloadingRetracted = true;
// Constructor
Sensors() {
LoadingExtended = false;
LoadingRetracted = true;
UnloadingExtended = false;
UnloadingRetracted = true;
}
// Methods
boolean senseLoadingExtended() {
return (Math.abs(Reality.LoadingArmPos - EXTENDED) < MARGIN);
}
boolean senseLoadingRetracted() {
return (Math.abs(Reality.LoadingArmPos - RETRACTED) < MARGIN);
}
boolean senseUnloadingExtended() {
return (Math.abs(Reality.UnloadingArmPos - EXTENDED) < MARGIN);
}
boolean senseUnloadingRetracted() {
return (Math.abs(Reality.UnloadingArmPos - RETRACTED) < MARGIN);
}
// called by Controller
boolean[] getSensorData() {
boolean[] ret = {LoadingExtended, LoadingRetracted, UnloadingExtended, UnloadingRetracted};
return ret;
}
// Sensor primary loop
public void run() {
while (run) {
try {
sleep(20);
}
catch (InterruptedException e) {
System.out.println("Sensors couldn't sleep");
}
LoadingExtended = senseLoadingExtended();
LoadingRetracted = senseLoadingRetracted();
UnloadingExtended = senseUnloadingExtended();
UnloadingRetracted = senseUnloadingRetracted();
}
}
void shutDown() {
run = false;
}
}
Not all fields and functions are read in this version. The program is a reworking of a previous single thread application mostly using function calls. I've cleaned up the code a bit for readability. Constructive design remarks are welcome even though it was not the original question. There is something really fishy going on. I am usually not a superstitious coder but I can for example replace the sleep call with a System.out.println() call and the program will work.
Google for 'Producer consumer queue'.
Don't use sleep() for inter-thread comms unless you want latency, inefficiency and lost data. There are much better mechanisms available that avoid sleep() and trying to read valid data directly from some shared/locked object.
If you load up 'comms' objects with commands/requests/data, queue them off to other threads and immediately create another comms object for subsequent communication, your inter-thread comms will be fast and safe, no sleep() latency and no chance of any thread reading data that is stale or being changed by another thread.
What occurred here was most probably a Memory Consistency Error. When the controller class set the internal control variables and then entered the loop waiting for sensors it most likely prevented the Actuators and Sensors classes from properly updating the readings seen to the controller and as such prevented the controller from seeing the correct values. By adding the synchronize statement to all functions which read from another class the problem was solved. I can only speculate that the sleep call had the controller thread enter a synchronized block of some kind which let the other threads' changes to the variables become visible.
Which JVM are you using?
As fast workaround you can set volatile for fields that shared between threads.
Also please look to actors' approach for messaging: http://doc.akka.io/docs/akka/snapshot/java/untyped-actors.html

Categories