###### tags: `FS`
# EPS code guide
## Index
[TOC]
## Analyze documentation
This analyze can be found in the report
## Design
The EPS component, SocketCspIpDriver and simulator design can be found in the report
## Simulator
### CspMain
It is the entry of the eps simulator, it init the csp server.
```python=
if __name__ == '__main__':
eps = EPSStateMachine()
print("state machine build")
print(eps.current_state)
print(f"eps volt : {eps.volt}")
print("create connexion")
cspServer.init(eps,27)
```
Here nothing needs to be change expect the csp address. Here:
- CSP address: 27
If you want to reuse the CSP server for an other simulator, you need to provide a object that implement a request function. this function must take the payload byte arreay in parameter. Here EPSStateMachin implements the request function
firstly we create the EPS state machine with:
```python=
eps = EPSStateMachine()
```
after the state machine is created, we start the csp serveur and pass the eps to it. this server need a initalisation phase, this init function will launch the server.:
```python=
cspServer.init(eps,27)
```
Then, the eps object will manage data that come from csp server
## CspServer
A csp server like a tcp server but a little bit different. Indeed, csp must communicate on a bus but we can you a tcp connection to send csp packet with a zmq interface.
We must include libcsp library into the python process. when libcsp is compiled, it provids a dynamic library to use lobcsp in python. it is necessary to set an envrionment variable to tell python where is the library
```bash=
export LD_LIBRARY_PATH=./simulators/EPS/packages/csp/lib
```
```python=
import packages.csp.lib.libcsp_py3 as libcsp
```
In the initalisation phase, we init libcsp with its address here 27. We need to set a zmq interface to communicate on the network with tcp. So we zmqhub_init make the bridge between the server address 27 and the 127.0.0.1 loopback address. Then, we create a simple route table with the zmq hub to redirect the tcp packet on the server address. and at the we start the route table task.
The server is lauched in a thread.
```python=
# init csp
libcsp.init(address, "test_service", "bindings", "1.2.3", 10, 300)
libcsp.zmqhub_init(27, "localhost")
libcsp.rtable_set(0, 0, "ZMQHUB")
libcsp.route_start_task()
print("Hostname: %s" % libcsp.get_hostname())
print("Model: %s" % libcsp.get_model())
print("Revision: %s" % libcsp.get_revision())
print("Routes:")
libcsp.print_routes()
# start CSP server
threading.Thread(target=csp_server,args=(eps,)).start()
```
then, we create a socket and bind it to accepet any csp protcol. the we open the connection with listen. the 10 say that we can have 10 connection at a time in parallel.
```python=
sock = libcsp.socket()
libcsp.bind(sock, libcsp.CSP_ANY)
libcsp.listen(sock, 10)
```
The server is infinite loop. it will wait on a connection with libcp.accepet function. Someinformation will be print
```python=
while True:
# wait for incoming connection
print("Waiting connexion")
conn = libcsp.accept(sock, libcsp.CSP_MAX_TIMEOUT)
if not conn:
continue
print ("connection: source=%i:%i, dest=%i:%i" % (libcsp.conn_src(conn),
libcsp.conn_sport(conn),
libcsp.conn_dst(conn),
libcsp.conn_dport(conn)))
```
Then we will read the incoming data. if no data are still available, we broke infinte loop and wait on a new connection.
libcsp is direct by the port. The socket is only liked with an address and not a port like in tcp. so the port can trigger different functionality. Here we only manage the port 7 the other ports can have current functinality. when the port 7 is trigger, we pass the payload data to the eps state machine. This state machine implement a request function that take byte array payload data in parameter. To reuse the server only pass an object with this function in server parameter.
To send back data we need to set a buffer. We fill this buffer with te return data and send it back to the eps.
```python=
while True:
# Read all packets on the connection
packet = libcsp.read(conn, 100)
if packet is None:
break
if libcsp.conn_dport(conn) == 7:
# print request
data = bytearray(libcsp.packet_get_data(packet))
length = libcsp.packet_get_length(packet)
print ("got packet, len=" + str(length) + ", data=" + ''.join('{:02x}'.format(x) for x in data))
# send reply
back_data = eps.request(data)
print(f"volt : {back_data[26]}")
print(f'sending data back to client : data {back_data}\n')
reply = libcsp.buffer_get(1)
libcsp.packet_set_data(reply, back_data)
libcsp.sendto_reply(packet, reply, libcsp.CSP_O_NONE)
else:
# pass request on to service handler
libcsp.service_handler(conn, packet)
```
### EPSStateMachine
THis not a real state machine for know. it respect the stat in documentation but for new it does nothing.
We have 3 simulated parameter, the voltage, the temperature and the battry mode.
```python=
class EPSStateMachine(StateMachine):
working = State('working')
default = State('Default', initial=True)
ground = State('Ground')
factory = State('Factory')
working_to_default = working.to(default)
working_to_ground = working.to(ground)
working_to_factory = working.to(factory)
default_to_working = default.to(working)
default_to_ground = default.to(ground)
default_to_factory = default.to(factory)
ground_to_default = ground.to(default)
ground_to_working = ground.to(working)
ground_to_factory = ground.to(factory)
factory_to_default = factory.to(default)
factory_to_ground = factory.to(ground)
factory_to_factory = factory.to(working)
volt = 10
temp = 50
mode = 2
```
At the moment, the state machine just switch voltage. The byte position of the data respect the eps documentation. we always return a good status. The otehr data are for now set to 0. This return a standatd telemetry packet to the driver.
```python=
def request(self, data:bytearray):
self.volt = 10 if self.volt == 11 else 11
back_data = bytearray(232)
back_data[0] = data[0]
back_data[1] = 0
back_data[26] = self.volt
back_data[206] = self.temp
back_data[226] = self.mode
return back_data
```
In this simulator we don't need to verify integrity of frame beacuse csp does it for us
## Driver
As we can see the driver as only 2 ports. *send* to send data and when data are received from ADCS simultor *recv*.Data exchange are put into a buffer array.
### Fprime xml
```xml=
<?xml version="1.0" encoding="UTF-8"?>
<component name="SocketCspIpDriver" kind="passive" namespace="Drv" modeler="true">
<import_port_type>App/EPS/EPSPort/EPSPortAi.xml</import_port_type>
<ports>
<!-- Functional ports -->
<port name="recv" data_type="App::EPSCmd" kind="output" max_number="1">
</port>
<port name="send" data_type="App::EPSCmd" kind="guarded_input" max_number="1">
</port>
</ports>
</component>
```
- recv is an output port
- send is a guarded_input port. The server can only deal with one request. so we need to lock a mutex to block the port when proceeding with th simulator
### Implementation
### SocketCspIpHelper
This the class that manage the connection and to send and reciev data on csp.
Every error message comes form the csp config or the Config of csp in the config directory
First we configure the csp stack configruation. this step is hard, an error can have a high consequence.
We need a csp_config. we use the defautl configue. for more information about config look the doc. we set the client address here 10 to this config.
then we init csp with csp_init and your cconfiguration in parameter.
Then, we start the router with csp_rout_task.
As we communicate on the network, we need to set a zmq interface. The function csp_zmqhub_init make the bridge between the csp address and le ip loopback address.
After that, we start set the route table with this new interface.
Make a sleep of 1 seconde to let the csp system and route table be initalized. If you don't make this break csp could not work.
```cpp=
SocketCspStatus SocketCspIpHelper::configure(I32 address,const char* zmqHost){
(void)strncpy(this->m_zmq_host,zmqHost,CSP_MAX_HOSTNAME_SIZE);
csp_conf_get_defaults(&csp_conf);
csp_conf.address = address;
error = csp_init(&csp_conf);
if (error != CSP_ERR_NONE) {
return CSP_SOCK_INIT_FAILED;
}
csp_route_start_task(500, 0);
default_iface = NULL;
error = csp_zmqhub_init(csp_get_address(), "localhost", 0, &default_iface);
if (error != CSP_ERR_NONE) {
return CSP_SOCK_INTERFACE_ZMQ_FAILED;
}
csp_rtable_set(CSP_DEFAULT_ROUTE, 0, default_iface, CSP_NO_VIA_ADDRESS);
csp_sleep_ms(1000);
return CSP_SOCK_SUCCESS;
}
```
We can use csp with connection or without.. The following function create a conncetion. it set the simulator address 27 and open a connection. this connection won't be used because we send CSP udp. for now it is just use to set the simulator address in the inner class variable m_address
```cpp=
SocketCspStatus SocketCspIpHelper::open(I32 server_address){
m_address = server_address;
int ping = csp_ping(server_address, 1000, 100, CSP_O_NONE);
if(ping < 0){
return CSP_SOCK_CONNNECT_FAILED;
}
Fw::Logger::logMsg("Connected to %u for %s using %s\n", server_address,
reinterpret_cast<POINTER_CAST>("EPS"),
reinterpret_cast<POINTER_CAST>("UDP"));
conn = csp_connect(CSP_PRIO_NORM,server_address, CSP_UDP_PORT, 1000, CSP_O_NONE);
if (conn == NULL) {
return CSP_SOCK_CONNNECT_FAILED;
}
return CSP_SOCK_SUCCESS;
}
```
Now, we can send data to the simulator. We have in parameter the trageted port, the data byte arrey, this size and the buffer for the back data.
csp_transaction send the byte array data to the sepcific port at the simulator address. It then wait for a reply.
we set the back data size to -1 because we don't know the back data size. The function will wait 1 second = CSP_TIMEOUT for the back data. the returned data are put into the bacl data buffer
```cpp=
SocketCspStatus SocketCspIpHelper::send_transaction(U8 port,U8* data, const U32 size, Fw::Buffer& back_data){
/* 5. Send packet */
I32 returnSize = csp_transaction(CSP_PRIO_NORM,m_address,port,CSP_TIMEOUT,data,size,back_data.getData(),-1);
if (!returnSize){
return CSP_SOCK_SEND_FAILED;
}
back_data.setSize(returnSize);
return CSP_SOCK_SUCCESS;
}
```
This function just use the csp_ping function to send a pin and return the result.
```cpp=
U32 SocketCspIpHelper::ping(void){
return csp_ping(m_address, CSP_PING_TIMEOUT, CSP_PING_SIZE, CSP_O_NONE);
}
```
### SocketCspIpDriver
This function handle the send in port. When the received ping correspond to a ping port, the ping function of the SocketCspIpHelper is called. The function return a time in millisecond. we can only transfer a byte array to te EPS component so the mllisecond time is transformed into a 4 bytes array in big endian with the function "intToU8BigEndian", put into the buffer and return to EPS. If the port is not a Ping port, the byte array data are send to teh simulator with to "send_transaction" function seen in the last chapter and return the back_data buffer.
```cpp=
void SocketCspIpDriverComponentImpl ::
send_handler(
const NATIVE_INT_TYPE portNum,
U8 port,
Fw::Buffer &data,
U8 isSched
)
{
U32 sizeBuffer = data.getSize();
U8* dataBuffer = data.getData();
if(port == (U8)CSP_PING_PORT){
I32 pingMs = m_helper.ping();
outBuffer.setSize(4);
intToU8BigEndian(pingMs,outBuffer.getData());
}else{
FW_ASSERT(dataBuffer);
this->m_helper.send_transaction(port,dataBuffer,sizeBuffer,outBuffer);
}
recv_out(0, port, outBuffer,isSched);
}
```
### test
The tests are the test for simulator and driver. It try to cover the maximum possiblity.
1. testSendCmdPort7: tries to send a telemetry request to the EPS python simulator and check the result.
2. testSendSchedPort7: tries to send a telemetry request with sched on to the EPS simulator and checks the result.
3. testSendingPing: tries sending a ping request to simulator and checks the result.
```cpp=
void testSendCmdPort7(void);
void testSendSchedPort7(void);
void testSendPing(void);
```
## Port
To communicate between EPS and Driver, we use a custom port. The port "EPSCmd" must have serilaized value.
The EPSCmd port contain:
- port: the csp port to send to data
- data: data buffer data contain the payload data
- isSched: to know if the function come from the EPS scheduler port or not.
This port is used in every communication between EPS
and SocketCspIpDriver.
```xml=
<interface name="EPSCmd" namespace="App">
<include_header>Fw/Buffer/Buffer.hpp</include_header>
<comment>
Port to send csp packet to eps
</comment>
<args>
<arg name="port" type="U8">
</arg>
<arg name="data" type="Fw::Buffer" pass_by="reference">
</arg>
<arg name="isSched" type="U8">
</arg>
</args>
</interface>
```
## EPS Component
### EpsHkAnalyzer
This analyzer decode the telemetry frame retrun by EPS. Don't forget that data are in little endian in eps return data. When the data is one byte long any conversion are needed but when it is more th little endian byte must be convert to cpp int variable.
The to16LittleEndian and to32LittleEndian convert respectively 2 bytes and 4 bytes little endian into shot int and int.
```cpp=
EPSHkAnalyser::EPSHkAnalyser(Fw::Buffer &data) {
this->payload = data.getData();
this->size = data.getSize();
}
EPSHkAnalyser::~EPSHkAnalyser(){
}
U8 EPSHkAnalyser::getCmd(){
return this->payload[EPSCMD];
}
I8 EPSHkAnalyser::getStatus(){
return this->payload[EPSSTATUS];
}
U16 EPSHkAnalyser::getvBatt(){
return this->to16LittleEndian(EPSVBATT);
}
U8 EPSHkAnalyser::getbattMode(){
return this->payload[EPSBATTMODE];
}
U16 EPSHkAnalyser::getTemp(){
return this->payload[EPSTEMP];
}
U16 EPSHkAnalyser::to16LittleEndian(U16 start){
return (U16)this->payload[start+1] << 8 | (U16)this->payload[start];
}
U32 EPSHkAnalyser::to32LittleEndian(U16 start){
return (U32)this->payload[start+3] << 24 | (U32)this->payload[start+2] << 16
| (U32)this->payload[start+1] << 8 | (U32)this->payload[start];
}
} // namespace App
```
The position of the data are set into the EPSCfg.hpp. Fata found in EPS doc.
```cpp=
enum IndexEPSTLM {
EPSCMD = 0,
EPSSTATUS = 1,
EPSVBATT = 26,
EPSTEMP = 206,
EPSBATTMODE = 226
};
```
### EPSComponent
#### Ports
There are 5 ports.
two output port
- DataOut: send data to the driver
- Pingout: send the ping out to say that everythingis ok
three input port
- DataIn port: async input so executed on the component thread. It receive data from the driver
- SchedIn: receive the tick from activerategroup every second. must be sync. the active raregroupe execute the function
- PingIn: in port to ping the component. asyn_input. the health component won't execute te function it is not ti task.
#### command
there is only one command. In this command there is two parameter. One to select the targeted port and one for the payload. The targeted port is an enum. the conversion into port is odne in the EPSCOmponent.
#### telemetry
There is 4 telemetry channels. EPS has important value such as battery voltage, its mide, or its temp. THis value have their own telemetry channle.
The last telemetry channel is for the return data of the command
#### events
There is 8 event to manage all the events on the EPS. read the following xml for more details in events.
```xml=
<component name="EPS" kind="active" namespace="App">
<comment>Component that manage ADCS</comment>
<import_port_type>App/EPS/EPSPort/EPSPortAi.xml</import_port_type>
<import_port_type>Fw/Buffer/BufferSendPortAi.xml</import_port_type>
<import_port_type>Svc/Sched/SchedPortAi.xml</import_port_type>
<import_port_type>Svc/Ping/PingPortAi.xml</import_port_type>
<ports>
<port name="DataOut" data_type="App::EPSCmd" kind="output">
<comment>
Port for sending EPS packet
</comment>
</port>
<port name="DataIn" data_type="App::EPSCmd" kind="async_input">
<comment>
Port for returning EPS Packet
</comment>
</port>
<port name="Schedin" data_type="Sched" kind="sync_input">
<comment>
The rate groupe scheduler input
</comment>
</port>
<!-- Output ping port -->
<port name="PingIn" data_type="Svc::Ping" kind="async_input" max_number="1">
<comment>
The ping input port
</comment>
</port>
<!-- Output ping port -->
<port name="PingOut" data_type="Svc::Ping" kind="output" max_number="1">
<comment>
The ping input port
</comment>
</port>
</ports>
<commands>
<command kind="async" opcode="0" mnemonic="MS_SEND_CMD">
<comment>
Send CMD to EPS
</comment>
<args>
<arg name="operation" type="ENUM">
<enum name="EPSOp">
<item name="EPS_DIRECT_CONTROL"/>
<item name="EPS_CONF_MGMT"/>
<item name="EPS_RESET_PORT"/>
<item name="EPS_INST_TLM"/>
<item name="EPS_TIME_SYNC"/>
</enum>
<comment>operation argument</comment>
</arg>
<arg name="payload" type="string" size="1000">
<comment>The payload data</comment>
</arg>
</args>
</command>
</commands>
<telemetry>
<channel id="0" name="EPS_Voltage_battery_mV" data_type="U16">
<comment>
Voltage
</comment>
</channel>
<channel id="1" name="EPS_Battery_mode" data_type="U8">
<comment>
Battery mode
</comment>
</channel>
<channel id="2" name="EPS_Temp_Battery_Celsuis" data_type="U16">
<comment>
Battery Temp
</comment>
</channel>
<channel id="3" name="EPS_LAST_CMD_RETURN" data_type="string" size="1000">
<comment>
Battery mode
</comment>
</channel>
</telemetry>
<events>
<event id="0" name="MS_CMD_SEND_EPS" severity="ACTIVITY_LO" format_string = "Commande send to EPS : port = %hu, cmd = %hu, data: %s" >
<comment>
Send cmd data
</comment>
<args>
<arg name="port" type="U8">
<comment>The port id</comment>
</arg>
<arg name="cmd" type="U8">
<comment>The cmd id</comment>
</arg>
<arg name="payload" type="string" size="1000">
<comment>The command data</comment>
</arg>
</args>
</event>
<event id="1" name="MS_CMD_RECV_EPS" severity="ACTIVITY_LO" format_string = "Commande return from EPS : port = %hu, cmd = %hu, data: %s" >
<comment>
Received cmd data
</comment>
<args>
<arg name="port" type="U8">
<comment>The port id</comment>
</arg>
<arg name="cmd" type="U8">
<comment>The command id</comment>
</arg>
<arg name="payload" type="string" size="3000">
<comment>The command return data</comment>
</arg>
</args>
</event>
<event id="2" name="MS_CMD_ERROR" severity="WARNING_LO" format_string = "Error in cmd , port : %hu, cmd : %hu, status : %hu" >
<comment>
cmd Error
</comment>
<args>
<arg name="port" type="U8">
<comment>The port id</comment>
</arg>
<arg name="cmd" type="U8">
<comment>the cmd error</comment>
</arg>
<arg name="status" type="U8">
<comment>the status error</comment>
</arg>
</args>
</event>
<event id="3" name="MS_BATT_VOLT_LOW" severity="WARNING_HI" format_string = "Warning battery voltage low : %hu mV" >
<comment>
low voltage
</comment>
<args>
<arg name="volt" type="U16">
<comment>Voltage</comment>
</arg>
</args>
</event>
<event id="4" name="MS_BATT_TEMP_HIGH" severity="WARNING_HI" format_string = "Warning battery temperature HIGH : %hu celsius" >
<comment>
High voltage
</comment>
<args>
<arg name="temp" type="U8">
<comment>the id error</comment>
</arg>
</args>
</event>
<event id="5" name="MS_CHNG_BATT_MOD" severity="WARNING_HI" format_string = "Battery mode change : %hu" >
<comment>
Payload Error
</comment>
<args>
<arg name="mode" type="U8">
<comment>battery mode</comment>
</arg>
</args>
</event>
<event id="6" name="MS_CMD_PAYLOAD_ERROR" severity="WARNING_LO" format_string = "Error in EPS command : payload : %s" >
<comment>
Payload Error
</comment>
<args>
<arg name="payload" type="string" size="1000">
<comment>the payload error</comment>
</arg>
</args>
</event>
<event id="7" name="MS_CMD_PORT_ERROR" severity="WARNING_LO" format_string = "Error in EPS command : port : %s" >
<comment>
Payload Error
</comment>
<args>
<arg name="port" type="U8">
<comment>The port id</comment>
</arg>
</args>
</event>
</events>
</component>
```
### impementation
#### PingIn port
This port manage the ping from the Health component. ping is sending with a key. If the ping is okay the key must be return to the Health component. so the key is save in a object variable "m_key" and a ping is sent to the EPS simulator. No data are put in the buffer. just call the DataOut port with the CSP_PING_PORT
```cpp=
void EPSComponentImpl ::
PingIn_handler(
const NATIVE_INT_TYPE portNum,
U32 key
)
{
U8 noData[0];
m_key = key;
dataSendCmdBuffer.setData(noData);
dataSendCmdBuffer.setSize(0);
this->DataOut_out(0,CSP_PING_PORT,dataSendCmdBuffer,0);
}
```
The port handle the back data from EPS simulator. Mor explanation can be found in the report. But globaly it will check the port. if it is a ping, it reploy the the HEalth component with the key in object variable. the buffer data must be converted to an int befor checking if the ping from simulator is okay.
if it is not a ping the data status is checked. if not okay a MS_CDM_ERROR event is triggered. Then we check if the command came from the scheduler and trigger the telemetry and events according to it.
When it came from the scheduler, the important telemetry data must be checked and an event is triggerd if criticlal value is detected or the mode change. The mode is save in a object variable.
The telemetry are parse with the EspHkAnaylzer
#### DataIn port
```cpp=
void EPSComponentImpl ::
DataIn_handler(
const NATIVE_INT_TYPE portNum,
U8 port,
Fw::Buffer &data,
U8 isSched
)
{
U8* returnData = data.getData();
U32 returnSize = data.getSize();
U8 status = returnData[EPSSTATUS];
if(port == (U8)CSP_PING_PORT){
U32 ping = bytesToInt(returnData);
if((I32)ping != -1){
PingOut_out(0,m_key);
}
return;
}
if(status){
log_WARNING_LO_MS_CMD_ERROR(port,returnData[EPSCMD],returnData[EPSSTATUS]);
}else{
if(isSched){
EPSHkAnalyser helper(data);
U8 mode = helper.getbattMode();
U16 voltage = helper.getvBatt();
U16 temp = helper.getTemp();
tlmWrite_EPS_Battery_mode(mode);
tlmWrite_EPS_Voltage_battery_mV(voltage);
tlmWrite_EPS_Temp_Battery_Celsuis(temp);
if(mode != this->battMode){
log_WARNING_HI_MS_CHNG_BATT_MOD(mode);
}
if(voltage < EPS_LIMIT_LOW_BATTERY){
log_WARNING_HI_MS_BATT_VOLT_LOW(voltage);
}
if(temp > EPS_LIMIT_TEMP_HIGH){
log_WARNING_HI_MS_BATT_TEMP_HIGH(temp);
}
}else{
if(port == EPS_INST_TLM_PORT){
char returnString[returnSize*3];
hexToString(returnString,returnData,returnSize);
Fw::LogStringArg logString(returnString);
log_ACTIVITY_LO_MS_CMD_RECV_EPS(port,returnData[EPSCMD],logString);
Fw::TlmString tlmString(returnString);
this->tlmWrite_EPS_LAST_CMD_RETURN(tlmString);
}
}
}
}
```
This is the critical value set in EPSCfg.hpp
```cpp=
enum EPSParam{
EPS_LIMIT_LOW_BATTERY = 10,
EPS_LIMIT_TEMP_HIGH = 100
};
```
#### Schedin port
This port is triggered by an ActiveRateGroup every second. it will asked telemetry data to the EPS. Here we must put to true the Sched boolean in DataOut port. The payload data 0x00 just say the command id for the telemetry data and the EPS_INST_TLM_PORT the telemetry port
```cppp
void EPSComponentImpl ::
Schedin_handler(
const NATIVE_INT_TYPE portNum,
NATIVE_UINT_TYPE context
)
{
dataSendCmdBuffer.setSize(1);
U8 hexData[] = {0x00};
dataSendCmdBuffer.setData(hexData);
this->DataOut_out(0,EPS_INST_TLM_PORT,dataSendCmdBuffer,1);
}
```
#### Command handler
This is the function that manage command.
Globally. It try to parse the payload string. if fail trigger and error event.
Then, the enzm is converted into CSP port and the data are sent to the driver.
```cpp=
void EPSComponentImpl ::
MS_SEND_CMD_cmdHandler(
const FwOpcodeType opCode,
const U32 cmdSeq,
EPSOp operation,
const Fw::CmdStringArg& payload
)
{
const char* data = payload.toChar();
Fw::LogStringArg eventLog(data);
if(!isPayloadOK(data,payload.length())){
log_WARNING_LO_MS_CMD_PAYLOAD_ERROR(eventLog);
this->cmdResponse_out(opCode,cmdSeq,Fw::COMMAND_EXECUTION_ERROR);
return;
}
const NATIVE_UINT_TYPE len = payload.length()/2;
U8 hexData[len];
stringToHex(data,hexData);
dataSendCmdBuffer.setSize(len);
dataSendCmdBuffer.setData(hexData);
U8 port;
switch(operation){
case EPS_DIRECT_CONTROL:
port = EPS_DIRECT_CONTROL_PORT;
break;
case EPS_CONF_MGMT:
port = EPS_CONF_MGMT_PORT;
break;
case EPS_RESET_PORT:
port = EPS_RESET_PORT_PORT;
break;
case EPS_INST_TLM:
port = EPS_INST_TLM_PORT;
break;
case EPS_TIME_SYNC:
port = EPS_TIME_SYNC_PORT;
break;
default:
this->cmdResponse_out(opCode,cmdSeq,Fw::COMMAND_EXECUTION_ERROR);
log_WARNING_LO_MS_CMD_PORT_ERROR(operation);
return;
break;
}
this->DataOut_out(0,port,dataSendCmdBuffer,0);
Fw::LogStringArg logString(data);
log_ACTIVITY_LO_MS_CMD_SEND_EPS(port,hexData[EPSCMD],logString);
this->cmdResponse_out(opCode,cmdSeq,Fw::COMMAND_OK);
}
```
The port convesion is done with Data in EpsConfig.hpp
```cpp=
enum EPSPort{
EPS_DIRECT_CONTROL_PORT = 14,
EPS_CONF_MGMT_PORT = 15,
EPS_RESET_PORT_PORT = 16,
EPS_INST_TLM_PORT = 7,
EPS_TIME_SYNC_PORT = 8
};
```
#### more function
- stringToHex: converts payload string int byte array
- heyToString: converts byte array into string
- isPayloadOk: checks if the payload string contains only hex charactr and has an even size
- bytesToInt: converts a 4 bytes long byte array into unsigned int.
```cpp=
oid EPSComponentImpl::stringToHex(const char* data, U8 hexArray[]){
for (size_t count = 0; count < sizeof data/sizeof *data; count++) {
sscanf(data, "%2hhx", &hexArray[count]);
data += 2;
}
}
void EPSComponentImpl::hexToString (char* string, U8 hex[], U16 size){
char* endofbuf = string + size*3*sizeof(char);
for (int i = 0; i < size; i++)
{
if (string + 3 < endofbuf)
{
if (i > 0)
{
string += sprintf(string, ":");
}
string += sprintf(string, "%02X", hex[i]);
}
}
}
bool EPSComponentImpl::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;
}
U32 EPSComponentImpl::bytesToInt (U8* bytes){
return (U32)bytes[0] << 24 | (U32)bytes[1] << 16
| (U32)bytes[2] << 8 | (U32)bytes[3];
}
```
#### topology
Just call this methids in addition of the basic FPrime topology function. This configures the cps address of the client add set the address of the server
- confgure: set the adress of the client in flight software here 10
- opensocket: open the connection with the serveur: server address 27.
```cpp=
socketCspIpDriverEPS.configure(10,"localhost");
socketCspIpDriverEPS.openSocket(27);
```
For the xml just look the below code and refer to the design section
```xml=
<!-- EPS connections-->
<!-- Command Registration Ports - Registration port number must match dispatch port for each component -->
<connection name = "EPSReg">
<source component = "EPS" port = "CmdReg" type = "CmdReg" num = "0"/>
<target component = "cmdDisp" port = "compCmdReg" type = "CmdReg" num = "26"/>
</connection>
<!-- Command Dispatch Ports - Dispatch port number must match registration port for each component -->
<connection name = "EPSDisp">
<source component = "cmdDisp" port = "compCmdSend" type = "Cmd" num = "26"/>
<target component = "EPS" port = "CmdDisp" type = "Cmd" num = "0"/>
</connection>
<!-- Command Reply Ports - Go to the same response port on the dispatcher -->
<connection name = "EPSReply">
<source component = "EPS" port = "CmdStatus" type = "CmdResponse" num = "0"/>
<target component = "cmdDisp" port = "compCmdStat" type = "CmdResponse" num = "0"/>
</connection>
<!-- Event Logger Binary Connections -->
<connection name = "EPSLog">
<source component = "EPS" port = "Log" type = "Log" num = "0"/>
<target component = "eventLogger" port = "LogRecv" type = "Log" num = "0"/>
</connection>
<!-- Event Logger Text Connections -->
<connection name = "EPSTextLog">
<source component = "EPS" port = "LogText" type = "LogText" num = "0"/>
<target component = "textLogger" port = "TextLogger" type = "LogText" num = "0"/>
</connection>
<!-- Telemetry Connections -->
<connection name = "EPSTlm">
<source component = "EPS" port = "Tlm" type = "Tlm" num = "0"/>
<target component = "chanTlm" port = "TlmRecv" type = "Tlm" num = "0"/>
</connection>
<!-- Time Connections -->
<connection name = "EPSTime">
<source component = "EPS" port = "Time" type = "Time" num = "0"/>
<target component = "linuxTime" port = "timeGetPort" type = "Time" num = "0"/>
</connection>
<connection name = "EPSCspDriver">
<source component = "EPS" port = "DataOut" type = "App::EPSCmd" num = "0"/>
<target component = "socketCspIpDriverEPS" port = "send" type = "App::EPSCmd" num = "0"/>
</connection>
<connection name = "CspEPSDriver">
<source component = "socketCspIpDriverEPS" port = "recv" type = "App::EPSCmd" num = "0"/>
<target component = "EPS" port = "DataIn" type = "App::EPSCmdd" num = "0"/>
</connection>
```
### test
The test cover all the possible mistakes and good behaviour of the component.
```cpp=
void cmdCmdGoodpayloadGoodPort(void);
void cmdCmdbadPayload(void);
/*void cmdCmdbadPort(void);*/
void portStatusOKSched(void);
void portStatusOKNoSched(void);
void portStatusNOK(void);
void portTestLowVolt(void);
void portTestHighTemp(void);
void portChangeMode(void);
void portPingIn(void);
void portPingOk(void);
void portPingNOK(void);
```
1. cmdCmdGoodpayloadGoodPort: tries to trigger EPS command with different good payloads.
2. cmdCmdbadPalyoad: tries to trigger EPS command with different bad payloads.
3. portStatusOKNoSched: Triggers the DataIn port with no sched and good data.
4. portStatusOkSched. Triggers the DataIn port with sched and good data.
5. portStatusNOK: triggers the data with a bad status.
6. portChangeMode: triggers the data with sched and with a changing batt mode.
7. portTestLowVolt: triggers the data with sched and a critical battery voltage.
8. portTestHighTemp: triggers the data with sched and a critical battery temperature.
9. portPingIn: triggers the PingIn port and tests sending values.
10. portPingOk: triggers the DataIn port with a good ping request.
11. portPingNOK: triggers DataIn port with bad ping request.