Come continuare ad utilizzare una vecchia e costosa stampante non più compatibile con Windows 7/8/10

La stampante è uno di quei strumenti che cambi solo quando si rompe oppure… quando non è più compatibile con l’ultimo aggiornamento di Windows.

Ma spesso è frustrante dover cambiare una stampante solo perchè non più supportata dal nuovo sistema operativo, soprattutto se la stampante in questione possiede queste caratteristiche:

  1. funziona ed ha funzionato sempre benissimo
  2. è costata parecchio
  3. è di ottima qualità
  4. consuma poco inchiostro

Nel mio caso ho avuto a che fare con una stampante professionale da ufficio Develop 1650D. La stampante ha sempre funzionato egregiamente con WindowsXP, funzionava in alcuni casi ristretti su Windows7 ed un bel giorno ha smesso del tutto di funzionare dopo l’ultimo aggiornamento del suddetto sistema operativo.

529ef87ed1beb529d4026ee8ed4ab96c33430541

La stampante non ha porte Ethernet o Wifi. Ha una singola porta USB ed una porta parallela: ciò significa che deve essere collegata direttamente ad un computer.

Soluzione “semplice” è buttare la stampante da 2000 e rotti euro e comprarne una nuova: non ci piace.

 

Tentativo: utilizzo di un print server commerciale

Acquistare un print server commerciale sembrerebbe la soluzione ottimale: il print server funge da ponte tra la stampante e la rete LAN dell’ufficio scavalcando del tutto il problema del sistema operativo.

41NowUSrunL._SY300_

Purtroppo però tutti i print server in circolazione supportano solo alcuni modelli specifici di stampanti ed in genere solo quelle più recenti. Inutile dire che non andava bene per il modello Develop 1650D.

 

Tentativo: Macchina XP dedicata e condivisione stampante

Una soluzione alternativa è collegare un PC WindowsXP direttamente alla stampante e condividerla in rete. Questa soluzione non ha funzionato in quanto, sebbene la stampante fosse perfettamente visibile in rete dagli altri PC Windows7, le stampe non partivano a causa dell’incompatibilità tra i driver XP  e i PC utilizzatori (Windows7).

 

Soluzione finale: print server ad-hoc

La soluzione è consistita nel disaccoppiare utenti Windows7 e stampante tramite l’utilizzo di una stampante virtuale PDF. Un PC WindowsXP dedicato gestisce la stampante reale via USB e al tempo stesso condivide in rete una stampante virtuale PDF. I PDF (le stampe degli utenti) vengono creati sul disco della macchina XP all’interno di una directory specifica che funge da coda di stampa. Un programma ad-hoc realizzato in .NET C# (printserver.exe) e residente nella macchina XP provvede a scodare i file PDF dalla directory e a stamparli sulla stampante reale.  Il programma è avviato automaticamente e non richiede alcun intervento manuale per funzionare.

Screen shot 2015-11-02 at 5.38.30 PM

Lato utente il meccanismo è del tutto trasparente: in rete è visibile solo la stampante virtuale. Ogni comando di stampa verso la stampante virtuale si traduce in una stampa vera e propria sulla Develop 1650D.

Screen shot 2015-11-02 at 5.38.15 PM

Il tutto realizzato su un comunissimo PC desktop privo di mouse, tastiera e schermo. L’unica cosa da ricordare è accendere il printserver la mattina prima di utilizzare la stampante!

printserver

 

Aspetti interessanti della soluzione

Allo stato attuale i PDF vengono scodati, stampati e quindi cancellati; Nessuno però vieta di copiare i PDF in una ulteriore cartella di backup in modo da mantenere uno storico digitale delle stampe.

E’ possibile estendere il meccanismo ad altre stampanti obsolete e collegarle tutte alla stessa macchina XP; l’unico limite è il numero di porte USB e parallele disponibili (di solito 4 o 6 USB e 1 o 2 parallele).

La stampante funzionerà anche con Windows 8, Windows 10 e anche sistemi Linux e Mac.

Forza quattro per Android, MVC + SurfaceView

Ho realizzato una nuova app per Android: Forza quattro (in inglese connect4), il mitico gioco a turno dove vince chi riesce ad allineare per primo 4 o più pedine dello stesso colore.

Realizzata per sperimentare il rendering SurfaceView+Canvas e il pattern MVC in ambiente Android.

device-2015-10-03-112816

device-2015-09-06-160459

 

E’ possibile giocare contro lo smartphone selezionando uno dei 32 livelli di difficoltà. L’intelligenza artificiale è basata sull’algoritmo minimax con alcune ottimizzazioni per renderlo più veloce:

Il gioco include:

  • Effetti sonori tramite SoundPool
  • Musica di sottofondo tramite classe MediaPlayer
  • Rendering software tramite SurfaceView e Canvas (su thread separato, come spiegato qui)
  • Effetto caduta dei gettoni

Lo trovate qui in google play!

googleplay

L’ambiente Android si sa, è fortemente event-driven. L’approccio tramite pattern classico MVC è comunque fattibile: ho mappato il pattern alle seguenti componenti Android (non necessariamente la soluzione migliore!):

  • Model: sostanzialmente lo stato della scacchiera, un array bidimensionale.
  • View: classe SurfaceView che legge i dati dal modello e fa il rendering su schermo
  • Controller: activity Android che legge l’input utente e coordina Model e View assieme (più una terza classe Sound responsabile della musica ed effetti sonori).

20150913_170934

L’applicazione nel suo complesso richiede 3 thread: quello principale dell’activity stessa (UI), quello dentro la classe View che legge lo stato di gioco e aggiorna lo schermo e l’ultimo quello che incorpora l’algoritmo minimax per la scelta della mossa da parte del computer.

La caduta dei gettoni è realizzata con la classica equazione del moto uniformemente accelerato: s = 1/2 * g * t^2 dove t è il tempo espresso in tick millisecondi e g è l’accelerazione espressa in pixel / ms /ms.

Serializzazione JSON in C#

JSON è un formato per l’interscambio dei dati tra applicazioni client-server,

in C# possiamo utilizzare:

Json.Encode()
Json.Decode()

che troviamo nel namespace System.Web.Helpers

Con queste due funzioni possiamo rispettivamente serializzare e deserializzare un qualsiasi oggetto C# da e verso JSON.
La serializzazione standard prevede la creazione di stringhe JSON in forma di coppie chiave-valore.

Ad esempio, immaginiamo di avere la seguente classe C# che memorizza lo stato di gioco corrente in un ambiente multiplayer (ad esempio da dover inviare a tutti i client connessi ogni tot millisecondi):

private class GameSnapshot
{
            public class EnemyPosition
            {
                public double x;
                public double y;
                public double direction;
                public int energy;
            }

            public string playerName;
            public double playerXpos;
            public double playerYpos;
            public int lives;
            public int level;
            public int points;
            public List<EnemyPosition> enemiesPositions;
            public DateTime timestamp;
}

La lista enemiesPositions contiene le posizioni degli avversari, gli altri parametri sono quelli relativi al giocatore (posizione, stato, vite rimanenti etc..).
Una plausible conversione JSON dell’oggetto potrebbe essere la seguente:

{
  "playerName": "unnamed player",
  "playerXpos": 12.124354328,
  "playerYpos": 3.8943532434,
  "lives": 4,
  "level": 12,
  "points": 81000,
  "enemiesPositions": [
    {
      "x": 0.17568866497636246,
      "y": 0.8758737812172034,
      "direction": 0.5097326890145115,
      "energy": 1102696000
    },
    {
      "x": 0.26358662371690694,
      "y": 0.8292919941382911,
      "direction": 0.26260223484719275,
      "energy": 2123401755
    },
    {
      "x": 0.7504166782602746,
      "y": 0.5961088224296034,
      "direction": 0.5173756976227163,
      "energy": 1782593816
    },
    {
      "x": 0.4766914893298836,
      "y": 0.8596706198806272,
      "direction": 0.1079285652879293,
      "energy": 1907324897
    },
    {
      "x": 0.5031359705622941,
      "y": 0.13449626934458328,
      "direction": 0.21105524069213086,
      "energy": 1125504690
    }
  ],
  "timestamp": "/Date(1437732572430)/"
}

Per un totale di 923 byte, quasi 1K di dati da trasmettere. Non molti, ma dobbiamo considerare che i dati potrebbero dover essere trasmessi molte volte al secondo e che i client connessi potrebbero essere molti.

Di default la serializzazione prevede la creazione di coppie chiavi-valore dove chiave è il nome della variabile (es. “playerName”) e valore il suo valore corrispondente (es. “unnamed player”). Per una codifica più efficiente possiamo eliminare tutte le chiavi e lasciare solo i valori: la posizione relativa dei valori nella struttura dati JSON è sufficiente a ricostruire completamente (deserializzare) l’oggetto originale.

A tal fine possiam utilizzare il tipo dati dynamic, introdotto in C# 4.0. Dynamic è un tipo dati appunto dinamico, il compilatore ignora il tipo reale della variabile o lo pospone all’esecuzione. Creando una lista di dynamic possiamo creare sequenze di oggetti non omogenei tra loro (liste o array di tipi diversi) ognuno dei quali è anche anomimo (non ha un nome esplicito, è solo un elemento della lista).

La classe di prima diventa semplicemente una lista di tipi dynamic:

var gameShapshot = new List<dynamic>();

Possiamo aggiungere a questa lista (metodo .Add()) qualsiasi oggetto: un int, una striga, un DateTime, liste di altri oggetti e cosi’ via. Ad esempio, possiamo creare un oggetto dynamic che contiene le stesse informazioni della classe GameSnapshot vista in precedenza.

var gs = new List<dynamic>();

gs.Add("unnamed player");
gs.Add(12);
gs.Add(4);
gs.Add(81000);
gs.Add(DateTime.Now);
gs.Add(12.124354328);
gs.Add(3.8943532434);
var enemiesPositions = new List<dynamic>();

for (var i = 0; i < 5; i++)
{
   var enemyPos = new List<dynamic>();
   enemyPos.Add(rnd.NextDouble());
   enemyPos.Add(rnd.NextDouble());
   enemyPos.Add(rnd.NextDouble());
   enemyPos.Add(rnd.Next());

   enemiesPositions.Add(enemyPos);
}
gs.Add(enemiesPositions);

String snapShot = Json.Encode(gs);
System.Console.WriteLine(snapShot);

Abbiamo ora un oggetto C# che contiene le stesse informazioni (sia in valore che in struttura) ma la serializzazione JSON risulta molto più snella:

[
  "unnamed player",
  12,
  4,
  81000,
  "/Date(1437732515952)/",
  12.124354328,
  3.8943532434,
  [
    [
      0.5849644861114046,
      0.9460975015284948,
      0.9270875434982998,
      1612467919
    ],
    [
      0.5084204606285414,
      0.6327498385835205,
      0.7234090821460863,
      784917789
    ],
    [
      0.8641532142014025,
      0.2696795273896677,
      0.21300438335770946,
      1166561007
    ],
    [
      0.6842819497381719,
      0.10087060700211237,
      0.3484976507483505,
      686316417
    ],
    [
      0.27328705707252354,
      0.2811392360744715,
      0.3885680466837101,
      1938705260
    ]
  ]
]

Occupa 650 byte, poco più della metà. Riducendo i decimali di default (spesso non serve tutta quella risoluzione, ovviamente dipende dal contesto) arriviamo a circa 400 byte. A parità di banda significa raddoppiare il numero di client connessi contemporaneamente, raddoppiare il rate di invio dei dati o una combinazione dei due.

Per lunghi array di valori numerici, ad esempio per dati relativi a grafici la serializzazione senza nomi risulta particolarmente efficiente e si adatta bene a linguaggi loosely typed come Javascript e PHP.

Ad esempio:

[
   [
      0.5849644861114046,
      0.9460975015284948,
      0.9270875434982998,
      0.8641532142014025
    ],
    [
      0.5084204606285414,
      0.6327498385835205,
      0.7234090821460863,
      0.2811392360744715
    ],
    [
      0.8641532142014025,
      0.2696795273896677,
      0.21300438335770946,
      0.2696795273896677
    ],
    [
      0.6842819497381719,
      0.10087060700211237,
      0.3484976507483505,
      0.9270875434982998
    ],
    [
      0.27328705707252354,
      0.2811392360744715,
      0.3885680466837101,
      0.6327498385835205
    ],
    [
      0.8641532142014025,
      0.2696795273896677,
      0.21300438335770946,
      0.2696795273896677
    ]
]

Ho trovato la soluzione con i dynamic abbastanza utile e veloce da implementare ma credo ne esistano altre. BSON per esempio? Oppure Message Pack.