Keep `# bootstrap rosdep` comment

Keep `# bootstrap rosdep` comment

@(TEMPLATE(
    'snippet/add_generated_comment.Dockerfile.em',
    user_name=user_name,
    tag_name=tag_name,
    source_template_name=template_name,
))@
@(TEMPLATE(
    'snippet/from_base_image.Dockerfile.em',
    template_packages=template_packages,
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
    base_image=base_image,
    maintainer_name=maintainer_name,
))@
@(TEMPLATE(
    'snippet/setup_tzdata.Dockerfile.em',
    os_name=os_name,
    os_code_name=os_code_name,
))@
@{
template_dependencies = [
    'dirmngr',
    'gnupg2',
    'lsb-release',
]
# add 'python3-pip' to 'template_dependencies' if pip dependencies are declared
if 'pip3_install' in locals():
    if isinstance(pip3_install, list) and pip3_install != []:
        template_dependencies.append('python3-pip')
}@
@(TEMPLATE(
    'snippet/install_upstream_package_list.Dockerfile.em',
    packages=template_dependencies,
    upstream_packages=upstream_packages if 'upstream_packages' in locals() else [],
))@
@
# setup keys
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 421C365BD9FF1F717815A3895523BAEEB01FA116

# setup sources.list
RUN echo "deb http://packages.ros.org/ros2/ubuntu `lsb_release -sc` main" > /etc/apt/sources.list.d/ros2-latest.list

# setup environment
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8

@{
# add colcon packages to 'ros2_repo_packages' if colcon is used
if 'colcon_args' in locals():
    colcon_packages = [
        'python3-colcon-common-extensions',
    ]
    ros2_repo_packages.extend(colcon_packages)
}@
@[if 'ros2_repo_packages' in locals()]@
@[  if ros2_repo_packages]@
# install packages from the ROS repositories
RUN apt-get update && apt-get install -y --no-install-recommends \
    @(' \\\n    '.join(sorted(ros2_repo_packages)))@  \
    && rm -rf /var/lib/apt/lists/*

@[  end if]@
@[end if]@
@
@[if 'pip3_install' in locals()]@
@[  if pip3_install]@
# install python packages
RUN pip3 install -U \
    @(' \\\n    '.join(pip3_install))@

@[  end if]@
@[end if]@
@
@[if 'vcs' in locals()]@
@[  if vcs]@

@[if 'rosdep' in locals()]@
# bootstrap rosdep
@[  if 'rosdistro_index_url' in rosdep]@
ENV ROSDISTRO_INDEX_URL @(rosdep['rosdistro_index_url'])
@[  end if]@
RUN rosdep init \
    && rosdep update
@[end if]@

# clone source
ENV ROS2_WS @(ws)
RUN mkdir -p $ROS2_WS/src
WORKDIR $ROS2_WS
@(TEMPLATE(
    'snippet/vcs_import.Dockerfile.em',
    vcs=vcs,
    ws='src',
))@

@[  end if]@
@[end if]@
@
@[if 'rosdep' in locals()]@
@[  if 'install' in rosdep]@
@(TEMPLATE(
    'snippet/install_rosdep_dependencies.Dockerfile.em',
    install_args=rosdep['install'],
))@
@[  end if]@
@[end if]@

@[if 'colcon_args' in locals()]@
@[  if colcon_args]@
# build source
WORKDIR $ROS2_WS
RUN colcon \
    @(' \\\n    '.join(colcon_args))@

@[  end if]@
@[end if]@
# setup bashrc
RUN cp /etc/skel/.bashrc ~/

@[if 'entrypoint_name' in locals()]@
@[  if entrypoint_name]@
@{
entrypoint_file = entrypoint_name.split('/')[-1]
}@
# setup entrypoint
COPY ./@entrypoint_file /

ENTRYPOINT ["/@entrypoint_file"]
@[  end if]@
@[end if]@
@{
cmds = [
'bash',
]
}@
CMD ["@(' && '.join(cmds))"]

@(TEMPLATE(
    'snippet/add_generated_comment.Dockerfile.em',
    user_name=user_name,
    tag_name=tag_name,
    source_template_name=template_name,
))@
@(TEMPLATE(
    'snippet/from_base_image.Dockerfile.em',
    template_packages=template_packages,
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
    base_image=base_image,
    maintainer_name=maintainer_name,
))@
@(TEMPLATE(
    'snippet/setup_tzdata.Dockerfile.em',
    os_name=os_name,
    os_code_name=os_code_name,
))@
@{
template_dependencies = [
    'dirmngr',
    'gnupg2',
    'lsb-release',
]
# add 'python3-pip' to 'template_dependencies' if pip dependencies are declared
if 'pip3_install' in locals():
    if isinstance(pip3_install, list) and pip3_install != []:
        template_dependencies.append('python3-pip')
}@
@(TEMPLATE(
    'snippet/install_upstream_package_list.Dockerfile.em',
    packages=template_dependencies,
    upstream_packages=upstream_packages if 'upstream_packages' in locals() else [],
))@
@
# setup keys
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 421C365BD9FF1F717815A3895523BAEEB01FA116

# setup sources.list
RUN echo "deb http://packages.ros.org/ros2/ubuntu `lsb_release -sc` main" > /etc/apt/sources.list.d/ros2-latest.list

# setup environment
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8

@{
# add colcon packages to 'ros2_repo_packages' if colcon is used
if 'colcon_args' in locals():
    colcon_packages = [
        'python3-colcon-common-extensions',
    ]
    ros2_repo_packages.extend(colcon_packages)
}@
@[if 'ros2_repo_packages' in locals()]@
@[  if ros2_repo_packages]@
# install packages from the ROS repositories
RUN apt-get update && apt-get install -y --no-install-recommends \
    @(' \\\n    '.join(sorted(ros2_repo_packages)))@  \
    && rm -rf /var/lib/apt/lists/*

@[  end if]@
@[end if]@
@
@[if 'pip3_install' in locals()]@
@[  if pip3_install]@
# install python packages
RUN pip3 install -U \
    @(' \\\n    '.join(pip3_install))@

@[  end if]@
@[end if]@
@
@[if 'vcs' in locals()]@
@[  if vcs]@

@[if 'rosdep' in locals()]@
@[  if 'rosdistro_index_url' in rosdep]@
# bootstrap rosdep
ENV ROSDISTRO_INDEX_URL @(rosdep['rosdistro_index_url'])
@[  end if]@
RUN rosdep init \
    && rosdep update
@[end if]@

# clone source
ENV ROS2_WS @(ws)
RUN mkdir -p $ROS2_WS/src
WORKDIR $ROS2_WS
@(TEMPLATE(
    'snippet/vcs_import.Dockerfile.em',
    vcs=vcs,
    ws='src',
))@

@[  end if]@
@[end if]@
@
@[if 'rosdep' in locals()]@
@[  if 'install' in rosdep]@
@(TEMPLATE(
    'snippet/install_rosdep_dependencies.Dockerfile.em',
    install_args=rosdep['install'],
))@
@[  end if]@
@[end if]@

@[if 'colcon_args' in locals()]@
@[  if colcon_args]@
# build source
WORKDIR $ROS2_WS
RUN colcon \
    @(' \\\n    '.join(colcon_args))@

@[  end if]@
@[end if]@
# setup bashrc
RUN cp /etc/skel/.bashrc ~/

@[if 'entrypoint_name' in locals()]@
@[  if entrypoint_name]@
@{
entrypoint_file = entrypoint_name.split('/')[-1]
}@
# setup entrypoint
COPY ./@entrypoint_file /

ENTRYPOINT ["/@entrypoint_file"]
@[  end if]@
@[end if]@
@{
cmds = [
'bash',
]
}@
CMD ["@(' && '.join(cmds))"]


updated setup

updated setup

#!/bin/bash

# DO NOT REMOVE THIS BLOCK UNLESS YOU DON'T WANT TO USE CUSTOM SCRIPTS
@[if DEVELSPACE]@
export ROSWSS_SCRIPTS="@(PROJECT_SOURCE_DIR)/scripts:$ROSWSS_SCRIPTS"
@[else]@
export ROSWSS_SCRIPTS="@(CMAKE_INSTALL_PREFIX)/@(CATKIN_PACKAGE_SHARE_DESTINATION)/scripts:$ROSWSS_SCRIPTS"
@[end if]@

# SET HERE YOUR WORKSPACE PREFIX
export ROSWSS_PREFIX="thor"
export ROSWSS_ROOT_RELATIVE_PATH="../.."

# SETUP YOUR ENVIRONMENT HERE
export ONBOARD_LAUNCH_PKG="thor_mang_onboard_launch"
export GAZEBO_LAUNCH_PKG="thor_mang_gazebo"
export GAZEBO_LAUNCH_FILE="start_all.launch"
export GAZEBO_LAUNCH_W_ONBOARD_FILE="start_onboard_all.launch"
export GAZEBO_WORLDS_PKG="thor_mang_gazebo"

export ROBOT_MASTER_HOSTNANE="thor-motion"
export ROBOT_HOSTNAMES="thor-motion thor-perception thor-onboard"
export ROBOT_USER="thor"

# THOR SPECIFIC EXPORTS
export GAZEBO_MODEL_PATH=$WS_ROOT/src/thor/thor_mang_simulation/thor_mang_gazebo/models
export THOR_MOTION_HOSTNAME="thor-motion"
export THOR_PERCEPTION_HOSTNAME="thor-perception"
export THOR_ONBOARD_HOSTNAME="thor-perception"

# THOR PCs/LAUNCHES
add_remote_pc "motion" "thor-motion" "motion" "roslaunch thor_mang_onboard_launch motion.launch"
add_remote_pc "perception" "thor-perception" "perception" "export ROS_MASTER_URI=http://thor-motion:11311; export ROS_IP=192.168.1.11; roslaunch thor_mang_onboard_launch perception.launch"
add_remote_pc "onboard" "thor-motion" "onboard" "export ROS_MASTER_URI=http://thor-motion:11311; export ROS_IP=192.168.1.11; roslaunch thor_mang_onboard_launch onboard.launch"

# Custom completion scripts
add_completion "ui" "_ui_complete"

#!/bin/bash

# DO NOT REMOVE THIS BLOCK UNLESS YOU DON'T WANT TO USE CUSTOM SCRIPTS
@[if DEVELSPACE]@
export ROSWSS_SCRIPTS="@(PROJECT_SOURCE_DIR)/scripts:$ROSWSS_SCRIPTS"
@[else]@
export ROSWSS_SCRIPTS="@(CMAKE_INSTALL_PREFIX)/@(CATKIN_PACKAGE_SHARE_DESTINATION)/scripts:$ROSWSS_SCRIPTS"
@[end if]@

# SET HERE YOUR WORKSPACE PREFIX
export ROSWSS_PREFIX="thor"
export ROSWSS_ROOT_RELATIVE_PATH="../.."

# SETUP YOUR ENVIRONMENT HERE
export ONBOARD_LAUNCH_PKG="thor_mang_onboard_launch"
export GAZEBO_LAUNCH_PKG="thor_mang_gazebo"
export GAZEBO_WORLDS_PKG="thor_mang_gazebo"
export ROBOT_MASTER_HOSTNANE="thor-motion"
export ROBOT_HOSTNAMES="thor-motion thor-perception thor-onboard"
export ROBOT_USER="thor"

# THOR SPECIFIC EXPORTS
export GAZEBO_MODEL_PATH=$WS_ROOT/src/thor/thor_mang_simulation/thor_mang_gazebo/models
export THOR_MOTION_HOSTNAME="thor-motion"
export THOR_PERCEPTION_HOSTNAME="thor-perception"
export THOR_ONBOARD_HOSTNAME="thor-perception"

# THOR PCs/LAUNCHES
add_remote_pc "motion" "thor-motion" "motion" "roslaunch thor_mang_onboard_launch motion.launch"
add_remote_pc "perception" "thor-perception" "perception" "export ROS_MASTER_URI=http://thor-motion:11311; export ROS_IP=192.168.1.11; roslaunch thor_mang_onboard_launch perception.launch"
add_remote_pc "onboard" "thor-motion" "onboard" "export ROS_MASTER_URI=http://thor-motion:11311; export ROS_IP=192.168.1.11; roslaunch thor_mang_onboard_launch onboard.launch"

# Custom completion scripts
add_completion "ui" "_ui_complete"


[CmtGen] update

[CmtGen] update

macro GetCurLineString( )
{
	//
	//  Get current line text
	//
	hbuf = GetCurrentBuf( )
	line = GetBufLnCur( hbuf )
	text = GetBufLine( hbuf, line )

	//
	//	Truncate the last spaces and semicolon
	//
  len = strlen(text)
  len = len - 1
  while(len > 0)
  {
  	if( text[len] == " " || text[len] == ";" )
  	{
  		len = len - 1
  	}
  	else
  	{
	    text = strtrunc( text, len + 1 )
  		break;
  	}
  }

	return text
}

macro GetFileName( )
{
	hbuf = GetCurrentBuf( )

	fname = GetBufname( hbuf ) //name style:  D:\dirname\filename.c

	len = strlen( fname )
	i = strlen( fname )

	while ( i > 0 )
	{
		ch = fname[ i ]
		if ( ch == "\\" )
		{
			i = i + 1
			break
	  }
		i = i - 1
	}

	sname = ""
	while ( i < len )
	{
		sname = Cat( sname, fname[ i ] )
		i = i + 1
	}

	return sname
}

macro IsHeaderFile( )
{
	fname = GetFileName( )
	len   = strlen( fname )
	i     = 0

	while ( i < len )
	{
		ch = fname[ i ]
		if ( ch == "." )
			break
		i = i + 1
	}
	ch = fname[ i + 1 ]

	if ( ( ch == "h" ) || ( ch == "H" ) )
		return 1
	else
		return 0
}

macro CmtGenFile( )
{
	var sz

	hbuf = GetCurrentBuf( )
	fname = GetFileName( )
	tempname = "sourcefileheadinformation"

	len = strlen( fname )
	i = 0

	sz = Cat( sz, "_" )
	while (i < len )
	{
		ch = fname[ i ]
		if ( ch == "." )
		break;
		sz = Cat( sz, toupper( ch ) )
		if ( islower( ch ) && isupper( fname[ i + 1 ] )
		sz = Cat( sz, "_" )
		i = i + 1
	}
	sz = Cat( sz, "_H_" )

	line = 0;
	hbuftemp = OpenBuf( tempname )
	if ( hbuftemp != hNil )
	{
		count = GetBufLineCount( hbuftemp );
		line = 0;
		while ( line < count )
		{
			linestr = GetBufLine( hbuftemp, line );
			InsBufLine( hbuf, line++, linestr );
		}
	}
	else
	{
		InsBufLine( hbuf, line++, "//---------------------------------------------------------------------------//" )
	}

	InsBufLine( hbuf, line++, "/// \\file   @fname@" )
	InsBufLine( hbuf, line++, "/// \\brief" )
	InsBufLine( hbuf, line++, "//---------------------------------------------------------------------------//" )

	if ( IsHeaderFile( ) )
	{
		InsBufLine( hbuf, line++, "#ifndef @sz@" )
		InsBufLine( hbuf, line++, "#define @sz@" )
		InsBufLine( hbuf, line++, "" )
		InsBufLine( hbuf, line++, "#ifdef __cplusplus" )
		InsBufLine( hbuf, line++, "extern \"C\"" )
		InsBufLine( hbuf, line++, "{" )
		InsBufLine( hbuf, line++, "#endif" )
		InsBufLine( hbuf, line++, "" )
		InsBufLine( hbuf, line++, "" )

		AppendBufLine( hbuf, "#ifdef __cplusplus" )
		AppendBufLine( hbuf, "}" )
		AppendBufLine( hbuf, "#endif" )
		AppendBufLine( hbuf, "" )
		AppendBufLine( hbuf, "#endif" )
	}
}

macro CmtGenFunction( )
{
	hbuf = GetCurrentBuf( )
	i = GetBufLnCur( hbuf );
	text = GetCurLineString( );
	name = Cat( "/// \\fn : ", text )

	InsBufLine( hbuf, i++, "//---------------------------------------------------------------------------//" )
	InsBufLine( hbuf, i++, name )
	InsBufLine( hbuf, i++, "///    " )
	InsBufLine( hbuf, i++, "/// \\brief      : None" )
	InsBufLine( hbuf, i++, "/// \\param [in] : None" )
	InsBufLine( hbuf, i++, "/// \\param [out]: None" )
	InsBufLine( hbuf, i++, "/// \\raise error: None" )
	InsBufLine( hbuf, i++, "/// \\return     : None" )
	InsBufLine( hbuf, i++, "//---------------------------------------------------------------------------//" )
}

macro CmtGenVariable( )
{
	hbuf = GetCurrentBuf( )
	i = GetBufLnCur( hbuf );
	text = GetCurLineString( );
	name = Cat( "/// \\var  : ", text )

	InsBufLine( hbuf, i++, name )
	InsBufLine( hbuf, i++, "/// \\brief: " )
}

macro CmtGenDefinition( )
{
	hbuf = GetCurrentBuf( )
	i = GetBufLnCur( hbuf );
	text = GetCurLineString( );
	name = Cat( "/// \\def  : ", text )

	InsBufLine( hbuf, i++, name )
	InsBufLine( hbuf, i++, "/// \\brief: " )
}

macro GetCurLineString( )
{
	//
	//  Get current line text
	//
	hbuf = GetCurrentBuf( )
	line = GetBufLnCur( hbuf )
	text = GetBufLine( hbuf, line )

	//
	//	Truncate the last spaces and semicolon
	//
  len = strlen(text)
  len = len - 1
  while(len > 0)
  {
  	if( text[len] == " " || text[len] == ";" )
  	{
  		len = len - 1
  	}
  	else
  	{
	    text = strtrunc( text, len + 1 )
  		break;
  	}
  }

	return text
}

macro GetFileName( )
{
	hbuf = GetCurrentBuf( )

	fname = GetBufname( hbuf ) //name style:  D:\dirname\filename.c

	len = strlen( fname )
	i = strlen( fname )

	while ( i > 0 )
	{
		ch = fname[ i ]
		if ( ch == "\\" )
		{
			i = i + 1
			break
	  }
		i = i - 1
	}

	sname = ""
	while ( i < len )
	{
		sname = Cat( sname, fname[ i ] )
		i = i + 1
	}

	return sname
}

macro IsHeaderFile( )
{
	fname = GetFileName( )
	len   = strlen( fname )
	i     = 0

	while ( i < len )
	{
		ch = fname[ i ]
		if ( ch == "." )
			break
		i = i + 1
	}
	ch = fname[ i + 1 ]

	if ( ( ch == "h" ) || ( ch == "H" ) )
		return 1
	else
		return 0
}

macro CmtGenFile( )
{
	var sz

	hbuf = GetCurrentBuf( )
	fname = GetFileName( )
	tempname = "sourcefileheadinformation"

	len = strlen( fname )
	i = 0

	sz = Cat( sz, "_" )
	while (i < len )
	{
		ch = fname[ i ]
		if ( ch == "." )
		break;
		sz = Cat( sz, toupper( ch ) )
		if ( islower( ch ) && isupper( fname[ i + 1 ] )
		sz = Cat( sz, "_" )
		i = i + 1
	}
	sz = Cat( sz, "_H_" )

	line = 0;
	hbuftemp = OpenBuf( tempname )
	if ( hbuftemp != hNil )
	{
		count = GetBufLineCount( hbuftemp );
		line = 0;
		while ( line < count )
		{
			linestr = GetBufLine( hbuftemp, line );
			InsBufLine( hbuf, line++, linestr );
		}
	}
	else
	{
		InsBufLine( hbuf, line++, "//---------------------------------------------------------------------------//" )
	}

	InsBufLine( hbuf, line++, "/// \\file   @fname@" )
	InsBufLine( hbuf, line++, "/// \\brief" )
	InsBufLine( hbuf, line++, "//---------------------------------------------------------------------------//" )

	if ( IsHeaderFile( ) )
	{
		InsBufLine( hbuf, line++, "#ifndef @sz@" )
		InsBufLine( hbuf, line++, "#define @sz@" )
		InsBufLine( hbuf, line++, "" )
		InsBufLine( hbuf, line++, "#ifdef __cplusplus" )
		InsBufLine( hbuf, line++, "extern \"C\"" )
		InsBufLine( hbuf, line++, "{" )
		InsBufLine( hbuf, line++, "#endif" )
		InsBufLine( hbuf, line++, "" )
		InsBufLine( hbuf, line++, "" )

		AppendBufLine( hbuf, "#ifdef __cplusplus" )
		AppendBufLine( hbuf, "}" )
		AppendBufLine( hbuf, "#endif" )
		AppendBufLine( hbuf, "" )
		AppendBufLine( hbuf, "#endif" )
	}
}

macro CmtGenFunction( )
{
	hbuf = GetCurrentBuf( )
	i = GetBufLnCur( hbuf );
	text = GetCurLineString( );
	name = Cat( "/// \\fn : ", text )

	InsBufLine( hbuf, i++, "//-----------------------------------------------------------------------//" )
	InsBufLine( hbuf, i++, name )
	InsBufLine( hbuf, i++, "///    " )
	InsBufLine( hbuf, i++, "/// \\brief      : None" )
	InsBufLine( hbuf, i++, "/// \\param [in] : None" )
	InsBufLine( hbuf, i++, "/// \\param [out]: None" )
	InsBufLine( hbuf, i++, "/// \\raise error: None" )
	InsBufLine( hbuf, i++, "/// \\return     : None" )
	InsBufLine( hbuf, i++, "//-----------------------------------------------------------------------//" )
}

macro CmtGenVariable( )
{
	hbuf = GetCurrentBuf( )
	i = GetBufLnCur( hbuf );
	text = GetCurLineString( );
	name = Cat( "/// \\var  : ", text )

	InsBufLine( hbuf, i++, name )
	InsBufLine( hbuf, i++, "/// \\brief: " )
}

macro CmtGenDefinition( )
{
	hbuf = GetCurrentBuf( )
	i = GetBufLnCur( hbuf );
	text = GetCurLineString( );
	name = Cat( "/// \\def  : ", text )

	InsBufLine( hbuf, i++, name )
	InsBufLine( hbuf, i++, "/// \\brief: " )
}


work around groovy limitation when triggering release jobs

import hudson.model.Cause.UpstreamCause
import jenkins.model.Jenkins

job_names = []
@{
job_names = sorted(job_names)
group_size = 1000
}@
// group job names in chunks of @group_size to not exceed groovy limits
@[for i in range(0, len(job_names), group_size)]@
def add_job_names_@(int(i / group_size) + 1)(job_names) {
@[for j in range(i, min(i + group_size, len(job_names)))]@
job_names << '@(job_names[j])'
@[end for]@
}
add_job_names_@(int(i / group_size) + 1)(job_names)
@[end for]@

println "Triggering " + job_names.size + " jobs..."
triggered = 0
skipped = 0
for (job_name in job_names) {
    for (p in Jenkins.instance.allItems) {
        if (p.name != job_name) continue
        if (p.isDisabled()) {
            println "  " + job_name + " (skipped disabled project)"
            skipped += 1
            break
        }
        if (p.isBuilding()) {
            println "  " + job_name + " (skipped already building project)"
            skipped += 1
            break
        }
        if (p.isInQueue()) {
            println "  " + job_name + " (skipped already queued project)"
            skipped += 1
            break
        }
        println job_name
        p.scheduleBuild(new UpstreamCause(binding.variables["build"]))
        triggered += 1
        break
    }
}
println "Triggered " + triggered + " jobs, skipped " + skipped + " jobs."

import hudson.model.Cause.UpstreamCause
import jenkins.model.Jenkins

job_names = []
@[for job_name in sorted(job_names)]@
job_names << "@job_name"
@[end for]@

println "Triggering " + job_names.size + " jobs..."
triggered = 0
skipped = 0
for (job_name in job_names) {
    for (p in Jenkins.instance.allItems) {
        if (p.name != job_name) continue
        if (p.isDisabled()) {
            println "  " + job_name + " (skipped disabled project)"
            skipped += 1
            break
        }
        if (p.isBuilding()) {
            println "  " + job_name + " (skipped already building project)"
            skipped += 1
            break
        }
        if (p.isInQueue()) {
            println "  " + job_name + " (skipped already queued project)"
            skipped += 1
            break
        }
        println job_name
        p.scheduleBuild(new UpstreamCause(binding.variables["build"]))
        triggered += 1
        break
    }
}
println "Triggered " + triggered + " jobs, skipped " + skipped + " jobs."


microRTPS: client: use structs for pub/subs to avoid increasing the stack usage

@###############################################
@#
@# EmPy template for generating microRTPS_client.cpp file
@#
@###############################################
@# Start of Template
@#
@# Context:
@#  - msgs (List) list of all msg files
@#  - multi_topics (List) list of all multi-topic names
@#  - ids (List) list of all RTPS msg ids
@###############################################
@{
import os

import genmsg.msgs

from px_generate_uorb_topic_helper import * # this is in Tools/
from px_generate_uorb_topic_files import MsgScope # this is in Tools/

topic_names = [s.short_name for s in spec]
send_topics = [(alias[idx] if alias[idx] else s.short_name) for idx, s in enumerate(spec) if scope[idx] == MsgScope.SEND]
send_base_types = [s.short_name for idx, s in enumerate(spec) if scope[idx] == MsgScope.SEND]
recv_topics = [(alias[idx] if alias[idx] else s.short_name) for idx, s in enumerate(spec) if scope[idx] == MsgScope.RECEIVE]
receive_base_types = [s.short_name for idx, s in enumerate(spec) if scope[idx] == MsgScope.RECEIVE]
}@
/****************************************************************************
 *
 * Copyright (c) 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima).
 * Copyright (c) 2018-2019 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

#include "microRTPS_transport.h"
#include "microRTPS_client.h"

#include <inttypes.h>
#include <cstdio>
#include <ctime>
#include <pthread.h>

#include <ucdr/microcdr.h>
#include <px4_time.h>
#include <uORB/uORB.h>

#include <uORB/Publication.hpp>
#include <uORB/Subscription.hpp>
@[for topic in list(set(topic_names))]@
#include <uORB/topics/@(topic).h>
#include <uORB_microcdr/topics/@(topic).h>
@[end for]@

void* send(void *data);

uint8_t last_remote_msg_seq = 0;
uint8_t last_msg_seq = 0;

@[if recv_topics]@
// Publishers for received messages
struct RcvTopicsPubs
{
@[    for idx, topic in enumerate(recv_topics)]@
    uORB::Publication<@(receive_base_types[idx])_s> @(topic)_pub{ORB_ID(@(topic))};
@[    end for]@
};
@[end if]@

@[if send_topics]@
// Subscribers for messages to send
struct SendTopicsSubs
{
@[    for idx, topic in enumerate(send_topics)]@
    uORB::Subscription @(topic)_sub{ORB_ID(@(topic))};
@[    end for]@
};
@[end if]@

@[if send_topics]@
void* send(void* /*unused*/)
{
    char data_buffer[BUFFER_SIZE] = {};
    uint64_t sent = 0, total_sent = 0;
    int loop = 0, read = 0;
    uint32_t length = 0;
    size_t header_length = 0;
    struct SendTopicsSubs subs;

    // ucdrBuffer to serialize using the user defined buffer
    ucdrBuffer writer;
    header_length = transport_node->get_header_length();
    ucdr_init_buffer(&writer, reinterpret_cast<uint8_t*>(&data_buffer[header_length]), BUFFER_SIZE - header_length);

    struct timespec begin;
    px4_clock_gettime(CLOCK_REALTIME, &begin);

    while (!_should_exit_task)
    {
@[for idx, topic in enumerate(send_topics)]@
        {
            @(send_base_types[idx])_s @(topic)_data;
            if (subs.@(topic)_sub.update(&@(topic)_data)) {
@[if topic == 'Timesync' or topic == 'timesync']@
                if(@(topic)_data.sys_id == 0 && @(topic)_data.seq != last_remote_msg_seq && @(topic)_data.tc1 == 0) {
                    last_remote_msg_seq = @(topic)_data.seq;

                    @(topic)_data.timestamp = hrt_absolute_time();
                    @(topic)_data.sys_id = 1;
                    @(topic)_data.seq = last_msg_seq;
                    @(topic)_data.tc1 = hrt_absolute_time() * 1000ULL;
                    @(topic)_data.ts1 = @(topic)_data.ts1;

                    last_msg_seq++;
@[end if]@
                    // copy raw data into local buffer. Payload is shifted by header length to make room for header
                    serialize_@(send_base_types[idx])(&writer, &@(topic)_data, &data_buffer[header_length], &length);
                    if (0 < (read = transport_node->write(static_cast<char>(@(rtps_message_id(ids, topic))), data_buffer, length)))
                    {
                        total_sent += read;
                        ++sent;
                    }
@[if topic == 'Timesync' or topic == 'timesync']@
                }
@[end if]@
             }
        }
@[end for]@
        px4_usleep(_options.sleep_ms * 1000);
        ++loop;
    }

    struct timespec end;
    px4_clock_gettime(CLOCK_REALTIME, &end);
    double elapsed_secs = end.tv_sec - begin.tv_sec + (end.tv_nsec - begin.tv_nsec) / 1e9;
    PX4_INFO("SENT: %" PRIu64 " messages in %d LOOPS, %" PRIu64 " bytes in %.03f seconds - %.02fKB/s",
                sent, loop, total_sent, elapsed_secs, total_sent / (1e3 * elapsed_secs));

    return nullptr;
}

static int launch_send_thread(pthread_t &sender_thread)
{
    pthread_attr_t sender_thread_attr;
    pthread_attr_init(&sender_thread_attr);
    pthread_attr_setstacksize(&sender_thread_attr, PX4_STACK_ADJUSTED(4096));
    struct sched_param param;
    (void)pthread_attr_getschedparam(&sender_thread_attr, &param);
    param.sched_priority = SCHED_PRIORITY_DEFAULT;
    (void)pthread_attr_setschedparam(&sender_thread_attr, &param);
    pthread_create(&sender_thread, &sender_thread_attr, send, nullptr);
    pthread_attr_destroy(&sender_thread_attr);

    return 0;
}
@[end if]@

void micrortps_start_topics(struct timespec &begin, uint64_t &total_read, uint64_t &received, int &loop)
{
@[if recv_topics]@

    char data_buffer[BUFFER_SIZE] = {};
    int read = 0;
    uint8_t topic_ID = 255;
    struct RcvTopicsPubs pubs;

    // ucdrBuffer to deserialize using the user defined buffer
    ucdrBuffer reader;
    ucdr_init_buffer(&reader, reinterpret_cast<uint8_t*>(data_buffer), BUFFER_SIZE);
@[end if]@

    px4_clock_gettime(CLOCK_REALTIME, &begin);
    _should_exit_task = false;
@[if send_topics]@

    // create a thread for sending data to the simulator
    pthread_t sender_thread;
    launch_send_thread(sender_thread);
@[end if]@

    while (!_should_exit_task)
    {
@[if recv_topics]@
        while (0 < (read = transport_node->read(&topic_ID, data_buffer, BUFFER_SIZE)))
        {
            total_read += read;
            switch (topic_ID)
            {
@[for idx, topic in enumerate(recv_topics)]@
                case @(rtps_message_id(ids, topic)):
                {
                    @(receive_base_types[idx])_s @(topic)_data;
                    deserialize_@(receive_base_types[idx])(&reader, &@(topic)_data, data_buffer);
                    pubs.@(topic)_pub.publish(@(topic)_data);
                    ++received;
                }
                break;
@[end for]@
                default:
                    PX4_WARN("Unexpected topic ID\n");
                break;
            }
        }
@[end if]@

        // loop forever if informed loop number is negative
        if (_options.loops >= 0 && loop >= _options.loops) break;

        px4_usleep(_options.sleep_ms * 1000);
        ++loop;
    }
@[if send_topics]@
    _should_exit_task = true;
    pthread_join(sender_thread, nullptr);
@[end if]@
}

@###############################################
@#
@# EmPy template for generating microRTPS_client.cpp file
@#
@###############################################
@# Start of Template
@#
@# Context:
@#  - msgs (List) list of all msg files
@#  - multi_topics (List) list of all multi-topic names
@#  - ids (List) list of all RTPS msg ids
@###############################################
@{
import os

import genmsg.msgs

from px_generate_uorb_topic_helper import * # this is in Tools/
from px_generate_uorb_topic_files import MsgScope # this is in Tools/

topic_names = [s.short_name for s in spec]
send_topics = [(alias[idx] if alias[idx] else s.short_name) for idx, s in enumerate(spec) if scope[idx] == MsgScope.SEND]
send_base_types = [s.short_name for idx, s in enumerate(spec) if scope[idx] == MsgScope.SEND]
recv_topics = [(alias[idx] if alias[idx] else s.short_name) for idx, s in enumerate(spec) if scope[idx] == MsgScope.RECEIVE]
receive_base_types = [s.short_name for idx, s in enumerate(spec) if scope[idx] == MsgScope.RECEIVE]
}@
/****************************************************************************
 *
 * Copyright (c) 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima).
 * Copyright (c) 2018-2019 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

#include "microRTPS_transport.h"
#include "microRTPS_client.h"

#include <inttypes.h>
#include <cstdio>
#include <ctime>
#include <pthread.h>

#include <ucdr/microcdr.h>
#include <px4_time.h>
#include <uORB/uORB.h>

#include <uORB/Publication.hpp>
#include <uORB/Subscription.hpp>
@[for topic in list(set(topic_names))]@
#include <uORB/topics/@(topic).h>
#include <uORB_microcdr/topics/@(topic).h>
@[end for]@

void* send(void *data);

uint8_t last_remote_msg_seq = 0;
uint8_t last_msg_seq = 0;

@[if send_topics]@
void* send(void* /*unused*/)
{
    char data_buffer[BUFFER_SIZE] = {};
    uint64_t sent = 0, total_sent = 0;
    int loop = 0, read = 0;
    uint32_t length = 0;
    size_t header_length = 0;

    /* subscribe to topics */
@[for idx, topic in enumerate(send_topics)]@
    uORB::Subscription @(topic)_sub{ORB_ID(@(topic))};
@[end for]@

    // ucdrBuffer to serialize using the user defined buffer
    ucdrBuffer writer;
    header_length = transport_node->get_header_length();
    ucdr_init_buffer(&writer, reinterpret_cast<uint8_t*>(&data_buffer[header_length]), BUFFER_SIZE - header_length);

    struct timespec begin;
    px4_clock_gettime(CLOCK_REALTIME, &begin);

    while (!_should_exit_task)
    {
@[for idx, topic in enumerate(send_topics)]@
        @(send_base_types[idx])_s @(topic)_data;
        if (@(topic)_sub.update(&@(topic)_data)) {
@[if topic == 'Timesync' or topic == 'timesync']@
            if(@(topic)_data.sys_id == 0 && @(topic)_data.seq != last_remote_msg_seq && @(topic)_data.tc1 == 0) {
                last_remote_msg_seq = @(topic)_data.seq;

                @(topic)_data.timestamp = hrt_absolute_time();
                @(topic)_data.sys_id = 1;
                @(topic)_data.seq = last_msg_seq;
                @(topic)_data.tc1 = hrt_absolute_time() * 1000ULL;
                @(topic)_data.ts1 = @(topic)_data.ts1;

                last_msg_seq++;
@[end if]@
                // copy raw data into local buffer. Payload is shifted by header length to make room for header
                serialize_@(send_base_types[idx])(&writer, &@(topic)_data, &data_buffer[header_length], &length);
                if (0 < (read = transport_node->write(static_cast<char>(@(rtps_message_id(ids, topic))), data_buffer, length)))
                {
                    total_sent += read;
                    ++sent;
                }
@[if topic == 'Timesync' or topic == 'timesync']@
            }
@[end if]@
        }
@[end for]@
        px4_usleep(_options.sleep_ms * 1000);
        ++loop;
    }

    struct timespec end;
    px4_clock_gettime(CLOCK_REALTIME, &end);
    double elapsed_secs = end.tv_sec - begin.tv_sec + (end.tv_nsec - begin.tv_nsec) / 1e9;
    PX4_INFO("SENT: %" PRIu64 " messages in %d LOOPS, %" PRIu64 " bytes in %.03f seconds - %.02fKB/s",
                sent, loop, total_sent, elapsed_secs, total_sent / (1e3 * elapsed_secs));

    return nullptr;
}

static int launch_send_thread(pthread_t &sender_thread)
{
    pthread_attr_t sender_thread_attr;
    pthread_attr_init(&sender_thread_attr);
    pthread_attr_setstacksize(&sender_thread_attr, PX4_STACK_ADJUSTED(4000));
    struct sched_param param;
    (void)pthread_attr_getschedparam(&sender_thread_attr, &param);
    param.sched_priority = SCHED_PRIORITY_DEFAULT;
    (void)pthread_attr_setschedparam(&sender_thread_attr, &param);
    pthread_create(&sender_thread, &sender_thread_attr, send, nullptr);
    pthread_attr_destroy(&sender_thread_attr);

    return 0;
}
@[end if]@

void micrortps_start_topics(struct timespec &begin, uint64_t &total_read, uint64_t &received, int &loop)
{
@[if recv_topics]@

    char data_buffer[BUFFER_SIZE] = {};
    int read = 0;
    uint8_t topic_ID = 255;

    // Declare received topics
@[for idx, topic in enumerate(recv_topics)]@
    @(receive_base_types[idx])_s @(topic)_data;
    uORB::Publication<@(receive_base_types[idx])_s> @(topic)_pub{ORB_ID(@(topic))};
@[end for]@

    // ucdrBuffer to deserialize using the user defined buffer
    ucdrBuffer reader;
    ucdr_init_buffer(&reader, reinterpret_cast<uint8_t*>(data_buffer), BUFFER_SIZE);
@[end if]@

    px4_clock_gettime(CLOCK_REALTIME, &begin);
    _should_exit_task = false;
@[if send_topics]@

    // create a thread for sending data to the simulator
    pthread_t sender_thread;
    launch_send_thread(sender_thread);
@[end if]@

    while (!_should_exit_task)
    {
@[if recv_topics]@
        while (0 < (read = transport_node->read(&topic_ID, data_buffer, BUFFER_SIZE)))
        {
            total_read += read;
            switch (topic_ID)
            {
@[for idx, topic in enumerate(recv_topics)]@
                case @(rtps_message_id(ids, topic)):
                {
                    deserialize_@(receive_base_types[idx])(&reader, &@(topic)_data, data_buffer);
                    @(topic)_pub.publish(@(topic)_data);
                    ++received;
                }
                break;
@[end for]@
                default:
                    PX4_WARN("Unexpected topic ID\n");
                break;
            }
        }
@[end if]@

        // loop forever if informed loop number is negative
        if (_options.loops >= 0 && loop >= _options.loops) break;

        px4_usleep(_options.sleep_ms * 1000);
        ++loop;
    }
@[if send_topics]@
    _should_exit_task = true;
    pthread_join(sender_thread, nullptr);
@[end if]@
}


Allows multiple restorations to happen in parallel.  Ewen had already done most of this work by separating presences that were being restored by the key name of the entire restoration operation.  I re-arranged things a bit, and removed logic blocking additional restore calls to occur simultaneously.

if (typeof(std.persist) === 'undefined')
    throw new Error('Error.  Do not import this file directly.  Only import it from persistService.em');

(function()
 {

     /**
      Each element of this array contains the unique id given to the
      presence object when it was being persisted.  And each element
      correpsonds to a presence that is trying to be restored in the
      world.
      */
     var allPresStillRest = {};

     function registerPresenceStillRestoring(keyName, presObjID)
     {
         if (allPresStillRest[keyName] === undefined)
             allPresStillRest[keyName] = {};
         allPresStillRest[keyName][presObjID] = presObjID;
     }

     //removes the presence with name presObjID from the allPresStilRest
     //If all presences that were registered to be restored have been, then
     //execute cb.
     function deregisterPresenceStillRestoring(keyName,presObjID,cb)
     {
         delete allPresStillRest[keyName][presObjID];
         if (Object.keys(allPresStillRest[keyName]).length == 0) {
             delete allPresStillRest[keyName];
             cb(true, null); // Not sure about this being null....
         }
     }

     //returns a bool.  If all the presences associated the restoration id keyName
     //have been restored, then return true.  Otherwise, return false.
     function allPresencesRestored(keyName)
     {
         return (!(allPresStillRest[keyName] !== undefined
                   && Object.keys(allPresStillRest[keyName]).length != 0));
     }

     /**
      Each restored presence has an onConnected callback.  The
      onConnected callback should be generated from this afterRestored
      function.  All that the callback does is update the nameService
      that we're using with the new presence object and removes this
      presence from the list of presences that we are restoring that
      are still waiting to be connected (in allPresStillRest).

      Second stage of restoration (fixing up pointers to other
      objects) waits until all presences have been connected (polls
      allPresStillRest to see if have zero length).

      @param {int} presObjID, unique name that was given to presence
      before restore.  (ie name that goes into nameService.)

      @param nameService nameService used during restoration.
      */
     function afterRestored(keyName,presObjID,nameService,cb)
     {
         var returner = function()
         {
             //insert the presence into nameService;
             nameService.insertObjectWithName(system.self,presObjID);
             //removes this presence from the list of outstanding restores.
             //if all presences have been restored, executes cb.
             deregisterPresenceStillRestoring(keyName,presObjID,cb);
        };
         return returner;
     }

     function checkIsTriplet(toCheck)
     {
         if ((typeof(toCheck) !== 'object') || (!('length' in toCheck)) || (toCheck.length != 3))
             return false;

         return true;
     }

     function tripletGetIndexName(triplet)
     {
         if (!checkIsTriplet(triplet))
         {
             throw new Error('Error in tripletGetIndexName.  Requires triplet to be passed in');
         }

         return triplet[0];
     }

     function tripletGetValue(triplet)
     {
         if (!checkIsTriplet(triplet))
             throw new Error('Error in tripletGetValue.  Requires triplet to be passed in');

         var type = tripletGetType(triplet);
         if (type == 'number')
             return parseFloat(triplet[1]);
         if (type == 'string')
             return triplet[1];
         if (type == 'boolean')
             return (triplet[1] == 'true');

         return triplet[1];
     }

     function tripletGetType(triplet)
     {
         if (!checkIsTriplet(triplet))
             throw new Error('Error in tripletGetType.  Requires triplet to be passed in');
         return triplet[2];
     }

    /**
     @param {array} triplet Array containing three values.  Indices of
     array follow:
       0 : index
       1 : value to set to/object pointer/'null'
       2 : type record

     @return {boolean} Returns true if the value in this object is a
     value type
     */
    function tripletIsValueType (triplet)
    {
        if (! checkIsTriplet(triplet))
            throw new Error('Error in checkTripletIsValueType.  Requires triplet to be passed in');

        var tripType = tripletGetType(triplet);

        if ((tripType != 'object') && (tripType != 'function') &&
            (tripType!=std.persist.SYSTEM_TYPE_STRING) &&
            (tripType!=std.persist.PRESENCE_OBJECT_TYPE_STRING) &&
            (tripType!=std.persist.VISIBLE_TYPE_STRING) &&
            (tripType!=std.persist.TIMER_TYPE_STRING) &&
            (tripType!=std.persist.PRESENCE_ENTRY_TYPE_STRING) &&
            (tripType!=std.persist.FUNCTION_OBJECT_TYPE_STRING) &&
            (tripType!=std.persist.BASIC_OBJECT_TYPE_STRING))
            return true;

        return false;
    };

     /**
      @param {int} ptrId The unique name of the object that should be
      referenced by calling localCopyToPoint[index]
      @param index of the local object to perform fixup on.
      @param {object} localCopyToPoint localCopyToPoint[index] should be the new
      object pointer
      @param {array} allToFix an array.  Push the above three arguments onto that
      array (as an array).

      After rest of deserialization occurs, final step is to call
      performFinalFixups with this array as an argument.
      performPtrFinalFixups traverses array, and points
      localCopyToPoint[index] to the objects described by objPtrToFix.
      */
     function registerFixupObjectPointer(ptrId, index, localCopyToPoint, allToFix)
     {
         allToFix.push([ptrId, index,localCopyToPoint]);
     };

     /**
      locaCopyToPoint[index] should end up pointing to the object represented
      by the lookup into nameService of ptrId

      @see registerFixupObjectPointer
      @see fixSinglePtr
      */
     function fixSinglePtr (ptrId, index,localCopyToPoint,nameService)
     {
         if ((typeof(localCopyToPoint) != 'object') || (localCopyToPoint== null))
             throw new Error('Error in fixSinglePtr.  Should not have received non-obect or null ptr record.');

         var fixedObj = nameService.lookupObject(ptrId);
         if (fixedObj == nameService.DNE)
             throw new Error('Error in fixSinglePtr.  Have no record of object that you are trying to point to: ' + fixedObj);

         localCopyToPoint[index] = fixedObj;
     };

     /**
      @param {array} allToFix array.  Each element has three values. [0]
      ptrId, [1] index, [2] localCopyToPoint.

      Runs through array generated by calls to registerFixupObjectPointer,
      and individually performs fixups for each one.

      @see registerFixupObjectPointer
      @see fixSinglePtr
      */
     function performPtrFinalFixups(allToFix,nameService)
     {
         for (var s in allToFix)
             fixSinglePtr(allToFix[s][0],allToFix[s][1],allToFix[s][2],nameService);
     };

     /**
      fixReferences opens the keyName table, and returns an object whose
      subgraph is rooted at the object with id ptrId. Note: all objects the
      root object references may not be linked to correctly.  Need to call
      performPtrFinalFixups on ptrsToFix after running this function.

      @param {keyName}, Passed to the backend.  This is the entry to
      access.

      @param {int}, ptrId, Each object has a unique id.  ptrId is the
      identifier for the object that we should be fixing references for (in
      the keyName table).

      @param {array} ptrsToFix is an array that tracks all objects that we
      will need to fixup the references too.  See performFixupPtr function.

      @param nameService can be used to lookup the ids for objects that
      we've already re-read.

      @param cb callback to invoke when complete. Passed success flag (boolean)
      and, if successful, an object whose subgraph is rooted at the object with id ptrId.

      */
     function fixReferences(keyName, ptrId, ptrsToFix, nameService, cb)
     {
         // Before starting the read, insert the object as a
         // known-but-not-yet-available object in the name
         // service. This should prevent others from starting another
         // read on it but won't make it available yet.
         nameService.insertNA(ptrId);
         readObject(
             keyName,ptrId,
             std.core.bind(finishFixReferences, undefined, keyName, ptrId, ptrsToFix, nameService, cb) // + success, object
         );
     }

     function finishFixReferences(keyName, ptrId, ptrsToFix, nameService, cb, success, unfixedObj) {
         if (!success)
         {
             cb(false);
             return;
         }

         var id = unfixedObj['mID'];
         if (id != ptrId) //throw new Error('Error: ptrId and object id must be identical');
             cb(false);

         if (nameService.lookupObject(ptrId) != nameService.NA) {
             throw new Error("Error. Called fixReferences on unknown object or object I've already seen: " + keyName);
         }

         var type = unfixedObj['type'];
         if (type == std.persist.FUNCTION_OBJECT_TYPE_STRING)
             restoreFunction(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else if (type == std.persist.PRESENCE_OBJECT_TYPE_STRING)
             restorePresence(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else if (type == std.persist.BASIC_OBJECT_TYPE_STRING)
             restoreBasicObject(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else if (type == std.persist.VEC_TYPE_STRING)
             restoreVec3Object(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else if (type == std.persist.QUAT_TYPE_STRING)
             restoreQuatObject(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else if (type == std.persist.VISIBLE_TYPE_STRING)
             restoreVisibleObject(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else
             throw new Error('Error in fixReferences.  Do not have any other types in the system to restore from.');
     }

     /**
      @see fixReferences for arguments
      Returns a new visible object.
      */
     function restoreVisibleObject(keyName, visRecord,ptrId,ptrsToFix,nameService,cb)
     {
         var pToFix = [];
         restoreBasicObject(
             keyName,visRecord,ptrId,pToFix,nameService,
             std.core.bind(finishRestoreVisibleObject, undefined, keyname, visRecord, ptrId, pToFix, nameService, cb)
         );
     };
     function finishRestoreVisibleObject(keyName, visRecord,ptrId,pToFix,nameService,cb, success, toRestoreFrom) {
         if(!success) {
             cb(false);
             return;
         }

         // Note that we use a separate set of ptrs and do a fixup
         // here because multiple fields in a presence might point to
         // the same object (e.g. identical strings). In that case, we
         // need to fix up pointers before we can use the object
         // safely. Note that this currently only works because we
         // don't serialize any extra fields on presences. When we do,
         // we can't do ptr fixups until the entire object graph is
         // restored.
         //
         //perform the fixups for the objects that this presence was
         //trying to point to.  (Eg. pos object, vel obj, etc.)
         performPtrFinalFixups(pToFix,nameService);

         var returner = system.createVisible(
             toRestoreFrom.sporef,
             toRestoreFrom.pos,
             toRestoreFrom.vel,
             toRestoreFrom.posTime,
             toRestoreFrom.orient,
             toRestoreFrom.orientVel,
             toRestoreFrom.orientTime,
             toRestoreFrom.pos,
             toRestoreFrom.scale,
             toRestoreFrom.mesh,
             toRestoreFrom.physics,
             toRestoreFrom.solidAngleQuery
         );

         cb(true, returner);
     }

     /**
      @see fixReferencs for arguments.
      Returns a new vec3.
      */
     function restoreVec3Object(keyName, unfixedObj,ptrId,ptrsToFix,nameService, cb)
     {
         //lkjs: may want some check here.
         var x = getValueField(unfixedObj,'x');
         var y = getValueField(unfixedObj,'y');
         var z = getValueField(unfixedObj,'z');
         var returner = new util.Vec3(x,y,z);
         nameService.insertObjectWithName(returner,ptrId);
         cb(true, returner);
     }

     /**
      @see fixReferencs for arguments.
      Returns a new quaternion.
      */
     function restoreQuatObject(keyName, unfixedObj,ptrId,ptrsToFix,nameService, cb)
     {
         var x = getValueField(unfixedObj,'x');
         var y = getValueField(unfixedObj,'y');
         var z = getValueField(unfixedObj,'z');
         var w = getValueField(unfixedObj,'w');

         var returner = new util.Quaternion(x,y,z,w);
         nameService.insertObjectWithName(returner,ptrId);
         cb(true, returner);
     }

     /**
      @param unfixedObj Is an object that is read in directly from the
      backend.  It has three types of fields: 1) triplets, 2) mID (obj
      id), 3) type (obj type).

      @param Field name that we're looking for in one of the triplets.

      @return returns the value of the triplet associated with
      filedName

      @throw Throws an error if fieldName does not exist as a field in
      any of unfixedObj's triplets.

      This function runs through all the triplets in unfixedObj, and
      returns the value of the triplet that matches the field.
      */
     function getValueField(unfixedObj,fieldName)
     {
         for (var s in unfixedObj)
         {
             if ((s == 'mID') || (s == 'type'))
                 continue;

             var propValTypeTriple = unfixedObj[s];

             var tripFieldName = tripletGetIndexName(unfixedObj[s]);
             if (tripFieldName == fieldName)
                 return tripletGetValue(propValTypeTriple);
         }
         throw new Error('Error in getValueField.  Have no field named: '+ fieldName);
     }

     /**
      For keyName,ptrId,ptrsToFix, and nameService, @see fixReferences.

      unfixedPres has all the data that you would get from a call to
      getAllData on a presence.

      */
     function restorePresence(keyName,unfixedPres,ptrId,ptrsToFix,nameService, cb)
     {
         var pToFix = [];
         restoreBasicObject(
             keyName,unfixedPres,ptrId,pToFix,nameService,
             std.core.bind(finishRestorePresence, undefined, keyName, unfixedPres, ptrId, pToFix, nameService, cb) // + success, obj
         );
     }
     function finishRestorePresence(keyName,unfixedPres,ptrId,pToFix,nameService, cb, success, toRestoreFrom) {
         if(!success) {
             cb(false);
             return;
         }

         // Note that we use a separate set of ptrs and do a fixup
         // here because multiple fields in a presence might point to
         // the same object (e.g. identical strings). In that case, we
         // need to fix up pointers before we can use the object
         // safely. Note that this currently only works because we
         // don't serialize any extra fields on presences. When we do,
         // we can't do ptr fixups until the entire object graph is
         // restored.
         //
         //perform the fixups for the objects that this presence was
         //trying to point to.  (Eg. pos object, vel obj, etc.)
         performPtrFinalFixups(pToFix,nameService);

         for (var s in system.presences)
         {
             if (system.presences[s].toString() == toRestoreFrom.sporef)
             {
                 cb(true,system.presences[s]);
                 return;
             }
         }

         //tells the system that we have begun trying to restore this
         //presence, and not to continue with later stages of
         //restoration (fixing looped object pointers) until the
         //system has connected this presence.
         registerPresenceStillRestoring(keyName, ptrId);

         var onConnectCB = afterRestored(keyName,ptrId,nameService,cb);
         system.restorePresence(
             toRestoreFrom.sporef,
             toRestoreFrom.pos,
             toRestoreFrom.vel,
             toRestoreFrom.posTime,
             toRestoreFrom.orient,
             toRestoreFrom.orientVel,
             toRestoreFrom.orientTime,
             toRestoreFrom.mesh,
             toRestoreFrom.physics,
             toRestoreFrom.scale,
             toRestoreFrom.isCleared,
             toRestoreFrom.contextId,
             toRestoreFrom.isConnected,
             onConnectCB,
             toRestoreFrom.isSuspended,
             toRestoreFrom.suspendedVelocity,
             toRestoreFrom.suspendedOrientationVelocity,
             toRestoreFrom.solidAngleQuery
         );
     };

     /**
      For keyName,ptrId,ptrsToFix, and nameService, @see fixReferences.

      unfixedFunc right now just has one field: 'funcField' with the
      text of the function that we're creating.
      */
     function restoreFunction(keyName,unfixedFunc,ptrId,ptrsToFix,nameService, cb)
     {
         //FIXME: lkjs;  GROSS!
         var funcTriple = unfixedFunc[0];

         var index = tripletGetIndexName(funcTriple);
         if (index != 'funcField')
             throw new Error('Error restoring function.  Restoring object does not have funcField.');

         var funcAsString = tripletGetValue(funcTriple);
         funcAsString = '(' + funcAsString + ')';

         var returner = null;
         var success = true;
         try
         {
             returner = eval(funcAsString);
         }
         catch (excep)
         {
             system.print('ERROR: Error in restoreFunction.  Trying to restore a function with faulty syntax. Key: ' + keyName + '\n');
             returner = function(){};
             success = false;
         }
         nameService.insertObjectWithName(returner,ptrId);
         cb(success, returner);
     }

     /**
      For keyName, ptrId,ptrsToFix, and nameService, @see fixReferences.

      @param unfixedObj is the object that we get back from reading
      from reading from backend.  It is a basic object (not a
      function, presence, system obj, etc.).  This function runs
      through all of its fields and registers them to be fixed up.

      */
     function restoreBasicObject(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb)
     {
         var returner = { };
         nameService.insertObjectWithName(returner,ptrId);
         //run through all the fields in unfixedObj.  If the field is a
         //value type, point returner to the new field right away.  If the
         //field is an object type and we have a copy of that object, then
         //point returner to that new object right away.  If the field is
         //an object type and we do not have a copy of that object, then
         //register returner to be fixed-up with the new pointer later.

         var unfinished_fields = 0;
         var did_callback = false;
         var finish_field_cb = function(success, val) {
             if (!success && !did_callback) {
                 did_callback = true;
                 cb(false);
                 return;
             }
             unfinished_fields -= 1;
             if (unfinished_fields == 0) {
                 did_callback = true;
                 cb(true, returner);
             }
         };
         for (var s in unfixedObj)
         {
             if ((s == 'mID') || (s == 'type'))
                 continue;

             var index = tripletGetIndexName(unfixedObj[s]);

             //sets to value type
             if (tripletIsValueType(unfixedObj[s]))
                 returner[index] = tripletGetValue(unfixedObj[s]);
             else
             {
                 if ((tripletGetType(unfixedObj[s])) == 'null')
                     returner[index] = null;
                 else
                 {
                     var ptrIdInner  = tripletGetValue(unfixedObj[s]);
                     var ptrObj = nameService.lookupObject(ptrIdInner);
                     if (ptrObj == nameService.DNE)
                     {
                         //will have to register this pointer to be fixed up
                         registerFixupObjectPointer( ptrIdInner ,index,returner,ptrsToFix);

                         // We need to request the data from storage since we didn't even know about it yet
                         unfinished_fields += 1;
                         fixReferences(keyName,ptrIdInner,ptrsToFix,nameService, std.core.bind(finish_field_cb, undefined));
                     }
                     else if (ptrObj == nameService.NA) {
                         //will have to register this pointer to be fixed up, but the request for data is already out
                         registerFixupObjectPointer( ptrIdInner ,index,returner,ptrsToFix);
                     }
                     else
                     {
                         //already have the object.  just link to it.
                         returner[index] = ptrObj;
                     }
                 }
             }
         }

         // Special case for empty objects
         if (unfinished_fields == 0) {
             cb(true, returner);
         }
     };

     /**
      @see std.persist.restoreFromAndGetNamesAsync
      */
     std.persist.restoreFromAsync = function (keyName,cb)
     {
         return std.persist.restoreFromAndGetNamesAsync(keyName,0,cb);
     };

     /**
      This function should be used if the object graph that you are
      trying to restore has presences in it, and you need to wait for
      the system to actually connect these presences.

      @param cb Takes in three parameters, first is restored obj graph
      (if pres failure, all presences become null objs.), second
      boolean for success (ie true if eveything restored
      appropriately, and space allowed new presences to reconnect,
      false otherwise), the third is the nameService that we used.
      */
     std.persist.restoreFromAndGetNamesAsync = function (keyName,id,cb)
     {
         var nameService = new std.persist.NameService();
         var ptrsToFix = [];

         // Setup a timeout in case we fail to get all presences reconnected
         var restoreCheckback = function() {
             cb(false);
         };
         var tout = system.timeout(60,restoreCheckback);

         fixReferences(
             keyName, id,ptrsToFix,nameService,
             std.core.bind(finishRestoreFromAndGetNamesAsync, undefined, keyName, id, cb, ptrsToFix, nameService, tout) // + success, obj
         );
     };

     var finishRestoreFromAndGetNamesAsync = function (keyName,id,cb, ptrsToFix, nameService, timeout, success, returner) {
         // Clear timeout since we've either succeeded or permanently failed
         if (!timeout.cleared) {
             timeout.clear();
             timeout.cleared = true;
         }

         if (!success) {
             cb(false);
             return;
         }
         if (!allPresencesRestored(keyName))
         {
             system.print("Error: Shouldn't get finishRestoreFromAndGetNamesAsync callback until all presences are restored.\n");
             cb(false);
             return;
         }

         // Success
         performPtrFinalFixups(ptrsToFix,nameService);
         cb(true,returner,nameService);
     };

 })();

if (typeof(std.persist) === 'undefined')
    throw new Error('Error.  Do not import this file directly.  Only import it from persistService.em');

(function()
 {

     /**
      Each element of this array contains the unique id given to the
      presence object when it was being persisted.  And each element
      correpsonds to a presence that is trying to be restored in the
      world.
      */
     var allPresStillRest = {};

     var mRestoring = false;

     function registerPresenceStillRestoring(keyName, presObjID)
     {
         if (allPresStillRest[keyName] === undefined)
             allPresStillRest[keyName] = {};
         allPresStillRest[keyName][presObjID] = presObjID;
     }

     /**
      Each restored presence has an onConnected callback.  The
      onConnected callback should be generated from this afterRestored
      function.  All that the callback does is update the nameService
      that we're using with the new presence object and removes this
      presence from the list of presences that we are restoring that
      are still waiting to be connected (in allPresStillRest).

      Second stage of restoration (fixing up pointers to other
      objects) waits until all presences have been connected (polls
      allPresStillRest to see if have zero length).

      @param {int} presObjID, unique name that was given to presence
      before restore.  (ie name that goes into nameService.)

      @param nameService nameService used during restoration.
      */
     function afterRestored(keyName,presObjID,nameService,cb)
     {
         var returner = function()
         {
             //insert the presence into nameService;
             nameService.insertObjectWithName(system.self,presObjID);
             delete allPresStillRest[keyName][presObjID];
             if (Object.keys(allPresStillRest[keyName]).length == 0) {
                 delete allPresStillRest[keyName];
                 cb(true, null); // Not sure about this being null....
             }
        };
         return returner;
     }

     function checkIsTriplet(toCheck)
     {
         if ((typeof(toCheck) !== 'object') || (!('length' in toCheck)) || (toCheck.length != 3))
             return false;

         return true;
     }

     function tripletGetIndexName(triplet)
     {
         if (!checkIsTriplet(triplet))
         {
             throw new Error('Error in tripletGetIndexName.  Requires triplet to be passed in');
         }

         return triplet[0];
     }

     function tripletGetValue(triplet)
     {
         if (!checkIsTriplet(triplet))
             throw new Error('Error in tripletGetValue.  Requires triplet to be passed in');

         var type = tripletGetType(triplet);
         if (type == 'number')
             return parseFloat(triplet[1]);
         if (type == 'string')
             return triplet[1];
         if (type == 'boolean')
             return (triplet[1] == 'true');

         return triplet[1];
     }

     function tripletGetType(triplet)
     {
         if (!checkIsTriplet(triplet))
             throw new Error('Error in tripletGetType.  Requires triplet to be passed in');
         return triplet[2];
     }

    /**
     @param {array} triplet Array containing three values.  Indices of
     array follow:
       0 : index
       1 : value to set to/object pointer/'null'
       2 : type record

     @return {boolean} Returns true if the value in this object is a
     value type
     */
    function tripletIsValueType (triplet)
    {
        if (! checkIsTriplet(triplet))
            throw new Error('Error in checkTripletIsValueType.  Requires triplet to be passed in');

        var tripType = tripletGetType(triplet);

        if ((tripType != 'object') && (tripType != 'function') &&
            (tripType!=std.persist.SYSTEM_TYPE_STRING) &&
            (tripType!=std.persist.PRESENCE_OBJECT_TYPE_STRING) &&
            (tripType!=std.persist.VISIBLE_TYPE_STRING) &&
            (tripType!=std.persist.TIMER_TYPE_STRING) &&
            (tripType!=std.persist.PRESENCE_ENTRY_TYPE_STRING) &&
            (tripType!=std.persist.FUNCTION_OBJECT_TYPE_STRING) &&
            (tripType!=std.persist.BASIC_OBJECT_TYPE_STRING))
            return true;

        return false;
    };

     /**
      @param {int} ptrId The unique name of the object that should be
      referenced by calling localCopyToPoint[index]
      @param index of the local object to perform fixup on.
      @param {object} localCopyToPoint localCopyToPoint[index] should be the new
      object pointer
      @param {array} allToFix an array.  Push the above three arguments onto that
      array (as an array).

      After rest of deserialization occurs, final step is to call
      performFinalFixups with this array as an argument.
      performPtrFinalFixups traverses array, and points
      localCopyToPoint[index] to the objects described by objPtrToFix.
      */
     function registerFixupObjectPointer(ptrId, index, localCopyToPoint, allToFix)
     {
         allToFix.push([ptrId, index,localCopyToPoint]);
     };

     /**
      locaCopyToPoint[index] should end up pointing to the object represented
      by the lookup into nameService of ptrId

      @see registerFixupObjectPointer
      @see fixSinglePtr
      */
     function fixSinglePtr (ptrId, index,localCopyToPoint,nameService)
     {
         if ((typeof(localCopyToPoint) != 'object') || (localCopyToPoint== null))
             throw new Error('Error in fixSinglePtr.  Should not have received non-obect or null ptr record.');

         var fixedObj = nameService.lookupObject(ptrId);
         if (fixedObj == nameService.DNE)
             throw new Error('Error in fixSinglePtr.  Have no record of object that you are trying to point to: ' + fixedObj);

         localCopyToPoint[index] = fixedObj;
     };

     /**
      @param {array} allToFix array.  Each element has three values. [0]
      ptrId, [1] index, [2] localCopyToPoint.

      Runs through array generated by calls to registerFixupObjectPointer,
      and individually performs fixups for each one.

      @see registerFixupObjectPointer
      @see fixSinglePtr
      */
     function performPtrFinalFixups(allToFix,nameService)
     {
         for (var s in allToFix)
             fixSinglePtr(allToFix[s][0],allToFix[s][1],allToFix[s][2],nameService);
     };

     /**
      fixReferences opens the keyName table, and returns an object whose
      subgraph is rooted at the object with id ptrId. Note: all objects the
      root object references may not be linked to correctly.  Need to call
      performPtrFinalFixups on ptrsToFix after running this function.

      @param {keyName}, Passed to the backend.  This is the entry to
      access.

      @param {int}, ptrId, Each object has a unique id.  ptrId is the
      identifier for the object that we should be fixing references for (in
      the keyName table).

      @param {array} ptrsToFix is an array that tracks all objects that we
      will need to fixup the references too.  See performFixupPtr function.

      @param nameService can be used to lookup the ids for objects that
      we've already re-read.

      @param cb callback to invoke when complete. Passed success flag (boolean)
      and, if successful, an object whose subgraph is rooted at the object with id ptrId.

      */
     function fixReferences(keyName, ptrId, ptrsToFix, nameService, cb)
     {
         // Before starting the read, insert the object as a
         // known-but-not-yet-available object in the name
         // service. This should prevent others from starting another
         // read on it but won't make it available yet.
         nameService.insertNA(ptrId);
         readObject(
             keyName,ptrId,
             std.core.bind(finishFixReferences, undefined, keyName, ptrId, ptrsToFix, nameService, cb) // + success, object
         );
     }

     function finishFixReferences(keyName, ptrId, ptrsToFix, nameService, cb, success, unfixedObj) {
         if (!success)
         {
             cb(false);
             return;
         }

         var id = unfixedObj['mID'];
         if (id != ptrId) //throw new Error('Error: ptrId and object id must be identical');
             cb(false);

         if (nameService.lookupObject(ptrId) != nameService.NA) {
             throw new Error("Error. Called fixReferences on unknown object or object I've already seen: " + keyName);
         }

         var type = unfixedObj['type'];
         if (type == std.persist.FUNCTION_OBJECT_TYPE_STRING)
             restoreFunction(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else if (type == std.persist.PRESENCE_OBJECT_TYPE_STRING)
             restorePresence(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else if (type == std.persist.BASIC_OBJECT_TYPE_STRING)
             restoreBasicObject(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else if (type == std.persist.VEC_TYPE_STRING)
             restoreVec3Object(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else if (type == std.persist.QUAT_TYPE_STRING)
             restoreQuatObject(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else if (type == std.persist.VISIBLE_TYPE_STRING)
             restoreVisibleObject(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb);
         else
             throw new Error('Error in fixReferences.  Do not have any other types in the system to restore from.');
     }

     /**
      @see fixReferences for arguments
      Returns a new visible object.
      */
     function restoreVisibleObject(keyName, visRecord,ptrId,ptrsToFix,nameService,cb)
     {
         var pToFix = [];
         restoreBasicObject(
             keyName,visRecord,ptrId,pToFix,nameService,
             std.core.bind(finishRestoreVisibleObject, undefined, keyname, visRecord, ptrId, pToFix, nameService, cb)
         );
     };
     function finishRestoreVisibleObject(keyName, visRecord,ptrId,pToFix,nameService,cb, success, toRestoreFrom) {
         if(!success) {
             cb(false);
             return;
         }

         // Note that we use a separate set of ptrs and do a fixup
         // here because multiple fields in a presence might point to
         // the same object (e.g. identical strings). In that case, we
         // need to fix up pointers before we can use the object
         // safely. Note that this currently only works because we
         // don't serialize any extra fields on presences. When we do,
         // we can't do ptr fixups until the entire object graph is
         // restored.
         //
         //perform the fixups for the objects that this presence was
         //trying to point to.  (Eg. pos object, vel obj, etc.)
         performPtrFinalFixups(pToFix,nameService);

         var returner = system.createVisible(
             toRestoreFrom.sporef,
             toRestoreFrom.pos,
             toRestoreFrom.vel,
             toRestoreFrom.posTime,
             toRestoreFrom.orient,
             toRestoreFrom.orientVel,
             toRestoreFrom.orientTime,
             toRestoreFrom.pos,
             toRestoreFrom.scale,
             toRestoreFrom.mesh,
             toRestoreFrom.physics,
             toRestoreFrom.solidAngleQuery
         );

         cb(true, returner);
     }

     /**
      @see fixReferencs for arguments.
      Returns a new vec3.
      */
     function restoreVec3Object(keyName, unfixedObj,ptrId,ptrsToFix,nameService, cb)
     {
         //lkjs: may want some check here.
         var x = getValueField(unfixedObj,'x');
         var y = getValueField(unfixedObj,'y');
         var z = getValueField(unfixedObj,'z');
         var returner = new util.Vec3(x,y,z);
         nameService.insertObjectWithName(returner,ptrId);
         cb(true, returner);
     }

     /**
      @see fixReferencs for arguments.
      Returns a new quaternion.
      */
     function restoreQuatObject(keyName, unfixedObj,ptrId,ptrsToFix,nameService, cb)
     {
         var x = getValueField(unfixedObj,'x');
         var y = getValueField(unfixedObj,'y');
         var z = getValueField(unfixedObj,'z');
         var w = getValueField(unfixedObj,'w');

         var returner = new util.Quaternion(x,y,z,w);
         nameService.insertObjectWithName(returner,ptrId);
         cb(true, returner);
     }

     /**
      @param unfixedObj Is an object that is read in directly from the
      backend.  It has three types of fields: 1) triplets, 2) mID (obj
      id), 3) type (obj type).

      @param Field name that we're looking for in one of the triplets.

      @return returns the value of the triplet associated with
      filedName

      @throw Throws an error if fieldName does not exist as a field in
      any of unfixedObj's triplets.

      This function runs through all the triplets in unfixedObj, and
      returns the value of the triplet that matches the field.
      */
     function getValueField(unfixedObj,fieldName)
     {
         for (var s in unfixedObj)
         {
             if ((s == 'mID') || (s == 'type'))
                 continue;

             var propValTypeTriple = unfixedObj[s];

             var tripFieldName = tripletGetIndexName(unfixedObj[s]);
             if (tripFieldName == fieldName)
                 return tripletGetValue(propValTypeTriple);
         }
         throw new Error('Error in getValueField.  Have no field named: '+ fieldName);
     }

     /**
      For keyName,ptrId,ptrsToFix, and nameService, @see fixReferences.

      unfixedPres has all the data that you would get from a call to
      getAllData on a presence.

      */
     function restorePresence(keyName,unfixedPres,ptrId,ptrsToFix,nameService, cb)
     {
         var pToFix = [];
         restoreBasicObject(
             keyName,unfixedPres,ptrId,pToFix,nameService,
             std.core.bind(finishRestorePresence, undefined, keyName, unfixedPres, ptrId, pToFix, nameService, cb) // + success, obj
         );
     }
     function finishRestorePresence(keyName,unfixedPres,ptrId,pToFix,nameService, cb, success, toRestoreFrom) {
         if(!success) {
             cb(false);
             return;
         }

         // Note that we use a separate set of ptrs and do a fixup
         // here because multiple fields in a presence might point to
         // the same object (e.g. identical strings). In that case, we
         // need to fix up pointers before we can use the object
         // safely. Note that this currently only works because we
         // don't serialize any extra fields on presences. When we do,
         // we can't do ptr fixups until the entire object graph is
         // restored.
         //
         //perform the fixups for the objects that this presence was
         //trying to point to.  (Eg. pos object, vel obj, etc.)
         performPtrFinalFixups(pToFix,nameService);

         for (var s in system.presences)
         {
             if (system.presences[s].toString() == toRestoreFrom.sporef)
             {
                 cb(true,system.presences[s]);
                 return;
             }
         }

         //tells the system that we have begun trying to restore this
         //presence, and not to continue with later stages of
         //restoration (fixing looped object pointers) until the
         //system has connected this presence.
         registerPresenceStillRestoring(keyName, ptrId);

         var onConnectCB = afterRestored(keyName,ptrId,nameService,cb);
         system.restorePresence(
             toRestoreFrom.sporef,
             toRestoreFrom.pos,
             toRestoreFrom.vel,
             toRestoreFrom.posTime,
             toRestoreFrom.orient,
             toRestoreFrom.orientVel,
             toRestoreFrom.orientTime,
             toRestoreFrom.mesh,
             toRestoreFrom.physics,
             toRestoreFrom.scale,
             toRestoreFrom.isCleared,
             toRestoreFrom.contextId,
             toRestoreFrom.isConnected,
             onConnectCB,
             toRestoreFrom.isSuspended,
             toRestoreFrom.suspendedVelocity,
             toRestoreFrom.suspendedOrientationVelocity,
             toRestoreFrom.solidAngleQuery
         );
     };

     /**
      For keyName,ptrId,ptrsToFix, and nameService, @see fixReferences.

      unfixedFunc right now just has one field: 'funcField' with the
      text of the function that we're creating.
      */
     function restoreFunction(keyName,unfixedFunc,ptrId,ptrsToFix,nameService, cb)
     {
         //FIXME: lkjs;  GROSS!
         var funcTriple = unfixedFunc[0];

         var index = tripletGetIndexName(funcTriple);
         if (index != 'funcField')
             throw new Error('Error restoring function.  Restoring object does not have funcField.');

         var funcAsString = tripletGetValue(funcTriple);
         funcAsString = '(' + funcAsString + ')';

         var returner = null;
         var success = true;
         try
         {
             returner = eval(funcAsString);
         }
         catch (excep)
         {
             system.print('ERROR: Error in restoreFunction.  Trying to restore a function with faulty syntax. Key: ' + keyName + '\n');
             returner = function(){};
             success = false;
         }
         nameService.insertObjectWithName(returner,ptrId);
         cb(success, returner);
     }

     /**
      For keyName, ptrId,ptrsToFix, and nameService, @see fixReferences.

      @param unfixedObj is the object that we get back from reading
      from reading from backend.  It is a basic object (not a
      function, presence, system obj, etc.).  This function runs
      through all of its fields and registers them to be fixed up.

      */
     function restoreBasicObject(keyName,unfixedObj,ptrId,ptrsToFix,nameService,cb)
     {
         var returner = { };
         nameService.insertObjectWithName(returner,ptrId);
         //run through all the fields in unfixedObj.  If the field is a
         //value type, point returner to the new field right away.  If the
         //field is an object type and we have a copy of that object, then
         //point returner to that new object right away.  If the field is
         //an object type and we do not have a copy of that object, then
         //register returner to be fixed-up with the new pointer later.

         var unfinished_fields = 0;
         var did_callback = false;
         var finish_field_cb = function(success, val) {
             if (!success && !did_callback) {
                 did_callback = true;
                 cb(false);
                 return;
             }
             unfinished_fields -= 1;
             if (unfinished_fields == 0) {
                 did_callback = true;
                 cb(true, returner);
             }
         };
         for (var s in unfixedObj)
         {
             if ((s == 'mID') || (s == 'type'))
                 continue;

             var index = tripletGetIndexName(unfixedObj[s]);

             //sets to value type
             if (tripletIsValueType(unfixedObj[s]))
                 returner[index] = tripletGetValue(unfixedObj[s]);
             else
             {
                 if ((tripletGetType(unfixedObj[s])) == 'null')
                     returner[index] = null;
                 else
                 {
                     var ptrIdInner  = tripletGetValue(unfixedObj[s]);
                     var ptrObj = nameService.lookupObject(ptrIdInner);
                     if (ptrObj == nameService.DNE)
                     {
                         //will have to register this pointer to be fixed up
                         registerFixupObjectPointer( ptrIdInner ,index,returner,ptrsToFix);

                         // We need to request the data from storage since we didn't even know about it yet
                         unfinished_fields += 1;
                         fixReferences(keyName,ptrIdInner,ptrsToFix,nameService, std.core.bind(finish_field_cb, undefined));
                     }
                     else if (ptrObj == nameService.NA) {
                         //will have to register this pointer to be fixed up, but the request for data is already out
                         registerFixupObjectPointer( ptrIdInner ,index,returner,ptrsToFix);
                     }
                     else
                     {
                         //already have the object.  just link to it.
                         returner[index] = ptrObj;
                     }
                 }
             }
         }

         // Special case for empty objects
         if (unfinished_fields == 0) {
             cb(true, returner);
         }
     };

     /**
      Returns true if we're in the middle of a restore operation.
      Otherwise, returns false.
      */
     std.persist.inRestore = function()
     {
         return mRestoring;
     };

     /**
      @see std.persist.restoreFromAndGetNamesAsync
      */
     std.persist.restoreFromAsync = function (keyName,cb)
     {
         return std.persist.restoreFromAndGetNamesAsync(keyName,0,cb);
     };

     /**
      This function should be used if the object graph that you are
      trying to restore has presences in it, and you need to wait for
      the system to actually connect these presences.

      @param cb Takes in three parameters, first is restored obj graph
      (if pres failure, all presences become null objs.), second
      boolean for success (ie true if eveything restored
      appropriately, and space allowed new presences to reconnect,
      false otherwise), the third is the nameService that we used.
      */
     std.persist.restoreFromAndGetNamesAsync = function (keyName,id,cb)
     {
         if (std.persist.inRestore())
             throw new Error('Error, cannot request additional restores when in middle of current restore.  Check back later.');
         mRestoring = true;

         var nameService = new std.persist.NameService();
         var ptrsToFix = [];

         // Setup a timeout in case we fail to get all presences reconnected
         var restoreCheckback = function() {
             cb(false);
         };
         var tout = system.timeout(60,restoreCheckback);

         fixReferences(
             keyName, id,ptrsToFix,nameService,
             std.core.bind(finishRestoreFromAndGetNamesAsync, undefined, keyName, id, cb, ptrsToFix, nameService, tout) // + success, obj
         );
     };

     var finishRestoreFromAndGetNamesAsync = function (keyName,id,cb, ptrsToFix, nameService, timeout, success, returner) {
         // Clear timeout since we've either succeeded or permanently failed
         if (!timeout.cleared) {
             timeout.clear();
             timeout.cleared = true;
         }

         if (!success) {
             mRestoring = false;
             cb(false);
             return;
         }
         if (allPresStillRest[keyName] !== undefined && Object.keys(allPresStillRest[keyName]).length != 0) {
             system.print("Error: Shouldn't get finishRestoreFromAndGetNamesAsync callback until all presences are restored.\n");
             mRestoring = false;
             cb(false);
             return;
         }

         // Success
         performPtrFinalFixups(ptrsToFix,nameService);
         mRestoring = false;
         cb(true,returner,nameService);
     };

 })();