Skocz do zawartości

Instlacja Skanera HP Linux


Ralliart

Recommended Posts

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ć.

Odnośnik do komentarza
Udostępnij na stronach

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.

Odnośnik do komentarza
Udostępnij na stronach

Gość
Ten temat jest zamknięty i nie można dodawać odpowiedzi.
×
×
  • Dodaj nową pozycję...