diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..2648c1f --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,28 @@ +name: CI + +on: + push: + pull_request: + schedule: + - cron: '0 0 * * 0' # Runs every Sunday at midnight + +jobs: + build: + name: Build and Test + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: sudo apt-get update && sudo apt-get install -y build-essential + + - name: Run make all and wait for Sent + run: make wait-for-sent + working-directory: ./docker + + - name: Archive all.txt logs + if: always() + uses: actions/upload-artifact@v4 + with: + path: /quickstart/docker/all.txt diff --git a/.gitignore b/.gitignore index 9ed18f9..4456b6b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,9 @@ # VSCode .vscode/settings.json -/.history/ \ No newline at end of file + +# Docker logs for CI +docker/all.log + +# Local un-stage files +/.history/ diff --git a/docker/Dockerfile b/docker/Dockerfile index 07f47b4..dcc5643 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,3 +1,3 @@ -FROM maven:3.8.5-openjdk-17 +FROM maven:3.9.9-eclipse-temurin-17 WORKDIR /yamcs diff --git a/docker/Makefile b/docker/Makefile index 8645a3d..7effc2b 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -1,33 +1,89 @@ -ifeq ($(shell which python3),) - PYTHON = python -else - PYTHON = python3 -endif +SHELL := /bin/bash +.ONESHELL: +.SHELLFLAGS := -eu -o pipefail -c +.DELETE_ON_ERROR: +MAKEFLAGS += --warn-undefined-variables +MAKEFLAGS += --no-builtin-rules + +PYTHON := $(shell command -v python3 2>/dev/null || echo python) .DEFAULT_GOAL := help -all: ## run all, yamcs-up (yamcs-down) and yamcs-simulator - $(MAKE) yamcs-simulator - +# Define function to print messages in different colors +# Usage: $(call print_message,ANSI_COLOR_CODE,Your message here) +define print_message + @printf "\033[$(1)m$(2)\033[0m\n" +endef + +.PHONY: all all-10hz clean yamcs-up yamcs-down yamcs-simulator yamcs-simulator-10hz yamcs-shell help + +all: clean yamcs-up yamcs-simulator ## run all: clean, yamcs-up (yamcs-down) and yamcs-simulator + +all-10hz: clean yamcs-up yamcs-simulator-10hz ## run all: clean, yamcs-up (yamcs-down) and yamcs-simulator + yamcs-up: | yamcs-down ## bring up yamcs system docker compose up -d yamcs-down: ## bring down yamcs system + $(call print_message,33,Stopping any running docker-yamcs containers...) docker compose down -v --remove-orphans -yamcs-simulator: yamcs-up ## run yamcs simulator - @echo "connect via http://localhost:8090/ system make take about 50 seconds to startup" && \ +yamcs-simulator: ## run yamcs simulator + $(call print_message,36,Connect via http://localhost:8090/ system may take about 50 seconds to startup) cd .. && $(PYTHON) ./simulator.py +yamcs-simulator-10hz: ## run yamcs simulator at 10hz + $(call print_message,36,Connect via http://localhost:8090/ system may take about 50 seconds to startup) + cd .. && $(PYTHON) ./simulator.py --rate=10 + +yamcs-simulator-down: ## stop the yamcs simulator + $(call print_message,33,Stopping the yamcs simulator...) + pkill -f simulator.py + $(call print_message,32,Simulator stopped successfully) + +yamcs-simulator-restart: yamcs-simulator-down yamcs-simulator ## stop the yamcs simulator and start it again + yamcs-shell: ## shell into yamcs container docker compose up -d && docker compose exec yamcs bash -help: - @printf "\033[37m%-30s\033[0m %s\n" "#----------------------------------------------------------------------------------" - @printf "\033[37m%-30s\033[0m %s\n" "# Makefile " - @printf "\033[37m%-30s\033[0m %s\n" "#----------------------------------------------------------------------------------" - @printf "\033[37m%-30s\033[0m %s\n" "#-targets----------------------description-----------------------------------------" - @grep -E '^[a-zA-Z_-].+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +clean: | yamcs-down ## remove yamcs build artifacts and docker resources created by this Makefile + $(call print_message,33,Cleaning up docker-yamcs resources (containers, volumes, networks)...) + docker compose rm -f -s -v + $(call print_message,33,Removing docker-yamcs image...) + docker image rm -f docker-yamcs 2>/dev/null || true + $(call print_message,33,Cleaning up yamcs build artifacts...) + rm -rf ../target + $(call print_message,32,Done!) + +wait-for-sent: | yamcs-up ## run make all and wait up to 10 minutes for "Sent:" in the output + @echo "Running make all and waiting for 'Sent:' in the output..." && \ + nohup $(MAKE) all > all.log 2>&1 & \ + pid=$$!; \ + timeout=600; \ + while ! grep -q "Sent:" all.log; do \ + sleep 1; \ + timeout=$$((timeout - 1)); \ + if [ $$timeout -le 0 ]; then \ + echo "Timeout waiting for 'Sent:' in the output"; \ + kill $$pid; \ + exit 1; \ + fi; \ + done; \ + echo "Found 'Sent:' in the output"; \ + kill $$pid + +TERM_WIDTH := $(shell tput cols 2>/dev/null || echo 80) + +define print_header + @printf '%*s\n' "$(TERM_WIDTH)" '' | tr ' ' '-' + @printf '%-*s\n' "$(TERM_WIDTH)" "$(1)" + @printf '%*s\n' "$(TERM_WIDTH)" '' | tr ' ' '-' +endef + + +help: ## display this help message + $(call print_header,"Makefile") + @awk 'BEGIN {FS = ":.*##"; printf "\033[36m%-30s\033[0m %s\n", "Target", "Description"} /^[a-zA-Z_-]+:.*?##/ {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) | sort -print-%: +print-%: ## Print any variable (e.g., make print-PYTHON) @echo $* = $($*) diff --git a/pom.xml b/pom.xml index 084b400..e39df3b 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ Update this to the latest Yamcs release. Check https://mvnrepository.com/artifact/org.yamcs/yamcs-core --> - 5.9.8 + 5.10.1 @@ -52,7 +52,7 @@ org.yamcs yamcs-maven-plugin - 1.3.1 + 1.3.2 diff --git a/simulator.py b/simulator.py index 3d30440..7c9de19 100755 --- a/simulator.py +++ b/simulator.py @@ -4,15 +4,42 @@ import io import socket import sys +import argparse + from struct import unpack_from from threading import Thread from time import sleep +parser = argparse.ArgumentParser(description='Yamcs Simulator') +parser.add_argument('--testdata', type=str, default='testdata.ccsds', help='simulated testdata.ccsds data') + +# telemetry +parser.add_argument('--tm_host', type=str, default='127.0.0.1', help='TM host') +parser.add_argument('--tm_port', type=int, default=10015, help='TM port') +parser.add_argument('-r', '--rate', type=int, default=1, help='TM playback rate. 1 = 1Hz, 10 = 10Hz, etc.') + +# telecommand +parser.add_argument('--tc_host', type=str, default='127.0.0.1', help='TC host') +parser.add_argument('--tc_port', type=int, default=10025 , help='TC port') + +args = vars(parser.parse_args()) + +# test data +TEST_DATA = args['testdata'] + +# telemetry +TM_SEND_ADDRESS = args['tm_host'] +TM_SEND_PORT = args['tm_port'] +RATE = args['rate'] + +# telecommand +TC_RECEIVE_ADDRESS = args['tc_host'] +TC_RECEIVE_PORT = args['tc_port'] def send_tm(simulator): tm_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - with io.open('testdata.ccsds', 'rb') as f: + with io.open(TEST_DATA, 'rb') as f: simulator.tm_counter = 1 header = bytearray(6) while f.readinto(header) == 6: @@ -22,15 +49,15 @@ def send_tm(simulator): f.seek(-6, io.SEEK_CUR) f.readinto(packet) - tm_socket.sendto(packet, ('127.0.0.1', 10015)) + tm_socket.sendto(packet, (TM_SEND_ADDRESS, TM_SEND_PORT)) simulator.tm_counter += 1 - sleep(1) + sleep(1 / simulator.rate) def receive_tc(simulator): tc_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - tc_socket.bind(('127.0.0.1', 10025)) + tc_socket.bind((TC_RECEIVE_ADDRESS, TC_RECEIVE_PORT )) while True: data, _ = tc_socket.recvfrom(4096) simulator.last_tc = data @@ -39,12 +66,13 @@ def receive_tc(simulator): class Simulator(): - def __init__(self): + def __init__(self, rate): self.tm_counter = 0 self.tc_counter = 0 self.tm_thread = None self.tc_thread = None self.last_tc = None + self.rate = rate def start(self): self.tm_thread = Thread(target=send_tm, args=(self,)) @@ -63,9 +91,11 @@ def print_status(self): if __name__ == '__main__': - simulator = Simulator() + simulator = Simulator(RATE) simulator.start() - + sys.stdout.write('Using playback rate of ' + str(RATE) + 'Hz, '); + sys.stdout.write('TM host=' + str(TM_SEND_ADDRESS) + ', TM port=' + str(TM_SEND_PORT) + ', '); + sys.stdout.write('TC host=' + str(TC_RECEIVE_ADDRESS) + ', TC port=' + str(TC_RECEIVE_PORT) + '\r\n'); try: prev_status = None while True: diff --git a/src/main/yamcs/mdb/xtce.xml b/src/main/yamcs/mdb/xtce.xml index 7825200..ba8fa4c 100644 --- a/src/main/yamcs/mdb/xtce.xml +++ b/src/main/yamcs/mdb/xtce.xml @@ -141,6 +141,9 @@ + + + @@ -262,6 +265,20 @@ + + + out0.setFloatValue(in.getEngValue().getFloatValue()); + + + + + + + + + + +