Init errors if we don't run a Blocks OpMode before a OBJ OpMode

We have written a software library of java classes and many are exported to Blocks for our rookie team members.

We have noticed that if we run a java OpMode as the first OpMode after boot up we receive Java initialization errors when the first java class that has an Export To Blocks declaration. We do not get this error from the same Java OpMode AFTER we run a Blocks OpMode.

Are we missing something that we need to declare or initialize in the Java OpMode that the Blocks OpModes are doing?

Coach Breton
Bearbotics FTC Head Coach

It is hard to tell what’s going on without the actual errors you are seeing and software you are using.

Can you post the error(s) and your java source code, or a link to your source if you have it up on GitHub.

Not on site today, but here’s a link to our GitHub shared library:

Bearbotics-FTC/SharedLibrary: A library of Java classes for FTC Programmers. End user for Bearbotics is our rookie Blocks programmers. (github.com)

The Java Classes were originally developed under OBJ and were only called from Blocks OpModes. When we transferred all the classes to Android Studio, even the OpModes, we noticed this problem.

No code changes in the library Java classes though. Only noticed because we didn’t have a Blocks OpMode sample to test so ran our Java OpMode and kept getting this error…until we saw the pattern that if we ran a Blocks OpMode BEFORE we didn’t have a problem. Code works fine.

Will make a note to self to get a log of the error tomorrow during our team meeting.

Here’s a snippet of the error from the log files:

01-29 17:08:22.630   993  1194 D HardwareMap: Clearing which device instances have been retrieved
01-29 17:08:22.634   993 26390 V RobotCore: thread: 'OpModeThread' starting...
01-29 17:08:22.661   993 24681 V Robocol : sending CMD_NOTIFY_INIT_OP_MODE(3492), attempt: 0
01-29 17:08:22.663   993 24681 V Robocol : sending CMD_NOTIFY_RUN_OP_MODE(3493), attempt: 0
01-29 17:08:25.371   993  1181 V Robocol : received command: CMD_SET_MATCH_NUMBER(10163) 0
01-29 17:08:25.373   993  1181 V Robocol : received command: CMD_INIT_OP_MODE(10164) TeleOpMode_withDriveToTag_V1_7 (Blocks to Java)
01-29 17:08:25.393   993 26390 V RobotCore: thread: ...terminating 'OpModeThread'
01-29 17:08:25.398   993  1194 I RobotCore: Attempting to switch to OpMode TeleOpMode_withDriveToTag_V1_7 (Blocks to Java)
01-29 17:08:25.401   993  1194 I RobotCore: ******************** START - OPMODE TeleOpMode_withDriveToTag_V1_7 (Blocks to Java) ********************
01-29 17:08:25.487   993  1194 D HardwareMap: Clearing which device instances have been retrieved
01-29 17:08:25.492   993 26391 V RobotCore: thread: 'OpModeThread' starting...
01-29 17:08:25.493   993 26391 V RobotCore: thread: ...terminating 'OpModeThread'
01-29 17:08:25.493   993  1194 E OpModeManager: User code threw an uncaught exception
01-29 17:08:25.501   993 24681 V Robocol : sending CMD_NOTIFY_INIT_OP_MODE(3548), attempt: 0
01-29 17:08:25.506   993  1194 E OpModeManager: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object com.qualcomm.robotcore.hardware.HardwareMap.get(java.lang.Class, java.lang.String)' on a null object reference
01-29 17:08:25.508   993  1194 E OpModeManager: 	at org.firstinspires.ftc.teamcode.PixelEjector.init(PixelEjector.java:27)
01-29 17:08:25.509   993  1194 E OpModeManager: 	at org.firstinspires.ftc.teamcode.TeleOpMode_withDriveToTag_V1_7.runOpMode(TeleOpMode_withDriveToTag_V1_7.java:57)
01-29 17:08:25.511   993  1194 E OpModeManager: 	at com.qualcomm.robotcore.eventloop.opmode.LinearOpMode.internalRunOpMode(LinearOpMode.java:199)
01-29 17:08:25.512   993  1194 E OpModeManager: 	at com.qualcomm.robotcore.eventloop.opmode.OpModeInternal.lambda$internalInit$1$com-qualcomm-robotcore-eventloop-opmode-OpModeInternal(OpModeInternal.java:181)
01-29 17:08:25.513   993  1194 E OpModeManager: 	at com.qualcomm.robotcore.eventloop.opmode.OpModeInternal$$ExternalSyntheticLambda0.run(D8$$SyntheticClass)
01-29 17:08:25.515   993  1194 E OpModeManager: 	at com.qualcomm.robotcore.util.ThreadPool.logThreadLifeCycle(ThreadPool.java:737)
01-29 17:08:25.516   993  1194 E OpModeManager: 	at com.qualcomm.robotcore.eventloop.opmode.OpModeInternal.lambda$internalInit$2$com-qualcomm-robotcore-eventloop-opmode-OpModeInternal(OpModeInternal.java:179)
01-29 17:08:25.517   993  1194 E OpModeManager: 	at com.qualcomm.robotcore.eventloop.opmode.OpModeInternal$$ExternalSyntheticLambda1.run(D8$$SyntheticClass)
01-29 17:08:25.519   993  1194 E OpModeManager: 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
01-29 17:08:25.520   993  1194 E OpModeManager: 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
01-29 17:08:25.521   993  1194 E OpModeManager: 	at com.qualcomm.robotcore.util.ThreadPool$ThreadFactoryImpl$1.run(ThreadPool.java:793)
01-29 17:08:25.522   993  1194 E OpModeManager: 	at java.lang.Thread.run(Thread.java:761)
01-29 17:08:25.527   993  1194 I RobotCore: Attempting to switch to OpMode $Stop$Robot$
01-29 17:08:25.529   993  1194 I RobotCore: ******************** STOP - OPMODE /storage/emulated/0/FIRST/matchlogs/Match-0-TeleOpMode_withDriveToTag_V1_7_(Blocks_to_Java).txt ********************
01-29 17:08:25.535   993  1194 I RobotCore: Pruning old match logs: deleting Match-0-TeleOpMode_GoToPoses.txt
01-29 17:08:25.538   993  1194 D HardwareMap: Clearing which device instances have been retrieved
01-29 17:08:25.538   993 26392 I RobotCore: saving match logcat to /storage/emulated/0/FIRST/matchlogs/Match-0-TeleOpMode_withDriveToTag_V1_7_(Blocks_to_Java).txt
01-29 17:08:25.538   993 26392 I RobotCore: logging command line: exec logcat -d -T '1-29 17:8:25.000' -f '/storage/emulated/0/FIRST/matchlogs/Match-0-TeleOpMode_withDriveToTag_V1_7_(Blocks_to_Java).txt' -n4 -v threadtime UsbRequestJNI:S UsbRequest:S art:W ThreadPool:W System:W ExtendedExtractor:W OMXClient:W MediaPlayer:W dalvikvm:W  *:V
01-29 17:08:25.541   993 24681 V Robocol : sending CMD_SHOW_STACKTRACE(3549), attempt: 0
01-29 17:08:25.544   993 26395 V RobotCore: thread: 'OpModeThread' starting...
01-29 17:08:25.581   993 24681 V Robocol : sending CMD_NOTIFY_INIT_OP_MODE(3557), attempt: 0
01-29 17:08:25.582   993 24681 V Robocol : sending CMD_NOTIFY_RUN_OP_MODE(3558), attempt: 0
01-29 17:08:25.585   993 26392 I RobotCore: Done running exec logcat -d -T '1-29 17:8:25.000' -f '/storage/emulated/0/FIRST/matchlogs/Match-0-TeleOpMode_withDriveToTag_V1_7_(Blocks_to_Java).txt' -n4 -v threadtime UsbRequestJNI:S UsbRequest:S art:W ThreadPool:W System:W ExtendedExtractor:W OMXClient:W MediaPlayer:W dalvikvm:W  *:V
01-29 17:08:25.585   993 26392 I RobotCore: exiting match logcat for /storage/emulated/0/FIRST/matchlogs/Match-0-TeleOpMode_withDriveToTag_V1_7_(Blocks_to_Java).txt

That log file says you’re using a hardwareMap.get() call with an incorrect (or blank) hardware description string, one that doesn’t exist in the hardware map. That should have nothing to do with Java/Blocks. Look in your PixelEjector.init() method for the offending call.

-Danny

Agree it is using and looking up a servo in the hardware map.

With no code change, if we run any Blocks OpMode before we run this Java OpMode the Java OpMode does not complain. By itself the hardware map is not loading for the Java OpMode even though we are extending the Java OpMode from a LinearOpMode.

Then I think you’re doing it wrong. If you load a blocks opmode and that solves your problem, that’s because blocks is populating the hardware map for you - you probably can’t load the hardware map in Java because the hardware map is not being loaded properly using the process you’re using.

Java only loads the hardware map in the background for the main opmode class before RunOpMode() is called, and that process doesn’t “automatically” happen for any other class. You’d need to pass the hardware map to your extra class (if you’re using one) and set the classes hardwareMap to the passed in hardwareMap.

You’re getting the crash because the hardwareMap in your extra class that is trying to access “a” hardwareMap (but it’s not the same as your main program hardwareMap) is empty, it doesn’t have the configuration information loaded.

-Danny

Thanks for your insights, @ddiaz! Will try to resolve by explicitly declare all hardware classes in our library within the Java OpMode.

Even after declaring our library modules and explicitly constructing the java class instance, we are having to run a Blocks OpMode to not get an initialization error on the hardware map.

Shown below is an example of our declaration of two key shared library java classes:

  DrivetrainMecanumWithSmarts ourDrivetrain = new DrivetrainMecanumWithSmarts();
  Vision ourVision = new Vision();

Would you be willing to point me to a GitHub repo with your code, or provide a Google Drive link with a copy of your current code?

-Danny

Sure!

The SharedLibrary code is in the repo posted above in response to @cmacfarlane.

A Java Code implementation using the SharedLibrary is in my Coaches Bot repo. Here’s a link to the teleop one: CENTERSTAGE/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/TeleOpMode_V1_8.java at master · chaosbuster/CENTERSTAGE (github.com)

Actually, as I read the log it appears to me that .get() is being called on a null pointer – i.e., as if the hardwareMap variable itself has not been initialized yet.

The log points to this line in the code:
image

and I think it’s likely that hardwareMap itself isn’t initialized yet. I presume the hardwareMap variable here is coming from the base class (BlocksOpModeCompanion), and it probably doesn’t initialize its fields until a Blocks opmode is requested.

Those are my guesses, at least. The suggested solution – explicitly get the hardwareMap from somewhere not directly related to Blocks – is almost certainly the correct one.

Pm

1 Like

Yup, that’s exactly what I thought is going on.

Your entire library is built on top of the BlocksOpModeCompanion class, which in and of itself isn’t necessarily a problem. The BlocksOpModeCompanion class has a hardwareMap member, meant to be used by a Blocks OpMode to hold a copy of the main HardwareMap. When you use the hardwareMap from within your library, you’re NOT directly accessing the hardwareMap defined/scoped within your main OpMode, you’re instead using the hardwareMap scoped within the BlocksOpModeCompanion class. The problem you’re running into is that the hardwareMap member isn’t actually being loaded with a hardwareMap until a Blocks OpMode copies the hardwareMap into it from within the runOpMode function.

What you essentially want to do is create a public setHardwareMap() method within your library where you pass in your main opMode’s hardwareMap, or you want a constructor to your library class that takes in a hardwareMap so you can initialize the BlocksOpModeCompanion class’ hardwareMap before you try to use it.

The same will go for telemetry and gamepads if you choose to access them in your library.

-Danny

Agreed.

It looks like we will need to split out our library into two based on the target coding language.

You can do that, but why? This is going to be an issue for any library trying to use an OpMode’s HardwareMap - libraries don’t belong to the instance of the program class, you’re going to have the exact same problem if you extend a LinearOpMode or any class that also contains a HardwareMap member. You either have to remove all references to HardwareMaps, Gamepads, and Telemetry in your library (and take them all in as parameters) or you can bite the bullet and initialize the objects on construction. At least this way you support both languages.

-Danny

Please expand on:

We have construction happening of library classes (shown above):

And we have Init methods in all the library classes.

Sure. Currently you do not define a constructor in any of your library classes - as such, you’re just depending on the default constructor. You should set up a constructor that takes in a HardwareMap. For example, your DrivetrainMecanumWithSmarts class should have this:

import com.qualcomm.robotcore.hardware.HardwareMap;
...
public class DrivetrainMecanumWithSmarts extends BlocksOpModeCompanion {
...
   public DrivetrainMecanumWithSmarts( HardwareMap hardwareMapParam ) {
      BlocksOpModeCompanion.hardwareMap = hardwareMapParam;
   }
...
}

and then in order to create your class, you use:

DrivetrainMecanumWithSmarts ourDrivetrain = new DrivetrainMecanumWithSmarts( hardwareMap );

And then you can reference the hardwareMap within the DrivetrainMecanumWithSmarts class to your heart’s content. Technically since you’re using a static member of the class it only has to be done one time ever, but since you never know which class with the same base will be constructed first it’s just easier to initialize each time.

-Danny

Thanks, @ddiaz, for the clarification! In the process of converting some core classes to Java that our veteran team intended to focus on using for Autonomous, we started down this path you mention above of passing the Hardware handle.

In Blocks would we pass the hardware variable?