Proiect - Etapa 2 - Sistem energetic

Obiective

  • dezvoltarea unor abilități de bază de organizare și design orientat-obiect
  • scrierea de cod pornind de la un alt cod existent, prin adăugarea de noi funcționalități
  • crearea unui design decuplat și ușor citibil
  • respectarea unui stil de codare și de comentare

Scenariu

Rețeaua de curent electric a unei țări este un sistem complex, format din producători, distribuitori, consumatori (casnici sau industriali) și instituții ale statului care reglementează și supraveghează buna funcționare a sistemului.

Etapa trecută s-a bazat pe interacțiunea dintre două entități - consumatori și distribuitori. În cadrul acestei etape, vom mai adăuga o entitate - producătorii - și vom extinde responsabilitățile distribuitorilor, păstrând în același timp și funcționalitățile de bază din prima etapă.

Consumatori

Aceștia funcționează ca în etapa trecută, cu o singura precizare suplimentară:

În cazul în care un consumator ramane cu o datorie în ultima lună de contract, acesta va fi în una din două situații:

  • dacă noul contract este la același distribuitor, va trebui sa plătească factura veche + penalități + factura nouă;
  • dacă noul contract este la alt distribuitor, acesta poate plăti doar factura veche + penalități, amânând noua factură.

Distribuitori

În aceasta etapă, distribuitorii vor interacționa și cu producătorii. În ceea ce privește consumatorii, interacțiunea va fi cea de la etapa anterioară, singura modificare fiind dată de înlocuirea costului de producție cu un cost obținut din cumpărarea energiei de la producători.

Interacțiunea cu producătorii:

  • Distribuitorii au nevoie de o cantitate de energie lunară. Aceasta este constantă și dată ca input.
  • În prima rundă distribuitorii își aleg unul sau mai mulți producători care să le ofere cantitatea de energie necesară
    • alegerea se face pe baza unor strategii - fiecare distribuitor are o strategie primită ca input
  • Dacă a apărut o schimbare a cantității de energie oferită de un producător, atunci la începutul următoarei luni, distribuitorii care iau energie de la acel producător își for aplica din nou strategia de alegere producători.

Schimbări lunare date de simulare:

  • costul pentru infrastructură

În input și în schimbările lunare s-a eliminat costul de producție pentru că acesta va fi calculat pe baza energiei luate de le producători.

Producători

Aceștia reprezintă noua entitate introdusă în această etapă. Un producător va produce energie de un anumit tip și o vinde mai multor distribuitori.

Proprietățile unui producător:

  • tip de energie produsă, care poate fi regenerabilă (wind, solar, hydro) sau nu (coal, nuclear)
    • tipurile sunt definite într-un enum EnergyType dat în schelet
  • preț pe KWh
  • cantitatea lunară de energie oferită fiecărui distribuitor - oferă aceeași cantitate de energie fiecăruia
  • numărul maxim de distribuitori către care poate oferi energie

Schimbări lunare date de simulare:

  • energia oferită fiecărui distribuitor

Condiții, simplificări:

  • Un producător nu este obligat să își vândă lunar energie către numărul maxim de distribuitori.
  • Un distribuitor trebuie să iși ia de la producători toata cantitatea de energie lunară necesară
  • Vom considera că vor fi suficienți producători și cu cantități lunare oferite suficiente astfel încât să se poată acoperi tot necesarul distribuitorilor - testele vor asigura aceasta condiție

Strategii de alegere producători

  • Green Strategy - un distribuitor își alege producătorii prioritizându-i pe cei cu renewable energy întâi, apoi după preț, apoi după cantitate
  • Price Strategy - un distribuitor își alege producătorii prioritizând doar după preț, apoi după cantitate
  • Quantity Strategy - un distribuitor își alege producătorii prioritizând după cantitatea de energie oferită per distribuitor

Dacă și tipul și prețul și cantitatea sunt identice atunci producătorii se aleg în ordinea id-urilor lor.

Detalii de implementare

Mecanismul simulării și părțile de input și output sunt similare celor de la prima etapă. Simularea se bazează pe luni (runde), al căror număr este fixat din input (numberOfTurns), și se termină când au fost rulate numberOfTurns + 1 runde și se afișează starea curentă a simulării. În cazul în care toți distribuitorii dau faliment, simularea se va încheia.

Input, Output, Rulare

Atât inputul, cât și outputul vor fi de tip json. Pentru exemple despre cum să citiți/scrie fișiere JSON folosind biblioteca jackson aveți acest tutorial pe wiki. Puteți folosi și alte biblioteci pentru citirea lor însă să cereți întâi responsabililor temei (pe forum) adăugarea acelor jar-uri și pe vmchecker.

Inputul este același pentru consumatori și are modificări pentru distribuitori și producători.

Click pentru exemplu input

Click pentru exemplu input

{
  "numberOfTurns": 6,
  "initialData": {
    "consumers": [
      {
        "id": 0,
        "initialBudget": 150,
        "monthlyIncome": 28
      }
    ],
    "distributors": [
      {
        "id": 0,
        "contractLength": 6,
        "initialBudget": 60,
        "initialInfrastructureCost": 24,
        "energyNeededKW": 1930,
        "producerStrategy": "GREEN"
      }
    ],
    "producers": [
      {
        "id": 0,
        "energyType": "WIND",
        "maxDistributors": 10,
        "priceKW": 0.01,
        "energyPerDistributor": 2695
      }
    ]
  },
  "monthlyUpdates": [
    {
      "newConsumers": [
        {
          "id": 0,
          "initialBudget": 140,
          "monthlyIncome": 24
        }
      ],
      "distributorChanges": [
        {
          "id": 0,
          "infrastructureCost": 20
        }
      ],
      "producerChanges": [
        {
          "id": 0,
          "energyPerDistributor": 6319
        }
      ]
    },
    {
      "newConsumers": [
        {
          "id": 0,
          "initialBudget": 56,
          "monthlyIncome": 97
        }
      ],
      "distributorChanges": [
        {
          "id": 0,
          "infrastructureCost": 25
        }
      ],
      "producerChanges": [
        {
          "id": 0,
          "energyPerDistributor": 4430
        }
      ]
    },
    {
      "newConsumers": [
        {
          "id": 0,
          "initialBudget": 188,
          "monthlyIncome": 43
        }
      ],
      "distributorChanges": [
        {
          "id": 0,
          "infrastructureCost": 17
        }
      ],
      "producerChanges": [
        {
          "id": 0,
          "energyPerDistributor": 9965
        }
      ]
    },
    {
      "newConsumers": [
        {
          "id": 0,
          "initialBudget": 55,
          "monthlyIncome": 65
        }
      ],
      "distributorChanges": [
        {
          "id": 0,
          "infrastructureCost": 17
        }
      ],
      "producerChanges": [
        {
          "id": 0,
          "energyPerDistributor": 2569
        }
      ]
    },
    {
      "newConsumers": [
        {
          "id": 0,
          "initialBudget": 163,
          "monthlyIncome": 27
        }
      ],
      "distributorChanges": [
        {
          "id": 0,
          "infrastructureCost": 13
        }
      ],
      "producerChanges": [
        {
          "id": 0,
          "energyPerDistributor": 9077
        }
      ]
    },
    {
      "newConsumers": [
        {
          "id": 0,
          "initialBudget": 140,
          "monthlyIncome": 85
        }
      ],
      "distributorChanges": [
        {
          "id": 0,
          "infrastructureCost": 16
        }
      ],
      "producerChanges": [
        {
          "id": 0,
          "energyPerDistributor": 4523
        }
      ]
    }
  ]
}

Output-ul este același ca în etapa 1 pentru distribuitori și consumatori, iar pentru producători vom avea update-uri lunare despre câți distribuitori au avut.

Click pentru exemplu output

Click pentru exemplu output

{
  "consumers" : [ {
    "id" : 0,
    "isBankrupt" : false,
    "budget" : 178
  }, {
    "id" : 0,
    "isBankrupt" : false,
    "budget" : 152
  }, {
    "id" : 0,
    "isBankrupt" : false,
    "budget" : 446
  }, {
    "id" : 0,
    "isBankrupt" : false,
    "budget" : 324
  }, {
    "id" : 0,
    "isBankrupt" : false,
    "budget" : 208
  }, {
    "id" : 0,
    "isBankrupt" : false,
    "budget" : 209
  }, {
    "id" : 0,
    "isBankrupt" : false,
    "budget" : 213
  } ],
  "distributors" : [ {
    "id" : 0,
    "energyNeededKW" : 1930,
    "contractCost" : 12,
    "budget" : 261,
    "producerStrategy" : "GREEN",
    "isBankrupt" : false,
    "contracts" : [ {
      "consumerId" : 0,
      "price" : 22,
      "remainedContractMonths" : 0
    }, {
      "consumerId" : 0,
      "price" : 19,
      "remainedContractMonths" : 1
    }, {
      "consumerId" : 0,
      "price" : 9,
      "remainedContractMonths" : 2
    }, {
      "consumerId" : 0,
      "price" : 14,
      "remainedContractMonths" : 3
    }, {
      "consumerId" : 0,
      "price" : 4,
      "remainedContractMonths" : 4
    }, {
      "consumerId" : 0,
      "price" : 12,
      "remainedContractMonths" : 5
    }, {
      "consumerId" : 0,
      "price" : 12,
      "remainedContractMonths" : 5
    } ]
  } ],
  "energyProducers" : [ {
    "id" : 0,
    "maxDistributors" : 10,
    "priceKW" : 0.01,
    "energyType" : "WIND",
    "energyPerDistributor" : 4523,
    "monthlyStats" : [ {
      "month" : 1,
      "distributorsIds" : [ 0 ]
    }, {
      "month" : 2,
      "distributorsIds" : [ 0 ]
    }, {
      "month" : 3,
      "distributorsIds" : [ 0 ]
    }, {
      "month" : 4,
      "distributorsIds" : [ 0 ]
    }, {
      "month" : 5,
      "distributorsIds" : [ 0 ]
    }, {
      "month" : 6,
      "distributorsIds" : [ 0 ]
    } ]
  } ]
}

Pentru testarea soluției, rulați funcția main a clasei Test. Aceasta va rula atat testele, cat și checkstyle-ul. Pentru rularea checkerului, aveți nevoie ca proiectul vostru sa aiba încărcate bibliotecile pentru citirea fișierelor json. Mai multe detalii aici.

Dacă doriți să verificați individual un test, rulați main-ul din clasa Main dând ca argumente fișierul de intrare și fișierul de output.

Click aici pentru a vedea cum să dați argumente main-ului din IDE

Click aici pentru a vedea cum să dați argumente main-ului din IDE

Simularea

Simularea va fi aproape identică cu cea din etapa 1 a proiectului. Apariția producătorilor va introduce o nouă funcționalitate, astfel:

  1. La începutul primei luni, distribuitorii își vor alege un producător de la care vor cumpara energie lunar.
  2. Distribuitorii vor observa producătorii de la care iau energie, și își vor reaplica strategia de alegere producatori doar în lunile în care apare un update la vreunul din producători.

Runda inițială:

  1. Se încarcă datele de intrare despre distribuitori, consumatori și producători.
    • dacă păstrați liste pentru producători să le aveți sortate crescător în funcție de id-ul lor.
  2. Distribuitorii
    1. își aleg producătorii
    2. își calculează costul de producție
    3. își calculează costul inițial al contractelor
  3. Consumatorii - la fel ca la etapa 1
    1. își aleg distribuitorii și îi plătesc
  4. Distribuitorii obțin bani de la consumatori și își plătesc costurile, actualizând costul infrastructurii

Flow în fiecare lună:

Începutul lunii:

  1. se obțin noile valori din test
  2. se actualizează valorile pentru distribuitori
  3. se actualizează valorile pentru consumatori
  4. consumatorii își aleg distributorii și îi plătesc
  5. distribuitorii își plătesc costurile

În timpul lunii

  1. se actualizează valorile citite din test pentru luna respectivă pentru producători

La sfârșitul lunii

  1. toți distribuitorii non-bankrupt își actualizează producătorii dacă e cazul și își calculează costul de producție
    1. ordinea actualizării se face în ordinea crescătoare a id-urilor distribuitorilor
    2. în momentul în care unui distribuitor îi vine randul să își actualizeze producătorii, întâi se scoate acest distribuitor de la toți producătorii de la care lua energie și apoi se aplică strategia de alegere.
  2. se rețin câți distribuitori a avut fiecare producător

La finalul simulării se scriu în format json în fișierul de output - informațiile despre consumatori și distribuitori în mod similar primei etape - pentru fiecare producător câți distribuitori a avut în fiecare lună

:!: În momentul în care un distribuitor devine bankrupt, producătorii vor fi informați ca să nu mai îi dea energie (de exemplu dacă păstrați o listă în producător cu ce distribuitori are, îl scoateți din listă).

Formule

Formulele de la prima etapă rămân nemodificate, și se adaugă calcului costului de producție cerut fiecărui distribuitor:

  • cost = sum (cantitate energie de la producator * pret pe Kw de la producator)
  • productionCost = Math.round(Math.floor(cost / 10));

Exemplu:

  • Un productor A cu energie de tip solar, oferind 1200 KWh lunar fiecărui distribuitor, la prețul 0.1 pe KWh
  • Un producător B cu energie de tip nuclear, oferind 2000 KWh lunar fiecărui distribuitor, la prețul 0.3 pe KWh
  • Un producător C cu energie de tip nuclear, oferind 1800 KWh lunar fiecărui distribuitor, la prețul 0.2 pe KWh
  • Un distribuitor D care are nevoie de 2200 KWh lunar, strategie de tip Green.

Distribuitorul D va lua energie de la A și de la C. Costul producției va fi: (1200 * 0.1 + 1800 * 0.2)/10 = 48

Design

Design-ul codului vostru ar trebui să fie cât mai decuplat și ușor de extins și urmărit. În afară de folosirea unor design patterns, în evaluarea temei o să luăm în considere aspecte legate de ce obiecte folosiți, ce relații aveți între ele, care este scopul lor, vizibilitatea câmpurilor și metodelor etc (exemplu de astfel de guidelines: clean code summary

Design patterns-urile pe care le recomandăm pentru aceasta etapă sunt Strategy și Observer. În mod evident, strategy merge folosit pentru alegerea producatorilor. Legat de observer, în situația din scenariul temei puteți să îl adaptați în mai multe feluri, nu vă impunem un anumit mod, este la latitudinea voastră cum alegeți să îl folosiți.

Câteva posibilități pentru aplicarea Observer:

  • fiecare distribuitor trebuie să afle dacă au apărut modificări la producătorii săi. Puteți avea ca Observers distribuitorii și ca Observables consumatorii. În momentul în care un producător primeșste de la sistemul de simulare update pt energia oferita, atunci își notifică și distribuitorii. Distribuitorii iau în considerare că s-a efectuat o schimbare iar în luna următoare își realeg producătorii
  • ca să nu mai fie relație many-to-many între observeri și observables, o mitigare ar fi folosirea unei alte clase intermediare drept Subiect care doar ține o structură de date cu energia oferită de fiecare productor. Când se modifică energia unui producător, se anunță toți distribuitorii și aceștia verifică dacă sunt afectați sau nu.
  • putem avea ca observatori producătorii și ca observable sistemul de simulare, care lunar primește schimbările legate de energia oferită de producători.

Pentru a avea deja mecanismul de notificare dintre observers si observables, puteți folosi interfața Observer și clasa Observable din java.util. Dacă doriți puteți să vă implementați propriile interfețe/clase. Unul dintre motivele pentru care acest API ese deprecated din java 9 pentru că este prea simplu, însă acest lucru îl face potrivit pt tema și laborator. Într-o aplicație reală puteți folosi alte api-uri care sunt mult mai complexe și oferă foarte multe tipuri de obiecte și mecanisme (termenul folosit este reactive programming).

Indicații

  • Separați conceptele de sine stătătoare în clase separate, nu le îmbinați - clasele ar trebui să aibă un sigur rol
  • Adaptați agregarea și moștenirea la situație, grupați pe cât posibil informația și acțiunile comune în clase generale
  • Nu vă apucați să scrieți direct; alocați timp modelării și abstractizării, pentru că altfel vă puteți trezi cu o temă muncitorească, cu mult cod din care să nu înțelegeți prea multe și pe care să-l extindeți greu
  • Vă recomandăm să porniți implementarea de la codul scris în prima etapă a proiectului, la care să adăugați noile funcționalități, dar se poate trimite și o implementare scrisă de la zero, care să nu păstreze codul primei părți, dar care să conțină funcționalitatea primei părți
  • Etapa a doua se poate trimite fără să fi trimis prima etapă a proiectului, însă va fi nevoie să se implementeze și funcționalitățile primei etape pentru a putea primi punctajul total pe teste; în acest caz se va primi punctaj doar pe a doua etapă
  • Pentru calculele ce includ procente și numere cu zecimale, veți folosi doar partea întreagă a numărului.

Evaluare

Punctajul constă din:

  • 80p implementare - trecerea testelor
  • 10p coding style (vezi checkstyle)
  • 5p README
    • Puteți porni de la template-ul dat în schelet, redenumindu-l în README.md. Template-ul este în română însă puteți scrie readme-ul și în engleză
  • 5p folosire git pentru versionarea temei

Pe pagina Indicații pentru teme găsiți indicații despre scrierea readme-ului și depunctările generale pentru teme

Depunctarile pentru designul și organizarea codului se vor scădea din punctajul testelor.

  • -7p dacă nu se folosește Observer
  • -5p dacă nu se folosește Strategy
  • -1 … -10 nefolosire concepte OOP, greșeli în folosire etc

Dacă vor apărea depunctari specifice temei în momentul evaluării, nemenționate pe pagina cu depunctări generale, ele se vor încadra în limitele de maxim 15 pentru design, 5p pentru readme. Dacă tema nu respecta cerințele, sau are zero design OOP atunci se pot face depunctari suplimentare.

Folosirea git pentru versionare va fi verificata din folderul .git pe care trebuie să îl includeți în arhiva temei. Punctajul se va acorda dacă ați făcut minim 3 commit-uri relevante și cu mesaj sugestiv.

Bonusuri: La evaluare, putem oferi bonusuri pentru design foarte bun, cod bine documentat dar și pentru diverse elemente suplimentare alese de voi.

Temele vor fi testate împotriva plagiatului. Orice tentativă de copiere va duce la anularea punctajului de pe parcursul semestrului şi repetarea materiei atât pentru sursă(e) cât şi pentru destinație(ii), fără excepție.

Checkstyle

Unul din obiectivele temei este învățarea respectării code-style-ului limbajului pe care îl folosiți. Aceste convenții (de exemplu cum numiți fișierele, clasele, variabilele, cum indentați) sunt verificate pentru temă de către tool-ul checkstyle.

Pe pagina de Recomandări cod găsiți câteva exemple de coding style.

Dacă numărul de erori depistate de checkstyle depășește 30, atunci punctele pentru coding-style nu vor fi acordate. Dacă punctajul este negativ, acesta se trunchiază la 0.

Exemple:

  • punctaj_total = 100 și nr_erori = 200nota_finala = 90
  • punctaj_total = 100 și nr_erori = 29nota_finala = 100
  • punctaj_total = 80 și nr_erori = 30nota_finala = 80
  • punctaj_total = 80 și nr_erori = 31nota_finala = 70

Upload temă

Arhiva pe care o veţi urca pe VMChecker va trebui să conţină în directorul rădăcină:

  • fișierul README
  • folder-ul src cu pachetele și cu fișierele .java
  • folderul .git

Resurse și linkuri utile

poo-ca-cd/teme/proiect/etapa2.txt · Last modified: 2021/01/17 14:27 by florin.mihalache
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0