One of the few things everyone seems to agree on is that code duplication is evil. But. Admit it, you’ve done it too: For whatever contrived reasons you take a piece of source code and copy and paste it into some other project. Problem solved, on to the next one. The proper way of doing it would, of course, be creating a library to avoid having the same code in two separate projects.
I’m not so sure about that anymore.
One of the big problems I see with library design is how to make a library a) general enough to be useful in lots of situations, b) simple to extend if need be, without breaking backward compatibility, and c) simple to use. The fourth requirement for libraries, not wasting too much space, seems to be a thing of the past by now, so let’s concentrate on the other ones.
If, as an example, I had to access a CANopen network from a PC, I would start looking for a library that allows me to connect to a CAN interface and proceed to upload and download SDOs, send and receive PDOs and whatnot. The problem here is that such a library would have to support the whole CANopen protocol to be considered useful, as you can’t just implement a small part of it — it’s either all or nothing. Adding the extensibility requirement, you can throw simplicity out of the window. Or can anyone out there show me a CANopen library that is easy to use, well documented in a human-readable language, and stable?
If you need a solution to that problem FAST, you can do two things. The default would be to suck it up and use whatever library seems most suited. The other approach would be what I have come to call the Forth path, or “Pulling a Moore”, from Chuck Moore, the inventor of Forth: If the problem is hard, just modify it to make it simpler. Detractors might call it “cheating”. It’s related to the Not Invented Here Syndrome, which describes the perceived need to Do It Yourself even if someone else has already solved the problem. But what if none of the ready-made solutions satisfy you right now?
So if CANopen is too complicated to just use in your programs, look at the problem as it looked like before you started to solve it. We have a PC that will be connected to a CAN bus. No problem so far, we have an interface that allows us to send and receive raw CAN messages. Now we know that a CANopen message is, basically, just a CAN message with a special ID and payload. The ID tells us what kind of message we are looking at, and, most importantly, how to react to it. If some other node asks you to read a value from your Object Dictionary (OD), which is a kind of database that stores information about your node, you had better send an answer to that request. But as we are a PC that will query other devices, and we KNOW that those other devices won’t start any queries of their own, we can simply omit that part. In fact, we can replace our OD with a few variables. If all we need are heartbeat messages, SDOs and PDOs from the other devices, and we KNOW that heartbeats and PDOs will come in without the need to ask for them, we can skip a big part of CANopen.
What we need to do now is write a few functions that encode and decode SDO upload and download requests, the respective response messages, and PDO and heartbeat messages. Helpfully, our devices go to Operative state without us having to ask them to, so we can skip that part, too. Now all we do is listen for messages on the CAN bus and react to them. If a message is of no use to us, we quietly ignore it.
And lo and behold, problem solved. Weeks of looking for good libraries, experimenting, trying to decipher the few comments that were left in the source, visiting web forums, mailing lists etc., and now we solved the problem by simply… duplicating a piece of work that has been done before. By writing our own stripped-down CANopen layer, that won’t ever be useful to anyone else or in another situation. But boy was that easier and faster.
So there seem to be situations where using, or even writing, a library could make your problem worse. Sometimes, duplicating the parts of a solution that are relevant RIGHT NOW, RIGHT HERE, seems justified. Sometimes you don’t need to pull in 10’000 lines of code just to parse your command line parameters. Just loop over your argv entries and strcmp with “-flag” to set a variable. Done.
That’s the Forth way: Reuse ideas, not implementations. When you write the code, you can adjust it to do exactly what is needed, and nothing more. Simpler code, less configuration hassles. While superficially this looks like a lot of wasted time, I think I’ll use this method a little more often from now on. Like with that CANopen library.
But make sure to encapsulate such shortcuts in your code, just in case you do need a full CANopen stack sometime in the future. You know, with a custom OD containing a few thousand entries that describe some weird communication parameter, and some strange packet type that configures how some other strange packet (that you might receive every four and a half years) should be handled.
Oh, and put that custom CANopen stuff in a library. You might need it somewhere else sometime in the future.