Ralliart Posted August 22, 2010 Report Share Posted August 22, 2010 Wydawałoby się, że sprawa jest prosta i oczywista, bo jest przecież open sourcowy projekt pod nazwą HPLIP (HP Linux Inkjet Driver Project), który wspiera praktycznie wszystkie drukarki tego producenta. I faktycznie dzięki tym sterownikom instalacja drukarek i skanerów to praktycznie formalność. Z jednym wyjątkiem. Urządzenia klasy MFP (Multi Function Printer) tego producenta z dolnej półki nie posiadają zaawansowanych funkcji skanera sieciowego. Co mam przez to na myśli? Otóż HP w swojej wspaniałomyślności olało najtańszy sprzęt i poskąpiło w firmware opcji skanowania do e-mail. Sprawia to nieliche kłopoty w rozwiązaniach biznesowych. Pomimo posiadania magicznego guzika "Skanuj do" nasze urządzenie testowe HP 3390 nie wykona tego bez odpowiedniej konfiguracji. Oczywiście pod Windowsem sprawa jest w miarę oczywista, bo producent udostępnił aplikację do obsługi skanowania (notabene - instaluje się cały pakiet o wadze ~250MB!), ale do przejrzystości mu daleko... Niemniej po krótkiej walce skaner będzie działał prawidłowo, a skany lądują tam gdzie trzeba. Do czasu. Problemy są zasadniczo dwa. Pierwszy: brak oprogramowania do skanowania po sieci dla Windowsa 2003 serwer - jak się domyślacie rozwiązanie domenowe w tym momencie leży, bo trzeba utrzymywać jeden komputer z Windowsem XP który obsługuje skaner. Drugi: w przypadku gdy komputer ma adres sieciowy z DHCP co 30 dni skaner gubi konfigurację - jednym słowem kicha. Dlatego zdecydowaliśmy się na rozwiązanie oparte na linuxa. Jak już zaznaczyłem na wstępie sama instalacja przebiegła bezproblemowo - drukarka i skaner działa. Teraz czas na gwóźdź programu czyli "Skanuj Do". Oczywiście HPLIP nie wspiera tego rozwiązania (w systemach typu unix nie ma co liczyć na rozwiązania typu od a do z), ale jest nadzieja na rozwiązanie problemu. Krótkie poszukiwanie w sieci i mamy coś takiego: Rozwiązanie #1 http://rende.se/index.php?n=Main.ScanToFolder, Scan-to folder on Linux This function is simple to use - put something in the scanner and push the "Scan to" button and then the "Start Scanning" button. The scanner scans and sends the result to a file in a folder in a server on your local network. You can also have multiple folders on the server that you can select on the printer before scanning. Hidden pages By using Wireshark I listened to the traffic on the net when using this function from a windows machine. The result was much simpler that I suspected. The printer is a web server that you can access with a web browser - this is documented in the manual. But there are some undocumented web addresses that can be used to detect the Scan-to-button and other things. Here are som examples: My printer has ip address 192.168.0.43, so change to the ip of your printer in these examples. http://192.168.0.43/hp/device/notifications.xml - returns an xml document with the name of the selected scan-to-folder, or empty if no selected scan. http://192.168.0.43/hp/device/info_scanto_destinations.xml - xml that lists the destinations that can be selected on the printer when doing a "Scan to". There are also addresses for changing things: http://192.168.0.43/hp/device/set_config.html - post a command to this address to set various parameters - like adding or removing Scan-to destinations. (see below for more protocol details) Destination list utility I wrote a utility bash script for maintaining the list of scan-to destinations in the printer. ./scanto_destination -l Lists all destinations in the printer. ./scanto_destination -a <destname> Adds a new destination name, where <destname> can be name or host:name. ./scanto_destination -d <destname> Deletes a destination name. Note that the destination list is stored in the printer and is cleared when the printer is restarted. Example output on my system: ./scanto_destination -l gong2:color150 gong2:color300 gong2:color75 scanto_destination: #!/bin/bash # command to maintain the scan-to destination list in the HP Color LaserJet 2840 printer if [ "$1" == "-a" ]; then name=$2 [[ ! "$name" =~ "" ]]&& name="$HOSTNAME:$name" postdata="AddScanToDest_1=127.0.0.1-$HOSTNAME%5e${name/:/%3a}%5eDestFolder" wget -q -O - --post-data="$postdata" http://192.168.0.43/hp/device/set_config.html elif [ "$1" == "-d" ]; then name=$2 [[ ! "$name" =~ "" ]]&& name="$HOSTNAME:$name" postdata="RemoveScanToDest_1=${name/:/%3a}" wget -q -O - --post-data="$postdata" http://192.168.0.43/hp/device/set_config.html elif [ "$1" == "-l" ]; then wget -q -O - http://192.168.0.43/hp/device/info_scanto_destinations.xml | sed -n '/<DeviceDisplay>/s/</*DeviceDisplay>//gp' else echo 'usage: scanto_destination -l -a <destname> -d <destname> for listing adding and deleting destinations' fi A Linux scanner server The following bash script is a loop that checks each 5 seconds if the scan-to button has been pressed, and then makes a scan according to the destination selected on the printer. The scan is performed by a script named after the destination. the scan result is placed in the scans folder and the scan command scripts are in the scan-command folder. The server also checks if the scan-to destinations in the printer are the same as the command files in the sca-command directory, and adds or deletes destinations in the printer if they don't match. scanloop: #!/bin/bash cd "`dirname $0`" set -o pipefail while true; do # if scan-to button pressed - run the command corresponding to the destination name name=`wget -q -O - http://192.168.0.43/hp/device/notifications.xml | egrep -o '<ScanToDeviceDisplay>(.*)</ScanToDeviceDisplay>' | sed -e 's/<ScanToDeviceDisplay>//' | sed -e 's/</ScanToDeviceDisplay>//' | sed -e 's/.*://'` && { if [ "$name" != "" ]; then if [ -x "scan-command/$name" ]; then (cd scans; "../scan-command/$name" >/dev/null 2>&1) chmod a+r scans/* fi fi } # update scan-to destinations in printer if necessary ./scanto_destination -l | sed 's/.*://' >/tmp/printer-dests && ls scan-command >/tmp/command-dests && diff /tmp/printer-dests /tmp/command-dests | egrep '<|>' | sed 's#<#./scanto_destination -d#;s#>#./scanto_destination -a#' | bash sleep 5 done scan-command/color75: hp-scan -n -r75 scan-command/color150: hp-scan -n -r150 scan-command/color300: hp-scan -n -r300 Protocol details http://192.168.0.43/hp/device/notifications.xml - no Scan-to in progress The following document is returned: <?xml version="1.0" encoding="UTF-8"?> <Notifications> <ScanToNotifications> <ScanToDeviceDisplay/> <ScanToHostID/> <ScanToNotSetup>0</ScanToNotSetup> <ADFLoaded>0</ADFLoaded> </ScanToNotifications> <StartScanNotifications> <StartScan>0</StartScan> <ADFLoaded>0</ADFLoaded> </StartScanNotifications> <FaxNotifications> <FaxReceiveFunction>1</FaxReceiveFunction> <FaxPrinting>0</FaxPrinting> <FaxMasterHostID/> <FaxUploadState>1</FaxUploadState> <FaxLogChangeIndicator>0</FaxLogChangeIndicator> <FaxForwardEnabled>0</FaxForwardEnabled> <FaxForwardNumber/> </FaxNotifications> </Notifications> http://192.168.0.43/hp/device/notifications.xml - Scan-to in progress The selected Scan-to destination is tpw:TPW. <?xml version="1.0" encoding="UTF-8"?> <Notifications> <ScanToNotifications> <ScanToDeviceDisplay>tpw:TPW</ScanToDeviceDisplay> <ScanToHostID>127.0.0.1-tpw</ScanToHostID> <ScanToNotSetup>0</ScanToNotSetup> <ADFLoaded>0</ADFLoaded> </ScanToNotifications> <StartScanNotifications> <StartScan>0</StartScan> <ADFLoaded>0</ADFLoaded> </StartScanNotifications> <FaxNotifications> <FaxReceiveFunction>1</FaxReceiveFunction> <FaxPrinting>0</FaxPrinting> <FaxMasterHostID/> <FaxUploadState>1</FaxUploadState> <FaxLogChangeIndicator>0</FaxLogChangeIndicator> <FaxForwardEnabled>0</FaxForwardEnabled> <FaxForwardNumber/> </FaxNotifications> </Notifications> http://192.168.0.43/hp/device/info_scanto_destinations.xml The scan-to destinations are tpw:TPW and tpw:TPW2. <?xml version="1.0" encoding="UTF-8"?> <ScanToDestinations> <MaxScanToDestinations>30</MaxScanToDestinations> <AvailableScanToDestinations>28</AvailableScanToDestinations> <ScanToDestinationList> <ScanToDestination DestinationID="0"> <HostID>127.0.0.1-tpw</HostID> <DeviceDisplay>tpw:TPW</DeviceDisplay> <ScanToType>DestFolder</ScanToType> </ScanToDestination> <ScanToDestination DestinationID="1"> <HostID>127.0.0.1-tpw</HostID> <DeviceDisplay>tpw:TPW2</DeviceDisplay> <ScanToType>DestFolder</ScanToType> </ScanToDestination> </ScanToDestinationList> </ScanToDestinations> http://192.168.0.43/hp/device/set_config.html To add the scan-to destination tpw:TPW2, POST the following string to this address: AddScanToDest_1=127.0.0.1-tpw%5etpw%3aTPW2%5eDestFolder Make sure you have supplied the correct CONTENT-LENGTH header. In this case CONTENT-LENGTH:55. I'm not sure about the 127.0.0.1-tpw, but it appears to represent the ip and host name of the sender. To remove the scan-to destination tpw:TPW2, POST string: RemoveScanToDest_1=tpw%3aTPW2 Szacunek dla gościa, bo przy pomocy wiresharka namierzył pliki w których znajduje się konfiguracja skanera i modułów odpowiedzialnych za skanowanie do folderu. Niezła robota z małym ale: konfiguracja jest trochę enigmatyczna i mało opisana, po 5 godzinach błędów, testów i ciągłym zawieszani się drukarki musieliśmy się poddać. Rozwiązanie działa, ale jest maksymalnie kapryśne. Rozwiązanie #2 https://answers.launchpad.net/hplip/+question/35378 #!/usr/bin/perl -w # quick hack to # a) setup a single default scan-to destination on the HP after reboot # activate that destination when scan-to is pressed # # Only supports 1 simultaneous printer # requires apt-get install libwww-mechanize-perl # mkdir /var/log/scanner && chmod 777 /var/log/scanner # installation of hp-scan utility from hplip # # Any suggestions for improvements (I'm sure there are lots) # may be addressed to me care of launchpad article # [url]https://answers.launchpad.net/hplip/+question/35378[/url] # # Sometimes the hp-scan software locks up (has happened a couple of times over a couple of years). # on these occasions you must stop/restart this software. # # Copyright Saliya Wimalaratne 2008-2010 # # This file is free software; # you can redistribute it and/or modify it under the terms of the GNU # General Public License (GPL) version 2 as published by the Free Software Foundation. # # This software is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY of any kind. use strict; use WWW::Mechanize; use POSIX qw(strftime); my $cfg; # ip of printer, will read as argument $cfg->{'ip'} = shift(@ARGV) || '1.2.3.4'; $cfg->{'logfile'} = "/var/log/scanner/hp_scanto_output.log"; Log("INFO: $0: Started, using IP $cfg->{'ip'}", $cfg->{'logfile'}); # Feel free to alter any parameters to suit yourself # stuff hp-scan needs. $cfg->{'hp_scan'} = "/usr/bin/hp-scan -n -mcolor -r150 --size=a4"; # the ID of the scanning host that will show on the printer $cfg->{'hostid'} = "SERVER"; # the ID of the destination folder that will show on the printer $cfg->{'devicedisplay'} = $cfg->{'hostid'} . ":DOCS"; # where the output files go (needs to be writable by the user running the prog) $cfg->{'output_dir'} = "/space/unfiled"; # I wouldn't change these. $cfg->{'mech'} = WWW::Mechanize->new(autocheck => 0); $cfg->{'scan_destinations'}{'list'} = "http://" . $cfg->{'ip'} . "/hp/device/info_scanto_destinations.xml"; $cfg->{'scan_destinations'}{'modify'} = "http://" . $cfg->{'ip'} . "/hp/device/set_config.html"; $cfg->{'notifications'} = "http://" . $cfg->{'ip'} . "/hp/device/notifications.xml"; # the program my $counter = 0; while (1) { $counter++; # log every 10 mins if (($counter % 60) == 0) { $counter = 0; Log("MARK: $0 running", $cfg->{'logfile'}); } &setup_destinations($cfg); if (&poll_for_button_press($cfg)) { while (&document_loaded($cfg)) { &scan_to_file($cfg); sleep(5); } } sleep(5); } Log("INFO: $0 finished", $cfg->{'logfile'}); exit(0); sub document_loaded { my $lfn = "document_loaded"; my $cfg = shift; my $logstr = "$lfn: IP:$cfg->{'ip'}"; my $logfile = $cfg->{'logfile'}; my $uri = $cfg->{'notifications'}; # ask the printer if there is a document ready my $response = $cfg->{'mech'}->get( $uri ); if (!$response->is_success) { Log(sprintf("ERROR: $logstr: Can't fetch $uri: '%s' returning", $response->status_line), $logfile); return 0; } # print $response->content; if ($response->content =~ /<ADFLoaded>1</ADFLoaded>/) { Log("INFO: $logstr: Document feeder is loaded: scheduling scan", $logfile); return 1; } Log("WARNING: $logstr: Document feeder is NOT loaded: not scanning", $logfile); return 0; } sub scan_to_file { my $lfn = "scan_to_file"; my $cfg = shift; my $logstr = "$lfn: IP:$cfg->{'ip'}"; my $logfile = $cfg->{'logfile'}; # place to scan to? if (! -d $cfg->{'output_dir'}) { Log("WARNING: $lfn: Output directory '$cfg->{'output_dir'}' doesn't exist: attempting to create", $logfile); if (system("/bin/mkdir -p $cfg->{'output_dir'}") != 0) { Log("ERROR: $lfn: Output directory '$cfg->{'output_dir'}' cannot be created: $! exiting", $logfile); exit(1); } } # output filename my $timestr = strftime("%Y%m%d-%H%M%S", localtime( time())); my $fn = sprintf("scanned_%s.png", $timestr); my $full_fn = $cfg->{'output_dir'} . "/" . $fn; my $tmpfn = $full_fn; my $count = 0; my $max_count = 20; if (-e $tmpfn) { while(-e $tmpfn && ($count < $max_count)) { Log(sprintf("WARNING: $lfn: Destination file '%s' exists: trying another (%d/%d)", $tmpfn, $count, $max_count), $logfile); $tmpfn = sprintf("scanned_%s_%02d.png", $timestr, $count); $count++; } $full_fn = $tmpfn; } Log("INFO: $logstr: Scan-to-file requested: initiating scan to $full_fn", $logfile); my $syscmd = sprintf("%s --output=%s", $cfg->{'hp_scan'}, $full_fn); if (system($syscmd) != 0) { Log("ERROR: $logstr: System command '$syscmd' failed: returning", $logfile); return; } # success Log("INFO: $logstr: Scan-to-file successfully completed ($full_fn)", $logfile); } sub poll_for_button_press { my $lfn = "poll_for_button_press"; my $cfg = shift; my $logstr = "$lfn: IP:$cfg->{'ip'}"; my $logfile = $cfg->{'logfile'}; my $uri = $cfg->{'notifications'}; # ask the printer if the button's been pressed my $response = $cfg->{'mech'}->get( $uri ); if (!$response->is_success) { Log(sprintf("ERROR: $logstr: Can't fetch $uri: '%s' returning", $response->status_line), $logfile); return 0; } # print $response->content; if ($response->content =~ /<ScanToDeviceDisplay>$cfg->{'devicedisplay'}</ScanToDeviceDisplay>/) { Log("INFO: $logstr: Network scan requested for $cfg->{'devicedisplay'} scheduling scan", $logfile); return 1; } return 0; } sub setup_destinations { my $lfn = "setup_destinations"; my $cfg = shift; my $logstr = "$lfn: IP:$cfg->{'ip'}"; # list destinations my $uri = $cfg->{'scan_destinations'}{'list'}; my $logfile = $cfg->{'logfile'}; my $response = $cfg->{'mech'}->get( $uri ); if (!$response->is_success) { Log(sprintf("ERROR: $logstr: Can't fetch $uri: '%s' returning", $response->status_line), $logfile); return; } # print $response->content; if ($response->content =~ /<DeviceDisplay>$cfg->{'devicedisplay'}</DeviceDisplay>/) { # Log("DEBUG: $logstr: Destination '$cfg->{'devicedisplay'}' exists: returning", $logfile); return; } Log("INFO: $logstr: Destination '$cfg->{'devicedisplay'}' doesn't exist: adding", $logfile); # destination doesn't exist, set it up $uri = $cfg->{'scan_destinations'}{'modify'}; $response = $cfg->{'mech'}->post($uri, [ 'AddScanToDest_1' => sprintf("127.0.0.1-%s^%s^DestFolder", $cfg->{'hostid'}, $cfg->{'devicedisplay'}) ]); if (!$response->is_success) { Log(sprintf("ERROR: $logstr: Can't POST to $uri: '%s' returning", $response->status_line), $logfile); return; } Log("INFO: $logstr: Added destination $cfg->{'devicedisplay'}", $logfile); } sub Log { my $logstring = $_[0]; my $logfile = $_[1] || "/var/log/default-perl-logfile.log"; # logfile is "logfile-YYYY-MM"; if ($logfile !~ /-(d{4})-(d{2})$/) { # logfile is not in correct format, modify # extract all digits from logfile $logfile =~ tr/[0-9]//d; # delete last non-word characters $logfile =~ s/(W+)$//g; # append -YYYY-MM $logfile .= strftime("-%Y-%m", localtime( time() ) ); } chomp($logstring); open(LOG, ">>$logfile") or die "ERROR: Can't open logfile $logfile: $!n"; select(LOG); $|=1; select(STDOUT); my $logdate = strftime("%Y-%m-%d %H:%M:%S", localtime( time() ) ); print LOG "$logdate $logstringn" or die "ERROR:PerlLog: Cannot write to logfile $logfile: $!n"; close(LOG); } Po raz drugi szcunek - za skrypt z pełną obsługą błędów i logowaniem operacji. Zero kombinowania z katalogami - to mi się podoba. Tyle, że... 1. WWW::Mechanize - fajna funkcja, szkoda, że znalezienie dobrej paczki do Suse 11.2 zajęło 2 godziny... :/ ale takie są uroki linuksa 2. /<ADFLoaded>1</ADFLoaded>/ - i tu lekkie zdziwienie, bo ten parametr odpowiada za informację o tym, czy papier jest założony w podajniku... Czyli z szyby nie zeskanujesz, a jak spróbujesz to masz od razu pętlę z którą skrypt powyżej nie może sobie poradzić i zawiesza drukarkę. Moim zdaniem co najmniej dziwne rozwiązanie - ok ma zalety, ale nie spełnia oczekiwań. Dlatego przerabiamy skrypt i podajemy /<ScanToHostID>127.0.0.1-SERVER:DOCS</ScanToHostID>/ W tym wypadku oprogramowanie reaguje na polecenie Skanuj do i nie wpada w pętlę, bo nie sprawdza czy skanujemy z szyby czy z podajnika, a po skanowaniu podany przeze mnie parametr znika z pliku XML który sprawdza skrypt i nie mamy wiecznej pętli. Ten post jest wynikiem 6 godzin walki z MFP 3390 i Suse 11.2 - solucja jak widzicie jest, ale macie to szczęście, że sami nie musieliście go szukać. Link to comment Share on other sites More sharing options...
Micz Posted August 22, 2010 Report Share Posted August 22, 2010 I takie poradniki pokazują, że Linuxowi daleko jeszcze do prostoty instalacji sterowników w Windowsie. Wyobraźcie sobie, że taką drukarkę ma zainstalować laik komputerowy. Nawet by nie wiedział czego ma szukać. Link to comment Share on other sites More sharing options...
Ralliart Posted August 22, 2010 Author Report Share Posted August 22, 2010 Nie do końca Micz - osoby korzystające z Linuxa mają generalnie większe pojęcie w zakresie posiadanego os-a niż użytkownicy windowsa. Jak pisałem sama instalacja skanera i drukarki nie jest problematyczna, bo jest nawet zbliżona do tej z windowsa. Mi bardziej chodziło o rozwiązanie problemu z przyciskiem "Skanuj Do" i w tym momencie zgodzę się z Tobą, że przeciętny user jest bez szans. Link to comment Share on other sites More sharing options...
Recommended Posts