just wondering if it is all that beneficial with the library changes.
I.E. if i write a class now will it be usable next year with out too much work done in updating it.
we are trying to get a strong base built up wince the majority of our programing team is graduating, but we are a little unsure of how to do that. my original thought was get some generic classes up for future use, then i realized that last years code doesn’t compile with this year’s libraries so i’m a little reluctant to spend a lot of time writing something to be used in the future when it may not work.
Thta how you should ALWAYS program imo. Of course you might have to adjust it abit but I doubt you would have to adjust it at all if you code it right. Like make the code generic and not too specific. Like make a class for imaging if you are going to do something with that, you can make a location/coordinate/vertex class if you want to do some lacation stuff. There are tons of generic classes you can make that can be benefitial. Hell you can even make your own library if you want.
its the adjusting bit that i’m worried about, i understand that code that is “written right” as you put it should work for years to come, but i feel that the libraries that we have to use are advancing enough at the moment that it may be a moot point to do it.
We have a few minor classes that end up being re-used from year to year (well, only one year with the new system). Primarily PID-related stuff (dual-motor offsets, PIDInput classes, etc)
A good way to handle library changes is to not call the library in your classes. This will do two things:
make the code usable in places where you did not intend on using it, e.g. if you had a pot + motor you could now use it for an encoder+motor instead.
Allow you to handle library changes.
Last years code dosen’t compile now? it should, since there were no modifications to the interface of existing VI’s, just modifications within and new ones, at least in LabVIEW.
We use c, and while there were not a lit of changes there were just enough to make it throw errors, took a couple of minutes to fix, not a big deal if you know what are you doing, but still a pain, and it kinda defeats the purpose
Keep in mind that any code you use on your robot but do not write during the build season must be COTS software or released for all other teams to use.
Remember inheritances, you can just inherit the prewritten class and in the inherited class, just modify the functions that need modification or something
Thank you for the reminder, the code is meant to be used as an example only. I will be sure to talk it over with mentors on the team so they know the situation and we will make a decision then as to if we should release it for use, and if so how to go about it.
As for inheritance, yes it is often useful and yes it could be used here, but things change and I feel that doing this with inheritance would make the problem worse by tying directly to that class
eh I really don’t care anymore, I am going to publish it, I never really comment my code and its going to take people quite a while to even understand what something does
Comments make good programming practice. I have gotten used to them in LabVIEW, and I can’t see any reason to not at least describe what a function does in C/C++
If they cant understand what it does, then it is either too complex or not implemented “well”.
Example: If I have a VI that processes the lookup of kicker pullback for kick distance, I name it “kicker_lookup_distance.vi” (similar to how you would name a function “lookup_distance” and put it in namespace “kicker”.) Within the VI, it is written in two portions: lookup range (0-1) and scale that to the correct voltage. I have a comment describing what each does, the variables defining vMax and vMin are global constants, and comments describing the process for determining the pullback, etc.
If someone were to look at this VI, knowing that it’s name was “kicker_lookup_distance”, they would be able to tell what it does and what its purpose is. All of my code is written in this way, with things like state machines in seperate VI’s from PID processing, gain scheduling, etc. and seperate VI’s for more complex states (e.g. those that don’t feed a position directly to the PID control). Each is well commented and a decent programmer should be able to figure out what it does between the comments, VI connector names, and VI (function) names.
This is what an API is for. If you design an API before hand and code to that API, then when the new libraries roll around, you can have a single person modify the code that run underneath the API to use the new library, and all the other programmers can continue writing code for the robot since they know what the API looks like.
You could take the time in the off season to write the API, then when the season rolls around. The programmers can write and test code based on previous library releases knowing that it will work when they change to the new library.
On the topic of reusable code. I always make my code as generic as possible. As example, I recently wrote a quick hearts game, and instead of writing a Deal() function that returned a bunch of hands I wrote a Deck class that would allow me to write a game like go fish if I chose to. Generic and reusable code is one of the hallmarks a good software design.
Writing re-usable code for something like a drive train should be pretty easy if you just pay attention to how you build up the code base
Separate your constants from your code. Make a constants.h. Avoid magic numbers, “const RIGHT_DRIVE_SPEED_BIAS = .97” instead of throwing .97 into your function that calculates the drive train speed. Do this with speed controller ports, sensor ports, sensor center voltages (i.e. some potentiometer is at 3.423 volts when this arm is at rest), etc etc etc. Almost anything that is physically constant on the robot should be here. Why is this useful? You moved a motor? One side is a bit too fast? Replaced a pot? You change the code on 1 line and 1 line only, everything else still works perfectly.
Abstraction, use it. For our drive train code, the only public functions are accessible through a DriveTrainManager object. The manager has 2 DriveSide objects, each of which has a SteeringMotor, and each steering motor has its own PID class. DriveTrainManager has NO idea that SteeringMotors even exist, it only knows about the functions in DriveSide. Why? Theoretically (if we didn’t do swerve drive again) the DriveTrainManager and DriveSide objects should be completely reusable, except for the functions that rotate the wheel modules obviously, if we decided to do tank drive.
Avoid coupled code (spaghetti code or the infamous spaghetti and meatballs code :eek: ). Your drive train should not be using code in your arm class, and visa versa, try to encapsulate each physical aspect of your robot into its own completely modular class. Then make 1 class that deals with all the interactions. Why? Say next year you don’t have an arm, so you delete the arm class. Compile the code, and now you have 5 functions in your drive train that are referencing code that no longer exists, so now you have to restructure your drive train code. This can be a complete nightmare once your code base is large enough.
Always make a plan, but never follow it. Planning is going to make you understand the code before you even write it, but as you begin to program you’re going to realize that its all wrong and restructure the code over and over. You should know each object and module in your code before you start writing anything, or else you’re going to end up writing coupled code in a struggle to get thing to work.