How complex is your code?

I’m working on some stuff now and I’m just curious, how complex is everyone’s robot code? I ask because I decided it would be a decent idea to write our own functional classes for just about everything and only use WPILibJ for the really low-level stuff. So far I’ve written just over 5000 lines of code. I’ve created separate classes for just about every functional component of the robot. In some cases, I just extended a WPILibJ class, such as with the compressor. In other cases, I’ve written fully-fledged control classes.

This year, I jumped at the chance to write our robot’s code in Java. Aside from my being the senior programmer, it’s given me a chance to really do what I’ve always wanted in regard to controlling the robot. I love Java’s inheritance capabilities and approach to object oriented programming. I was determined to use both to their fullest extent.

I started by flushing out a number of classes for all the basic components of the robot. I put these in a “devices” packaged and named them all something like GenericJaguar or GenericJoystick. I also created a couple of interfaces. For example, GenericJaguar and GenericVictor both implement GenericSpeedController.

The next thing I worked on was something I call the orders system. Pretty much an order is a small class that gets instantiated and passed as a parameter to an “intelligent component”. An intelligent component is just a class that extends IntelligentComponent, which contains a bunch of methods to accept, process, and retrieve orders from a queue. Orders can be either one-shot or timed interval. They process in the order they are received and orders will be moved from the waiting queue into the active queue until a WaitUntilCompletion order is reached. That way you can send all the orders to make the robot dance across the field in one go, with the proper wait orders spaced throughout, and it will handle it as it goes.

Then I worked on driving. I have TankDriveSystem class, which implements SmartDriveSystem, which extends DriveSystem. The DriveSystem interface contains just the bare minimum functions for driving. The SmartDriveSystem incorporates a procedure for recording sensor data (gyro, accelerometer, encoders) and using it to determine where on the field the robot is. This is of course most useful in autonomous and it depends on the robot being at a known starting position. With the order system, the autonomous routine can tell the robot to go to an X/Y position and the robot will (theoretically) turn towards the target and move forward until it records going the linear distance to the target. The drive package also contains stuff like DriveMotor and DriveJoystick, which are extensions of the generic versions with a side variable (0/1 for left/right) added.

Then came pneumatics. Initially I wasn’t sure if we would be controlling our solenoids with spike relays or the solenoid module so I wrote both classes implementing PneumaticsOutput, with methods like Set, Toggle, and GetLastState. While the generic devices were relatively simple, for the pneumatics extensions I wrote full toggling functionality for ease of use. Then I wrote a SolenoidSystem interface, implemented by GenericSolenoidSystem. That class would just use orders to be triggered, as I had already decided that’s how my kicker class would trigger it. In the future, I could write a SolenoidSystem that was controlled by buttons fairly easily.

Next was the kicker. In package manaipulators I wrote the class that represents our soccer ball kicker. Our kicker is essentially a foot on a pivot with bungie cords. It gets pulled cocked by a cylinder and latched by another cylinder. Then the loading cylinder retracts and we open the latch to fire. During construction I demanded that limit switches be placed on the bot so we could tell when the loading and latching was complete. This proved to be a good idea as it can take several seconds to load when we’re low on air pressure. Anyway, I just set up a multi-step procedure that checks what step we’re at and increments it when conditions are met. I passed two SolenoidSystems (one for the loader, one for the latch) as parameters to the kicker and I send orders to each system to tell it to extend or retract. Everyone on the team was wowed the first time the kicker went through it’s cycle.

Autonomous was an interesting challenge. I wanted to write a system that allowed for easy creation of new routines. Therefore I wrote a Controller class that reads switches on the robot to determine what routine to select, then uses a switch case to instantiate the chosen routine into a generic AutonomousRoutine instance variable. Since all the possible routines (each is a separate class) implement AutonomousRoutine I can call them to execute with the same methods. The Controller also passes to the AutonomousRoutine (regardless of its type) all of the IntelligentComponents it is aware of. Since all the IntelligentComponent extensions can accept orders, the routine controls each component with orders. The routine just loads all the orders all in one go. In the future, I may write a SmartAutonomousRoutine that reacts to sensor data, but that’s a ways off. I have yet to test the autonomous system, but it’s really not that complicated, with probably the most complex thing being the bitfield shifting used to turn a bunch of binary switches into an integer value.

So yeah, that’s my code. It’s highly adaptable and easy to use. Even some of the densest builders on the team could understand how to use my library, as long as they don’t actually look inside the classes :P. My goal was create a codebase where as little code as possible is actually put in the main class. I think I did a pretty smashing job, but we’ll see on the game field. :slight_smile:

Its interesting that you bring this up, because we have a similar degree of complexity, both in terms of code structure, features as well as physical lines of code. The abstraction building you describe is something we also did up front, building a neat little framework that supports and organizes the abstractions(with similar queueing systems etc). We also modularized the logic-level control over entire mechanisms, and have all these moving pieces setup in a threaded environment.
Something interesting we did was to implement an event-driven system that allows for asynchronous control, akin to AWT Events(MouseListener, KeyListener, ActionListener). This makes it so that many things that happen in teleoperated and autonomous modes arent run continuously, instead triggered by a button event or a sensor event or something.

If you are curious, we’ve published the event model and the entire framework code at http://code.google.com/p/grtframework for teams to peruse and use.

Wow, spartango, that’s impressive that you have not only set up a Google Code for your program, but you’ve also added some decent Wiki documentation, complete with code examples and command explanations. It’s not much in the way of professional documentation, but since the code’s meant for less experienced programmers, the way that you’ve written it is perfect, telling the reader just what they need to know while not launching into any long explanations. Very nicely done.

Thank you. I’d appreciate any feedback you’ve got about the framework, and would love to implement any of the features you have, Lord_Jeremy, into it.
I apologize about the poor docs, hopefully the javadoc makes up for that somewhat :wink:

I’ve got my code set up on an ssh-enabled SVN, but I’m afraid I can’t give login details because I didn’t have time to set up access controls. I started out with insane documentation but as time became more precious I had to forego that in favor of actual functionality. When I have more time to correct some stupid hacks and finish the documentation I’ll release my libraries.

One thing I did in my system that may or may not be a good idea is component registration. The way it works is that say you construct a TankDriveSystem. The only parameter it takes is an instance ID, which is an int you set so you can tell that TankDriveSystem apart from other TankDriveSystems, for example in autonomous handling. Anyway, after you construct a TankDriveSystem, you have to call RegisterMotor with a DriveMotor parameter. In this way, you give the TankDriveSystem instances of four (or two, or six, or seventeen) motors that are stored in a vector. The DriveMotor class contains an instance variable for the “control index,” pretty much whether it’s left or right. In something like holonomic drive systems, you might have four or even eight control indexes, while there are just two for tank drive. Next, in your teleopInit, you need to call RegisterJoystick with a DriveJoystick parameter. Since a DriveJoystick is just a GenericJoystick with the addition of a control index parameter, you can still use DriveJoystick members in your main class in other classes that just take a GenericJoystick. Then you have to ensure all settings are in place, by calling SetDriveMode (Linear or LowParabolic), SetMaxSpeed, and SetDeadzones. When I release my main class, you’ll see what I mean.

When I began using the registration concept, my idea was that I wouldn’t have to write half a million constructors to handle cases of two motors, four motors, or whatever. It also saves memory because when you hit autonomousInit, the DriveJoystick vector is cleared. I figured that the overhead this system created wouldn’t be too much of a problem, considering how powerful the cRIO is. So far the only issue I had with the system was when I forgot to call the constructor on the Kicker, it kept throwing NullPointerException whenever I registered anything (I’m ashamed to say that it took me a half-hour to realize my mistake). In the drive class, conditionals testing the control index of a DriveMotor and DriveJoystick when they’re pulled out of the vector are used to determine whether they’re left or right. I think the biggest advantage of the registration is that the functions RegisterJoystick, ClearJoysticks, RegisterMotor, and ClearMotors are all implemented from the interface DriveSystem, so the drive system can be set up without really knowing exactly what kind of drive system it is. If I created two or three different tank drive systems that each used a different algorithm for controlling speed I would only have to change the member variable type and the constructor in the main class.

EDIT:
By the way, I’m extremely interested in your even-driven implementation. I don’t really have time or the wherewithal to incorporate that into my libraries but post-season I may definitely look into that. Also, I just had a pretty cool idea. Currently the only really specific classes in my system are the autonomous routines and the kicker class. In that vein I named the kicker class Kicker2010. It pretty much just uses a stepping system, with limit switch tests, to execute a series of steps to load and fire the kicker. My idea is to create an interface SteppingSystem and class GenericSteppingSystem that you could register steps with. Heh, I’d probably create classes Step, TimedStep, ConditionalStep, and TimedConditionalStep. That way you could “build” your array of components into a functional system that acts at the touch of a button. Heh, I doubt I’ll implement that for our Regional (again, I would need much testing before declaring such a system fit for the competition) but it’s another post-season project.

2ND EDIT:
I just took a brief glance at your GRT framework and you did something I said I was going to do but completely forgot about! I need to implement a Disable and Enable order so that specific components are loaded and unloaded on demand…

Heh, it’s funny you should mention this because I did the exact same thing last year, my senior year on my high school team. Except I was using C++. After reading an awesome C++ book and having an “aha!” moment about polymorphism and object-oriented design, I was disappointed with WPI’s failure to fully utilize these features and determined to basically rewrite the library, completely ignoring high-level WPI classes such as Gyro and PID and writing wrappers around the low-level hardware classes. Unfortunately, I never got my library working due to memory management bugs, but it was quite an experience! In one build season I went from a very limited knowledge of embedded C to an expert in object-oriented program design and implementation. In fact, I think C++ gave me a little too much to chew: I stumbled upon the template system and was no later trying to utilize template meta-programming in the robot code. For example, if you tried to instantiate an analog input on a channel that did not exist, you would get an error - at compile time. :smiley:

This year, I’m a mentor for 619, we’ve got Java (which is awesome btw; it is so nice not having to worry about memory management!), and I’m teaching object-oriented programming to my team by having us finish the project that I started last year. It’s actually working this time and is really awesome. Oddly enough, it’s almost done now and has around 5,000 lines; I guess that’s the number of lines necessary to make an FRC robot work. :stuck_out_tongue:

We have three basic packages: input, controller, and output. Inputs implement one of two interfaces - AnalogIn, which has a get() method returning a double, and DigitalIn, which has an isOn() method returning a boolean. Outputs implement the similar AnalogOut and DigitalOut interfaces with set() methods. We make extensive use of Java’s inner classes to break everything down into chunks of this simplicity. My rule is that if your sensor is too complex for these interfaces, you haven’t sub-classed it enough.

The controller package contains controllers, which control outputs with inputs using some algorithm in a periodic thread. We also have an asynchronous package for the handling of asynchronous events.

The code isn’t quite finished yet, but we plan to release it later this year.

Lord_Jeremy: be sure to keep working on your project to completion. What you’re doing is probably the fastest way to develop object-oriented programming skills I can think of. An FRC robot provides a great sandbox where you can experiment with design patterns, make mistakes, and refactor the code every week if it isn’t working out. You can’t do that in an industrial setting. Take advantage of the opportunity you have now to learn and explore freely. Even if your code never works well enough to be used on the field, you’ll gain more from the experience of writing it than you will in any classroom. I speak from experience here. It sounds like your code does work well enough to be used on the field, so that’s even more awesome! :slight_smile:

I actually recently started work on a Java project of my own. I’m attempting to write an RTS game engine from the ground up. Although it’s primarily a learning experience, I do intend to bring it to completion. Naturally, I’m fleshing out a beautifully complex inheritance tree, the majority of which is centered around objects.

It all starts with class Dependent, which is the base of all the objects in the game. In the objects package, there are a bunch of interfaces and an abstract class. The interfaces each represent a different functional element. For instance there is an EquipableObject and a MoveableObject and a DamageableObject and etc. They all extend GameObject, which contains some methods common to all objects. There is also the BaseObject abstract class, which extends Dependent and implements a few of the interfaces, containing method implementations generally common to all the types of objects in the game world. At the next level, there is the Unit abstract class, which implements just about all the interfaces and extends BaseObject. This is where most of the actual functionality specific to units gets flushed out. There’s also Equipment, Prop, Projectile, and possibly more. They each implement different sets of interfaces. The other half of my tree is the default stats system. It starts with interface Info, which is empty. Then, for each object type interface, there’s an object info interface. There’s MoveableObjectInfo for MoveableObject and so on. they all extend Info. In each functionality interface (MoveableObject, etc), there’s a method GetInfo which returns the Info subtype specific to that interface. Of course, there is the UnitInfo interface, which extends the info interfaces for all the interfaces Unit implements. Then in the Unit class, there is method GetInfo which returns a UnitInfo. Because of the way Java’s typing works, that returned UnitInfo can be automatically casted to whatever it needs to be. So if DamageableObjectInfo contains a static float flStartingHP I can do unitInstance.GetInfo().flStartingHP.

You guys are all very impressive. We basically rewrote the few vision classes in the WPILibj and ported a lot of functions from nivision.h. We used the default gyro class, but wrote a custom PID controller to turn. The other classes–Joystick, Jaguar, AxisCamera, Encoder, etc–worked very well for us and didn’t require any changes.

I had to rewrite the holonomic drive function (it failed miserably), it took me a loooong time. I named it com.shadowh511.mayor.WesleyCrusher.impulse (yes, i named my classes after TNG-2nd season bridge crew). It was about 14 lines of code rewritten 100 times. I have over 50 MB in backups from that function alone

I don’t know much about holonomic drive, but why couldn’t you put the 14 lines in a function and call it 100 times?

Did you destruct Lieutenant Yar?

@kingof1337: no, she was already dead to me

@ideasrule: We had no mentors and we needed to get mecanum strafing and rotating done. I brute-forced it.

With no students interested in programming, and my being the closest to learning Java, I just took the default code for the camera, the default code for the tankdrive, and added in simple stuff to use the joystick to control the kicker.

So, not really complex at all.

But it’s intriguing to know that it can get as complex as we need it in the future.