# 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