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') diff --git a/Definicoes.py b/Definicoes.py deleted file mode 100644 index 4d64084..0000000 --- a/Definicoes.py +++ /dev/null @@ -1,6 +0,0 @@ - -porta = 27950 # Nossa porta inicialmente -portaPar = 27950 # Porta de um par... - -PORTA_TCP = 47555 # Nossa porta tcp inicialmente -PORTA_TCP_PAR = 47555 # Porta tcp de um par... diff --git a/Job.py b/Job.py deleted file mode 100644 index f3218fc..0000000 --- a/Job.py +++ /dev/null @@ -1,194 +0,0 @@ -import os -import datetime -import thread - -lock = thread.allocate_lock() # Objeto para lock - -# estados possiveis de uma parte -BRANCO = 'BRANCO' -ATRIBUIDO = 'ATRIBUIDO' -COMPLETO = 'COMPLETO' - -class Parte: - estado = BRANCO - par = None - data = None # data de atribuicao - entrada = None - saida = None - - def is_branco(self): - if self.estado == BRANCO: - return True - else: - return False - - def is_atribuido(self): - if self.estado == ATRIBUIDO: - return True - else: - return False - - def is_completo(self): - if self.estado == COMPLETO: - return True - else: - return False - - 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 - self.par = None - self.data = None - -class Job: - nome = None - programa = None - diretorio = None - arquivo = None - partes = 0 - 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 '' - - for file in os.listdir(dirEntrada): - if file.endswith(".in"): - parte = Parte() - parte.entrada = file - - if os.path.isfile(dirSaida + '/' + file[0:-3] + '.out'): - parte.estado = COMPLETO - parte.saida = file[0:-3] + '.out' - - self.listaPartes.append(parte) - self.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) - 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 - - # 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 - ok = False - elif par not in self.listaPares: - 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: - 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.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 - 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 - 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 - # 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 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/Util.py b/Util.py deleted file mode 100644 index 999bd21..0000000 --- a/Util.py +++ /dev/null @@ -1,20 +0,0 @@ - -# util.py (funcoes utilitarias soltas) - -#---------------------------------------------------------------------------------------- -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 '' -#---------------------------------------------------------------------------------------- diff --git a/definicoes.py b/definicoes.py new file mode 100644 index 0000000..24b4d20 --- /dev/null +++ b/definicoes.py @@ -0,0 +1,9 @@ +""" +Constantes e definições usadas no programa. +""" + +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 985354b..08bde7c 100644 --- a/grid.py +++ b/grid.py @@ -1,609 +1,735 @@ -from socket import * +# 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. +""" + +# pylint: disable=fixme,broad-except +# TODO: better error handling... + 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 Job +from definicoes import PORTA_UDP, PORTA_UDP_PAR, PORTA_TCP, PORTA_TCP_PAR +from util import exibir_ajuda_geral_de_comandos + +meu_socket_udp = socket(AF_INET, SOCK_DGRAM) # IPv4 e UDP +meu_socket_udp.settimeout(3) +meu_socket_udp.bind(('', PORTA_UDP)) -from Job import * -from Definicoes import * -from Util import * +MAX_DE_PARES = 3 +lista_pares = [] -meuSocket = socket(AF_INET, SOCK_DGRAM) # IPv4 e UDP -meuSocket.settimeout(3) -meuSocket.bind(('', porta)) +job = None # pylint: disable=invalid-name -maxDePares = 3 -listaPares = [] +#---------------------------------------------------------------------------------------- +def encerrar_programa(): + """ + Encerra o programa de forma ordenada. + """ + print('') + print('\nParando o programa.') + for par in lista_pares: + meu_socket_udp.sendto('disconect', par) + meu_socket_udp.close() + 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 +#---------------------------------------------------------------------------------------- -job = None +#---------------------------------------------------------------------------------------- +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 encerrarPrograma(): - print '' - print '\nParando o programa.' - for par in listaPares: - meuSocket.sendto('disconect', par) - meuSocket.close() - exit(0) +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 processaPacote(msgComDados, enderecoPar): - msg = msgComDados.split(';') - +def processa_pacote(msg_com_dados, endereco_par): + """ + Processa um pacote UDP recebido de um 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(listaPares) < maxDePares: - if not enderecoPar in listaPares: - listaPares.append(enderecoPar) - print '\nNosso pedido de contato foi aceito por ', str(enderecoPar) - else: - resposta = 'disconect' - + resposta = processa_pacote_ok(msg, endereco_par) elif msg[0] == 'conect': - if len(listaPares) < maxDePares: - if not enderecoPar in listaPares: - listaPares.append(enderecoPar) - - resposta = 'ok;contact' - - print '\nPedido de contato aceito originado de ' + str(enderecoPar) - else: - resposta = 'not' - + resposta = processa_pacote_conect(endereco_par) elif msg[0] == 'disconect': - if enderecoPar in listaPares: - listaPares.remove(enderecoPar) - print 'Contato desfeito por solicitacao de ' + str(enderecoPar) - - elif enderecoPar in listaPares: - + processa_pacote_disconect(endereco_par) + elif endereco_par in lista_pares: if msg[0] == 'do': resposta = 'what?' 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 WindowsError: + except Exception as ex: resposta = 'erro cmd' - + print(ex) elif msg[0] == 'msg': - msg_print = 'Msg. de ' + str(enderecoPar) + ' : ' + msg_print = f"Msg. de {str(endereco_par)} : " 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) + 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) - - thread.start_new_thread(processaPacote, tuple([msgComDados, enderecoPar])) - except Exception as e: - print e - pass + 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'. + """ + linhas_arquivo_pares = [] + try: - arquivoPares = [line.strip() for line in open('peerlist')] - except Exception as e: - print 'Erro ao acessar o arquivo peerlist!' - print e + 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(arquivoPares) == 0: - 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 + if len(linhas_arquivo_pares) == 0: + print('Arquivo peerlist vazio!') + 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(f'\nTentando contactar a: {endereco_par}') #---------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------- -def enviarArquivo(par, arquivo): - tcpSocket = socket(AF_INET, SOCK_STREAM) - tcpSocket.connect((par[0], PORTA_TCP_PAR)) - f = open (arquivo, 'rb') - buff = 1024 +def enviar_arquivo(par, arquivo): + """ + Transfere via TCP um arquivo para um par. + """ + tcp_socket = socket(AF_INET, SOCK_STREAM) + tcp_socket.connect((par[0], PORTA_TCP_PAR)) + with open(arquivo, 'rb', encoding='utf-8') as file: + buff = 1024 + arquivo = arquivo.replace('\\', '/') + nome = arquivo.split('/')[-1] + tamanho = os.path.getsize(arquivo) + 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) + cabecalho += ' ' * (1024 - len(cabecalho)) + + print('ENVIANDO: ', arquivo, ' de ', tamanho) + + tcp_socket.send(cabecalho) + + dados = file.read(buff) + while dados: + tcp_socket.send(dados) + dados = file.read(buff) - 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)) - - 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 -#---------------------------------------------------------------------------------------- - -#---------------------------------------------------------------------------------------- -def preparaJobNoPar(par): - global job - - tcpSocket = socket(AF_INET, SOCK_STREAM) - tcpSocket.connect((par[0], PORTA_TCP_PAR)) - - tcpSocket.send('job|' + job.diretorio + '|') - + tcp_socket.close() + print('FIM DO ENVIO DE: ', arquivo) +#---------------------------------------------------------------------------------------- + +#---------------------------------------------------------------------------------------- +def prepara_job_no_par(par): + """ + Prepara um par para receber o job carregado. + """ + 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) - - if r == 'ok': - print 'PAR ', par[0], ' esta preparado para o job ', job.nome - job.inserePar(par) + print('') + resp = tcp_socket.recv(buff).decode('utf-8') + if resp == 'ok': + print(f"PAR {par[0]} esta preparado para o job {job.nome}") + job.insere_par(par) else: - print 'PAR ', par[0], ' nao pode preparar (ou nao e possivel confirmar) para o job ', job.nome - job.removePar(par) - - tcpSocket.close() + 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 enviaEntrada(entrada, par): - f = None +#---------------------------------------------------------------------------------------- +def envia_entrada(entrada : str, par): + """ + Transfere via TCP um arquivo de entrada para um par. + """ + file_path = f"./jobs/{job.diretorio}/entrada/{entrada}" - 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 - 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 - 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 - tcpSocket.close() - f.close() + 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 = 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) + cabecalho += ' ' * (1024 - len(cabecalho)) + + 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) return False - - tcpSocket.close() - f.close() - - print 'FIM DO ENVIO DE: ', arquivo + + print('FIM DO ENVIO DE: ', file_path) return True #---------------------------------------------------------------------------------------- -#-Envia um comando para o par executar uma parte do job---------------------------------- -def executaParteNoPar(parte, par): - tcpSocket = socket(AF_INET, SOCK_STREAM) - +#---------------------------------------------------------------------------------------- +def executa_parte_no_par(parte, par): + """ + Envia um comando para o par executar uma parte do job. + """ + 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() -#---------------------------------------------------------------------------------------- - -#-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.' + msg = f"executa|{job.programa}|{job.diretorio}|{parte.entrada}|" + + print('EXECUTANDO: ', job.programa, ' sobre a entrada ', parte.entrada, ' em ', par) + + tcp_socket.connect((par[0], PORTA_TCP_PAR)) + tcp_socket.send(msg) + tcp_socket.close() +#---------------------------------------------------------------------------------------- + +#---------------------------------------------------------------------------------------- +def job_thread(): + """ + Thread que cuida da divisao e execucao das partes do job nos pares. + """ + + 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) - - if len(job.listaPares) == 0: - print '\nNenhum par ficou pronto para o processamento.' + + # esse loop tambem popula a lista job.lista_pares com os pares prontos + for par in lista_pares: + prepara_job_no_par(par) + + 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: - print parte.estado, parte.entrada + 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 == 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.' + + print('\nSUCESSO. O job foi concluido.') #---------------------------------------------------------------------------------------- #-Inicia a execucao do job--------------------------------------------------------------- -def executaJob(): - global job - +def executa_job(): + """ + Inicia a execucao do job carregado na memoria. + """ if not job: - print '\nNenhum job carregado.' + print('\nNenhum job carregado.') return - if len(job.listaPartes) == 0: - print '\nJob sem tarefas.' + if len(job.lista_partes) == 0: + 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: + + for parte in job.lista_partes: if parte.is_branco(): - print 'PARTE: ', parte.entrada, ' EM BRANCO' - - thread.start_new_thread( jobThread, () ) - + print('PARTE: ', parte.entrada, ' EM BRANCO') + + threading.Thread(target=job_thread, args=()).start() #---------------------------------------------------------------------------------------- #-Carrega para a memoria o job descrito pelo arquivo------------------------------------- -def carregaJob(nomeArquivo): - global job - arquivoJob = [] +def carrega_job(nome_arquivo : str): + """ + Carrega um job a partir de um arquivo .job especificado. + """ + global job # pylint: disable=global-statement,invalid-name + arquivo_job = [] - try: - for line in open('jobs/' + nomeArquivo): + try: + with open(f"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: - 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) -#---------------------------------------------------------------------------------------- - -#-Interpretacao dos comandos do prompt--------------------------------------------------- -def trataComando(stringComando): - if len(stringComando) == 0: + if len(entrada) > 0 and entrada[0] != '#': + arquivo_job.append(entrada) + except OSError as ex: + print(f'Erro ao acessar o arquivo de job: {nome_arquivo}') + print(ex) + return + + if len(arquivo_job) == 0: + print(f'Arquivo de job {nome_arquivo} está vazio.') + return + + if len(arquivo_job) < 3: + print(f'Arquivo de job {nome_arquivo} faltando parâmetros.') + return + + job = Job(arquivo_job[0], arquivo_job[1], arquivo_job[2], nome_arquivo) +#---------------------------------------------------------------------------------------- + +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. + """ + if len(string_comando) == 0: + return + + comando = string_comando.split(' ') - comando = stringComando.split(' ') - if comando[0] == 'ajuda': - exibirAjudaDeComandos() - + 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]) - except ValueError: - 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.' - else: - msgConteudo = '' - for s in comando[2:]: - msgConteudo += s + ' ' - meuSocket.sendto('msg;' + msgConteudo, listaPares[idPar]) - + executa_comando_mensagem(comando) elif comando[0] == 'enviar': - if len(comando) < 3: - print '\nArgumentos incorretos no comando.' - else: - try: - idPar = 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): - print '\nNao temos este par na nossa lista.' - - enviarArquivo(listaPares[idPar], comando[2]) - + executa_comando_enviar(comando) elif comando[0] == 'pares': - i = 0 - for par in listaPares: - print '#', i, ' - ', str(par), ' - Ocup.:', job.isParOcupado(par) if job != 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 - - carregaJob(comando[1]) - + executa_comando_carrega(comando) elif comando[0] == 'estado': - if job == None: - print 'Sem job carregado.' - else: - for parte in job.listaPartes: - print '#', parte.entrada, '-', parte.estado - + executa_comando_estado() elif comando[0] == 'executa': - executaJob() - + executa_job() elif comando[0] == 'sair': - encerrarPrograma() - + encerrar_programa() else: - print 'Comando nao reconhecido.' + 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 enviaSaida(dir, saida, par): - f = None +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_path = f"./temp/{diretorio}/saida/{saida}" - 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 - 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 - - 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 - tcpSocket.close() - f.close() + 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: saida|diretorio_job|nome_saida|tamanho| + cabecalho = f"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)) + + print(f"\nENVIANDO SAIDA: {file_path} de {str(tamanho)} bytes") + + 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_os_error: + print(f'\nERRO ao acessar o arquivo de saida {file_path}') + print(ex_os_error) return False - - tcpSocket.close() - f.close() - - print '\nFIM DO ENVIO DE: ', arquivo + + print(f'\nFIM DO ENVIO DE: {file_path}') return True #---------------------------------------------------------------------------------------- -#-Thread da conexao criada numa interacao com um par------------------------------------- -def conexaoTcpThread(con, par): - global job +#---------------------------------------------------------------------------------------- +def trata_comando_tcp_envio(con, nome : str, tamanho : int): + """ + Trata a recepcao de arquivo via TCP oriundo de um outro par. + """ 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+') - + with open(f'./recebidos/{nome}', 'wb+', encoding='utf-8') as file: 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() - - elif comando == 'job': - diretorioJob = './temp/' + cabecalho[1] - resultado = '' - - try: - if os.path.isdir(diretorioJob): - resultado = 'ok' - else: - os.makedirs(diretorioJob + '/entrada') - os.makedirs(diretorioJob + '/saida') - resultado = 'ok' - except Exception as e: - resultado = 'erro' - 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+') - + 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: - 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() - - 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+') - + print('\nFECHANDO ARQUIVO: ', file_path, + ' - 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: - 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() - - # 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 + 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 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): + """ + Thread que cuida de uma conexao TCP criada pela interacao com um par. + """ + buff = 1024 + resp = con.recv(buff).decode('utf-8') + + cabecalho = resp.split('|') + comando = cabecalho[0] + + if comando == 'envio': + nome = cabecalho[1] + tamanho = int(cabecalho[2]) + trata_comando_tcp_envio(con, nome, tamanho) + + elif comando == 'job': + diretorio_job = f"./temp/{cabecalho[1]}" + 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| + nome_diretorio = cabecalho[1] + nome_saida = cabecalho[2] + tamanho = int(cabecalho[3]) + trata_comando_tcp_saida(con, par, nome_diretorio, nome_saida, tamanho) + + 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' - remetente = par[0] - - resposta = '' - - try: - print '\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 - resposta = 'erro' - - enviaSaida(nomeDiretorioJob, nomeSaida, par) - + dir_job = cabecalho[2] + nome_entrada = cabecalho[3] + nome_saida = nome_entrada[0:-3] + '.out' + trata_comando_tcp_executa(par, programa, dir_job, nome_entrada, nome_saida) + 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(): - s = socket(AF_INET, SOCK_STREAM) - s.bind(('', PORTA_TCP)) - s.listen(10) - +def tcp_thread(): + """ + Thread que aguarda conexoes TCP de pares. + """ + sock = socket(AF_INET, SOCK_STREAM) + sock.bind(('', PORTA_TCP)) + sock.listen(10) while True: - con, par = s.accept() - thread.start_new_thread(conexaoTcpThread, tuple([con, par])) + con, par = sock.accept() + threading.Thread(target=conexao_tcp_thread, args=tuple([con, par])).start() #---------------------------------------------------------------------------------------- #-Inicializacao das threads-------------------------------------------------------------- try: - thread.start_new_thread( tcpThread, () ) - thread.start_new_thread( recepcaoThread, () ) -except Exception as e: - print 'Problemas com uma thread.' - print e - meuSocket.close() - exit(1) + threading.Thread(target=tcp_thread, args=()).start() + threading.Thread(target=recepcao_thread, args=()).start() +except Exception as ex: + print('Problemas com uma thread.') + print(ex) + meu_socket_udp.close() + sys.exit(1) -print 'Pronto.' -print '' +print('Pronto.') +print('') #---------------------------------------------------------------------------------------- #-Loop principal, usado para entrada de comandos----------------------------------------- while True: try: - comando = raw_input('Comando: ') - trataComando(comando) + str_comando = input('Comando: ') + trata_comando(str_comando) except KeyboardInterrupt: - encerrarPrograma() - except Exception as e: - print '\nHouve um erro!' - print e + encerrar_programa() + except Exception as ex: + print('\nHouve um erro!') + print(ex) #---------------------------------------------------------------------------------------- diff --git a/job.py b/job.py new file mode 100644 index 0000000..c796c1a --- /dev/null +++ b/job.py @@ -0,0 +1,229 @@ +# 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. +""" + +import os +import datetime +import threading + +lock = threading.Lock() + +# estados possiveis de uma parte +BRANCO = 'BRANCO' +ATRIBUIDO = 'ATRIBUIDO' +COMPLETO = 'COMPLETO' + +class Parte: + """ + Representa uma parte de um job. + """ + estado = BRANCO + par = None + data = None # data de atribuicao + entrada = None + 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 + arquivo = None + partes = 0 + 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 + dir_entrada = f'./jobs/{diretorio}/entrada' + dir_saida = f'./jobs/{diretorio}/saida' + print('') + + for file in os.listdir(dir_entrada): + if file.endswith(".in"): + parte = Parte() + parte.entrada = file + if os.path.isfile(f'{dir_saida}/{file[:-3]}.out'): + parte.estado = COMPLETO + parte.saida = f'{file[:-3]}.out' + self.lista_partes.append(parte) + self.partes += 1 + print(file, ' - ', parte.estado) + + print('Job carregado com ', self.partes, ' partes.') + + def finalizado(self): + """ + Retorna True se todas as partes do job estiverem completas. + """ + valor = True + with lock: + for parte in self.lista_partes: + if parte.estado != COMPLETO: + valor = False + break + return valor + + def insere_par(self, par): + """ + Insere um par na lista de pares que participam do job. + """ + if par not in self.lista_pares: + self.lista_pares.append(par) + + def remove_par(self, par): + """ + Remove um par da lista de pares que participam do job. + """ + 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 atribui_parte_ao_par(self, parte, par): + """ + Atribui uma parte do job ao par especificado + """ + ok = True + 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. + """ + with lock: + retorno = len(self.lista_par_ocupado) < len(self.lista_pares) + return retorno + + def proximo_par_livre(self): + """ + Retorna o proximo par livre para receber uma parte. + """ + par_retorno = None + 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): + """ + 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). + """ + 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 + 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. + """ + with lock: + for parte in self.lista_partes: + print('#', parte.entrada, '-', parte.estado) diff --git a/util.py b/util.py new file mode 100644 index 0000000..dcbbacb --- /dev/null +++ b/util.py @@ -0,0 +1,38 @@ +# 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('microGrid v0.2') + 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('')