Esempio n. 1
0
    def __init__(self,
                 number,
                 debug=False,
                 sanity_checks=True,
                 auto_load=True):
        from fpesocketconnection import FPESocketConnection

        # First sanity check: ping the observatory simulator
        if not ping():
            raise Exception("Cannot ping 192.168.100.1")
        self._debug = debug
        self._dir = os.path.dirname(os.path.realpath(__file__))
        self._reset_in_progress = False
        self._loading_wrapper = False
        self.fpe_number = number
        self.connection = FPESocketConnection(5554 + number, self._debug)

        # self.ops implemented with lazy getter
        self._ops = None

        frames_status = None
        if sanity_checks is True:
            from unit_tests import check_house_keeping_voltages
            from unit_tests import UnexpectedHousekeeping
            from fpesocketconnection import TimeOutError
            try:
                try:
                    frames_status = self.frames_running_status
                    assert frames_status is True or frames_status is False
                except Exception as e:
                    raise type(
                        e
                    )("Could not read if frames are running on the Observatory Simulator... {0}\n"
                      .format(str(e)) +
                      "Are you sure the firmware for the Observatory Simulator is properly installed?"
                      )
                try:
                    version = self.version
                    if self._debug:
                        print version
                except Exception as e:
                    raise type(
                        e
                    )("Could not read Observatory Simulator version... {0}\n".
                      format(str(e)) +
                      "Are you sure you firmware for the Observatory Simulator is properly installed?"
                      )
                if frames_status is not True:
                    try:
                        check_house_keeping_voltages(self)
                    except (UnexpectedHousekeeping, TimeOutError) as e:
                        if auto_load is True:
                            self.load_wrapper()
                        else:
                            raise e

            finally:
                if frames_status is not None:
                    self.frames_running_status = frames_status
Esempio n. 2
0
    def __init__(self,
                 number,
                 debug=False,
                 sanity_checks=True,
                 auto_load=True):
        from fpesocketconnection import FPESocketConnection
        import os

        self.FPE_HOSTNAME = os.environ["FPE_HOSTNAME"] if "FPE_HOSTNAME" in os.environ else DEFAULT_FPE_HOSTNAME

        # First sanity check: ping the observatory simulator
        if not ping(DEFAULT_FPE_HOSTNAME):
            raise Exception("Cannot ping " + DEFAULT_FPE_HOSTNAME)
        self._debug = debug
        self._dir = os.path.dirname(os.path.realpath(__file__))
        self._reset_in_progress = False
        self._loading_wrapper = False
        self.fpe_number = number
        assert self.fpe_number in [1, 2], "FPE number must be either 1 or 2, was {}".format(self.fpe_number)
        self.connection = FPESocketConnection(5554 + number, self.FPE_HOSTNAME, self._debug)

        # self.ops implemented with lazy getter
        self._ops = None

        frames_status = None
        if sanity_checks is True:
            from unit_tests import check_house_keeping_voltages
            from unit_tests import UnexpectedHousekeeping
            from fpesocketconnection import TimeOutError
            try:
                try:
                    frames_status = self.frames_running_status
                    assert frames_status is True or frames_status is False
                except Exception as e:
                    raise type(e)(
                        "Could not read if frames are running on the Observatory Simulator... {0}\n".format(str(e)) +
                        "Are you sure the firmware for the Observatory Simulator is properly installed?")
                try:
                    version = self.version
                    if self._debug:
                        print version
                except Exception as e:
                    raise type(e)("Could not read Observatory Simulator version... {0}\n".format(str(e)) +
                                  "Are you sure you firmware for the Observatory Simulator is properly installed?")
                if frames_status is not True:
                    try:
                        check_house_keeping_voltages(self)
                    except (UnexpectedHousekeeping, TimeOutError) as e:
                        if auto_load is True:
                            self.load_wrapper()
                        else:
                            raise e

            finally:
                if frames_status is not None:
                    self.frames_running_status = frames_status
Esempio n. 3
0
File: fpe.py Progetto: TESSUser/FPE
    def __init__(self, number, FPE_Wrapper_version=None, debug=False, check_hk=True):
        from fpesocketconnection import FPESocketConnection
        from unit_tests import check_house_keeping_voltages
        import os
        import time

        # First sanity check: ping the observatory simulator
        if not self.ping():
            raise Exception("Cannot ping 192.168.100.1")
        self._debug = debug
        self.fpe_number = number
        self.connection = FPESocketConnection(5554 + number, self._debug)

        # Second sanity check: get the observatory simulator version
        try:
           version = self.version
           if self._debug:
              print version
        except Exception as e:
           raise type(e)("Could not read Observatory Simulator version... {0}\n".format(str(e)) + 
                         "Are you sure you firmware for the Observatory Simulator is properly installed?")
           

        self._dir = os.path.dirname(os.path.realpath(__file__))
        # Default memory configuration files
        self._program_file = os.path.join(self._dir, "..", "data", "files", "default_program.fpe")
        self.register_memory = os.path.join(self._dir, "MemFiles", "Reg.bin")

        # Set the House Keeping and Operating Parameters
        self.hsk_byte_array = house_keeping.identity_map
        self.ops = OperatingParameters(self)

        # Load the wrapper.  First check if loading the wrapper is *necessary* by checking reference values on housekeeping channels
        if FPE_Wrapper_version != None:
            try:
                check_house_keeping_voltages(self)
                if self._debug:
                    print "House keeping reports sane values for reference voltages, *NOT* loading wrapper"
            except:
                fpe_wrapper_bin = os.path.join(self._dir, "MemFiles",
                                               "FPE_Wrapper-{version}.bin".format(version=FPE_Wrapper_version))
                self.upload_fpe_wrapper_bin(fpe_wrapper_bin)

        self.upload_register_memory(self.register_memory)
        self.upload_housekeeping_memory(
            binary_files.write_hskmem(self.hsk_byte_array))
        self.ops.send()
        self.load_code()

        time.sleep(.01) # Need to wait for 1/100th of a sec for the box to catch up with us

        # Run sanity checks on the FPE to make sure basic functions are working (if specified)
        if check_hk:
            check_house_keeping_voltages(self)
Esempio n. 4
0
    def __init__(self,
                 number,
                 debug=False,
                 sanity_checks=True):
        from fpesocketconnection import FPESocketConnection
        from unit_tests import check_house_keeping_voltages

        # First sanity check: ping the observatory simulator
        if not ping():
            raise Exception("Cannot ping 192.168.100.1")
        self._debug = debug
        self._dir = os.path.dirname(os.path.realpath(__file__))
        self._reset_in_progress = False
        self.fpe_number = number
        self.connection = FPESocketConnection(5554 + number, self._debug)

        # self.ops implemented with lazy getter
        self._ops = None

        # Second sanity check: check if frames are running, get the observatory simulator version
        original_frames_running_status = None
        if sanity_checks:
            try:
                original_frames_running_status = self.frames_running_status
            except Exception as e:
                raise type(e)(
                    "Could not read if frames are running on the Observatory Simulator... {0}\n".format(str(e)) +
                    "Are you sure the firmware for the Observatory Simulator is properly installed?")
            try:
                version = self.version
                if self._debug:
                    print version
            except Exception as e:
                raise type(e)("Could not read Observatory Simulator version... {0}\n".format(str(e)) +
                              "Are you sure you firmware for the Observatory Simulator is properly installed?")

        # Run sanity checks on the FPE to make sure basic functions are working (if specified)
        if sanity_checks:
            check_house_keeping_voltages(self)
            if original_frames_running_status is not None:
                self.frames_running_status = original_frames_running_status
Esempio n. 5
0
File: fpe.py Progetto: ebokhour/FPE
    def __init__(self, number, FPE_Wrapper_version="6.1.1", debug=False, preload=False, hsk_byte_array=house_keeping.identity_map):
        from fpesocketconnection import FPESocketConnection
        from unit_tests import check_house_keeping_voltages
        import os
        import time
        if not self.ping():
            raise Exception("Cannot ping 192.168.100.1")
        self._debug = debug
        self.fpe_number = number
        self.connection = FPESocketConnection(5554 + number, self._debug)
        self._dir = os.path.dirname(os.path.realpath(__file__))

        # Default memory configuration files
        self.fpe_wrapper_bin = os.path.join(self._dir, "MemFiles",
                                            "FPE_Wrapper-{version}.bin".format(version=FPE_Wrapper_version))

        self._program_file = os.path.join(self._dir, "..", "data", "files", "default_program.fpe")

        # TODO: Sunset this
        self.register_memory = os.path.join(self._dir, "MemFiles", "Reg.bin")

        # Set the House Keeping and Operating Parameters
        self.hsk_byte_array = hsk_byte_array
        self.ops = OperatingParameters(self)

        self._safe_to_load_FPE = None

        if preload:
            self.upload_fpe_wrapper_bin(self.fpe_wrapper_bin)
        self.upload_register_memory(self.register_memory)
        self.upload_housekeeping_memory(
            binary_files.write_hskmem(self.hsk_byte_array))
        self.ops.send()
        self.load_code()

        # Run sanity checks on the FPE to make sure basic functions are working
        time.sleep(.01) # Need to wait for 1/100th of a sec for prince charming
        check_house_keeping_voltages(self)
Esempio n. 6
0
class FPE(object):
    """An object for interacting with an FPE in an Observatory Simulator"""

    def __init__(self,
                 number,
                 debug=False,
                 sanity_checks=True):
        from fpesocketconnection import FPESocketConnection
        from unit_tests import check_house_keeping_voltages

        # First sanity check: ping the observatory simulator
        if not ping():
            raise Exception("Cannot ping 192.168.100.1")
        self._debug = debug
        self._dir = os.path.dirname(os.path.realpath(__file__))
        self._reset_in_progress = False
        self.fpe_number = number
        self.connection = FPESocketConnection(5554 + number, self._debug)

        # self.ops implemented with lazy getter
        self._ops = None

        # Second sanity check: check if frames are running, get the observatory simulator version
        original_frames_running_status = None
        if sanity_checks:
            try:
                original_frames_running_status = self.frames_running_status
            except Exception as e:
                raise type(e)(
                    "Could not read if frames are running on the Observatory Simulator... {0}\n".format(str(e)) +
                    "Are you sure the firmware for the Observatory Simulator is properly installed?")
            try:
                version = self.version
                if self._debug:
                    print version
            except Exception as e:
                raise type(e)("Could not read Observatory Simulator version... {0}\n".format(str(e)) +
                              "Are you sure you firmware for the Observatory Simulator is properly installed?")

        # Run sanity checks on the FPE to make sure basic functions are working (if specified)
        if sanity_checks:
            check_house_keeping_voltages(self)
            if original_frames_running_status is not None:
                self.frames_running_status = original_frames_running_status

    def load_wrapper(self, wrapper_version='6.1t.4'):
        import os.path
        from unit_tests import check_house_keeping_voltages, UnexpectedHousekeeping
        from fpesocketconnection import TimeOutError
        fpe_wrapper_bin = os.path.join(self._dir, "MemFiles",
                                       "FPE_Wrapper-{version}.bin".format(version=wrapper_version))

        assert os.path.isfile(fpe_wrapper_bin), "Wrapper does not exist for version {}".format(wrapper_version)
        status = self.frames_running_status
        try:
            self.frames_running_status = False
            self.cmd_hsk(retries=1)
            check_house_keeping_voltages(self)
            return "House keeping reports sane values for reference voltages," \
                   " *NOT* loading wrapper (tried to load version {})".format(wrapper_version)
        except (UnexpectedHousekeeping, TimeOutError):
            self.cmd_rst(upload=False, sanity_checks=False)
            # assert "Cam FPGA done." in self.cmd_fpga_rst(), "Could not reset the FPGA"
            assert "Resetting Cam FPGA" in self.cmd_fpga_rst(), "Could not reset the FPGA"
            assert self.upload_fpe_wrapper_bin(fpe_wrapper_bin), "Could not load wrapper: {}".format(fpe_wrapper_bin)
            assert self.cmd_rst(upload=True, sanity_checks=False), "Could not reset camera"
            # Set the housekeeping memory to the identity map
            assert self.upload_housekeeping_memory(
                binary_files.write_hskmem(
                    house_keeping.identity_map)), "Could not load house keeping memory: {}".format(house_keeping_memory)
            # Set the operating parameters to their defaults
            assert self.ops.reset_to_defaults(), "Could not send operating parameters"
            check_house_keeping_voltages(self)
            return "Wrapper version {} loaded successfully".format(wrapper_version)
        finally:
            self.frames_running_status = status

    def close(self):
        """Close the fpe object (namely its socket connection)"""
        return self.connection.close()

    def __enter__(self):
        """Enter the python object, used for context management.  See: https://www.python.org/dev/peps/pep-0343/"""
        return self

    def __exit__(self, *_):
        """Exit the python object, used for context management.  See: https://www.python.org/dev/peps/pep-0343/"""
        return self.close()

    def tftp_put(self, file_name, destination):
        """Upload a file to the FPE"""
        from sh import tftp, ErrorReturnCode_1
        import re
        import os.path
        assert os.path.isfile(file_name), "Could not find file for TFTP upload: {}".format(file_name)
        tftp_mode = "mode binary"
        tftp_port = "connect 192.168.100.1 {}".format(68 + self.fpe_number)
        tftp_file = "put {} {}".format(file_name, destination)
        tftp_command = "\n" + tftp_mode + "\n" + tftp_port + "\n" + tftp_file

        status = self.frames_running_status
        try:
            if self._debug:
                print "Running:\ntftp <<EOF\n", \
                    tftp_command, "\n", \
                    "EOF"
            self.frames_running_status = False
            try:
                tftp(_in=tftp_command)
            except ErrorReturnCode_1 as e:
                # tftp *always* fails on OS X because it's awesome
                # so just check that it reports in stdout it sent the thing
                if self._debug:
                    print e
                if not re.match(r'Sent [0-9]+ bytes in [0-9]+\.[0-9]+ seconds',
                                e.stdout):
                    raise e
            # Wait for the fpe to report the load is complete
            self.connection.wait_for_pattern(r'.*Load complete\n\r')
            return True
        finally:
            self.frames_running_status = status

    def cmd_rst(self, upload=True, sanity_checks=True):
        """Reset the camera after running frames"""
        from unit_tests import check_house_keeping_voltages
        if self._reset_in_progress:
            return False
        self._reset_in_progress = True
        self.cmd_stop_frames()
        assert 'FPE Reset complete' in self.connection.send_command(
            'camrst',
            reply_pattern='FPE Reset complete'), "Could not successfully issue camera reset command"
        # Clear cam_control
        self.control_status = 1
        status = self.control_status
        assert status is 0, "camera control status memory could not zeroed, was {}".format(hex(status))
        if sanity_checks or upload:
            register_memory = os.path.join(self._dir, 'MemFiles', 'Reg.bin')
            assert self.upload_register_memory(register_memory), \
                'Could not load register memory: {}'.format(register_memory)
        if sanity_checks:
            check_house_keeping_voltages(self)
        self._reset_in_progress = False
        return True

    def cmd_status(self):
        """Get the camera status"""
        response = self.connection.send_command(
            "cam_status",
            reply_pattern="cam_status = 0x[0-9a-f]+")[13:]
        return int(response, 16)

    def cmd_control(self, val=None):
        """Get the camera control status"""
        response = self.connection.send_command(
            "cam_control" if val is None else "cam_control = {}".format(val),
            reply_pattern="cam_control = 0x[0-9a-f]+")[14:]
        return int(response, 16)

    def cmd_version(self):
        """Get the version of the Observatory Simulator DHU software"""
        import re
        # Frames must be stopped to read version, otherwise the observatory simulator will not respond
        status = self.frames_running_status
        try:
            self.frames_running_status = False
            return \
                re.sub(r'FPE[0-9]>', '',
                       self.connection.send_command(
                           "version",
                           reply_pattern="Observatory Simulator Version .*"))
        finally:
            self.frames_running_status = status

    def cmd_fpga_rst(self):
        """Reset the FPGA so that another wrapper can be uploaded"""
        assert self.cmd_rst(upload=False, sanity_checks=False), "Could not reset camera"
        return self.connection.send_command(
            "cam_fpga_rst",
            # TODO: switch on "Cam FPGA done."
            reply_pattern="Resetting Cam FPGA",
            timeout=3
        )

    def cmd_start_frames(self):
        """Start running frames"""
        if self.frames_running_status is True:
            return "Control status indicates frames are already running"
        return self.connection.send_command(
            "cam_start_frames",
            reply_pattern="(Starting frames...|Frames already enabled)"
        )

    def cmd_stop_frames(self):
        """Stop running frames"""
        if self.frames_running_status is False:
            return "Control status indicates frames are already stopped;" \
                   " however the FPE may not be safely in debug mode"
        assert "Frames Stopped..." in self.connection.send_command(
            "cam_stop_frames",
            reply_pattern="Frames Stopped...")
        self.cmd_rst()
        assert self._reset_in_progress is False, "Reset should no longer be in progress"
        return "Frames have been stopped and the FPE has been placed in debug mode"

    def cmd_hsk(self, retries=None):
        """Get the camera housekeeping data, outputs an array of the housekeeping data"""
        import re
        channels = 128
        # TODO: switch on whether frames have been started and use obsim
        status = self.frames_running_status
        try:
            self.frames_running_status = False
            out = self.connection.send_command(
                "cam_hsk",
                reply_pattern="Hsk\[[0-9]+\] = 0x[0-9a-f]+",
                matches=channels,
                retries=retries
            )
            return [int(n, 16) for n in re.findall('0x[0-9a-f]+', out)]
        finally:
            self.frames_running_status = status

    def cmd_clv(self):
        """Get the clock voltage memory, returns the twelve bit values"""
        import re
        entries = 64
        status = self.frames_running_status
        try:
            self.frames_running_status = False
            out = self.connection.send_command(
                "cam_clv",
                reply_pattern="FpeClv\[[0-9]+\] = 0x[0-9a-f]+",
                matches=entries
            )
            return [(reverse_bytes32(int(n, 16)) >> shift) & mask
                    for n in re.findall('0x[0-9a-f]+', out)
                    for (mask, shift) in [(0x0FFF, 16), (0x00000FFF, 0)]]
        finally:
            self.frames_running_status = status

    def compile_and_load_fpe_program(self, program):
        """Loads the program code using tftp"""
        from ..sequencer_dsl.program import compile_programs
        from ..sequencer_dsl.sequence import compile_sequences
        from ..sequencer_dsl.parse import parse_file
        ast = parse_file(program)
        sequencer_byte_code = binary_files.write_seqmem(compile_sequences(ast))
        program_byte_code = binary_files.write_prgmem(compile_programs(ast))
        return self.upload_sequencer_memory(sequencer_byte_code) and self.upload_program_memory(program_byte_code)

    def capture_frames(self, n):
        """Capture frames"""
        import subprocess
        import os.path
        self.cmd_start_frames()
        proc = subprocess.Popen(
            [os.path.join(self._dir, "..", "fits_capture", "tess_obssim", "tess_obssim"), '-n', str(n)],
            shell=False)
        proc.communicate()
        self.cmd_stop_frames()

    @property
    def house_keeping(self):
        """Return the house keeping as a map"""
        hsk = self.cmd_hsk()
        # Create a dictionary of the analogue outputs
        analogue = house_keeping.hsk_to_analogue_dictionary(hsk)
        # Create array of digital outs
        digital = house_keeping.hsk_to_digital_dictionary(
            [k for i in range(0, 128, 32)
             for j in hsk[17 + i:24 + i]
             for k in house_keeping.unpack_pair(j)])
        return {"analogue": analogue,
                "digital": digital}

    @property
    def analogue_house_keeping_with_units(self):
        hsk = self.cmd_hsk()
        return house_keeping.hsk_to_analogue_dictionary_with_units(hsk)

    @property
    def version(self):
        """Version property for the Observatory Simulator DHU software"""
        return self.cmd_version()

    @property
    def status(self):
        """Get the camera status for the Observatory Simulator for a particular FPE"""
        return self.cmd_status()

    @property
    def control_status(self):
        """Get the camera control status for the Observatory Simulator for a particular FPE"""
        return self.cmd_control()

    @control_status.setter
    def control_status(self, val):
        "Set the camera control status for the Observatority Simulator for a particular FPE"
        self.cmd_control(val)

    @property
    def expected_housekeeping(self):
        """Report the expected values for the housekeeping"""
        from copy import deepcopy
        from ..data.housekeeping_channels import housekeeping_channels
        from ..dhu.unit_tests import voltage_reference_values, temperature_sensor_calibration_values
        expected_values = deepcopy(self.ops.values)
        for k in housekeeping_channels:
            if 'bias' in k:
                expected_values[k] = 0
        for v in voltage_reference_values:
            expected_values[v] = voltage_reference_values[v]
        for v in temperature_sensor_calibration_values:
            expected_values[v] = temperature_sensor_calibration_values[v]
        return expected_values

    @property
    def ops(self):
        if self._ops is None:
            self._ops = OperatingParameters(self)
        return self._ops

    @property
    def frames_running_status(self):
        """Check if frames are being run or not"""
        return (0b10 & self.control_status) == 0b10

    @frames_running_status.setter
    def frames_running_status(self, value):
        """Set if frames are running or not"""
        if value is self.frames_running_status:
            pass
        elif value is True and self.frames_running_status is not True:
            self.cmd_start_frames()
        elif value is False and self.frames_running_status is not False:
            self.cmd_stop_frames()
        else:
            raise Exception("Trying to set frames_running_status to value that is not boolean: {0}".format(value))

    def upload_fpe_wrapper_bin(self, fpe_wrapper_bin):
        """Upload the FPE Wrapper binary file to the FPE"""
        return self.tftp_put(
            fpe_wrapper_bin,
            "bitmem" + str(self.fpe_number))

    def upload_sequencer_memory(self, sequencer_memory):
        """Upload the Sequencer Memory to the FPE"""
        return self.tftp_put(
            sequencer_memory,
            "seqmem" + str(self.fpe_number))

    def upload_register_memory(self, register_memory):
        """Upload the Register Memory to the FPE"""
        return self.tftp_put(
            register_memory,
            "regmem" + str(self.fpe_number))

    def upload_program_memory(self, program_memory):
        """Upload the Program Memory to the FPE"""
        return self.tftp_put(
            program_memory,
            "prgmem" + str(self.fpe_number))

    def upload_operating_parameter_memory(self, operating_parameter_memory):
        """Upload the Operating Parameter Memory to the FPE"""
        return self.tftp_put(
            operating_parameter_memory,
            "clvmem" + str(self.fpe_number))

    def upload_housekeeping_memory(self, hsk_memory):
        """Upload the Operating Parameter Memory to the FPE"""
        return self.tftp_put(
            hsk_memory,
            "hskmem" + str(self.fpe_number))
Esempio n. 7
0
class FPE(object):
    """An object for interacting with an FPE in an Observatory Simulator"""

    def __init__(self,
                 number,
                 debug=False,
                 sanity_checks=True,
                 auto_load=True):
        from fpesocketconnection import FPESocketConnection
        import os

        self.FPE_HOSTNAME = os.environ["FPE_HOSTNAME"] if "FPE_HOSTNAME" in os.environ else DEFAULT_FPE_HOSTNAME

        # First sanity check: ping the observatory simulator
        if not ping(DEFAULT_FPE_HOSTNAME):
            raise Exception("Cannot ping " + DEFAULT_FPE_HOSTNAME)
        self._debug = debug
        self._dir = os.path.dirname(os.path.realpath(__file__))
        self._reset_in_progress = False
        self._loading_wrapper = False
        self.fpe_number = number
        assert self.fpe_number in [1, 2], "FPE number must be either 1 or 2, was {}".format(self.fpe_number)
        self.connection = FPESocketConnection(5554 + number, self.FPE_HOSTNAME, self._debug)

        # self.ops implemented with lazy getter
        self._ops = None

        frames_status = None
        if sanity_checks is True:
            from unit_tests import check_house_keeping_voltages
            from unit_tests import UnexpectedHousekeeping
            from fpesocketconnection import TimeOutError
            try:
                try:
                    frames_status = self.frames_running_status
                    assert frames_status is True or frames_status is False
                except Exception as e:
                    raise type(e)(
                        "Could not read if frames are running on the Observatory Simulator... {0}\n".format(str(e)) +
                        "Are you sure the firmware for the Observatory Simulator is properly installed?")
                try:
                    version = self.version
                    if self._debug:
                        print version
                except Exception as e:
                    raise type(e)("Could not read Observatory Simulator version... {0}\n".format(str(e)) +
                                  "Are you sure you firmware for the Observatory Simulator is properly installed?")
                if frames_status is not True:
                    try:
                        check_house_keeping_voltages(self)
                    except (UnexpectedHousekeeping, TimeOutError) as e:
                        if auto_load is True:
                            self.load_wrapper()
                        else:
                            raise e

            finally:
                if frames_status is not None:
                    self.frames_running_status = frames_status

    def load_wrapper(self,
                     fpe_wrapper_binary=None,
                     wrapper_version='6.2.4',
                     force=False,
                     dhu_reset=False):
        """
        Load an FPGA wrapper.  Checks to see if housekeeping is reporting sane values
        :type wrapper_version: str
        :param fpe_wrapper_binary: A string containing a file name, if None is provided then wrapper_version is used
        :param wrapper_version: A string containing the version of the wrapper to be used, defaults to '6.1t.5'
        :param force: A Boolean, which flags whether the wrapper should be (re)installed even if it is already installed
        :param dhu_reset: A Boolean, which flags whether the DHU should be reset
        :return: A string saying the status of the loaded wrapper
        """
        import os.path
        from unit_tests import check_house_keeping_voltages, UnexpectedHousekeeping
        from fpesocketconnection import TimeOutError
        if self._loading_wrapper is True:
            return "Already in the process of trying to load the wrapper, not proceeding"
        try:
            self._loading_wrapper = True
            frames_status = self.frames_running_status
            if fpe_wrapper_binary is None:
                fpe_wrapper_binary = os.path.join(self._dir, "MemFiles",
                                                  "FPE_Wrapper-{version}.bin".format(version=wrapper_version))
            if not os.path.isfile(fpe_wrapper_binary):
                # Maybe we specified a version instead of a real file? No harm in trying...
                file_name = os.path.join(self._dir, "MemFiles",
                                         "FPE_Wrapper-{version}.bin".format(version=fpe_wrapper_binary))
                if os.path.isfile(file_name):
                    fpe_wrapper_binary = file_name
            assert os.path.isfile(fpe_wrapper_binary), "Wrapper file '{}' does not exist".format(fpe_wrapper_binary)
            if self.frames_running_status is True \
                    and force is not True \
                    and dhu_reset is not True:
                return "Frames are reporting to be running, *NOT* loading wrapper (tried to load '{}')".format(
                    fpe_wrapper_binary)
            if force or dhu_reset:
                raise ForcedWrapperLoad()
            self.frames_running_status = False
            self.cam_hsk(retries=1)
            check_house_keeping_voltages(self)
            return "House keeping reports sane values for reference voltages," \
                   " *NOT* loading wrapper (tried to load {})".format(fpe_wrapper_binary)
        except (ForcedWrapperLoad, UnexpectedHousekeeping, TimeOutError):
            if dhu_reset is True:
                self.dhu_reset()
            self.cam_reset(upload=False, sanity_checks=False)
            self.cam_fpga_rst()
            self.upload_fpe_wrapper_bin(fpe_wrapper_binary)
            # Reset the camera again, which uploads the register memory but doesn't check if housekeeping is sane
            self.cam_reset(upload=True, sanity_checks=False)
            # Set the housekeeping memory to the identity map
            house_keeping_memory = binary_files.write_hskmem(house_keeping.identity_map)
            assert self.upload_housekeeping_memory(house_keeping_memory), \
                "Could not load house keeping memory: {}".format(house_keeping_memory)
            # Reset the camera again, this time checking that housekeeping is reporting sane values
            # self.cam_reset(upload=True, sanity_checks=True)
            # Set the operating parameters to their defaults
            assert self.ops.reset_to_defaults(), "Could not send default operating parameters"
            # Check the house keeping is porting sane values (since we are paranoid)
            check_house_keeping_voltages(self)
            return "Wrapper {} loaded successfully".format(fpe_wrapper_binary)
        finally:
            self.frames_running_status = frames_status
            self._loading_wrapper = False

    def close(self):
        """Close the fpe object (namely its socket connection)"""
        return self.connection.close()

    def __enter__(self):
        """Enter the python object, used for context management.  See: https://www.python.org/dev/peps/pep-0343/"""
        return self

    def __exit__(self, *_):
        """Exit the python object, used for context management.  See: https://www.python.org/dev/peps/pep-0343/"""
        return self.close()

    def tftp_put(self, file_name, destination, timeout=1, retries=8):
        """Upload a file to the FPE"""
        from sh import tftp, ErrorReturnCode_1
        import re
        import os.path
        from time import sleep
        from fpesocketconnection import TimeOut, TimeOutError

        assert os.path.isfile(file_name), "Could not find file for TFTP upload: {}".format(file_name)
        assert self.fpe_number in [1, 2], "FPE number must be either 1 or 2, was {}".format(self.fpe_number)
        tftp_mode = "mode binary"
        tftp_port = "connect {FPE_HOSTNAME} 69".format(FPE_HOSTNAME=DEFAULT_FPE_HOSTNAME)
        tftp_file = "put {} {}{}".format(file_name, destination, "2" if self.fpe_number is 2 else "")
        tftp_command = "\n" + tftp_mode + "\n" + tftp_port + "\n" + tftp_file

        status = self.frames_running_status
        t = None
        sleep_time = .5

        try:
            if self._debug:
                print "Running:\ntftp <<EOF\n", \
                    tftp_command, "\n", \
                    "EOF"
            self.frames_running_status = False
            for trial in range(retries):
                try:
                    with TimeOut(seconds=timeout,
                                 error_message="Timeout on trial {}".format(trial + 1)):
                        try:
                            tftp(_in=tftp_command)
                        except ErrorReturnCode_1 as e:
                            # tftp *always* fails on OS X because it's awesome
                            # so just check that it reports in stdout it sent the thing
                            if self._debug:
                                print e
                            if not re.match(r'Sent [0-9]+ bytes in [0-9]+\.[0-9]+ seconds',
                                            e.stdout):
                                raise e
                        # Wait for the fpe to report the load is complete
                        self.connection.wait_for_pattern(r'.*Load complete\n\r')
                        return True
                except TimeOutError as e:
                    sleep(sleep_time * 2 ** trial)
                    t = e
            raise t
        finally:
            self.frames_running_status = status

    def cam_reset(self, upload=True, sanity_checks=True):
        """Reset the camera after running frames"""
        from unit_tests import check_house_keeping_voltages, UnexpectedHousekeeping
        from fpesocketconnection import TimeOutError
        if self._reset_in_progress:
            return False
        self._reset_in_progress = True
        self.cam_stop_frames()
        assert 'FPE Reset complete' in self.connection.send_command(
            'camrst',
            reply_pattern='FPE Reset complete'), "Could not successfully issue camera reset command"
        # Clear cam_control
        self.control_status = 1
        status = self.control_status
        assert status is 0, "camera control status memory could not zeroed, was 0x{}".format(hex(status))
        if sanity_checks or upload:
            register_memory = os.path.join(self._dir, 'MemFiles', 'Reg.bin')
            assert self.upload_register_memory(register_memory), \
                'Could not load register memory: {}'.format(register_memory)
            # Set the housekeeping memory to the identity map
            house_keeping_memory = binary_files.write_hskmem(house_keeping.identity_map)
            assert self.upload_housekeeping_memory(house_keeping_memory), \
                "Could not load house keeping memory: {}".format(house_keeping_memory)
        if sanity_checks:
            # Try checking the housekeeping memory.  If it's bad, give reloading the wrapper
            try:
                check_house_keeping_voltages(self)
            except (TimeOutError, UnexpectedHousekeeping):
                self._reset_in_progress = False
                self.load_wrapper()
        self._reset_in_progress = False
        return True

    def cam_rst(self, upload=True, sanity_checks=True):
        """Reset the camera after running frames"""
        return self.cam_reset(upload=upload, sanity_checks=sanity_checks)

    def cam_status(self):
        """Get the camera status"""
        response = self.connection.send_command(
            "cam_status",
            reply_pattern="cam_status = 0x[0-9a-f]+")[13:]
        return int(response, 16)

    def cam_control(self, val=None):
        """Get the camera control status"""
        response = self.connection.send_command(
            "cam_control" if val is None else "cam_control = {}".format(val),
            reply_pattern="cam_control = 0x[0-9a-f]+")[14:]
        return int(response, 16)

    def cam_version(self):
        """Get the version of the Observatory Simulator DHU software"""
        import re
        # Frames must be stopped to read version, otherwise the observatory simulator will not respond
        status = self.frames_running_status
        try:
            self.frames_running_status = False
            return \
                re.sub(r'FPE[0-9]>', '',
                       self.connection.send_command(
                           "version",
                           reply_pattern="Observatory Simulator Version .*"))
        finally:
            self.frames_running_status = status

    def cam_fpga_reset(self):
        """Reset the FPGA so that another wrapper can be uploaded"""
        assert self.cam_reset(upload=False, sanity_checks=False), "Could not reset camera"
        return self.connection.send_command(
            "cam_fpga_rst",
            # TODO: switch on "Cam FPGA done."
            reply_pattern="Resetting Cam FPGA",
            timeout=3
        )

    def dhu_reset(self):
        """Reset the DHU; note that this clobbers *both* cameras attached"""
        return self.connection.send_command(
            "dhu_rst",
            reply_pattern="DHU Reset complete",
            timeout=3
        )

    def cam_fpga_rst(self):
        """Reset the FPGA so that another wrapper can be uploaded"""
        return self.cam_fpga_reset()

    def dhu_rst(self):
        """Reset the DHU; note that this clobbers *both* cameras attached"""
        return self.dhu_reset()

    def cam_start_frames(self):
        """Start running frames"""
        if self.frames_running_status is True:
            return "Control status indicates frames are already running"
        return self.connection.send_command(
            "cam_start_frames",
            reply_pattern="(Starting frames...|Frames already enabled)"
        )

    def cam_stop_frames(self):
        """Stop running frames"""
        if self.frames_running_status is False:
            return "Control status indicates frames are already stopped;" \
                   " however the FPE may not be safely in debug mode" \
                   " if cam_stop_frames was issued via the netcat console"
        assert "Frames Stopped..." in self.connection.send_command(
            "cam_stop_frames",
            reply_pattern="Frames Stopped...")
        self.cam_reset()
        assert self._reset_in_progress is False, "Reset should no longer be in progress"
        return "Frames have been stopped and the FPE has been placed in debug mode"

    def cam_hsk(self, retries=None):
        """Get the camera housekeeping data, outputs an array of the housekeeping data"""
        import re
        channels = 128
        # TODO: switch on whether frames have been started and use obsim
        status = self.frames_running_status
        try:
            self.frames_running_status = False
            out = self.connection.send_command(
                "cam_hsk",
                reply_pattern="Hsk\[[0-9]+\] = 0x[0-9a-f]+",
                matches=channels,
                retries=retries
            )
            return [int(n, 16) for n in re.findall('0x[0-9a-f]+', out)]
        finally:
            self.frames_running_status = status

    def cam_clv(self):
        """Get the clock voltage memory, returns the twelve bit values"""
        import re
        entries = 64
        status = self.frames_running_status
        try:
            self.frames_running_status = False
            out = self.connection.send_command(
                "cam_clv",
                reply_pattern="FpeClv\[[0-9]+\] = 0x[0-9a-f]+",
                matches=entries
            )
            return [(reverse_bytes32(int(n, 16)) >> shift) & mask
                    for n in re.findall('0x[0-9a-f]+', out)
                    for (mask, shift) in [(0x0FFF, 16), (0x00000FFF, 0)]]
        finally:
            self.frames_running_status = status

    def compile_and_load_fpe_program(self, program):
        """Loads the program code using tftp"""
        from ..sequencer_dsl.program import compile_programs
        from ..sequencer_dsl.sequence import compile_sequences
        from ..sequencer_dsl.parse import parse_file
        from ..data.data import data_dir
        import os.path
        if not hasattr(program, 'read'):
            data_dir_fpe_program = os.path.join(data_dir, 'sequencer', program)
            if os.path.isfile(data_dir_fpe_program):
                program = data_dir_fpe_program
            else:
                data_dir_fpe_program_fpe = os.path.join(data_dir, 'sequencer', program + ".fpe")
                if os.path.isfile(data_dir_fpe_program_fpe):
                    program = data_dir_fpe_program_fpe
        ast = parse_file(program)
        sequencer_byte_code = binary_files.write_seqmem(compile_sequences(ast))
        program_byte_code = binary_files.write_prgmem(compile_programs(ast))
        return self.upload_sequencer_memory(sequencer_byte_code) and self.upload_program_memory(program_byte_code)

    @property
    def house_keeping(self):
        """Return the house keeping as a map"""
        hsk = self.cam_hsk()
        # Create a dictionary of the analogue outputs
        analogue = house_keeping.hsk_to_analogue_dictionary(hsk)
        # Create array of digital outs; this gets wiped every time we
        # re-upload the register memory so it's not very useful
        digital = [k for i in range(0, 128, 32)
                   for j in hsk[17 + i:24 + i]
                   for k in house_keeping.unpack_pair(j)]
        return {"analogue": analogue,
                "digital": digital}

    @property
    def version(self):
        """Version property for the Observatory Simulator DHU software"""
        return self.cam_version()

    @property
    def status(self):
        """Get the camera status for the Observatory Simulator for a particular FPE"""
        return self.cam_status()

    @property
    def control_status(self):
        """Get the camera control status for the Observatory Simulator for a particular FPE"""
        return self.cam_control()

    @control_status.setter
    def control_status(self, val):
        """Set the camera control status for the Observatory Simulator for a particular FPE"""
        self.cam_control(val)

    @property
    def ops(self):
        if self._ops is None:
            self._ops = OperatingParameters(self)
        return self._ops

    @property
    def frames_running_status(self):
        """Status of whether the Observatory Simulator server is running frames."""
        return (0b10 & self.control_status) == 0b10

    @frames_running_status.setter
    def frames_running_status(self, value):
        """Set if frames are running or not"""
        if value is self.frames_running_status:
            pass
        elif value is True and self.frames_running_status is not True:
            self.cam_start_frames()
        elif value is False and self.frames_running_status is not False:
            self.cam_stop_frames()
        else:
            raise Exception("Trying to set frames_running_status to value that is not boolean: {0}".format(value))

    def upload_fpe_wrapper_bin(self, fpe_wrapper_bin):
        """Upload the FPE Wrapper binary file to the FPE
        :param fpe_wrapper_bin: Name of FPE wrapper binary to upload
        """
        return self.tftp_put(
            fpe_wrapper_bin,
            "bitmem" + str(self.fpe_number),
            timeout=16)

    def upload_sequencer_memory(self, sequencer_memory):
        """Upload the Sequencer Memory to the FPE"""
        return self.tftp_put(
            sequencer_memory,
            "seqmem" + str(self.fpe_number))

    def upload_register_memory(self, register_memory):
        """Upload the Register Memory to the FPE"""
        return self.tftp_put(
            register_memory,
            "regmem" + str(self.fpe_number))

    def upload_program_memory(self, program_memory):
        """Upload the Program Memory to the FPE"""
        return self.tftp_put(
            program_memory,
            "prgmem" + str(self.fpe_number))

    def upload_operating_parameter_memory(self, operating_parameter_memory):
        """Upload the Operating Parameter Memory to the FPE"""
        return self.tftp_put(
            operating_parameter_memory,
            "clvmem" + str(self.fpe_number))

    def upload_housekeeping_memory(self, hsk_memory):
        """Upload the Operating Parameter Memory to the FPE"""
        return self.tftp_put(
            hsk_memory,
            "hskmem" + str(self.fpe_number))
Esempio n. 8
0
File: fpe.py Progetto: ebokhour/FPE
class FPE(object):
    """An object for interacting with an FPE in an Observatory Simulator"""

    def __init__(self, number, FPE_Wrapper_version="6.1.1", debug=False, preload=False, hsk_byte_array=house_keeping.identity_map):
        from fpesocketconnection import FPESocketConnection
        from unit_tests import check_house_keeping_voltages
        import os
        import time
        if not self.ping():
            raise Exception("Cannot ping 192.168.100.1")
        self._debug = debug
        self.fpe_number = number
        self.connection = FPESocketConnection(5554 + number, self._debug)
        self._dir = os.path.dirname(os.path.realpath(__file__))

        # Default memory configuration files
        self.fpe_wrapper_bin = os.path.join(self._dir, "MemFiles",
                                            "FPE_Wrapper-{version}.bin".format(version=FPE_Wrapper_version))

        self._program_file = os.path.join(self._dir, "..", "data", "files", "default_program.fpe")

        # TODO: Sunset this
        self.register_memory = os.path.join(self._dir, "MemFiles", "Reg.bin")

        # Set the House Keeping and Operating Parameters
        self.hsk_byte_array = hsk_byte_array
        self.ops = OperatingParameters(self)

        self._safe_to_load_FPE = None

        if preload:
            self.upload_fpe_wrapper_bin(self.fpe_wrapper_bin)
        self.upload_register_memory(self.register_memory)
        self.upload_housekeeping_memory(
            binary_files.write_hskmem(self.hsk_byte_array))
        self.ops.send()
        self.load_code()

        # Run sanity checks on the FPE to make sure basic functions are working
        time.sleep(.01) # Need to wait for 1/100th of a sec for prince charming
        check_house_keeping_voltages(self)

    def tftp_put(self, file_name, destination):
        """Upload a file to the FPE"""
        from sh import tftp, ErrorReturnCode_1
        import re
        tftp_mode = "mode binary"
        tftp_port = "connect 192.168.100.1 {}".format(68 + self.fpe_number)
        tftp_file = "put {} {}".format(file_name, destination)
        tftp_command = tftp_mode + "\n" + tftp_port + "\n" + tftp_file

        if self._debug:
            print "Running:\ntftp <<EOF\n", \
                tftp_command, "\n", \
                "EOF"
        try:
            tftp(_in=tftp_command)
        except ErrorReturnCode_1 as e:
            # tftp *always* fails because it's awesome
            # so just check that it reports in stdout it sent the thing
            if self._debug:
                print e
            if not re.match(r'Sent [0-9]+ bytes in [0-9]+\.[0-9]+ seconds',
                            e.stdout):
                raise e

        # Wait for the fpe to report the load is complete
        self.connection.wait_for_pattern(r'.*Load complete\n\r')

    @staticmethod
    def ping():
        """Ping the Observation Simulator to make sure it is alive"""
        from sh import ping
        out = ping('-c', '1', '-t', '1', '192.168.100.1')
        return '1 packets transmitted, 1 packets received' in str(out)

    def cmd_camrst(self):
        """Reset the camera after running frames"""
        # Is it just me, or shouldn't this be "Reset" not "Rest"?
        return self.connection.send_command("camrst", reply_pattern='FPE Rest complete')

    def cmd_cam_status(self):
        """Get the camera status"""
        response = self.connection.send_command(
            "cam_status",
            reply_pattern="cam_status = 0x[0-9a-f]+")[13:]
        val = int(response, 16)
        return val

    def cmd_version(self):
        """Get the version of the Observatory Simulator DHU software"""
        import re
        return \
            re.sub(r'FPE[0-9]>', '',
                   self.connection.send_command(
                       "version",
                       reply_pattern="Observatory Simulator Version .*"))

    def cmd_start_frames(self):
        return self.connection.send_command(
            "cam_start_frames",
            reply_pattern="(Starting frames...|Frames already enabled)"
        )

    def cmd_stop_frames(self):
        return self.connection.send_command(
            "cam_stop_frames",
            reply_pattern="Frames Stopped..."
        )

    def cmd_cam_hsk(self):
        """Get the camera housekeeping data, outputs an array of the housekeeping data"""
        import re
        channels = 128
        # TODO: switch on whether frames have been started
        out = self.connection.send_command(
            "cam_hsk",
            reply_pattern="Hsk\[[0-9]+\] = 0x[0-9a-f]+",
            matches=channels
        )
        return [int(n, 16) for n in re.findall('0x[0-9a-f]+', out)]

    def load_code(self):
        """Loads the program code using tftp"""
        self.upload_sequencer_memory(
            binary_files.write_seqmem(self.sequences_byte_array))
        self.upload_program_memory(
            binary_files.write_prgmem(self.programs_byte_array))

    def capture_frames(self, n):
        """Capture frames"""
        import subprocess
        import os.path
        self.cmd_start_frames()
        proc = subprocess.Popen(
            [os.path.join(self._dir, "..", "fits_capture", "tess_obssim", "tess_obssim"), '-n', str(n)],
            shell=False)
        proc.communicate()
        self.cmd_stop_frames()

    @property
    def sequences_byte_array(self):
        """A byte array representing the sequences set by the program code"""
        from ..sequencer_dsl.sequence import compile_sequences
        return compile_sequences(self._ast)

    @property
    def programs_byte_array(self):
        """A byte array representing the programs set by the program code"""
        from ..sequencer_dsl.program import compile_programs
        return compile_programs(self._ast)

    @property
    def _ast(self):
        from ..sequencer_dsl.parse import parse_file
        return parse_file(self._program_file)

    @property
    def code(self):
        """The program code"""
        with open(self._program_file) as f:
            return "// File: {filename}\n\n{code}".format(
                file=self._program_file,
                code=f.read())

    @property
    def house_keeping(self):
        hsk = self.cmd_cam_hsk()
        # Create a dictionary of the analogue outputs
        analogue = house_keeping.hsk_to_analogue_dictionary(hsk)
        # Create array of digital outs
        digital = [k for i in range(0, 128, 32)
                   for j in hsk[17 + i:24 + i]
                   for k in house_keeping.unpack_pair(j)]
        return {"analogue": analogue,
                "digital": digital}

    @property
    def parameters(self):
        """The parameters set by the program code"""
        return self._ast["parameters"]

    @property
    def hold(self):
        """The hold instruction set by the program code"""
        return self._ast["hold"]

    @property
    def sequences(self):
        """The sequences set by the program code"""
        return self._ast["sequences"]

    @property
    def defaults(self):
        """The sequencer default values set by the program code"""
        return self._ast["defaults"]

    @property
    def version(self):
        """Version property for the Observatory Simulator DHU software"""
        return self.cmd_version()

    @property
    def cam_status(self):
        """Get the camera status for the Observatory Simulator for a particular FPE"""
        return self.cmd_cam_status()

    def upload_fpe_wrapper_bin(self, fpe_wrapper_bin):
        """Upload the FPE Wrapper binary file to the FPE"""
        return self.tftp_put(
            fpe_wrapper_bin,
            "bitmem")

    def upload_sequencer_memory(self, sequencer_memory):
        """Upload the Sequencer Memory to the FPE"""
        # self.camrst()
        return self.tftp_put(
            sequencer_memory,
            "seqmem")

    def upload_register_memory(self, register_memory):
        """Upload the Register Memory to the FPE"""
        return self.tftp_put(
            register_memory,
            "regmem")

    def upload_program_memory(self, program_memory):
        """Upload the Program Memory to the FPE"""
        return self.tftp_put(
            program_memory,
            "prgmem")

    def upload_operating_parameter_memory(self, operating_parameter_memory):
        """Upload the Operating Parameter Memory to the FPE"""
        return self.tftp_put(
            operating_parameter_memory,
            "clvmem")

    def upload_housekeeping_memory(self, hsk_memory):
        """Upload the Operating Parameter Memory to the FPE"""
        return self.tftp_put(
            hsk_memory,
            "hskmem")
Esempio n. 9
0
class FPE(object):
    """An object for interacting with an FPE in an Observatory Simulator"""
    def __init__(self,
                 number,
                 debug=False,
                 sanity_checks=True,
                 auto_load=True):
        from fpesocketconnection import FPESocketConnection

        # First sanity check: ping the observatory simulator
        if not ping():
            raise Exception("Cannot ping 192.168.100.1")
        self._debug = debug
        self._dir = os.path.dirname(os.path.realpath(__file__))
        self._reset_in_progress = False
        self._loading_wrapper = False
        self.fpe_number = number
        self.connection = FPESocketConnection(5554 + number, self._debug)

        # self.ops implemented with lazy getter
        self._ops = None

        frames_status = None
        if sanity_checks is True:
            from unit_tests import check_house_keeping_voltages
            from unit_tests import UnexpectedHousekeeping
            from fpesocketconnection import TimeOutError
            try:
                try:
                    frames_status = self.frames_running_status
                    assert frames_status is True or frames_status is False
                except Exception as e:
                    raise type(
                        e
                    )("Could not read if frames are running on the Observatory Simulator... {0}\n"
                      .format(str(e)) +
                      "Are you sure the firmware for the Observatory Simulator is properly installed?"
                      )
                try:
                    version = self.version
                    if self._debug:
                        print version
                except Exception as e:
                    raise type(
                        e
                    )("Could not read Observatory Simulator version... {0}\n".
                      format(str(e)) +
                      "Are you sure you firmware for the Observatory Simulator is properly installed?"
                      )
                if frames_status is not True:
                    try:
                        check_house_keeping_voltages(self)
                    except (UnexpectedHousekeeping, TimeOutError) as e:
                        if auto_load is True:
                            self.load_wrapper()
                        else:
                            raise e

            finally:
                if frames_status is not None:
                    self.frames_running_status = frames_status

    def load_wrapper(self,
                     fpe_wrapper_binary=None,
                     wrapper_version='6.2.3',
                     force=False,
                     dhu_reset=False):
        """
        Load an FPGA wrapper.  Checks to see if housekeeping is reporting sane values
        :type wrapper_version: str
        :param fpe_wrapper_binary: A string containing a file name, if None is provided then wrapper_version is used
        :param wrapper_version: A string containing the version of the wrapper to be used, defaults to '6.1t.5'
        :param force: A Boolean, which flags whether the wrapper should be (re)installed even if it is already installed
        :param dhu_reset: A Boolean, which flags whether the DHU should be reset
        :return: A string saying the status of the loaded wrapper
        """
        import os.path
        from unit_tests import check_house_keeping_voltages, UnexpectedHousekeeping
        from fpesocketconnection import TimeOutError
        if self._loading_wrapper is True:
            return "Already in the process of trying to load the wrapper, not proceeding"
        try:
            self._loading_wrapper = True
            frames_status = self.frames_running_status
            if fpe_wrapper_binary is None:
                fpe_wrapper_binary = os.path.join(
                    self._dir, "MemFiles", "FPE_Wrapper-{version}.bin".format(
                        version=wrapper_version))
            if not os.path.isfile(fpe_wrapper_binary):
                # Maybe we specified a version instead of a real file? No harm in trying...
                file_name = os.path.join(
                    self._dir, "MemFiles", "FPE_Wrapper-{version}.bin".format(
                        version=fpe_wrapper_binary))
                if os.path.isfile(file_name):
                    fpe_wrapper_binary = file_name
            assert os.path.isfile(
                fpe_wrapper_binary), "Wrapper file '{}' does not exist".format(
                    fpe_wrapper_binary)
            if self.frames_running_status is True \
                    and force is not True \
                    and dhu_reset is not True:
                return "Frames are reporting to be running, *NOT* loading wrapper (tried to load '{}')".format(
                    fpe_wrapper_binary)
            if force or dhu_reset:
                raise ForcedWrapperLoad()
            self.frames_running_status = False
            self.cam_hsk(retries=1)
            check_house_keeping_voltages(self)
            return "House keeping reports sane values for reference voltages," \
                   " *NOT* loading wrapper (tried to load {})".format(fpe_wrapper_binary)
        except (ForcedWrapperLoad, UnexpectedHousekeeping, TimeOutError):
            if dhu_reset is True:
                self.dhu_reset()
            self.cam_reset(upload=False, sanity_checks=False)
            self.cam_fpga_rst()
            self.upload_fpe_wrapper_bin(fpe_wrapper_binary)
            # Reset the camera again, which uploads the register memory but doesn't check if housekeeping is sane
            self.cam_reset(upload=True, sanity_checks=False)
            # Set the housekeeping memory to the identity map
            house_keeping_memory = binary_files.write_hskmem(
                house_keeping.identity_map)
            assert self.upload_housekeeping_memory(house_keeping_memory), \
                "Could not load house keeping memory: {}".format(house_keeping_memory)
            # Reset the camera again, this time checking that housekeeping is reporting sane values
            # self.cam_reset(upload=True, sanity_checks=True)
            # Set the operating parameters to their defaults
            assert self.ops.reset_to_defaults(
            ), "Could not send default operating parameters"
            # Check the house keeping is porting sane values (since we are paranoid)
            check_house_keeping_voltages(self)
            return "Wrapper {} loaded successfully".format(fpe_wrapper_binary)
        finally:
            self.frames_running_status = frames_status
            self._loading_wrapper = False

    def close(self):
        """Close the fpe object (namely its socket connection)"""
        return self.connection.close()

    def __enter__(self):
        """Enter the python object, used for context management.  See: https://www.python.org/dev/peps/pep-0343/"""
        return self

    def __exit__(self, *_):
        """Exit the python object, used for context management.  See: https://www.python.org/dev/peps/pep-0343/"""
        return self.close()

    def tftp_put(self, file_name, destination):
        """Upload a file to the FPE"""
        from sh import tftp, ErrorReturnCode_1
        import re
        import os.path
        assert os.path.isfile(
            file_name), "Could not find file for TFTP upload: {}".format(
                file_name)
        tftp_mode = "mode binary"
        tftp_port = "connect 192.168.100.1 {}".format(68 + self.fpe_number)
        tftp_file = "put {} {}".format(file_name, destination)
        tftp_command = "\n" + tftp_mode + "\n" + tftp_port + "\n" + tftp_file

        status = self.frames_running_status
        try:
            if self._debug:
                print "Running:\ntftp <<EOF\n", \
                    tftp_command, "\n", \
                    "EOF"
            self.frames_running_status = False
            try:
                tftp(_in=tftp_command)
            except ErrorReturnCode_1 as e:
                # tftp *always* fails on OS X because it's awesome
                # so just check that it reports in stdout it sent the thing
                if self._debug:
                    print e
                if not re.match(r'Sent [0-9]+ bytes in [0-9]+\.[0-9]+ seconds',
                                e.stdout):
                    raise e
            # Wait for the fpe to report the load is complete
            self.connection.wait_for_pattern(r'.*Load complete\n\r')
            return True
        finally:
            self.frames_running_status = status

    def cam_reset(self, upload=True, sanity_checks=True):
        """Reset the camera after running frames"""
        from unit_tests import check_house_keeping_voltages
        if self._reset_in_progress:
            return False
        self._reset_in_progress = True
        self.cam_stop_frames()
        assert 'FPE Reset complete' in self.connection.send_command(
            'camrst', reply_pattern='FPE Reset complete'
        ), "Could not successfully issue camera reset command"
        # Clear cam_control
        self.control_status = 1
        status = self.control_status
        assert status is 0, "camera control status memory could not zeroed, was 0x{}".format(
            hex(status))
        if sanity_checks or upload:
            register_memory = os.path.join(self._dir, 'MemFiles', 'Reg.bin')
            assert self.upload_register_memory(register_memory), \
                'Could not load register memory: {}'.format(register_memory)
        if sanity_checks:
            check_house_keeping_voltages(self)
        self._reset_in_progress = False
        return True

    def cam_rst(self, upload=True, sanity_checks=True):
        """Reset the camera after running frames"""
        return self.cam_reset(upload=upload, sanity_checks=sanity_checks)

    def cam_status(self):
        """Get the camera status"""
        response = self.connection.send_command(
            "cam_status", reply_pattern="cam_status = 0x[0-9a-f]+")[13:]
        return int(response, 16)

    def cam_control(self, val=None):
        """Get the camera control status"""
        response = self.connection.send_command(
            "cam_control" if val is None else "cam_control = {}".format(val),
            reply_pattern="cam_control = 0x[0-9a-f]+")[14:]
        return int(response, 16)

    def cam_version(self):
        """Get the version of the Observatory Simulator DHU software"""
        import re
        # Frames must be stopped to read version, otherwise the observatory simulator will not respond
        status = self.frames_running_status
        try:
            self.frames_running_status = False
            return \
                re.sub(r'FPE[0-9]>', '',
                       self.connection.send_command(
                           "version",
                           reply_pattern="Observatory Simulator Version .*"))
        finally:
            self.frames_running_status = status

    def cam_fpga_reset(self):
        """Reset the FPGA so that another wrapper can be uploaded"""
        assert self.cam_reset(upload=False,
                              sanity_checks=False), "Could not reset camera"
        return self.connection.send_command(
            "cam_fpga_rst",
            # TODO: switch on "Cam FPGA done."
            reply_pattern="Resetting Cam FPGA",
            timeout=3)

    def dhu_reset(self):
        """Reset the DHU; note that this clobbers *both* cameras attached"""
        return self.connection.send_command("dhu_rst",
                                            reply_pattern="DHU Reset complete",
                                            timeout=3)

    def cam_fpga_rst(self):
        """Reset the FPGA so that another wrapper can be uploaded"""
        return self.cam_fpga_reset()

    def dhu_rst(self):
        """Reset the DHU; note that this clobbers *both* cameras attached"""
        return self.dhu_reset()

    def cam_start_frames(self):
        """Start running frames"""
        if self.frames_running_status is True:
            return "Control status indicates frames are already running"
        return self.connection.send_command(
            "cam_start_frames",
            reply_pattern="(Starting frames...|Frames already enabled)")

    def cam_stop_frames(self):
        """Stop running frames"""
        if self.frames_running_status is False:
            return "Control status indicates frames are already stopped;" \
                   " however the FPE may not be safely in debug mode" \
                   " if cam_stop_frames was issued via the netcat console"
        assert "Frames Stopped..." in self.connection.send_command(
            "cam_stop_frames", reply_pattern="Frames Stopped...")
        self.cam_reset()
        assert self._reset_in_progress is False, "Reset should no longer be in progress"
        return "Frames have been stopped and the FPE has been placed in debug mode"

    def cam_hsk(self, retries=None):
        """Get the camera housekeeping data, outputs an array of the housekeeping data"""
        import re
        channels = 128
        # TODO: switch on whether frames have been started and use obsim
        status = self.frames_running_status
        try:
            self.frames_running_status = False
            out = self.connection.send_command(
                "cam_hsk",
                reply_pattern="Hsk\[[0-9]+\] = 0x[0-9a-f]+",
                matches=channels,
                retries=retries)
            return [int(n, 16) for n in re.findall('0x[0-9a-f]+', out)]
        finally:
            self.frames_running_status = status

    def cam_clv(self):
        """Get the clock voltage memory, returns the twelve bit values"""
        import re
        entries = 64
        status = self.frames_running_status
        try:
            self.frames_running_status = False
            out = self.connection.send_command(
                "cam_clv",
                reply_pattern="FpeClv\[[0-9]+\] = 0x[0-9a-f]+",
                matches=entries)
            return [(reverse_bytes32(int(n, 16)) >> shift) & mask
                    for n in re.findall('0x[0-9a-f]+', out)
                    for (mask, shift) in [(0x0FFF, 16), (0x00000FFF, 0)]]
        finally:
            self.frames_running_status = status

    def compile_and_load_fpe_program(self, program):
        """Loads the program code using tftp"""
        from ..sequencer_dsl.program import compile_programs
        from ..sequencer_dsl.sequence import compile_sequences
        from ..sequencer_dsl.parse import parse_file
        ast = parse_file(program)
        sequencer_byte_code = binary_files.write_seqmem(compile_sequences(ast))
        program_byte_code = binary_files.write_prgmem(compile_programs(ast))
        return self.upload_sequencer_memory(
            sequencer_byte_code) and self.upload_program_memory(
                program_byte_code)

    @property
    def house_keeping(self):
        """Return the house keeping as a map"""
        hsk = self.cam_hsk()
        # Create a dictionary of the analogue outputs
        analogue = house_keeping.hsk_to_analogue_dictionary(hsk)
        # Create array of digital outs; this gets wiped every time we
        # re-upload the register memory so it's not very useful
        digital = [
            k for i in range(0, 128, 32) for j in hsk[17 + i:24 + i]
            for k in house_keeping.unpack_pair(j)
        ]
        return {"analogue": analogue, "digital": digital}

    @property
    def analogue_house_keeping_with_units(self):
        hsk = self.cam_hsk()
        return house_keeping.hsk_to_analogue_dictionary_with_units(hsk)

    @property
    def version(self):
        """Version property for the Observatory Simulator DHU software"""
        return self.cam_version()

    @property
    def status(self):
        """Get the camera status for the Observatory Simulator for a particular FPE"""
        return self.cam_status()

    @property
    def control_status(self):
        """Get the camera control status for the Observatory Simulator for a particular FPE"""
        return self.cam_control()

    @control_status.setter
    def control_status(self, val):
        """Set the camera control status for the Observatory Simulator for a particular FPE"""
        self.cam_control(val)

    @property
    def expected_housekeeping(self):
        """Report the expected values for the housekeeping"""
        from copy import deepcopy
        from ..data.housekeeping_channels import housekeeping_channels
        from ..dhu.unit_tests import voltage_reference_values, temperature_sensor_calibration_values
        expected_values = deepcopy(self.ops.values)
        for k in housekeeping_channels:
            if 'bias' in k:
                expected_values[k] = 0
        for v in voltage_reference_values:
            expected_values[v] = voltage_reference_values[v]
        for v in temperature_sensor_calibration_values:
            expected_values[v] = temperature_sensor_calibration_values[v]
        return expected_values

    @property
    def ops(self):
        if self._ops is None:
            self._ops = OperatingParameters(self)
        return self._ops

    @property
    def frames_running_status(self):
        """Status of whether the Observatory Simulator server is running frames."""
        return (0b10 & self.control_status) == 0b10

    @frames_running_status.setter
    def frames_running_status(self, value):
        """Set if frames are running or not"""
        if value is self.frames_running_status:
            pass
        elif value is True and self.frames_running_status is not True:
            self.cam_start_frames()
        elif value is False and self.frames_running_status is not False:
            self.cam_stop_frames()
        else:
            raise Exception(
                "Trying to set frames_running_status to value that is not boolean: {0}"
                .format(value))

    def upload_fpe_wrapper_bin(self, fpe_wrapper_bin):
        """Upload the FPE Wrapper binary file to the FPE
        :param fpe_wrapper_bin: Name of FPE wrapper binary to upload
        """
        return self.tftp_put(fpe_wrapper_bin, "bitmem" + str(self.fpe_number))

    def upload_sequencer_memory(self, sequencer_memory):
        """Upload the Sequencer Memory to the FPE"""
        return self.tftp_put(sequencer_memory, "seqmem" + str(self.fpe_number))

    def upload_register_memory(self, register_memory):
        """Upload the Register Memory to the FPE"""
        return self.tftp_put(register_memory, "regmem" + str(self.fpe_number))

    def upload_program_memory(self, program_memory):
        """Upload the Program Memory to the FPE"""
        return self.tftp_put(program_memory, "prgmem" + str(self.fpe_number))

    def upload_operating_parameter_memory(self, operating_parameter_memory):
        """Upload the Operating Parameter Memory to the FPE"""
        return self.tftp_put(operating_parameter_memory,
                             "clvmem" + str(self.fpe_number))

    def upload_housekeeping_memory(self, hsk_memory):
        """Upload the Operating Parameter Memory to the FPE"""
        return self.tftp_put(hsk_memory, "hskmem" + str(self.fpe_number))