From 39d51b3b3e45876d3c88d07a6da472474279570b Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Wed, 28 Jan 2026 21:17:54 -0300 Subject: [PATCH 01/31] code upgraded to python 3 --- Job.py | 106 ++++++---------- Util.py | 28 ++--- grid.py | 374 +++++++++++++++++++++++--------------------------------- 3 files changed, 208 insertions(+), 300 deletions(-) diff --git a/Job.py b/Job.py index f3218fc..3476021 100644 --- a/Job.py +++ b/Job.py @@ -1,8 +1,8 @@ import os import datetime -import thread +import threading -lock = thread.allocate_lock() # Objeto para lock +lock = threading.Lock() # estados possiveis de uma parte BRANCO = 'BRANCO' @@ -15,30 +15,21 @@ class Parte: data = None # data de atribuicao entrada = None saida = None - + def is_branco(self): - if self.estado == BRANCO: - return True - else: - return False - + return self.estado == BRANCO + def is_atribuido(self): - if self.estado == ATRIBUIDO: - return True - else: - return False - + return self.estado == ATRIBUIDO + def is_completo(self): - if self.estado == COMPLETO: - return True - else: - return False - + return self.estado == COMPLETO + def atribui(self, par): self.estado = ATRIBUIDO self.par = par self.data = datetime.datetime.now() - + def set_completo(self, saida): self.estado = COMPLETO self.saida = saida @@ -54,50 +45,44 @@ class Job: listaPartes = [] listaPares = [] listaParOcupado = [] - + def __init__(self, nome, programa, diretorio, arquivo): self.nome = nome self.programa = programa self.diretorio = diretorio self.arquivo = arquivo - - dirEntrada = './jobs/' + diretorio + '/entrada' - dirSaida = './jobs/' + diretorio + '/saida' - - print '' - + dirEntrada = f'./jobs/{diretorio}/entrada' + dirSaida = f'./jobs/{diretorio}/saida' + print('') + for file in os.listdir(dirEntrada): if file.endswith(".in"): parte = Parte() parte.entrada = file - - if os.path.isfile(dirSaida + '/' + file[0:-3] + '.out'): + if os.path.isfile(f'{dirSaida}/{file[:-3]}.out'): parte.estado = COMPLETO - parte.saida = file[0:-3] + '.out' - + parte.saida = f'{file[:-3]}.out' self.listaPartes.append(parte) - self.partes = self.partes + 1 - print file, ' - ', parte.estado - - print 'Job carregado com ', self.partes, ' partes.' - + self.partes += 1 + print(file, ' - ', parte.estado) + + print('Job carregado com ', self.partes, ' partes.') + # True se todas tarefas estao completas, False se contrario def finalizado(self): valor = True - lock.acquire() ### Inicio de secao critica ### for p in self.listaPartes: if p.estado != COMPLETO: valor = False break lock.release() ### Fim de secao critica ### - return valor - + def inserePar(self, par): if par not in self.listaPares: self.listaPares.append(par) - + def removePar(self, par): if par in self.listaPares: self.listaPares.remove(par) @@ -109,86 +94,71 @@ def removePar(self, par): t.estado = BRANCO t.par = None t.data = None - + # Atribui uma parte do job ao par especificado def atribuiParteAoPar(self, parte, par): ok = True - lock.acquire() ### Inicio de secao critica ### - if parte not in self.listaPartes: - print 'ERRO: Parte ', parte.entrada, ' nao esta no job ', self.nome + print('ERRO: Parte ', parte.entrada, ' nao esta no job ', self.nome) ok = False elif par not in self.listaPares: - print 'ERRO: Par ', par, ' nao participa no job ', self.nome + print('ERRO: Par ', par, ' nao participa no job ', self.nome) ok = False elif parte.estado == COMPLETO: - print 'NAO pode atribuir a parte ', parte.entrada, ' do job ', self.nome, ' pois ja esta completa.' + print('NAO pode atribuir a parte ', parte.entrada, ' do job ', self.nome, ' pois ja esta completa.') ok = False elif par in self.listaParOcupado: - print 'ERRO: Par ', par, ' ja esta ocupado com uma tarefa' + print('ERRO: Par ', par, ' ja esta ocupado com uma tarefa') ok = False elif parte.estado == ATRIBUIDO: - print 'A PARTE ', parte.entrada, ' ja esta com ', parte.par + print('A PARTE ', parte.entrada, ' ja esta com ', parte.par) ok = False - # tudo ok, pode encadear if ok: self.listaPartes[self.listaPartes.index(parte)].atribui(par) self.listaParOcupado.append(par) - lock.release() ### Fim de secao critica ### - + def possuiParLivre(self): lock.acquire() ### Inicio de secao critica ### - if len(self.listaParOcupado) < len(self.listaPares): - retorno = True - else: - retorno = False + retorno = len(self.listaParOcupado) < len(self.listaPares) lock.release() ### Fim de secao critica ### - return retorno - + def proximoParLivre(self): parRetorno = None - lock.acquire() ### Inicio de secao critica ### for par in self.listaPares: if par not in self.listaParOcupado: parRetorno = par + break lock.release() ### Fim de secao critica ### - return parRetorno - + # Muda para COMPLETO o estado da parte que esta relacionada a nomeSaida # e tambem retira da listaParOcupado aquele que estiver com essa parte (se existir) def finalizaParte(self, nomeSaida, par): lock.acquire() ### Inicio de secao critica ### - for parte in self.listaPartes: - if parte.entrada[0:-3] == nomeSaida[0:-4]: # correspondencia entre os nomes sem as extensoes + if parte.entrada[:-3] == nomeSaida[:-4]: # correspondencia entre os nomes sem as extensoes # se a parte ja estiver completa, apenas passamos - if parte.is_branco(): parte.set_completo(nomeSaida) elif parte.is_atribuido(): for parOcup in self.listaParOcupado: - print ' ' , parOcup[0] if parOcup[0] == par[0]: self.listaParOcupado.remove(parOcup) parte.set_completo(nomeSaida) - lock.release() ### Fim de secao critica ### - + # Retorna True se um dado par esta ocupado em alguma parte do job def isParOcupado(self, par): valor = False - lock.acquire() ### Inicio de secao critica ### for parOcup in self.listaParOcupado: if parOcup[0] == par[0]: valor = True break lock.release() ### Fim de secao critica ### - - return valor \ No newline at end of file + return valor diff --git a/Util.py b/Util.py index 999bd21..822ce61 100644 --- a/Util.py +++ b/Util.py @@ -3,18 +3,18 @@ #---------------------------------------------------------------------------------------- def exibirAjudaDeComandos(): - print '' - print 'Meu Grid 0.1 por Tiago Matos' - print '' - print 'Comandos:' - print '' - print ' contato' - print ' pares' - print ' carrega [job]' - print ' executa' - print ' mensagem [#par] [toda mensagem]' - print ' enviar [#par] [arquivo]' - print ' ajuda' - print ' sair' - print '' + print('') + print('Meu Grid 0.2 por Tiago Matos') + print('') + print('Comandos:') + print('') + print(' contato') + print(' pares') + print(' carrega [job]') + print(' executa') + print(' mensagem [#par] [toda mensagem]') + print(' enviar [#par] [arquivo]') + print(' ajuda') + print(' sair') + print('') #---------------------------------------------------------------------------------------- diff --git a/grid.py b/grid.py index 985354b..1dd063f 100644 --- a/grid.py +++ b/grid.py @@ -1,8 +1,9 @@ -from socket import * from subprocess import call -import thread +from socket import socket, AF_INET, SOCK_DGRAM, SOCK_STREAM +import threading import time import os +import sys from Job import * from Definicoes import * @@ -19,47 +20,39 @@ #---------------------------------------------------------------------------------------- def encerrarPrograma(): - print '' - print '\nParando o programa.' - for par in listaPares: - meuSocket.sendto('disconect', par) - meuSocket.close() - exit(0) + print('') + print('\nParando o programa.') + for par in listaPares: + meuSocket.sendto('disconect', par) + meuSocket.close() + sys.exit(0) #---------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------- def processaPacote(msgComDados, enderecoPar): - msg = msgComDados.split(';') - + msg = msgComDados.decode('utf-8').split(';') # Decode bytes to string resposta = 'void' - if msg[0] == 'ok': if msg[1] == 'contact': if len(listaPares) < maxDePares: - if not enderecoPar in listaPares: + if enderecoPar not in listaPares: listaPares.append(enderecoPar) - print '\nNosso pedido de contato foi aceito por ', str(enderecoPar) + print(f'\nNosso pedido de contato foi aceito por {str(enderecoPar)}') else: resposta = 'disconect' - elif msg[0] == 'conect': if len(listaPares) < maxDePares: - if not enderecoPar in listaPares: + if enderecoPar not in listaPares: listaPares.append(enderecoPar) - resposta = 'ok;contact' - - print '\nPedido de contato aceito originado de ' + str(enderecoPar) + print(f'\nPedido de contato aceito originado de {str(enderecoPar)}') else: resposta = 'not' - elif msg[0] == 'disconect': if enderecoPar in listaPares: listaPares.remove(enderecoPar) - print 'Contato desfeito por solicitacao de ' + str(enderecoPar) - + print(f'Contato desfeito por solicitacao de {str(enderecoPar)}') elif enderecoPar in listaPares: - if msg[0] == 'do': resposta = 'what?' if len(msg) > 3: @@ -67,24 +60,22 @@ def processaPacote(msgComDados, enderecoPar): try: call(['./programs/' + msg[2], msg[3]]) resposta = 'done cmd' - except WindowsError: + except Exception: # Changed to catch all exceptions resposta = 'erro cmd' - elif msg[0] == 'msg': msg_print = 'Msg. de ' + str(enderecoPar) + ' : ' if len(msg) > 1: msg_print += msg[1] - print '' - print msg_print + print('') + print(msg_print) resposta = 'done' - else: resposta = 'not' if resposta != 'void': - meuSocket.sendto(resposta, enderecoPar) - #print '' - #print 'Enviei: ', resposta, ' Para: ', str(enderecoPar) + meuSocket.sendto(resposta.encode('utf-8'), enderecoPar) # Encode string to bytes + #print('') + #print('Enviei: ', resposta, ' Para: ', str(enderecoPar)) #---------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------- @@ -92,11 +83,9 @@ def recepcaoThread(): while True: try: msgComDados, enderecoPar = meuSocket.recvfrom(2048) - - thread.start_new_thread(processaPacote, tuple([msgComDados, enderecoPar])) + threading.Thread(target=processaPacote, args=(msgComDados, enderecoPar)).start() except Exception as e: - print e - pass + print(e) #---------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------- @@ -104,16 +93,15 @@ def contactaPares(): try: arquivoPares = [line.strip() for line in open('peerlist')] except Exception as e: - print 'Erro ao acessar o arquivo peerlist!' - print e + print('Erro ao acessar o arquivo peerlist!') + print(e) return - if len(arquivoPares) == 0: - print 'Arquivo peerlist vazio!' + print('Arquivo peerlist vazio!') elif len(listaPares) < len(arquivoPares) and len(listaPares) < 3: for enderecoPar in arquivoPares: - meuSocket.sendto('conect', (enderecoPar, portaPar)) - print '\nTentando contactar a: ' + enderecoPar + meuSocket.sendto(b'conect', (enderecoPar, portaPar)) # Send bytes + print('\nTentando contactar a: ' + enderecoPar) #---------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------- @@ -122,113 +110,99 @@ def enviarArquivo(par, arquivo): tcpSocket.connect((par[0], PORTA_TCP_PAR)) f = open (arquivo, 'rb') buff = 1024 - arquivo = arquivo.replace('\\', '/') - nome = arquivo.split('/')[-1] + nome = arquivo.split('/')[-1] tamanho = os.path.getsize(arquivo) - cabecalho = 'envio|' + nome + '|' + str(tamanho) + '|' - + # aqui preenchemos o cabecalho com esp. em branco ate ele ficar com tam. do buffer # isto e, o cabecalho deve ter buff bytes de tamanho (wrkrnd) cabecalho += ' ' * (1024 - len(cabecalho)) - - print 'ENVIANDO: ', arquivo, ' de ', tamanho - + + print('ENVIANDO: ', arquivo, ' de ', tamanho) + tcpSocket.send(cabecalho) - + dados = f.read(buff) while (dados): tcpSocket.send(dados) dados = f.read(buff) - + tcpSocket.close() f.close() - print 'FIM DO ENVIO DE: ', arquivo + print('FIM DO ENVIO DE: ', arquivo) #---------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------- def preparaJobNoPar(par): global job - tcpSocket = socket(AF_INET, SOCK_STREAM) tcpSocket.connect((par[0], PORTA_TCP_PAR)) - - tcpSocket.send('job|' + job.diretorio + '|') - + tcpSocket.sendall(f'job|{job.diretorio}|'.encode('utf-8')) buff = 1024 - - print '' - - r = tcpSocket.recv(buff) - + print('') + r = tcpSocket.recv(buff).decode('utf-8') if r == 'ok': - print 'PAR ', par[0], ' esta preparado para o job ', job.nome + print(f'PAR {par[0]} esta preparado para o job {job.nome}') job.inserePar(par) else: - print 'PAR ', par[0], ' nao pode preparar (ou nao e possivel confirmar) para o job ', job.nome + print(f'PAR {par[0]} nao pode preparar (ou nao e possivel confirmar) para o job {job.nome}') job.removePar(par) - tcpSocket.close() #---------------------------------------------------------------------------------------- #-Transfere via TCP um arquivo de entrada para um par------------------------------------ def enviaEntrada(entrada, par): f = None - arquivo = './jobs/' + job.diretorio + '/entrada/' + entrada - + try: f = open(arquivo, 'rb') except Exception as e: - print 'ERRO ao acessar o arquivo de entrada ', arquivo - print e + print('ERRO ao acessar o arquivo de entrada ', arquivo) + print(e) return False - + tamanho = os.path.getsize(arquivo) - buff = 1024 tcpSocket = socket(AF_INET, SOCK_STREAM) - cabecalho = 'entrada|' + job.diretorio + '|' + entrada + '|' + str(tamanho) + '|' # entrada|diretorio_job|nome_entrada|tamanho| - + # aqui preenchemos o cabecalho com esp. em branco ate ele ficar com tam. do buffer # isto e, o cabecalho deve ter buff bytes de tamanho (wrkrnd) cabecalho += ' ' * (1024 - len(cabecalho)) - - try: - print 'ENVIANDO ENTRADA: ', arquivo, ' de ', tamanho + + try: + print('ENVIANDO ENTRADA: ', arquivo, ' de ', tamanho) tcpSocket.connect((par[0], PORTA_TCP_PAR)) - tcpSocket.send(cabecalho) - dados = f.read(buff) while (dados): tcpSocket.send(dados) dados = f.read(buff) except Exception as e: - print '\nHouve um erro na tranf. de um arquivo!' - print e + print('\nHouve um erro na tranf. de um arquivo!') + print(e) tcpSocket.close() f.close() return False - + tcpSocket.close() f.close() - - print 'FIM DO ENVIO DE: ', arquivo + + print('FIM DO ENVIO DE: ', arquivo) return True #---------------------------------------------------------------------------------------- #-Envia um comando para o par executar uma parte do job---------------------------------- def executaParteNoPar(parte, par): tcpSocket = socket(AF_INET, SOCK_STREAM) - + # Formato: executa|programa|diretorio_job|nome_entrada| msg = 'executa|' + job.programa + '|' + job.diretorio + '|' + parte.entrada + '|' - - print 'EXECUTANDO: ', job.programa, ' sobre a entrada ', parte.entrada, ' em ', par - + + print('EXECUTANDO: ', job.programa, ' sobre a entrada ', parte.entrada, ' em ', par) + tcpSocket.connect((par[0], PORTA_TCP_PAR)) tcpSocket.send(msg) tcpSocket.close() @@ -237,83 +211,82 @@ def executaParteNoPar(parte, par): #-Thread que cuida da divisao e execucao das partes do job nos pares--------------------- def jobThread(): global job - + if len(listaPares) == 0: - print '\nSem contato de pares para compartilhar o processamento.' + print('\nSem contato de pares para compartilhar o processamento.') return - + # esse loop tambem popula a lista job.listaPares com os pares prontos for par in listaPares: preparaJobNoPar(par) - + if len(job.listaPares) == 0: - print '\nNenhum par ficou pronto para o processamento.' + print('\nNenhum par ficou pronto para o processamento.') return - + while not job.finalizado(): if job.possuiParLivre(): for parte in job.listaPartes: - print parte.estado, parte.entrada + print(parte.estado, parte.entrada) if parte.is_branco(): parLivre = job.proximoParLivre() - if parLivre == None: + if parLivre is None: break if not enviaEntrada(parte.entrada, parLivre): break job.atribuiParteAoPar(parte, parLivre) executaParteNoPar(parte, parLivre) time.sleep(0.7) # Estimar com experimentos qual o melhor valor... - - print '\nSUCESSO. O job foi concluido.' + + print('\nSUCESSO. O job foi concluido.') #---------------------------------------------------------------------------------------- #-Inicia a execucao do job--------------------------------------------------------------- def executaJob(): global job - + if not job: - print '\nNenhum job carregado.' + print('\nNenhum job carregado.') return if len(job.listaPartes) == 0: - print '\nJob sem tarefas.' + print('\nJob sem tarefas.') return if job.finalizado(): - print '\nTodas as tarefas do job \'' + job.nome + '\' foram completas.' + print(f'\nTodas as tarefas do job \'{job.nome}\' foram completas.') return - + for parte in job.listaPartes: if parte.is_branco(): - print 'PARTE: ', parte.entrada, ' EM BRANCO' - - thread.start_new_thread( jobThread, () ) - + print('PARTE: ', parte.entrada, ' EM BRANCO') + + threading.Thread(target=jobThread, args=()).start() #---------------------------------------------------------------------------------------- #-Carrega para a memoria o job descrito pelo arquivo------------------------------------- def carregaJob(nomeArquivo): - global job - arquivoJob = [] + global job + arquivoJob = [] - try: - for line in open('jobs/' + nomeArquivo): - entrada = line.strip() - if len(entrada) > 0: - if entrada[0] != '#': - arquivoJob.append(entrada) - except Exception as e: - print 'Erro ao acessar o arquivo de job: ', nomeArquivo - print e - return - - if len(arquivoJob) == 0: - print 'Arquivo de job ', nomeArquivo, ' esta vazio.' - return - - if len(arquivoJob) < 3: - print 'Arquivo de job ', nomeArquivo, ' faltando parametros.' - return - - job = Job(arquivoJob[0], arquivoJob[1], arquivoJob[2], nomeArquivo) + try: + for line in open('jobs/' + nomeArquivo): + entrada = line.strip() + if len(entrada) > 0: + if entrada[0] != '#': + arquivoJob.append(entrada) + except Exception as e: + print(f'Erro ao acessar o arquivo de job: {nomeArquivo}') + print(e) + return + + if len(arquivoJob) == 0: + print(f'Arquivo de job {nomeArquivo} está vazio.') + return + + if len(arquivoJob) < 3: + print(f'Arquivo de job {nomeArquivo} faltando parâmetros.') + return + + job = Job(arquivoJob[0], arquivoJob[1], arquivoJob[2], nomeArquivo) #---------------------------------------------------------------------------------------- #-Interpretacao dos comandos do prompt--------------------------------------------------- @@ -322,76 +295,66 @@ def trataComando(stringComando): return comando = stringComando.split(' ') - + if comando[0] == 'ajuda': exibirAjudaDeComandos() - elif comando[0] == 'contato': contactaPares() - elif comando[0] == 'mensagem': if len(comando) < 3: - print '\nArgumentos incorretos no comando.' + print('\nArgumentos incorretos no comando.') else: try: idPar = int(comando[1]) except ValueError: - print '\nArgumentos incorretos no comando. Id do par deve ser numero.' + print('\nArgumentos incorretos no comando. Id do par deve ser numero.') return - + if idPar < 0 or idPar+1 > len(listaPares): - print '\nNao temos este par na nossa lista.' + print('\nNao temos este par na nossa lista.') else: msgConteudo = '' for s in comando[2:]: msgConteudo += s + ' ' meuSocket.sendto('msg;' + msgConteudo, listaPares[idPar]) - elif comando[0] == 'enviar': if len(comando) < 3: - print '\nArgumentos incorretos no comando.' + print('\nArgumentos incorretos no comando.') else: try: idPar = int(comando[1]) except ValueError: - print '\nArgumentos incorretos no comando. Id do par deve ser numero.' + print('\nArgumentos incorretos no comando. Id do par deve ser numero.') return - + if idPar < 0 or idPar+1 > len(listaPares): - print '\nNao temos este par na nossa lista.' - + print('\nNao temos este par na nossa lista.') + enviarArquivo(listaPares[idPar], comando[2]) - elif comando[0] == 'pares': i = 0 for par in listaPares: - print '#', i, ' - ', str(par), ' - Ocup.:', job.isParOcupado(par) if job != None else False + print('#', i, ' - ', str(par), ' - Ocup.:', job.isParOcupado(par) if job is not None else False) i = i + 1 if i == 0: - print 'Sem pares contactados.' - + print('Sem pares contactados.') elif comando[0] == 'carrega': if len(comando) < 2: - print '\nArgumentos incorretos. Especifique o arquivo do job.' + print('\nArgumentos incorretos. Especifique o arquivo do job.') return - carregaJob(comando[1]) - elif comando[0] == 'estado': - if job == None: - print 'Sem job carregado.' + if job is None: + print('Sem job carregado.') else: for parte in job.listaPartes: - print '#', parte.entrada, '-', parte.estado - + print('#', parte.entrada, '-', parte.estado) elif comando[0] == 'executa': executaJob() - elif comando[0] == 'sair': encerrarPrograma() - else: - print 'Comando nao reconhecido.' + print('Comando nao reconhecido.') #---------------------------------------------------------------------------------------- #-Transf. via TCP, o result. do process. e arquivo de saida (se houver) para um par------ @@ -399,47 +362,44 @@ def enviaSaida(dir, saida, par): f = None arquivo = './temp/' + dir + '/saida/' + saida - + try: f = open(arquivo, 'rb') except Exception as e: - print '\nERRO ao acessar o arquivo de saida ', arquivo - print e + print(f'\nERRO ao acessar o arquivo de saida {arquivo}') + print(e) return False - + tamanho = os.path.getsize(arquivo) - buff = 1024 tcpSocket = socket(AF_INET, SOCK_STREAM) - cabecalho = 'saida|' + dir + '|' + saida + '|' + str(tamanho) + '|' # saida|diretorio_job|nome_saida|tamanho| - + # aqui preenchemos o cabecalho com esp. em branco ate ele ficar com tam. do buffer # isto e, o cabecalho deve ter buff bytes de tamanho (wrkrnd) cabecalho += ' ' * (1024 - len(cabecalho)) - - print '\nENVIANDO SAIDA: ', arquivo, ' de ', tamanho - + + print(f'\nENVIANDO SAIDA: {arquivo} de {tamanho}') + tcpSocket.connect((par[0], PORTA_TCP_PAR)) - + try: tcpSocket.send(cabecalho) - dados = f.read(buff) while (dados): tcpSocket.send(dados) dados = f.read(buff) except Exception as e: - print '\nHouve um erro na tranf. de um arquivo!' - print e + print('\nHouve um erro na transf. de um arquivo!') + print(e) tcpSocket.close() f.close() return False - + tcpSocket.close() f.close() - - print '\nFIM DO ENVIO DE: ', arquivo + + print(f'\nFIM DO ENVIO DE: {arquivo}') return True #---------------------------------------------------------------------------------------- @@ -447,39 +407,33 @@ def enviaSaida(dir, saida, par): def conexaoTcpThread(con, par): global job buff = 1024 - + r = con.recv(buff) #print '' #print 'TCP>', r[:200] ####### DBG - + cabecalho = r.split('|') comando = cabecalho[0] - + if comando == 'envio': nome = cabecalho[1] tamanho = int(cabecalho[2]) - f = open('./recebidos/' + nome, 'wb+') - recebidos = 0 - while recebidos < tamanho: r = con.recv(buff) while (r): recebidos += len(r) f.write(r) r = con.recv(buff) - if not r: break - - print '\nFECHANDO ARQUVIO', '- RECEBIDOS: ', recebidos + print('\nFECHANDO ARQUVIO', '- RECEBIDOS: ', recebidos) f.close() - elif comando == 'job': diretorioJob = './temp/' + cabecalho[1] resultado = '' - + try: if os.path.isdir(diretorioJob): resultado = 'ok' @@ -489,59 +443,48 @@ def conexaoTcpThread(con, par): resultado = 'ok' except Exception as e: resultado = 'erro' - print e - + print(e) + con.send(resultado) - elif comando == 'entrada': diretorioEntrada = './temp/' + cabecalho[1] + '/entrada/' nomeEntrada = cabecalho[2] tamanho = int(cabecalho[3]) - f = open(diretorioEntrada + nomeEntrada, 'wb+') - recebidos = 0 - while recebidos < tamanho: r = con.recv(buff) while (r): recebidos += len(r) f.write(r) r = con.recv(buff) - if not r: break - - print '\nFECHANDO ARQUIVO: ', diretorioEntrada + nomeEntrada, ' - RECEBIDOS: ', recebidos, ' de ', tamanho + + print('\nFECHANDO ARQUIVO: ', diretorioEntrada + nomeEntrada, ' - RECEBIDOS: ', recebidos, ' de ', tamanho) f.close() - elif comando == 'saida': # Formato: saida|diretorio_job|nome_saida|tamanho| nomeDiretorio = cabecalho[1] diretorioSaida = './jobs/' + nomeDiretorio + '/saida/' nomeSaida = cabecalho[2] tamanho = int(cabecalho[3]) - f = open(diretorioSaida + nomeSaida, 'wb+') - recebidos = 0 - while recebidos < tamanho: r = con.recv(buff) while (r): recebidos += len(r) f.write(r) r = con.recv(buff) - if not r: break - - print '\nFECHANDO ARQUIVO ', diretorioSaida + nomeSaida, ' - RECEBIDOS: ', recebidos, ' de ', tamanho + + print('\nFECHANDO ARQUIVO ', diretorioSaida + nomeSaida, ' - RECEBIDOS: ', recebidos, ' de ', tamanho) f.close() - + # esta parte eh muito importante, nela mudamos o estado de uma tarefa concorrentemente if job.diretorio == nomeDiretorio: job.finalizaParte(nomeSaida, par) - elif comando == 'executa': # Formato da msg: executa|programa|diretorio_job|nome_entrada|remetente programa = cabecalho[1] nomeDiretorioJob = cabecalho[2] @@ -550,24 +493,20 @@ def conexaoTcpThread(con, par): nomeEntrada = cabecalho[3] nomeSaida = nomeEntrada[0:-3] + '.out' remetente = par[0] - resposta = '' - try: - print '\nIniciando execucao do programa ', programa + print(f'\nIniciando execucao do programa {programa}') call(['./programs/' + programa, diretorioEntrada + nomeEntrada, diretorioSaida + nomeSaida]) resposta = 'pronto' except Exception as e: - print '\nERRO na execucao do programa ', programa - print e + print(f'\nERRO na execucao do programa {programa}') + print(e) resposta = 'erro' - enviaSaida(nomeDiretorioJob, nomeSaida, par) - else: while (r): r = con.recv(buff) - + con.close() #---------------------------------------------------------------------------------------- @@ -576,34 +515,33 @@ def tcpThread(): s = socket(AF_INET, SOCK_STREAM) s.bind(('', PORTA_TCP)) s.listen(10) - while True: con, par = s.accept() - thread.start_new_thread(conexaoTcpThread, tuple([con, par])) + threading.Thread(target=conexaoTcpThread, args=tuple([con, par])).start() #---------------------------------------------------------------------------------------- #-Inicializacao das threads-------------------------------------------------------------- try: - thread.start_new_thread( tcpThread, () ) - thread.start_new_thread( recepcaoThread, () ) + threading.Thread(target=tcpThread, args=()).start() + threading.Thread(target=recepcaoThread, args=()).start() except Exception as e: - print 'Problemas com uma thread.' - print e + print('Problemas com uma thread.') + print(e) meuSocket.close() - exit(1) + sys.exit(1) -print 'Pronto.' -print '' +print('Pronto.') +print('') #---------------------------------------------------------------------------------------- #-Loop principal, usado para entrada de comandos----------------------------------------- while True: try: - comando = raw_input('Comando: ') + comando = input('Comando: ') trataComando(comando) except KeyboardInterrupt: encerrarPrograma() except Exception as e: - print '\nHouve um erro!' - print e + print('\nHouve um erro!') + print(e) #---------------------------------------------------------------------------------------- From 2650f6434080e7b652efd9b7925ada4b7c6c192d Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Wed, 28 Jan 2026 21:18:42 -0300 Subject: [PATCH 02/31] CI: pyliny job on python 3 --- .github/workflows/pylint.yml | 37 ++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index 5518e84..7472cd6 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -1,22 +1,27 @@ name: Pylint -on: [push] +on: [push, pull_request] jobs: + build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest + steps: - - uses: actions/checkout@v3 - - name: Set up Python 2.7 - run: | - sudo add-apt-repository universe - sudo apt update - sudo apt install -y python2 - - name: Install dependencies - run: | - curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py - sudo python2 get-pip.py - pip2 install pylint - - name: Analysing the code with pylint - run: | - pylint $(git ls-files '*.py') + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pylint + + - name: Analyze the code with Pylint + run: | + pylint --output-format=colorized $(git ls-files '*.py') From 469118a9c0204e582713dc4143596afa9df1d723 Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Thu, 29 Jan 2026 23:11:23 -0300 Subject: [PATCH 03/31] add all missing docstrings --- Definicoes.py | 3 +++ Job.py | 57 +++++++++++++++++++++++++++++++++++++++++++++------ Util.py | 8 ++++++-- grid.py | 51 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 8 deletions(-) diff --git a/Definicoes.py b/Definicoes.py index 4d64084..a1ecc58 100644 --- a/Definicoes.py +++ b/Definicoes.py @@ -1,3 +1,6 @@ +""" +Constantes e definições usadas no programa. +""" porta = 27950 # Nossa porta inicialmente portaPar = 27950 # Porta de um par... diff --git a/Job.py b/Job.py index 3476021..c18894b 100644 --- a/Job.py +++ b/Job.py @@ -1,3 +1,7 @@ +""" +Modulo que implementa a classe Job e Parte para o sistema de distribuicao de tarefas. +""" + import os import datetime import threading @@ -10,6 +14,9 @@ COMPLETO = 'COMPLETO' class Parte: + """ + Representa uma parte de um job. + """ estado = BRANCO par = None data = None # data de atribuicao @@ -17,26 +24,44 @@ class Parte: saida = None def is_branco(self): + """ + Retorna True se a parte esta em estado BRANCO. + """ return self.estado == BRANCO def is_atribuido(self): + """ + Retorna True se a parte esta em estado ATRIBUIDO. + """ return self.estado == ATRIBUIDO def is_completo(self): + """ + Retorna True se a parte esta em estado COMPLETO. + """ return self.estado == COMPLETO def atribui(self, par): + """ + Atribui a parte ao par especificado. + """ self.estado = ATRIBUIDO self.par = par self.data = datetime.datetime.now() def set_completo(self, saida): + """ + Marca a parte como COMPLETO, definindo o nome do arquivo de saida. + """ self.estado = COMPLETO self.saida = saida self.par = None self.data = None class Job: + """ + Representa um job para processamento, composto por varias partes. + """ nome = None programa = None diretorio = None @@ -68,8 +93,10 @@ def __init__(self, nome, programa, diretorio, arquivo): print('Job carregado com ', self.partes, ' partes.') - # True se todas tarefas estao completas, False se contrario def finalizado(self): + """ + Retorna True se todas as partes do job estiverem completas. + """ valor = True lock.acquire() ### Inicio de secao critica ### for p in self.listaPartes: @@ -80,10 +107,16 @@ def finalizado(self): return valor def inserePar(self, par): + """ + Insere um par na lista de pares que participam do job. + """ if par not in self.listaPares: self.listaPares.append(par) def removePar(self, par): + """ + Remove um par da lista de pares que participam do job. + """ if par in self.listaPares: self.listaPares.remove(par) if par in self.listaParOcupado: @@ -95,8 +128,10 @@ def removePar(self, par): t.par = None t.data = None - # Atribui uma parte do job ao par especificado def atribuiParteAoPar(self, parte, par): + """ + Atribui uma parte do job ao par especificado + """ ok = True lock.acquire() ### Inicio de secao critica ### if parte not in self.listaPartes: @@ -121,12 +156,18 @@ def atribuiParteAoPar(self, parte, par): lock.release() ### Fim de secao critica ### def possuiParLivre(self): + """ + Retorna True se houver algum par livre para receber uma parte. + """ lock.acquire() ### Inicio de secao critica ### retorno = len(self.listaParOcupado) < len(self.listaPares) lock.release() ### Fim de secao critica ### return retorno def proximoParLivre(self): + """ + Retorna o proximo par livre para receber uma parte. + """ parRetorno = None lock.acquire() ### Inicio de secao critica ### for par in self.listaPares: @@ -136,9 +177,11 @@ def proximoParLivre(self): lock.release() ### Fim de secao critica ### return parRetorno - # Muda para COMPLETO o estado da parte que esta relacionada a nomeSaida - # e tambem retira da listaParOcupado aquele que estiver com essa parte (se existir) - def finalizaParte(self, nomeSaida, par): + def finalizaParte(self, nomeSaida, par): + """ + Muda para COMPLETO o estado da parte que esta relacionada a nomeSaida + e também retira da listaParOcupado aquele que estiver com essa parte (se existir). + """ lock.acquire() ### Inicio de secao critica ### for parte in self.listaPartes: if parte.entrada[:-3] == nomeSaida[:-4]: # correspondencia entre os nomes sem as extensoes @@ -152,8 +195,10 @@ def finalizaParte(self, nomeSaida, par): parte.set_completo(nomeSaida) lock.release() ### Fim de secao critica ### - # Retorna True se um dado par esta ocupado em alguma parte do job def isParOcupado(self, par): + """ + Retorna True se um dado par está ocupado em alguma parte do job. + """ valor = False lock.acquire() ### Inicio de secao critica ### for parOcup in self.listaParOcupado: diff --git a/Util.py b/Util.py index 822ce61..79a5d47 100644 --- a/Util.py +++ b/Util.py @@ -1,8 +1,12 @@ - -# util.py (funcoes utilitarias soltas) +""" +Funções utilitárias diversas. +""" #---------------------------------------------------------------------------------------- def exibirAjudaDeComandos(): + """ + Exibe a ajuda de comandos na tela. + """ print('') print('Meu Grid 0.2 por Tiago Matos') print('') diff --git a/grid.py b/grid.py index 1dd063f..1e255ef 100644 --- a/grid.py +++ b/grid.py @@ -1,3 +1,8 @@ +""" +Módulo principal do sistema de computacao em grid. +Contém o ponto de entrada e a lógica principal do programa. +""" + from subprocess import call from socket import socket, AF_INET, SOCK_DGRAM, SOCK_STREAM import threading @@ -20,6 +25,9 @@ #---------------------------------------------------------------------------------------- def encerrarPrograma(): + """ + Encerra o programa de forma ordenada. + """ print('') print('\nParando o programa.') for par in listaPares: @@ -30,6 +38,9 @@ def encerrarPrograma(): #---------------------------------------------------------------------------------------- def processaPacote(msgComDados, enderecoPar): + """ + Processa um pacote UDP recebido de um par. + """ msg = msgComDados.decode('utf-8').split(';') # Decode bytes to string resposta = 'void' if msg[0] == 'ok': @@ -80,6 +91,9 @@ def processaPacote(msgComDados, enderecoPar): #---------------------------------------------------------------------------------------- def recepcaoThread(): + """ + Thread que aguarda a recepção de pacotes UDP e os envia para o devido processamento. + """ while True: try: msgComDados, enderecoPar = meuSocket.recvfrom(2048) @@ -90,6 +104,9 @@ def recepcaoThread(): #---------------------------------------------------------------------------------------- def contactaPares(): + """ + Tenta contactar pares listados no arquivo 'peerlist'. + """ try: arquivoPares = [line.strip() for line in open('peerlist')] except Exception as e: @@ -106,6 +123,9 @@ def contactaPares(): #---------------------------------------------------------------------------------------- def enviarArquivo(par, arquivo): + """ + Transfere via TCP um arquivo para um par. + """ tcpSocket = socket(AF_INET, SOCK_STREAM) tcpSocket.connect((par[0], PORTA_TCP_PAR)) f = open (arquivo, 'rb') @@ -135,6 +155,9 @@ def enviarArquivo(par, arquivo): #---------------------------------------------------------------------------------------- def preparaJobNoPar(par): + """ + Prepara um par para receber o job carregado. + """ global job tcpSocket = socket(AF_INET, SOCK_STREAM) tcpSocket.connect((par[0], PORTA_TCP_PAR)) @@ -153,6 +176,9 @@ def preparaJobNoPar(par): #-Transfere via TCP um arquivo de entrada para um par------------------------------------ def enviaEntrada(entrada, par): + """ + Transfere via TCP um arquivo de entrada para um par. + """ f = None arquivo = './jobs/' + job.diretorio + '/entrada/' + entrada @@ -196,6 +222,9 @@ def enviaEntrada(entrada, par): #-Envia um comando para o par executar uma parte do job---------------------------------- def executaParteNoPar(parte, par): + """ + Envia um comando para o par executar uma parte do job. + """ tcpSocket = socket(AF_INET, SOCK_STREAM) # Formato: executa|programa|diretorio_job|nome_entrada| @@ -210,6 +239,9 @@ def executaParteNoPar(parte, par): #-Thread que cuida da divisao e execucao das partes do job nos pares--------------------- def jobThread(): + """ + Thread que cuida da divisao e execucao das partes do job nos pares. + """ global job if len(listaPares) == 0: @@ -243,6 +275,9 @@ def jobThread(): #-Inicia a execucao do job--------------------------------------------------------------- def executaJob(): + """ + Inicia a execucao do job carregado na memoria. + """ global job if not job: @@ -264,6 +299,9 @@ def executaJob(): #-Carrega para a memoria o job descrito pelo arquivo------------------------------------- def carregaJob(nomeArquivo): + """ + Carrega um job a partir do arquivo especificado. + """ global job arquivoJob = [] @@ -291,6 +329,9 @@ def carregaJob(nomeArquivo): #-Interpretacao dos comandos do prompt--------------------------------------------------- def trataComando(stringComando): + """ + Interpreta e executa os comandos digitados no prompt. + """ if len(stringComando) == 0: return @@ -359,6 +400,10 @@ def trataComando(stringComando): #-Transf. via TCP, o result. do process. e arquivo de saida (se houver) para um par------ def enviaSaida(dir, saida, par): + """ + Transfere via TCP resultados sobre o processamento e o arquivo de saida (caso haja) + gerado por um job para um determinado par. + """ f = None arquivo = './temp/' + dir + '/saida/' + saida @@ -405,6 +450,9 @@ def enviaSaida(dir, saida, par): #-Thread da conexao criada numa interacao com um par------------------------------------- def conexaoTcpThread(con, par): + """ + Thread que cuida de uma conexao TCP com um par. + """ global job buff = 1024 @@ -512,6 +560,9 @@ def conexaoTcpThread(con, par): #-Thread do socket de boas-vindas do tcp------------------------------------------------- def tcpThread(): + """ + Thread que aguarda conexoes TCP de pares. + """ s = socket(AF_INET, SOCK_STREAM) s.bind(('', PORTA_TCP)) s.listen(10) From 9a508ef13d3c1e17a3f93fab05c8e710087d123d Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Thu, 29 Jan 2026 23:19:24 -0300 Subject: [PATCH 04/31] remove all unnecessary parens --- grid.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/grid.py b/grid.py index 1e255ef..9c39238 100644 --- a/grid.py +++ b/grid.py @@ -144,7 +144,7 @@ def enviarArquivo(par, arquivo): tcpSocket.send(cabecalho) dados = f.read(buff) - while (dados): + while dados: tcpSocket.send(dados) dados = f.read(buff) @@ -203,7 +203,7 @@ def enviaEntrada(entrada, par): tcpSocket.connect((par[0], PORTA_TCP_PAR)) tcpSocket.send(cabecalho) dados = f.read(buff) - while (dados): + while dados: tcpSocket.send(dados) dados = f.read(buff) except Exception as e: @@ -431,7 +431,7 @@ def enviaSaida(dir, saida, par): try: tcpSocket.send(cabecalho) dados = f.read(buff) - while (dados): + while dados: tcpSocket.send(dados) dados = f.read(buff) except Exception as e: @@ -470,7 +470,7 @@ def conexaoTcpThread(con, par): recebidos = 0 while recebidos < tamanho: r = con.recv(buff) - while (r): + while r: recebidos += len(r) f.write(r) r = con.recv(buff) @@ -502,7 +502,7 @@ def conexaoTcpThread(con, par): recebidos = 0 while recebidos < tamanho: r = con.recv(buff) - while (r): + while r: recebidos += len(r) f.write(r) r = con.recv(buff) @@ -520,7 +520,7 @@ def conexaoTcpThread(con, par): recebidos = 0 while recebidos < tamanho: r = con.recv(buff) - while (r): + while r: recebidos += len(r) f.write(r) r = con.recv(buff) @@ -552,7 +552,7 @@ def conexaoTcpThread(con, par): resposta = 'erro' enviaSaida(nomeDiretorioJob, nomeSaida, par) else: - while (r): + while r: r = con.recv(buff) con.close() From 69211d4a1046e7614575b0cf0069b9a6cfb3a360 Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Thu, 29 Jan 2026 23:24:49 -0300 Subject: [PATCH 05/31] adjust module to snake_case naming style --- grid.py | 4 ++-- Util.py => util.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename Util.py => util.py (94%) diff --git a/grid.py b/grid.py index 9c39238..8986c11 100644 --- a/grid.py +++ b/grid.py @@ -12,7 +12,7 @@ from Job import * from Definicoes import * -from Util import * +from util import exibir_ajuda_geral_de_comandos meuSocket = socket(AF_INET, SOCK_DGRAM) # IPv4 e UDP meuSocket.settimeout(3) @@ -338,7 +338,7 @@ def trataComando(stringComando): comando = stringComando.split(' ') if comando[0] == 'ajuda': - exibirAjudaDeComandos() + exibir_ajuda_geral_de_comandos() elif comando[0] == 'contato': contactaPares() elif comando[0] == 'mensagem': diff --git a/Util.py b/util.py similarity index 94% rename from Util.py rename to util.py index 79a5d47..6d9aace 100644 --- a/Util.py +++ b/util.py @@ -3,7 +3,7 @@ """ #---------------------------------------------------------------------------------------- -def exibirAjudaDeComandos(): +def exibir_ajuda_geral_de_comandos(): """ Exibe a ajuda de comandos na tela. """ From 898ba71cfe89d5dc20d3bce267b81433f8922dd0 Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Fri, 30 Jan 2026 12:40:02 -0300 Subject: [PATCH 06/31] rename a couple of const names --- Definicoes.py | 4 ++-- grid.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Definicoes.py b/Definicoes.py index a1ecc58..24b4d20 100644 --- a/Definicoes.py +++ b/Definicoes.py @@ -2,8 +2,8 @@ Constantes e definições usadas no programa. """ -porta = 27950 # Nossa porta inicialmente -portaPar = 27950 # Porta de um par... +PORTA_UDP = 27950 # Nossa porta inicialmente +PORTA_UDP_PAR = 27950 # Porta de um par... PORTA_TCP = 47555 # Nossa porta tcp inicialmente PORTA_TCP_PAR = 47555 # Porta tcp de um par... diff --git a/grid.py b/grid.py index 8986c11..2cc8205 100644 --- a/grid.py +++ b/grid.py @@ -16,7 +16,7 @@ meuSocket = socket(AF_INET, SOCK_DGRAM) # IPv4 e UDP meuSocket.settimeout(3) -meuSocket.bind(('', porta)) +meuSocket.bind(('', PORTA_UDP)) maxDePares = 3 listaPares = [] @@ -117,7 +117,7 @@ def contactaPares(): print('Arquivo peerlist vazio!') elif len(listaPares) < len(arquivoPares) and len(listaPares) < 3: for enderecoPar in arquivoPares: - meuSocket.sendto(b'conect', (enderecoPar, portaPar)) # Send bytes + meuSocket.sendto(b'conect', (enderecoPar, PORTA_UDP_PAR)) # Send bytes print('\nTentando contactar a: ' + enderecoPar) #---------------------------------------------------------------------------------------- From aa786f621c06232a314eec25746b11c05b386c0a Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Fri, 30 Jan 2026 12:43:09 -0300 Subject: [PATCH 07/31] rename module --- Definicoes.py => definicoes.py | 0 grid.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename Definicoes.py => definicoes.py (100%) diff --git a/Definicoes.py b/definicoes.py similarity index 100% rename from Definicoes.py rename to definicoes.py diff --git a/grid.py b/grid.py index 2cc8205..eef88ad 100644 --- a/grid.py +++ b/grid.py @@ -11,7 +11,7 @@ import sys from Job import * -from Definicoes import * +from definicoes import PORTA_UDP, PORTA_UDP_PAR, PORTA_TCP, PORTA_TCP_PAR from util import exibir_ajuda_geral_de_comandos meuSocket = socket(AF_INET, SOCK_DGRAM) # IPv4 e UDP From d67ede392a826c17b28d397a52179f08d16cde2c Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Fri, 30 Jan 2026 13:24:31 -0300 Subject: [PATCH 08/31] enforcing snake_case naming style --- Job.py | 100 ++++++------- grid.py | 424 ++++++++++++++++++++++++++++---------------------------- 2 files changed, 262 insertions(+), 262 deletions(-) diff --git a/Job.py b/Job.py index c18894b..4b97948 100644 --- a/Job.py +++ b/Job.py @@ -67,27 +67,27 @@ class Job: diretorio = None arquivo = None partes = 0 - listaPartes = [] - listaPares = [] - listaParOcupado = [] + lista_partes = [] + lista_pares = [] + lista_par_ocupado = [] def __init__(self, nome, programa, diretorio, arquivo): self.nome = nome self.programa = programa self.diretorio = diretorio self.arquivo = arquivo - dirEntrada = f'./jobs/{diretorio}/entrada' - dirSaida = f'./jobs/{diretorio}/saida' + dir_entrada = f'./jobs/{diretorio}/entrada' + dir_saida = f'./jobs/{diretorio}/saida' print('') - for file in os.listdir(dirEntrada): + for file in os.listdir(dir_entrada): if file.endswith(".in"): parte = Parte() parte.entrada = file - if os.path.isfile(f'{dirSaida}/{file[:-3]}.out'): + if os.path.isfile(f'{dir_saida}/{file[:-3]}.out'): parte.estado = COMPLETO parte.saida = f'{file[:-3]}.out' - self.listaPartes.append(parte) + self.lista_partes.append(parte) self.partes += 1 print(file, ' - ', parte.estado) @@ -99,51 +99,51 @@ def finalizado(self): """ valor = True lock.acquire() ### Inicio de secao critica ### - for p in self.listaPartes: - if p.estado != COMPLETO: + for parte in self.lista_partes: + if parte.estado != COMPLETO: valor = False break lock.release() ### Fim de secao critica ### return valor - def inserePar(self, par): + def insere_par(self, par): """ Insere um par na lista de pares que participam do job. """ - if par not in self.listaPares: - self.listaPares.append(par) + if par not in self.lista_pares: + self.lista_pares.append(par) - def removePar(self, par): + def remove_par(self, par): """ Remove um par da lista de pares que participam do job. """ - if par in self.listaPares: - self.listaPares.remove(par) - if par in self.listaParOcupado: - self.listaParOcupado.remove(par) - for t in self.listaPartes: - if t.estado == ATRIBUIDO: - if t.par == par: - t.estado = BRANCO - t.par = None - t.data = None + if par in self.lista_pares: + self.lista_pares.remove(par) + if par in self.lista_par_ocupado: + self.lista_par_ocupado.remove(par) + for parte in self.lista_partes: + if parte.estado == ATRIBUIDO: + if parte.par == par: + parte.estado = BRANCO + parte.par = None + parte.data = None - def atribuiParteAoPar(self, parte, par): + def atribui_parte_ao_par(self, parte, par): """ - Atribui uma parte do job ao par especificado + Atribui uma parte do job ao par especificado """ ok = True lock.acquire() ### Inicio de secao critica ### - if parte not in self.listaPartes: + if parte not in self.lista_partes: print('ERRO: Parte ', parte.entrada, ' nao esta no job ', self.nome) ok = False - elif par not in self.listaPares: + elif par not in self.lista_pares: print('ERRO: Par ', par, ' nao participa no job ', self.nome) ok = False elif parte.estado == COMPLETO: print('NAO pode atribuir a parte ', parte.entrada, ' do job ', self.nome, ' pois ja esta completa.') ok = False - elif par in self.listaParOcupado: + elif par in self.lista_par_ocupado: print('ERRO: Par ', par, ' ja esta ocupado com uma tarefa') ok = False elif parte.estado == ATRIBUIDO: @@ -151,58 +151,58 @@ def atribuiParteAoPar(self, parte, par): ok = False # tudo ok, pode encadear if ok: - self.listaPartes[self.listaPartes.index(parte)].atribui(par) - self.listaParOcupado.append(par) + self.lista_partes[self.lista_partes.index(parte)].atribui(par) + self.lista_par_ocupado.append(par) lock.release() ### Fim de secao critica ### - def possuiParLivre(self): + def possui_par_livre(self): """ Retorna True se houver algum par livre para receber uma parte. """ lock.acquire() ### Inicio de secao critica ### - retorno = len(self.listaParOcupado) < len(self.listaPares) + retorno = len(self.lista_par_ocupado) < len(self.lista_pares) lock.release() ### Fim de secao critica ### return retorno - def proximoParLivre(self): + def proximo_par_livre(self): """ Retorna o proximo par livre para receber uma parte. """ - parRetorno = None + par_retorno = None lock.acquire() ### Inicio de secao critica ### - for par in self.listaPares: - if par not in self.listaParOcupado: - parRetorno = par + for par in self.lista_pares: + if par not in self.lista_par_ocupado: + par_retorno = par break lock.release() ### Fim de secao critica ### - return parRetorno + return par_retorno - def finalizaParte(self, nomeSaida, par): + def finaliza_parte(self, nome_saida, par): """ Muda para COMPLETO o estado da parte que esta relacionada a nomeSaida e também retira da listaParOcupado aquele que estiver com essa parte (se existir). """ lock.acquire() ### Inicio de secao critica ### - for parte in self.listaPartes: - if parte.entrada[:-3] == nomeSaida[:-4]: # correspondencia entre os nomes sem as extensoes + for parte in self.lista_partes: + if parte.entrada[:-3] == nome_saida[:-4]: # correspondencia entre os nomes sem as extensoes # se a parte ja estiver completa, apenas passamos if parte.is_branco(): - parte.set_completo(nomeSaida) + parte.set_completo(nome_saida) elif parte.is_atribuido(): - for parOcup in self.listaParOcupado: - if parOcup[0] == par[0]: - self.listaParOcupado.remove(parOcup) - parte.set_completo(nomeSaida) + for par_ocupado in self.lista_par_ocupado: + if par_ocupado[0] == par[0]: + self.lista_par_ocupado.remove(par_ocupado) + parte.set_completo(nome_saida) lock.release() ### Fim de secao critica ### - def isParOcupado(self, par): + def is_par_ocupado(self, par): """ Retorna True se um dado par está ocupado em alguma parte do job. """ valor = False lock.acquire() ### Inicio de secao critica ### - for parOcup in self.listaParOcupado: - if parOcup[0] == par[0]: + for par_ocupado in self.lista_par_ocupado: + if par_ocupado[0] == par[0]: valor = True break lock.release() ### Fim de secao critica ### diff --git a/grid.py b/grid.py index eef88ad..3f8bb9e 100644 --- a/grid.py +++ b/grid.py @@ -14,56 +14,56 @@ from definicoes import PORTA_UDP, PORTA_UDP_PAR, PORTA_TCP, PORTA_TCP_PAR from util import exibir_ajuda_geral_de_comandos -meuSocket = socket(AF_INET, SOCK_DGRAM) # IPv4 e UDP -meuSocket.settimeout(3) -meuSocket.bind(('', PORTA_UDP)) +meu_socket_udp = socket(AF_INET, SOCK_DGRAM) # IPv4 e UDP +meu_socket_udp.settimeout(3) +meu_socket_udp.bind(('', PORTA_UDP)) -maxDePares = 3 -listaPares = [] +MAX_DE_PARES = 3 +lista_pares = [] job = None #---------------------------------------------------------------------------------------- -def encerrarPrograma(): +def encerrar_programa(): """ Encerra o programa de forma ordenada. """ print('') print('\nParando o programa.') - for par in listaPares: - meuSocket.sendto('disconect', par) - meuSocket.close() + for par in lista_pares: + meu_socket_udp.sendto('disconect', par) + meu_socket_udp.close() sys.exit(0) #---------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------- -def processaPacote(msgComDados, enderecoPar): +def processa_pacote(msg_com_dados, endereco_par): """ Processa um pacote UDP recebido de um par. """ - msg = msgComDados.decode('utf-8').split(';') # Decode bytes to string + msg = msg_com_dados.decode('utf-8').split(';') # Decode bytes to string resposta = 'void' if msg[0] == 'ok': if msg[1] == 'contact': - if len(listaPares) < maxDePares: - if enderecoPar not in listaPares: - listaPares.append(enderecoPar) - print(f'\nNosso pedido de contato foi aceito por {str(enderecoPar)}') + if len(lista_pares) < MAX_DE_PARES: + if endereco_par not in lista_pares: + lista_pares.append(endereco_par) + print(f'\nNosso pedido de contato foi aceito por {str(endereco_par)}') else: resposta = 'disconect' elif msg[0] == 'conect': - if len(listaPares) < maxDePares: - if enderecoPar not in listaPares: - listaPares.append(enderecoPar) + if len(lista_pares) < MAX_DE_PARES: + if endereco_par not in lista_pares: + lista_pares.append(endereco_par) resposta = 'ok;contact' - print(f'\nPedido de contato aceito originado de {str(enderecoPar)}') + print(f'\nPedido de contato aceito originado de {str(endereco_par)}') else: resposta = 'not' elif msg[0] == 'disconect': - if enderecoPar in listaPares: - listaPares.remove(enderecoPar) - print(f'Contato desfeito por solicitacao de {str(enderecoPar)}') - elif enderecoPar in listaPares: + if endereco_par in lista_pares: + lista_pares.remove(endereco_par) + print(f'Contato desfeito por solicitacao de {str(endereco_par)}') + elif endereco_par in lista_pares: if msg[0] == 'do': resposta = 'what?' if len(msg) > 3: @@ -74,7 +74,7 @@ def processaPacote(msgComDados, enderecoPar): except Exception: # Changed to catch all exceptions resposta = 'erro cmd' elif msg[0] == 'msg': - msg_print = 'Msg. de ' + str(enderecoPar) + ' : ' + msg_print = 'Msg. de ' + str(endereco_par) + ' : ' if len(msg) > 1: msg_print += msg[1] print('') @@ -84,51 +84,51 @@ def processaPacote(msgComDados, enderecoPar): resposta = 'not' if resposta != 'void': - meuSocket.sendto(resposta.encode('utf-8'), enderecoPar) # Encode string to bytes + meu_socket_udp.sendto(resposta.encode('utf-8'), endereco_par) # Encode string to bytes #print('') #print('Enviei: ', resposta, ' Para: ', str(enderecoPar)) #---------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------- -def recepcaoThread(): +def recepcao_thread(): """ Thread que aguarda a recepção de pacotes UDP e os envia para o devido processamento. """ while True: try: - msgComDados, enderecoPar = meuSocket.recvfrom(2048) - threading.Thread(target=processaPacote, args=(msgComDados, enderecoPar)).start() - except Exception as e: - print(e) + msg_com_dados, endereco_par = meu_socket_udp.recvfrom(2048) + threading.Thread(target=processa_pacote, args=(msg_com_dados, endereco_par)).start() + except Exception as ex: + print(ex) #---------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------- -def contactaPares(): +def contacta_pares(): """ Tenta contactar pares listados no arquivo 'peerlist'. """ try: - arquivoPares = [line.strip() for line in open('peerlist')] - except Exception as e: + arquivo_pares = [line.strip() for line in open('peerlist')] + except Exception as ex: print('Erro ao acessar o arquivo peerlist!') - print(e) + print(ex) return - if len(arquivoPares) == 0: + if len(arquivo_pares) == 0: print('Arquivo peerlist vazio!') - elif len(listaPares) < len(arquivoPares) and len(listaPares) < 3: - for enderecoPar in arquivoPares: - meuSocket.sendto(b'conect', (enderecoPar, PORTA_UDP_PAR)) # Send bytes - print('\nTentando contactar a: ' + enderecoPar) + elif len(lista_pares) < len(arquivo_pares) and len(lista_pares) < 3: + for endereco_par in arquivo_pares: + meu_socket_udp.sendto(b'conect', (endereco_par, PORTA_UDP_PAR)) # Send bytes + print('\nTentando contactar a: ' + endereco_par) #---------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------- -def enviarArquivo(par, arquivo): +def enviar_arquivo(par, arquivo): """ Transfere via TCP um arquivo para um par. """ - tcpSocket = socket(AF_INET, SOCK_STREAM) - tcpSocket.connect((par[0], PORTA_TCP_PAR)) - f = open (arquivo, 'rb') + tcp_socket = socket(AF_INET, SOCK_STREAM) + tcp_socket.connect((par[0], PORTA_TCP_PAR)) + file = open (arquivo, 'rb') buff = 1024 arquivo = arquivo.replace('\\', '/') nome = arquivo.split('/')[-1] @@ -141,57 +141,57 @@ def enviarArquivo(par, arquivo): print('ENVIANDO: ', arquivo, ' de ', tamanho) - tcpSocket.send(cabecalho) + tcp_socket.send(cabecalho) - dados = f.read(buff) + dados = file.read(buff) while dados: - tcpSocket.send(dados) - dados = f.read(buff) + tcp_socket.send(dados) + dados = file.read(buff) - tcpSocket.close() - f.close() + tcp_socket.close() + file.close() print('FIM DO ENVIO DE: ', arquivo) #---------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------- -def preparaJobNoPar(par): +def prepara_job_no_par(par): """ Prepara um par para receber o job carregado. """ global job - tcpSocket = socket(AF_INET, SOCK_STREAM) - tcpSocket.connect((par[0], PORTA_TCP_PAR)) - tcpSocket.sendall(f'job|{job.diretorio}|'.encode('utf-8')) + tcp_socket = socket(AF_INET, SOCK_STREAM) + tcp_socket.connect((par[0], PORTA_TCP_PAR)) + tcp_socket.sendall(f'job|{job.diretorio}|'.encode('utf-8')) buff = 1024 print('') - r = tcpSocket.recv(buff).decode('utf-8') - if r == 'ok': + resp = tcp_socket.recv(buff).decode('utf-8') + if resp == 'ok': print(f'PAR {par[0]} esta preparado para o job {job.nome}') - job.inserePar(par) + job.insere_par(par) else: print(f'PAR {par[0]} nao pode preparar (ou nao e possivel confirmar) para o job {job.nome}') - job.removePar(par) - tcpSocket.close() + job.remove_par(par) + tcp_socket.close() #---------------------------------------------------------------------------------------- #-Transfere via TCP um arquivo de entrada para um par------------------------------------ -def enviaEntrada(entrada, par): +def envia_entrada(entrada, par): """ Transfere via TCP um arquivo de entrada para um par. """ - f = None + file = None arquivo = './jobs/' + job.diretorio + '/entrada/' + entrada try: - f = open(arquivo, 'rb') - except Exception as e: + file = open(arquivo, 'rb') + except Exception as ex: print('ERRO ao acessar o arquivo de entrada ', arquivo) - print(e) + print(ex) return False tamanho = os.path.getsize(arquivo) buff = 1024 - tcpSocket = socket(AF_INET, SOCK_STREAM) + tcp_socket = socket(AF_INET, SOCK_STREAM) cabecalho = 'entrada|' + job.diretorio + '|' + entrada + '|' + str(tamanho) + '|' # entrada|diretorio_job|nome_entrada|tamanho| # aqui preenchemos o cabecalho com esp. em branco ate ele ficar com tam. do buffer @@ -200,81 +200,81 @@ def enviaEntrada(entrada, par): try: print('ENVIANDO ENTRADA: ', arquivo, ' de ', tamanho) - tcpSocket.connect((par[0], PORTA_TCP_PAR)) - tcpSocket.send(cabecalho) - dados = f.read(buff) + tcp_socket.connect((par[0], PORTA_TCP_PAR)) + tcp_socket.send(cabecalho) + dados = file.read(buff) while dados: - tcpSocket.send(dados) - dados = f.read(buff) - except Exception as e: + tcp_socket.send(dados) + dados = file.read(buff) + except Exception as ex: print('\nHouve um erro na tranf. de um arquivo!') - print(e) - tcpSocket.close() - f.close() + print(ex) + tcp_socket.close() + file.close() return False - tcpSocket.close() - f.close() + tcp_socket.close() + file.close() print('FIM DO ENVIO DE: ', arquivo) return True #---------------------------------------------------------------------------------------- #-Envia um comando para o par executar uma parte do job---------------------------------- -def executaParteNoPar(parte, par): +def executa_parte_no_par(parte, par): """ Envia um comando para o par executar uma parte do job. """ - tcpSocket = socket(AF_INET, SOCK_STREAM) + tcp_socket = socket(AF_INET, SOCK_STREAM) # Formato: executa|programa|diretorio_job|nome_entrada| msg = 'executa|' + job.programa + '|' + job.diretorio + '|' + parte.entrada + '|' print('EXECUTANDO: ', job.programa, ' sobre a entrada ', parte.entrada, ' em ', par) - tcpSocket.connect((par[0], PORTA_TCP_PAR)) - tcpSocket.send(msg) - tcpSocket.close() + tcp_socket.connect((par[0], PORTA_TCP_PAR)) + tcp_socket.send(msg) + tcp_socket.close() #---------------------------------------------------------------------------------------- #-Thread que cuida da divisao e execucao das partes do job nos pares--------------------- -def jobThread(): +def job_thread(): """ Thread que cuida da divisao e execucao das partes do job nos pares. """ global job - if len(listaPares) == 0: + if len(lista_pares) == 0: print('\nSem contato de pares para compartilhar o processamento.') return # esse loop tambem popula a lista job.listaPares com os pares prontos - for par in listaPares: - preparaJobNoPar(par) + for par in lista_pares: + prepara_job_no_par(par) - if len(job.listaPares) == 0: + if len(job.lista_pares) == 0: print('\nNenhum par ficou pronto para o processamento.') return while not job.finalizado(): - if job.possuiParLivre(): - for parte in job.listaPartes: + if job.possui_par_livre(): + for parte in job.lista_partes: print(parte.estado, parte.entrada) if parte.is_branco(): - parLivre = job.proximoParLivre() - if parLivre is None: + par_livre = job.proximo_par_livre() + if par_livre is None: break - if not enviaEntrada(parte.entrada, parLivre): + if not envia_entrada(parte.entrada, par_livre): break - job.atribuiParteAoPar(parte, parLivre) - executaParteNoPar(parte, parLivre) + job.atribui_parte_ao_par(parte, par_livre) + executa_parte_no_par(parte, par_livre) time.sleep(0.7) # Estimar com experimentos qual o melhor valor... print('\nSUCESSO. O job foi concluido.') #---------------------------------------------------------------------------------------- #-Inicia a execucao do job--------------------------------------------------------------- -def executaJob(): +def executa_job(): """ Inicia a execucao do job carregado na memoria. """ @@ -283,99 +283,99 @@ def executaJob(): if not job: print('\nNenhum job carregado.') return - if len(job.listaPartes) == 0: + if len(job.lista_partes) == 0: print('\nJob sem tarefas.') return if job.finalizado(): print(f'\nTodas as tarefas do job \'{job.nome}\' foram completas.') return - for parte in job.listaPartes: + for parte in job.lista_partes: if parte.is_branco(): print('PARTE: ', parte.entrada, ' EM BRANCO') - threading.Thread(target=jobThread, args=()).start() + threading.Thread(target=job_thread, args=()).start() #---------------------------------------------------------------------------------------- #-Carrega para a memoria o job descrito pelo arquivo------------------------------------- -def carregaJob(nomeArquivo): +def carrega_job(nome_arquivo): """ Carrega um job a partir do arquivo especificado. """ global job - arquivoJob = [] + arquivo_job = [] try: - for line in open('jobs/' + nomeArquivo): + for line in open('jobs/' + nome_arquivo): entrada = line.strip() if len(entrada) > 0: if entrada[0] != '#': - arquivoJob.append(entrada) - except Exception as e: - print(f'Erro ao acessar o arquivo de job: {nomeArquivo}') - print(e) + arquivo_job.append(entrada) + except Exception as ex: + print(f'Erro ao acessar o arquivo de job: {nome_arquivo}') + print(ex) return - if len(arquivoJob) == 0: - print(f'Arquivo de job {nomeArquivo} está vazio.') + if len(arquivo_job) == 0: + print(f'Arquivo de job {nome_arquivo} está vazio.') return - if len(arquivoJob) < 3: - print(f'Arquivo de job {nomeArquivo} faltando parâmetros.') + if len(arquivo_job) < 3: + print(f'Arquivo de job {nome_arquivo} faltando parâmetros.') return - job = Job(arquivoJob[0], arquivoJob[1], arquivoJob[2], nomeArquivo) + job = Job(arquivo_job[0], arquivo_job[1], arquivo_job[2], nome_arquivo) #---------------------------------------------------------------------------------------- #-Interpretacao dos comandos do prompt--------------------------------------------------- -def trataComando(stringComando): +def trata_comando(string_comando): """ Interpreta e executa os comandos digitados no prompt. """ - if len(stringComando) == 0: + if len(string_comando) == 0: return - comando = stringComando.split(' ') + comando = string_comando.split(' ') if comando[0] == 'ajuda': exibir_ajuda_geral_de_comandos() elif comando[0] == 'contato': - contactaPares() + contacta_pares() elif comando[0] == 'mensagem': if len(comando) < 3: print('\nArgumentos incorretos no comando.') else: try: - idPar = int(comando[1]) + id_par = int(comando[1]) except ValueError: print('\nArgumentos incorretos no comando. Id do par deve ser numero.') return - if idPar < 0 or idPar+1 > len(listaPares): + if id_par < 0 or id_par+1 > len(lista_pares): print('\nNao temos este par na nossa lista.') else: - msgConteudo = '' - for s in comando[2:]: - msgConteudo += s + ' ' - meuSocket.sendto('msg;' + msgConteudo, listaPares[idPar]) + msg_conteudo = '' + for char in comando[2:]: + msg_conteudo += char + ' ' + meu_socket_udp.sendto('msg;' + msg_conteudo, lista_pares[id_par]) elif comando[0] == 'enviar': if len(comando) < 3: print('\nArgumentos incorretos no comando.') else: try: - idPar = int(comando[1]) + id_par = int(comando[1]) except ValueError: print('\nArgumentos incorretos no comando. Id do par deve ser numero.') return - if idPar < 0 or idPar+1 > len(listaPares): + if id_par < 0 or id_par+1 > len(lista_pares): print('\nNao temos este par na nossa lista.') - enviarArquivo(listaPares[idPar], comando[2]) + enviar_arquivo(lista_pares[id_par], comando[2]) elif comando[0] == 'pares': i = 0 - for par in listaPares: - print('#', i, ' - ', str(par), ' - Ocup.:', job.isParOcupado(par) if job is not None else False) + for par in lista_pares: + print('#', i, ' - ', str(par), ' - Ocup.:', job.is_par_ocupado(par) if job is not None else False) i = i + 1 if i == 0: print('Sem pares contactados.') @@ -383,41 +383,41 @@ def trataComando(stringComando): if len(comando) < 2: print('\nArgumentos incorretos. Especifique o arquivo do job.') return - carregaJob(comando[1]) + carrega_job(comando[1]) elif comando[0] == 'estado': if job is None: print('Sem job carregado.') else: - for parte in job.listaPartes: + for parte in job.lista_partes: print('#', parte.entrada, '-', parte.estado) elif comando[0] == 'executa': - executaJob() + executa_job() elif comando[0] == 'sair': - encerrarPrograma() + encerrar_programa() else: print('Comando nao reconhecido.') #---------------------------------------------------------------------------------------- #-Transf. via TCP, o result. do process. e arquivo de saida (se houver) para um par------ -def enviaSaida(dir, saida, par): +def envia_saida(dir, saida, par): """ Transfere via TCP resultados sobre o processamento e o arquivo de saida (caso haja) gerado por um job para um determinado par. """ - f = None + file = None arquivo = './temp/' + dir + '/saida/' + saida try: - f = open(arquivo, 'rb') - except Exception as e: + file = open(arquivo, 'rb') + except Exception as ex: print(f'\nERRO ao acessar o arquivo de saida {arquivo}') - print(e) + print(ex) return False tamanho = os.path.getsize(arquivo) buff = 1024 - tcpSocket = socket(AF_INET, SOCK_STREAM) + tcp_socket = socket(AF_INET, SOCK_STREAM) cabecalho = 'saida|' + dir + '|' + saida + '|' + str(tamanho) + '|' # saida|diretorio_job|nome_saida|tamanho| # aqui preenchemos o cabecalho com esp. em branco ate ele ficar com tam. do buffer @@ -426,159 +426,159 @@ def enviaSaida(dir, saida, par): print(f'\nENVIANDO SAIDA: {arquivo} de {tamanho}') - tcpSocket.connect((par[0], PORTA_TCP_PAR)) + tcp_socket.connect((par[0], PORTA_TCP_PAR)) try: - tcpSocket.send(cabecalho) - dados = f.read(buff) + tcp_socket.send(cabecalho) + dados = file.read(buff) while dados: - tcpSocket.send(dados) - dados = f.read(buff) - except Exception as e: + tcp_socket.send(dados) + dados = file.read(buff) + except Exception as ex: print('\nHouve um erro na transf. de um arquivo!') - print(e) - tcpSocket.close() - f.close() + print(ex) + tcp_socket.close() + file.close() return False - tcpSocket.close() - f.close() + tcp_socket.close() + file.close() print(f'\nFIM DO ENVIO DE: {arquivo}') return True #---------------------------------------------------------------------------------------- #-Thread da conexao criada numa interacao com um par------------------------------------- -def conexaoTcpThread(con, par): +def conexao_tcp_thread(con, par): """ Thread que cuida de uma conexao TCP com um par. """ global job buff = 1024 - r = con.recv(buff) + resp = con.recv(buff) #print '' #print 'TCP>', r[:200] ####### DBG - cabecalho = r.split('|') + cabecalho = resp.split('|') comando = cabecalho[0] if comando == 'envio': nome = cabecalho[1] tamanho = int(cabecalho[2]) - f = open('./recebidos/' + nome, 'wb+') + file = open('./recebidos/' + nome, 'wb+') recebidos = 0 while recebidos < tamanho: - r = con.recv(buff) - while r: - recebidos += len(r) - f.write(r) - r = con.recv(buff) - if not r: + resp = con.recv(buff) + while resp: + recebidos += len(resp) + file.write(resp) + resp = con.recv(buff) + if not resp: break print('\nFECHANDO ARQUVIO', '- RECEBIDOS: ', recebidos) - f.close() + file.close() elif comando == 'job': - diretorioJob = './temp/' + cabecalho[1] + diretorio_job = './temp/' + cabecalho[1] resultado = '' try: - if os.path.isdir(diretorioJob): + if os.path.isdir(diretorio_job): resultado = 'ok' else: - os.makedirs(diretorioJob + '/entrada') - os.makedirs(diretorioJob + '/saida') + os.makedirs(diretorio_job + '/entrada') + os.makedirs(diretorio_job + '/saida') resultado = 'ok' - except Exception as e: + except Exception as ex: resultado = 'erro' - print(e) + print(ex) con.send(resultado) elif comando == 'entrada': - diretorioEntrada = './temp/' + cabecalho[1] + '/entrada/' - nomeEntrada = cabecalho[2] + diretorio_entrada = './temp/' + cabecalho[1] + '/entrada/' + nome_entrada = cabecalho[2] tamanho = int(cabecalho[3]) - f = open(diretorioEntrada + nomeEntrada, 'wb+') + file = open(diretorio_entrada + nome_entrada, 'wb+') recebidos = 0 while recebidos < tamanho: - r = con.recv(buff) - while r: - recebidos += len(r) - f.write(r) - r = con.recv(buff) - if not r: + resp = con.recv(buff) + while resp: + recebidos += len(resp) + file.write(resp) + resp = con.recv(buff) + if not resp: break - print('\nFECHANDO ARQUIVO: ', diretorioEntrada + nomeEntrada, ' - RECEBIDOS: ', recebidos, ' de ', tamanho) - f.close() + print('\nFECHANDO ARQUIVO: ', diretorio_entrada + nome_entrada, ' - RECEBIDOS: ', recebidos, ' de ', tamanho) + file.close() elif comando == 'saida': # Formato: saida|diretorio_job|nome_saida|tamanho| - nomeDiretorio = cabecalho[1] - diretorioSaida = './jobs/' + nomeDiretorio + '/saida/' - nomeSaida = cabecalho[2] + nome_diretorio = cabecalho[1] + diretorio_saida = './jobs/' + nome_diretorio + '/saida/' + nome_saida = cabecalho[2] tamanho = int(cabecalho[3]) - f = open(diretorioSaida + nomeSaida, 'wb+') + file = open(diretorio_saida + nome_saida, 'wb+') recebidos = 0 while recebidos < tamanho: - r = con.recv(buff) - while r: - recebidos += len(r) - f.write(r) - r = con.recv(buff) - if not r: + resp = con.recv(buff) + while resp: + recebidos += len(resp) + file.write(resp) + resp = con.recv(buff) + if not resp: break - print('\nFECHANDO ARQUIVO ', diretorioSaida + nomeSaida, ' - RECEBIDOS: ', recebidos, ' de ', tamanho) - f.close() + print('\nFECHANDO ARQUIVO ', diretorio_saida + nome_saida, ' - RECEBIDOS: ', recebidos, ' de ', tamanho) + file.close() # esta parte eh muito importante, nela mudamos o estado de uma tarefa concorrentemente - if job.diretorio == nomeDiretorio: - job.finalizaParte(nomeSaida, par) + if job.diretorio == nome_diretorio: + job.finaliza_parte(nome_saida, par) elif comando == 'executa': # Formato da msg: executa|programa|diretorio_job|nome_entrada|remetente programa = cabecalho[1] - nomeDiretorioJob = cabecalho[2] - diretorioEntrada = './temp/' + nomeDiretorioJob + '/entrada/' - diretorioSaida = './temp/' + nomeDiretorioJob + '/saida/' - nomeEntrada = cabecalho[3] - nomeSaida = nomeEntrada[0:-3] + '.out' + nome_diretorio_job = cabecalho[2] + diretorio_entrada = './temp/' + nome_diretorio_job + '/entrada/' + diretorio_saida = './temp/' + nome_diretorio_job + '/saida/' + nome_entrada = cabecalho[3] + nome_saida = nome_entrada[0:-3] + '.out' remetente = par[0] resposta = '' try: print(f'\nIniciando execucao do programa {programa}') - call(['./programs/' + programa, diretorioEntrada + nomeEntrada, diretorioSaida + nomeSaida]) + call(['./programs/' + programa, diretorio_entrada + nome_entrada, diretorio_saida + nome_saida]) resposta = 'pronto' - except Exception as e: + except Exception as ex: print(f'\nERRO na execucao do programa {programa}') - print(e) + print(ex) resposta = 'erro' - enviaSaida(nomeDiretorioJob, nomeSaida, par) + envia_saida(nome_diretorio_job, nome_saida, par) else: - while r: - r = con.recv(buff) + while resp: + resp = con.recv(buff) con.close() #---------------------------------------------------------------------------------------- #-Thread do socket de boas-vindas do tcp------------------------------------------------- -def tcpThread(): +def tcp_thread(): """ Thread que aguarda conexoes TCP de pares. """ - s = socket(AF_INET, SOCK_STREAM) - s.bind(('', PORTA_TCP)) - s.listen(10) + sock = socket(AF_INET, SOCK_STREAM) + sock.bind(('', PORTA_TCP)) + sock.listen(10) while True: - con, par = s.accept() - threading.Thread(target=conexaoTcpThread, args=tuple([con, par])).start() + con, par = sock.accept() + threading.Thread(target=conexao_tcp_thread, args=tuple([con, par])).start() #---------------------------------------------------------------------------------------- #-Inicializacao das threads-------------------------------------------------------------- try: - threading.Thread(target=tcpThread, args=()).start() - threading.Thread(target=recepcaoThread, args=()).start() -except Exception as e: + threading.Thread(target=tcp_thread, args=()).start() + threading.Thread(target=recepcao_thread, args=()).start() +except Exception as ex: print('Problemas com uma thread.') - print(e) - meuSocket.close() + print(ex) + meu_socket_udp.close() sys.exit(1) print('Pronto.') @@ -589,10 +589,10 @@ def tcpThread(): while True: try: comando = input('Comando: ') - trataComando(comando) + trata_comando(comando) except KeyboardInterrupt: - encerrarPrograma() - except Exception as e: + encerrar_programa() + except Exception as ex: print('\nHouve um erro!') - print(e) + print(ex) #---------------------------------------------------------------------------------------- From d38a77e8c303cd4305451ac97609f35f43f643f5 Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Fri, 30 Jan 2026 21:12:39 -0300 Subject: [PATCH 09/31] refactor: extract code to method inside a class --- Job.py | 9 +++++++++ grid.py | 3 +-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Job.py b/Job.py index 4b97948..61fc132 100644 --- a/Job.py +++ b/Job.py @@ -207,3 +207,12 @@ def is_par_ocupado(self, par): break lock.release() ### Fim de secao critica ### return valor + + def print_status(self): + """ + Imprime em stdout o estado do job. + """ + lock.acquire() ### Inicio de secao critica ### + for parte in self.lista_partes: + print('#', parte.entrada, '-', parte.estado) + lock.release() ### Fim de secao critica ### diff --git a/grid.py b/grid.py index 3f8bb9e..f740da9 100644 --- a/grid.py +++ b/grid.py @@ -388,8 +388,7 @@ def trata_comando(string_comando): if job is None: print('Sem job carregado.') else: - for parte in job.lista_partes: - print('#', parte.entrada, '-', parte.estado) + job.print_status() elif comando[0] == 'executa': executa_job() elif comando[0] == 'sair': From ddc2880bbcd3b70a6dbfdf6b1432ee7565731815 Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Fri, 30 Jan 2026 21:26:14 -0300 Subject: [PATCH 10/31] refactor: extract func envia_mensagem --- grid.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/grid.py b/grid.py index f740da9..2c10abf 100644 --- a/grid.py +++ b/grid.py @@ -350,14 +350,7 @@ def trata_comando(string_comando): except ValueError: print('\nArgumentos incorretos no comando. Id do par deve ser numero.') return - - if id_par < 0 or id_par+1 > len(lista_pares): - print('\nNao temos este par na nossa lista.') - else: - msg_conteudo = '' - for char in comando[2:]: - msg_conteudo += char + ' ' - meu_socket_udp.sendto('msg;' + msg_conteudo, lista_pares[id_par]) + envia_mensagem(id_par, comando[2:]) elif comando[0] == 'enviar': if len(comando) < 3: print('\nArgumentos incorretos no comando.') @@ -367,10 +360,8 @@ def trata_comando(string_comando): except ValueError: print('\nArgumentos incorretos no comando. Id do par deve ser numero.') return - if id_par < 0 or id_par+1 > len(lista_pares): print('\nNao temos este par na nossa lista.') - enviar_arquivo(lista_pares[id_par], comando[2]) elif comando[0] == 'pares': i = 0 @@ -397,6 +388,18 @@ def trata_comando(string_comando): print('Comando nao reconhecido.') #---------------------------------------------------------------------------------------- +#---------------------------------------------------------------------------------------- +def envia_mensagem(id_par : int, textos : list): + """ + Envia uma mensagem de texto, via UDP, para um par com determinado id. + """ + if id_par < 0 or id_par+1 > len(lista_pares): + print('\nNao temos este par na nossa lista.') + return + mensagem = 'msg:' + ' '.join(textos) + meu_socket_udp.sendto(mensagem.encode('utf-8'), id_par) +#---------------------------------------------------------------------------------------- + #-Transf. via TCP, o result. do process. e arquivo de saida (se houver) para um par------ def envia_saida(dir, saida, par): """ From 6ae4a339bb05591feaa4780ee27e49c93f5d6c1e Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Fri, 30 Jan 2026 21:31:38 -0300 Subject: [PATCH 11/31] refactor: rename and delete some vars --- grid.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/grid.py b/grid.py index 2c10abf..080644d 100644 --- a/grid.py +++ b/grid.py @@ -401,14 +401,14 @@ def envia_mensagem(id_par : int, textos : list): #---------------------------------------------------------------------------------------- #-Transf. via TCP, o result. do process. e arquivo de saida (se houver) para um par------ -def envia_saida(dir, saida, par): +def envia_saida(diretorio, saida, par): """ Transfere via TCP resultados sobre o processamento e o arquivo de saida (caso haja) gerado por um job para um determinado par. """ file = None - arquivo = './temp/' + dir + '/saida/' + saida + arquivo = './temp/' + diretorio + '/saida/' + saida try: file = open(arquivo, 'rb') @@ -420,7 +420,7 @@ def envia_saida(dir, saida, par): tamanho = os.path.getsize(arquivo) buff = 1024 tcp_socket = socket(AF_INET, SOCK_STREAM) - cabecalho = 'saida|' + dir + '|' + saida + '|' + str(tamanho) + '|' # saida|diretorio_job|nome_saida|tamanho| + cabecalho = 'saida|' + diretorio + '|' + saida + '|' + str(tamanho) + '|' # saida|diretorio_job|nome_saida|tamanho| # aqui preenchemos o cabecalho com esp. em branco ate ele ficar com tam. do buffer # isto e, o cabecalho deve ter buff bytes de tamanho (wrkrnd) @@ -542,8 +542,6 @@ def conexao_tcp_thread(con, par): diretorio_saida = './temp/' + nome_diretorio_job + '/saida/' nome_entrada = cabecalho[3] nome_saida = nome_entrada[0:-3] + '.out' - remetente = par[0] - resposta = '' try: print(f'\nIniciando execucao do programa {programa}') call(['./programs/' + programa, diretorio_entrada + nome_entrada, diretorio_saida + nome_saida]) @@ -590,8 +588,8 @@ def tcp_thread(): #-Loop principal, usado para entrada de comandos----------------------------------------- while True: try: - comando = input('Comando: ') - trata_comando(comando) + str_comando = input('Comando: ') + trata_comando(str_comando) except KeyboardInterrupt: encerrar_programa() except Exception as ex: From 647d063013dbee84b59eb9ca63fb5e37a9eb8850 Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Fri, 30 Jan 2026 21:41:10 -0300 Subject: [PATCH 12/31] rename module --- grid.py | 2 +- Job.py => job.py | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename Job.py => job.py (100%) diff --git a/grid.py b/grid.py index 080644d..6d29a11 100644 --- a/grid.py +++ b/grid.py @@ -10,7 +10,7 @@ import os import sys -from Job import * +from job import Job from definicoes import PORTA_UDP, PORTA_UDP_PAR, PORTA_TCP, PORTA_TCP_PAR from util import exibir_ajuda_geral_de_comandos diff --git a/Job.py b/job.py similarity index 100% rename from Job.py rename to job.py From 441fc92282ab8d89bae020da5e18f99f9ca95e04 Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Fri, 30 Jan 2026 21:50:19 -0300 Subject: [PATCH 13/31] remove global var def when not needed --- grid.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/grid.py b/grid.py index 6d29a11..47ab565 100644 --- a/grid.py +++ b/grid.py @@ -158,7 +158,6 @@ def prepara_job_no_par(par): """ Prepara um par para receber o job carregado. """ - global job tcp_socket = socket(AF_INET, SOCK_STREAM) tcp_socket.connect((par[0], PORTA_TCP_PAR)) tcp_socket.sendall(f'job|{job.diretorio}|'.encode('utf-8')) @@ -242,13 +241,12 @@ def job_thread(): """ Thread que cuida da divisao e execucao das partes do job nos pares. """ - global job if len(lista_pares) == 0: print('\nSem contato de pares para compartilhar o processamento.') return - # esse loop tambem popula a lista job.listaPares com os pares prontos + # esse loop tambem popula a lista job.lista_pares com os pares prontos for par in lista_pares: prepara_job_no_par(par) @@ -278,8 +276,6 @@ def executa_job(): """ Inicia a execucao do job carregado na memoria. """ - global job - if not job: print('\nNenhum job carregado.') return @@ -455,9 +451,7 @@ def conexao_tcp_thread(con, par): """ Thread que cuida de uma conexao TCP com um par. """ - global job buff = 1024 - resp = con.recv(buff) #print '' #print 'TCP>', r[:200] ####### DBG From 9e22e33681c4286fe928917d6a1cab3507d49f3c Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Fri, 30 Jan 2026 21:51:57 -0300 Subject: [PATCH 14/31] comment out an unused var --- grid.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grid.py b/grid.py index 47ab565..da4d020 100644 --- a/grid.py +++ b/grid.py @@ -539,11 +539,11 @@ def conexao_tcp_thread(con, par): try: print(f'\nIniciando execucao do programa {programa}') call(['./programs/' + programa, diretorio_entrada + nome_entrada, diretorio_saida + nome_saida]) - resposta = 'pronto' + #resposta = 'pronto' except Exception as ex: print(f'\nERRO na execucao do programa {programa}') print(ex) - resposta = 'erro' + #resposta = 'erro' envia_saida(nome_diretorio_job, nome_saida, par) else: while resp: From b46b4a2873e28e7a0519610a1c9cd96f81ebd3bc Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Wed, 4 Feb 2026 10:00:48 -0300 Subject: [PATCH 15/31] break very long lines --- grid.py | 27 +++++++++++++++++++-------- job.py | 6 ++++-- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/grid.py b/grid.py index da4d020..f0ca2d7 100644 --- a/grid.py +++ b/grid.py @@ -191,7 +191,9 @@ def envia_entrada(entrada, par): tamanho = os.path.getsize(arquivo) buff = 1024 tcp_socket = socket(AF_INET, SOCK_STREAM) - cabecalho = 'entrada|' + job.diretorio + '|' + entrada + '|' + str(tamanho) + '|' # entrada|diretorio_job|nome_entrada|tamanho| + + #formato: entrada|diretorio_job|nome_entrada|tamanho| + cabecalho = 'entrada|' + job.diretorio + '|' + entrada + '|' + str(tamanho) + '|' # aqui preenchemos o cabecalho com esp. em branco ate ele ficar com tam. do buffer # isto e, o cabecalho deve ter buff bytes de tamanho (wrkrnd) @@ -362,7 +364,8 @@ def trata_comando(string_comando): elif comando[0] == 'pares': i = 0 for par in lista_pares: - print('#', i, ' - ', str(par), ' - Ocup.:', job.is_par_ocupado(par) if job is not None else False) + print('#', i, ' - ', str(par), + ' - Ocup.:', job.is_par_ocupado(par) if job is not None else False) i = i + 1 if i == 0: print('Sem pares contactados.') @@ -416,7 +419,9 @@ def envia_saida(diretorio, saida, par): tamanho = os.path.getsize(arquivo) buff = 1024 tcp_socket = socket(AF_INET, SOCK_STREAM) - cabecalho = 'saida|' + diretorio + '|' + saida + '|' + str(tamanho) + '|' # saida|diretorio_job|nome_saida|tamanho| + + #formato: saida|diretorio_job|nome_saida|tamanho| + cabecalho = 'saida|' + diretorio + '|' + saida + '|' + str(tamanho) + '|' # aqui preenchemos o cabecalho com esp. em branco ate ele ficar com tam. do buffer # isto e, o cabecalho deve ter buff bytes de tamanho (wrkrnd) @@ -505,7 +510,8 @@ def conexao_tcp_thread(con, par): if not resp: break - print('\nFECHANDO ARQUIVO: ', diretorio_entrada + nome_entrada, ' - RECEBIDOS: ', recebidos, ' de ', tamanho) + print('\nFECHANDO ARQUIVO: ', diretorio_entrada + nome_entrada, + ' - RECEBIDOS: ', recebidos, ' de ', tamanho) file.close() elif comando == 'saida': # Formato: saida|diretorio_job|nome_saida|tamanho| nome_diretorio = cabecalho[1] @@ -523,13 +529,16 @@ def conexao_tcp_thread(con, par): if not resp: break - print('\nFECHANDO ARQUIVO ', diretorio_saida + nome_saida, ' - RECEBIDOS: ', recebidos, ' de ', tamanho) + print('\nFECHANDO ARQUIVO ', diretorio_saida + nome_saida, + ' - RECEBIDOS: ', recebidos, ' de ', tamanho) file.close() - # esta parte eh muito importante, nela mudamos o estado de uma tarefa concorrentemente + # esta parte eh muito importante, nela + # mudamos o estado de uma tarefa concorrentemente if job.diretorio == nome_diretorio: job.finaliza_parte(nome_saida, par) - elif comando == 'executa': # Formato da msg: executa|programa|diretorio_job|nome_entrada|remetente + elif comando == 'executa': + # Formato da msg: executa|programa|diretorio_job|nome_entrada|remetente programa = cabecalho[1] nome_diretorio_job = cabecalho[2] diretorio_entrada = './temp/' + nome_diretorio_job + '/entrada/' @@ -538,7 +547,9 @@ def conexao_tcp_thread(con, par): nome_saida = nome_entrada[0:-3] + '.out' try: print(f'\nIniciando execucao do programa {programa}') - call(['./programs/' + programa, diretorio_entrada + nome_entrada, diretorio_saida + nome_saida]) + call(['./programs/' + programa, + diretorio_entrada + nome_entrada, + diretorio_saida + nome_saida]) #resposta = 'pronto' except Exception as ex: print(f'\nERRO na execucao do programa {programa}') diff --git a/job.py b/job.py index 61fc132..55ececa 100644 --- a/job.py +++ b/job.py @@ -141,7 +141,8 @@ def atribui_parte_ao_par(self, parte, par): print('ERRO: Par ', par, ' nao participa no job ', self.nome) ok = False elif parte.estado == COMPLETO: - print('NAO pode atribuir a parte ', parte.entrada, ' do job ', self.nome, ' pois ja esta completa.') + print('NAO pode atribuir a parte ', parte.entrada, + ' do job ', self.nome, ' pois ja esta completa.') ok = False elif par in self.lista_par_ocupado: print('ERRO: Par ', par, ' ja esta ocupado com uma tarefa') @@ -184,7 +185,8 @@ def finaliza_parte(self, nome_saida, par): """ lock.acquire() ### Inicio de secao critica ### for parte in self.lista_partes: - if parte.entrada[:-3] == nome_saida[:-4]: # correspondencia entre os nomes sem as extensoes + # correspondencia entre os nomes sem as extensoes + if parte.entrada[:-3] == nome_saida[:-4]: # se a parte ja estiver completa, apenas passamos if parte.is_branco(): parte.set_completo(nome_saida) From 794697bd962142005b88093de0e85b213f909a15 Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Wed, 4 Feb 2026 10:07:05 -0300 Subject: [PATCH 16/31] specify encoding when opening files --- grid.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/grid.py b/grid.py index f0ca2d7..076f5b8 100644 --- a/grid.py +++ b/grid.py @@ -108,7 +108,7 @@ def contacta_pares(): Tenta contactar pares listados no arquivo 'peerlist'. """ try: - arquivo_pares = [line.strip() for line in open('peerlist')] + arquivo_pares = [line.strip() for line in open('peerlist', encoding='utf-8')] except Exception as ex: print('Erro ao acessar o arquivo peerlist!') print(ex) @@ -128,7 +128,7 @@ def enviar_arquivo(par, arquivo): """ tcp_socket = socket(AF_INET, SOCK_STREAM) tcp_socket.connect((par[0], PORTA_TCP_PAR)) - file = open (arquivo, 'rb') + file = open(arquivo, 'rb', encoding='utf-8') buff = 1024 arquivo = arquivo.replace('\\', '/') nome = arquivo.split('/')[-1] @@ -182,7 +182,7 @@ def envia_entrada(entrada, par): arquivo = './jobs/' + job.diretorio + '/entrada/' + entrada try: - file = open(arquivo, 'rb') + file = open(arquivo, 'rb', encoding='utf-8') except Exception as ex: print('ERRO ao acessar o arquivo de entrada ', arquivo) print(ex) @@ -304,7 +304,7 @@ def carrega_job(nome_arquivo): arquivo_job = [] try: - for line in open('jobs/' + nome_arquivo): + for line in open('jobs/' + nome_arquivo, encoding='utf-8'): entrada = line.strip() if len(entrada) > 0: if entrada[0] != '#': @@ -410,7 +410,7 @@ def envia_saida(diretorio, saida, par): arquivo = './temp/' + diretorio + '/saida/' + saida try: - file = open(arquivo, 'rb') + file = open(arquivo, 'rb', encoding='utf-8') except Exception as ex: print(f'\nERRO ao acessar o arquivo de saida {arquivo}') print(ex) @@ -467,7 +467,7 @@ def conexao_tcp_thread(con, par): if comando == 'envio': nome = cabecalho[1] tamanho = int(cabecalho[2]) - file = open('./recebidos/' + nome, 'wb+') + file = open('./recebidos/' + nome, 'wb+', encoding='utf-8') recebidos = 0 while recebidos < tamanho: resp = con.recv(buff) @@ -499,7 +499,7 @@ def conexao_tcp_thread(con, par): diretorio_entrada = './temp/' + cabecalho[1] + '/entrada/' nome_entrada = cabecalho[2] tamanho = int(cabecalho[3]) - file = open(diretorio_entrada + nome_entrada, 'wb+') + file = open(diretorio_entrada + nome_entrada, 'wb+', encoding='utf-8') recebidos = 0 while recebidos < tamanho: resp = con.recv(buff) @@ -518,7 +518,7 @@ def conexao_tcp_thread(con, par): diretorio_saida = './jobs/' + nome_diretorio + '/saida/' nome_saida = cabecalho[2] tamanho = int(cabecalho[3]) - file = open(diretorio_saida + nome_saida, 'wb+') + file = open(diretorio_saida + nome_saida, 'wb+', encoding='utf-8') recebidos = 0 while recebidos < tamanho: resp = con.recv(buff) From 58169737aada1533d140c7075a757771f8f1408c Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Wed, 4 Feb 2026 19:06:59 -0300 Subject: [PATCH 17/31] acquiring and releasing the lock using 'with' --- job.py | 113 +++++++++++++++++++++++++++------------------------------ 1 file changed, 53 insertions(+), 60 deletions(-) diff --git a/job.py b/job.py index 55ececa..b8d9cd5 100644 --- a/job.py +++ b/job.py @@ -98,12 +98,11 @@ def finalizado(self): Retorna True se todas as partes do job estiverem completas. """ valor = True - lock.acquire() ### Inicio de secao critica ### - for parte in self.lista_partes: - if parte.estado != COMPLETO: - valor = False - break - lock.release() ### Fim de secao critica ### + with lock: + for parte in self.lista_partes: + if parte.estado != COMPLETO: + valor = False + break return valor def insere_par(self, par): @@ -133,36 +132,34 @@ def atribui_parte_ao_par(self, parte, par): Atribui uma parte do job ao par especificado """ ok = True - lock.acquire() ### Inicio de secao critica ### - if parte not in self.lista_partes: - print('ERRO: Parte ', parte.entrada, ' nao esta no job ', self.nome) - ok = False - elif par not in self.lista_pares: - print('ERRO: Par ', par, ' nao participa no job ', self.nome) - ok = False - elif parte.estado == COMPLETO: - print('NAO pode atribuir a parte ', parte.entrada, - ' do job ', self.nome, ' pois ja esta completa.') - ok = False - elif par in self.lista_par_ocupado: - print('ERRO: Par ', par, ' ja esta ocupado com uma tarefa') - ok = False - elif parte.estado == ATRIBUIDO: - print('A PARTE ', parte.entrada, ' ja esta com ', parte.par) - ok = False - # tudo ok, pode encadear - if ok: - self.lista_partes[self.lista_partes.index(parte)].atribui(par) - self.lista_par_ocupado.append(par) - lock.release() ### Fim de secao critica ### + with lock: + if parte not in self.lista_partes: + print('ERRO: Parte ', parte.entrada, ' nao esta no job ', self.nome) + ok = False + elif par not in self.lista_pares: + print('ERRO: Par ', par, ' nao participa no job ', self.nome) + ok = False + elif parte.estado == COMPLETO: + print('NAO pode atribuir a parte ', parte.entrada, + ' do job ', self.nome, ' pois ja esta completa.') + ok = False + elif par in self.lista_par_ocupado: + print('ERRO: Par ', par, ' ja esta ocupado com uma tarefa') + ok = False + elif parte.estado == ATRIBUIDO: + print('A PARTE ', parte.entrada, ' ja esta com ', parte.par) + ok = False + # tudo ok, pode encadear + if ok: + self.lista_partes[self.lista_partes.index(parte)].atribui(par) + self.lista_par_ocupado.append(par) def possui_par_livre(self): """ Retorna True se houver algum par livre para receber uma parte. """ - lock.acquire() ### Inicio de secao critica ### - retorno = len(self.lista_par_ocupado) < len(self.lista_pares) - lock.release() ### Fim de secao critica ### + with lock: + retorno = len(self.lista_par_ocupado) < len(self.lista_pares) return retorno def proximo_par_livre(self): @@ -170,12 +167,11 @@ def proximo_par_livre(self): Retorna o proximo par livre para receber uma parte. """ par_retorno = None - lock.acquire() ### Inicio de secao critica ### - for par in self.lista_pares: - if par not in self.lista_par_ocupado: - par_retorno = par - break - lock.release() ### Fim de secao critica ### + with lock: + for par in self.lista_pares: + if par not in self.lista_par_ocupado: + par_retorno = par + break return par_retorno def finaliza_parte(self, nome_saida, par): @@ -183,38 +179,35 @@ def finaliza_parte(self, nome_saida, par): Muda para COMPLETO o estado da parte que esta relacionada a nomeSaida e também retira da listaParOcupado aquele que estiver com essa parte (se existir). """ - lock.acquire() ### Inicio de secao critica ### - for parte in self.lista_partes: - # correspondencia entre os nomes sem as extensoes - if parte.entrada[:-3] == nome_saida[:-4]: - # se a parte ja estiver completa, apenas passamos - if parte.is_branco(): - parte.set_completo(nome_saida) - elif parte.is_atribuido(): - for par_ocupado in self.lista_par_ocupado: - if par_ocupado[0] == par[0]: - self.lista_par_ocupado.remove(par_ocupado) - parte.set_completo(nome_saida) - lock.release() ### Fim de secao critica ### + with lock: + for parte in self.lista_partes: + # correspondencia entre os nomes sem as extensoes + if parte.entrada[:-3] == nome_saida[:-4]: + # se a parte ja estiver completa, apenas passamos + if parte.is_branco(): + parte.set_completo(nome_saida) + elif parte.is_atribuido(): + for par_ocupado in self.lista_par_ocupado: + if par_ocupado[0] == par[0]: + self.lista_par_ocupado.remove(par_ocupado) + parte.set_completo(nome_saida) def is_par_ocupado(self, par): """ Retorna True se um dado par está ocupado em alguma parte do job. """ valor = False - lock.acquire() ### Inicio de secao critica ### - for par_ocupado in self.lista_par_ocupado: - if par_ocupado[0] == par[0]: - valor = True - break - lock.release() ### Fim de secao critica ### + with lock: + for par_ocupado in self.lista_par_ocupado: + if par_ocupado[0] == par[0]: + valor = True + break return valor def print_status(self): """ Imprime em stdout o estado do job. """ - lock.acquire() ### Inicio de secao critica ### - for parte in self.lista_partes: - print('#', parte.entrada, '-', parte.estado) - lock.release() ### Fim de secao critica ### + with lock: + for parte in self.lista_partes: + print('#', parte.entrada, '-', parte.estado) From a79d5e24f7105763ea71504b56cc3e11754596f1 Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Wed, 4 Feb 2026 20:01:16 -0300 Subject: [PATCH 18/31] opening all files under a 'with' scope --- grid.py | 257 +++++++++++++++++++++++++++----------------------------- 1 file changed, 124 insertions(+), 133 deletions(-) diff --git a/grid.py b/grid.py index 076f5b8..26e79bb 100644 --- a/grid.py +++ b/grid.py @@ -107,16 +107,20 @@ def contacta_pares(): """ Tenta contactar pares listados no arquivo 'peerlist'. """ + linhas_arquivo_pares = [] + try: - arquivo_pares = [line.strip() for line in open('peerlist', encoding='utf-8')] - except Exception as ex: + with open('peerlist', encoding='utf-8') as file: + linhas_arquivo_pares = [line.strip() for line in file] + except OSError as ex: print('Erro ao acessar o arquivo peerlist!') print(ex) return - if len(arquivo_pares) == 0: + + if len(linhas_arquivo_pares) == 0: print('Arquivo peerlist vazio!') - elif len(lista_pares) < len(arquivo_pares) and len(lista_pares) < 3: - for endereco_par in arquivo_pares: + elif len(lista_pares) < len(linhas_arquivo_pares) and len(lista_pares) < 3: + for endereco_par in linhas_arquivo_pares: meu_socket_udp.sendto(b'conect', (endereco_par, PORTA_UDP_PAR)) # Send bytes print('\nTentando contactar a: ' + endereco_par) #---------------------------------------------------------------------------------------- @@ -128,28 +132,27 @@ def enviar_arquivo(par, arquivo): """ tcp_socket = socket(AF_INET, SOCK_STREAM) tcp_socket.connect((par[0], PORTA_TCP_PAR)) - file = open(arquivo, 'rb', encoding='utf-8') - buff = 1024 - arquivo = arquivo.replace('\\', '/') - nome = arquivo.split('/')[-1] - tamanho = os.path.getsize(arquivo) - cabecalho = 'envio|' + nome + '|' + str(tamanho) + '|' + with open(arquivo, 'rb', encoding='utf-8') as file: + buff = 1024 + arquivo = arquivo.replace('\\', '/') + nome = arquivo.split('/')[-1] + tamanho = os.path.getsize(arquivo) + cabecalho = 'envio|' + nome + '|' + str(tamanho) + '|' - # aqui preenchemos o cabecalho com esp. em branco ate ele ficar com tam. do buffer - # isto e, o cabecalho deve ter buff bytes de tamanho (wrkrnd) - cabecalho += ' ' * (1024 - len(cabecalho)) + # aqui preenchemos o cabecalho com esp. em branco ate ele ficar com tam. do buffer + # isto e, o cabecalho deve ter buff bytes de tamanho (wrkrnd) + cabecalho += ' ' * (1024 - len(cabecalho)) - print('ENVIANDO: ', arquivo, ' de ', tamanho) + print('ENVIANDO: ', arquivo, ' de ', tamanho) - tcp_socket.send(cabecalho) + tcp_socket.send(cabecalho) - dados = file.read(buff) - while dados: - tcp_socket.send(dados) dados = file.read(buff) + while dados: + tcp_socket.send(dados) + dados = file.read(buff) - tcp_socket.close() - file.close() + tcp_socket.close() print('FIM DO ENVIO DE: ', arquivo) #---------------------------------------------------------------------------------------- @@ -174,50 +177,45 @@ def prepara_job_no_par(par): #---------------------------------------------------------------------------------------- #-Transfere via TCP um arquivo de entrada para um par------------------------------------ -def envia_entrada(entrada, par): +def envia_entrada(entrada : str, par): """ Transfere via TCP um arquivo de entrada para um par. """ - file = None - arquivo = './jobs/' + job.diretorio + '/entrada/' + entrada + file_path = f"./jobs/{job.diretorio}/entrada/{entrada}" try: - file = open(arquivo, 'rb', encoding='utf-8') - except Exception as ex: - print('ERRO ao acessar o arquivo de entrada ', arquivo) - print(ex) - return False - - tamanho = os.path.getsize(arquivo) - buff = 1024 - tcp_socket = socket(AF_INET, SOCK_STREAM) + with open(file_path, 'rb', encoding='utf-8') as file: + tamanho = os.path.getsize(file_path) + buff = 1024 + tcp_socket = socket(AF_INET, SOCK_STREAM) - #formato: entrada|diretorio_job|nome_entrada|tamanho| - cabecalho = 'entrada|' + job.diretorio + '|' + entrada + '|' + str(tamanho) + '|' + #formato: entrada|diretorio_job|nome_entrada|tamanho| + cabecalho = 'entrada|' + job.diretorio + '|' + entrada + '|' + str(tamanho) + '|' - # aqui preenchemos o cabecalho com esp. em branco ate ele ficar com tam. do buffer - # isto e, o cabecalho deve ter buff bytes de tamanho (wrkrnd) - cabecalho += ' ' * (1024 - len(cabecalho)) + # aqui preenchemos o cabecalho com esp. em branco ate ele ficar com tam. do buffer + # isto e, o cabecalho deve ter buff bytes de tamanho (wrkrnd) + cabecalho += ' ' * (1024 - len(cabecalho)) - try: - print('ENVIANDO ENTRADA: ', arquivo, ' de ', tamanho) - tcp_socket.connect((par[0], PORTA_TCP_PAR)) - tcp_socket.send(cabecalho) - dados = file.read(buff) - while dados: - tcp_socket.send(dados) - dados = file.read(buff) - except Exception as ex: - print('\nHouve um erro na tranf. de um arquivo!') + try: + print('ENVIANDO ENTRADA: ', file_path, ' de ', tamanho) + tcp_socket.connect((par[0], PORTA_TCP_PAR)) + tcp_socket.send(cabecalho) + dados = file.read(buff) + while dados: + tcp_socket.send(dados) + dados = file.read(buff) + except Exception as ex: + print('\nHouve um erro na tranf. de um arquivo!') + print(ex) + return False + finally: + tcp_socket.close() + except OSError as ex: + print('ERRO ao acessar o arquivo de entrada ', file_path) print(ex) - tcp_socket.close() - file.close() return False - tcp_socket.close() - file.close() - - print('FIM DO ENVIO DE: ', arquivo) + print('FIM DO ENVIO DE: ', file_path) return True #---------------------------------------------------------------------------------------- @@ -296,7 +294,7 @@ def executa_job(): #---------------------------------------------------------------------------------------- #-Carrega para a memoria o job descrito pelo arquivo------------------------------------- -def carrega_job(nome_arquivo): +def carrega_job(nome_arquivo : str): """ Carrega um job a partir do arquivo especificado. """ @@ -304,12 +302,15 @@ def carrega_job(nome_arquivo): arquivo_job = [] try: - for line in open('jobs/' + nome_arquivo, encoding='utf-8'): - entrada = line.strip() - if len(entrada) > 0: - if entrada[0] != '#': + with open('jobs/' + nome_arquivo, encoding='utf-8') as file: + if not file: + print(f'Arquivo de job {nome_arquivo} nao encontrado.') + return + for line in file: + entrada = line.strip() + if len(entrada) > 0 and entrada[0] != '#': arquivo_job.append(entrada) - except Exception as ex: + except OSError as ex: print(f'Erro ao acessar o arquivo de job: {nome_arquivo}') print(ex) return @@ -400,54 +401,47 @@ def envia_mensagem(id_par : int, textos : list): #---------------------------------------------------------------------------------------- #-Transf. via TCP, o result. do process. e arquivo de saida (se houver) para um par------ -def envia_saida(diretorio, saida, par): +def envia_saida(diretorio : str, saida : str, par): """ Transfere via TCP resultados sobre o processamento e o arquivo de saida (caso haja) gerado por um job para um determinado par. """ - file = None - - arquivo = './temp/' + diretorio + '/saida/' + saida + file_path = f"./temp/{diretorio}/saida/{saida}" try: - file = open(arquivo, 'rb', encoding='utf-8') - except Exception as ex: - print(f'\nERRO ao acessar o arquivo de saida {arquivo}') - print(ex) - return False + with open(file_path, 'rb', encoding='utf-8') as file: + tamanho = os.path.getsize(file_path) + buff = 1024 + tcp_socket = socket(AF_INET, SOCK_STREAM) - tamanho = os.path.getsize(arquivo) - buff = 1024 - tcp_socket = socket(AF_INET, SOCK_STREAM) + # formato: saida|diretorio_job|nome_saida|tamanho| + cabecalho = f"saida|{diretorio}|{saida}|{str(tamanho)}|" - #formato: saida|diretorio_job|nome_saida|tamanho| - cabecalho = 'saida|' + diretorio + '|' + saida + '|' + str(tamanho) + '|' + # aqui preenchemos o cabecalho com esp. em branco ate ele ficar com tam. do buffer + # isto e, o cabecalho deve ter buff bytes de tamanho (wrkrnd) + cabecalho += ' ' * (1024 - len(cabecalho)) - # aqui preenchemos o cabecalho com esp. em branco ate ele ficar com tam. do buffer - # isto e, o cabecalho deve ter buff bytes de tamanho (wrkrnd) - cabecalho += ' ' * (1024 - len(cabecalho)) + print(f"\nENVIANDO SAIDA: {file_path} de {str(tamanho)} bytes") - print(f'\nENVIANDO SAIDA: {arquivo} de {tamanho}') - - tcp_socket.connect((par[0], PORTA_TCP_PAR)) - - try: - tcp_socket.send(cabecalho) - dados = file.read(buff) - while dados: - tcp_socket.send(dados) - dados = file.read(buff) - except Exception as ex: - print('\nHouve um erro na transf. de um arquivo!') + tcp_socket.connect((par[0], PORTA_TCP_PAR)) + try: + tcp_socket.send(cabecalho) + dados = file.read(buff) + while dados: + tcp_socket.send(dados) + dados = file.read(buff) + except Exception as ex: + print('\nHouve um erro na transf. de um arquivo!') + print(ex) + tcp_socket.close() + return False + tcp_socket.close() + except OSError as ex: + print(f'\nERRO ao acessar o arquivo de saida {file_path}') print(ex) - tcp_socket.close() - file.close() return False - tcp_socket.close() - file.close() - - print(f'\nFIM DO ENVIO DE: {arquivo}') + print(f'\nFIM DO ENVIO DE: {file_path}') return True #---------------------------------------------------------------------------------------- @@ -467,22 +461,21 @@ def conexao_tcp_thread(con, par): if comando == 'envio': nome = cabecalho[1] tamanho = int(cabecalho[2]) - file = open('./recebidos/' + nome, 'wb+', encoding='utf-8') - recebidos = 0 - while recebidos < tamanho: - resp = con.recv(buff) - while resp: - recebidos += len(resp) - file.write(resp) + with open('./recebidos/' + nome, 'wb+', encoding='utf-8') as file: + recebidos = 0 + while recebidos < tamanho: resp = con.recv(buff) - if not resp: - break - print('\nFECHANDO ARQUVIO', '- RECEBIDOS: ', recebidos) - file.close() + while resp: + recebidos += len(resp) + file.write(resp) + resp = con.recv(buff) + if not resp: + break + print('\nFECHANDO ARQUVIO', '- RECEBIDOS: ', recebidos) + elif comando == 'job': diretorio_job = './temp/' + cabecalho[1] resultado = '' - try: if os.path.isdir(diretorio_job): resultado = 'ok' @@ -493,50 +486,48 @@ def conexao_tcp_thread(con, par): except Exception as ex: resultado = 'erro' print(ex) - con.send(resultado) + elif comando == 'entrada': diretorio_entrada = './temp/' + cabecalho[1] + '/entrada/' nome_entrada = cabecalho[2] tamanho = int(cabecalho[3]) - file = open(diretorio_entrada + nome_entrada, 'wb+', encoding='utf-8') - recebidos = 0 - while recebidos < tamanho: - resp = con.recv(buff) - while resp: - recebidos += len(resp) - file.write(resp) + with open(diretorio_entrada + nome_entrada, 'wb+', encoding='utf-8') as file: + recebidos = 0 + while recebidos < tamanho: resp = con.recv(buff) - if not resp: - break + while resp: + recebidos += len(resp) + file.write(resp) + resp = con.recv(buff) + if not resp: + break + print('\nFECHANDO ARQUIVO: ', diretorio_entrada + nome_entrada, + ' - RECEBIDOS: ', recebidos, ' de ', tamanho) - print('\nFECHANDO ARQUIVO: ', diretorio_entrada + nome_entrada, - ' - RECEBIDOS: ', recebidos, ' de ', tamanho) - file.close() elif comando == 'saida': # Formato: saida|diretorio_job|nome_saida|tamanho| nome_diretorio = cabecalho[1] diretorio_saida = './jobs/' + nome_diretorio + '/saida/' nome_saida = cabecalho[2] tamanho = int(cabecalho[3]) - file = open(diretorio_saida + nome_saida, 'wb+', encoding='utf-8') - recebidos = 0 - while recebidos < tamanho: - resp = con.recv(buff) - while resp: - recebidos += len(resp) - file.write(resp) + with open(diretorio_saida + nome_saida, 'wb+', encoding='utf-8') as file: + recebidos = 0 + while recebidos < tamanho: resp = con.recv(buff) - if not resp: - break - - print('\nFECHANDO ARQUIVO ', diretorio_saida + nome_saida, - ' - RECEBIDOS: ', recebidos, ' de ', tamanho) - file.close() + while resp: + recebidos += len(resp) + file.write(resp) + resp = con.recv(buff) + if not resp: + break + print('\nFECHANDO ARQUIVO ', diretorio_saida + nome_saida, + ' - RECEBIDOS: ', recebidos, ' de ', tamanho) # esta parte eh muito importante, nela # mudamos o estado de uma tarefa concorrentemente if job.diretorio == nome_diretorio: job.finaliza_parte(nome_saida, par) + elif comando == 'executa': # Formato da msg: executa|programa|diretorio_job|nome_entrada|remetente programa = cabecalho[1] From 4b8e0e68e48c0235b7cf289a87d68935f017380d Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Wed, 4 Feb 2026 21:16:19 -0300 Subject: [PATCH 19/31] add AGPL License and update docs --- LICENSE.txt | 619 ++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 16 +- grid.py | 16 ++ job.py | 16 ++ util.py | 20 +- 5 files changed, 677 insertions(+), 10 deletions(-) create mode 100644 LICENSE.txt diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..4ec8c3f --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,619 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS diff --git a/README.md b/README.md index d66570b..90599f2 100644 --- a/README.md +++ b/README.md @@ -3,23 +3,25 @@ microGrid Plataforma simples para deploy de grids ad-hoc e P2P em redes locais. -Permite que a execução de programas portáteis com múltiplos arquivos de entradas seja distribuída sobre -computadores diferentes em uma rede local, dividindo-se estas entradas entre estações específicas. +Permite que a execução de programas portáteis, com múltiplos arquivos de +entrada seja distribuída entre computadores diferentes em uma rede local. +Isto é possível através da divisão destas entradas entre estações específicas. -## Requisitos: +## Requisitos -Python 2.7 +Apenas Python 3. ## Uso -Para executar: `python grid.py` +Para executar: `python grid.py`. Para ajuda com comandos escreva, no prompt: `ajuda`. ### Nós da rede -Os nós pares da rede que fazem parte do grid deverão ter seus endereços IP especificados no arquivo `peerlist`. +Os nós pares da rede que fazem parte do grid deverão ter seus endereços IP +especificados no arquivo: `peerlist`. --- -(C) 2014 Tiago Matos +Copyright (C) 2014-2026 Tiago Matos diff --git a/grid.py b/grid.py index 26e79bb..2142be4 100644 --- a/grid.py +++ b/grid.py @@ -1,3 +1,19 @@ +# microGrid - Sistema de computacao em grid +# Copyright (C) 2026 Tiago Matos +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + """ Módulo principal do sistema de computacao em grid. Contém o ponto de entrada e a lógica principal do programa. diff --git a/job.py b/job.py index b8d9cd5..c796c1a 100644 --- a/job.py +++ b/job.py @@ -1,3 +1,19 @@ +# microGrid - Sistema de computacao em grid +# Copyright (C) 2026 Tiago Matos +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + """ Modulo que implementa a classe Job e Parte para o sistema de distribuicao de tarefas. """ diff --git a/util.py b/util.py index 6d9aace..dcbbacb 100644 --- a/util.py +++ b/util.py @@ -1,14 +1,29 @@ +# microGrid - Sistema de computacao em grid +# Copyright (C) 2026 Tiago Matos +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + """ Funções utilitárias diversas. """ -#---------------------------------------------------------------------------------------- def exibir_ajuda_geral_de_comandos(): """ Exibe a ajuda de comandos na tela. """ print('') - print('Meu Grid 0.2 por Tiago Matos') + print('microGrid v0.2') print('') print('Comandos:') print('') @@ -21,4 +36,3 @@ def exibir_ajuda_geral_de_comandos(): print(' ajuda') print(' sair') print('') -#---------------------------------------------------------------------------------------- From 64c8061b75a66daaec5f75feca5af539971cd4e2 Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Thu, 5 Feb 2026 19:56:54 -0300 Subject: [PATCH 20/31] unnest if statement --- grid.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/grid.py b/grid.py index 2142be4..982f6dc 100644 --- a/grid.py +++ b/grid.py @@ -61,10 +61,9 @@ def processa_pacote(msg_com_dados, endereco_par): resposta = 'void' if msg[0] == 'ok': if msg[1] == 'contact': - if len(lista_pares) < MAX_DE_PARES: - if endereco_par not in lista_pares: - lista_pares.append(endereco_par) - print(f'\nNosso pedido de contato foi aceito por {str(endereco_par)}') + if len(lista_pares) < MAX_DE_PARES and endereco_par not in lista_pares: + lista_pares.append(endereco_par) + print(f'\nNosso pedido de contato foi aceito por {str(endereco_par)}') else: resposta = 'disconect' elif msg[0] == 'conect': From d62c32159fb47a250a5d7e107e1784d4c382b39f Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Thu, 5 Feb 2026 20:30:47 -0300 Subject: [PATCH 21/31] refactor: extract some funcs --- grid.py | 62 ++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/grid.py b/grid.py index 982f6dc..909caf7 100644 --- a/grid.py +++ b/grid.py @@ -52,6 +52,49 @@ def encerrar_programa(): sys.exit(0) #---------------------------------------------------------------------------------------- +#---------------------------------------------------------------------------------------- +def processa_pacote_ok(msg : list, endereco_par : int) -> str: + """ + Processa um pacote UDP do tipo 'ok' recebido de um par. + """ + resposta = 'void' + if msg[1] == 'contact': + if len(lista_pares) < MAX_DE_PARES and endereco_par not in lista_pares: + lista_pares.append(endereco_par) + print(f'\nNosso pedido de contato foi aceito por {str(endereco_par)}') + else: + resposta = 'disconect' + return resposta +#---------------------------------------------------------------------------------------- + +#---------------------------------------------------------------------------------------- +def processa_pacote_conect(endereco_par : int) -> str: + """ + Processa um pacote UDP do tipo 'conect' recebido de um par. + """ + resposta = 'void' + if len(lista_pares) < MAX_DE_PARES: + if endereco_par not in lista_pares: + lista_pares.append(endereco_par) + resposta = 'ok;contact' + print(f'\nPedido de contato aceito originado de {str(endereco_par)}') + else: + resposta = 'not' + return resposta +#---------------------------------------------------------------------------------------- + +#---------------------------------------------------------------------------------------- +def processa_pacote_disconect(endereco_par : int): + """ + Processa um pacote UDP do tipo 'disconect' recebido de um par. + """ + if endereco_par in lista_pares: + lista_pares.remove(endereco_par) + print(f'Contato desfeito por solicitacao de {str(endereco_par)}') + else: + print(f'Recebi disconect de {str(endereco_par)}, ele nao constava conectado.') +#---------------------------------------------------------------------------------------- + #---------------------------------------------------------------------------------------- def processa_pacote(msg_com_dados, endereco_par): """ @@ -60,24 +103,11 @@ def processa_pacote(msg_com_dados, endereco_par): msg = msg_com_dados.decode('utf-8').split(';') # Decode bytes to string resposta = 'void' if msg[0] == 'ok': - if msg[1] == 'contact': - if len(lista_pares) < MAX_DE_PARES and endereco_par not in lista_pares: - lista_pares.append(endereco_par) - print(f'\nNosso pedido de contato foi aceito por {str(endereco_par)}') - else: - resposta = 'disconect' + resposta = processa_pacote_ok(msg, endereco_par) elif msg[0] == 'conect': - if len(lista_pares) < MAX_DE_PARES: - if endereco_par not in lista_pares: - lista_pares.append(endereco_par) - resposta = 'ok;contact' - print(f'\nPedido de contato aceito originado de {str(endereco_par)}') - else: - resposta = 'not' + resposta = processa_pacote_conect(endereco_par) elif msg[0] == 'disconect': - if endereco_par in lista_pares: - lista_pares.remove(endereco_par) - print(f'Contato desfeito por solicitacao de {str(endereco_par)}') + processa_pacote_disconect(endereco_par) elif endereco_par in lista_pares: if msg[0] == 'do': resposta = 'what?' From a8a72b3aa34b3073c625d6bff61bc260e36f7df8 Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Thu, 5 Feb 2026 21:41:52 -0300 Subject: [PATCH 22/31] refactor: extract even more funcs --- grid.py | 120 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 37 deletions(-) diff --git a/grid.py b/grid.py index 909caf7..445dcd0 100644 --- a/grid.py +++ b/grid.py @@ -371,8 +371,84 @@ def carrega_job(nome_arquivo : str): job = Job(arquivo_job[0], arquivo_job[1], arquivo_job[2], nome_arquivo) #---------------------------------------------------------------------------------------- -#-Interpretacao dos comandos do prompt--------------------------------------------------- -def trata_comando(string_comando): +def executa_comando_mensagem(comando : list): + """ + Executa o comando de envio de mensagem para um par. + """ + if len(comando) < 3: + print('\nArgumentos incorretos no comando.') + return + try: + id_par = int(comando[1]) + if id_par < 0: + raise ValueError + except ValueError: + print('\nArgumentos incorretos no comando. ', + 'O id do par deve ser numero inteiro nao negativo.') + return + envia_mensagem(id_par, comando[2:]) +#---------------------------------------------------------------------------------------- + +#---------------------------------------------------------------------------------------- +def executa_comando_enviar(comando : list): + """ + Executa o comando de envio arquivo para um par. + """ + if len(comando) < 3: + print('\nArgumentos incorretos no comando.') + return + try: + id_par = int(comando[1]) + if id_par < 0: + raise ValueError + except ValueError: + print('\nArgumentos incorretos no comando. ', + 'O id do par deve ser numero inteiro nao negativo.') + return + if id_par+1 > len(lista_pares): + print('\nNao temos este par na nossa lista.') + return + enviar_arquivo(lista_pares[id_par], comando[2]) +#---------------------------------------------------------------------------------------- + +#---------------------------------------------------------------------------------------- +def executa_comando_pares(): + """ + Executa o comando de listar pares contactados. + """ + i = 0 + for par in lista_pares: + print('#', i, ' - ', str(par), + ' - Ocup.:', job.is_par_ocupado(par) if job is not None else False) + i = i + 1 + if i == 0: + print('Sem pares contactados.') +#---------------------------------------------------------------------------------------- + +#---------------------------------------------------------------------------------------- +def executa_comando_carrega(comando : list): + """ + Executa o comando de carregar um job. + """ + if len(comando) < 2: + print('\nArgumentos incorretos. Especifique o arquivo do job.') + return + carrega_job(comando[1]) +#---------------------------------------------------------------------------------------- + +#---------------------------------------------------------------------------------------- +def executa_comando_estado(): + """ + Executa o comando de exibir o estado do job. + """ + if job is None: + print('Sem job carregado.') + else: + job.print_status() +#---------------------------------------------------------------------------------------- + +#---------------------------------------------------------------------------------------- +def trata_comando(string_comando : str): """ Interpreta e executa os comandos digitados no prompt. """ @@ -386,45 +462,15 @@ def trata_comando(string_comando): elif comando[0] == 'contato': contacta_pares() elif comando[0] == 'mensagem': - if len(comando) < 3: - print('\nArgumentos incorretos no comando.') - else: - try: - id_par = int(comando[1]) - except ValueError: - print('\nArgumentos incorretos no comando. Id do par deve ser numero.') - return - envia_mensagem(id_par, comando[2:]) + executa_comando_mensagem(comando) elif comando[0] == 'enviar': - if len(comando) < 3: - print('\nArgumentos incorretos no comando.') - else: - try: - id_par = int(comando[1]) - except ValueError: - print('\nArgumentos incorretos no comando. Id do par deve ser numero.') - return - if id_par < 0 or id_par+1 > len(lista_pares): - print('\nNao temos este par na nossa lista.') - enviar_arquivo(lista_pares[id_par], comando[2]) + executa_comando_enviar(comando) elif comando[0] == 'pares': - i = 0 - for par in lista_pares: - print('#', i, ' - ', str(par), - ' - Ocup.:', job.is_par_ocupado(par) if job is not None else False) - i = i + 1 - if i == 0: - print('Sem pares contactados.') + executa_comando_pares() elif comando[0] == 'carrega': - if len(comando) < 2: - print('\nArgumentos incorretos. Especifique o arquivo do job.') - return - carrega_job(comando[1]) + executa_comando_carrega(comando) elif comando[0] == 'estado': - if job is None: - print('Sem job carregado.') - else: - job.print_status() + executa_comando_estado() elif comando[0] == 'executa': executa_job() elif comando[0] == 'sair': From 9a44023fd82adacde8664b915715e9f889e27209 Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Thu, 5 Feb 2026 22:36:27 -0300 Subject: [PATCH 23/31] extract func and minor syntax changes --- grid.py | 58 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/grid.py b/grid.py index 445dcd0..de2798a 100644 --- a/grid.py +++ b/grid.py @@ -527,24 +527,41 @@ def envia_saida(diretorio : str, saida : str, par): tcp_socket.close() return False tcp_socket.close() - except OSError as ex: + except OSError as ex_os_error: print(f'\nERRO ao acessar o arquivo de saida {file_path}') - print(ex) + print(ex_os_error) return False print(f'\nFIM DO ENVIO DE: {file_path}') return True #---------------------------------------------------------------------------------------- -#-Thread da conexao criada numa interacao com um par------------------------------------- +#---------------------------------------------------------------------------------------- +def trata_comando_tcp_envio(con, nome : str, tamanho : int): + """ + Trata a recepcao de arquivo via TCP oriundo de um outro par. + """ + buff = 1024 + with open(f'./recebidos/{nome}', 'wb+', encoding='utf-8') as file: + recebidos = 0 + while recebidos < tamanho: + resp = con.recv(buff) + while resp: + recebidos += len(resp) + file.write(resp) + resp = con.recv(buff) + if not resp: + break + print('\nFECHANDO ARQUVIO', '- RECEBIDOS: ', recebidos, ' bytes,') +#---------------------------------------------------------------------------------------- + +#---------------------------------------------------------------------------------------- def conexao_tcp_thread(con, par): """ - Thread que cuida de uma conexao TCP com um par. + Thread que cuida de uma conexao TCP criada pela interacao com um par. """ buff = 1024 - resp = con.recv(buff) - #print '' - #print 'TCP>', r[:200] ####### DBG + resp = con.recv(buff).decode('utf-8') cabecalho = resp.split('|') comando = cabecalho[0] @@ -552,28 +569,16 @@ def conexao_tcp_thread(con, par): if comando == 'envio': nome = cabecalho[1] tamanho = int(cabecalho[2]) - with open('./recebidos/' + nome, 'wb+', encoding='utf-8') as file: - recebidos = 0 - while recebidos < tamanho: - resp = con.recv(buff) - while resp: - recebidos += len(resp) - file.write(resp) - resp = con.recv(buff) - if not resp: - break - print('\nFECHANDO ARQUVIO', '- RECEBIDOS: ', recebidos) + trata_comando_tcp_envio(con, nome, tamanho) elif comando == 'job': - diretorio_job = './temp/' + cabecalho[1] + diretorio_job = f"./temp/{cabecalho[1]}" resultado = '' try: - if os.path.isdir(diretorio_job): - resultado = 'ok' - else: + if not os.path.isdir(diretorio_job): os.makedirs(diretorio_job + '/entrada') os.makedirs(diretorio_job + '/saida') - resultado = 'ok' + resultado = 'ok' except Exception as ex: resultado = 'erro' print(ex) @@ -612,7 +617,7 @@ def conexao_tcp_thread(con, par): if not resp: break print('\nFECHANDO ARQUIVO ', diretorio_saida + nome_saida, - ' - RECEBIDOS: ', recebidos, ' de ', tamanho) + ' - RECEBIDOS: ', recebidos, ' de ', tamanho) # esta parte eh muito importante, nela # mudamos o estado de uma tarefa concorrentemente @@ -623,8 +628,8 @@ def conexao_tcp_thread(con, par): # Formato da msg: executa|programa|diretorio_job|nome_entrada|remetente programa = cabecalho[1] nome_diretorio_job = cabecalho[2] - diretorio_entrada = './temp/' + nome_diretorio_job + '/entrada/' - diretorio_saida = './temp/' + nome_diretorio_job + '/saida/' + diretorio_entrada = f'./temp/{nome_diretorio_job}/entrada/' + diretorio_saida = f'./temp/{nome_diretorio_job}/saida/' nome_entrada = cabecalho[3] nome_saida = nome_entrada[0:-3] + '.out' try: @@ -638,6 +643,7 @@ def conexao_tcp_thread(con, par): print(ex) #resposta = 'erro' envia_saida(nome_diretorio_job, nome_saida, par) + else: while resp: resp = con.recv(buff) From 589f0914f95ebbda64cfb0da26b19ce36cbeb879 Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Fri, 6 Feb 2026 01:12:40 -0300 Subject: [PATCH 24/31] refactor: extract two funcs --- grid.py | 68 ++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 25 deletions(-) diff --git a/grid.py b/grid.py index de2798a..82152fa 100644 --- a/grid.py +++ b/grid.py @@ -281,7 +281,7 @@ def executa_parte_no_par(parte, par): tcp_socket.close() #---------------------------------------------------------------------------------------- -#-Thread que cuida da divisao e execucao das partes do job nos pares--------------------- +#---------------------------------------------------------------------------------------- def job_thread(): """ Thread que cuida da divisao e execucao das partes do job nos pares. @@ -341,7 +341,7 @@ def executa_job(): #-Carrega para a memoria o job descrito pelo arquivo------------------------------------- def carrega_job(nome_arquivo : str): """ - Carrega um job a partir do arquivo especificado. + Carrega um job a partir de um arquivo .job especificado. """ global job arquivo_job = [] @@ -555,6 +555,44 @@ def trata_comando_tcp_envio(con, nome : str, tamanho : int): print('\nFECHANDO ARQUVIO', '- RECEBIDOS: ', recebidos, ' bytes,') #---------------------------------------------------------------------------------------- +#---------------------------------------------------------------------------------------- +def trata_comando_tcp_job(con, diretorio_job : str): + """ + Recebe o comando de algum um par para ficar preparado a receber um novo job. + """ + resultado = '' + try: + if not os.path.isdir(diretorio_job): + os.makedirs(diretorio_job + '/entrada') + os.makedirs(diretorio_job + '/saida') + resultado = 'ok' + except Exception as ex: + resultado = 'erro' + print(ex) + con.send(resultado) +#---------------------------------------------------------------------------------------- + +#---------------------------------------------------------------------------------------- +def trata_comando_tcp_entrada(con, dir_entrada, nome_entrada, tamanho): + """ + Trata a recepcao de arquivo de entrada via TCP oriundo de um outro par. + """ + buff = 1024 + file_path = dir_entrada + nome_entrada + with open(file_path, 'wb+', encoding='utf-8') as file: + recebidos = 0 + while recebidos < tamanho: + resp = con.recv(buff) + while resp: + recebidos += len(resp) + file.write(resp) + resp = con.recv(buff) + if not resp: + break + print('\nFECHANDO ARQUIVO: ', file_path, + ' - RECEBIDOS: ', recebidos, ' de ', tamanho) +#---------------------------------------------------------------------------------------- + #---------------------------------------------------------------------------------------- def conexao_tcp_thread(con, par): """ @@ -573,33 +611,13 @@ def conexao_tcp_thread(con, par): elif comando == 'job': diretorio_job = f"./temp/{cabecalho[1]}" - resultado = '' - try: - if not os.path.isdir(diretorio_job): - os.makedirs(diretorio_job + '/entrada') - os.makedirs(diretorio_job + '/saida') - resultado = 'ok' - except Exception as ex: - resultado = 'erro' - print(ex) - con.send(resultado) + trata_comando_tcp_job(con, diretorio_job) elif comando == 'entrada': - diretorio_entrada = './temp/' + cabecalho[1] + '/entrada/' + dir_entrada = f"./temp/{cabecalho[1]}/entrada/" nome_entrada = cabecalho[2] tamanho = int(cabecalho[3]) - with open(diretorio_entrada + nome_entrada, 'wb+', encoding='utf-8') as file: - recebidos = 0 - while recebidos < tamanho: - resp = con.recv(buff) - while resp: - recebidos += len(resp) - file.write(resp) - resp = con.recv(buff) - if not resp: - break - print('\nFECHANDO ARQUIVO: ', diretorio_entrada + nome_entrada, - ' - RECEBIDOS: ', recebidos, ' de ', tamanho) + trata_comando_tcp_entrada(con, dir_entrada, nome_entrada, tamanho) elif comando == 'saida': # Formato: saida|diretorio_job|nome_saida|tamanho| nome_diretorio = cabecalho[1] From 67b1feb953817eb03667870f97952aaea16a2ba5 Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Fri, 6 Feb 2026 01:35:44 -0300 Subject: [PATCH 25/31] using fstrings more --- grid.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/grid.py b/grid.py index 82152fa..7c9d38c 100644 --- a/grid.py +++ b/grid.py @@ -114,12 +114,12 @@ def processa_pacote(msg_com_dados, endereco_par): if len(msg) > 3: if msg[1] == 'cmd': try: - call(['./programs/' + msg[2], msg[3]]) + call([f"./programs/{msg[2]}", msg[3]]) resposta = 'done cmd' except Exception: # Changed to catch all exceptions resposta = 'erro cmd' elif msg[0] == 'msg': - msg_print = 'Msg. de ' + str(endereco_par) + ' : ' + msg_print = f"Msg. de {str(endereco_par)} : " if len(msg) > 1: msg_print += msg[1] print('') @@ -129,7 +129,7 @@ def processa_pacote(msg_com_dados, endereco_par): resposta = 'not' if resposta != 'void': - meu_socket_udp.sendto(resposta.encode('utf-8'), endereco_par) # Encode string to bytes + meu_socket_udp.sendto(resposta.encode('utf-8'), endereco_par) # Encode string to bytes #print('') #print('Enviei: ', resposta, ' Para: ', str(enderecoPar)) #---------------------------------------------------------------------------------------- @@ -167,7 +167,7 @@ def contacta_pares(): elif len(lista_pares) < len(linhas_arquivo_pares) and len(lista_pares) < 3: for endereco_par in linhas_arquivo_pares: meu_socket_udp.sendto(b'conect', (endereco_par, PORTA_UDP_PAR)) # Send bytes - print('\nTentando contactar a: ' + endereco_par) + print(f'\nTentando contactar a: {endereco_par}') #---------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------- @@ -182,7 +182,7 @@ def enviar_arquivo(par, arquivo): arquivo = arquivo.replace('\\', '/') nome = arquivo.split('/')[-1] tamanho = os.path.getsize(arquivo) - cabecalho = 'envio|' + nome + '|' + str(tamanho) + '|' + cabecalho = f"envio|{nome}|{str(tamanho)}|" # aqui preenchemos o cabecalho com esp. em branco ate ele ficar com tam. do buffer # isto e, o cabecalho deve ter buff bytes de tamanho (wrkrnd) @@ -208,20 +208,21 @@ def prepara_job_no_par(par): """ tcp_socket = socket(AF_INET, SOCK_STREAM) tcp_socket.connect((par[0], PORTA_TCP_PAR)) - tcp_socket.sendall(f'job|{job.diretorio}|'.encode('utf-8')) + tcp_socket.sendall(f"job|{job.diretorio}|".encode('utf-8')) buff = 1024 print('') resp = tcp_socket.recv(buff).decode('utf-8') if resp == 'ok': - print(f'PAR {par[0]} esta preparado para o job {job.nome}') + print(f"PAR {par[0]} esta preparado para o job {job.nome}") job.insere_par(par) else: - print(f'PAR {par[0]} nao pode preparar (ou nao e possivel confirmar) para o job {job.nome}') + print(f"PAR {par[0]} nao pode preparar (ou nao e possivel confirmar este fato) ", + f"para o job {job.nome}.") job.remove_par(par) tcp_socket.close() #---------------------------------------------------------------------------------------- -#-Transfere via TCP um arquivo de entrada para um par------------------------------------ +#---------------------------------------------------------------------------------------- def envia_entrada(entrada : str, par): """ Transfere via TCP um arquivo de entrada para um par. @@ -235,7 +236,7 @@ def envia_entrada(entrada : str, par): tcp_socket = socket(AF_INET, SOCK_STREAM) #formato: entrada|diretorio_job|nome_entrada|tamanho| - cabecalho = 'entrada|' + job.diretorio + '|' + entrada + '|' + str(tamanho) + '|' + cabecalho = f"entrada|{job.diretorio}|{entrada}|{str(tamanho)}|" # aqui preenchemos o cabecalho com esp. em branco ate ele ficar com tam. do buffer # isto e, o cabecalho deve ter buff bytes de tamanho (wrkrnd) @@ -264,7 +265,7 @@ def envia_entrada(entrada : str, par): return True #---------------------------------------------------------------------------------------- -#-Envia um comando para o par executar uma parte do job---------------------------------- +#---------------------------------------------------------------------------------------- def executa_parte_no_par(parte, par): """ Envia um comando para o par executar uma parte do job. @@ -272,7 +273,7 @@ def executa_parte_no_par(parte, par): tcp_socket = socket(AF_INET, SOCK_STREAM) # Formato: executa|programa|diretorio_job|nome_entrada| - msg = 'executa|' + job.programa + '|' + job.diretorio + '|' + parte.entrada + '|' + msg = f"executa|{job.programa}|{job.diretorio}|{parte.entrada}|" print('EXECUTANDO: ', job.programa, ' sobre a entrada ', parte.entrada, ' em ', par) @@ -328,7 +329,7 @@ def executa_job(): print('\nJob sem tarefas.') return if job.finalizado(): - print(f'\nTodas as tarefas do job \'{job.nome}\' foram completas.') + print(f"\nTodas as tarefas do job \'{job.nome}\' foram completas.") return for parte in job.lista_partes: @@ -347,7 +348,7 @@ def carrega_job(nome_arquivo : str): arquivo_job = [] try: - with open('jobs/' + nome_arquivo, encoding='utf-8') as file: + with open(f"jobs/{nome_arquivo}", encoding='utf-8') as file: if not file: print(f'Arquivo de job {nome_arquivo} nao encontrado.') return @@ -621,7 +622,7 @@ def conexao_tcp_thread(con, par): elif comando == 'saida': # Formato: saida|diretorio_job|nome_saida|tamanho| nome_diretorio = cabecalho[1] - diretorio_saida = './jobs/' + nome_diretorio + '/saida/' + diretorio_saida = f"./jobs/{nome_diretorio}/saida/" nome_saida = cabecalho[2] tamanho = int(cabecalho[3]) with open(diretorio_saida + nome_saida, 'wb+', encoding='utf-8') as file: @@ -652,7 +653,7 @@ def conexao_tcp_thread(con, par): nome_saida = nome_entrada[0:-3] + '.out' try: print(f'\nIniciando execucao do programa {programa}') - call(['./programs/' + programa, + call([f'./programs/{programa}', diretorio_entrada + nome_entrada, diretorio_saida + nome_saida]) #resposta = 'pronto' From 4b7f8edac67c1d55cc72b68004f70d9b7de388ea Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Sat, 7 Feb 2026 13:39:14 -0300 Subject: [PATCH 26/31] extract func: 'trata_comando_tcp_saida' --- grid.py | 48 +++++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/grid.py b/grid.py index 7c9d38c..5c6ce7d 100644 --- a/grid.py +++ b/grid.py @@ -594,6 +594,31 @@ def trata_comando_tcp_entrada(con, dir_entrada, nome_entrada, tamanho): ' - RECEBIDOS: ', recebidos, ' de ', tamanho) #---------------------------------------------------------------------------------------- +#---------------------------------------------------------------------------------------- +def trata_comando_tcp_saida(con, par, nome_dir : str, nome_saida : str, tamanho : int): + """ + Trata a recepcao de arquivo de saida via TCP oriundo de um outro par. + """ + buff = 1024 + diretorio_saida = f"./jobs/{nome_dir}/saida/" + file_path = diretorio_saida + nome_saida + with open(file_path, 'wb+', encoding='utf-8') as file: + recebidos = 0 + while recebidos < tamanho: + resp = con.recv(buff) + while resp: + recebidos += len(resp) + file.write(resp) + resp = con.recv(buff) + if not resp: + break + print('\nFECHANDO ARQUIVO: ', file_path, + ' - RECEBIDOS: ', recebidos, ' de ', tamanho) + # esta parte eh muito importante, nela mudamos o estado duma tarefa concorrentemente + if job.diretorio == nome_dir: + job.finaliza_parte(nome_saida, par) +#---------------------------------------------------------------------------------------- + #---------------------------------------------------------------------------------------- def conexao_tcp_thread(con, par): """ @@ -615,33 +640,18 @@ def conexao_tcp_thread(con, par): trata_comando_tcp_job(con, diretorio_job) elif comando == 'entrada': + # Formato: entrada|diretorio|nome_entrada|tamanho| dir_entrada = f"./temp/{cabecalho[1]}/entrada/" nome_entrada = cabecalho[2] tamanho = int(cabecalho[3]) trata_comando_tcp_entrada(con, dir_entrada, nome_entrada, tamanho) - elif comando == 'saida': # Formato: saida|diretorio_job|nome_saida|tamanho| + elif comando == 'saida': + # Formato: saida|diretorio_job|nome_saida|tamanho| nome_diretorio = cabecalho[1] - diretorio_saida = f"./jobs/{nome_diretorio}/saida/" nome_saida = cabecalho[2] tamanho = int(cabecalho[3]) - with open(diretorio_saida + nome_saida, 'wb+', encoding='utf-8') as file: - recebidos = 0 - while recebidos < tamanho: - resp = con.recv(buff) - while resp: - recebidos += len(resp) - file.write(resp) - resp = con.recv(buff) - if not resp: - break - print('\nFECHANDO ARQUIVO ', diretorio_saida + nome_saida, - ' - RECEBIDOS: ', recebidos, ' de ', tamanho) - - # esta parte eh muito importante, nela - # mudamos o estado de uma tarefa concorrentemente - if job.diretorio == nome_diretorio: - job.finaliza_parte(nome_saida, par) + trata_comando_tcp_saida(con, par, nome_diretorio, nome_saida, tamanho) elif comando == 'executa': # Formato da msg: executa|programa|diretorio_job|nome_entrada|remetente From f6e68db9fd95b17cb8280e93e009d44cfdddc859 Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Sat, 7 Feb 2026 13:55:52 -0300 Subject: [PATCH 27/31] extract func: 'trata_comando_tcp_executa' --- grid.py | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/grid.py b/grid.py index 5c6ce7d..a04437c 100644 --- a/grid.py +++ b/grid.py @@ -619,6 +619,29 @@ def trata_comando_tcp_saida(con, par, nome_dir : str, nome_saida : str, tamanho job.finaliza_parte(nome_saida, par) #---------------------------------------------------------------------------------------- +#---------------------------------------------------------------------------------------- +def trata_comando_tcp_executa(par, programa, dir_job, nome_entrada, nome_saida): + """ + Trata o comando de execucao de um programa sobre um arquivo de entrada, + e ja faz envio de resultado e arquivo de saida ao par que solicitou a execucao. + """ + dir_entrada = f'./temp/{dir_job}/entrada/' + dir_saida = f'./temp/{dir_job}/saida/' + try: + print(f'\nIniciando execucao do programa {programa}') + call([f'./programs/{programa}', + dir_entrada + nome_entrada, + dir_saida + nome_saida]) + #resposta = 'pronto' + except Exception as ex: + print(f'\nERRO na execucao do programa {programa}') + print(ex) + #resposta = 'erro' + envia_saida(dir_job, nome_saida, par) + # NOTE: avaliar a possibilidade de enviar a conexao 'con' por aqui, + # evitando a necessidade de abrir nova conexao TCP dentro da func acima +#---------------------------------------------------------------------------------------- + #---------------------------------------------------------------------------------------- def conexao_tcp_thread(con, par): """ @@ -646,7 +669,7 @@ def conexao_tcp_thread(con, par): tamanho = int(cabecalho[3]) trata_comando_tcp_entrada(con, dir_entrada, nome_entrada, tamanho) - elif comando == 'saida': + elif comando == 'saida': # Formato: saida|diretorio_job|nome_saida|tamanho| nome_diretorio = cabecalho[1] nome_saida = cabecalho[2] @@ -656,22 +679,10 @@ def conexao_tcp_thread(con, par): elif comando == 'executa': # Formato da msg: executa|programa|diretorio_job|nome_entrada|remetente programa = cabecalho[1] - nome_diretorio_job = cabecalho[2] - diretorio_entrada = f'./temp/{nome_diretorio_job}/entrada/' - diretorio_saida = f'./temp/{nome_diretorio_job}/saida/' + dir_job = cabecalho[2] nome_entrada = cabecalho[3] nome_saida = nome_entrada[0:-3] + '.out' - try: - print(f'\nIniciando execucao do programa {programa}') - call([f'./programs/{programa}', - diretorio_entrada + nome_entrada, - diretorio_saida + nome_saida]) - #resposta = 'pronto' - except Exception as ex: - print(f'\nERRO na execucao do programa {programa}') - print(ex) - #resposta = 'erro' - envia_saida(nome_diretorio_job, nome_saida, par) + trata_comando_tcp_executa(par, programa, dir_job, nome_entrada, nome_saida) else: while resp: From 684c9daadbc9a8ac296d1039eaa9242f0a180ebd Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Sat, 7 Feb 2026 19:56:29 -0300 Subject: [PATCH 28/31] to ignore a couple of pylint warnings --- grid.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grid.py b/grid.py index a04437c..17f137f 100644 --- a/grid.py +++ b/grid.py @@ -37,7 +37,7 @@ MAX_DE_PARES = 3 lista_pares = [] -job = None +job = None # pylint: disable=invalid-name #---------------------------------------------------------------------------------------- def encerrar_programa(): @@ -344,7 +344,7 @@ def carrega_job(nome_arquivo : str): """ Carrega um job a partir de um arquivo .job especificado. """ - global job + global job # pylint: disable=global-statement,invalid-name arquivo_job = [] try: From bac4f64ff9215cbc6b4034edb5c90eb8bf3e4e8a Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Sat, 7 Feb 2026 20:00:40 -0300 Subject: [PATCH 29/31] print exception content --- grid.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/grid.py b/grid.py index 17f137f..b4ad5f3 100644 --- a/grid.py +++ b/grid.py @@ -116,8 +116,9 @@ def processa_pacote(msg_com_dados, endereco_par): try: call([f"./programs/{msg[2]}", msg[3]]) resposta = 'done cmd' - except Exception: # Changed to catch all exceptions + except Exception as ex: resposta = 'erro cmd' + print(ex) elif msg[0] == 'msg': msg_print = f"Msg. de {str(endereco_par)} : " if len(msg) > 1: From 56678d1f095f5b7e94eb2e5596e058b3f3543111 Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Sat, 7 Feb 2026 22:20:59 -0300 Subject: [PATCH 30/31] disable 'broad-except' warning in pylint --- grid.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grid.py b/grid.py index b4ad5f3..a6897ed 100644 --- a/grid.py +++ b/grid.py @@ -19,6 +19,9 @@ Contém o ponto de entrada e a lógica principal do programa. """ +# TODO: better error handling... +# pylint: disable=broad-except + from subprocess import call from socket import socket, AF_INET, SOCK_DGRAM, SOCK_STREAM import threading From d2327e43ef7de52277ddd3a9b9b4020584a82517 Mon Sep 17 00:00:00 2001 From: Tiago Matos Date: Sat, 7 Feb 2026 22:24:12 -0300 Subject: [PATCH 31/31] disable 'fixme' warning in pylint --- grid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grid.py b/grid.py index a6897ed..08bde7c 100644 --- a/grid.py +++ b/grid.py @@ -19,8 +19,8 @@ Contém o ponto de entrada e a lógica principal do programa. """ +# pylint: disable=fixme,broad-except # TODO: better error handling... -# pylint: disable=broad-except from subprocess import call from socket import socket, AF_INET, SOCK_DGRAM, SOCK_STREAM