2

I'm writing a procedure in Python which at it's fundamental level communicates with a motor controller. It is possible for the controller to throw flags indicating that an error has occurred. I'm trying to figure how to best handle these errors.

In the example below, there are three possible errors, a temperature fault, a current limit fault and a voltage fault. I've handled them differently. Is there a correct way or is it subjective?

class motor_fault(Exception):
    def __init__(self,error):
        motor.move_at = 0  #Stop motor
        self.error = error
    def __str__(self):
        return repr(self.value)

motor.velocity_limit = motor.slow
motor.velocity_limit_enable = True
try:
    motor.move_to_absolute = motor.instrument_pos
    while motor.in_position == 0:
        if motor.current_limit == 1:
            motor.move_at = 0 #Stop motor
            print('Motor current error')
            break
        if motor.temp_fault == 1: raise motor_fault('Temperature Fault')
        if motor.voltage_fault == 1: raise voltage_fault:
        time.sleep(0.5)
    else:
        print('reached desired instrument position with no faults')
except motor_temp_fault as e:
    #Not sure what I'd do here...
    print('My exception occurred, value:', e.error)
    pass
except:
    motor.move_at = 0 #Stop motor just in case
    print(' some other fault, probably voltage')
else:
    print (' this is only printed if there were no errors')
finally:
    print ('this is printed regardless of how the try exits')

It seems a lot simpler to drop the whole try:. Just set a flag in the while loop and break. After the loop, look at the flag and see if the while loop exited successfully.

fault = False
while motor.in_position == 0:
    if motor.current_limit == 1:
        fault = 'Motor current error'
        break
    if motor.temp_fault == 1:
        fault = 'Motor temperature error'
        break
    if motor.voltage_fault == 1:
        fault = 'Motor voltage error'
        break
    time.sleep(0.5)
else:
    print('reached waterline with no faults')
if fault:
    motor.move_at = 0 #Stop motor
    print(fault)
    # Now look at the fault string to determine the next course of action.

But that somehow seems wrong or non-pythonic to use a term I don't really understand. Is there really anything wrong with this? Thanks and please keep in mind I'm not CS major and I haven't taken a programming class since 1982.

RyanN
  • 740
  • 8
  • 20
  • It seems like defining MotorFault classes and using try and except as Brian and S.Lotts suggest in their answers is the way to go. To give a little more background for anyone interested. The motor class was written by someone else for me, but I can modify it. The motor's methods are actually translated into JSON-RPC messages sent to the motor controller driver. This makes the motor object a very simple message handler. For this reason, the motor status check shouldn't really go in the motor object. I suppose I could wrap it in another object, but I like the idea of a check() function. – RyanN Dec 30 '10 at 15:35
  • ...The motor controller driver (written in Java) sends status updates via JSON-RPC back to my python program whenever any values change. As you might suspect, there's a separate thread which listens for these messages and updates the motor's attributes. The code I'm writing is a port from a simple version of BASIC which ran on a micro-controller. The code needs to be simple enough that someone with a somewhat limited programming knowledge can modify the behavior if not the functionality. – RyanN Dec 30 '10 at 15:35
  • ... I had to argue hard that Python would be more appropriate than Java so I'm trying to keep the procedural part of the code as simple as possible. – RyanN Dec 30 '10 at 15:36
  • What are all these comments? It's your question. You can **update** your question to contain **all** the information. Please do not add comments to a question when you can simply fix it to be complete. Please fold this extra stuff into the question and then delete the confusing, hard-to-read comments. – S.Lott Jan 04 '11 at 11:59

4 Answers4

2

My approach, for what it's worth, would be to define a small hierarchy of exceptions, say:

class MotorFaultError(Exception) # top level exception
class MotorTempFault(MotorFaultError)
class MotorVoltageFault(MotorFaultError)
# etc

Then, on any error, make sure your API throws one of those. If your API, itself, has to catch an exception from the underlying motor API, wrap that exception in one of your own exceptions.

Rationale:

Your own exception hierarchy is part of the API and serves to isolate the calling code from the specifics of the underlying motor API. By throwing a defined set of exceptions, rather than allowing the motor API's exceptions bubble up, you further hide the underlying API. Doing so makes it easier to drop another motor API in place, for whatever reason, including:

  • You found a better one.
  • You want to do some testing with a mocked-up motor API.

Also, exceptions (rather than flags) are more consistent with the way other Python APIs behave.

Brian Clapper
  • 25,705
  • 7
  • 65
  • 65
0

I'd go for exceptions with many except clauses for all the different exceptions you would like to handle at this point as these cases seem to be exceptional/failure scenarios.

I would not use flags to represent these scenarios as it would add more fields to motor which dont seem to be useful/relevant outside of this use case.

As far as knowing if it is the 'correct' way to handle this well, if both solution works they are both correct!

Hope I was clear enough... ;-)

tousdan
  • 196
  • 1
  • 13
0

I don't see anything wrong with either approach. Personally, I prefer the try-except one, but it's just preference.

Emilio M Bumachar
  • 2,532
  • 3
  • 26
  • 30
0

Is there a correct way

Yes.

or is it subjective?

No.

Use the raise statement.

First, please use CapitalLetters for your unique exceptions

class Motor_Fault( Exception ): pass
class Temperature_Fault( Motor_Fault ): pass
class Voltage_Fault( Motor_Fault ): pass
class Current_Fault( Motor_Fault ): pass

Second, separate the error detection from the rest of your processing.

Third, don't do anything in the exception class. Handle the motor stop business in your application.

Fourth, The motor status check does not belong in your application's motor loop. This is all part of the method function that implements motor.move_to_absolute.

    if motor.current_limit == 1: raise Current_Fault()
    if motor.temp_fault == 1: raise Temperature_Fault()
    if motor.voltage_fault == 1: raise Voltage_Fault()

Fifth, your application loop should look like this.

motor.velocity_limit = motor.slow
motor.velocity_limit_enable = True
try:
    motor.move_to_absolute = motor.instrument_pos
    while motor.in_position == 0:
        time.sleep(0.5)
    print('reached desired instrument position with no faults')
except Motor_Fault, e:
    motor.move_at = 0 #Stop motor
    print(fault)

The motor should raise it's own exceptions. If, for some reason, it can't, then you can "wrap" the motor with some status checking. This isn't ideal, since the motor should raise it's own exceptions.

 def check():
    if motor.current_limit == 1: raise Current_Fault()
    if motor.temp_fault == 1: raise Temperature_Fault()
    if motor.voltage_fault == 1: raise Voltage_Fault()

Call this function just before sleep.

S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • 2
    -1. This is just wrong. There is no correct way and it is in fact subjective. Some ways are better than others. But to say always use exceptions is wrong. – Falmarri Dec 29 '10 at 21:50
  • @Falmarri: Do you have a concrete counter-example? Something specific that would clarify your claim? – S.Lott Dec 29 '10 at 21:51
  • 1
    My claim is that it's subjective. How can I have a concrete proof of that? It's coding style. Being that this is a motor controller, I would almost say go with state flags as handling exceptions is expensive. But I have no idea what the project as a whole looks like so I can't say, and neither can you. – Falmarri Dec 29 '10 at 21:56
  • @Falmarri: "proof"? I don't think my comment said "proof". It looks like I said "counter-example". Which isn't proof, is it? I would think it was just an example. – S.Lott Dec 29 '10 at 22:33
  • 1
    What kind of example should I show you then? Like I said, it's subjective and it depends a lot on the situation. I gave you a counter-example. This being a motor controller, handling exceptions might be too expensive. – Falmarri Dec 29 '10 at 22:35
  • 1
    Exceptions are the mechanism used in Python to represent an error object and (when raised) interrupt the execution of the rest of the code unless someone handles or ignores the error. It is, by definition, the correct way. There's nothing subjective about it, it was made for this. Doing it otherwise would be more or less reinventing the wheel. – Rosh Oxymoron Dec 30 '10 at 00:26