First draft
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
**.vscode/
|
||||
**out/
|
||||
15
README.md
Normal file
15
README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# INCP2
|
||||
|
||||
This is the home of the INCP remastering.
|
||||
|
||||
## QOL implemented
|
||||
|
||||
- Automatic 'answering' of requests (pings, datareqs, etc.)
|
||||
|
||||
## Current problems and things to change
|
||||
|
||||
- Subsystem code should not see ASN1 Encode/Decode functions. (Split ASN1 code files, structs and typedefs on one side, functions on other?)
|
||||
|
||||
## Improvements to make
|
||||
|
||||
- Optional metrics (messages of each type tx/rx, etc.)
|
||||
10
doc/examples/OBC+TTC/shared/bus/bus.h
Normal file
10
doc/examples/OBC+TTC/shared/bus/bus.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
//This shoulb be handled by CSP
|
||||
#define ADDRESS_OBC ((int)1)
|
||||
#define ADDRESS_TTC ((int)2)
|
||||
|
||||
bool bus_send(int destination, void *buf, size_t size);
|
||||
11
doc/examples/OBC+TTC/shared/incp/asn1/asn1_wrapper.c
Normal file
11
doc/examples/OBC+TTC/shared/incp/asn1/asn1_wrapper.c
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "asn1_wrapper.h"
|
||||
|
||||
err_t incp_decode_header(BitStream *in_bs, IncpMsg *msg)
|
||||
{
|
||||
int asn1_err = 0;
|
||||
|
||||
if (!IncpMsg_Decode(msg, in_bs, &asn1_err))
|
||||
return ERR_ERROR; //TODO: Proper error setting based on asn1_err
|
||||
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
23
doc/examples/OBC+TTC/shared/incp/asn1/asn1_wrapper.h
Normal file
23
doc/examples/OBC+TTC/shared/incp/asn1/asn1_wrapper.h
Normal file
@@ -0,0 +1,23 @@
|
||||
// asn1_wrapper.h - Wraps all ASN.1 generated files to conform with the rest of the C interface (return type to err_t
|
||||
// and alike).
|
||||
// Should only be used inside INCP implementation.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef _INCP_INTERNAL
|
||||
#error "asn1_wrapper.h should not be imported outside the implementation of INCP"
|
||||
#endif
|
||||
|
||||
#include "incp2.h"
|
||||
#include "messaging.h"
|
||||
#include "out/incp_static.h"
|
||||
|
||||
typedef IncpMsg_selection incp_msgt_t;
|
||||
|
||||
err_t incp_encode_header(BitStream *bs, IncpMsg *msg);
|
||||
err_t incp_decode_header(BitStream *bs, IncpMsg *msg);
|
||||
|
||||
err_t incp_encode_datareq(BitStream *bs, U32 id);
|
||||
err_t incp_decode_datareq(BitStream *bs, U32 id);
|
||||
err_t incp_encode_dataret(BitStream *bs, AnyData *data);
|
||||
err_t incp_decode_dataret(BitStream *bs, AnyData *data);
|
||||
19
doc/examples/OBC+TTC/shared/incp/asn1/incp_generated.asn
Normal file
19
doc/examples/OBC+TTC/shared/incp/asn1/incp_generated.asn
Normal file
@@ -0,0 +1,19 @@
|
||||
INCP-Generated DEFINITIONS AUTOMATIC TAGS ::= BEGIN
|
||||
|
||||
EXPORTS ALL;
|
||||
IMPORTS U32 FROM INCP-Types;
|
||||
|
||||
TTC-TxTotal ::= SEQUENCE {
|
||||
bytes U32
|
||||
}
|
||||
|
||||
TTC-RxTotal ::= SEQUENCE {
|
||||
bytes U32
|
||||
}
|
||||
|
||||
AnyData ::= CHOICE {
|
||||
ttcTxTotal TTC-TxTotal,
|
||||
ttcRxTotal TTC-RxTotal
|
||||
}
|
||||
|
||||
END
|
||||
51
doc/examples/OBC+TTC/shared/incp/asn1/incp_static.asn
Normal file
51
doc/examples/OBC+TTC/shared/incp/asn1/incp_static.asn
Normal file
@@ -0,0 +1,51 @@
|
||||
INCP-Types DEFINITIONS AUTOMATIC TAGS ::= BEGIN
|
||||
|
||||
EXPORTS ALL;
|
||||
|
||||
U8 ::= INTEGER(0..255)
|
||||
U16 ::= INTEGER(0..65535)
|
||||
U24 ::= INTEGER(8388608..16777215)
|
||||
U32 ::= INTEGER(0..4294967295)
|
||||
U64 ::= INTEGER(0..18446744073709551615)
|
||||
I8 ::= INTEGER(-128..127)
|
||||
I16 ::= INTEGER(-32768..32767)
|
||||
I24 ::= INTEGER(-8388608..8388607)
|
||||
I32 ::= INTEGER(-2147483648..2147483647)
|
||||
|
||||
END
|
||||
|
||||
INCP-Static DEFINITIONS AUTOMATIC TAGS ::= BEGIN
|
||||
|
||||
IMPORTS U8, U16, U32 FROM INCP-Types
|
||||
AnyData FROM INCP-Generated;
|
||||
|
||||
PingMsg ::= SEQUENCE {
|
||||
payload SEQUENCE (SIZE(32)) OF U8
|
||||
}
|
||||
|
||||
DataReqMsg ::= SEQUENCE {
|
||||
id U32 -- REVIEW: How to limit range to only accept IDs that are valid
|
||||
}
|
||||
|
||||
DataRetMsg ::= SEQUENCE {
|
||||
data AnyData
|
||||
}
|
||||
|
||||
CommandReqMsg ::= SEQUENCE {
|
||||
id U16
|
||||
}
|
||||
|
||||
CommandRetMsg ::= SEQUENCE {
|
||||
id U16
|
||||
}
|
||||
|
||||
IncpMsg ::= CHOICE {
|
||||
ping PingMsg,
|
||||
pong PingMsg,
|
||||
dataReq DataReqMsg,
|
||||
dataRet DataRetMsg,
|
||||
commandReq CommandReqMsg,
|
||||
commandRet CommandRetMsg
|
||||
}
|
||||
|
||||
END
|
||||
11
doc/examples/OBC+TTC/shared/incp/incp2.h
Normal file
11
doc/examples/OBC+TTC/shared/incp/incp2.h
Normal file
@@ -0,0 +1,11 @@
|
||||
// incp2.h - Defines types common to all INCP interfaces.
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
ERR_SUCCESS = 0,
|
||||
ERR_ERROR,
|
||||
ERR_NOT_IMPL,
|
||||
ERR_NOT_MINE,
|
||||
} err_t;
|
||||
|
||||
15
doc/examples/OBC+TTC/shared/incp/messaging.h
Normal file
15
doc/examples/OBC+TTC/shared/incp/messaging.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// messaging.h - Defines structures used during (de)construction of messages.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "incp2.h"
|
||||
|
||||
typedef int endpoint_t; //REVIEW: Figure this one out. Where the values come from, where they
|
||||
// are defined, etc.
|
||||
#define INCP_ENDPOINT_NVAL (~(endpoint_t)0)
|
||||
#define INCP_ENDPOINT_TTC ((endpoint_t)1)
|
||||
|
||||
#define DEFAULT_MSGBUF_SIZE 64
|
||||
177
doc/examples/OBC+TTC/shared/incp/service.c
Normal file
177
doc/examples/OBC+TTC/shared/incp/service.c
Normal file
@@ -0,0 +1,177 @@
|
||||
#define _INCP_INTERNAL
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "incp2.h"
|
||||
#include "service.h"
|
||||
#include "messaging.h"
|
||||
#include "asn1/asn1_wrapper.h"
|
||||
|
||||
|
||||
|
||||
static err_t handle_incoming(endpoint_t source, BitStream *in_bs);
|
||||
|
||||
|
||||
|
||||
void incp_receiver_task(void *args)
|
||||
{
|
||||
(void)args; //Stfu
|
||||
|
||||
while (1)
|
||||
{
|
||||
//LATER: Blocking wait for RTOS queue to fetch bus / radio message (go wild on that routing)
|
||||
BitStream in_bs = { 0 }; // <-- Populate from transport
|
||||
endpoint_t source = INCP_ENDPOINT_NVAL; // <-- Populate from transport
|
||||
|
||||
handle_incoming(source, &in_bs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
err_t incp_async_datareq(endpoint_t subsystem, U32 id)
|
||||
{
|
||||
err_t err; //Don't init on purpose
|
||||
BitStream out_bs;
|
||||
uint8_t buf[DEFAULT_MSGBUF_SIZE];
|
||||
BitStream_Init(&out_bs, buf, sizeof(buf));
|
||||
|
||||
IncpMsg msg;
|
||||
msg.kind = dataReq_PRESENT;
|
||||
msg.u.dataReq.id = id;
|
||||
|
||||
err = incp_encode_header(&out_bs, &msg);
|
||||
if (err != ERR_SUCCESS) goto _exit;
|
||||
|
||||
err = impl_transmit(subsystem, buf, BitStream_GetLength(&out_bs));
|
||||
|
||||
_exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
err_t incp_async_ping(endpoint_t subsystem, uint8_t *bytes)
|
||||
{
|
||||
err_t err; //Don't init on purpose
|
||||
BitStream out_bs;
|
||||
uint8_t buf[DEFAULT_MSGBUF_SIZE];
|
||||
BitStream_Init(&out_bs, buf, sizeof(buf));
|
||||
|
||||
IncpMsg msg;
|
||||
msg.kind = ping_PRESENT;
|
||||
memcpy(&msg.u.ping.payload, bytes != NULL ? bytes : "I am a very fancy ping payload!", 32);
|
||||
|
||||
err = incp_encode_header(&out_bs, &msg);
|
||||
if (err != ERR_SUCCESS) goto _exit;
|
||||
|
||||
err = impl_transmit(subsystem, buf, BitStream_GetLength(&out_bs));
|
||||
|
||||
_exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline err_t handle_incoming_ping(endpoint_t source, IncpMsg *in_msg, BitStream *in_bs)
|
||||
{
|
||||
(void)in_bs;
|
||||
err_t err; //Don't init on purpose
|
||||
BitStream out_bs;
|
||||
uint8_t buf[DEFAULT_MSGBUF_SIZE];
|
||||
BitStream_Init(&out_bs, buf, sizeof(buf));
|
||||
|
||||
IncpMsg msg = *in_msg;
|
||||
msg.kind = pong_PRESENT;
|
||||
|
||||
err = incp_encode_header(&out_bs, &msg);
|
||||
if (err != ERR_SUCCESS) goto _exit;
|
||||
|
||||
err = impl_transmit(source, buf, BitStream_GetLength(&out_bs)); //"Destroy a Ghast with a fireball"
|
||||
|
||||
_exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline err_t handle_incoming_pong(endpoint_t source, IncpMsg *in_msg, BitStream *in_bs)
|
||||
{
|
||||
(void)in_bs;
|
||||
err_t err; //Don't init on purpose
|
||||
|
||||
impl_callback_pong(source, in_msg);
|
||||
err = ERR_SUCCESS;
|
||||
|
||||
_exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline err_t handle_incoming_datareq(endpoint_t source, IncpMsg *in_msg, BitStream *in_bs)
|
||||
{
|
||||
err_t err; //Don't init on purpose
|
||||
BitStream out_bs;
|
||||
uint8_t buf[DEFAULT_MSGBUF_SIZE];
|
||||
BitStream_Init(&out_bs, buf, sizeof(buf));
|
||||
|
||||
AnyData data = { 0 };
|
||||
data.kind = in_msg->u.dataReq.id; //REVIEW: Dubious types
|
||||
|
||||
err = impl_populate_dataret(&data);
|
||||
if (err != ERR_SUCCESS) goto _exit;
|
||||
|
||||
err = incp_encode_dataret(&out_bs, &data);
|
||||
if (err != ERR_SUCCESS) goto _exit;
|
||||
|
||||
err = impl_transmit(source, buf, BitStream_GetLength(&out_bs));
|
||||
|
||||
_exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline err_t handle_incoming_dataret(endpoint_t source, IncpMsg *in_msg, BitStream *in_bs)
|
||||
{
|
||||
err_t err; //Don't init on purpose
|
||||
AnyData data; //Careful with lifetime of this! Rust my beloved (never tried it)
|
||||
|
||||
err = incp_decode_dataret(in_bs, &data);
|
||||
if (err != ERR_SUCCESS) goto _exit;
|
||||
|
||||
impl_callback_dataret(source, &data);
|
||||
err = ERR_SUCCESS;
|
||||
|
||||
_exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static err_t handle_incoming(endpoint_t source, BitStream *in_bs)
|
||||
{
|
||||
err_t err; //Don't init on purpose
|
||||
IncpMsg msg;
|
||||
|
||||
|
||||
err = incp_decode_header(in_bs, &msg);
|
||||
if (err != ERR_SUCCESS) goto _exit;
|
||||
|
||||
switch (msg.kind)
|
||||
{
|
||||
case ping_PRESENT:
|
||||
err = handle_incoming_ping(source, &msg, in_bs);
|
||||
break;
|
||||
|
||||
case pong_PRESENT:
|
||||
err = handle_incoming_pong(source, &msg, in_bs);
|
||||
break;
|
||||
|
||||
case dataReq_PRESENT:
|
||||
err = handle_incoming_datareq(source, &msg, in_bs);
|
||||
break;
|
||||
|
||||
case dataRet_PRESENT:
|
||||
err = handle_incoming_dataret(source, &msg, in_bs);
|
||||
break;
|
||||
|
||||
default:
|
||||
err = ERR_NOT_IMPL;
|
||||
break;
|
||||
}
|
||||
|
||||
_exit:
|
||||
return err;
|
||||
}
|
||||
75
doc/examples/OBC+TTC/shared/incp/service.h
Normal file
75
doc/examples/OBC+TTC/shared/incp/service.h
Normal file
@@ -0,0 +1,75 @@
|
||||
// serivce.h - Defines INCP service interface and functions to implement per subsystem.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "incp2.h"
|
||||
#include "messaging.h"
|
||||
#include "asn1/out/incp_generated.h"
|
||||
|
||||
|
||||
|
||||
// ==========================
|
||||
// === RTOS SETUP RELATED ===
|
||||
// ==========================
|
||||
|
||||
/// @brief [DO NOT CALL] Entry point for the receiver task of the INCP service.
|
||||
/// @param args Not used
|
||||
/// @remark Do not call directly, instead, pass it to the RTOS during task creation.
|
||||
void incp_receiver_task(void *args);
|
||||
|
||||
|
||||
|
||||
// ========================
|
||||
// === INCP SERVICE API ===
|
||||
// ========================
|
||||
|
||||
/// @brief Sends a datareq message of a given ID to a given subsystem.
|
||||
/// @param subsystem The subsystem to target.
|
||||
/// @param id The ID of the variable/report to request.
|
||||
/// @return ERR_SUCCESS if successfully transmitted, an adequate error otherwise.
|
||||
err_t incp_async_datareq(endpoint_t subsystem, U32 id);
|
||||
|
||||
/// @brief Sends a ping message to a given subsystem.
|
||||
/// @param subsystem The subsystem to target.
|
||||
/// @param bytes The data to populate the ping with. Must NULL or contain 32 readable bytes.
|
||||
/// @return ERR_SUCCESS if successfully transmitted, an adequate error otherwise.
|
||||
err_t incp_async_ping(endpoint_t subsystem, uint8_t *bytes);
|
||||
|
||||
|
||||
|
||||
// ==================================
|
||||
// === TO IMPLEMENT PER SUBSYSTEM ===
|
||||
// ==================================
|
||||
|
||||
/// @brief Implement this function to send (through whatever means, that's a you problem) the given buffer to a given
|
||||
/// destination.
|
||||
/// @param destination Where to send the buffer.
|
||||
/// @param buf The buffer to send.
|
||||
/// @param size The length of the data in the buffer.
|
||||
/// @return ERR_SUCCESS if successful, an adequate error otherwise.
|
||||
/// @remark Do not __attribute__((weak)) to force linker error.
|
||||
err_t impl_transmit(endpoint_t destination, void *buf, size_t size);
|
||||
|
||||
/// @brief Implement this function to populate msg_buf with the requested variables.
|
||||
/// @param data The destination to write to. Includes the variable/report id, which should not be changed.
|
||||
/// @return ERR_SUCCESS if successful, an adequate error otherwise.
|
||||
/// @remark Do not __attribute__((weak)) to force linker error.
|
||||
err_t impl_populate_dataret(AnyData *data);
|
||||
|
||||
|
||||
|
||||
// === Callbacks ===
|
||||
|
||||
/// @brief Implement this function as the callback handler to a dataret message. Can (but shoudln't) be empty.
|
||||
/// @param source The subsystem that send the message.
|
||||
/// @param data The buffer that holds the data. (See remarks)
|
||||
/// @remark Do not copy this value by reference, it's validity is only ensured during the execution of this function.
|
||||
/// @remark Do not __attribute__((weak)) to force linker error if undefined.
|
||||
void impl_callback_dataret(endpoint_t source, AnyData *data);
|
||||
|
||||
/// @brief Implement this function as the callback handler to a pong message. Can (but shoudln't) be empty.
|
||||
/// @param source The subsystem that send the message.
|
||||
/// @param pong The received pong. (See remarks)
|
||||
/// @remark Do not copy this value by reference, it's validity is only ensured during the execution of this function.
|
||||
/// @remark Do not __attribute__((weak)) to force linker error if undefined.
|
||||
void impl_callback_pong(endpoint_t source, PingMsg *pong);
|
||||
43
doc/examples/OBC+TTC/subsystems/OBC/main.c
Normal file
43
doc/examples/OBC+TTC/subsystems/OBC/main.c
Normal file
@@ -0,0 +1,43 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include "../../shared/incp/service.h"
|
||||
#include "../../shared/incp/asn1/out/incp_generated.h"
|
||||
#include "../../shared/bus/bus.h"
|
||||
|
||||
|
||||
|
||||
//In this example, OBC will only ping and then request a variable from TTC
|
||||
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
incp_async_ping(INCP_ENDPOINT_TTC, NULL);
|
||||
incp_async_datareq(INCP_ENDPOINT_TTC, ttcRxTotal_PRESENT);
|
||||
|
||||
for (;;);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// === SUBSYSTEM SPECIFIC INCP IMPLEMENTATIONS ===
|
||||
err_t impl_transmit(endpoint_t destination, void *buf, size_t size)
|
||||
{
|
||||
bus_send(destination, buf, size);
|
||||
}
|
||||
|
||||
err_t impl_populate_dataret(AnyData *data)
|
||||
{
|
||||
//LATER: Populate or deny
|
||||
}
|
||||
|
||||
void impl_callback_dataret(endpoint_t source, AnyData *data)
|
||||
{
|
||||
//LATER: Do whatever
|
||||
}
|
||||
|
||||
void impl_callback_pong(endpoint_t source, PingMsg *pong)
|
||||
{
|
||||
//LATER: Do whatever
|
||||
}
|
||||
50
doc/examples/OBC+TTC/subsystems/TTC/main.c
Normal file
50
doc/examples/OBC+TTC/subsystems/TTC/main.c
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "../../shared/incp/service.h"
|
||||
#include "../../shared/incp/asn1/out/incp_generated.h"
|
||||
#include "../../shared/bus/bus.h"
|
||||
|
||||
|
||||
|
||||
//In this example, TTC will be passive and will only reply to datareqs and pings
|
||||
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
for (;;);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// === SUBSYSTEM SPECIFIC INCP IMPLEMENTATIONS ===
|
||||
err_t impl_transmit(endpoint_t destination, void *buf, size_t size)
|
||||
{
|
||||
bus_send(destination, buf, size);
|
||||
}
|
||||
|
||||
err_t impl_populate_dataret(AnyData *data)
|
||||
{
|
||||
switch (data->kind)
|
||||
{
|
||||
case ttcRxTotal_PRESENT:
|
||||
data->u.ttcRxTotal.bytes = 69; //LATER: Proper data
|
||||
return ERR_SUCCESS;
|
||||
|
||||
case ttcTxTotal_PRESENT:
|
||||
data->u.ttcTxTotal.bytes = 420; //LATER: Proper data
|
||||
return ERR_SUCCESS;
|
||||
|
||||
default:
|
||||
return ERR_NOT_MINE;
|
||||
}
|
||||
}
|
||||
|
||||
void impl_callback_dataret(endpoint_t source, AnyData *data)
|
||||
{
|
||||
//LATER: Do whatever
|
||||
}
|
||||
|
||||
void impl_callback_pong(endpoint_t source, PingMsg *pong)
|
||||
{
|
||||
//LATER: Do whatever
|
||||
}
|
||||
Reference in New Issue
Block a user