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('')