[HowTo] Erweiterter USB-Printserver, insbesondere Drucker ein und aus schalten

RalfFriedl

IPPF-Urgestein
Mitglied seit
22 Apr 2007
Beiträge
12,343
Punkte für Reaktionen
1
Punkte
0
Im [THREAD=232493]Thread über steuerbare Steckdosenleisten[/THREAD] kam die Idee auf, [POST=1783099]einen Drucker einzuschalten, wenn ein Druckjob an die Box kommt[/POST].

Hier erstmal ein einfacher Ansatz dazu.
Im Prinzip muss so ein Printserver nicht viel tun. Auf eingehende Verbindungen warten, diese annehmen und die Daten an den Drucker weiter geben.
Das kann man so machen:
Code:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>


int
main (int argc, char **argv)
{
  int opt;
  u_short port = 9100;
  char const *device = "/dev/usblp0";
  char const *logfile = "/dev/stdout";
  int fd_listen;
  int ret;
  int int_val;
  struct sockaddr_in sin;

  while ((opt = getopt (argc, argv, "d:p:c:h")) != EOF)
    switch (opt) {
    case 'd':
      device = optarg;
      break;
    case 'p':
      port = atoi (optarg);
      break;
    case 'c':
      logfile = optarg;
      break;
    }
  fd_listen = socket (PF_INET, SOCK_STREAM, 0);
  int_val = 1;
  ret = setsockopt (fd_listen, SOL_SOCKET, SO_REUSEADDR, &int_val, sizeof (int_val));
  sin.sin_family = AF_INET;
  sin.sin_port = htons (port);
  sin.sin_addr.s_addr = INADDR_ANY;
  ret = bind (fd_listen, (struct sockaddr *)&sin, sizeof (sin));
  ret = listen (fd_listen, 1);
  ret = fork ();
  if (ret < 0) {
    perror ("fork");
    exit (1);
  }
  if (ret > 0)
    exit (0);
  setsid ();
  for (;;) {
    int fd_accept, fd_printer;
    char buf[0x1000];

    fd_accept = accept (fd_listen, NULL, NULL);
    ret = system ("printserv-job start");
    fd_printer = open (device, O_WRONLY);
    while ((ret = read (fd_accept, buf, sizeof (buf))) > 0) {
      write (fd_printer, buf, ret);
    }
    close (fd_printer);
    close (fd_accept);
    ret = system ("printserv-job end");
  }
}
Bei AVM gibt es dafür gleich 4 Prozesse, ich weiß nicht, warum die das so kompliziert gemacht haben. Die Optionen sind mit Absicht kompatibel zu denen von AVM gewählt. Man könnte/sollte noch an einigen Stellen auf Fehler prüfen, außerdem geht das Programm nicht von selbst in den Hintergrund, aber das Programm sollte so funktionieren. Ich habe aber keinen USB-Drucker an der Box, um das zu testen.
Vor und nach jedem Druckauftrag wird printserv-job aufgerufen, einmal mit start und einmal mit end als Parameter. Bei Start könnte man den Drucker einschalten und 30 Sekunden warten, oder wie lang auch immer der Drucker braucht. Bei End kann man festhalten, dass der Druckauftrag zu Ende ist, und dafür sorgen, dass der Drucker irgendwann ausgeschaltet wird, wenn keine neuen Aufträge kommen.

Bei AVM wird auch noch eine Datei /var/log/printer_status erstellt. Wenn jemand herausfindet, was dort zu verschiedenen Gelegenheiten drin steht, kann man das auch ergänzen.
 
Zuletzt bearbeitet:
Moin Ralf,

klasse, wie schnell Du ein erstes Proggi zum Umsetzen gestrickt hast. Ich bin jetzt kein Fachmann, aber soweit ich das jetzt verstehe, hast Du den Port und Vorlauf- sowie Nachlaufzeit fest in das Programm codiert? Wie wäre es, quasi per Aufrufparameter oder per cfg diese Dinge zu übergeben, da
  • der Port abhängig davon ist, wieviele Drucker angeschlossen sind (oder sich auch aus anderen Gründen evtl. wieder mal verschieben könnte im Trunk, auch wenn es von AVM kommt ...
  • jeder Drucker durchaus seine eigene Vorlaufzeit benötigt, und sie somit individuell optimiert werden kann
  • jeder, der einen Drucker auf diese Art steuern möchte, vlt. individuell festlegen möchte, wie lange nach dem Ende sein Drucker eingeschaltet bleibt
  • möglicherweise die Aufrufparameter zur sispmctl geändert werden sollen, da ja mehrere Schaltleisten angeschlossen sein können sowie der Drucker zudem auch an unterschiedlichen Steckdosen je Leiste sitzen kann

Auf jeden Fall erstmal ein großes Danke für Deine schnellen Bemühungen!

Gruß, Christoph
 
Der Port wird über den Parameter -p gewählt, wie beim AVM Programm.
Der Drucker wird über den Parameter -d gewählt, wie beim AVM Programm.
Die Logdatei wird über den Parameter -c gewählt, wie beim AVM Programm, obwohl dies im jetzigen Programm nicht verwendet wird.
Wie schon geschrieben, ist es Absicht, dass die Optionen identisch gewählt sind.
Unabhängig davon werden für alle Optionen Vorgaben gesetzt, die in den meisten Fällen sinnvoll sind.

Wenn man noch einbauen würde, dass das Programm selbst in den Hintergrund geht, könnte man es direkt als Ersatz für das AVM Programm verwenden. Die Datei /var/log/printer_status könnte man auch noch entsprechend aktualisieren, wenn jemand mit einem USB-Drucker an der Box mal schaut, was in der Datei drinsteht, wenn der Drucker ausgeschaltet ist, eingeschaltet bzw. gerade am Drucken, ohne Papier usw.

Vorlauf- sowie Nachlaufzeit ist weder fest noch unfest im Programm. Das Programm ruft vor und nach einem Druck "printserv-job" auf. Damit das etwas bringt, muss man ein Programm/Skript mit dem Namen erstellen, in dem man die beabsichtigten Aktionen ausführt.
 
Die könnte man verwenden.
Man kann es aber auch von Hand machen:
Code:
    ret = fork ();
    if (ret < 0) {
      perror ("fork");
      exit (1);
    }
    if (ret > 0)
      exit (0);
    setsid ();
 
Hallo Ralf,

also wenn ich das jetzt richtig verstanden habe, müßte Dein Printserver der von AVM ergänzt werden und als permanenter Dienst eingebunden werden, damit via z.B. /var/log/printer_status (sofern dort vernünftige Stati abgelegt werden ...) überprüfen kann, ob tatsächlich ein Drucker vorhanden bzw. eingeschaltet ist. Sofern nicht, schaltet Dein PS den Drucker via Leiste ein, um danach den Druckjob an den eigentlichen PS (oder ggf. auch den Drucker) zu übergeben.

Wenn ich den AVM-Mechanismus richtig verstehe, wird doch bei denen der PS vom Hotplug gestartet, sobald an USB ein Drucker angeschlossen wird. Könnte man da nicht mit der /var/log/printer_id (wird, soweit ich weiß, angelegt, wenn ein Drucker vorhanden, mit der Druckerbezeichnung, wie sie auch im Webfrontend gezeigt wird)? Da könnte dann auch überwacht werden, wenn jemand mehrere Drucker angeschlossen hat und steuern will, ob der richtige bereits an ist ...

Voraussetzung ist allerdings ein USB-Drucker - ob es mit einer bidirektionalen USB2LPT-Verbindung geht, kann ich im Moment nicht testen, mein Adapter geht nur in eine Richtung :( (da gibt es auch keine Stati in printer_status außer 0, und in der printer_id steht die Bezeichnung des Adapters ...)

Gruß, Christoph
 
Dieses Programm ist nicht als Ergänzung, sondern als Ersatz für das Programm von AVM gedacht. Insbesondere mit dem fork aus #5 geht das Programm auch selbst in den Hintergrund. Ein gleichzeitiger Betrieb mit dem AVM Programm ist weder notwendig noch möglich. Es wäre jedoch auch denkbar, ein Programm zu erstellen, das zusätzlich zum AVM Programm läuft und die Daten an dieses Programm weiter gibt.

Bekannte Unterschiede zum AVM Programm:
Das Programm aktualisiert nicht die Datei /var/log/printer_status. Hauptsächlich weil ich nicht weiß, was man da rein schreiben kann und was es bringt. Bei mir steht in der Datei "4" drin.
Das Programm öffnet nicht zusätzlich den Port 9101. Auch hier weiß ich nicht, worauf der reagiert und wofür der gut ist.
Kann jemand, der einen USB Drucker angeschlossen hat, mal herausfinden, was auf dem Port 9101 passiert?

Die Datei /var/log/printer_id kommt von /usr/share/ctlmgr/libctlusb.so und nicht von printserv.
 
Hi,
ich habe inzwischen auch so eine steuerbare Steckdosenleiste von Gembird und bin an der printserver-Lösung interessiert.

Der Inhalt von /var/log/printer_status äußert sich bei mir folgendermaßen im AVM WebIF unter Heimnetz -> USB-Geräte:
Code:
printer_status  Anzeige im AVM WebIF
0               Status: Bereit
1               Status: Beim Drucken
2               Status: Bitte Papier einlegen
3               Status: Allgemeiner Fehler
4 und höher     Status nicht verfügbar

Wenn ich /var/log/printer_status ändere, wird sie innerhalb weniger Sekunden wieder überschrieben.
 
Hi Ralf,

ich habe dein Programm mal getestet (ohne fork) und folgendes festgestellt:
Code:
    fd_printer = open (device, O_WRONLY);
    ret = system ("printserv-job start");
sollte in umgekehrter Reihenfolge aufgerufen werden, denn man kann den Drucker erst öffnen wenn er eingeschaltet ist :)

Und der Drucker schaltet immer nach einen Job (und Wartezeit) ab, auch wenn schon der nächste Job ansteht. Man müsste also in printserv oder printserv-jobs prüfen ob ein neuer Druckjob ansteht und das Abschalten nach der Wartezeit verwerfen.


Hier mein printserv-job
Code:
#! /bin/sh
# switch printer on/off 
# using SIS-PM power strip

# device number
SIS_DEVICE=0
# plug number
SIS_PLUG=1
# waiting time after switching on (let printer boot)
WAIT_START=30
# waiting time before switching off (wait for another job)
WAIT_END=30


case $1 in
start)
	echo "[printserv-job] Switching on plug $SIS_PLUG on device $SIS_DEVICE..."
	sispmctl -d $SIS_DEVICE -o $SIS_PLUG
	echo "[printserv-job] done"
	echo "[printserv-job] Waiting $WAIT_START seconds..."
	sleep $WAIT_START
	;;
end|stop)
	echo "[printserv-job] Waiting $WAIT_END seconds..."
	sleep $WAIT_END
	echo "[printserv-job] Switching off plug $SIS_PLUG on device $SIS_DEVICE..."
	sispmctl -d $SIS_DEVICE -f $SIS_PLUG
	echo "[printserv-job] done"
	;;
*)
	echo "[printserv-job] start or end expected"
	;;
esac
 
Den Drucker zuerst einzuschalten ist sicher eine gute Idee.

Das Skript sollte etwas flexibler sein, wenn man mehrere Druckjobs nacheinander drucken will. Insbesondere sollte es nicht warten und dann den Drucker grundsätzlich ausschalten, denn in der Wartezeit ist der Printerserver nicht ansprechbar.

Code:
#! /bin/sh
# switch printer on/off 
# using SIS-PM power strip

# device number
SIS_DEVICE=0
# plug number
SIS_PLUG=1
# waiting time after switching on (let printer boot)
WAIT_START=30
# waiting time before switching off (wait for another job)
WAIT_END=30

FILE_START=/tmp/printserv_start
FILE_END=/tmp/printserv_end
FILE_TMP=/tmp/printserv_tmp

case $1 in
start)
	touch $FILE_START
	while ! ln $FILE_START $FILE_TMP 2> /dev/null; do
		sleep 1
	done
	# Wenn Drucker noch nicht an ...
	if test ! -e $FILE_END; then
		echo "[printserv-job] Switching on plug $SIS_PLUG on device $SIS_DEVICE..."
		sispmctl -d $SIS_DEVICE -o $SIS_PLUG
		echo "[printserv-job] done"
		echo "[printserv-job] Waiting $WAIT_START seconds..."
		sleep $WAIT_START
	fi
	rm $FILE_TMP
	;;
end|stop)
	touch $FILE_END
	(
	echo "[printserv-job] Waiting $WAIT_END seconds..."
	sleep $WAIT_END
	if ln $FILE_START $FILE_TMP 2> /dev/null && test $FILE_START -ot $FILE_END; then
		echo "[printserv-job] Switching off plug $SIS_PLUG on device $SIS_DEVICE..."
		sispmctl -d $SIS_DEVICE -f $SIS_PLUG
		echo "[printserv-job] done"
		# Wartezeit, damit der Drucker nicht sofort wieder eingeschaltet wird.
		sleep 1
		rm -f $FILE_END $FILE_TMP
	fi
	) &
	;;
*)
	echo "[printserv-job] start or end expected"
	;;
esac
Damit wird nach Beenden des Jobs im Hintergrund 30 Sekunden gewartet, das Skript wird jedoch beendet und der Printserver ist bereit für den nächsten Job. Die Zeit, zu der der Job beendet wurde, wird in FILE_END festgehalten.

Falls in den nächsten 30 Sekunden ein neuer Job gestartet wird, wird dessen Zeit in FILE_START festgehalten. Da dies nach dem Beenden des ersten Jobs ist, ist in dem Test FILE_START neuer als FILE_END, und der Drucker wird nicht ausgeschaltet.

Umgekehrt wird beim Start auf die Existenz von FILE_END getestet. Diese Datei wird gelöscht, wenn der Drucker ausgeschaltet wird. Wenn die Datei noch existiert, muss der Drucker nicht eingeschaltet werden.

Die Datei FILE_TMP wird als Semaphore verwendet. Wenn die Datei existiert, wird der Drucker nicht ausgeschaltet, weil dann gerade ein neuer Druckauftrag gestartet wurde. Wenn andererseits die Datei existiert, wenn ein Druckauftrag gestartet werden soll, dann wird gerade der Drucker ausgeschaltet. In diesem Fall wird solange gewartet, bis die Datei nicht mehr existiert. Die Verzögerung nach dem Ausschalten dient dazu, dass der Drucker etwas Pause zwischen aus und einschalten hat. Manchen Geräten bekommt eine zu kurze Pause nicht, evtl. muss man die Zeit verlängern.
 
Danke Ralf, das sieht gut aus.
Ich teste es mal diese Woche und melde mich dann wieder.
 
Hey,

klasse, die Arbeiten von Euch smilie_op_010.gif
Jetzt müsste nur noch eine Möglichkeit angebunden werden, die Parameter z.B. per Config-File zu beeinflussen, ohne gleich den Quellcode neu übersetzen zu müssen ;)

Leider kann ich nicht coden, so daß das meine Möglichkeiten weit überschreitet ...

Gruß, Christoph
 
Hey,

oh, sorry, klar, hier kommen sie:

Code:
# device number
SIS_DEVICE=0
# plug number
SIS_PLUG=1
# waiting time after switching on (let printer boot)
WAIT_START=30
# waiting time before switching off (wait for another job)
WAIT_END=30

Somit müßte nicht jedes Mal der Quellcode beackert werden, wenn jemand andere Vorstellungen hat, oder das Gerät anderes Vorgehen verlangt.

Ein Laserdrucker zum Bsp. bzw. seine Fixiereinheit hat es nicht gerne, zu oft ein- und wieder ausgeschaltet zu werden - da sollte dann evtl. auch ein längerer Zeitraum des Wartens auf weitere Druckjobs einstellbar sein.
Zudem hängt es ja auch stark vom jeweiligen Drucker ab, wie lange nach dem Einschalten noch gewartet werden sollte.

auf welchem Port der Steckdose liegt der Drucker?

Welches Device (Steckdosenleiste) muß nicht unbedingt - ich nehme mal an, daß nicht viele einen Drucker via SISPM und Fritzbox steuern wollen und auch noch mehrere SISPM angeschlossen haben ;)

Letztlich macht es das ganze hinterher auch einfacher, wenn z.B. mal wieder ein neuer Drucker angeschlossen werden soll, weil der Alte wieder einmal seinen Dienst verweigert, da abgeraucht :D

Gruß, Christoph
 
Ich hab kein gcc auf der Box, so dass ich sowas direkt ändern könnte. :mrgreen:
 
Und ich dachte, Du hättest mal den GCC für die Box erstellt.

Aber der Ausschnitt ist aus einem Shell-Skript. Das C-Programm oben ist so aufgebaut, dass es nur ein Skript aufruft, dieses ist dann für die weiteren Aktionen zuständig.
 
Und inwieweit ist es einfacher, eine Konfigurationsdatei zu ändern als die Zeilen direkt in der Datei zu ändern?

Ich dachte da an User, die sich nicht auf Shellebene mit Telnet auf der Box bewegen ... Abgesehen davon, wenn man nach einem z.B. Jahr einen neuen Drucker bekommt, findet man solche Einstellungen in der Freetz-Oberfläche mit sicherheit schneller wieder als in den Tiefen der Box ;) Und dann kann man bestimmt mittels Webfrontend auch direkt diese Parameter in der Datei beackern, oder?

Gruß, Christoph
 
Auch, vorhin hat noch ein Config-File gereicht, jetzt soll es gleich ein Webfrontend sein?

Das Skript ist sowieso nur ein Beispiel dafür, was man mit den Start und Stop Aktionen anstellen kann.
 
Hey,

ich denke dabei ja nur an solche Nutzer, die halt nicht soo tief in allem drin stecken ;)

Gruß, Christoph
 
Holen Sie sich 3CX - völlig kostenlos!
Verbinden Sie Ihr Team und Ihre Kunden Telefonie Livechat Videokonferenzen

Gehostet oder selbst-verwaltet. Für bis zu 10 Nutzer dauerhaft kostenlos. Keine Kreditkartendetails erforderlich. Ohne Risiko testen.

3CX
Für diese E-Mail-Adresse besteht bereits ein 3CX-Konto. Sie werden zum Kundenportal weitergeleitet, wo Sie sich anmelden oder Ihr Passwort zurücksetzen können, falls Sie dieses vergessen haben.