Software Design For Critical Resource Hardware

Answering in a separate thread to allow for expansion on this topic:

The GoPiGo3 red board has lots of set and get commands:

$ grep "GET_" ~/Dexter/GoPiGo3/Software/Python/gopigo3.py
        GET_MANUFACTURER,
        GET_NAME,
        GET_HARDWARE_VERSION,
        GET_FIRMWARE_VERSION,
        GET_ID,
        GET_VOLTAGE_5V,
        GET_VOLTAGE_VCC,
        GET_MOTOR_ENCODER_LEFT,
        GET_MOTOR_ENCODER_RIGHT,
        GET_MOTOR_STATUS_LEFT,
        GET_MOTOR_STATUS_RIGHT,
        GET_GROVE_VALUE_1,
        GET_GROVE_VALUE_2,
        GET_GROVE_STATE_1_1,
        GET_GROVE_STATE_1_2,
        GET_GROVE_STATE_2_1,
        GET_GROVE_STATE_2_2,
        GET_GROVE_VOLTAGE_1_1,
        GET_GROVE_VOLTAGE_1_2,
        GET_GROVE_VOLTAGE_2_1,
        GET_GROVE_VOLTAGE_2_2,
        GET_GROVE_ANALOG_1_1,
        GET_GROVE_ANALOG_1_2,
        GET_GROVE_ANALOG_2_1,
        GET_GROVE_ANALOG_2_2,


$ grep "SET_" ~/Dexter/GoPiGo3/Software/Python/gopigo3.py
        SET_LED,
        SET_SERVO,
        SET_MOTOR_PWM,
        SET_MOTOR_POSITION,
        SET_MOTOR_POSITION_KP,
        SET_MOTOR_POSITION_KD,
        SET_MOTOR_DPS,
        SET_MOTOR_LIMITS,
        OFFSET_MOTOR_ENCODER,
        SET_GROVE_TYPE,
        SET_GROVE_MODE,
        SET_GROVE_STATE,
        SET_GROVE_PWM_DUTY,
        SET_GROVE_PWM_FREQUENCY,

But noticeably missing are:

  • get_motor_limits
  • get_grove_type
  • get_grove_mode

When a user instantiates an EasyGoPiGo3 class object to talk with the red board GoPiGo3 controller, the EasyGoPiGo3 class performs these initializations even if the board was already initialized by another EasyGoPiGo3 object in a different process:

        self.sensor_1 = None
        self.sensor_2 = None
        self.DEFAULT_SPEED = 300
        self.NO_LIMIT_SPEED = 1000
        self.set_speed(self.DEFAULT_SPEED)
        self.left_eye_color = (0, 255, 255)
        self.right_eye_color = (0, 255, 255)
        self.use_mutex = use_mutex

There is no such thing as a “single, visible to every EasyGoPiGo3 object, red board software object” that could remember the state of the speed limit, provide access to any initialized sensors, and remember the type and mode of the Grove ports.

For most use cases this really is esoteric. The GoPiGo3 is so phenomenally rich with learning capabilities, that only a very advanced user would need this complexity, and that advanced user is not prevented from creating a single software access point to the red board and a software system design that offers multi-threaded, multi-process red board state access and management.

Can classes have privately global flags/variables? That is, something local to the class that can be set and read by any instance of the class?

Yes, but not across different processes’ memory space. It would be a big security nightmare to default to allowing access to an object’s values from outside the process.

That said, Python is replete with packages that make interprocess communication and parallel processing easier than writing it from scratch. Albeit, simple APIs for complex concepts do not make designing with complex concepts easy.

2 Likes

Maybe it’s me, but my entire SQA self shudders at the thought of blindly initializing something that might already be in use, without some kind of flag or mutex to signal its state.

Maybe something like the “lock” that apt gets on the update repository directory - if the “lock” file exists, the speed has already been set board has already been initialized and should be left alone.

2 Likes

Oh, weird. I have a version somewhere of easygopigo3.py that has an extra parameter to control whether it should get initialized completely or not.
Why is it not there???

2 Likes

There was a branch that got out of sync with the main. Don’t worry about it. It is a lesson waiting for people like me who try everything not in the book.

Seriously, noinit is not a total solution. A complete object oriented redesign of the GoPiGo3.py software interface would be needed to solve this esoteric issue. Not worth investing.

3 Likes

Correct me if I am wrong, but wouldn’t some type of “initialized” flag cover the lion’s share of the possibilities?

I think that it is reasonable for the user to expect the class to be smart enough to know not to re-initialize and totally reset everything whenever it’s instantiated.

2 Likes

It just isn’t easy to implement. There are a lot of “oops, that’s bad” cases that have to handled.

It is very likely that the process that first instantiates an object will exception out and not unset the flag.

Then there are permissions issues, do all the processes trying to instantiate have access to set the flag.

The number of people that are going to need this protection is just not worth investing in designing for it - believe me it is not simple and there are some weird gotcha situations that will eat your lunch, or eat you for lunch.

2 Likes

You make valid points, and it is possible that I am being simplistic.

My thoughts were like this:

  1. On boot:
    Unconditionally clear flag if exists.

  2. On instantiation:
    Test if flag exists.

    • If not exist, set it and initialize everything.
    • If exist, skip initialization.
  3. On shutdown:
    Unconditionally clear flag.

This is similar to the way aptitude handles the lock on the repository cache.

2 Likes