Skip to content

Commit

Permalink
Merge pull request #18 from Jbsco/main
Browse files Browse the repository at this point in the history
Implementing COSMOS support
  • Loading branch information
dinkelk authored Jun 12, 2024
2 parents dcd1d8c + 9a82142 commit f54cb85
Show file tree
Hide file tree
Showing 17 changed files with 514 additions and 3 deletions.
1 change: 1 addition & 0 deletions gnd/cosmos/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
venv
12 changes: 12 additions & 0 deletions gnd/cosmos/build_cosmos_plugin.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/sh

#
# Configure cosmos plugin
#

assembly_dir=$1
# Build all cosmos plugin command and telemetry files:
cd $assembly_dir
plugin=`redo what 2>&1 | grep "cosmos" | awk '{ print $2 }'`
echo $plugin | xargs redo-ifchange
cd - >/dev/null
57 changes: 57 additions & 0 deletions gnd/cosmos/install_cosmos_plugin.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/bin/bash

#
# Copy COSMOS plugin configuration from Adamant into COSMOS plugin directory
# Ex. `./install_cosmos_plugin.sh ../../src/assembly/linux/linux_example.assembly.yaml ../../../cosmos-project` if COSMOS is adjacent to adamant_example

adamant_assembly_model=$1 # path to assembly yaml file
cosmos_plugin_dir=$2 # path to COSMOS plugin within the COSMOS installation

if [[ $1 == "" ]]
then
echo "Adamant assembly model argument not provided."
echo "Usage: \"./install_cosmos_plugin.sh ../../src/assembly/linux/linux_example.assembly.yaml ../../../cosmos-project/openc3-cosmos-plugin-dir\""
echo "Exiting."
exit 1
elif [[ $2 == "" ]]
then
echo "COSMOS plugin directory argument not provided."
echo "Usage: \"./install_cosmos_plugin.sh ../../src/assembly/linux/linux_example.assembly.yaml ../../../cosmos-project/openc3-cosmos-plugin-dir\""
echo "Exiting."
exit 1
fi

this_dir=`readlink -f "${BASH_SOURCE[0]}" | xargs dirname`
adamant_assembly_name=${adamant_assembly_model%.assembly.yaml}
adamant_assembly_name=${adamant_assembly_name##*/}
adamant_assembly_dir=`dirname $adamant_assembly_model`
cosmos_plugin_dir=`realpath $cosmos_plugin_dir`

# Get build directory:
adamant_assembly_name_short=(${adamant_assembly_name//_/ })
adamant_assembly_name_upper=$(tr [:lower:] [:upper:] <<< "$adamant_assembly_name")
adamant_assembly_cmdtlm_dir=`realpath $adamant_assembly_dir/build/cosmos/plugin`
adamant_assembly_plugin_dir=`realpath $adamant_assembly_dir/main/cosmos/plugin`
adamant_protocol_dir=`realpath $this_dir/../../../adamant/gnd/cosmos`

# Copy all protocol files (plugins compile with only needed protocols):
echo "Copying over plugin files..."
cp -vfa $adamant_protocol_dir/*.rb $cosmos_plugin_dir/targets/$adamant_assembly_name_upper/lib/

do_copy() {
src=$1
dest=$2
if [[ -f "$src" ]]; then
cp -vf "$src" "$dest"
else
echo "\"$src\" does not exist, run \"redo cosmos_config\" from the Adamant assembly, or make sure the required source is present."
exit 1
fi
}

# Copy plugin configuration files with error checking:
do_copy "$adamant_assembly_cmdtlm_dir/${adamant_assembly_name}_ccsds_cosmos_commands.txt" $cosmos_plugin_dir/targets/$adamant_assembly_name_upper/cmd_tlm/cmd.txt
do_copy "$adamant_assembly_cmdtlm_dir/${adamant_assembly_name}_ccsds_cosmos_telemetry.txt" $cosmos_plugin_dir/targets/$adamant_assembly_name_upper/cmd_tlm/tlm.txt
do_copy "$adamant_assembly_plugin_dir/plugin.txt" $cosmos_plugin_dir/plugin.txt
echo "Success."
echo "Plugin files copied to $cosmos_plugin_dir."
96 changes: 96 additions & 0 deletions gnd/cosmos/serial_tcp_bridge.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/usr/bin/env python3

import argparse
import socket
import threading
import binascii
import serial

SYNC_PATTERN = bytearray([0xFE, 0xD4, 0xAF, 0xEE]) # Byte pattern for syncing


def setup_serial(tty, baudrate):
seri = serial.Serial(tty, baudrate)
return seri


def recv_from_serial(seri, sock, stop_event):
first_time = True
buffer = bytearray()
while not stop_event.is_set():
if seri.in_waiting:
# read all available bytes
data = seri.read(seri.in_waiting)
buffer.extend(data)

# find sync pattern index
sync_idx = buffer.find(SYNC_PATTERN)

# if we found the sync pattern in the buffer
while sync_idx != -1:
# exclude the first iteration
if sync_idx != 0:

if not first_time:
# before the sync pattern is the data
print("tlm: " + str(binascii.hexlify(buffer[:sync_idx])))
sock.sendall(buffer[:sync_idx])
else:
first_time = False
# remove processed bytes (including sync pattern)
buffer = buffer[sync_idx + len(SYNC_PATTERN):]
# try to find the next sync pattern
sync_idx = buffer.find(SYNC_PATTERN)


def send_to_serial(seri, sock, stop_event):
while not stop_event.is_set():
data = sock.recv(2048)
if data:
# prepend sync pattern and write to serial port
print("cmd: " + str(binascii.hexlify(data)))
seri.write(SYNC_PATTERN + data)


def main(args):
# Setup serial connection
seri = setup_serial(args.tty, args.baudrate)
# Setup TCP/IP connection
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((args.ip, args.port))

# Event to signal the threads to stop
stop_event = threading.Event()

# Create and start threads
recv_thread = threading.Thread(
target=recv_from_serial, args=(seri, sock, stop_event)
)
send_thread = threading.Thread(target=send_to_serial, args=(seri, sock, stop_event))
recv_thread.start()
send_thread.start()

try:
# Wait for both threads to finish
recv_thread.join()
send_thread.join()
except KeyboardInterrupt:
# When Ctrl+C is pressed, signal the threads to stop
stop_event.set()
recv_thread.join()
send_thread.join()


if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Bridge between serial device and TCP, using CCSDS 32-bit sync pattern as a delimiter over serial."
)
parser.add_argument("--tty", help="Path to the tty device.", required=True)
parser.add_argument("--baudrate", help="The baud rate.", required=True, type=int)
parser.add_argument("--ip", help="The IP address to connect to.", required=True)
parser.add_argument(
"--port", help="The port to connect to.", required=True, type=int
)

args = parser.parse_args()
main(args)
22 changes: 22 additions & 0 deletions src/assembly/linux/linux_example.product_packets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,25 @@ packets:
use_timestamp: True
- name: Oscillator_B.Oscillator_Value
period: "1" # create every 3 ticks
- name: Software_Status_Packet
description: This packet contains software status information.
id: 2
data_products:
- name: Ccsds_Command_Depacketizer_Instance.Accepted_Packet_Count
- name: Ccsds_Command_Depacketizer_Instance.Rejected_Packet_Count
- name: Command_Router_Instance.Command_Receive_Count
- name: Command_Router_Instance.Command_Success_Count
- name: Command_Router_Instance.Command_Failure_Count
- name: Command_Router_Instance.Last_Received_Command
- name: Command_Router_Instance.Last_Successful_Command
- name: Command_Router_Instance.Last_Failed_Command
- name: Command_Router_Instance.Noop_Arg_Last_Value
- name: Fault_Correction_Instance.Fault_Counter
- name: Fault_Correction_Instance.Last_Fault_Id_Received
- name: Fault_Correction_Instance.Time_Of_Last_Fault_Received
- name: Product_Packetizer_Instance.Housekeeping_Packet_Period
- name: Product_Packetizer_Instance.Software_Status_Packet_Period
- name: Cpu_Monitor_Instance.Packet_Period
- name: Queue_Monitor_Instance.Packet_Period
- name: Stack_Monitor_Instance.Packet_Period
period: "1" # create every tick
97 changes: 97 additions & 0 deletions src/assembly/linux/main/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,103 @@ Starting Linux demo... Use Ctrl+C to exit.
0000168827.862054941 - Counter_Instance.Sending_Value (0x00000091) : (Value = 3)
...
```
## Commanding and Telemetry with [COSMOS](https://github.com/OpenC3/cosmos)

![`Commanding and Telemetry with OpenC3 COSMOS`](img/cosmos.png "Commanding and Telemetry with OpenC3 COSMOS")

To best interact with the Linux assembly, we need to use a ground system interface, such as OpenC3 [COSMOS](https://github.com/OpenC3/cosmos). To install COSMOS, navigate to an appropriate directory on your host machine and clone the COSMOS example project repository:

```
$ git clone https://github.com/openc3/cosmos-project.git
```

The COSMOS Docker container must be configured to use the same ports as Adamant. Edit this configuration at `cosmos-project/compose.yaml` and add the following entry to the `openc3-operator:` section:

```
ports:
- "2003:2003/tcp"
```

Start COSMOS by running:

```
$ cd cosmos-project
$ ./openc3.sh start
```

After the containers start, open a web browser and navigate to `http://localhost:2900`. You will be prompted to configure a password, and then presented with the COSMOS interface. Some modules may still be loading, but after a minute the interface should be complete. Navigate to `ADMIN CONSOLE` and uninstall the "openc3-cosmos-demo" plugin by selecting the `Delete Plugin` icon.

OpenC3 COSMOS runs inside of its own Docker containers. The COSMOS example project provides the necessary tools and commands to create an interface plugin from our generated configuration files. While still in the `cosmos-project` directory, run:

```
$ ./openc3.sh cli generate plugin LINUX_EXAMPLE
```

This creates a new directory called `openc3-cosmos-linux-example`. Next, run:

```
$ cd openc3-cosmos-linux-example
$ ../openc3.sh cli generate target LINUX_EXAMPLE
```

This creates the plugin directory structure as well as template configuration files that our generated configuration will replace. These files are located at:

```
cosmos-project/openc3-cosmos-linux-example/plugin.txt
cosmos-project/openc3-cosmos-linux-example/targets/LINUX_EXAMPLE/cmd_tlm/cmd.txt
cosmos-project/openc3-cosmos-linux-example/targets/LINUX_EXAMPLE/cmd_tlm/tlm.txt
```

After installing and running COSMOS we need to build the Linux Example plugin configuration files. This will allow COSMOS to decode telemetry from the Adamant virtual environment and properly format outgoing commands.

These files are autocoded by Adamant. *From the Adamant virtual environment* run:

```
$ cd adamant_example/src/assembly/linux/main
$ redo cosmos_config
```

This generates the command and telemetry configurations in the `adamant_example/src/assembly/linux/build/cosmos/plugin/` directory. Note that the main plugin configuration file is located in `adamant_example/src/assembly/linux/main/cosmos/plugin/`. This file should be reviewed to ensure the interface and required protocols are correct for your configuration. All of these files will be installed into the COSMOS plugin in the following steps.

Next, run the helper script to install the Adamant plugin configuration files to the COSMOS plugin directory. *The following should be run from the host machine*. If the COSMOS and Adamant example project directories are adjacent, the command to install the configuration would be:

```
$ cd adamant_example/src/assembly/linux/main
$ ./install_cosmos_plugin.sh ../../../../../cosmos-project/openc3-cosmos-linux-example/
```

The plugin can now be compiled. Next, run:

```
$ cd ../../../../../cosmos-project/openc3-cosmos-linux-example/
$ ../openc3.sh cli rake build VERSION=1.0.0
```

In the COSMOS Admin Console, select `Click to select plugin .gem file to install` and navigate to the compiled plugin gem file at `cosmos-project/openc3-cosmos-linux-example/openc3-cosmos-linux-example-1.0.0.gem` to install our generated plugin. The plugin is templated to allow changing of parameters such as ports, but the default values are already set to correspond with this project.

The plugin will be installed. Return to the Adamant virtual environment and start the Linux assembly by running:

```
$ cd adamant_example/src/assembly/linux/main
$ redo run
```

In the Log Messages panel you should see that the TCPIP server has accepted a new connection from Adamant once the assembly is up and running.

With COSMOS running, here are some interesting things you can try:

1. View messages generated from the Linux assembly in the main panel.
2. View telemetry from the Counter and Oscillator components by opening the `Telemetry Grapher` panel and selecting the "LINUX_EXAMPLE" target, "HOUSEKEEPING_PACKET" packet, "OSCILLATOR_A.OSCILLATOR_VALUE.VALUE" as item, and selecting "Add Item".
3. Send any command by selecting it in the `Command Sender` panel. Try sending a NOOP or changing the Oscillator frequencies by selecting "Oscillator_A-Set_Frequency", changing "Value", and selecting "Send".
4. View the queue usage for each component by opening the `Packet Viewer` panel and selecting "HOUSEKEEPING_PACKET".
5. Send an interrupt to the running assembly by running `sh send_interrupt.sh` from within another terminal in the Adamant virtual environment.

```
$ cd adamant_example/src/assembly/linux/main
$ sh send_interrupt.sh
```

You should see the software respond by printing out `Interrupt received` with a time stamp in the terminal where `redo run` was started.

## Commanding and Telemetry with Hydra

Expand Down
15 changes: 15 additions & 0 deletions src/assembly/linux/main/cosmos/plugin/plugin.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# COSMOS interface documentation is available at: https://docs.openc3.com/docs/configuration/interfaces
# Set variables here to allow variation in your plugin.
# Variables are able to be modified at time of install in COSMOS.
Variable linux_example_target_name Linux_Example
Variable crc_parameter_name CRC
Variable checksum_parameter_name Checksum
Variable port_w 2003
Variable port_r 2003

Target Linux_Example <%= linux_example_target_name %>
# TCP example interface:
Interface <%= linux_example_target_name %>_INT tcpip_server_interface.rb <%= port_w %> <%= port_r %> 10.0 nil Length 32 16 7
Map_Target <%= linux_example_target_name %>
Protocol Read crc_protocol.rb <%= crc_parameter_name %> false "ERROR" -16 16
Protocol Write cmd_checksum.rb <%= checksum_parameter_name %>
6 changes: 6 additions & 0 deletions src/assembly/linux/main/cosmos_config.do
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Create Cosmos plugin configuration files:
export TARGET=
this_dir=`dirname "$0"`
cosmos_dir=`realpath $this_dir/../../../../gnd/cosmos`
assembly_dir=`realpath $this_dir/..`
$cosmos_dir/build_cosmos_plugin.sh $assembly_dir >/dev/null
Binary file added src/assembly/linux/main/img/cosmos.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions src/assembly/linux/main/install_cosmos_plugin.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash

if [[ $1 == "" ]]
then
echo "COSMOS plugin directory path not provided."
echo "Usage: \"./install_cosmos_plugin.sh ../../../../../cosmos-project/openc3-cosmos-linux-example/\""
echo "Exiting."
exit 1
fi

cosmos_dir=$1
this_dir=`readlink -f "${BASH_SOURCE[0]}" | xargs dirname`
assembly_yaml=`ls -1 $this_dir/../*.assembly.yaml`
$this_dir/../../../../gnd/cosmos/install_cosmos_plugin.sh $assembly_yaml $cosmos_dir
Loading

0 comments on commit f54cb85

Please sign in to comment.