Skip to content

Development

This is a guide for developers working on the navproc and logging systems at MBARI. There are two sections maintained here to capture the differences in the development stack for the two main ships (Rachel Carson and soon, the David Packard)

Rachel Carson

For the software running on the Rachel Carson, there are four types of processes that run to support data management. They are:

  1. Navproc processes which are LCM publishers or serial writers that are started and managed by procman
  2. logr processes which consist of LCM subscribers that read in messages from navproc processes, create log file and then publish the messages again.
  3. bcserver processes which consume the messages being published by the logr processes and stage the latest data items so that clients can grab the latest data over UDP without crossing into the ROV subnet.
  4. dm-to-amqp process which takes data from the bcserver and pushes summary messages to the AMQP server on shore.

Code

Class Diagram

--- title: Navproc/Logging Classes --- classDiagram class mbari_object{ #bool mShowStatus #std::string mClassName #std::string mObjectName +mbari_object(const std::string &class_name, const std::string &object_name) +std::String get_class_name() +set_class_name(const std::string &name) +status_on() +status_off() +bool is_status_on() +info_msg(const std::string &msg, bool nl) +warn_msg(const std::string &msg, bool nl) +err_msg(const std::string &msg, bool nl) +debug_msg(const std::string &msg, bool nl) +status_msg(status_level lvl, const std::string &msg, bool nl) +status_prefix(const std::string &level) } class lcm_interface{ -lcm::LCM*mLcm_ptr -bool mStopHandlerThread -std::thread mHandlerThread -bool mHandlerTimeoutWarings +lcm_interface(const std::string &name) +initialize(const std::string &lcm_url) +add_publisher(lcm_publisher &publisher) +add_subscriber(lcm_subscriber &subscriber) +start() +stop() +timeout_warnings_on() +timeout_warnings_off() +bool is_status_on() -server() } class lcm_publisher{ #lcm::LCM \*mLcm_ptr #std::string mChannelName #uint8_t \*mDataBuf #int mDataLen +lcm_publisher() +std::string get_channel_name() +set_channel_name(std::string& name) +set_lcm_instance(lcm::LCM* lcm_ptr) #create_message_buffer(int enc_sz) #bool publish_message(MessageType &msg) #bool encoded_size_error(int enc_sz) #bool publish_message(int enc_sz) } class lcm_serial_string_pub{ -std::string mProcessId -int64_t mSequence -corenav::serial_msg_string_t mMsg +lcm_serial_msg_string_pub(std::string &channel) +bool publish(corenav::serial_msg_string_t &msg) } class lcm_nmeaGPS_pub{ -std::string mProcessId -int64_t mSequence -corenav::nmeaGPS_t mMsg +lcm_nmeaGPS_pub(std::string &name) +bool initialize(std::string &channel) +bool publish(corenav::nmeaGPS_t &msg) } class logr_item_list{ -logrItemNode*head -logrItemNode*tail -float update_period -int m_item_count +logr_item_list() +load_config(std::string filename) +createlognode(std::vector~string~ vec) +display() } class item_status{ +bool item_valid +double age_seconds +int64_t sequence +bool payload_connected +bool payload_stale +double payload_timestamp +int64_t payload_age_seconds +int64_t payload_sequence } class lcm_subscriber{ #double mRxTime #int64_t mRxSequence #int64_t mSequence #double mTimeStamp #string mChannelName #mutex mDataMutex #semaphore mSemaphore +lcm_subscriber() +bool initialize(std::string &channel) +string get_channel_name() +set_channel_name(std::string &name) +handle_msg(lcm::ReceiveBuffer *rbuf, std::string &chan) +int64_t get_sequence() +double get_timestamp() +double get_receive_time() +double get_receive_age() +double get_receive_sequence() +double get_message_latency() +double get_message_age() +get_item(std::string &name, item_status &status, bool &val) +get_item(std::string &name, item_status &status, int64_t &val) +get_item(std::string &name, item_status &status, double &val) +get_item(std::string &name, item_status &status, std::string &val) +set_semaphore(mbari::semaphore &sem) +post_semaphore() -process_msg(lcm::ReceiveBuffer*rbuf) } class lcm_nmeaGPS_sub{ -corenav::nmeaGPS_t mMsg +nmeaGPS_sub(std::string &name) +bool initialize(std::string &channel) +get_msg(corenav::nmeaGPS_t &msg) +get_item(std::string &name, item_status &status, bool &val) +get_item(std::string &name, item_status &status, int64_t &val) +get_item(std::string &name, item_status &status, double &val) +get_item(std::string &name, item_status &status, std::string &val) -process_msg(lcm::ReceiveBuffer *rbuf) } mbari_object <|-- lcm_interface mbari_object <|-- lcm_publisher mbari_object <|-- lcm_subscriber lcm_publisher <|-- lcm_serial_string_pub lcm_publisher <|-- lcm_nmeaGPS_pub lcm_subscriber <|-- lcm_nmeaGPS_sub

The navproc related code is stored in a BitBucket repository and consists of two main repositories. The navproc-common repository and the navproc-process repository. To manage the navproc processes, procman from the libbot2 library is used. When the libbot2 library is built (as specified in the computer_setup.md file), an executable named 'bot-procman-deputy' is installed in /usr/local/bin. This deputy process is started and somehow it knows to read in the bot-proc.cfg file that is located in the navproc-processes directory under the bin/(platform) directory. This configuration file dictates what processes are the be started, what 'host' they are associated with and if they should be auto respawned if they die.

To develop these processes, you would need to build up a computer using the computer setup instruction and then check out the navproc-common and navproc-process repos. In the navproc-process repo, you can then run 'make' which will generate the executables that will be started up by the bot-procman-deputy. Note that as part of the make, the lcm types that are defined in navproc-common are processed into _t.hpp files located in the corenav directory local to each publisher that is defined in the src directory. As an example, you can look at the NMEA GPS LCM Type. When 'make' gets run in the navproc-process directory, for this example, a 'corenav' subdirectory gets created under the nmeaGPS_pub directory in navproc-process/src directory and all the LCM types are processed and (among others), a nmeaGPS_t.hpp file will get created. For each LCM type, there is also an equivalent *_pub.cpp file that is used to publish LCM message for this specific type. Along with the *_pub.cpp file, there is also a *_frame.cpp file for the type. The make process also compiles the *_pub.cpp file into an executable which is what is run by the procman processes. As a general rule for these processes:

  1. It creates an lcm_interface object by passing in the lcm url from the configuration file. There is then an initialize process that constructs a native LCM object using that URL.
  2. The *_pub process starts up and creates a type specific lcm publisher (nmeaGPS for example), a generic serial string publisher, and a navproc publisher.
  3. It then adds those publishers to the lcm_interface which basically assigns the LCM object that was created with the lcm url to each publisher so the LCM object is shared across all publishers.
  4. A serial port object is created using the parameters from the configuration file and then a loop is started that looks for data on that port.
  5. If no data is received, a stale message gets published after a timeout.
  6. If data is received, the raw string messages gets published.
  7. Once the string message gets published, the process uses the appropriate type_frame.cpp methods to try and parse the message into the LCM type that was defined for that type. If that message gest parsed OK, it then gets published using the type-specific publisher

Logr

The logr processes are built and managed by code from three different repositories. The logr process code itself is located in the navproc-logger repository which has dependencies on the navproc-common repository, but the scripts, config files and crontab entries are managed in the corelogging repository. They are started by cron which passes in two parameters to each logger. The first is the location of the .ini file and the second is that task name. The task name has to match a section header in the .ini file so that configuration of the logr can be read in correctly. The configuration is read in and if the location of the navproc .ini file is found, that is also read in. There is also a .cfg file associated with the task name and that is also read in. That configuration is then handed to the load_config method of the logr_item_list object. In that load method, each line of the .cfg file is parsed into a logrItemNode struct and a linked list of these items is built out of the .cfg file. This is basically just a way to convert the information from the .cfg file into a linked list. Once this linked list is created, the process marches through the list and creates a subscriberNode struct for each list item and then stores it in a list of subscriberNodes held in the SubscriberNodeList code. This basically just stores information in a struct that will be used later to actually create the subscriber. Once the struct list is created, the code iterates over that list and creates subcribers for each channel. After the subscribers are created (and registered with the lcm_interface), the code calls start() on the lcm interface. At this point, the subscribers start to receive messages and the type-specific handlers process them. For example, the lcm_nmeaGPS_sub class decodes the LCM message and populates the type-specific data.

There is a class defined in the lcm_subscriber called item_status that is used to track the status of the lcm message stream. In each type-specfic subscriber class, it overrides the get_item methods and sets the values on the incoming status item. From what I can tell, at least in the subscriber, it reads data from the decoded LCM message and then sets the items on the status object, but it does not use the status_item in the type-specific class at least.

BCServer

The bcserver code is kept in the bcserver repository.

DM to AMQP

The dm-to-amqp code and management scripts are maintained in the corelogging repository and are located under the common/dm-to-amqp directory.

Process Startup and Monitoring

When the computer is booted, at some point, the bot procman deputy kicks off the processes that are defined in the /home/ops/CoreNav/navproc-process/bin/sim/bot_proc.cfg file. This file has entries like:

cmd "nmeaGPS_pub"
{
    exec = "$APP_PATH/nmeaGPS_pub/nmeaGPS_pub $CORENAV_INI";
    host = "navproc";
    auto_respawn = "false";
}

which define a single process to be started automatically. The APP_PATH points to /home/ops/CoreNav/navproc-process/src directory and the CORENAV_INI file is the /home/ops/CoreNav/navproc-process/bin/sim/core_nav_sim.ini file. This file contains all the configuration for the processes. It defined the URL where the LCM messages will be published over UDP (lcmUrl=udpm://239.255.76.67:4224?ttl=0) and has sections for each of the processes. For example, the section for the NMEA GPS process above looks like:

    ##############################################################################
    [nmeaGPS_pub]
    ##############################################################################
    serialPort = /dev/ttya01
    serialTimeout = 5

This basically tells the process what serial port to look. There are more complex configurations and options for serial outs, etc. If you run a ps -ef on the navproc machine, you will see a list of processes that look like this:

    ops         2836    1965  0 Feb22 pts/0    00:06:01 bot-procman-deputy -l navproc.2023-Feb-22_08.47.36.log -n navproc
    ops         2842    2836  0 Feb22 pts/1    00:00:00 /home/ops/CoreNav/navproc-process/src/zmq_proxy/proxy -f 5555 -b 5556
    ops         2845    2836  0 Feb22 pts/2    00:04:09 /home/ops/CoreNav/navproc-process/src/octans_pub/octans_pub /home/ops/CoreNav/navproc-process/bin/sim/core_nav_sim.ini
    ops         2846    2836  0 Feb22 pts/3    00:04:44 /home/ops/CoreNav/navproc-process/src/digiquartz_pub/digiquartz_pub /home/ops/CoreNav/navproc-process/bin/sim/core_nav_sim.ini
    ops         2847    2836  0 Feb22 pts/4    00:03:23 /home/ops/CoreNav/navproc-process/src/mbaridepth_pub/mbaridepth_pub /home/ops/CoreNav/navproc-process/bin/sim/core_nav_sim.ini
    ops         2848    2836  0 Feb22 pts/5    00:02:11 /home/ops/CoreNav/navproc-process/src/seabirdctd_pub/seabirdctd_pub /home/ops/CoreNav/navproc-process/bin/sim/core_nav_sim.ini
    ops         2849    2836  0 Feb22 pts/6    00:02:10 /home/ops/CoreNav/navproc-process/src/nmeaGPS_pub/nmeaGPS_pub /home/ops/CoreNav/navproc-process/bin/sim/core_nav_sim.ini
    ops         2850    2836  0 Feb22 pts/7    00:01:18 /home/ops/CoreNav/navproc-process/src/ventanaCSP_pub/ventanaCSP_pub /home/ops/CoreNav/navproc-process/bin/sim/core_nav_sim.ini
    ops         2851    2836  0 Feb22 pts/8    00:01:32 /home/ops/CoreNav/navproc-process/src/shipGyro_pub/shipGyro_pub /home/ops/CoreNav/navproc-process/bin/sim/core_nav_sim.ini
    ops         2858    2836  0 Feb22 pts/9    00:01:32 /home/ops/CoreNav/navproc-process/src/winfrog_pub/winfrog_pub /home/ops/CoreNav/navproc-process/bin/sim/core_nav_sim.ini
    ops         2861    2836  0 Feb22 pts/10   00:04:01 /home/ops/CoreNav/navproc-process/src/winfrog_ser_out/winfrog_ser_out /home/ops/CoreNav/navproc-process/bin/sim/core_nav_sim.ini
    ops         2862    2836  0 Feb22 pts/11   00:01:33 /home/ops/CoreNav/navproc-process/src/dvecsIn_pub/dvecsIn_pub /home/ops/CoreNav/navproc-process/bin/sim/core_nav_sim.ini
    ops         2863    2836  0 Feb22 pts/12   00:08:28 /home/ops/CoreNav/navproc-process/src/dvecsOut_pub/dvecsOut_pub /home/ops/CoreNav/navproc-process/bin/sim/core_nav_sim.ini
    ops         2864    2836  0 Feb22 pts/13   00:03:27 /home/ops/CoreNav/navproc-process/src/lapbox_hd_pub/lapbox_hd_pub /home/ops/CoreNav/navproc-process/bin/sim/core_nav_sim.ini
    ops         2865    2836  0 Feb22 pts/14   00:04:57 /home/ops/CoreNav/navproc-process/src/vorne_ser_out/vorne_ser_out /home/ops/CoreNav/navproc-process/bin/sim/core_nav_sim.ini

These are the various navproc processes that are started by procman deputy. To get a graphical UI view/management of these processes, you can run the procman sheriff from the console/VNC by double-clicking on the navprocGUI shortcut on the desktop. If you expand the 'groups' you will see a list of these processes and their status and you can start/stop them from the GUI (see figure below).

procman-sheriff

Once the processes are started, they start reading from the various ports defined in the core_nav_xxx.ini file and then start spitting out UDP datagrams at the lcmUrl.

If you want to look at the various LCM messages, you can run the lcm-spy tool by double-clicking on the navprocSpy icon on the Desktop and that will open up the lcm-spy tool and it will automatically be pointed at the LCM URL. You can then click on items to inspect the messages coming across the various channels (see figure below).

navproc-spy

The next set of processes to start are the logr processes and they are started by crontab. If you look at the crontab by running crontab -l you will see a bunch of stuff, but specifically there are lines that look like

    *  *  *  *  *  /home/ops/corelogging/sim/scripts/startLogr shipnavlogr > /home/ops/corelogging/sim/logs/out_shipnav_logr 2>&1

This entry runs a script called startLogr which is located in the corelogging source code repository. This script looks up some configuration information and then starts an instance of the logr executable and passes in the configuration file and the name of the logger which will link it to the correct section in the configuration file. For example, the above cron entry passes in the shipnavlogr name which has a corresponding section in the core_logging.ini file that looks like:

    ##############################################################################
    [shipnavlogr]
    ##############################################################################

    #name of coredata root directory
    coredataRoot = /home/ops/corelogging/sim

    #coredata console output logs dirctory
    coredataLogsDir = logs

    #coredata cfg files directory
    coredataCfgsDir = cfgs

    #coredata data files directory
    coredataDataDir = data

    # name of logr items config file (in the cfgs dirctory)
    coredataLogItemsCfg = shipnavlogr.cfg

    #platform  may be used in forming filenames or tagging datafiles etc
    #typically wfly or rcsn
    coredataPlatform = sim

    #Broadcast and client UDP port assignments
    coredataBroadcastPort = 54002
    clientConnectPort = 54003

This configuration tells the logr executable a few things like where to read the configs from and where to write data and log files to. There are two key configuration properties to pay attention to. The coredataLogItemsCfg property tells the logr executable what file to read in for the configuration of what items to read from the LCM messages and what items to write to the data files (and what format to write it in). The second is the coredataBroadcastPort which is the port where the UDP messages from the logr executable will be pushed to. The clientConnectPort will be used by the BCServer process, which is described later.

If you look at the logger configuration file (shipnavlogr.cfg in this example) you will see the properties used to set up the loggers. Here is an example for the shipnavlogr (comments and blank lines removed)

    UPDATE 1
    LOG  NMEA_GPS     lcm_nmeaGPS_sub        ZDA_USECS       Int          SHIP.GPS.TIME             %d      No    No    Slave      10     No
    LOG  VENTANA_CSP  lcm_ventanaCSP_sub     PRESSURE        Double       ROV.PRESSURE              %4.2lf  No    No    Slave      30     No
    LOG  SEABIRD_CTD  lcm_seabirdctd_sub     PRESSURE        Double       ROV.CTD.PRESSURE          %5.1lf  No    No    Slave      30     No
    LOG  NMEA_GPS     lcm_nmeaGPS_sub        LAT             Double       SHIP.GPS.LAT              %3.6lf  No    No    Slave      30     No
    LOG  NMEA_GPS     lcm_nmeaGPS_sub        LON             Double       SHIP.GPS.LON              %4.6lf  No    No    Slave      30     No
    LOG  NMEA_GPS     lcm_nmeaGPS_sub        DIFFERENTIAL    BOOL         SHIP.GPS.DIFFERENTIAL     %d      No    No    Slave      30     No
    LOG  NMEA_GPS     lcm_nmeaGPS_sub        QUALITY         Int          SHIP.GPS.QUALITY          %d      No    No    Slave      30     No
    LOG  NMEA_GPS     lcm_nmeaGPS_sub        HDOP            DOUBLE       SHIP.GPS.HDOP             %3.1lf  No    No    Slave      30     No
    LOG  SHIP_GYRO    lcm_shipGyro_sub       HEADING         DOUBLE       SHIP.GYRO.DEGREES         %3.1lf  Yes   No    Slave      30     No
    LOG  NMEA_GPS     lcm_nmeaGPS_sub        NO_ITEM         Double       SHIP.WIND.TRUE_SPEED      %lf     Yes   No    Slave      30     No
    LOG  NMEA_GPS     lcm_nmeaGPS_sub        NO_ITEM         Double       SHIP.WIND.TRUE_DIRECTION  %lf     No    No    Slave      30     No
    LOG  WINFROG      lcm_winfrog_sub        LAT             Double       ROV.POSITION.LAT          %lf     Yes   No    Slave      30     Yes
    LOG  WINFROG      lcm_winfrog_sub        LON             Double       ROV.POSITION.LON          %lf     No    No    Slave      30     Yes
    LOG  VENTANA_CSP  lcm_ventanaCSP_sub     DEPTH           Double       ROV.MBARI.DEPTH           %lf     No    No    Slave      30     No
    LOG  VENTANA_CSP  lcm_ventanaCSP_sub     HEADING         Double       ROV.HEADING               %lf     No    No    Slave      30     No
    LOG  VENTANA_CSP  lcm_ventanaCSP_sub     PITCH           Double       ROV.PITCH                 %lf     No    No    Slave      30     No
    LOG  VENTANA_CSP  lcm_ventanaCSP_sub     ROLL            Double       ROV.ROLL                  %lf     No    No    Slave      30     No
    LOG  VENTANA_CSP  lcm_ventanaCSP_sub     ALTITUDE        Double       ROV.ALTITUDE              %lf     No    No    Slave      30     No

The UPDATE property has a couple of different meanings.

Below is a diagram that captures the high-level picture of these processes and the data flows.

flowchart LR subgraph SerialPorts ttya0(/dev/ttya00) ttya1(/dev/ttya01) ttya2(/dev/ttya02) ttya3(/dev/ttya03) ttya4(/dev/ttya04) ttya5(/dev/ttya05) ttya6(/dev/ttya06) ttya7(/dev/ttya07) ttya8(/dev/ttya08) ttya9(/dev/ttya09) ttya10(/dev/ttya10) ttya11(/dev/ttya11) ttya12(/dev/ttya12) ttya13(/dev/ttya13) ttya14(/dev/ttya14) ttya15(/dev/ttya15) end subgraph Navproc Processes direction LR octans_pub nmeaGPS_pub seabirdctd_pub dvecsIn_pub digiquartz_pub winfrog_pub shipGyro_pub ventanaCSP_pub mbaridepth_pub dvecsOut_pub lapbox_hd_pub winfrog_ser_out vorne_ser_out end lcmUrl subgraph Logger Processes direction LR shipnavlogr --> udp54002(UDP 54002) videologr --> udp54004(UDP 54004) rovctdlogr --> udp54006(UDP 54006) dataprobelogr --> udp54008(UDP 54008) winfroglogr --> udp54016(UDP 54016) m3rslogr --> udp54018(UDP 54018) eventlogr end subgraph BCServers direction LR udp54002 --> bcserversn(bcserver shipnavserver) --> udp54003(UDP 54003) udp54004 --> bcservervs(bcserver videoserver) --> udp54005(UDP 54005) udp54006 --> bcserverrs(bcserver rovctdserver) --> udp54007(UDP 54007) udp54008 --> bcserverdp(bcserver dataprobeserver) --> udp54009(UDP 54009) udp54016 --> bcserverwf(bcserver winfrogserver) --> udp54017(UDP 54017) udp54018 --> bcserverm3(bcserver m3rsserver) --> udp54019(UDP 54019) end ttya0 --> octans_pub --> lcmUrl ttya1 --> nmeaGPS_pub --> lcmUrl ttya2 --> seabirdctd_pub --> lcmUrl ttya10 --> dvecsIn_pub --> lcmUrl ttya11 --> digiquartz_pub --> lcmUrl ttya12 --> winfrog_pub --> lcmUrl ttya13 --> shipGyro_pub --> lcmUrl ttya14 --> ventanaCSP_pub --> lcmUrl mbaridepth_pub --> lcmUrl dvecsOut_pub --> lcmUrl lapbox_hd_pub --> lcmUrl lcmUrl --> eventlogr lcmUrl --> dataprobelogr lcmUrl --> shipnavlogr lcmUrl --> videologr lcmUrl --> rovctdlogr lcmUrl --> winfroglogr lcmUrl --> m3rslogr

Rachel Carson Signal Mapping

flowchart LR subgraph Bridge wind(Wind) gps(GPS) fog(FOG) end subgraph ROV Control Room subgraph Serial Converters wind-serial-converter(Wind Serial Converter) gps-serial-converter(GPS Serial Converter) fog-serial-converter(FOG Serial Converter) end subgraph Serial Splitters wind-serial-splitter(Wind Serial Splitter) gps-serial-splitter(GPS Serial Splitter) fog-serial-splitter(FOG Serial Splitter) end end wind --> wind-serial-converter --> wind-serial-splitter gps --> gps-serial-converter --> gps-serial-splitter fog --> fog-serial-converter --> fog-serial-splitter