Visualizzazione post con etichetta tips. Mostra tutti i post
Visualizzazione post con etichetta tips. Mostra tutti i post

sabato 27 giugno 2015

Come estrarre un file tar con php

A partire dal Php 5.2.0, è disponibile la classe Phar, che permette di interagire con archivi Zip e Tar.
In questo post mostro un semplice snippet per estrarre un archivio Tar in una directory temporanea.

$phar = new PharData('nome-archivio.tar');
//Estraggo tutti i files posizionandoli in /tmp/
$phar->extractTo('/tmp/', null, true);

venerdì 26 giugno 2015

Elencare tutti i files con estensione jpg in una directory con php

Oggi pubblico un semplice codice php che sfrutta la comoda funzione glob().
Questa funzione permette di ricercare files con un determinato pattern all'interno di una cartella specificata.
Di seguito, un semplice snippet per ricercare files con estensione .jpg

$_DIR = '/var/www/html/images/';
$images_array = glob($_DIR . '*.jpg');       
foreach ($images_array as $image) {
   echo $image;
}

giovedì 18 giugno 2015

Automatic scaling di una finestra in C#

Sviluppando un software a finestre in C# e non una web application, spesso si ha il problema di renderlo in qualche modo "responsive" e quindi adattabile alle varie risoluzioni dello schermo o al ridimensionamento della finestra.

Dopo aver cercato su internet per un po', sono finalmente giunto ad uno script facile e veloce per gestire la cosa. Una premessa è doverosa: il C# ha un suo metodo di scaling che però non agisce direttamente sui Font. Il che significa che, oltre allo scaling del controllo, è necessario effettuare anche un ridimensionamento del font con la stessa proporzione.
E' importante disabilitare la funzionalità di autoscale nativa, che permette un adattamento solo parziale e successivamente ridimensionare ogni controllo della finestra con una determinata proporzione (ratio).


Di seguito i pochi passi necessari per il corretto funzionamento del ridimensionamento.

1. Aggiungere nelle dichiarazione delle variabili
private Size currSize;
2. Aggiungere nel costruttore, dopo InitializeComponent()
this.AutoScaleMode = AutoScaleMode.None;
currSize = this.Size;
3. Aggiungere la seguente funzione
private void autoscale()
{
   Font tempFont;
   SizeF ratio = SizeF.Empty;
   //calculate resizing ratio
   ratio = new SizeF((float)this.Width / currSize.Width, (float)this.Height / currSize.Height);
   //loop through all controls and scale them
   foreach (Control c in this.Controls)
   {//Get current font size, Scale object and scale font
      tempFont = new Font(c.Font.Name,
      c.Font.SizeInPoints * ratio.Width * ratio.Height);
      c.Scale(ratio);
      c.Font = tempFont;
   }
   //update current size
   currSize = this.Size;
}
4. Utilizzare la funzione precedente nell'evento SizeChanged della windows form
private void Form1_SizeChanged(object sender, EventArgs e)
{
   autoscale();
}

venerdì 5 giugno 2015

Caricamento automatico delle classi in Php

In principio fu l'include (o il require) di tanti file php che contenevano funzioni. 
Poi vennero le classi, incluse a mano una per una.
Ed ora? Adesso il Php è un linguaggio molto più maturo, dotato di namespace, che permettono a classi con lo stesso nome di coesistere (con namespace diversi) all'interno dello stesso progetto. In questo modo, includere una libreria di terze parti progettata a namespace non comporta più problemi di omonimia di classi.
Altra caratteristica utilissima, novità rispetto al passato, è l'autocaricamento delle classi. Onde evitare una lunghissima lista di inclusioni, una per ogni classe, è stata introdotta la funzionalità di autoload.
Come funziona? Semplice: utilizzando la funzione spl_autoload_register() spiegheremo all'interprete Php come comportarsi in caso di classi "che non conosce", ovvero che non sono state esplicitamente incluse nello script. Per maggiori informazioni, consiglio il sito ufficiale.
Vediamo un semplice esempio:
//Autoloading classes
spl_autoload_register(function($className)
{
     //Obtain the pure class name
     $pureClassName = getRealClassName($className);
     //Build the path
     $namespace = getNameSpace($className);
     if(file_exists($namespace.'/'.$pureClassName.'.php')) {
         include_once($namespace.'/'.$pureClassName.'.php');
     }
});

function getRealClassName($fullClassName) {
     //Explode class name
     $classNameArray = explode('\\',$fullClassName);
     //Obtain the pure class name
     return end($classNameArray);
}
    
function getNameSpace($fullClassName) {
     //Explode class name
     $classNameArray = explode('\\',$fullClassName);
     //Remove the pure class name
     array_pop($classNameArray);
     //Remove the main namespace (first)
     array_shift($classNameArray); 
     //Build the path
     $namespace = implode('/', $classNameArray);
     return $namespace;
}
Breve spiegazione: nel caso il Php non trovi una classe, tenta una inclusione automatica, cercando il file posizionato nel percorso indicato dal namespace (stessa struttura delle cartelle quindi) e chiamato come il nome della classe, con estensione php. Le due funzioni getRealClassName() e getNameSpace() sono semplici funzioni che interpretano il nome della classe separando namespace dal nome "proprio" della classe stessa. Unica cosa degna di nota, viene eliminato il primo ramo del namespace, che solitamente è il nome del progetto.
Quindi, riassumendo, un comando simile:
new \ProjectName\Core\Othernamespace\Classname()
Causerà una include automatica del percorso
Core/Othernamespace/Classname.php

lunedì 7 aprile 2014

Includere un file html esterno con jQuery

Un semplice snippet per includere dinamicamente il contenuto html di un file esterno con jQuery:

 $('body').append($('<div></div>').load('file_esterno.html', function() {
   //Qui operazioni opzionali sull'html appena caricato
 });
:-)

sabato 29 marzo 2014

C# - Stampante direttamente con comandi nativi

Poco tempo fa ho avuto la necessità di stampare, con comandi nativi, su una stampante termica Zebra.
In passato mi era capitato di dover stampare con comandi nativi, ma su stampantine con porta seriale, quindi i comandi altro non erano che stringhe inviate in seriale.
In questo caso, invece, la stampante era collegata via usb e accessibile solo tramite drivers. Come fare? Incredibilmente la Microsoft ci viene in aiuto, e ha pubblicato una classe fantastica chiamata RawPrinterHelper che permette proprio di interagire direttamente con la stampante, a qualsiasi porta essa sia collegata (parallela, usb, seriale).
E' una classe statica, e per usarla è sufficiente eseguire il metodo SendStringToPrinter; il primo parametro sarà il nome della stampante, così come è definita nell'elenco stampanti di Windows, mentre il secondo parametro sarà la stringa di comandi da inviare direttamente alla stampante.
Ad esempio, in caso di una stampante Zebra:
RawPrinterHelper.SendStringToPrinter("Zebra GX420T", "^XA^FDESEMPIO^FS^XZ");
Di seguito posto anche il codice originale della classe, che si può trovare comunque a questo indirizzo: http://support.microsoft.com/kb/322091/it
using System;
using System.Drawing;
using System.Drawing.Printing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;

namespace TintoLavaApp
{
    public class RawPrinterHelper
    {
        // Structure and API declarions:
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public class DOCINFOA
        {
            [MarshalAs(UnmanagedType.LPStr)]
            public string pDocName;
            [MarshalAs(UnmanagedType.LPStr)]
            public string pOutputFile;
            [MarshalAs(UnmanagedType.LPStr)]
            public string pDataType;
        }
        [DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);

        [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool ClosePrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);

        [DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool EndDocPrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool StartPagePrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool EndPagePrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten);

        // SendBytesToPrinter()
        // When the function is given a printer name and an unmanaged array
        // of bytes, the function sends those bytes to the print queue.
        // Returns true on success, false on failure.
        public static bool SendBytesToPrinter(string szPrinterName, IntPtr pBytes, Int32 dwCount)
        {
            Int32 dwError = 0, dwWritten = 0;
            IntPtr hPrinter = new IntPtr(0);
            DOCINFOA di = new DOCINFOA();
            bool bSuccess = false; // Assume failure unless you specifically succeed.

            di.pDocName = "My C#.NET RAW Document";
            di.pDataType = "RAW";

            // Open the printer.
            if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))
            {
                // Start a document.
                if (StartDocPrinter(hPrinter, 1, di))
                {
                    // Start a page.
                    if (StartPagePrinter(hPrinter))
                    {
                        // Write your bytes.
                        bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
                        EndPagePrinter(hPrinter);
                    }
                    EndDocPrinter(hPrinter);
                }
                ClosePrinter(hPrinter);
            }
            // If you did not succeed, GetLastError may give more information
            // about why not.
            if (bSuccess == false)
            {
                dwError = Marshal.GetLastWin32Error();
            }
            return bSuccess;
        }

        public static bool SendFileToPrinter(string szPrinterName, string szFileName)
        {
            // Open the file.
            FileStream fs = new FileStream(szFileName, FileMode.Open);
            // Create a BinaryReader on the file.
            BinaryReader br = new BinaryReader(fs);
            // Dim an array of bytes big enough to hold the file's contents.
            Byte[] bytes = new Byte[fs.Length];
            bool bSuccess = false;
            // Your unmanaged pointer.
            IntPtr pUnmanagedBytes = new IntPtr(0);
            int nLength;

            nLength = Convert.ToInt32(fs.Length);
            // Read the contents of the file into the array.
            bytes = br.ReadBytes(nLength);
            // Allocate some unmanaged memory for those bytes.
            pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength);
            // Copy the managed byte array into the unmanaged array.
            Marshal.Copy(bytes, 0, pUnmanagedBytes, nLength);
            // Send the unmanaged bytes to the printer.
            bSuccess = SendBytesToPrinter(szPrinterName, pUnmanagedBytes, nLength);
            // Free the unmanaged memory that you allocated earlier.
            Marshal.FreeCoTaskMem(pUnmanagedBytes);
            return bSuccess;
        }
        public static bool SendStringToPrinter(string szPrinterName, string szString)
        {
            IntPtr pBytes;
            Int32 dwCount;
            // How many characters are in the string?
            dwCount = szString.Length;
            // Assume that the printer is expecting ANSI text, and then convert
            // the string to ANSI text.
            pBytes = Marshal.StringToCoTaskMemAnsi(szString);
            // Send the converted ANSI string to the printer.
            SendBytesToPrinter(szPrinterName, pBytes, dwCount);
            Marshal.FreeCoTaskMem(pBytes);
            return true;
        }
    }
}

venerdì 28 marzo 2014

Chiamate cross thread su controlli winform - C#

Se si utilizzano thread durante lo sviluppo di applicazioni c#, si può avere la necessità di agire direttamente su controlli winform, ad esempio impostare il contenuto di una casella di testo in una finestra.
Se però si tenta di agire direttamente sul controllo, ad esempio da un oggetto backgroundWorker (che altro non è che un thread), a runtime viene generata una eccezione e il debugger si lamenta per una chiamata cross-thread non correttamente gestita.
Per fortuna la soluzione è alquanto semplice, racchiusa in poche righe di codice. Ad esempio, nel codice eseguito dal backgroundWorker, basterà inserire una chiamata invoke sul controllo con il quale intendiamo interagire.
Dato una textbox chiamata txt_esempio, il codice quindi sarà:

try
{
    txt_esempio.Invoke((MethodInvoker)delegate() { 
        txt_esempio.Text = "Testo modificato da backgroundWorker"; 
    });
}
catch (InvalidOperationException ioe) { }

venerdì 6 dicembre 2013

Manipolazione di immagini con CodeIgniter


CodeIgniter permette di manipolare le immagini con una apposita libreria, eseguendo le seguenti operazioni:
  • ridimensionamento
  • cropping
  • rotazione
  • aggiunta di watermark

Ridimensionamento

La image library viene caricata dal framework mediante l'apposita funzione del framework (load), con parametro un array di configurazione. A seconda della configurazione utilizzata può essere richiamata una funzione di manipolazione immagini. Ad esempio, per eseguire un ridimensionamento devono essere specificati i parametri width e height:
$config['image_library'] = 'gd2';
$config['source_image'] = '/percorso/mypic.jpg';
$config['maintain_ratio'] = TRUE;
$config['width'] = 75;
$config['height'] = 50;
//Carico la libreria con la configurazione
$this->load->library('image_lib', $config);
//Ridimensiono l'immagine /percorso/mypic.jpg
$this->image_lib->resize();
Il listato precedente ridimensiona l'immagine sovrascrivendola. Se si preferisce mantenere l'immagine originale, creando una nuova immagine ridimensionata, è possibile utilizzare il parametro di configurazione new_image:
$config['new_image'] = '/percorso/nuovaimmaginediversa.jpg';

Rotazione

Una rotazione di una immagine può essere eseguita con 5 opzioni diverse:
  • 90 gradi
  • 180 gradi
  • 270 gradi
  • "hor'' (inversione orizzontale)
  • "vrt'' (inversione verticale)
Di seguito un esempio di codice per l'inversione orizzontale dell'immagine:
$config['image_library'] = 'gd2';
$config['source_image'] = '/percorso/mypic.jpg';
$config['rotation_angle'] = 'hor';

//Carico la libreria con la configurazione
$this->load->library('image_lib', $config);
$this->image_lib->rotate();

Cropping

Per ritagliare una immagine si può utilizzare la funzione crop specificando gli assi X e Y lungo i quali eseguire il cropping:
$config['image_library'] = 'gd2';
$config['source_image'] = '/percorso/mypic.jpg';
$config['x_axis'] = '100';
$config['y_axis'] = '60';
//Carico la libreria con la configurazione
$this->load->library('image_lib', $config);
//Eseguo il cropping 
$this->image_lib->crop();

Watermark

L'aggiunta di un watermark (filigrana) può avvenire attraverso due modalità, text (aggiunta di un testo all'immagine) e overlay (aggiunta di una piccola immagine in overlay all'immagine originale). Vediamo un esempio dell'ultimo caso, con una piccola immagine da inserire in basso a destra, con un padding di 5 pixel dai vertici:
$config['image_library'] = 'gd2';
$config['source_image'] = '/percorso/mypic.jpg';
$config['wm_vrt_alignment'] = 'bottom';
$config['wm_hor_alignment'] = 'right';
$config['wm_padding'] = '5';
$config['wm_overlay_path'] = '/percorso/watermark.png';
//Carico la libreria con la configurazione
$this->load->library('image_lib', $config);
//Eseguo l'aggiunta del watermark
$this->image_lib->watermark();

Ora la risposta ad un problema che mi ha fatto sbattere un po' la testa prima di comprenderne la (ovvia) soluzione. Se voglio manipolare due immagini di fila, ma con una configurazione diversa? Notiamo nei listati precedenti come la libreria venga caricata dal metodo load con l'array di configurazione, ma questo deve avvenire solo una volta, dopodiché la libreria è già stata caricata all'interno dell'esecuzione di codeigniter. La risposta è l'utilizzo delle funzioni clear ed initialize, vediamone un esempio concreto:
//Impostazioni cropping
$config['image_library'] = 'gd2';
$config['source_image'] = '/percorso/mypic.jpg';
$config['x_axis'] = '100';
$config['y_axis'] = '60';
//Carico la libreria con la configurazione
$this->load->library('image_lib', $config);
//Eseguo il cropping
$this->image_lib->crop();

//Cancello le impostazioni della libreria
$this->image_lib->clear();
//Impostazioni per rotazione orizzontale
$config['image_library'] = 'gd2';
$config['source_image'] = '/percorso/mypic.jpg';
$config['rotation_angle'] = 'hor';

//Carico la libreria con la configurazione
$this->image_lib->initialize($config);
//Eseguo la rotazione
$this->image_lib->rotate();

giovedì 28 novembre 2013

API RESTful con Codeigniter usando codeigniter-restserver


Nel post precedente ho mostrato come creare un semplice sistema API con il solo metodo GET. Ma se volessimo un server API che risponda secondo lo standard RESTful, dovremmo gestire anche i metodi POST, PUT e DELETE.
In particolare, lo standard consiglia di utilizzare:
  • GET per richieste di lettura di una risorsa
  • POST per richieste di creazione di una risorsa
  • PUT per richieste di modi ca di una risorsa
  • DELETE per richieste di eliminazione di una risorsa 
 Per fare questo velocemente e possibile appoggiarsi ad una libreria di terze parti, codeigniter-restserver.

Installazione

Il primo passo è ovviamente scaricare il pacchetto dalla pagina github della libreria. Il pacchetto includerebbe anche CodeIgniter, ma è molto probabile che si voglia integrare restserver in un progetto codeigniter gi à esistente. In questo caso è su fficiente copiare i files
application/libraries/Format.php
application/libraries/REST_Controller.php
application/config/rest.php
nella propria directory application, e ricordarsi di caricare automaticamente la classe REST_Controller come libreria nel fi le di con gurazione
application/config/autoload.php
Per personalizzare le opzioni di restserver, basta modi care l'apposito fi le precedentemente copiato:
application/config/rest.php

Funzionamento di base

Per il buon funzionamento del sistema è necessario creare controller che estendono la classe base REST_Controller. La libreria elabora le richieste sulla base del metodo HTTP utilizzato e della risorsa (controller), eseguendo la funzione corrispondente all'insieme dei due elementi, sempre seguendo la logica CodeIgniter. Per spiegarmi meglio, ipotizziamo di avere il seguente controller:
class News extends REST_Controller
{
    public function index_get()
    {
        // Lettura delle news
    }

    public function index_post()
    {
        // Crea una news
    }
 
 public function index_put()
    {
        // Modifica una news
    }
 public function index_delete()
    {
        // Elimina una news
    }
}
Una richiesta del tipo
GET http://www.example.com/news
comporterà l'esecuzione della funzione index_get, index perché l'url è senza una funzione specificata (news è direttamente il controller) e get perché il metodo HTTP utilizzato è, appunto, GET.

L'accesso ai parametri è garantito dalle funzioni get, post e put.
$this->get('id'); //mappatura di $this->input->get()
$this->post('id'); //mappatura di $this->input->post() 
$this->put('id'); 
Per i parametri del metodo DELETE, poiché lo standard non li prevede, è sufficiente gestirli direttamente dalla funzione richiamata dal controllore:
public function index_delete($id)
    {
        $this->response(array(
            'returned from delete:' => $id,
        ));         
    }

E' possibile inviare in output una struttura dati con la funzione response, gestendo anche la risposta HTTP direttamente (opzionale). In questo modo la libreria si preoccuperà di formattare la risposta a seconda dello standard impostato nella configurazione (io consiglio JSON). Inoltre, gestendo le risposte HTTP, è possibile utilizzare codici appropriati come il 201 per HTTP 201 Created e 404 per HTTP 404 Not Found (in quest'ultimo caso il primo parametro sarà una risorsa vuota, come ad esempio un array senza elementi).
public function index_post()
{
    // ...crea una news

    $this->response($news, 201); // Manda la risposta HTTP 201
}
Queste sono i tips principali per lo sviluppo di un sistema API con codeigniter-restserver. Non ho parlato per motivi di tempo delle altre caratteristiche della libreria, quali la gestione di API Keys, la gestione dell'autenticazione ecc. per le quali rimando direttamente al sito ufficiale.

mercoledì 27 novembre 2013

Un semplice sistema API GET CodeIgniter con risposta JSON

In questo post volevo descrivere una implementazione relativa ad un sistema API che usa esclusivamente il metodo HTTP GET, sempre in ottica di progetti web strutturati con una netta separazione tra vista (lato client) e controllo (lato server).
Il mio framework php preferito (CodeIgniter) gestisce in modo nativo le richieste con metodo HTTP GET, quindi creare un semplice sistema di API a sola lettura è molto semplice. Ipotizziamo di creare una sottocartella chiamata api nella cartella controller, e di posizionarvi dentro i vari controller che rispecchieranno le risorse delle API.

application
   |- controllers
      |- api
         |- news.php

A questo punto sappiamo che CodeIgniter gestirà che richieste del tipo
http://www.example.com/api/news/getAll
http://www.example.com/api/news/getSingle/12
richiedendo l'esecuzione del controller news.php che compare nell'albero di esempio, con i metodi getAll senza parametri e getSingle con un parametro GET valorizzato a "12".
Vediamo quindi il codice del controller:
class News extends CI_Controller {

 public function getAll()
 {
  $query = $this->db->get('news');
  $elenco_news = array();
  foreach ($query->result() as $row)
  {
       $news = new stdClass();
       $news->id = $row->id;
       $news->titolo = $row->titolo;
       $news->contenuto = $row->contenuto;
       array_push($elenco_news, $news);
  }
  echo json_encode($elenco_news);
 }
 
 public function getSingle(id)
 {
  $this->db->from('news')->where('id',$id);
  $query = $this->db->get();
  $row = $query->row(); 
  $news = new stdClass();
  $news->id = $row->id;
  $news->titolo = $row->titolo;
  $news->contenuto = $row->contenuto;
  echo json_encode($news);
 }

}
Sarà sufficiente quindi creare un controller per ogni tipo di risorsa (ad esempio news, eventi, banner) ed i relativi metodi, ricordandosi di strutturare le risposte in formato JSON come da esempio.

sabato 27 luglio 2013

Android: backup e restore di un database sqlite

Per l'applicazione Mie Ricette mi sono trovato a risolvere un problema: dare la possibilità all'utente di eseguire un backup del database delle ricette.
Il database usato nello sviluppo Android è Sqlite, quindi è essenzialmente un file che può essere copiato in una posizione "sicura" come la scheda SD, o comunque un percorso accessibile collegando il telefono al computer.
Nello script quindi sono presenti due funzioni, backupDatabase e restoreDatabase. Il primo l'ho trovato in qualche blog (perdonatemi, non mi ricordo quale). Entrambi utilizzano i FileChannel, ma mentre il cuore del funzionamento del primo si ha nella funzione nativa FileChannel.transferFrom, nel secondo specularmente si ha in FileChannel.transferTo.

package net.stefanobianchini.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import net.stefanobianchini.ricette.R;

import android.app.Activity;
import android.content.res.Resources;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;

public class backupdb {

 public static void backupDatabase(String dbPath, String nomeFileBackup,
   Activity act, Resources res) {

  try {
    File currentDB = new File(dbPath);// path del db su telefono
    File backupDB = new File(sd, nomeFileBackup);// file di destinazione
    @SuppressWarnings("resource")
    FileChannel src = new FileInputStream(currentDB)
       .getChannel();// apriamo un filechannel sul db e sul
           // file di destinazione
    @SuppressWarnings("resource")
    FileChannel dst = new FileOutputStream(backupDB)
       .getChannel();
    dst.transferFrom(src, 0, src.size());// trasferiamo il contenuto
    src.close();
     dst.close();
     Toast.makeText(
       act.getBaseContext(),

         res.getString(R.string.msg_db_location) + "\n " +
         backupDB.getAbsolutePath(),
       Toast.LENGTH_LONG).show();

  } catch (IOException e) {
   Toast.makeText(act.getBaseContext(), e.toString(),
     Toast.LENGTH_LONG).show();
  }
 }

 @SuppressWarnings("resource")
 private static void copyFile(File src, File dst) throws IOException {
  FileChannel inChannel = new FileInputStream(src).getChannel();
  FileChannel outChannel = new FileOutputStream(dst).getChannel();
  try {
   inChannel.transferTo(0, inChannel.size(), outChannel);
  } finally {
   if (inChannel != null)
    inChannel.close();
   if (outChannel != null)
    outChannel.close();
  }
 }

 public static void restoreDatabase(String dbPath, String percorsoFileBackup,
   Activity act, Resources res) {

  try {
   File currentDB = new File(dbPath);// path del db su telefono
   File backupDB = new File(percorsoFileBackup);// file di backup sorgente

   copyFile(backupDB, currentDB);

   Toast.makeText(
     act.getBaseContext(),
     String.format(res.getString(R.string.restore_done),
       backupDB.toString()), Toast.LENGTH_LONG).show();

  } catch (IOException e) {
   Toast.makeText(act.getBaseContext(), e.toString(),
     Toast.LENGTH_LONG).show();
  }
 }

}
Ora siamo pronti per eseguire il backup. Da notare che prima di eseguire il backup viene chiuso per sicurezza il db mediante il SQLiteOpenHelper (nel mio caso chiamato db_help);
//Backup del DB sqlite
db_help.close();
backupdb.backupDatabase(db_help.getReadableDatabase().getPath(), "backup_mie_ricette.db", HomeActivity.this, res);

venerdì 26 luglio 2013

C# Reflection, ovvero come richiamare librerie DLL a runtime


Per lavoro ultimamente mi son trovato a sviluppare un programma per la vendita al dettaglio di cosmesi. Le tecnologie scelte sono state C# (serviva un qualcosa solo su sistema Windows a finestre) e MySql (varie postazioni, sistema distribuito).
La sfida si è presentata quando ho dovuto progettare un sistema dinamico per gestire i registratori di cassa. Questo perché i registratori di cassa sono principalmente di due tipi, "Sweda" e "Ditron" con connessioni diverse e soprattutto comandi diversi. Lo Sweda ha una seriale rs232 mentre il Ditron è sempre collegato in seriale ma si appoggia su un programmino demone che fa il lavoro di comunicazione leggendo un determinato file (invio.txt) in una determinata cartella.
Per riassumere: per uno il C# deve aprire una connessione seriale, per l'altro creare un file. Entrambi hanno una sintassi completamente diversa.
Come fare per ottenere una progettazione "dinamica" che renda facile una successiva espansione, ad esempio l'inserimento di un altro tipo di registratore di cassa con un suo interfacciamento?

La soluzione intrapresa si basa sulla "Reflection", ossia sulla capacità del C# di caricare una DLL esterna a runtime (e gli oggetti e metodi in essa contenuti, quindi senza che sia per forza inclusa nel progetto), tenendo così separati il progetto della vendita al dettaglio dai singoli progetti C# delle librerie di interfacciamento ai registratori di cassa.
In questo modo, lo sviluppo delle suddette librerie dei registratori di cassa può essere anche lasciato ad un altra persona (o addirittura ditta).
La soluzione è quindi scalabile e si basa sul creare librerie con un nome differente (es. sweda.dll e ditron.dll) e comportamento differente ma con all'interno un oggetto chiamato RegistratoreFiscale, le cui funzioni pubbliche sono identiche in entrambe le librerie.
Vediamo un esempio di scheletro di libreria:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;
using System.Windows.Forms;

namespace Sweda
{
    public static class RegistratoreFiscale
    {
        public static SerialPort serialPort = null;
        public static void inizializza(string opzioni_di_inizializzazione) {}
        public static void aggiungiVoceAcquisto(double prezzo, string reparto, string descrizione, double prezzo_originale) {}
        public static void scegliOperatore(string operatore) {}
        public static void chiusuraScontrino(double totale) {}
     }
}
A questo punto la libreria Ditron sarà esattamente identica (o altre librerie in futuro che gestiranno altri registratori), quello che cambia è ovviamente l'implementazione dei metodi pubblici sopracitati.

Quello che si deve predisporre ora nel progetto principale (quello di vendita al dettaglio) è un oggetto "standard" che riceverà le chiamate ai metodi e si preoccuperà di richiamare questi metodi sulla libreria opportuna (sweda per i pc che hanno uno sweda collegato, ditron per i pc che hanno un ditron collegato, ecc.). Fondamentale la direttiva using System.Reflection per usare la caratteristica chiave di questa soluzione progettuale:

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace CosmeticSeller.oggetti
{
    class RegistratoreFiscale
    {
        private Type oggettoRegistratoreRuntime;

        public RegistratoreFiscale(string tipo_registratore)
        {
            Assembly SampleAssembly;
            SampleAssembly = Assembly.LoadFrom(tipo_registratore + ".dll");
            AppDomain.CurrentDomain.Load(SampleAssembly.GetName());
            foreach (Type t in SampleAssembly.GetTypes())
            {
                if (t.Name.Equals("RegistratoreFiscale"))
                {
                    oggettoRegistratoreRuntime = t;
                }
            }
        }

        public void aggiungiVoceAcquisto(double prezzo, string reparto, string descrizione, double prezzo_originale)
        {
            MethodInfo Method = oggettoRegistratoreRuntime.GetMethod("aggiungiVoceAcquisto");

            List>object< listaParametri = new List>object<();
            listaParametri.Add(prezzo);
            listaParametri.Add(reparto);
            listaParametri.Add(descrizione);
            listaParametri.Add(prezzo_originale);
            Method.Invoke(this, listaParametri.ToArray());
        }

        public void inizializza(string opzioni_di_inizializzazione)
        {
            MethodInfo Method = oggettoRegistratoreRuntime.GetMethod("inizializza");
            List<object> listaParametri = new List<object>();
            listaParametri.Add(opzioni_di_inizializzazione);
            Method.Invoke(this, listaParametri.ToArray());
        }

        public void chiusuraScontrino(double totale)
        {
            MethodInfo Method = oggettoRegistratoreRuntime.GetMethod("chiusuraScontrino");

            List<object> listaParametri = new List<object>();
            listaParametri.Add(totale);
            Method.Invoke(this, listaParametri.ToArray());
        }

        public void scegliOperatore(string operatore)
        {
            MethodInfo Method = oggettoRegistratoreRuntime.GetMethod("scegliOperatore");

            List<object> listaParametri = new List<object>();
            listaParametri.Add(operatore);
            Method.Invoke(this, listaParametri.ToArray());
        }
    }
}
Volendo descrivere questa classe in due parole, il suo compito è essere un wrapper tra il programma di vendita  e la libreria da utilizzare. Il cuore del funzionamento è nel costruttore, dove viene caricata dinamicamente la dll a partire dal nome passato come parametro. Nella libreria dll viene quindi cercato l'oggetto RegistratoreFiscale (implementato in tutte le librerie) e caricato in memoria come oggetto Type (oggettoRegistratoreRuntime).
L'oggetto deve essere di tipo Type poiché il compilatore non è in grado di capire che oggetto sia (sappiamo però quali metodi abbiamo implementato e possiamo richiamarli).
Nelle varie funzioni infatti, le quali mappano le implementazioni nelle varie librerie, viene eseguita l'invocazione del metodo desiderato a runtime (Method.Invoke). Durante l'invocazione è possibile passare anche i parametri al metodo desiderato (sotto forma di array di object).

A questo punto è possibile dal programma principale evocare il wrapper a seconda del registratore fiscale utilizzato (questa informazione nel mio caso è salvata su una tabella "impostazioni" su DB).
...
RegistratoreFiscale reg = new RegistratoreFiscale("Sweda");
reg.inizializza("COM1");
for( ... ) { //Ciclo tutti gli acquisti
    reg.aggiungiVoceAcquisto(prezzoComplessivo, reparto, descrizione, totaleriga);
}
reg.chiusuraScontrino(importo_totale);
...

lunedì 1 agosto 2011

Php: una comoda libreria per interagire con MySql

Pubblico una comoda libreria da utilizzarsi per interagire con un database MySql. Fa parte del codice di Opencart che, come al solito, si rivela per me fonte di ottimo codice Php. Nella realtà sarebbe un driver, ossia un codice specifico per DBMS; Opencart in realtà usa una classe wrapper (chiamata db.php) non interessandosi dell'implementazione. A seconda del DBMS utilizzato, viene incluso un driver oppure un altro (ad esempio, MySql.php oppure Mssql.php e così via).
Dopo questa precisazione, diamo una occhiata al codice:
<?php
final class MySQL {
 private $connection;

 public function __construct($hostname, $username, $password, $database) {
  if (!$this->connection = mysql_connect($hostname, $username, $password)) {
   exit('Error: Could not make a database connection using ' . $username . '@' . $hostname);
  }

  if (!mysql_select_db($database, $this->connection)) {
   exit('Error: Could not connect to database ' . $database);
  }

  mysql_query("SET NAMES 'utf8'", $this->connection);
  mysql_query("SET CHARACTER SET utf8", $this->connection);
  mysql_query("SET CHARACTER_SET_CONNECTION=utf8", $this->connection);
  mysql_query("SET SQL_MODE = ''", $this->connection);
 }

 public function query($sql) {
  $resource = mysql_query($sql, $this->connection);

  if ($resource) {
   if (is_resource($resource)) {
    $i = 0;
     
    $data = array();

    while ($result = mysql_fetch_assoc($resource)) {
     $data[$i] = $result;
      
     $i++;
    }

    mysql_free_result($resource);

    $query = new stdClass();
    $query->row = isset($data[0]) ? $data[0] : array();
    $query->rows = $data;
    $query->num_rows = $i;

    unset($data);

    return $query;
   } else {
    return TRUE;
   }
  } else {
   exit('Error: ' . mysql_error($this->connection) . '<br />Error No: ' . mysql_errno($this->connection) . '<br />' . $sql);
  }
 }

 public function escape($value) {
  return mysql_real_escape_string($value, $this->connection);
 }

 public function countAffected() {
  return mysql_affected_rows($this->connection);
 }

 public function getLastId() {
  return mysql_insert_id($this->connection);
 }

 public function __destruct() {
  mysql_close($this->connection);
 }
}
?>
Notate il costruttore (che si occupa di creare la connessione) ma soprattutto il distruttore (__destruct) che avrà il compito di chiudere la connessione (cosa che, diciamolo, viene spesso dimenticata).
L'utilizzo di questa classe è semplice e immediato:
//creo l'oggetto con i parametri desiderati 
$db = new MySql("localhost","user","password","test_db");
//ottengo il risultato della query
$result = $db->query("SELECT * FROM users");
//se il numero di righe è maggiore di zero
if($result->num_rows) { 
//Ciclo nelle righe del risultato stampando il campo "name"
   foreach ($result->rows as $user_row) {
      echo $user_row['name'];
   }
}

sabato 30 luglio 2011

Gestire le macchine virtuali vmware con uno script shell linux

Ecco un bello script Shell per gestire le macchine virtuali, l'ho preparato al lavoro per semplificarci la vita.
Funziona benino :-)
#!/bin/sh
while :
 do
    clear
dialog --backtitle "Gestione Macchine Virtuali" \
--menu "Seleziona dall'elenco l'azione che vuoi eseguire" \
15 60 5 \
1 "Visualizzare lista VM accese" \
2 "Visualizzare lista VM disponibili" \
3 "Accendere una VM" \
4 "Spegnere una VM" \
5 "Esci" \
2>/tmp/menuitem.$$
OPZIONE=`cat /tmp/menuitem.$$`

    case $OPZIONE in
        1) RESULT=`vmrun -T server -h https://localhost:8333/sdk -u administrator -p password list`
           dialog --title "Lista Macchine accese" --backtitle "Creato da Stefano Bianchini" --msgbox "$RESULT" 12 50
        ;;
        2) RESULT=`vmrun -T server -h https://localhost:8333/sdk -u administrator -p password listRegisteredVM`
        dialog --title "Lista Macchine disponibili" --backtitle "Creato da Stefano Bianchini" --msgbox "$RESULT" 12 50
        ;;
        3)
            vmrun -T server -h https://localhost:8333/sdk -u administrator -p password listRegisteredVM | grep ".vmx" > /tmp/regVM.$$
        righe=$(wc -l /tmp/regVM.$$ | awk '{print $1}')
            riga=0
            TOTALE=""
        while [ $riga -lt $righe ]; do
        #let riga+=1
              riga=`expr $riga + 1`
              #riga=$riga+1
              current=$(head -$riga /tmp/regVM.$$ | tail -1)
              TOTALE="$TOTALE \"$current\" =="
        done


        TOTALE="dialog --backtitle \"Creato da Stefano Bianchini\" --menu \"Seleziona la macchina virtuale\" 0 0 0 $TOTALE 2>/tmp/menuitem.$$"
        echo $TOTALE > /tmp/menu.$$

        chmod +x /tmp/menu.$$
        /tmp/menu.$$

        CHOICE=`cat /tmp/menuitem.$$`
        if [ "$CHOICE" != "" ] ; then
        vmrun -T server -h https://localhost:8333/sdk -u administrator -p password start "$CHOICE"
        fi
        ;;
        4) 
            vmrun -T server -h https://localhost:8333/sdk -u administrator -p password list | grep ".vmx" > /tmp/regVM.$$
        righe=$(wc -l /tmp/regVM.$$ | awk '{print $1}')
            riga=0
            TOTALE=""
        while [ $riga -lt $righe ]; do
              riga=`expr $riga + 1`
              current=$(head -$riga /tmp/regVM.$$ | tail -1)
              TOTALE="$TOTALE \"$current\" =="
        done


        TOTALE="dialog --backtitle \"Creato da Stefano Bianchini\" --menu \"Seleziona la macchina virtuale\" 0 0 0 $TOTALE 2>/tmp/menuitem.$$"
        echo $TOTALE > /tmp/menu.$$

        chmod +x /tmp/menu.$$
        /tmp/menu.$$

        CHOICE=`cat /tmp/menuitem.$$`
        if [ "$CHOICE" != "" ] ; then
        vmrun -T server -h https://localhost:8333/sdk -u administrator -p password stop "$CHOICE" soft
        fi
        ;;
        5) exit 0
        ;;
        *) exit 1
        ;;
    esac
done

venerdì 29 luglio 2011

C# - formattare in stringa la dimensione di un file

Semplice funzione per formattare la dimensione di un file, trovata a questo indirizzo.
Utilizzando in C# l'oggetto FileInfo (per ottenere informazioni su un determinato file) il valore emesso dalla funzione Lenght è di tipo long. Serve quindi una funzione che lo formatti capendo se si tratta di Mb, Gb, Kb eccetera.
public static string ToByteString(long bytes)
{
   long kilobyte = 1024;
   long megabyte = 1024 * kilobyte;
   long gigabyte = 1024 * megabyte;
   long terabyte = 1024 * gigabyte;
   if (bytes > terabyte) return (bytes / terabyte).ToString("0.00 TB");
   else if (bytes > gigabyte) return (bytes / gigabyte).ToString("0.00 GB");
   else if (bytes > megabyte) return (bytes / megabyte).ToString("0.00 MB");
   else if (bytes > kilobyte) return (bytes / kilobyte).ToString("0.00 KB");
   else return bytes + " Bytes";
}

Ed ora un semplice esempio di utilizzo:
FileInfo fi = new FileInfo(filename);
Console.Write("Dimensione file: " + ToByteString(fi.Length));
Questa funzione è fondamentale per un componente C# (User Control) che sto preparando: un fileBrowser.

mercoledì 27 luglio 2011

jQuery: semplice e veloce slideshow (base)

Girovagando per la rete, mi sono imbattuto in questo codice per creare una veloce slideshow con il framework jQuery.
Ovviamente fare il copia e incolla non è nel mio stile quindi ho apportato qualche piccola modifica nel codice (niente di speciale, tempistiche e stile degli elementi).
Questo perché, se utilizzate direttamente il codice originale, verrà mostrato un effetto non voluto: durante la transizione delle immagini, se ne vedono due in una volta che sembra che facciano a botte tra di loro per mostrarsi (ok, fate prima a provare di persona per capire cosa intendo). 
Per la cronaca, l'autore del codice originale lascia all'utilizzatore finale il compito di aggiungere tutto lo stile CSS. E così ho fatto :-)

Qui di seguito il listato della slideshow personalizzata. Iniziamo con la testata del file html (standard), dove andremo ad inglobare jQuery:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Prova Slideshow</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
Proseguiamo con il cuore del codice:
<script>
$(function(){
    $('.slideshow img:gt(0)').hide();
    setInterval(function(){$('.slideshow :first-child').fadeOut(1500).next('img').fadeIn(1500).end().appendTo('.slideshow');}, 2400);
});
</script>
Io ho aggiunto la tempistica del fadeOut e del fadeIn (1 secondo e mezzo).
Applichiamo uno stile css adeguato ai nostri scopi: slideshow centrato, sovrapposizione delle immagini per evitare effetto "litigio", eccetera:
<style type="text/css">
body {
 margin:0;
 padding:0;
 text-align:center;
}
.slideshow {
 margin:0 auto;
 padding:0;
 width:618px;
 height:246px;
 border:1px solid #eeeeee;
}
.slideshow img { 
 display:block;
 position:absolute;
}
</style>
E finiamo in bellezza con il codice html dello slideshow (e del resto della pagina)
</head>
<body>
 <div class="slideshow">
  <img src="1.jpg"/>
  <img src="2.jpg"/>
  <img src="3.jpg"/>
 </div>
</body>
</html>

mercoledì 13 luglio 2011

C# - intercettare la pressione dei tasti funzione in un form

Brevissimo tips su come intercettare la pressione di un tasto "speciale" (ad esempio, tasti funzione, oppure Alt, Canc eccetera) in un form C#.
Per prima cosa, dobbiamo impostare a true l'attributo KeyPreview del form stesso, come si nota da figura:

A questo punto dobbiamo associare all'evento KeyDown del form una determinata funzione:

private void FormCassa_KeyDown(object sender, KeyEventArgs e)
{
   //Intercetto il tasto F2
   if (e.KeyCode == Keys.F2) MessageBox.Show("F2 Premuto");
}
Chiaramente se voglio intercettare F5, basta confrontare il tasto premuto con Keys.F5 . Tutto qua :-)

mercoledì 13 ottobre 2010

C# - Dimensionare una finestra figlia adattandola al contenitore padre MDI

Inizio con questo post una serie di interventi relativi al liguaggio C#. Sono semplicemente dei "tips" e non degli articoli riguardanti le basi del C# :-)


Poco tempo fa, mi sono chiesto: data una finestra che contiene altre finestre (MDI, ossia una finestra utilizzata come sfondo di un'applicazione con interfaccia a documenti multipli, multiple document interface), come posso fare aprire una finestra figlio che automaticamente si adatta alla grandezza del padre?

Semplice come bere un bicchier d'acqua: basta posizionarsi nel codice relativo all'evento "Load" della finestra figlio e scrivere:

this.Dock = DockStyle.Fill;

Facile e veloce :-)

giovedì 26 novembre 2009

Image Zoom con MooTools

Ho trovato un interessante articolo per creare un effetto "zoom": per intenderci, quello con una miniatura da cui si sceglie, con il mouse, l'area da ingrandire e l'immagine zoomata che riflette la scelta precedente.
Nella figura sottostante è possibile vedere l'esempio concreto:
Il codice, che si basa sulla libreria MooTools, è scaricabile all'indirizzo: http://www.php-help.ro/mootools-12-javascript-examples/mootools-12-javascript-image-zoom/

giovedì 10 settembre 2009

Apt - informazioni e versione dei pacchetti

Un tips veloce veloce: per visualizzare la versione di un determinato pacchetto che ci apprestiamo ad installare via apt-get (per sistemi debian-based, tra cui ubuntu) basta un semplice:

apt-cache show nome-pacchetto

Ad esempio, per il pacchetto apache2 risulta:

$ apt-cache show apache2

Package: apache2

Priority: optional
Section: web
Installed-Size: 100
Maintainer: Ubuntu Core Developers

Original-Maintainer: Debian Apache Maintainers

Architecture: all
Version: 2.2.8-1ubuntu0.11
Depends: apache2-mpm-worker (>= 2.2.8-1ubuntu0.11) | apache2-mpm-prefork (>= 2.2.8-1ubuntu0.11) | apache2-mpm-event (>= 2.2.8-1ubuntu0.11)
Filename: pool/main/a/apache2/apache2_2.2.8-1ubuntu0.11_all.deb
Size: 45558
MD5sum: 987ba9930246a975044ff9424381808b
SHA1: 1a64fada82c96c165b0b5929a0bb1cf3d4b31c20
SHA256: 1e48402e87d17cea64dfba4ecaf40c31c22e98a40f44d1771157de534af70f3d
Description: Next generation, scalable, extendable web server
Apache v2 is the next generation of the omnipresent Apache web server. This
version - a total rewrite - introduces many new improvements, such as
threading, a new API, IPv6 support, request/response filtering, and more.
Homepage: http://httpd.apache.org/
Bugs: https://bugs.launchpad.net/ubuntu/+filebug
Origin: Ubuntu
Task: lamp-server

Package: apache2
Priority: optional
Section: web
Installed-Size: 100
Maintainer: Ubuntu Core Developers

Original-Maintainer: Debian Apache Maintainers

Architecture: all
Version: 2.2.8-1
Depends: apache2-mpm-worker (>= 2.2.8-1) | apache2-mpm-prefork (>= 2.2.8-1) | apache2-mpm-event (>= 2.2.8-1)
Filename: pool/main/a/apache2/apache2_2.2.8-1_all.deb
Size: 43978
MD5sum: b844d8196f279fa7d46007b2e8e862a9
SHA1: d09cb634bec34627415098f3b79e7a7f186ed8f5
SHA256: 62d235c87f7be5006099f803261fb6f7a77778e4601004f963c5e66695321bd9
Description-it: Web server di prossima generazione, scalabile ed estendibile
Apache v2 è la nuova versione dell'onnipresente server web Apache. Questa versione, totalmente riscritta, introduce parecchi nuovi miglioramenti come il threading, una nuova API, supporto IPv6, filtro richiesta/risposta e molto altro.
Homepage: http://httpd.apache.org/
Bugs: mailto:ubuntu-users@lists.ubuntu.com
Origin: Ubuntu
Task: lamp-server