Nius¶
Context¶
Es vol fer un atles d’ocells nidificats de Catalunya. Per fer-ho, dividim el territori amb una reixa de quadrats de 1km de costat, escampant molts observadors al llarg dels quadrats en estudi. Cada quadrat té una referència que anomenarem zona (geogràfica). Cada observador disposa d’un mòbil connectat a Internet amb una aplicació en que indica a un ordinador central si ha trobat un niu o més d’ocells, quin tipus d’ocell es tracta, i en quina zona geogràfica l’ha trobat. Anomenarem troballa a la tríada de dades observades.
L’ordinador central recull les troballes enviades pels observadors. Aquestes son tractades per tenir comptabilitzats per cada tipus d’ocell i zona on es troben, el nombre de nius trobats. El resultat es guarda en un contenidor del tipus Recull.
Per fer això, ens demanen la funció central() que farà el paper de servidor dels clients observadors. Gestionarà la comunicació de cada observador mitjançant l’execució d’un fil (observador()) independent dels altres observadors actius. El fil rebrà les troballes enviades per l’observador connectat al fil, actualitzant en el contenidor del tipus Recull, els comptadors de nius concernents a les troballes processades. El contenidor és el mateix per la resta de fils actius (observadors actius).
La comunicació entre l’ordinador de la central i els mòbils dels
observadors es fa via socket.socket seguint el patró de
client-servidor. És a dir, l’observador envia una troballa al servidor. El servidor envia la conformitat de haver-la rebuda i la processa.
La comunicació la farem via sockets que accepten la família d’adreces d’IP v4 (socket.AF_INET), i dades en forma de seqüència de bytes (socket.SOCK_STREAM). Les troballes dels observadors es donaran en forma de tuple (n, ocell, zona), on n (int) és el nombre de nius trobats, ocell (str) és l’ocell del niu, i zona (int o
str) una referència a la zona geogràfica on s’ubica el
niu. La comunicació es farà convertint tot el tuple en
str, i de str a bytes. Disposeu
del mòdul codis.py que us facilita dues funcions per rebre
o transmetre tuples (repTroballa() i
enviaTroballa()). A més, hi ha les funcions repOK() i enviaOK() com a protocols de conformitat en la comunicació
de dades.
Per comprovar el desenvolupament de tot el sistema farem una simulació del
comportament d’un observador mitjançant processos independents que
executaran la funció observador(). La funció la trobareu a
observador.py. El que fa és llegir un fitxer que conté
troballes i enviar-les a la central.
Es demana¶
Disposeu d’una primera versió del disseny de la classe
Recullarecull0.py. Copieu el contingut del fitxer al fitxer de nomrecull.py. La seva especificació és:Un exemple d’ús:
>>> from recull import Recull >>> troballes = [( 1, 'Alosa becuda', 100)] * 30 + \ ... [( 1, 'Alosa becuda', 101)] * 15 + \ ... [( 5, 'Pardal comú', 114)] * 40 >>> rec = Recull() >>> for t in troballes: ... n_nius, ocell, zona = t ... rec.compta(n_nius, ocell, zona) ... >>> print(rec) (Alosa becuda,100): 30 (Alosa becuda,101): 15 (Pardal comú,114): 200
Tenint en compte que una instància d’aquesta classe serà compartit per dos o més processos, cal retocar algun o alguns mètodes per tal que la classe funcioni de forma segura. Es tracta d’afegir on calgui, les instruccions oportunes. En cap cas, cal modificar les instruccions ja existents. Tingueu en compte també que en una zona geogràfica poden haver n exploradors observant el mateix ocell. També, que la operació d’accés al diccionari no és atòmica i per tant hi ha situació de competència entre observadors de distintes zones i ocells.
En el mòdul
dades_observador.pydissenyeu la funció:- dades_observador.dades_observador(ptConn, recull)¶
- Parameters:
ptConn (socket) – socket per rebre troballes i enviar conformitat.
recull (Recull) – on es guarden les dades
Gestiona la connexió ptConn d’un observador acceptada a la central. Rep de l’observador les seves troballes. Cada troballa rebuda, l’acumula en recull, enviant a l’observador la conformitat de la recepció.
La comunicació entre observador i central es tancarà per part de l”observador. Això suposarà també el final del fil d’obtenció de dades corresponent. La construcció de python
tryexceptus pot ajudar a preveure aquest final (socket.error: error del sistema operatiu al perdre la connexió). Quan hagi l’error, aprofitarem també per tancarptConni acabar la funció. Assegureu-vos també que el funcionament de la connexió tipussocket.socketsigui bloquejantsocket.socket.setblocking().En el mòdul
central.pydissenyeu la funció:- central.central(conn, port, nConnexions)¶
- Parameters:
La funció serveix als observadors que recullen dades pel territori. Crea un contenidor del tipus
Recull, i per cada observador que es vol connectar, crea un fil concurrent de la funciódades_observador()per gestionar la seva comunicació. La funciódades_observador()treballa a partir del socket de connexió acceptat i del contenidor creat i compartit per tots els altres fils engegats. La funció acaba un cop ha tractat nConnexions connexions.
Feu que el mòdul sigui també un programa amb un paràmetre de línia que serà el nombre d’observadors a atendre. Els dos primers arguments de la funció seran
''i50007. El tercer és el que es dona per la línia de comanda.Per exemple:
$ python3 central.py 3
En el mòdul
sistema.pydissenyeu la funció:- sistema.sistema(troballes)¶
- Parameters:
troballes (list) – llista de noms de fitxers de troballes per la simulació.. Cada fitxer és l’entrada a un procés observador.
Engega un procés que executa
central.central()amb els arguments de adreça'', el port50007, i el nombre d’observadors a atendre (nb=len(troballes)). Per evitar problemes de connexió, ens esperem dos segons per continuar amb la resta de processos a engegar.Iniciem i engeguem
nbprocessos amb la funcióobservador.observador()i els arguments de'localhost', port50007, i un nom de fitxer de la llista troballes.Esperem que tots els processos observadors acabin. Després, esperem el procés central. Un cop acabat els processos, acabem la funció.
Feu que el mòdul sigui també un programa tenint com a paràmetres de línia els fitxers a processar pels observadors.
Per exemple:
$ python3 sistema.py /tmp/dadesexpl1.dat /tmp/dadesexpl2.dat /tmp/dadesexpl3.dat
simularà 3 observadors amb la central. Cada observador processa un dels fitxers de troballes donats. Si s’haguessin posat 5 fitxers, en simularia 5. Tingueu-ho en compte doncs, el nombre variable d’arguments.
Escriviu en el fitxer
sistema.sh, una línia de comanda en bash que executi els programescentral.py, i 3 programesobservador.pyconcurrentment. Cal que el programa central.py sigui el primer en engegar-se, i un cop fet, esperar uns dos segons abans de llençar els programes observador. Com a arguments d’observador podeu usar els fitxers creats a l’executar el doctesttest-central-observador.txt. Trobareu els fitxers al directori /tmp/. Es valorarà el fer-ho en una sola línia de text.
Es disposa¶
En aquest graf d’importacions podem veure tots els mòduls de l’aplicació.
Disposeu de
codis.pyper l’enviament i recepció de troballes i protocols.
Heu de dissenyar:
recull.py(redisseny derecull0.py)
dades_observador.py
central.py
sistema.py
sistema.sh
Per fer proves¶
Per provar els mòduls que heu dissenyat o modificat disposeu dels fitxers:
test-central-observador.txt. Aquest doctest crea en el directori /tmp/ 5 fitxers per simular les troballes dels observadors (els noms son de l’estildadesexpl1.dat)