---
# System prepended metadata

title: RC - Overview do código

---

# RC - Overview do código

## Receiver 

### Topo do ficheiro
- Macros definidos incialmente, que já vêm dos ficheiros que forneceram 
- Macros de degub BYTE_TO_BINARY(para ser mais fácil dar display-não fazem nada)
- STOP é uma variável global que é usada para os loops de espera de resposta

### Main
- Main começa com validação de argumentos da linha de comandos e os passos gerais de tratar a serial port (que já é fornecido pelo código base)
- chama as funções que devem ser feitas no trabalho llopen, llread, llclose e depois volta a colocar a porta de série nos valores default e fecha a conexão


### llOpen 
- Chama ```setProtocol```, que basicamente faz um loop infinito até conseguir ler o SET que vem do lado do emissor. 
- O ```readSet``` faz um loop de leitura. Basicamente todos os autómatos de verificação que eles têm no trabalho funcionam de uma maneira semelhante 
    - Começa com o stop e flag a falso, e lê byte a byte e depois começa uma cadeia de ifs seguindo a lógica do pacote (neste caso o SET), a cada if que acerta vai lendo mais um byte até confirmar o pacote inteiro (quando chega a uma nova flag), e para o loop. Caso falhe a meio dá erro, salta os outros ifs e verifica se o que está atualmente no buffer é uma flag (imaginando que o envio foi mal feito/perdeu conexão e já voltou a enviar outro SET), assim não precisa de fazer o read inicial do loop e pode ir logo para a cadeia de ifs.
    - Fico na dúvida se não precisava de um timeout aqui, mas acho que não é necessário.


### llRead 
- Como tem muitas variáveis globais, às vezes fica um pouco confuso perceber o que as funções que o read usa, estão a fazer internamente. 
- Pelo que eu entendo, pega na variável global e aloca 2 * o tamanho de uma frame (deve ser para garantir que não há overflows). Primeiro aloca com o default de 256 e depois aloca com o tamanho máximo de pacote que vier do outro lado do emissor, com o START (não percebo porque o faz duas vezes, mas pronto). Aloca memória também para o buffer global, o fileData cujo tamanho também recebe do START packet. 
- ```recieveStart```(muito confuso para mim xd), corre o dataProtocol que está descrito aqui em baixo, devolvendo na global var data o pacote de start. Depois vai campo a campo (file size, file name, max frame size), fazer o inverso da criação deste pacote, ou seja alocar arrays com o tamanho de cada campo, ler os vários bytes que compõem esse campo e depois fazer um cáculo de shifts para obterem o valor real de cada um. Ficam com um cálculo deste género para cada cena ```fileSize =  (aux0 << 24) | (aux1 << 16) | (aux2 << 8) | (aux3);```. Aqueles ^0xFF em parte servem para remover o & 0xFF que é obrigatório fazer segundo os slides e o outro acho que é para quando o valor é negativo, conseguires extrair a informação corretamente, quando o teu número está em complemento de 2, se te lembras é aquela representação em que o número fica assim 111111101010, ou seja se guardares um 1010 do lado do emissor, como ele tem de preencher 8 espaços, ele presumo que ele é negativo e faz 11111010, mas na verdade querias 00001010 (acho que é isso que estão a colmatar). É esquisito terem sempre 4 termos (vai sempre até v[3]) penso eu, porque pode variar em função do nome que lhe dás etc, mas pode ter ficado assim por convenção ou facilidade. No final o start é guardado globalmente e o tamanho, nome e maxframesize também são passados para variáveis globais.
- ```dataProtocol```, aparentenemente é o que usam para ler pacotes de __dados__ do outro lado no geral. Loop infinito que usa o readData(). Se o readData retornar -1 basicamente o outro lado já mandou o ficheiro todo inclusivé o DISC, se for 1 o BCC do pacote (a paridade dos bytes) deu bosta e tem de lhe mandar o REJ para repetir o envio. Quando corre bem, estás em duas situações (mas correu bem logo manda-se o RR). Ou estamos a receber um Start e não um pacote genérico de dados, daí a var global ``ìsStart``estar a 1, e fechamos o loop ou é um pacote de dados (que é verificado se é duplicado no readData, com a var gloabl duplicate). Se passar o teste, é guardado no ficheiro. 
- ```saveFileData``` (guarda dados que recebeu no buffer global fileData), verifica quantos bytes já leu e compara com o tamanho do ficheiro a receber (valor que veio do START). Se já leu todos ou até a mais, ignora esses dados a mais e espera pelo END do lado do emissor para acabar com o loop de leitura (com o STOP global) e passar para o llclose. Senão faz uma escrita no filedata co offset do número de bytes que já recebeu. A matemática que está inicialmente serve para perceber se a escrita que vais fazer é do teu tamanho de pacote predefinido (256 ou o que vem no START), ou os dados restantes que já não ocupam um tamanho total de um pacote. Como a divisão no início da função é inteira, significa que para um ficheiro de 11B com frames de 5B, tens 11/5= 2, e depois tens 11 - 5 * 2 = 1B (é o resto dado pelo operador %, não sei porque não fizeram assim), tens 2 * o tamanho de um frame e um resto que vai ter de ser escrito à parte, com tamanho especificado. Os contadores de bytes globais são também incrementados.
- ```receiveEnd```, a única coisa que faz é ver se é um pacote de END (com o primeiro byte) e comparar com o start inicial que tem de coincidir (pode dar asneira porque ele fica à espera dele se não me engano, mas o sender acaba por mandar outro penso eu)
- ```readData``` é mais uma vez um daqueles automatos com ifs (só que como são mais condições ainda fica mais confuso). Loop inicial em que lê a 1ª flag, depois vai campo a campo (no caso do controlo verifica se o pacote que está a receber é o que espera com ```dataFrameNum```, questão dos duplicados). A uma jigajoga interessante no loop que começa em //DATA (linha 315), basicamente sabes que o BCC2 vai ser o último byte, ou seja lês tudo para um buffer, dizes que a posição i de tmpData é igual ao último valor lido pelo bcc2 e o novo bcc2 é igual ao novo valor lido. Isso faz com que, ao ler a flag e terminando a leitura interna dos dados (este loop ```while(receiving == 0)```), o bcc fique com o último byte lido antes da flag. Ao longo dos ifs tens os elses para quando cada passo corre mal, alguns geram retorno como o duplicado que retorna 0 e outros retornam erro como um bcc inválido.
 Da linha 334-376 tem o destuffing do pacote (reverter o processo que incluí os acaracteres de escape na própria mensagem, de uma forma convencionada para saberes o que está lá-foi e continua a ser confuso para mim perceber como se faz)
- ```writeToFile```, pega no filename que recebeu no receivestart e copia o conteúdo que foi guardado no buffer fileData  para o ficheiro na pasta de receção em ./received/"nomedoficheiro"
- ```sendREJ/sendRR```, constroem os pacotes de controlo REJ e RR, de acordo com as especificações dos slides (atenção à questão de especificar o 1 ou 0 do STOP and WAIT, de acordo com o último pacote que recebeu/não recebeu) e escreve para a porta

### llClose
- Chama ```disconnectProtocol```
- A seguir à transmissão dos dados propriamente dita, entras nesta função, esperas em loop inifinito pelo readDisc (com um áutomato semelhante aos outros, mas mais simples). Usa ```sendDiscWithAlarm``` para construir o pacote de DISC e envia para o outro lado se possível. 
- Antes disso chama ```signal(SIGALRM, sendDiscWithAlarm)```, o que isto faz é dizer ao programa/OS que quando um sinal de alarme for acionado, execute de novo a função de envio do DISC. Nalarm é incrementado a cada disc enviado no ```sendDiscWithAlarm```e se for maior que 3 ele simplesmente desiste de enviar mais coisas e retorna com exit(1), nesta fase em princípio já tens o ficheiro e quase não importa. Se correr bem e enviar o DISC tenta receber um Ack do lado do emissor com o ```readUA(fd)``` em loop também. O tempo até um novo alarme é definido todas as vezes no final de cada tentativa do disconnect com ``` alarm(3);```
- Não percebo é porque é que é a única que tem os alarms de timeout ao longo do receiver (no ```sendDiscWithAlarm```), acho que as outras também precisavam disso. 

## Sender 

### Topo
- Mesmas coisas que receiver, algumas varáveis para os argumentos da linha de comandos (percentagem de erros simulados, maxframesize etc) alarmes, buffers de escrita e frame atual. 

### Main 
- Lê e valida args da linha de coamndos e coloca a porta de seŕie com aqueles defaults dados no código base, dando flush do buffer dela para começar limpa.
- aloca globalData para mais tarde ler do ficheiro, chama as funções principais, llopen, llwrite e llclose


### llOpen
- Chama ```setConnection```. Muito semelhante ao lado do receiver só que tem o tal timeout com o alarm que o receiver tem no disconnect das 3 tentativas e dos 3 segundos . Envia o SET para iniciar o protocolo (```sendSetWithAlarm```) e espera que o receiver responda com o UA. Cria o SET com os valores dados pelo slide e valida o UA com um automato semelhante aos outros.

### llWrite 
- Começa com ```  sendStartOrEnd(filename,1);```, que envia o start ou end baseado no segundo argumento. Constroem os campos individuais de cada componente do pacote dse Start/End, type, size e value do nome do ficheiro, tamanho e maxFrameSize. Usam ``` stat(nameDest,&st)``` para saber o tamanho em bytes. Não sei se aqui estar tudo hardcoded é uma boa política, porque acho que é mais dinâmico, os campos não precisam de ter obrigatoriamente o tamanho que eles colocaram aqui, devia ser dinâmico para ocupar o mínimo espaço possível. Copiam isso para o ```globalData```, que depois é enviado com o ```int transferData()```.
- ```transferData()``` tem o mesmo mecanismo de alarme que as outras funções têm para o envio, com ```sendDataWithAlarm``` a ser acionada no alarm que é estabelicido por ```sendDataWithAlarm```. Depois entra num loop (enquanto estamos dentro das três tentativas) para esperar pela resposta de RR/REJ do receptor. Se for RR, paramos o loop e termina a função. Se for REJ, colocamos o resend a 1, e chamamos recursivamente a função (não é uma boa prática honestamente, especialmente se meter sinais ao barulho)
- ```sendDataWithAlarm```, aloca um buffer com 2* o tamanho máximo de um frame. Se ainda estivermos dentro das três tentativas (não esquecer que é chamada dentro de sendDataWithAlarm, caso contrário sai do programa), cria o header do pacote, e percorre globalData (a variável global de leitura) passando-a para o buffer que vai ser enviado. Durante este passo faz o tal stuffing dos caracteres de escape se for necessário. 
```c
if (globalData[i] == 0b01111110 || globalData[i] == 0b01111101)
      {
        buf[n] = ESC;
        buf[++n] = globalData[i] ^ STUFF;
        n++;
      }
```
- A mesma coisa é feita ao incluir o BCC, no caso de ser igual a um destes caracteres. 
- Se existir uma probabilidade de erro da frame dada na linha de comandos é colocada com isto ```buf[size-2] = buf[size-2] ^ 0x0F;```
- De seguida chama-se write para transferir a informação e aumenta-se o nAlarm para dizer que uma tentativa foi realizada e o alarme é atualizado.
- ```readDataResponse```
- ```sendFileData```, tem uma lógica semelhante à leitura dos dados no lado do receptor. Armazena-se todo o ficheiro dentro de um buffer global e faz-se o cálculo de quantos frames temos de enviar (incluindo aquela questão de não ser necessário um buffer completo para o que restar do ficheiro). 
    - Não sei muito bem o que l2 e l1 fazem, ainda por cima tem lá um TODO, mas suponho que tenha a ver com uma convenção do tamanho de frame algures. 
    ```c
    currentDataSize = frameMaxSize;
    int j = 0; //offset atual do buffer que tem o ficheiro

   for(int i = 0; i < numFramesToSend; i++){
    memset(globalData, 0, frameMaxSize*2); // resetar para 0 o globalData, que corresponde a uma frame 
    memcpy(globalData, buffer + j, currentDataSize); // copia os dados do ficheiro 
    transferData(); // envia-se a globalData
    j += currentDataSize; //aumenta o currentDataSize
    numOfFrame++; // passa para a frame seguinte
    ```
    - Depois utiliza o mesmo cálculo para perceber quanto falta para terminar o ficheiro em bytes, e realiza esse último envio, tudo feito com ```tranferdata``` 

### llClose 
- Semelhante ao lado do receiver. Sigalarm para ```sendDisconnectWithAlarm```, até receber um DISC do lado do recetor indicando a disconexão total, durante três tentativas, senão exit(1). Depois envia um UA indicando que recebeu o DISC.


### Resultados gerais (do meu trabalho)

- Regra geral quanto maior a probabilidade de erro, pior a eficiÊncia de transferência (erros nos dados são piores que no header porque requerem uma leitura completa do pacote que acaba por ir para o lixo)
- tempo de propagação prejudica a eficiência 
- baudrates acima de 76800 prejudicam a eficiẽncia (provavelmente a porta de série não suporta)
- Tamanho do ficheiro aumenta proporcionalmente com a eficiência, ficheiros mais pequenos têm diferenças mais notórias de tempo de transferência, ficheiros maiores vão praticamente iguais e a diferença torna-se mínima


## Possíveis alterações 

- O que alterava mais o trabalho para não haver confusão é dividir o receiver e o sender para ficheiros mais pequenos (separando nas camadas da app, linklayer application layer etc). Ter um makefile também vai parecer mais pro
- Se vires funções como o disconnect e readUA são muito iguais dos dois lados, ou seja se passassem para um só ficheiro (utils p.e.) em princípio poderiam ser partilhadas.
- As variáveis globais poderiam passas a ser uma struct passada por apontador, mas acho que não faz muita diferença 
- Mudar os nomes das funções e das variáveis mais gerais. 
- Tirar os pragma region, pelo que percebi não fazem nada a nível utilitário

Se ainda tiver algum tempo, vejo se consigo ajudar nesta parte. O importante é que continue a compilar, no máximo se uma nova versão não der quando chegarem à sala mudem para a anterior do repositório