Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/cmake-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -188,3 +188,13 @@ jobs:
EOF
cmake -G Ninja -S . -B build -DCAMERAD_SOURCE_DIR="$GITHUB_WORKSPACE"
cmake --build build -j"$(nproc)"

# The reference instrument built the way an instrument repository will,
# so a change that breaks a consumer fails here rather than downstream
- name: Build the reference instrument
shell: bash
run: |
cmake -G Ninja -S examples/reference_instrument -B "$RUNNER_TEMP/reference" \
-DFETCHCONTENT_SOURCE_DIR_CAMERA_INTERFACE="$GITHUB_WORKSPACE"
cmake --build "$RUNNER_TEMP/reference" -j"$(nproc)"
test -x "$RUNNER_TEMP/reference/camerad"
56 changes: 56 additions & 0 deletions .github/workflows/instrument-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Instrument CI

# Called by an instrument repository, which owns its own build but should not
# own a copy of this. Runs in the caller's context, so checkout takes the
# instrument, and the core arrives through its CMake fetch.
#
# jobs:
# build:
# uses: CaltechOpticalObservatories/camera-interface/.github/workflows/instrument-ci.yml@main

on:
workflow_call:
inputs:
cmake-args:
description: "Extra arguments for the configure step"
required: false
type: string
default: ""

jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
- name: Checkout the instrument
uses: actions/checkout@v4

- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
build-essential cmake ninja-build \
libccfits-dev libcfitsio-dev libcurl4-openssl-dev \
nlohmann-json3-dev libzmq3-dev \
libboost-thread-dev libboost-chrono-dev

# Not packaged for common distros, so every consumer builds it
- name: Build and install zmqpp
run: |
git clone --depth 1 https://github.com/zeromq/zmqpp.git /tmp/zmqpp
cmake -S /tmp/zmqpp -B /tmp/zmqpp/build
cmake --build /tmp/zmqpp/build -j"$(nproc)"
sudo cmake --install /tmp/zmqpp/build
sudo ldconfig

- name: Build
run: |
cmake -G Ninja -S . -B build ${{ inputs.cmake-args }}
cmake --build build -j"$(nproc)"

# camerad exits 0 with no config and prints nothing, so there is nothing
# here to assert beyond the daemon having been produced. An instrument
# wanting more adds its own job against the emulator.
- name: Check the daemon was built
run: test -x build/camerad
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ endif()
option(CAMERAD_BUILD_TOOLS "Build the emulator, listener, socksend and shm_reader" ${PROJECT_IS_TOP_LEVEL})
option(CAMERAD_INSTALL "Generate install rules" ${PROJECT_IS_TOP_LEVEL})

# An instrument builds its own daemon from camerad_server, so the core's would
# only collide with it
option(CAMERAD_BUILD_DAEMON "Build the camerad executable" ${PROJECT_IS_TOP_LEVEL})

add_subdirectory(${PROJECT_BASE_DIR}/utils)
add_subdirectory(${PROJECT_BASE_DIR}/common)
add_subdirectory(${PROJECT_BASE_DIR}/camerad)
Expand Down
20 changes: 13 additions & 7 deletions camerad/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -194,17 +194,14 @@ find_package(Threads)
# ----------------------------------------------------------------------------
# build the camera daemon
# ----------------------------------------------------------------------------
add_executable(camerad
# The server and its whole link interface, so a project consuming this one
# builds its own daemon by linking this and supplying Camera::Interface::create()
add_library(camerad_server STATIC
${CAMERAD_DIR}/camerad.cpp
${CAMERAD_DIR}/camera_server.cpp
${INSTRUMENT_REGISTRATION}
${INSTRUMENT_OBJECTS}
)

# ----------------------------------------------------------------------------
# link everything
# ----------------------------------------------------------------------------
target_link_libraries(camerad
target_link_libraries(camerad_server PUBLIC
network
utilities
logentry
Expand All @@ -220,6 +217,15 @@ target_link_libraries(camerad
${ZMQ_LIB}
)

if (CAMERAD_BUILD_DAEMON)
add_executable(camerad
${INSTRUMENT_REGISTRATION}
${INSTRUMENT_OBJECTS}
)

target_link_libraries(camerad camerad_server)
endif()

if (CAMERAD_INSTALL)
install(TARGETS camerad RUNTIME DESTINATION ${CAMERAD_INSTALL_BINDIR})
endif()
Expand Down
33 changes: 33 additions & 0 deletions examples/reference_instrument/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# ----------------------------------------------------------------------------
# @file examples/reference_instrument/CMakeLists.txt
# @brief how an instrument repository consumes camerad
#
# Copy this into a new instrument repository as its top-level CMakeLists.txt.
# Nothing here reaches into the core: it fetches camerad, links camerad_server,
# and supplies Camera::Interface::create().
# ----------------------------------------------------------------------------

cmake_minimum_required(VERSION 3.24)

project(reference_instrument CXX)

include(FetchContent)

# A consumer pins a tag here. The core's own CI points this at its working tree
# with -DFETCHCONTENT_SOURCE_DIR_CAMERA_INTERFACE, so a change that breaks a
# consumer fails there rather than in every instrument later.
FetchContent_Declare(camera_interface
GIT_REPOSITORY https://github.com/CaltechOpticalObservatories/camera-interface
GIT_TAG main
)

set(CONTROLLER archon CACHE STRING "Controller this instrument uses")

FetchContent_MakeAvailable(camera_interface)

add_executable(camerad
reference_instrument.cpp
reference_interface_factory.cpp
)

target_link_libraries(camerad PRIVATE camerad_server)
21 changes: 21 additions & 0 deletions examples/reference_instrument/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Reference instrument

The smallest thing that builds against camerad, and the starting point for a new
instrument repository.

An instrument owns its interface class, the translation unit defining
`Camera::Interface::create()`, its ACF and cfg, and its own `CMakeLists.txt`. It
fetches camerad and links `camerad_server`, which carries the daemon and its
whole link interface, so the instrument supplies only what is specific to it.

```bash
cmake -S . -B build
cmake --build build
```

That produces a `camerad` running this instrument. Copy the four files into a
new repository and pin `GIT_TAG` to a release of the core.

The core's own CI builds this with
`-DFETCHCONTENT_SOURCE_DIR_CAMERA_INTERFACE` pointed at its working tree, so a
change that breaks a consumer fails there rather than in every instrument later.
14 changes: 14 additions & 0 deletions examples/reference_instrument/reference_instrument.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* @file reference_instrument.cpp
* @brief the smallest instrument that builds against camerad
*/

#include "reference_instrument.h"

namespace Camera {

void ReferenceInstrument::configure_instrument() {
logwrite("Camera::ReferenceInstrument::configure_instrument", "");
}

}
17 changes: 17 additions & 0 deletions examples/reference_instrument/reference_instrument.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* @file reference_instrument.h
* @brief the smallest instrument that builds against camerad
*/
#pragma once

#include "archon_interface.h"

namespace Camera {

/// Instrument adding nothing to the Archon interface, as a starting point
class ReferenceInstrument : public ArchonInterface {
public:
void configure_instrument() override;
};

}
18 changes: 18 additions & 0 deletions examples/reference_instrument/reference_interface_factory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* @file reference_interface_factory.cpp
* @brief the seam between camerad and an instrument
*
* camerad calls this to obtain its interface, so every instrument supplies one
* translation unit defining it.
*/

#include "reference_instrument.h"
#include "camera_interface.h"

namespace Camera {

std::unique_ptr<Interface> Interface::create() {
return std::make_unique<ReferenceInstrument>();
}

}
Loading