alfredocentinaro.it https://www.alfredocentinaro.it/ Sito personale di Alfredo Centinaro, ingegnere informatico, insegnante, musicista. Programmazione, appunti, esercizi, sistemi e reti, tpsit, esami di stato, arduino Wed, 07 May 2025 15:27:22 +0000 it-IT hourly 1 https://wordpress.org/?v=6.8.1 https://www.alfredocentinaro.it/wp-content/uploads/2022/01/logo_alfredocentinaro-150x150.png alfredocentinaro.it https://www.alfredocentinaro.it/ 32 32 Creare una stampa in SQL https://www.alfredocentinaro.it/lezioni/sql/creare-una-stampa-in-sql/ Wed, 07 May 2025 15:27:18 +0000 https://www.alfredocentinaro.it/?p=8462 Lezione semplice e forse inutile ai più, ma quello che trattiamo è una domanda di esame di stato che è uscita alcune volte, trovando impreparati alunni e qualche collega che ne sottovalutava l’importanza di vederla a lezione. Come possiamo realizzare una stampa a video in SQL? Non vogliamo scomodare le brillanti librerie per creare pdf ... Leggi tutto

L'articolo Creare una stampa in SQL proviene da alfredocentinaro.it.

]]>
Lezione semplice e forse inutile ai più, ma quello che trattiamo è una domanda di esame di stato che è uscita alcune volte, trovando impreparati alunni e qualche collega che ne sottovalutava l’importanza di vederla a lezione. Come possiamo realizzare una stampa a video in SQL?

Non vogliamo scomodare le brillanti librerie per creare pdf da codice php. Per quanto siano sicuramente perfettamente a tema, usarle in una prova conclusiva da svolgere su carta sarebbe impossibile per un alunno senza manuale ed esempi preliminari.

Tabella e dati

Mi creo in primo luogo una tabella di prova, come una classica anagrafica studente. Ne riporto il codice di esempio.

CREATE TABLE studenti(
Matricola CHAR(6),
Nome VARCHAR(20),
Cognome VARCHAR(20),
DataIscrizione DATE) 

INSERT INTO studenti (Matricola, Nome, Cognome, DataIscrizione) VALUES
('A12345', 'Luca', 'Rossi', '2022-09-15'),
('B23456', 'Giulia', 'Verdi', '2023-01-10'),
('C34567', 'Marco', 'Bianchi', '2021-07-22'),
('D45678', 'Elena', 'Neri', '2022-11-03'),
('E56789', 'Andrea', 'Ferrari', '2023-02-28'),
('F67890', 'Sara', 'Conti', '2021-03-19'),
('G78901', 'Matteo', 'Gallo', '2022-05-30'),
('H89012', 'Chiara', 'Russo', '2023-04-12'),
('I90123', 'Davide', 'Romano', '2021-10-01'),
('J01234', 'Alice', 'Greco', '2023-06-20');

La stampa

In sql siamo abituati a stampare i dati nel classico e ragionevole formato tabellare, come ci si aspetta. In genere specifichiamo una SELECT * … oppure andiamo a inserire un certo numero di campi SELECT Matricola, Cognome …. La cosa interessante è che posso inserire anche del testo fisso virgolettato che compare come una sorta di etichetta fissa alternata ai campi dati. Assomiglia Un po’ a quello che si fa con i primi programmini C++ e il comando cout << concatenato.

SELECT  'Matricola: ', s.Matricola, ' Nome: ', s.Nome, ' Cognome: ', s.Cognome, ' Iscritto il: ', s.DataIscrizione FROM studenti AS s;

che come risultato su un db MySQL / Maria DB si comporta così:

Siccome non possiamo eliminare l’intestazione, possiamo ridefinarla in modo meno invasivo con la funzione di concatenazione:

SELECT  CONCAT('Matricola: ', s.Matricola, ' Nome: ', s.Nome, ' Cognome: ', s.Cognome, ' Iscritto il: ', s.DataIscrizione) AS "Anagrafica Studenti"
FROM studenti AS s;

Ultima modifica 7 Maggio 2025

L'articolo Creare una stampa in SQL proviene da alfredocentinaro.it.

]]>
Creare automaticamente strutture per progetti HTML e PHP con script Bash, Batch e Python (Tkinter) https://www.alfredocentinaro.it/lezioni/informatica/creare-strutture-per-progetti-html-php-con-bash-batch-e-python/ Sat, 29 Mar 2025 11:18:45 +0000 https://www.alfredocentinaro.it/?p=8371 Realizzare progetti web in HTML o PHP scolastici può risultare relativamente semplice semplice, ma ripetere la creazione manuale della struttura di file e cartelle può essere tedioso. Progetti più articolati con Laravel ad eempio prevedono gia uno script automatico per la creazione del necessario. In questo articolo vediamo come automatizzare il processo utilizzando semplici script ... Leggi tutto

L'articolo Creare automaticamente strutture per progetti HTML e PHP con script Bash, Batch e Python (Tkinter) proviene da alfredocentinaro.it.

]]>
Realizzare progetti web in HTML o PHP scolastici può risultare relativamente semplice semplice, ma ripetere la creazione manuale della struttura di file e cartelle può essere tedioso. Progetti più articolati con Laravel ad eempio prevedono gia uno script automatico per la creazione del necessario. In questo articolo vediamo come automatizzare il processo utilizzando semplici script in Bash (Linux), Batch DOS (Windows) e Python con interfaccia grafica (Tkinter). Esistono valice alternative come plugin di Visual Stuio Code e simili IDE di sviluppo ma possono risultare indigeste da configurare e non facilmenti trasportabili tra pc differenti casa/scuola.

1. Script Bash (Linux)

Crea un file template-html.sh con il seguente contenuto:

#!/bin/bash

echo -n "Inserisci il nome del progetto: "
read PROJECT_NAME

if [ -z "$PROJECT_NAME" ]; then
  echo "Errore: il nome del progetto non può essere vuoto."
  exit 1
fi

echo -n "Inserisci il percorso della destinazione: "
read DESTINATION

if [ ! -d "$DESTINATION" ]; then
  echo "Errore: la destinazione indicata non esiste."
  exit 1
fi

FULL_PATH="$DESTINATION/$PROJECT_NAME"

mkdir -p "$FULL_PATH/css" "$FULL_PATH/js" "$FULL_PATH/img"
touch "$FULL_PATH/css/style.css" "$FULL_PATH/js/script.js"

cat <<EOL > "$FULL_PATH/index.html"
<!DOCTYPE html>
<html lang="it">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link href="css/style.css" rel="stylesheet">
  <title>Document</title>
</head>
<body>
  <script src="js/script.js"></script>
</body>
</html>
EOL

echo "Struttura creata in $FULL_PATH"

Rendi eseguibile lo script o con interfaccia grafica col tasto destro sul file o da console con:

chmod +x setup_progetto.sh
./template-html.sh

oppure

sh template-html.sh

2. Script Batch DOS (Windows)

Crea un file template-html.bat partendo da un file di testo/blocco note modificando l’estensione da .txt a .bat.

@echo off
set /p PROJECT_NAME=Inserisci il nome del progetto:
if "%PROJECT_NAME%"=="" (
  echo Errore: il nome del progetto non puo' essere vuoto.
  goto end
)

set /p DESTINATION=Inserisci il percorso della destinazione:
if not exist "%DESTINATION%" (
  echo Errore: destinazione non trovata.
  goto end
)

set FULL_PATH=%DESTINATION%\%PROJECT_NAME%
mkdir "%FULL_PATH%\css" "%FULL_PATH%\js" "%FULL_PATH%\img"
type nul > "%FULL_PATH%\css\style.css"
type nul > "%FULL_PATH%\js\script.js"

(echo ^<!DOCTYPE html^>
echo ^<html lang="it"^>
echo ^<head^>
echo ^<meta charset="UTF-8"^>
echo ^<meta name="viewport" content="width=device-width, initial-scale=1.0"^>
echo ^<link href="css/style.css" rel="stylesheet"^>
echo ^<title^>Document^</title^>
echo ^</head^>
echo ^<body^>
echo ^<script src="js/script.js"^>^</script^>
echo ^</body^>
echo ^</html^>) > "%FULL_PATH%\index.html"

echo Struttura creata in %FULL_PATH%.
:end
pause

Eseguilo facendo doppio click. In alcune versioni di Windows o antivirus potrebbe segnala la possibilità che sia un virus.

3. Versione Python con GUI (Tkinter)

Installa il pacchetto Tkinter e le sue dipendenze se già non lo hai fatto(Ubuntu/Debian) :

sudo apt install python3-tk

Crea un file template-html.py con un editor di testo o IDE a piacere come Visual Studio Code:

import os
import tkinter as tk
from tkinter import filedialog, messagebox

class ProjectCreatorApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Creazione progetto web")
        self.root.geometry("450x200")

        tk.Label(root, text="Nome progetto:").pack(pady=(15, 5))
        self.project_name_entry = tk.Entry(root, width=50)
        self.project_name_entry.pack()

        tk.Label(root, text="Destinazione:").pack(pady=(10, 5))
        frame = tk.Frame(root)
        frame.pack()

        self.destination_entry = tk.Entry(frame, width=40)
        self.destination_entry.pack(side=tk.LEFT, padx=(0,5))

        browse_button = tk.Button(frame, text="Sfoglia", command=self.browse_destination)
        browse_button.pack(side=tk.LEFT)

        create_button = tk.Button(root, text="Crea Struttura", command=self.create_structure)
        create_button.pack(pady=15)

    def browse_destination(self):
        folder_selected = filedialog.askdirectory(initialdir=os.getcwd())
        if folder_selected:
            self.destination_entry.delete(0, tk.END)
            self.destination_entry.insert(0, folder_selected)

    def create_structure(self):
        name = self.project_name_entry.get().strip()
        dest = self.destination_entry.get().strip()

        if not name or not dest or not os.path.isdir(dest):
            messagebox.showerror("Errore", "Nome o destinazione non validi.")
            return

        path = os.path.join(dest, name)
        os.makedirs(os.path.join(path, "css"), exist_ok=True)
        os.makedirs(os.path.join(path, "js"), exist_ok=True)
        os.makedirs(os.path.join(path, "img"), exist_ok=True)

        open(os.path.join(path, "css/style.css"), "a").close()
        open(os.path.join(path, "js/script.js"), "a").close()

        with open(os.path.join(path, "index.html"), "w") as f:
            f.write("""<!DOCTYPE html><html lang=\"it\"><head>
<meta charset=\"UTF-8\">
<meta name=\"viewport\" content=\"width=device-width\">
<link href=\"css/style.css\" rel=\"stylesheet\">
<title>Document</title></head><body>
<script src=\"js/script.js\"></script></body></html>""")

        messagebox.showinfo("Successo", f"Struttura creata in:\n{path}")

root = tk.Tk()
app = ProjectCreatorApp(root)
root.mainloop()

Per seguirlo in una shell digita:

python template-html.py

Potrebbe essere più produttivo creare un eseguibile cliccabile sia Windows/Linux con la libreria PyInstaller. Per brevità non inseriamo qui la guida completa come procedere ma il imitiamo a segnalare il comando utile. Va controllato a dovere che il setup del vostro pc o ambiente di sviluppo abbia tutte le librerie necessarie tkinter e python per creare le librerie condivise .so necessarie. Alternativa potrebbe essere non inserireil parametro –onefile ed ottenere comunque una cartella di lavoro con tutto il necessario all’esecuzione, solo un po’ più scomodo.

 pyinstaller --onefile --windowed --icon=icona.ico html.py

Conclusioni

Grazie a questi script puoi velocizzare il tuo flusso di lavoro creando rapidamente strutture per nuovi progetti web. Personalizzali in base alle tue esigenze, file o cartelle e buona programmazione! Ti consiglio di inserirteli in una cartella sia sul pc dove lavori abitualmente, sia in un Drive/Cloud online per averli sempre a portata di mano.

(Articolo realizzato con l’ausilio di IA e supervisione umana)

Ultima modifica 15 Aprile 2025

L'articolo Creare automaticamente strutture per progetti HTML e PHP con script Bash, Batch e Python (Tkinter) proviene da alfredocentinaro.it.

]]>
Operazioni non bloccanti con Arduino https://www.alfredocentinaro.it/laboratori/arduino-esp/operazioni-non-bloccanti-con-arduino/ Fri, 28 Mar 2025 09:55:28 +0000 https://www.alfredocentinaro.it/?p=8317 Molti programmi per Arduino utilizzano il comando delay(), ma questo approccio può rallentare il loop principale, riducendo la reattività del microcontrollore. Fortunatamente, esistono strategie avanzate per eseguire più operazioni in parallelo senza bloccare il codice. In questa guida, esploreremo la programmazione non bloccante, una tecnica utile per migliorare le prestazioni del tuo sketch Arduino. Perché ... Leggi tutto

L'articolo Operazioni non bloccanti con Arduino proviene da alfredocentinaro.it.

]]>
Molti programmi per Arduino utilizzano il comando delay(), ma questo approccio può rallentare il loop principale, riducendo la reattività del microcontrollore. Fortunatamente, esistono strategie avanzate per eseguire più operazioni in parallelo senza bloccare il codice.

In questa guida, esploreremo la programmazione non bloccante, una tecnica utile per migliorare le prestazioni del tuo sketch Arduino.

Perché Evitare delay() in Arduino?

Il comando delay(ms) ferma l’esecuzione del codice per un determinato numero di millisecondi, impedendo l’esecuzione di altre istruzioni nel frattempo. Questo può essere un problema in progetti che richiedono:

  • Gestione di più eventi contemporaneamente (es. lettura di sensori, controllo di motori, comunicazione seriale).
  • Reattività elevata (es. robotica, domotica, giochi interattivi).
  • Ottimizzazione dell’uso della CPU per ridurre sprechi di risorse.

Soluzione: Usare millis()

Il segreto per evitare delay() è utilizzare la funzione millis(), che restituisce il tempo trascorso dall’accensione di Arduino in millisecondi. millis() restituisce un timer vita che viene attivato in automatico all’accensione della nostra basetta. Essendo un valore potenzialmente molto grande è bene utilizzare una variabile long per contenerlo.

Come Funziona?

  1. Salviamo il tempo corrente utilizzando millis().
  2. Confrontiamo il tempo attuale con l’ultimo evento eseguito.
  3. Se è trascorso abbastanza tempo, eseguiamo l’azione desiderata e aggiorniamo il valore registrato.

Esempio Pratico: Lampeggio di un LED senza delay()

Ecco un semplice esempio di codice che accende e spegne un LED ogni secondo senza bloccare l’esecuzione del programma, contemporaneamente alla possibilità di attivare un bottone che accende un altro led.

/* 
 * Questo sketch permette di eliminare i delay bloccanti ma di avere 
 * una temporizzazione di un led senza bloccarlo nel lampeggio
 * così da poter eseguire un'operazione fintamente in parallelo
 */
const int BOTTONE = 2;
const int LEDROSSO = 10; //led che si accende al click
const int LEDVERDE = 13; //led che lampeggia
const int DELAY= 1000;
int tempoPassato = 0;

void setup() 
{
  pinMode(LEDROSSO, OUTPUT);
  pinMode(LEDVERDE, OUTPUT);
  Serial.begin(9600);
}

void loop() 
{
  int bottone = digitalRead(BOTTONE);
  digitalWrite(LEDROSSO, bottone);

  //millis() legge il tempo del sistema
  //questa if viene eseguita
  int tempoAttuale = millis();
  if (tempoAttuale - tempoPassato >= DELAY)
  {
    //eseguo il codice che avrei dovuto fare senza delay
    digitalWrite(LEDVERDE, !digitalRead(LEDVERDE));
    tempoPassato = millis();    
  }


}

Vantaggi della Programmazione Non Bloccante

Migliore efficienza: Il microcontrollore può eseguire altre istruzioni mentre attende il tempo impostato.
Maggiore reattività: Puoi gestire più eventi contemporaneamente.
Codice più scalabile: Perfetto per progetti complessi come la comunicazione con sensori o il controllo di più dispositivi.

Conclusione

Se vuoi migliorare le prestazioni di Arduino e rendere il tuo codice più efficiente, la programmazione non bloccante con millis() è una tecnica essenziale. Evitare delay() permette di sviluppare progetti avanzati, ottimizzando il tempo di esecuzione del microcontrollore.

Ultima modifica 31 Marzo 2025

L'articolo Operazioni non bloccanti con Arduino proviene da alfredocentinaro.it.

]]>
Esempio XML con XSD: Ricetta torta al cioccolato spiegata passo-passo https://www.alfredocentinaro.it/lezioni/xml/esempio-xml-xsd-ricetta-torta/ Wed, 12 Feb 2025 12:27:30 +0000 https://www.alfredocentinaro.it/?p=8281 Voglio rappresentare una ricetta di una torta in formato XML. Una ricetta può essere descritta come una serie di informazioni strutturate che includono il nome del piatto, gli ingredienti, il procedimento e il tempo necessario per la preparazione e la cottura. La specifica progettuale del file XML Una ricetta è quindi caratterizzata da: Per semplicità, ... Leggi tutto

L'articolo Esempio XML con XSD: Ricetta torta al cioccolato spiegata passo-passo proviene da alfredocentinaro.it.

]]>
Voglio rappresentare una ricetta di una torta in formato XML. Una ricetta può essere descritta come una serie di informazioni strutturate che includono il nome del piatto, gli ingredienti, il procedimento e il tempo necessario per la preparazione e la cottura.

La specifica progettuale del file XML

Una ricetta è quindi caratterizzata da:

  • Un titolo, che indica il nome del dolce.
  • Un autore, che specifica chi ha scritto la ricetta.
  • Un insieme di ingredienti, ognuno dei quali ha un nome e una quantità associata a un’unità di misura (ad esempio grammi, pezzi, millilitri).
  • Una procedura, ovvero una sequenza di passaggi da seguire per preparare la torta.
  • Due informazioni numeriche che rappresentano il tempo di preparazione e il tempo di cottura, entrambi espressi in minuti.

Per semplicità, consideriamo la ricetta della Torta al Cioccolato, scritta dall’autore Gabriele D’Annunzio. La torta richiede i seguenti ingredienti:

  • 250 grammi di farina
  • 200 grammi di zucchero
  • 3 uova
  • 100 grammi di burro
  • 50 grammi di cacao

Il procedimento prevede quattro passaggi principali:

  1. Mescolare farina, zucchero e cacao in una ciotola.
  2. Aggiungere le uova e il burro fuso.
  3. Mescolare fino a ottenere un impasto omogeneo.
  4. Versare in una teglia imburrata e cuocere a 180°C per 30 minuti.

Il tempo di preparazione stimato è di 15 minuti, mentre la cottura richiede 30 minuti.

Come strutturare un file XML per le ricette

Leggendo con attenzione la specifica e basandoci un po’ sull’esperienza anche pratica, possiamo definire un file XML come il seguente. La cosa più complessa per l’alunno da afferrare è l’elemento che si ripete: nella maggior parte dei casi se ho più elementi che possono ripetersi, occorre inserirli in un elemento contenitore del tipo :

<contenitore>
      <contenuto>1</contenuto> 
      <contenuto>2</contenuto>
      <contenuto>3</contenuto>
</contenitore>

Questa dinamica qui la troviamo per gli ingredienti e i passi delle procedure

<?xml version="1.0" encoding="UTF-8"?>
<ricetta>
	<titolo>Torta al Cioccolato</titolo>
	<autore>Giuseppe Garibaldi</autore>
	<ingredienti>
    	<ingrediente>
        	<nome>Farina</nome>
        	<quantita>250</quantita>
			<unita>grammi</unita>
    	</ingrediente>
    	<ingrediente>
        	<nome>Zucchero</nome>
        	<quantita>200</quantita>
			<unita>grammi</unita>
    	</ingrediente>
    	<ingrediente>
        	<nome>Uova</nome>
        	<quantita>3</quantita>
			<unita>pz</unita>
    	</ingrediente>
    	<ingrediente>
        	<nome>Burro</nome>
        	<quantita>100</quantita>
			<unita>grammi</unita>
    	</ingrediente>
    	<ingrediente>
        	<nome>Cacao</nome>
        	<quantita>50</quantita>
			<unita>grammi</unita>
    	</ingrediente>
	</ingredienti>
	<procedura>
    	<passo>Mescolare farina, zucchero e cacao in una ciotola.</passo>
    	<passo>Aggiungere le uova e il burro fuso.</passo>
    	<passo>Mescolare fino a ottenere un impasto omogeneo.</passo>
    	<passo>Versare in una teglia imburrata e cuocere a 180°C per 30 minuti.</passo>
	</procedura>
	<tempoPreparazione>15</tempoPreparazione>
	<tempoCottura>30</tempoCottura>
</ricetta>

Come creare un file XSD per la validazione XML

Per il nostro file XSD non ci sono particolari criticità .La cosa complessa potrebbe essere comprendere la necessità di un contenitore ingredienti che diventa gioco forza un tipo complesso e sequenza di tre elementi nome, quantità e unità. Quantità ed unità potrebbero anche essere fusi in un solo elemento quantità con attributo unità/tipo unità. Analogamente il tipo complesso per il contenitore procedura che include più elementi passo.

Il codice XSD che valida questo XML potrebbe essere il seguente:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
	<xs:element name="ricetta">
    	<xs:complexType>
        	<xs:sequence>
            	<xs:element name="titolo" type="xs:string"/>
            	<xs:element name="autore" type="xs:string"/>
            	<xs:element name="ingredienti">
                	<xs:complexType>
                    	<xs:sequence>
                        	<xs:element name="ingrediente" maxOccurs="unbounded">
                            	<xs:complexType>
                                	<xs:sequence>
                                    	     <xs:element name="nome" type="xs:string"/>
                                    	     <xs:element name="quantita" type="xs:integer" />
					     <xs:element name="unita" type="xs:string" />
                                	</xs:sequence>
                            	</xs:complexType>
                        	</xs:element>
                    	</xs:sequence>
                	</xs:complexType>
            	</xs:element>
            	<xs:element name="procedura">
                	<xs:complexType>
                    	<xs:sequence>
                        	<xs:element name="passo" type="xs:string" maxOccurs="unbounded"/>
                    	</xs:sequence>
                	</xs:complexType>
            	</xs:element>
            	<xs:element name="tempoPreparazione" type="xs:integer"/>
            	<xs:element name="tempoCottura" type="xs:integer"/>
        	</xs:sequence>
    	</xs:complexType>
	</xs:element>

</xs:schema>

Conclusioni e risorse utili

L’esercizio può essere svolto in diversi modi, probabilmente con sfumature tutte esatte. L’alunno può cimentarsi nel complicare ulteriormente il file XSD o nello strutturare un file XML con più ricette tutte sempre validate.

Ultima modifica 19 Marzo 2025

L'articolo Esempio XML con XSD: Ricetta torta al cioccolato spiegata passo-passo proviene da alfredocentinaro.it.

]]>
CRUD in PHP con fetch API e JavaScript https://www.alfredocentinaro.it/lezioni/php/crud-in-php-con-fetch-api-e-javascript/ Sun, 30 Mar 2025 14:46:36 +0000 https://www.alfredocentinaro.it/?p=8270 Una mini tutorial con operazioni CRUD realizzate in PHP e la funzione Fetch API in JavaScript. La tecnologia dei microservizi, che abbiamo già commentato qui, fonda la sua funzionalità sulla possibilità di reperire o inviare dati in modo asincrono al server di interesse. Occorrono quindi dei punti di accesso a cui far collegare il JavaScript ... Leggi tutto

L'articolo CRUD in PHP con fetch API e JavaScript proviene da alfredocentinaro.it.

]]>
Una mini tutorial con operazioni CRUD realizzate in PHP e la funzione Fetch API in JavaScript.

La tecnologia dei microservizi, che abbiamo già commentato qui, fonda la sua funzionalità sulla possibilità di reperire o inviare dati in modo asincrono al server di interesse. Occorrono quindi dei punti di accesso a cui far collegare il JavaScript per effettuare le operazioni. Questi accessi sono le API, interfacce invocabili attraverso tecnologie come Fech o Ajax che consentono le operazioni CRUD necessarie all’evoluzione di un software web.

In questo articolo vogliamo realizzare e commentare un’interfaccia API JS/PHP che implementa le operazioni CRUD di creazione, lettura, aggiornamento e cancellazione di un record da tabella di articoli. L’esempio è semplice e didattico, ovviamente. Per dare un pizzico di interfaccia gradevole è stato usato comunque Bootstrap e un CSS minimale. L’alunno che volesse riproporre in una verifia o anche ad uno scritto di esame di stato, può tranquillamente bypassare la grafica e limitarsi ai tag html essenziali corredati degli id necessari al funzionamento del codice js.

Esempio di pagina

Il database

Cominciamo con preparare il nostro database con una tabella dove agire con le CRUD. Lo realizziamo volutamnte semplice per non complicare il codice. Nel nosro caso realizziamo una semplice tabella con 3 campi, di cui id è chiave numerica incrmentale. Aggiungiamo qualche dato di esempio.

CREATE TABLE `articoli` (
  `id` int NOT NULL,
  `nome` varchar(60) NOT NULL,
  `descrizione` varchar(120) DEFAULT NULL
) ENGINE=InnoDB 

--
-- Dump dei dati per la tabella `articoli`
--

INSERT INTO `articoli` (`id`, `nome`, `descrizione`) VALUES
(1, 'Philips Monitor 278E', 'Monitor 27\" curvo'),
(2, 'JoyAccess Tastiera wireless', 'Tastiera wireless con mouse e chiave bluetooth rosa');

La pagina HTML

La pagina HTML di prova contiene in pratica la possibilità di chiamare tutte le funzionalità CRUD in esame. E’ costituita da una tabella che si aggiorna effettuando il read da database, due link/bottoni aggiorna ed elimina, una form minimale per inserire nuovi articoli. Il bottone aggiorna apre una finestra modale. In realtà questa è nascosta nel codice della pagina stessa. Nalla modale si carica i dati del record da aggiornare e consente quindi le operazioni di update. Tutta la grafica è fatta con Bootstrap, la nota libreria e una paletta di colori ispirata da quelli della nota catena di mobili svedese. Il lettore che volesse affrontare l’esercizio per un compito o esame può tralasciare i dettagli di grafica e limitarsi solo ai necessari tag html spartani.

Essendo tutto su una unica pagina non ha pretesa di realismo di un software comune.

<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>API Test</title>
    <link href="css/style.css" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">

</head>
<body>


<div class="container">
  <div class="row">
    <div class="col"></div>
    <div class="col-10">



    <div class="box">    
        <h2>Read</h2>
        <table class="table table-striped">
        <thead>
            <tr>
            <th>ID</th>
            <th>Nome</th>
            <th>Descrizione</th>
            <th>Azioni</th>
            </tr>
        </thead>
        <tbody id="articoli">
        </tbody>
        </table>

    </div>

    <div class="box"> 
        <h2>Create</h2>
        <form class="mb-3 col-sm-4">

            <div class="mb-3">
            <label for="nome" class="form-label">Nome</label>
            <input type="text" class="form-control" id="nome" placeholder="nome articolo">
            </div>
            <div class="mb-3">
            <label for="descrizione" class="form-label">Descrizione</label>
            <textarea class="form-control" id="descrizione" rows="3"></textarea>
            </div>
            <button id="inserisci" type="button" class="btn btn-primary btn-brown">Inserisci</button>
        </form>
    </div>



    <!-- Modal -->
    <div class="modal fade" id="aggiornaModale" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
        <div class="modal-header">
            <h2 class="modal-title fs-5" id="exampleModalLabel">Update</h2>
            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
        </div>
        <div class="modal-body">

        
            <div class="mb-3">
            <label for="nome" class="form-label">ID</label>
            <input type="text" class="form-control" id="aggiornaid" placeholder="nome articolo">
            </div>      
            <div class="mb-3">
            <label for="nome" class="form-label">Nome</label>
            <input type="text" class="form-control" id="aggiornanome" placeholder="nome articolo">
            </div>
            <div class="mb-3">
            <label for="descrizione" class="form-label">Descrizione</label>
            <textarea class="form-control" id="aggiornadescrizione" rows="3"></textarea>
            </div>
            


        </div>
        <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Chiudi</button>
            <button type="button" class="btn btn-primary btn-brown" id="bottoneAggiorna">Aggiorna</button>
        </div>
        </div>
    </div>
    </div>
        </div>
        <div class="col"></div>
    </div>
    </div>


<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<script src="js/script.js"></script>
</body>
</html>

Per completezza, condivido anche il codice del foglio di stile

:root{
    --colore1: #f8d115;
    --colore2: #fbe444;
    --colore3: #04348c;
    --colore4: #978466;
    --colore5: #96aac1;
    --colore6: #043794; 
}

body
{
    background-color: var(--colore5);
}

.box
{
    background-color: var(--colore2);
    border: 1px solid var(--colore3);
    border-radius: 1rem;
    color:  var(--colore6);
    padding: 1rem;
    margin-top: 1rem;
}



#articoli.th, td {
    padding: 8px;
    text-align: left;
}

.btn-brown{
    background-color: var(--colore4) !important;
}

.btn-brown:hover, .btn-brown:active{
    background-color: var(--colore5) !important;    
}

Lo script JavaScript

Per utilizzare il codice va sicuramente visto l’url dove è depositato il file api.php. Nel nostro esempio agiamo in localhost su una macchina offline sostanzialmente. Per comodità abbiamo una struttura di cartelle api e magazzino annidate.


const apiUrl = "http://localhost/api/magazzino/api.php";

function _create() {
    const nome = document.querySelector("#nome").value;
    const descrizione = document.querySelector("#descrizione").value;

    const parametri = new URLSearchParams({ nome, descrizione });
    const opzioni = { method: "POST", body: parametri };

    fetch(apiUrl, opzioni)
        .then(response => response.json())
        .then(() => _read())
        .catch(error => console.error("Errore inserimento:", error));
}

function _read() 
{
    fetch(apiUrl)
        .then(response => response.json())
        .then(data => {
            //console.log(data);
            const articoli = document.querySelector("#articoli");
            articoli.innerHTML ='';
            
            data.forEach(articolo => {
                //console.log(articolo);
                const tr = document.createElement("tr");
                
                const td_nome = document.createElement("td");
                td_nome.textContent = articolo.nome;
                tr.appendChild(td_nome);

                const td_id = document.createElement("td");
                td_id.textContent = articolo.id;

                const td_descrizione = document.createElement("td");
                td_descrizione.textContent = articolo.descrizione;

                const td_azione = document.createElement("td");
              
                const bottone_update = document.createElement("a");
                bottone_update.href = "#"
                bottone_update.textContent = "Aggiorna";
                bottone_update.setAttribute("data-bs-toggle","modal");
                bottone_update.setAttribute("data-bs-target", "#aggiornaModale");
                bottone_update.setAttribute("data-id", articolo.id);
                bottone_update.classList.add("aggiorna");
                bottone_update.addEventListener("click", fillUpdate);

                const bottone_delete = document.createElement("a");
                bottone_delete.href = "#";
                bottone_delete.textContent = "Elimina";
                bottone_delete.setAttribute("data-id", articolo.id);
                bottone_delete.addEventListener("click", _delete);

                td_azione.appendChild(bottone_update);
                td_azione.appendChild(document.createTextNode(" | "));
                td_azione.appendChild(bottone_delete); 
                
                tr.appendChild(td_id);
                tr.appendChild(td_nome);
                tr.appendChild(td_descrizione);
                tr.appendChild(td_azione);

                articoli.appendChild(tr);

            }); 
        })
        .catch(error => console.error("Errore lettura:", error));
}

function _readById(id) 
{
    const parametri = new URLSearchParams({ id });
    
    //Il GET non consente un body con i parametri che vanno passati direttamente nell'URL
    const urlParametrico = apiUrl + "?" + parametri.toString(); 
    return fetch(urlParametrico)
        .then(response => response.json())
        .catch(error => console.error("Errore lettura:", error));
}

function fillUpdate(event) {
    const dataId = event.target.getAttribute("data-id");

    _readById(dataId) //il fetch torna direttamente dalla funzione readById
        .then(data => {
            document.querySelector("#aggiornaid").value = data[0].id;
            document.querySelector("#aggiornanome").value = data[0].nome;
            document.querySelector("#aggiornadescrizione").value = data[0].descrizione;
        })
        .catch(error => console.error("Errore nel caricamento dati per aggiornamento:", error));
}

function _update() {
    const id = document.querySelector("#aggiornaid").value;
    const nome = document.querySelector("#aggiornanome").value;
    const descrizione = document.querySelector("#aggiornadescrizione").value;

    const parametri = new URLSearchParams({ id, nome, descrizione });
    const opzioni = { method: "PUT", body: parametri };

    fetch(apiUrl, opzioni)
        .then(response => response.json())
        .then(() => _read())
        .catch(error => console.error("Errore aggiornamento:", error));
}

function _delete(event) {
    const id = event.target.getAttribute("data-id");
    if (confirm("Sei sicuro di voler eliminare questo articolo?")) 
    {
        const parametri = new URLSearchParams({ id });
        const opzioni = { method: "DELETE", body: parametri };

        fetch(apiUrl, opzioni)
            .then(response => response.json())
            .then(() => _read())
            .catch(error => console.error("Errore eliminazione:", error));
    }
}

// Inizializza
document.addEventListener("DOMContentLoaded", _read);
document.querySelector("#inserisci").addEventListener("click", _create);
document.querySelector("#bottoneAggiorna").addEventListener("click", _update);

Il backend API in PHP

Le API possono essere create in due modi. Nel primo creo un file che fa da routing sul metodo primitivo GET / POST / DELETE / PUT, scelta più sicura e flessibile. Oppure mi affido a chiamate e script specifici per ognuna delle funzionalità da gestire col database. A dire la verità molti programmatori spesso preferiscono questo secondo metodo perché più rapido e pigro. Infatti si gestisce tutto con passaggi di parametri o GET o POST al massimo. Il primo però è più completo, sicuro e professionale, quindi lo andremo a sviluppare con attenzione.

La prima cosa da curare e comprendere è: tutto il codice ragione e restituisce JSON, elemento imprescindibile per creare un frontend in Javascript. Quindi va bene messa in alto la dicituradel content-type.

Abbiamo poi bisogno di interagire con un database, qui il nostro buon MySQL. Nulla vieta di creare uno script separato che si occupa della connessione al db. Qui abbiamo inserito i suoi soliti parametri di autenticazione esternamente nel file config.db.php.

<?php

$host = "localhost";
$database = "test";
$user = "root";
$password = "mysqlroot";

Il resto del file in raltà è oltre che semplice, sempre assolutamente uguale, qualunque siano i dati o il database con cui interagire. Si tratta intercettare la chiamata fatta dal file Javascript e capire quale primitiva stia lanciando. Con un semplice if quindi andiamo a fare la require del funzione api associata.

Occhio: GET e POST per lettura ed inserimento godono di variabili superglobali $_GET e $_POST che possiamo usare in modo agevole nei nostri script php. Per intercettare invece i parametri inviati con le primitive PUT e DELETE devo necessariamente intercettarle nel flusso dati php://input.

<?php
header("Content-type: application/json");
require "config.db.php";

$connessione = mysqli_connect($host, 
                                $user, 
                                $password, 
                                $database);


if ($_SERVER["REQUEST_METHOD"] === 'GET')
{
    require "articoli/read.php";
}
   

if ($_SERVER["REQUEST_METHOD"] === 'POST')
{
    require "articoli/create.php";
}


if ($_SERVER["REQUEST_METHOD"] === 'PUT')
{
    parse_str(file_get_contents("php://input"), $_PUT);    
    require "articoli/update.php";
}


if ($_SERVER["REQUEST_METHOD"] === 'DELETE')
{
    parse_str(file_get_contents("php://input"), $_DELETE);    
    require "articoli/delete.php";
}

mysqli_close($connessione);
//echo json_encode(["errore" => "Errore"]);

Non ci resta che gestire ora le singole primitive con il codice che devono eseguire sul database e la restituzione del JSON o con i dati o con un messaggio di risposta per il debug. Per comodità li ho raccolti in una cartella. Diciamo sin da subito al lettore: il paradosso è che quesat struttura modulare dei file/script facilità enormente il lavoro di sviluppo. Infatti possiamo cambiare tabella/database e fondamentalemente le operazioni da fare saranno sempre identiche,basta solo cambiare le query nei prossimi file per adeguare il codice di backend.

<?php

$risultato = '';

if (isset($_POST['nome']) && isset($_POST['descrizione']))
{
    $nome =  $_POST['nome'];
    $descrizione =  $_POST['descrizione'];

    $sql = "INSERT INTO articoli (nome, descrizione) VALUES ('$nome', '$descrizione')";
    $risultato = mysqli_query($connessione, $sql);    
}


if($risultato)
{
    echo json_encode(["message" => "Articolo creato con successo"]);
} 
else 
{
    echo json_encode(["message" => "Errore creazione articolo"]);
}
exit();

<?php

if (isset($_REQUEST["id"]))
{
    $id = $_REQUEST["id"];
    $sql = "SELECT * FROM articoli WHERE id = $id";
}
else
{
    $sql = "SELECT * FROM articoli";  
}

$risultato = mysqli_query($connessione, $sql) or die("Query fallita");

echo json_encode(mysqli_fetch_all($risultato,MYSQLI_ASSOC)); 
exit();

<?php

if (isset($_PUT['id']))
{
    $id = $_PUT['id'];
    $nome = $_PUT['nome'];
    $descrizione = $_PUT['descrizione'];

    $sql = "UPDATE articoli SET nome = '$nome', descrizione = '$descrizione' WHERE id = $id";
    $risultato = mysqli_query($connessione, $sql);  
}

if($risultato)
{
    echo json_encode(["message" => "Articolo $id aggiornato con successo"]);
} 
else 
{
    echo json_encode(["message" => "Errore aggiornamento articolo"]);
}
exit();

<?php

$risultato = '';
$id ='';

if (isset($_DELETE['id']))
{
    $id =  $_DELETE['id'];

    $sql = "DELETE FROM articoli WHERE id=$id";
    $risultato = mysqli_query($connessione, $sql);    
}


if($risultato)
{
    echo json_encode(["message" => "Articolo rimosso con successo"]);
} 
else 
{
    echo json_encode(["message" => "Errore rimozione articolo n. $id"]);
}
exit();

Conclusioni

Siamo arrivati alla fine del nostro esercizio. E’ importante ricordare che, se impostato con questa struttura, gli esercizi per creare delle API sono praticamente sempre gli stessi identici a livello di difficoltà. Possiamo cambiare contesto dei dati, tabelle, database, creare delle query e quindi delle primitive più specifiche a seconda dei parametri che vengono passati. L’impostazione è sempre la stessa. Visto un esercizio bene, li abbiamo visti tutti!

Ultima modifica 30 Marzo 2025

L'articolo CRUD in PHP con fetch API e JavaScript proviene da alfredocentinaro.it.

]]>
Architetture per il web: monolitico vs microservizi https://www.alfredocentinaro.it/lezioni/sistemi-reti/architetture-per-il-web-monolitico-vs-microservizi/ Tue, 21 Jan 2025 21:53:52 +0000 https://www.alfredocentinaro.it/?p=8216 Il panorama dello sviluppo software si è evoluto rapidamente negli ultimi decenni, spinto dalla crescente domanda di applicazioni web sempre più performanti, scalabili e manutenibili. Per gli studenti di un istituto tecnico tecnologico, indirizzo informatica, è fondamentale comprendere le diverse architetture software e come queste influenzano la progettazione, lo sviluppo e la manutenzione di applicazioni ... Leggi tutto

L'articolo Architetture per il web: monolitico vs microservizi proviene da alfredocentinaro.it.

]]>
Il panorama dello sviluppo software si è evoluto rapidamente negli ultimi decenni, spinto dalla crescente domanda di applicazioni web sempre più performanti, scalabili e manutenibili. Per gli studenti di un istituto tecnico tecnologico, indirizzo informatica, è fondamentale comprendere le diverse architetture software e come queste influenzano la progettazione, lo sviluppo e la manutenzione di applicazioni web.

In questo articolo, esploreremo le principali architetture software per il web, concentrandoci in particolare sulla distinzione tra applicazioni monolitiche e a microservizi. Verranno forniti cenni su altre architetture e saranno inclusi esempi concreti per aiutare a visualizzare le differenze e comprendere l’applicazione pratica di ciascun approccio.

Il panorama dello sviluppo software si è evoluto rapidamente negli ultimi decenni, spinto dalla crescente domanda di applicazioni web sempre più performanti, scalabili e manutenibili. Per gli studenti di un istituto tecnico tecnologico, indirizzo informatica, è fondamentale comprendere le diverse architetture software e come queste influenzano la progettazione, lo sviluppo e la manutenzione di applicazioni web.

In questo articolo, esploreremo le principali architetture software per il web, concentrandoci in particolare sulla distinzione tra applicazioni monolitiche e a microservizi. Verranno forniti cenni su altre architetture e saranno inclusi esempi concreti per aiutare a visualizzare le differenze e comprendere l’applicazione pratica di ciascun approccio.

Le Architetture Software

Un’architettura software rappresenta la struttura organizzativa di un sistema software. Definisce il modo in cui i componenti del software sono progettati, interconnessi e comunicano tra loro. Le scelte architetturali hanno un impatto diretto su alcuni fattori chiave come:

  • Scalabilità: la capacità di un sistema di gestire un aumento del carico.
  • Manutenibilità: la facilità con cui un sistema può essere aggiornato e migliorato.
  • Prestazioni: la velocità con cui il sistema risponde alle richieste.
  • Affidabilità: la capacità del sistema di funzionare correttamente anche in caso di guasti parziali

A seconda dell’obiettivo e destinazione d’uso del software alcune di queste caratteristiche potrebbero essere più desiderabili delle altre o richiedere accortezze progettuali e di sviluppo diverse.

Architettura Monolitica

Un’applicazione monolitica è progettata come un’unica unità. Tutti i componenti, come il frontend, il backend e il database, sono integrati in un unico programma eseguibile. Non è detto che venga utilizzato il modello MVC per distinguere i tre ambiti. E’ la classica architettura che utilizziamo negli esempi scolastici, dove competenze di basso livello, tempo lezione di piccola entità e sporadico non consentono esempi strutturati e realistici.

Caratteristiche principali

  • Unità indivisibile: Tutte le funzionalità dell’applicazione sono contenute in un unico codice sorgente e vengono distribuite insieme.
  • Deploy unificato: Ogni modifica richiede la ricompilazione e il ridistribuzione dell’intero sistema.
  • Dipendenze condivise: I componenti condividono le stesse librerie e risorse
  • Aggiunta di Plugin: seppur la struttura sia unica, spesso può essere arricchita di elementi di software/codice esterno che finisce per integrarsi comunque con il monolita agli occhi dell’utente finale

Vantaggi

  • Facilità di sviluppo iniziale: Perfetta per piccoli progetti con team ridotti.
  • Debug centralizzato: Gli strumenti di debugging possono facilmente analizzare l’intero sistema.
  • Performance: L’assenza di comunicazione tra servizi riduce la latenza.
  • Framework diffusi e maturi: pensiamo a Laravel, DJango, sono framework (leggi qui Lo sviluppo MVC e i framework per il web) che nascono monoliti, la cui diffusione è molto estesa, con numerosi plugin, assistenza in forum, video per la formazione ecc. Un altro esempio classico è un sistema di gestione di contenuti (CMS) sviluppato in PHP come WordPress, Drupal, Joomla. Tutte le funzionalità, inclusa la gestione degli utenti, l’editor di contenuti e la gestione dei temi, sono integrate in un unico codice sorgente.

Svantaggi

  • Difficoltà di scalabilità: Non è semplice scalare componenti specifici senza duplicare l’intero sistema. Non a caso esistono sistemi di versioning del codice per evitare errori grossolani.
  • Manutenzione complessa: Modifiche a un componente possono influenzare l’intera applicazione, anche in modo imprevisto se si modifica il core.
  • Tempo di deploy lungo: Ogni aggiornamento richiede una nuova distribuzione completa di tutto il pacco software.

Architettura a Microservizi

L’architettura a microservizi scompone un’applicazione in piccoli servizi indipendenti che comunicano tra loro attraverso API. All’utente finale la frammentazione è invisibile. Può capitare che un microservizio a sua volta ricorra ad altri microsrvizi agendo da “broker”, cioè uno strato che raccoglie il risultato di più microservizi. I microservizi possono essere sviluppati in-house col software complessivo ma anche elementi terzi non direttamente sviluppati in azienda ma solo utilizzati.

Caratteristiche principali

  • Servizi autonomi: Ogni servizio è responsabile di una singola funzionalità e può essere sviluppato e distribuito indipendentemente.
  • Comunicazione tramite API: I servizi interagiscono utilizzando protocolli semplici ma efficaci come REST o gRPC.
  • Tecnologie diverse: I servizi possono essere scritti in linguaggi di programmazione diversi e utilizzare database differenti, framework differenti o essere scritti da zero.

Vantaggi

  • Scalabilità: È possibile scalare solo i servizi che necessitano di maggiori risorse.
  • Manutenzione facilitata: I piccoli team possono lavorare su singoli servizi senza interferire con altri.
  • Resilienza: Guasti in un servizio non compromettono necessariamente l’intero sistema. Alcuni microservizi potrebbero essere ridondati per avere sia un carico di lavoro bilanciato sia una continuità di servizio.
  • Non vincolato ad una sola tecnologia: ci sono alcune tipologie o moli di dati che potrebbe essere conveniente con alcuni db piuttosto che altri, come alcuni linguaggi nel lato backend abbiano caratteristiche spiccate per la sicurezza o le prestazioni o la sola semplicità di sviluppo.

Svantaggi

  • Maggiore complessità: La gestione di numerosi servizi richiede strumenti avanzati per orchestrazione e monitoraggio.
  • Overhead di comunicazione: Le interazioni tra servizi possono introdurre latenza.
  • Costi infrastrutturali: È necessaria un’infrastruttura più complessa per gestire i servizi, che implica come questa soluzione sia legata alle risorse di grandi aziende.

Esempio pratico

Un esempio è Netflix, che utilizza un’architettura a microservizi per gestire funzionalità come la raccomandazione di contenuti, lo streaming video e la gestione degli account. Ogni servizio è indipendente e può essere aggiornato senza interrompere gli altri.

Monolitico vs Microservizi: Confronto Diretto

CaratteristicaMonoliticoMicroservizi
Dimensione del teamPiccoli teamTeam dedicati per servizio
ScalabilitàLimitataElevata e mirata
Velocità di sviluppoRapida all’inizioPiù lenta, ma migliora nel tempo
Costi inizialiBassiAlti
ResilienzaLimitataAlta

Altre Architetture Software

Oltre alle architetture monolitiche e a microservizi, esistono altre tipologie di architettura utilizzate nello sviluppo web. Vediamole brevemente per completezza.

Architettura a Strati (Layered Architecture)

Questa è una delle architetture più comuni, organizzata in livelli è già ampiamente trattata nei nostri articoli. Un esempio è il classico modello MVC (Model-View-Controller) dove individuiamo i tre momenti:

  • Presentazione: Interfaccia utente.
  • Logica di business: Gestione delle regole aziendali.
  • Accesso ai dati: Interazione con il database.

Architettura Event-Driven

Sicuramente meno intuitiva. E’ utilizzata per applicazioni che reagiscono a eventi. Gli eventi vengono inviati da produttori e processati da consumatori. Questa architettura è comune nei sistemi IoT.

Architettura Serverless

In questo modello, il codice viene eseguito in risposta a eventi e i server sono completamente gestiti dal provider cloud. Amazon AWS Lambda è un esempio di piattaforma serverless. Soluzione molto diffusa in ambito business di altissimo profilo.

Conclusioni

La scelta dell’architettura dipende dalle esigenze del progetto. Per applicazioni piccole o medie, un’architettura monolitica è spesso sufficiente e più economica. Tuttavia, per applicazioni complesse o che prevedono una rapida crescita, i microservizi offrono vantaggi significativi in termini di scalabilità e manutenibilità.

Ultima modifica 28 Marzo 2025

L'articolo Architetture per il web: monolitico vs microservizi proviene da alfredocentinaro.it.

]]>
Gara di salti in Python https://www.alfredocentinaro.it/lezioni/python/gara-di-salti-in-python/ Sun, 29 Dec 2024 21:26:24 +0000 https://www.alfredocentinaro.it/?p=8182 Un esercizio dalla complessità più interessante per esplorare ed applicare le nozioni base di Python dall’uso dei file, le strutture dati come liste e dizionari (puoi ripassarli qui), cicli, funzioni, numeri casuali. Vogliamo simulare una gara di salto in lungo. I nomi degli atleti vengono caricati in una lista leggendo un file di testo, in ... Leggi tutto

L'articolo Gara di salti in Python proviene da alfredocentinaro.it.

]]>
Un esercizio dalla complessità più interessante per esplorare ed applicare le nozioni base di Python dall’uso dei file, le strutture dati come liste e dizionari (puoi ripassarli qui), cicli, funzioni, numeri casuali.

Vogliamo simulare una gara di salto in lungo. I nomi degli atleti vengono caricati in una lista leggendo un file di testo, in cui i nomi sono posti uno sotto l’altro. Successivamente il software deve simulare tre salti per ogni atleta lanciando un numero casuale compreso tra 6.00 e 9.99. Il salto può inoltre essere nullo con una probabilità del 50% in stile testa o croce e quindi in questo caso risulta una misura pari a zero. Le misure dei salti devono quindi essere archiviate per ogni atleta in un dizionario opportuno. La seconda fase della gara prevede che a passare siano solo i primi 8 ad aver totalizzato i salti più lunghi tra i tre a disposizione. Degli 8 vincono e vengono stampati i primi 3.

Lettura del file

Come ogni esercizio più complesso che si rispetti, c’è una interazione/caricamento di dati da file. Per abitudine su questo sito web, creiamo sempre una funzione su misura che ci restituisce una lista con i contenuti cercati e caricati. La chiamiamo leggiFile e usiamo il decoratore non obbligatorio ->list per tenere a mente il tipo di ritorno.

Usiamo il costrutto with per evitare le operazioni di try/catch e la necessità di chiudere esplicitamente il file con il comando close().

nomefile = "atleti.txt"



def leggiFile() ->list:

    lista = list()
    with open(nomefile, "r") as file:
        righe = file.readlines()
        for riga in righe:
            lista.append(riga.rstrip("\n")) #rstrip ripulisce andata a capo \n

    return lista

Appendere, ovvero aggiungere i nomi letti ad una lista è semplice ma c’è un dettaglio che spesso rende ardua la vita dello studente alle prime armi. Il file di testo ha un carattere jolly per indicare l’andata a capo che inevitabilmente verrebbe letto assieme al nome. Così lo rimuoviamo con la funzione rstrip in fase di lettura. Esistono anche altri modi per liberarsi del carattere \n.

La funzione per simulare il salto

Prepariamo una funzione per simulare il salto del singolo atleta. Anche qui uso il decoratore ->float Abbiamo bisogno di utilizzare il modulo random da cui possiamo importare solo le funzioni randint e uniform: la prima stacca numeri interi, la seconda numeri decimali in un determinato intervallo assegnato.

Per trovare se il salto è nullo o valido lanciamo una sorta di testa/croce con due numeri 0 e 1. Se esce zero assumo che il salto sia nullo, se 1 vado a trovare la misura del salto. Per il salto lanciamo un secondo numero casuale questa volta decimale invece che intero. Una tragedia del python è gestire i numeri decimali che vengono presentati con molte cifre. A noi occorrono spesso solo le prime due, così effettuiamo un preventivo arrotondamento con round.

from random import uniform, randint

def simulaSalto() -> float:
    
    valido = randint(0,1)
     
    #se esce 0 salto nullo
    if valido == 0:
        return 0
    
    #esce 1, scelgo un numero tra 6 e 9
    salto = round(uniform(6.00, 9.99),2)
    return salto

Il corpo principale

Siamo pronti per creare il nostro programma principale che consigli sempre di differenziare con il if __name__ == “__main__”: per tenere ben separati funzioni, variabili globali, import vari dal corpo centrale del programma.

Qui dobbiamo procedere a chiamare la funzione di caricamento del file. Quindi procediamo a far saltare tre volte ogni atleta della lista e salviamo tutto in un dizionario. Qui il vantaggio di usare python forse è più visibile se usiamo la proprietà di assegnazione di una intera lista con tanto di valori random richiamati in fase di inizializzazione della stessa. Ci sono modi anche più semplici e tradizionali ovviamente per fare la stessa operazione. Stampiamo il risultato finalmente.

    gara = dict()
    for atleta in listaatleti:
        gara[atleta]= [simulaSalto(), simulaSalto(), simulaSalto()] 

Alla seconda fase però partecipano soltanto i primi 8, tra quelli che hanno saltato ovviamente più lontano in modo assoluto e su salto singolo. Ora sta nel trovare per ogni atleta il suo salto massimo tra i tre che ha effettuato. Conviene usare una nuova struttura dizionario dove salvare come chiave ancora il nome e il valore del salto massimo. Ennesimo vantaggio di python è che fare il max di un vettore lista di 3 valori è semplicissimo con la chiamata max() con il vettore associato all’indice del nome atleta come parametro.

    massimi = dict()
    for atleta in gara:
        massimi[atleta]=max(gara[atleta])

Per controllo possiamo stampare quindi il massimo di ogni atleta appena trovato.

Siamo alla parte più complessa dell’esercizio: trovare i primi 8 atleti ad aver saltato più lontano tra i massimi appena individuati. Non c’è una sola soluzione ovviamente ma ne proponiamo una dove scorriamo tutti i valori possibili dei salti da 9,99 a 0,00 spostandoci di decimale in decimale con passo 0,01. Ad ogni possibile misura controlliamo se gli atleti hanno tra i loro salti massimi la misura in considerazione. Se ci sono atleti corrispondenti li inserisco in un nuovo dizionario nome/misura salto e mi decremento un contatore di 8 posi disponibili. Quando ho analizzato tutte le possibile misure o ho già piazzato 8 atleti, esco dal ciclo. Avremo gli 8 atleti con i salti tra l’altro ordinati!

#scorro tutte le misure possibile da 9.99 a 0.00
    #controllo se ci sono atleti corrispondenti a quella misura/salto
    #se trovo un atleta lo inserisco in nuovo dizionario
    #scalo 1 posto disponibile degli 8 ammessi
    secondafase = dict()
    numeroatleti = 8
    misura = 9.99
    while misura >= 0.00:
        for atleta in massimi:
            if massimi[atleta] == misura:
                secondafase[atleta] =massimi[atleta]
                numeroatleti -= 1
        if numeroatleti == 0:
            break
        #occhio alla sottrazione float che ha sempre cifre decimali sporche
        #9.99 - 0.01 non fa 9.98 
        misura = round(misura - 0.01,2) 

Listato completo

from random import uniform, randint


nomefile = "atleti.txt"


'''
FUNZIONI
'''

def leggiFile() ->list:

    lista = list()
    with open(nomefile, "r") as file:
        righe = file.readlines()
        for riga in righe:
            lista.append(riga.rstrip("\n")) #rstrip ripulisce andata a capo \n

    return lista




def simulaSalto() -> float:
    
    valido = randint(0,1)
     
    #se esce 0 salto nullo
    if valido == 0:
        return 0
    
    #esce 1, scelgo un numero tra 6 e 9
    salto = round(uniform(6.00, 9.99),2)
    return salto



'''
MAIN
'''
if __name__ == "__main__":
    listaatleti = leggiFile()
    
    
    '''
    PRIMA FASE: faccio saltare 3 volte tutti gli atleti e li immagazino in un dizionario gara
    '''
    gara = dict()
    for atleta in listaatleti:
        gara[atleta]= [simulaSalto(), simulaSalto(), simulaSalto()] 

    print("|-----------------------------------------------------------|")
    print("|                     RISULTATO SALTI                       |")
    print("|-----------------------------------------------------------|")
    for atleta in gara:
        print(f" ATLETA: {atleta} - SALTO 1: m{gara[atleta][0]}, SALTO 2: m{gara[atleta][0]}, SALTO 3: m{gara[atleta][0]}")
    
    print("")

    '''
    SECONDA FASE: prendo i primi 8
    '''
    
    #Scorro i risultati di ogni atleta e dei 3 prendo il massimo. 
    #Metto i massimi in un dizionario atleta/salto quindi    
    massimi = dict()
    for atleta in gara:
        massimi[atleta]=max(gara[atleta])

    print("|-----------------------------------------------------------|")
    print("|                  SALTO MAX PER ATLETA                     |")
    print("|-----------------------------------------------------------|")        
    for atleta in massimi:
        print(f" ATLETA: {atleta} - SALTO MAX: m{massimi[atleta]}")
    print("")

    #scorro tutte le misure possibile da 9.99 a 0.00
    #controllo se ci sono atleti corrispondenti a quella misura/salto
    #se trovo un atleta lo inserisco in nuovo dizionario
    #scalo 1 posto disponibile degli 8 ammessi
    secondafase = dict()
    numeroatleti = 8
    misura = 9.99
    while misura >= 0.00:
        for atleta in massimi:
            if massimi[atleta] == misura:
                secondafase[atleta] =massimi[atleta]
                numeroatleti -= 1
        if numeroatleti == 0:
            break
        #occhio alla sottrazione float che ha sempre cifre decimali sporche
        #9.99 - 0.01 non fa 9.98 
        misura = round(misura - 0.01,2) 

    print("|-----------------------------------------------------------|")
    print("|                   PODIO ATLETI SALTO                      |")
    print("|-----------------------------------------------------------|")        
    for indice in range(0,3):
        atleta, salto = list(secondafase.items())[indice]
        print(f" ATLETA: {atleta}, m{salto}")
    
    pass

Peri l file abbiamo semplicemente creato il file txt ed inserito nomi di personaggi di topolino, uno sotto l’altro.

Ultima modifica 29 Dicembre 2024

L'articolo Gara di salti in Python proviene da alfredocentinaro.it.

]]>
Esempio XML con XSD: alunno https://www.alfredocentinaro.it/lezioni/xml/esempio-xml-con-xsd-alunno/ Thu, 12 Dec 2024 18:22:23 +0000 https://www.alfredocentinaro.it/?p=8161 Un esempio molto semplice introduttivo per comprendere l’uso di XML, dello XML Schema, il suo file XSD e delle regole quindi di validazione. Vogliamo provare ad inviare i dati relativi ad un alunno con banali informazioni anagrafiche. Un esempio potrebbe essere il seguente quindi. Trovi una guida di come creare un file XML anche qui ... Leggi tutto

L'articolo Esempio XML con XSD: alunno proviene da alfredocentinaro.it.

]]>
Un esempio molto semplice introduttivo per comprendere l’uso di XML, dello XML Schema, il suo file XSD e delle regole quindi di validazione. Vogliamo provare ad inviare i dati relativi ad un alunno con banali informazioni anagrafiche. Un esempio potrebbe essere il seguente quindi. Trovi una guida di come creare un file XML anche qui sulle nostre pagine, mentre una valida guida alternativa, asciutta e sintetica sugli Schema XSD la trovate qui.

Esempio foglio dati di esempio XML

Il foglio xml è piuttosto semplice. Prevede solo un annidamento dentro il tag alunno. Ci sono tag che si riferiscono a dati testuali, numero, un carattere, un booleano. Proponiamo un esempio con dati per la successiva validazione.

<alunno>
  <cognome>Centinaro</cognome>
  <cognome>Rossi</cognome>
  <nome>Alfredo</nome>
  <anno>1982</anno>
  <sesso>M</sesso>
  <celibe>true</celibe>
</alunno>

Il file XSD ver.1

Partendo dal file XML, proviamo a costruire un foglio di schema XSD. In molti casi questa operazione potrebbe generare XSD differenti poiché prevede o meno determinate casistiche non immediatamente riscontrabili nel foglio dati. Vediamo una prima versione semplificata che ci porta però a fare qualche considerazione e qualche elemento più stringente.

Tolte le due righe di intestazione classiche che richiamano gli standard XML e XML Schema, il primo elemento è proprio alunno. In particolare poi questo non è un elemento secco singolo ma ha dei sotto elementi, come ci aspettiamo da un nodo radice del resto. Definiamo quindi il tag complexType che si apre e chiude, badate bene. Al suo interno i campi espressi non sono liberi ma ricadono in una sequenza ordinata che si aspetterà il parser che riceve il file dati.

<?xml version="1.0" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> 
<xs:element name="alunno">
    <xs:complexType>
      <xs:sequence>

      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Le restanti voci sono elementi semplici, quindi possono essere classificati nello schema semplicemente con la voce singola element che possiamo chiude o con il tag </element> o col il semplice slash finale. Il file completo potrebbe quindi essere quello sotto riportato.

<?xml version="1.0" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="alunno">
    <xs:complexType>
      <xs:sequence>
    	<xs:element name="cognome" type="xs:string" />
      	<xs:element name="nome" type="xs:string" />
      	<xs:element name="anno" type="xs:positiveInteger" />
        <xs:element name="sesso" type="xs:string" />
      	<xs:element name="celibe" type="xs:boolean" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>
 </xs:schema>

Il file XSD ver.2

Se proviamo il nostro XML e XSD in un validatore online o notepad o visual studio code, il file sicuramente passa ma vogliamo renderlo un po’ più specifico ed accurato.

Ad esempio potrebbe capitare che un alunno abbia più cognomi. In questo caso possiamo utilizzare gli attributi minOccurs e maxOccurs che specificano il numero minimo e massimo, in questo caso, di cognomi che un xml può contenere. Se specifichiamo minimo zero, vuol dire che il tag è opzionale mentre sul limite max possiamo inserire il numero congruo che vogliamo o digitare unbounded che signica che nel xml possono essere riportati infiniti cognomi (qui sarebbe ragionevole mettere 2).

<xs:element name="cognome" type="xs:string" minOccurs="1" maxOccurs="unbounded" />

Anno vogliamo che sia un numero positivo, quindi invece di intero potrebbe essere più ragionevole type=”xs:positiveInteger”.

Sul sesso vogliamo essere certi che il dato che arriva a mezzo di XML sia di una sola lettera. Possiamo allora ridefinire il tag element con uno di apertura/chiusura e specificare al suo interno che è un tipo semplice ma con dei vincoli quindi inseriamo simpleType, restriction specificandoci il tipo e finalmente il tax maxLenght che ha valore 1 per indicar il singolo carattere ammesso.

        <xs:element name="sesso">
          <xs:simpleType>
          	<xs:restriction base="xs:string">
            	<xs:maxLength value="1" />
          	</xs:restriction>
          </xs:simpleType>
        </xs:element>

Quest’ultima parte complica un po’ il nostro schema ma come spesso accade, è una questione di esercitazione, abitudine e sapere dove cercare tra manuali ed esercizi svolti.

Il file XSD completo:

<?xml version="1.0" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="alunno">
    <xs:complexType>
      <xs:sequence>
    	<xs:element name="cognome" type="xs:string" minOccurs="1" maxOccurs="unbounded" />
      	<xs:element name="nome" type="xs:string" />
      	<xs:element name="anno" type="xs:positiveInteger" />
        <xs:element name="sesso">
          <xs:simpleType>
          	<xs:restriction base="xs:string">
            	<xs:maxLength value="1" />
          	</xs:restriction>
          </xs:simpleType>
        </xs:element>
      	<xs:element name="celibe" type="xs:boolean" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>
 </xs:schema>

Ultima modifica 15 Gennaio 2025

L'articolo Esempio XML con XSD: alunno proviene da alfredocentinaro.it.

]]>
La struttura del pc https://www.alfredocentinaro.it/lezioni/sistemi-reti/la-struttura-del-pc/ Wed, 30 Oct 2024 14:50:49 +0000 https://www.alfredocentinaro.it/?p=7910 Ammetto sin da subito come questo articolo sia ambizioso. L’argomento è trattato nei libri del terzo anno degli istituti tecnici e, spesso, in maniera meno approfondita nei manuali del biennio di licei e scuole tecniche e professionali. Questo articolo vuole provare ad integrare e argomentare con alcuni dettagli e curiosità utili non solo per comprendere ... Leggi tutto

L'articolo La struttura del pc proviene da alfredocentinaro.it.

]]>
Ammetto sin da subito come questo articolo sia ambizioso. L’argomento è trattato nei libri del terzo anno degli istituti tecnici e, spesso, in maniera meno approfondita nei manuali del biennio di licei e scuole tecniche e professionali. Questo articolo vuole provare ad integrare e argomentare con alcuni dettagli e curiosità utili non solo per comprendere l’architettura del pc ma anche per poter fare acquisti attenti in pc autoassemblati (qui alcuni alunni alle prese con una manutenzione).

Il case

E’ la scatola che avvolge e protegge il nostro pc. E’ un componente spesso sottovalutato soprattutto in funzione dell’espansione del nostro pc nel caso si vogliano aggiungere schede di espansione o schede video successivamente. Viene anche chiamato cabinet o chassis (leggi in francese sciassì). Solitamente il case ha un formato indicato con una sigla indicativa ma spesso le classificazione è abbastanza relativa e dipendente da diversi fattori.

Mini tower

Di altezza modesta tra i 30 e 45 cm circa. E’ destinato in genere a pc dalle prestazioni contenute e per scopi di ufficio. Sono pericolosi da scegliere perché per una manciata di cm spesso supportano solo determinate schede madri e schede video non troppo grandi. Spesso non hanno spazio per aggiungere schede vide da gamer! Anche lo spazio per hd e ventole potrebbe essere decisamente poco. Occorre essere armati di metro per valutare al cm eventuali schede aggiuntive.

Mid tower

Più alti e larghi dei fratellini mini, di un altezza compresa tra 45 e 60 cm, sono probabilmente i case più diffusi per bilanciamento tra spazio interno ed esterno. In questi case è possibile alloggiare la maggior parte delle schede madri e delle schede video in commercio ma è sempre bene controllare dimensioni al cm. Supportano un nutrito numero di ventole e dissipatori ad alte prestazioni per le cpu, un nutrito numero di alloggiamenti per hard disk. In questo range di case troviamo spesso l’alimentatore in basso in un alloggiamento separato dal resto del pc per non far entrare la polvere. Occhio alla qualità costruttiva delle lamiere e della versatilità nello spostare paratie laterali ed alloggiamenti per poter esplorare e montare i componenti in modo agevole: in questa fascia di prezzo molti produttori risparmiano molto!

Full tower

Sono decisamente i case destinati a professioni o gamer che non vogliono compromessi. Le dimensioni possono superare i 60 cm senza particolari limiti ma permettono di alloggiare tutte le schede video e schede madri in commercio. Partono da cifre spesso già elevate ma non hanno compromessi nella qualità delle lamiere, degli alloggiamenti, dell’ispezionabilità e modularità del case che consentono di assemblare un pc in modo agevole per quanto riguarda la cavetteria e l’aggiunta di ventole o sistemi a raffreddamento, hard disk. I modelli per gamer di questo formato hanno sportelli in acetato trasparente per vedere i componenti, led RGB per effetti scenici.

Cube chassis

E’ un fattore di forma che si sta diffondendo molto negli ultimi anni. Anche detto barebone. Assomiglia per dimensioni ad un minitower ma spesso con la possibilità di sdraiarlo sul tavolo accanto al monitor. Sono pensati per schede madri micro atx, spesso personalizzate proprio per queste build. L’espansione di questi pc è pari a zero se non la sostituzione di un componente. E’ destinato a pc compatti da ufficio o da chi non vuole rinunciare ad avere un monitor con però un case facilmente spostabile e componenti non depotenziati come su un portatile.

SFF (Small Form Factor)

Li citiamo per completezza ma sono gli smartbox da posizionare sotto le tv, spesso con hardware e software simili a quelli di uno smartphone. Sono ovviamente destinate ad applicazioni specifiche di intrattenimento.

Racks e Server Cases

Sono strutture particolari che hanno rotaie ed alloggiamenti di dimensioni standardizzate in altezza e larghezza con le diciture 1U, 2U, 3U ecc per indicare l’ingombro in altezza. Sono pensati per hardware di alto livello professionale, server e dispositivi di rete come switch. Non sono belli da vedere ma sono decisamente funzionali per aggiungere sistemi di ventilazione e anti-incendio.

Come scegliere il case giusto

  • Compatibilità con le componenti: Assicurati che il case possa ospitare scheda madre, scheda grafica, e altre periferiche.
  • Capacità di raffreddamento: Valuta la possibilità di aggiungere ventole o sistemi di raffreddamento a liquido.
  • Gestione dei cavi: Un buon sistema di gestione dei cavi aiuta a mantenere l’interno del case ordinato e migliora il flusso d’aria.
  • Estetica: Se il design è importante per te, cerca case con pannelli in vetro temperato, acetato o illuminazione RGB.

L’alimentatore

Elemento spesso anche questo sottovalutato nell’assemblaggio home made di un pc o comprato già assemblato. L’alimentatore serve a trasformare la corrente della presa di casa in diversi spinotti con correnti e tensioni differenti che possono poi alimentare in modo specifico i singoli componenti, ognuno dei quali richiede uno spinotto e tensione distinti.

Affidabilità

L’alimentatore è un elemento di frontiera quindi tra il mondo esterno al case e quello interno. E’ un componente che scalda parecchio e spesso può essere raffreddato da una ventola. Lavorando con tensione, calore e polvere, nonché assorbendo sbalzi esterni di tensione proveniente dalla rete, la vita di un alimentatore può essere relativamente breve rispetto ai restanti componenti. Spesso i suoi condensatori e trasformatori cessano di lavorare lasciando al buoi il pc, senza possibilità alcuna di avviarlo.

Sconsiglio sempre alimentatori a basso budget o cinesi: la scarsità dei componenti provoca tensioni spesso ambigue e non rispondenti alle specifiche dei componenti. Questo può essere molto pericoloso. Nella migliore dell’ipotesi non si avvi il pc, nel peggiore una sovratensione può danneggiare i componenti interni, soprattutto la scheda video.

Potenza

Il secondo elemento da tenere presente è la potenza erogata, solitamente espressa in WATT. Come scegliere i watt giusti dipende dalla destinazione d’uso del nostro pc. Se usiamo il computer in modalità ufficio, navigazione web, fotoritocco, non avremo bisogno di hardware stellare come non avremo bisogno di alimentatori spinti. Mediamente un pc con configurazione uso ufficio richiede 300 watt, quindi un alimentatore del taglio standard 450w o 500w è già adeguatamente sovradimensionato.

Diversamente un videogiocatore con una cpu energivora ed una scheda video di ultima generazione può consumare già per questi due componenti, rispettivamente 300watt e 1000watt che aggiunti al resto del pc possono richiedere alimentatori anche da 2 kilowatt per reggere il carico. 2 kW per un pc è un bell’impiego di corrente considerando che i contatori dell’energia casalinghi sono tarati ad erogare fino a 3 kW.

Risparmio energetico

Altro dettagli oda considerare è il bollino con scritto 80 Gold, 75 Bronze, 83 Platinum o simili. Queste indicano l’efficienza dell’alimentatore che nella conversione che fa, rigetta nel pc l’80%, il 75% o l’83% della corrente assorbita dalla rete. Direi un bello spreco, visto che quasi il 20% viene sprecato in effetto joule, ovvero calore risultato dalla conversione.

Modulare o no

Gli alimentatori economici hanno un fascio di cavi che esce dall’unità principale. Questi comprendono una dotazione di attacchi diversi tra loro per la maggior parte delle configurazioni. Il problema sta nel fatto che molti di questi cavi potrebbero rimanere inutilizzati e scollegati penzolanti dentro il case rendendo meno omogeneo il flusso dell’aria, oltre che esteticamente poco gradevoli. Ecco perché alcuni alimentatori di alto livello questi cavi li hanno “staccabili/attaccabili” all’occorrenza rendendo il case più pulito. Ovviamente questa modularità si paga, anche un 20-30% in più.

La scheda madre

Il primo elemento collegato al nostro alimentatore e che occupa la totalità o quasi del case è la scheda madre, mother board in inglese, mobo abbreviato tra gli addetti ai lavori. Fondamentalmente è un componente stupido, una larga piastra di silicio su cui sono posti i bus, i canali di comunicazione tra i diversi componenti del nostro pc. I bus sono distinguibili solo in parte perché sono dei sottili filamenti di rame sciolti sul silicio o sotto il silicio, davanti e dietro. La mobo non ha capacità di calcolo o di memorizzazione! Alloggia i componenti e li collega.

North bridge

Uno dei componenti fondamentali della scheda madre è il North Bridge, o ponte nord, spesso indicato più brevemente come chipset. E’ il fulcro centrale che crea un bus ad alte prestazioni necessario a far scambiare dati tra CPU, RAM e Scheda video. Lo scambio è consistente obbligando i produttori di mobo ad inserire un dissipatore di calore passivo o con ventola di raffreddamento. Il chipset è un componente non banale: ne possono esistere più versioni che avranno specifiche e performance differenti.

A seconda di tale chipset potremo installare o meno determinate CPU, piuttosto che sia garantita una frequenza di esercizio delle ram (le DDR5 ad esempio vanno dai 4800 ai 6200 MHz), la sua versione DDR4 o DDR5, potremo avere un determinato canale dati col la scheda video che i alcuni casi potrebbe rappresentare un collo di bottiglia se il chipset è economico mentre la scheda video di fascia alta. I chipset di costo maggiore e prestazioni elevate garantiscono anche delle fasi di alimentazione differente dei componenti sulla scheda madre garantendo delle performance e delle capacità di overclock migliori. Ancora, a seconda del modello avremo supporto per un certo numero di banchi di ram, un certo numero di slot di espansione, di porte USB nelle varie versione 2 o 3, con aggancio A o C un certo numero di attacchi per dispositivi e hard disk SSD o M2 con il loro DMA integrato per prestazioni elevate di caricamento disco/ram. Negli ultimi anni la tendenza è quella di spostare alcune funzionalità del chipset direttamente nelle CPU e accorpando alcune funzionalità dei bridge nord e sud nel nord stesso.

A titolo puramente esemplificativo, mentre scriviamo, i chipeset migliore per Intel sono gli Z790 mentre versioni meno performanti sono H770 e B670. Per AMD possiamo citare X670 come alta fascia, seguito da il il B560 e B550. Numeri progressivi più bassi sono adatti ad hardware più datato o in genere meno performanti.

South bridge

E’ fortemente legata al ponte nord. In genere fornisce alcune funzionalità ora integrate nelle mobo come scheda audio, scheda di rete sia ethernet che Wi-Fi, controller per gli hard disk con o meno funzionalità RAID. In passato il south bridge poteva avere integrata anche la scheda video a basse prestazioni ma non è più contemplato nei sistemi moderni. Se un utente fosse particolarmente esigente, potrebbe disattivare le funzionalità audio e di rete e decidere di aggiungere sulla mobo una scheda dedicata con specifiche tecniche migliori.

La cpu

Il cuore del nostro computer. L’elemento più importante, costoso e su cui probabilmente vale la pena investire qualche soldo in più nella scelta di una configurazione. Le CPU sono divise commercialmente tra quelle della famiglia Intel e AMD. Le due aziende sono entrambe valide e all’avanguardia, alternandosi spesso ai vertici delle prestazioni dei loro prodotti. In generale AMD ha un costo leggermente più contenuto ed è consigliata per i videogamer mentre Intel per le applicazioni di ufficio. Diciamo che la distinzione è piuttosto inutile. A questi livelli tecnologici le differenze sono poche ed impercettibili. Le cpu vengono progettate sempre di nuove ed immesse sul mercato con regolarità. Dalla legge di Moore però possiamo ben capire come le differenze tra generazioni di cpu siano ormai molto sottili in termini di prestazioni aggiuntive e , forse, occorrono anche 3 o 4 generazioni prima di poter riscontrare un reale aumento di prestazioni a confronto. Gli ultimi trend sono quelli di progettare unità che assorbano poca corrente e watt a fronte di una migliore capacità di calcolo. CPU di fascia media consumano 65 /80 watt mentre quelle di fascia alta possono superare i 120watt e oltre i 200 watt!

Le ultime CPU hanno poi integrata anche la circuteria per fare la grafica, ovvero le GPU. Diciamo subito che tale circuteria è basilare e fornisce prestazioni video per applicazioni standard e di ufficio ma se la cava male con operazioni specifiche 3D per i videogiochi in cui è consigliata una scheda video dedicata.

Core e Threads

Se le prime CPU avevano una solo unità di calcolo, le moderne hanno più unità dette core, tecnologia introdotta da AMD negli anni 2000. Possiamo avere CPU con 4, 8, 16 core o anche più che svolgono individualmente o insieme (parallelo, concorrenza) delle istruzioni, migliorando la velocità del dispositivo. Con alcune strategie di parallelismo, le istruzioni possono essere scomposte ed eseguite ulteriormente aumentando il grado di parallelismo che raddoppia di fatto le istruzioni fattibili. Magari una CPU da 4 core finisce per avere 8 thread, ovvero 8 operazioni contemporaneamente su 4 core fisici!

I registri

A livello più atomico, le cpu effettuano operazioni mettendo dati vari su registri, un po’ come fossero le variabili di base della cpu stessa. Questi registri possono essere in un numero più o meno consistente, alcuni di base comune, altri specifici per le architetture e generazioni di cpu. Possono arrivare ad esserci anche 200 registri su una cpu moderna. Tale registri hanno una dimensione nell’ordine dei bit. Attualmente abbiamo registri di 64bit, mentre i primi spesso erano ad 8 o 16bit. Registri più grandi implica un miglioramento di alcune performance ma difficilmente vedremo registri a 128bit poiché istruzioni più lunghe implicano altre problematiche che non darebbero vantaggi particolari.

Clock e Frequenza

Le operazioni di calcolo sono sincronizzate da una sorta di orologio interno alla cpu detto appunto clock. Il clock “scatta” in frazioni di secondo e questi scatti sono misurabili in Ghz (Giga hertz). Le CPU moderne sono capaci di clock di 2,5 Ghz ovvero singoli colpi, ovvero 1/ 2.500.000.000 di secondo. Ovviamente per noi umani questo colpo è impercettibile ma i componenti elettronici possono viaggiare con questa velocità. All’interno della nostra CPU sono individuabile tre momenti: fetch, decode, execute. Il fetch è il momento in cui una istruzione viene caricata da cache o ram all’interno dei registri della cpu. Questa operazione può durare diversi cicli di clock poiché il reperimento e il trasferimento di tale dato sul bus impiega un certo tempo per essere effettuata. Stessa cosa accade nella fase di decode in cui la cpu legge l’operazione/dato e capisce cosa fare, e la fase di execute in cui esegue esplicitamente l’operazione. Le operazioni non vengono fatte in un singolo colpo di clock quasi mai, ma possono volerci anche decine di colpi di clock per ottenere il risultato.

Performance

Va da se che più la frequenza è alta più istruzioni possono essere fatte ma questo assunto non è più vero in assoluto. Le architetture hardware delle cpu hanno porzioni di circuiteria e, quindi, milioni di transistor spesso dedicati ad eseguire istruzioni mirate liberando la cpu dal farle in maniera generica e aumentando l’efficienza dei clock utilizzati per attivare magari solo quella porzione di transistor che occuperebbero meno colpi di clock di una cpu generica, senza tali istruzioni. Le porzioni di circuteria dedicate spesso prendono nomi come “set di istruzioni” come le storiche MMX, SSE4, 3DNow, AVX2. Le cpu ogni generazione cercano di inserire set di istruzioni mirati a migliorare determinate operazioni, basti pensare alle istruzioni specifiche per l’intelligenza artificiale che hanno costretto a rivedere intere porzioni di CPU per introdurre le NPU (Neural Processing Unit), dedicate proprio a queste operazioni specifiche.

In genere le CPU possono essere distinte tra due architetture CISC (Complex Instruction Set Computer) e RISC (Reduced Instruction Set Computer). Alla prima appartengono le CPU di Intel e AMD, alla seconda i processori ARM (Advanced Risc Machine) che abbiamo sui telefoni, tablet, le CPU di casa Apple Mi, M2 ed M3, le GPU di casa NVIDIA.

Nell’immagine una architettura delle XPU che contengono internamente CPU, GPU per la grafica ed NPU per le operazioni di IA che danno una idea di quanto le CPU e architetture siano diventate complesse rispetto alle classificazioni base di queste righe di articolo. Il lettore nota più core con clock differenti a seconda del core che possono aumentare con un boost , cache di generosa dimensione per ben 12 core, 8 per le operazioni standard, 4 per la grafica.

La struttura del processore Snapdragon
La struttura del processore Snapdragon

Cache

Abbiamo parlato di RAM e registri ma c’è un ulteriore tassello ricoperto da questa memoria velocissima che è presente nelle nostre CPU. La cache è decisamente più veloce della ram ma decisamente più costosa e a confronto occupa molto più spazio, motivo per cui è presente sulle cpu ben al di sotto del 100 megabyte. Eppure tale cache permette alla cpu di velocizzare le istruzioni se trova il dato (cache hit) e di non perdere molto ad andare nella ram a ripescare quello che eventualmente serve e manca in cache (cache miss).

Le cpu Intel

Un dato da tenere presente è: ogni CPU ha una versione o generazione, oltre che un modello/tipologia e un socket . Ad esempio le CPU Intel attualmente sono classificate con dicitura I3, I5, I7 e I9 che descrivono i processori rispettivamente di fascia bassa, media, alta, entusiasta. Assieme a questa una dicitura che indica la generazione. Ad esempio un modello attuale è: i5 14600k dove appunto distinguiamo la generazione 14 più un modello 600 che indica una velocità del clock ulteriore nel segmento.

Le cpu AMD

Per le CPU AMD è analogo ma con diciture diverse un filo più complesse. Partiamo dall’esempio Ryzen 5 7640. Il primo 5 è un po’ come la generazione i5 -> Ryzen 5, i3 -> Ryzen 3, fino a Ryzen 7 e 9. Nel numero invece a quattro cifre, da sinistra, il primo 7 corrisponde alla generazione/anno: 7 anno 2023, 8 anno 2024, 9 anno 2025. La seconda cifra indica la fascia di mercato con una leggera sfumatura rispetto alla dicitura a fianco Ryzen: ad esempio il 5 e 6 corrispondono ad un Ryzen 5, 3 e 4 al Ryzen 3. La terza cifra corrisponde all’architettura Zen 4, 5 ecc. Le altre cifre indicano ulterio segmento alto 5 o basso 0 con la lettera che indica il consumo energetico.

La ram

E’ la memoria di lavoro, quindi ricopre una importanza significativa per lavorare serenamente con il pc. I programmi e i file che apriamo vengono spostati su tale memoria e si evolvono mentre li utilizziamo sempre su tale memoria. Questa memoria ha un tecnologia puramente elettronica per poter funzionare ad alte prestazioni ma implica che sia volatile, ovvero spento il pc il suo contenuto è perso. Ecco perché occorre una memoria di massa, oltre a questa centrale, per salvare a tempo indeterminato. Le ram in commercio hanno una tecnologia denominata DDR, ovvero Double Data Rate. Per andare al doppio della velocità occorre che sul nostro pc siano presenti due banchi di memoria identici che consentono letture/scritture in parallelo. La cpu può accedere in modo diretto e casuale a tale memoria, da cui l’acronimo Random Access Memory. I modelli in commercio hanno la tecnologia DDR4 e DDR5.

Ogni memoria ha almeno due grandezze da tenere presente: frequenza e latenza. Il primo indica una sorta di velocità di lettura e scrittura, maggiore è la frequenza più performance hanno le ram. Le ddr5 possono raggiungere 8000 Mhz di frequenza. La latenza invece è un parametro che indica quanti cicli spreca la memoria ad accedere ad un dato. Le ddr4 possono avere latenze di 16 cicli, le ddr5 anche di 32 cicli. Più è basso tale parametro CAS, maggiore sarà il binomio CAS/frequenza. In generale aumentare la frequenza fa percepire meno grave una latenza troppo alta. Attenzione: non tutti i chipset “reggono” le frequenze soprattutto le più alte. Occorre verificare la compatibilità quindi chipset/frequenza ram.

Le ram vengono alimentate con una opportuna corrente/tensione, spesso di 1,5volt: maggiore è la dimensione della memoria, maggiore sarà il consumo che può influire sulla durata ad esempio della batteria dello smartphone o tablet o portatile. 16 gigabyte di ram DDR5 possono consumare circa 10watt che non è proprio banale.

Gli hard disk

Se la ram è una memoria di lavoro, gli hard disk rappresentano le memorie di massa in cui archiviare i dati e i software a tempo indeterminato, anche da pc spento. Sono proverbialmente molto più lenti della ram e quindi non permettono di lavorare al meglio con la cpu. I dati e programmi ogni volta che sono necessari vengono caricati attraverso il DMA direttamente in ram per poter essere utilizzati dalla cpu. Le tecnologie di archiviazione prevedono sostanzialmente un principio piuttosto banale che permette di salvare il bit sotto forma di carica o magnetica negli hd tradizionali a piatto magnetico, o una carica elettrica in opportune celle elettroniche sugli hd ssd e memorie usb.

Piatto magnetico

E’ la tecnologia più vecchia ma affidabile. Fino a pochi anni fa l’unica diffusa con prezzi anche molto convenienti. L’idea è impilare più piatti magnetici con una testina che legge le celle magnetiche. Il piatto girando permette di far raggiungere alla testina tutti gli elementi. Ovviamente il piatto non può girare a velocità stellari (solitamente fino a 15.000 giri al minuto al massimo, 7.200 mediamente) e, essendo meccanico, è un componente fragile che può danneggiarsi. Le tecnologie però hanno permesso di arrivare a 40 Terabyte di archiviazione che rende questa soluzione ancora molto interessante per server e sistemi NAS dove occorre spazio ma non prestazioni. Le prestazioni si aggirano intorno ai 120-160 MByte/secondo di

SSD

Decisamente più veloci dei piatti magnetici, sono parzialmente elettronici rendendo letture e scritture più rapide. Oggi gli SSD, dischi a stato solido, vantano una vita simile ai piatti magnetici e costi competitivi , rendendoli la soluzione mainstream e non. Letture/scritture si aggirano intorno ai 200-550 MB/s, 5 volte superiori ad un hd tradizionale quindi.

M.2

Consumo di un hard disk SSD è decisamente inferiore ad uno a piatto magnetico: 2/5watt contro 6/10 watt dei dischi tradizionali, mentre gli M.2 tornano a consumare anche 7/8 watt ma a fronte di prestazioni impareggiabili. A trarne il vantaggio sono quindi le prestazioni anche il 30% maggiori di un SSD con uno spazio occupato dagli stick stile ram decisamente più contenuto. Sulla longevità, questi hard disk dovrebbero durare teoricamente fino ai 300 anni circa, superando la vita di un hard disk a piatto magnetico. Letture e scritture qui arrivano tranquillamente tra i 1000-7000 MB/s, ben oltre anche 50 volte un disco tradizionale, o 10 di un SSD. Il costo forse l’unico inconveniente.

Le schede di espansione

Mella scheda madre, a seconda dei modelli, sono presenti più slot di espansione per aggiungere schede che possano fornire funzionalità aggiuntive al nostro personal computer. Diciamo sin da subito, che sono spesso schede destinate ad un pubblico professionale o molto esigente. Ho bisogno di una scheda di rete più performante rispetto a quella contenuta nel south bridge? Aggiungo una scheda apposita. Voglio un sistema audio per acquisizione di strumenti, audio dolby sofisticato? Scheda di espansione. Voglio una scheda di cattura video per collegare telecamere? Schede tv, schede per aumentare porte USB, schede per avere più dischi ssd o M2 ecc. Ce n’è un po’ per tutte le esigenze. Attualmente lo standard prevede che gli slot siano tutti PC-Express solo con velocità indicate solitamente con 16x, 8x, 4x, 1x. La 16x in genere è dedicata alla scheda video.

Scheda video

La scheda video è sicuramente la parte più ludica preferita dagli studenti e con buona probabilità in ogni classe c’è l’appassionato e cultore delle ultime tecnologie e modelli. Dagli albori dei primi modelli, le schede video si sono trasformate da semplici strumenti di rappresentazione a video a veri e proprio elementi hardware avanzati con capacità computazionali di altissimo livello tanto da far concorrenza alla complessità delle cpu. L’alunno vede tali capacità con una propensione alla grafica avanzata dei videogiochi, sempre più realistici.

Ma non si esaurisce qui la versatilità di questi “acceleratori grafici”. La tecnologia con cui sono costruite le GPU, le memorie ram con particolari accorgimenti tecnologici rendono queste schede ottime anche per svariati calcoli scientifici, il minaggio delle criptovalute, l’addestramento e l’inferenza dei sistemi di IA. In questi casi, vengono creati rack server di schede video che non hanno i normali contatti per collegare monitor, ma solo le unità di calcolo che, in parallelo, possono produrre quantità di calcoli mostruosi per gli scopi citati. (es elon-musk-installa-100000-gpu-nvidia-h200-in-19-giorni per addestrare modelli LLM). Lasciamo al lettore intuire quanto queste schede possano essere energivore e come si prospetti, in una soglia temporale non così ampia, un possibile problema di incompatibilità tra politiche green e pervasività della IA nel quotidiano lavorativo/vita.

Una Nvidia Tesla H100 da 40.000€
Una Nvidia Tesla H100 da 40.000€

Le schede video commerciali e da server hanno consumi spesso elevati che possono tranquillamente andare da 100watt fino a 300watt, costringendo a comperare un alimentatore adeguato a supportare il consumo di CPU, GPU e degli altri elementi visti tra hd e ram.

Ultima modifica 30 Dicembre 2024

L'articolo La struttura del pc proviene da alfredocentinaro.it.

]]>
Somma di due numeri in input fino a soglia in GO https://www.alfredocentinaro.it/lezioni/go-lang/somma-di-due-numeri-in-input-fino-a-soglia/ Tue, 15 Oct 2024 20:37:48 +0000 https://www.alfredocentinaro.it/?p=7850 La somma di numeri interi inseriti da tastiera fino a raggiungere una determinata soglia è un esercizio ideale per chi inizia a programmare in GoLang. In questo articolo vediamo passo dopo passo come realizzare un semplice programma Go che somma numeri in input fino al raggiungimento di una soglia, in questo caso 150. Questo esempio ... Leggi tutto

L'articolo Somma di due numeri in input fino a soglia in GO proviene da alfredocentinaro.it.

]]>
La somma di numeri interi inseriti da tastiera fino a raggiungere una determinata soglia è un esercizio ideale per chi inizia a programmare in GoLang. In questo articolo vediamo passo dopo passo come realizzare un semplice programma Go che somma numeri in input fino al raggiungimento di una soglia, in questo caso 150. Questo esempio è utile per comprendere l’uso del ciclo for, la lettura di input da tastiera e la gestione delle variabili.

Obiettivo del programma

Il programma chiede continuamente all’utente di inserire coppie di numeri interi, sommandoli progressivamente. Questo ciclo continua fino a quando la somma totale non raggiunge o supera il valore soglia di 150. Al termine, il programma stampa:

  • La somma finale dei numeri inseriti.
  • Il numero totale di cicli necessari per raggiungere la soglia.

Esempio di codice in GoLang

Di seguito trovi un esempio di codice semplice, chiaro e commentato per realizzare l’esercizio descritto:

package main

import (
	"fmt"
)

func main() {
	var totale int = 0
	var numero1 int
	var numero2 int
	var contatore int8 = 0

	for totale < 150 {
		fmt.Print("Inserisci il primo numero: ")
		fmt.Scan(&numero1)

		fmt.Print("Inserisci il secondo numero: ")
		fmt.Scan(&numero2)

		totale += numero1 + numero2

		fmt.Println("totale parziale=", totale)

		contatore++
	}

	fmt.Println("totale finale=", totale, " con ", contatore, " inserimenti")
}

Come eseguire il programma

Per eseguire questo programma sul tuo computer, segui questi semplici passaggi:

  1. Copia il codice sopra in un file chiamato, ad esempio, somma.go.
  2. Apri un terminale nella cartella del file e lancia il comando:
go run somma.go

Output tipico del programma

Ecco un esempio di output che puoi aspettarti:

Inserisci il primo numero: 40
Inserisci il secondo numero: 35
Totale parziale: 75
Inserisci il primo numero: 30
Inserisci il secondo numero: 50
Totale parziale: 155
Somma finale: 155 raggiunta in 2 cicli.

Concetti di programmazione in Go affrontati

Con questo semplice programma hai imparato alcuni rudimenti fondamentali di Go:

  • L’uso della funzione fmt.Scan() per leggere input da tastiera.
  • La gestione di cicli con il costrutto for in Go.
  • Come dichiarare e gestire variabili numeriche.

Conclusione

Questo esercizio di programmazione GoLang ti aiuta a familiarizzare con le basi del linguaggio e a scrivere codice pulito e funzionale. Prova ad adattarlo cambiando la soglia o aggiungendo controlli sugli input degli utenti per rendere il programma ancora più robusto. Segui questo blog per avere ulteriori casi di esempio e frammenti di codice.

Ultima modifica 29 Marzo 2025

L'articolo Somma di due numeri in input fino a soglia in GO proviene da alfredocentinaro.it.

]]>