---
# System prepended metadata

title: ADCS code guide
tags: [FS]

---

###### tags: `FS`
# ADCS code guide
## Index
[TOC]
## Analyze documentation
ADCS (Attitude Determination And Control System) is the system that manage the altitude of the satellite. It'a parametrized on ground and some commandes are accesible after configuration. In your satellite the ADCS from CubeSpace has been chosen. We can communicate with it with UART bus.

### Communication
As we use UART we need to follow the definition of UART protcole for ADCS. 

```csvpreview {header="true"}
Byte,Meaning
0x07,Start of Message
0xFF,End of Message
0x1F, Before start and end message
```
With this protocole we can send telecommande and telemetry request to ADCS

#### Telemtry request

Telemetry request must follow the following bytes order: 
![](https://i.imgur.com/Pr4gtVX.png)

When sending a telemetry request a response is always return by ADCS. 

![](https://i.imgur.com/3E9nIAj.png)


#### Telecommand request

Telecommand request must follow the following byte order:

![](https://i.imgur.com/6B8wybD.png)

When sending a telecommand request a response is always return by ADCS. 

![](https://i.imgur.com/t9Ml16M.png)


The list of telemetry id and telecommand are in ADCS documentation and note described here


#### Checksum

When sending a telecommand it is possible to add a byte to telecommand request with the a checksum. The following code implement the checksum. The checksum algorithm is described [Here]()

## Design
As we don't have the real component we must take in consideration the a simulator need to be developed for testing and simulate communication.

### Base design

![base diagram](https://i.imgur.com/NTGWtZ0.png)

This diagramme show a base design of the ADCS design. We have two component in Fprime implementation. The Active component "ADCS" manage the use of adcs, it create UART frame form data and send it to Driver. It's an active component because it need it's own thread to manage communication form adcs.

To send trame to hardware we need and Driver. ADCS send UART to driver. SocketTcpDriver is a tcp driver that can communicate with ADCS simulator. It creates a tcp connection with python simulator to send and receive data. This driver can be replace by the real UART driver to communicate with real hardware.

Python simulator simulate the ADCS hardware behaviour. It's a python tcp server with a state machine. It can send some basic parameters and respond to commande with and understand checksum.

The following diagram resume a sequence of all adcs system.
![séquence diagram](https://i.imgur.com/TvLtVYd.png)


### Simulator
![](https://i.imgur.com/gLPCwGQ.png)
The above class diagram show the organization of the ADCS simulator. The base is a state machine that has one parameter and 3 states. When state is changed the value of the parameter is updated. It must be imporved with more functionalities when tests will be specified.

The sate machine is very simple because we don't have enough informations about ADCS on waht must be simulate. The following diagram show the ADCS state machine.

![](https://i.imgur.com/pcx2dji.png)

### SocketTcpDriver and ADCS
![](https://i.imgur.com/abKGZeA.png)

The above diagram component shows the organizstion of ADCS design in Fprime. The socket is responsible for interaction on tcp with the python simulator and it's passive. The ADCS active component manages in command an create ADCS frames. There are two commands on for telemetry and one for command of ADCS.

To communicate between ADCS an socketTcpDriver a Fw::BufferSend is used. The bytearray UART Frames are put into buffer end send to socket. The return bytearrey UART frame from Python simulator is put into buffer en send back to ADCS.

## Simulator
This chapter describes the implementation and the code of python ADCS simulator.
### TcpMain
It is the entry of the program. it prints some information and init the TCP server.

**initialisation:**
```python=
if __name__ == '__main__':
    adcs = ADCSStateMachine()
    print("state machine build")
    print(adcs.current_state)
    print(f"adcs parameter : {adcs.param}")
    print("create connexion")
    runTCP(adcs,'127.0.0.1',5005)
```
Here nothing need to be changed except the runTCP() function. You can adapt:
- the ip: here 127.0.0.1
- the port: here 5005

If you want to reuse The TCP server for an other simulator, you need to provide a object that implement a "request()" function that take the payload byte array im parameter, here ADCSStateMachine implement this function..

Fristly we create the ADCS state machine with  :
```python=
adcs = ADCSStateMachine()
```

after the state machine is created we start the TCP server and pass the state machine to it :
```python=
runTCP(adcs,'127.0.0.1',5005)
```
The state machine will manage the data receive from tcp.

### TcpServer
This file provides the TCP server. It has only one function that has a while loop. This loop wait for tcp connection and start receiving packet. When the transaction is over, it will wait an other connection.

![](https://i.imgur.com/AsyY2yp.png)

In the code below we can see that the ip and server port are set with the function parameter provided by the main file. The buffer is the size of the data that can be manage by the server at a time. If the data are bigger, they are cut into the size buffer.

```python=
    TCP_IP = ip
    TCP_PORT = port
    BUFFER_SIZE = 2046
```

In the code bellow we have the configuration of the tcp server
socket.AF_INET says that it is a tcp server.
s.bind bind the just creat socket to the address and port.
s.listen(1) enebale the server to accepte connection. the backlog is set to 1 but can be change.
```python=
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    print(f"starting up on {TCP_IP} port {TCP_PORT}")
    s.bind((TCP_IP, TCP_PORT))
    s.listen(1)
```

On the code below we have the infinity loop. With this loop the python process never end.  The server waits for a connection with s.accepte. This function block the executing thread until something connected.

```python=
 while 1:
        print("Waiting connexion")
        conn, addr = s.accept()
```

In the code below we have the current loop when a connection is established. first we print the address that is connected. Then we will loop on the conn.recv we can recev maximum BUFFER_SIZE data a at one time. We put large enough beacuse a ADCS packet will never be bigger than 2046 so we can manage one ADCS frame at a time

After we can see the request function af adcs is called and the server is wainting fo sata to send back. the data from adcs are sent back to the incomming connection. when all the data is managed the connection is closed and the server will wait for an other connection
```python=

        try:
            print(f'Connection address: {addr} \n')
            while 1:
                data = conn.recv(BUFFER_SIZE)
                if data:

                    print(f"Data Received : {data}")
                    back_data = simulator.request(data)
                    print(f'sending data back to client : data {back_data}\n')
                    conn.send(back_data)
                else:
                    print(f"no data from {addr}")
                    break
        finally:
            conn.close()
```


### ADCSStateMachine

ADCSStateMachine class is derived from StateMachine. This class comes from python-statemachine. it can be installed with

```bash
pip install python-statemachine
```

With this class we need to define states and transition between states. Here we have 3 states and 4 transtion. Refer to the design for statemachine conception 

We set a basic param for basic testing.

```python=
    low_orbit = State('Low orbit', initial=True)
    middle_orbit = State('Middle orbit')
    high_orbit = State('High orbit')

    up_middle = low_orbit.to(middle_orbit)
    up_high = middle_orbit.to(high_orbit)
    down_middle = high_orbit.to(middle_orbit)
    down_low = middle_orbit.to(low_orbit)

    param = 10
```
 
When we receive an tc we can manage its payload. for now 

Here we switch the incomming tc id. . if the stat can be switch it return the 0x03 code "see ADCS documentation". Here you can add any callable function according to the TC id. Just a an "elif" and call the right function. Maybe call a function of the mathematical model. The payload data are used here beacause it is hard to know how the adcs reacts.

### Telecommand function
```python=
    def switcher(self, case, data):
        data = 0x00
        try:
            if case == 0:
                self.up_middle()
            elif case == 1:
                self.up_high()
            elif case == 2:
                self.down_middle()
            elif case == 3:
                self.down_low()
            else:
                print("error orbit")
                data = 0x03
        except:
            data = 0x03
        return data
```

These functions are called when current state change. We can see that the ADCSStateMachine param value change when the state change. This state can be substitued by an mathematical model.
```python=
    def on_enter_low_orbit(self):
        self.param = 10
        print(f"{self.current_state} set, param = {self.param}")

    def on_enter_middle_orbit(self):
        self.param = 20
        print(f"{self.current_state} set, param = {self.param}")

    def on_enter_high_orbit(self):
        self.param = 30
        print(f"{self.current_state} set, param = {self.param}")
```


### Packet management
When a tc is received, we send the tc id and data to the switcher. if an error occure we retunr 0x03 error code.

When a tm is received, we  send the id to the request_telemetry_data. this function will return the internal param parameter. A switcher can easily be added and return the telemetry in function of the id. But for know it is not necessar to return telemetry we doesn't know.
These two functions is implemented to react to command and telemetry. For now it's very basic. *exec_command* use switcher to change state and *request_telemetry_data* just return the ADCSStateMachine param value. **it must be improve when testing strategy will be defined**

```python=
    def exec_command(self, id, data):
        try:
            data = self.switcher(id,data)
        except Exception as e:
            print(e)
            data = 0x03

        return data

    def request_telemetry_data(self, id):
        return self.param
```

This function can check the checksum on tc packets.
```python=
    @classmethod
    def checksum(cls,array):
        crc = 0
        for b in array:
            crc = cls.CRC8Table[crc ^ b]
        return crc
```


This is the main function of ADCSStateMachine. It manages bytearray UART frame from TCP. In ADCS doc the id byte is divided into two parts. 0-127 = telecommand, 128-255 = telemtry. So we find the id form UART frame. Firstly we need to check if the frame fullfile the ADCS UART frame schema and after if the check pass, we can request telemetry or execute a command. The Uart class can exctracte data from payload and create a uart frame to return.

```python=
    def request(self,packet:bytearray) -> bytearray:
        code = Uart.check_packet(packet)
        id = Uart.get_id(packet)
        data = Uart.get_data(packet)
        uart_packet = Uart(id)
        if code == 0:
           if id < 128:
                code = self.exec_command(id,data)
                return uart_packet.end_packet(code)
           else:
                tm = self.request_telemetry_data(id)
                return uart_packet.end_packet(tm)
        else:
            return Uart(id).end_packet(code)
```



### Uart
This class is in charge to decompose UART Frame from Tcp data. This class can build Uart frame to return data. it can check sum with the algorithm porvide by the ADCS documentation.


Static data for checksum algorithm. Check ADCS documentation.
```python=
    CRC_POLY = 0x91
    CRC8Table = [0] * 256

    for i in range(256):
        val = i
        for j in range(8):
            if val & 1:
                val ^= CRC_POLY
            val >>= 1
        CRC8Table[i] = val
```


Non-static function. This function create a UART frame, set data on end Frame. This frame is return to the Tcp server to be send. The frame needs an id to be construct

```python=
    def __init__(self,id):
        self.id=id
        self.create_return_packet()

    def create_return_packet(self) -> bytearray:
        self.packet = bytearray([0x1F, 0x7F, self.id])

    def end_packet(self,data):
        self.packet.append(data)
        return self.packet + bytearray([0x1F, 0xFF])
```

Static methode are used with a frame that is already in UART format. These frames came directly from tcp server.
We need to check if the frame is in the good format and verify the checksum if it is an tc frame. The return code is in function of the ADCS documentation.  if th code.

```python=
    @classmethod
    def check_packet(cls,packet:bytearray) -> int:
        code = 0x00
        if packet[0] == 0x1F and packet[1] == 0x7F:
            if packet[2] < 128:
                print('Telecommand packet')
                if packet[-3] == 0x1F and packet[-2] == 0xFF:
                    if cls.checksum(packet[:-1]) == packet[-1]:
                        print("Telecommand packet OK")
                    else:
                        print("CRC error")
                        code = 0x04
                else:
                    print("error in  Telecommand End message or Data byte bytes")
                    code = 0x02
            else:
                print('Telemetry packet')
                if packet[-2] == 0x1F and packet[-1] == 0xFF:
                    print("Telemetry packet OK")
                else:
                    print("error in  Telecommand End message or Data byte bytes")
        else:
            print("error in Start message or Data byte bytes")

        return code
```

### TcpServer
TcpServeur is a simple python tcp server. it runs on the main process and is blocking. The serveur accept connection and wait data. Once a packet is recieved, it is send to ADCSStatemachine. This server can only be connected to one client at a time and proceed one request at a time. A request always return data.

- Address = 127.0.0.1
- Port = 5005
- Buffer size = 2046

```python=
def runTCP(adcs):
    TCP_IP = '127.0.0.1'
    TCP_PORT = 5005
    BUFFER_SIZE = 2046

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    print(f"starting up on {TCP_IP} port {TCP_PORT}")
    s.bind((TCP_IP, TCP_PORT))
    s.listen(1)

    while 1:
        print("Waiting connexion")
        conn, addr = s.accept()
        try:
            print(f'Connection address: {addr} \n')
            while 1:
                data = conn.recv(BUFFER_SIZE)
                if data:

                    print(f"Data Received : {data}")
                    back_data = adcs.request(data)
                    print(f'sending data back to client : data {back_data}\n')
                    conn.send(back_data)
                else:
                    print(f"no data from {addr}")
                    break
        finally:
            conn.close()
```

This 2 static method can exracte the data of a UART frame and the data from a tc frame.
```python=
    @staticmethod
    def get_id(packet):
        return packet[2]

    @staticmethod
    def get_data(packet):
        return packet[3:-3]
```

## Driver
As we can see in design the driver is only composed of two ports. *send* to send data and when data are received from ADCS simulator *recv*. Data exchange are put into a buffer.

### FPrime XML
```xml=
<?xml version="1.0" encoding="UTF-8"?>

<component name="SocketTcpDriver" kind="passive" namespace="Drv" modeler="true">
    <import_port_type>Fw/Buffer/BufferSendPortAi.xml</import_port_type>
    <ports>

        <!-- Functional ports -->
        <port name="recv" data_type="Fw::BufferSend" kind="output" max_number="1">
        </port>

        <port name="send" data_type="Fw::BufferSend" kind="guarded_input" max_number="1">
        </port>
    </ports>
</component>
```

- recv is an output port
- send is a guarded_input. The server can only deal with one request at a time. so we need to lock the mutex to block the port whene proceeding with the simulator.

### Implementation

There is 2 classes in SocketTcpDriver implementation. The first on is the Driver itself and the second is the an helper. The helper manage all the TCP functionlities and the Driver class controles the driver.

#### SocketTcpHelper

```cpp=
        public:

            SocketTcpHelper();
            virtual ~SocketTcpHelper();
            SocketIpStatus configure(
                    const char* hostname,
                    const U16 port,
                    const U32 timeout_seconds,
                    const U32 timeout_microseconds
                    );
            bool isOpened(void);
            SocketIpStatus open(void);
            void send(U8* data, const U32 size, Fw::Buffer& back_data); //Forwards to sendto, which on some OSes requires a non-const data pointer
            void close(void);
```

This helper controle all the TCP activites on the socket. There are five methodes.
1. confiugre : configure the client tcp socket
2. isOpenend : return true if the connecton with tcp server is open
3. open : open the connection
4. send : send data to tcp server
5. close : close connection

The implementation of this function retake code from fprime/Drv/SocketIpDriver of fprime. It's modified to repsond to Tcp connection. It't a simple C++ tcp client that wait a reponse when command is sent.

### SocketTcpDriverImpl

We need know define the behaviour of your socket when the send port is trigger.

```cpp=
  void SocketTcpDriverComponentImpl ::
    send_handler(
        const NATIVE_INT_TYPE portNum,
        Fw::Buffer &fwBuffer
    )
  {
      U32 size = fwBuffer.getSize();
      U8* data = fwBuffer.getData();
      FW_ASSERT(data);
      this->m_helper.send(data,size,outBuffer);
      recv_out(0, outBuffer);
  }
```

As we can see in the above code there is no specific difficulties. The buffer is a class attribut because its data will be used outside the function. If we create a buffer in the functon core, the buffer will be destroyed at the end of the function and can't be use in recv_out port.

### test

The unit test is also test for simulator, several UART frame will be sent to ADCS simuator and behaviour will be checked.

#### test definition
```cpp=
      void testSendTM(void);
      void testSendTC(void);
      void testSendTCWrongCrc(void);
```

1. testSendTM : send a telemetry frame and check result
2. testSendTC : send a telecommand frame and check result
3. testSendTCWrongCrc : send a telecommand with wrong crc and check result


## ADCS Component
ADCS component is a more complexe component. It's a active componnent with port, commands ,telemetries and event

### FPrime XML

```xml=
<component name="ADCS" kind="active" namespace="App">
    <comment>Component that manage ADCS</comment>
     <import_port_type>Fw/Buffer/BufferSendPortAi.xml</import_port_type>
    <ports>
        <port name="DataOut" data_type="Fw::BufferSend" kind="output">
            <comment>
            Port for sending ADCS packet
            </comment>
        </port>
        <port name="DataIn" data_type="Fw::BufferSend" kind="async_input">
            <comment>
            Port for returning ADCS Packet
            </comment>
        </port>
    </ports>
    <commands>
        <command kind="async" opcode="0" mnemonic="MS_GET_TM">
            <comment>
            Send telemetry to ADCS
            </comment>
            <args>
                <arg name="id" type="U8">
                    <comment>The telemetry id</comment>
                </arg>                  
             </args>
        </command>
        <command kind="async" opcode="1" mnemonic="MS_SEND_CMD">
            <comment>
            Send CMD to ADCS
            </comment>
            <args>
                <arg name="id" type="U8">
                    <comment>The telecommand id</comment>
                 </arg>
                <arg name="payload" type="string" size="100">
                    <comment>The payload data</comment>
                </arg>                     
             </args>
        </command>
    </commands>
    <telemetry>
        <channel id="0" name="ADCS_LAST_TM_DATA" data_type="F64">
            <comment>
            Tm Value
            </comment>
        </channel>
                <channel id="1" name="ADCS_LAST_CMD" data_type="U8">
            <comment>
            Last Telecommand return
            </comment>
        </channel>
    </telemetry>
    <events>
        <event id="0" name="MS_TM_SEND_ADCS" severity="ACTIVITY_LO" format_string="TM id=%hu send to ADCS"  >
            <comment>
            Send Tm command
            </comment>
            <args>
                <arg name="id" type="U8">
                    <comment>The telemetry id</comment>
                </arg>
            </args>          
        </event>
        <event id="1" name="MS_TM_RECV_ADCS" severity="ACTIVITY_LO" format_string = "TM id=%hu recv from ADCS data: %f" >
            <comment>
            Received TM data
            </comment>
            <args>
                <arg name="id" type="U8">
                    <comment>The telemetry id</comment>
                </arg>
                <arg name="tm" type="F64">
                    <comment>The telemetry data</comment>
                </arg>          
            </args>
        </event>
        <event id="2" name="MS_TC_SEND_ADCS" severity="ACTIVITY_LO" format_string = "TC id=%hu send to ADCS data: %s" >
            <comment>
            Send TC data
            </comment>
            <args>
                <arg name="id" type="U8">
                    <comment>The telecommand id</comment>
                </arg>
                <arg name="payload" type="string" size="100">
                    <comment>The telecommand data</comment>
                </arg>          
            </args>
        </event>
        <event id="3" name="MS_TC_RECV_ADCS" severity="ACTIVITY_LO" format_string = "TC id=%hu recv from ADCS return : %hu" >
            <comment>
            Received TC data
            </comment>
            <args>
                <arg name="id" type="U8">
                    <comment>The telecommand id</comment>
                </arg>
                <arg name="tc" type="U8">
                    <comment>The telecommand return data</comment>
                </arg>          
            </args>
        </event>
        <event id="4" name="MS_TC_PAYLOAD_ERROR" severity="WARNING_LO" format_string = "Error in Commande id : %hu, payload : %s" >
            <comment>
            Payload Error
            </comment>
            <args>
                <arg name="id" type="U8">
                    <comment>The telecommand id</comment>
                </arg>
                <arg name="payload" type="string" size="100">
                    <comment>the payload error</comment>
                </arg>    
            </args>
        </event>
        <event id="5" name="MS_ID_ERROR" severity="WARNING_LO" format_string = "Error ADCS in TC or TM id : %hu" >
            <comment>
            Payload Error
            </comment>
            <args>
                <arg name="id" type="U8">
                    <comment>the id error</comment>
                </arg>    
            </args>
        </event>
    </events>
</component>
```

#### Ports
This composent has 2 ports to communicate with SocketTcpDriver. DataOut to send UART frame to Tcp driver and DataIn to receive UART Frame from driver. As we have a active component we can have async_input port. With async_input port, the port call will be executed on ADCS component.

#### Commands
There is 2 kind of commands for ADCS component. The first one allows to send telemetry request to ADCS or simulator. It takes in argument the telemetrie's id. The second one allows to send telecomand request to ADCS. it takes in telecommand is and payload in paramater. At this  moment the payload is a string it need to be modified when we use PUS protocole.

#### telemetries
There are two telemetries channel. On for the last telemetriy data return from the ADCS and one for the return code of command execution on ADCS.

#### events

Events are to system and user. In ADCS there are  different kind of event. 
1. Event to informe data is sent en receive
    2. Telemetry
    3. Telecommand
3. Event to informe about errors
    4. id error
    5. payload error

### Implementation

There is 2 classes in ADCS implementation. The first on is the component itself and the second is the helper. The helper contains checksum algorithm and the component class represent the ADCS.



### ADCSHelper

This class only contains functions to run the checksum algorithm. The algorithm is provided by ADCS documentation.
We pass a the buffer tc frame and its size to te function and it return the calculated crc.
```cpp=
    U8 ADCSHelper::getCrc(U8* buffer,NATIVE_UINT_TYPE len){
        if (len == 0) return 0xff;
        U8 crc = 0;
        for (U16 i = 2; i < len-3; i++)
        crc = CRC8Table[crc ^ buffer[i]];
        return crc;

    }
```

### ADCSComponentImpl

The role of this component is to get data from command, send formatted data to driver and pass return data through telemetry.

The following function manages the return data from simulator.

if the id correspond to a TC the MS_TC_RECV_ADCS is trigger and it sends the return payload to the telemetry 

#### DataIn port
```cpp=
  void ADCSComponentImpl ::
    DataIn_handler(
        const NATIVE_INT_TYPE portNum,
        Fw::Buffer &fwBuffer
    )
  {
    U8* packet = fwBuffer.getData();
    if(packet[2] < 128){
      log_ACTIVITY_LO_MS_TC_RECV_ADCS(packet[2],packet[3]);
      tlmWrite_ADCS_LAST_CMD(packet[3]);
    }else{
      log_ACTIVITY_LO_MS_TM_RECV_ADCS(packet[2],packet[3]);
      tlmWrite_ADCS_LAST_TM_DATA(packet[3]);
    }

  }
```

THis function manage a telemtry command request. First we check if the given id is a tm id. if not the MS_ID_ERROR event is trigger.
If not we use a static pre-built uart frame and set the given id. The event MS_SEND_TM is trigger and the buffer is sent to the driver.

#### Tm Cmmand
```cpp=
  void ADCSComponentImpl ::
    MS_GET_TM_cmdHandler(
        const FwOpcodeType opCode,
        const U32 cmdSeq,
        U8 id
    )
  {
    if(id < 128){
      log_WARNING_LO_MS_ID_ERROR(id);  
      this->cmdResponse_out(opCode,cmdSeq,Fw::COMMAND_EXECUTION_ERROR);
      return;
    }
    this->dataSendTmBuffer.getData()[2] = id;
    log_ACTIVITY_LO_MS_TM_SEND_ADCS(id);
    this->DataOut_out(0,dataSendTmBuffer);
    this->cmdResponse_out(opCode,cmdSeq,Fw::COMMAND_OK);
         
  }
```

#### TC command

for the telecommand, it is more compicated. the payload is given in a string and must be converted to byte array. 
so first we check if the given id is a telecomand id, if not the MS_ID_ERROR event is trigger.

then, we check if the payload is "null". In fprime it an obligation to provide a value for every command parameters. so if no data are require by the  ADCS null string can be provided.  if null we don't need to exced the current size of a basi tc packet (6). contrary of a data string payload, we need to double the stringe size and add it the tc basic size for the payload data. Indeed an hex value require a 2 char (00 => 0x00). Then, the hexdata byte array is created. with the calculated len. 
if the len is not 0 (we have a payload), the string is coverted into byte array. the isPayloadOK function check the strin integrity. it must have only hex char and a even size.
If the size is not ok the TC_PAYLOAD event is triggerd ans the COMMAND_EXECUTION_ERROR is return the the command dispatcher.

if the payload is ok, it is converted into byte array and send to the driver.

```cpp=
  void ADCSComponentImpl::
  MS_SEND_CMD_cmdHandler(
          const FwOpcodeType opCode, /*!< The opcode*/
          const U32 cmdSeq, /*!< The command sequence number*/
          U8 id, /*!< The telecommand id*/
          const Fw::CmdStringArg& payload /*!< The payload data*/
      )
      {
      if(id > 127){
        log_WARNING_LO_MS_ID_ERROR(id);  
        this->cmdResponse_out(opCode,cmdSeq,Fw::COMMAND_EXECUTION_ERROR);
        return;
    }
        const char* data = payload.toChar();
        int isPayload = strcmp(data,"null");
        Fw::LogStringArg eventLog(data);
        const NATIVE_UINT_TYPE len = (isPayload != 0) ? payload.length()/2 + SIZE_TC_PACKET : SIZE_TC_PACKET;
        U8 hexData[len]; 
        dataSendTcBuffer.setSize(len);
        if(isPayload != 0){
            if(!isPayloadOK(data,payload.length())){
                log_WARNING_LO_MS_TC_PAYLOAD_ERROR(id,eventLog);
                this->cmdResponse_out(opCode,cmdSeq,Fw::COMMAND_EXECUTION_ERROR);
                return;
            }
            stringToHex(data,hexData);
        }
        setTcPacket(hexData,id,len);
        dataSendTcBuffer.setData(hexData);
        this->DataOut_out(0,dataSendTcBuffer);
        log_ACTIVITY_LO_MS_TC_SEND_ADCS(id,eventLog);
        this->cmdResponse_out(opCode,cmdSeq,Fw::COMMAND_OK);
      }
```


There are 2 more methods.

setTcPacket set a tc UART frame. it prepars the frame to send for TC command.

Is payload ok check the integrity of the string passed in tc command parameter.

Then, at the end we can see the bre-built array for TM frames.

```cpp=
     void ADCSComponentImpl::setTcPacket(U8 hexData[],U8 id, NATIVE_UINT_TYPE len){
        hexData[0]=0x1F;
        hexData[1]=0x7f;
        hexData[2]=id;
        hexData[len-1]=0xFF;
        hexData[len-2]=0x1F;
        hexData[len-3]= adcsHelper.getCrc(hexData,len);
      }
    
    bool ADCSComponentImpl::isPayloadOK(const char* payload,NATIVE_UINT_TYPE len){
        if(len % 2 == 1){
          return false;
        }

      bool flag = true;

      for (int i=0; i<len; i++)
      {
          if (!isxdigit(payload[i]))
          {
              flag = false;
              break;
          }
      }
      
      return flag;

    }
  U8 ADCSComponentImpl::TmPacket[] = {0x1F,0x7F,0x00,0x1F,0xFF};
```

### topology
Just call this methods in addition of the basic FPrime topology function
This configures the address and port of the server and opne the connection

```cpp=
    socketTcpDriverADCS.configure("127.0.0.1",5005,1,0);
    socketTcpDriverADCS.openSocket();
    
```

For the Xml just looc the bellow code and refer to the design section.
```xml=
     <!-- ADCS connections-->

     <!-- Command Registration Ports - Registration port number must match dispatch port for each component -->

   <connection name = "ADCSReg">
        <source component = "ADCS" port = "CmdReg" type = "CmdReg" num = "0"/>
        <target component = "cmdDisp" port = "compCmdReg" type = "CmdReg" num = "25"/>
   </connection>
   
    <!-- Command Dispatch Ports - Dispatch port number must match registration port for each component -->

   <connection name = "ADCSDisp">
        <source component = "cmdDisp" port = "compCmdSend" type = "Cmd" num = "25"/>
        <target component = "ADCS" port = "CmdDisp" type = "Cmd" num = "0"/>
   </connection>
  
    <!-- Command Reply Ports - Go to the same response port on the dispatcher -->

   <connection name = "ADCSReply">
      <source component = "ADCS" port = "CmdStatus" type = "CmdResponse" num = "0"/>
      <target component = "cmdDisp" port = "compCmdStat" type = "CmdResponse" num = "0"/>
   </connection>

      <!-- Event Logger Binary Connections -->
   
   <connection name = "ADCSLog">
       <source component = "ADCS" port = "Log" type = "Log" num = "0"/>
        <target component = "eventLogger" port = "LogRecv" type = "Log" num = "0"/>
   </connection>

   <!-- Event Logger Text Connections -->
   
   <connection name = "ADCSTextLog">
       <source component = "ADCS" port = "LogText" type = "LogText" num = "0"/>
        <target component = "textLogger" port = "TextLogger" type = "LogText" num = "0"/>
   </connection>



<connection name = "ADCSTcpDriver">
       <source component = "ADCS" port = "DataOut" type = "Fw::BufferSend" num = "0"/>
        <target component = "socketTcpDriverADCS" port = "send" type = "Fw::BufferSend" num = "0"/>
</connection>

<connection name = "TcpADCSDriver">
       <source component = "socketTcpDriverADCS" port = "recv" type = "Fw::BufferSend" num = "0"/>
        <target component = "ADCS" port = "DataIn" type = "Fw::BufferSend" num = "0"/>
</connection>
```
### test

The test cover all the possible mistakes and good behavior of the component

#### test definition
```cpp=
      void testCmdTMBadId(void);
      void testCmdTMGoodId(void);
      void testCmdTCBadId(void);
      void testCmdTCGoodIdPayload(void);
      void testCmdTCBadPayloadData(void);
      void testCmdTCBadPayloadSize(void);
      void testPortTM(void);
      void testPortTC(void);
```





1. testCmdTMBadId: Sends a TM command with a bad id.
2. testCmdTMGoodId: Sends a TM command with the good id.
3. testCmdTCBadId: Sends a TC command with a bad id.
4. testCmdTCGoodIdPayload: Sends a TC command with a good id and payload
5. testCmdTCBadPayloadData: Sends a TC command with a bad payload.
6. testCmdTCBadPayloadSize: Sends a TC command with a payload with bad size
7. testPortTM: Triggers the DataIn port with a TM packet.
8. testPortTC: Triggers the DataIn port with a TC packet.






