Blob Blame Raw


#include <assert.h>
#include <errno.h>
#include "texception.h"
#include "tscanner.h"
#include "tscannerepson.h"
#include "tscannerutil.h"
#include "tsystem.h"
#include "tconvert.h"
#include "trop.h"
#include <fstream>

#include "TScannerIO/TUSBScannerIO.h"

using namespace TScannerUtil;

void sense(bool) {}
int scsi_maxlen()
{
	assert(0);
	return 0;
}

#define BW_USES_GRAYTONES

#ifndef _WIN32
#define SWAPIT
#endif

#ifdef i386
#undef SWAPIT
#endif
/*
Commands used to drive the scanner:

cmd   spec                    Level
@     Reset                B2 B3 B4 B5 A5
Q     Sharpness                  B4 B5 A5
B     Halftoning        B1 B2 B3 B4 B5 A5
Z     Gamma                B2 B3 B4 B5 A5
L     Brightness           B2 B3 B4 B5 A5
t     Threshold
H     Zoom                 B2 B3 B4 B5 A5
R     Resolution        B1 B2 B3 B4 B5 A5
A     Set read Area     B1 B2 B3 B4 B5 A5
C     Linesequence Mode B1 B2 B3 B4 B5 A5
d     Line count                 B4 B5 A5
D     Dataformat        B1 B2 B3 B4 B5 A5
g     Scan speed                 B4 B5 A5
G     Start Scan        B1 B2 B3 B4 B5


e     Activate ADF   
0x19  Load from ADF
0x0C  Unload from ADF

*/

/* NOTE: you can find these codes with "man ascii". */
static unsigned char STX = 0x02;
static unsigned char ACK = 0x06;
static unsigned char NAK = 0x15;
static unsigned char CAN = 0x18;
static unsigned char ESC = 0x1B;
static unsigned char PF = 0x19;

/* STATUS bit in readLineData */
static unsigned char FatalError = 1 << 7;
static unsigned char NotReady = 1 << 6;

//-----------------------------------------------------------------------------

#define log

class TScannerExpection : public TException
{
	TString m_scannerMsg;

public:
	TScannerExpection(const vector<string> &notFatal, const string &fatal)
		: TException("Scanner Expection")
	{
		m_scannerMsg = toWideString(fatal);
		for (int i = notFatal.size(); i; i--)
			m_scannerMsg += toWideString("\n") + toWideString(notFatal[i - 1]);
		log(string("Exception created: ") + toString(m_scannerMsg));
	}
	TString getMessage() const { return m_scannerMsg; }
};

//-----------------------------------------------------------------------------

namespace
{
/*
void log(string s)
{
  std::ofstream os("C:\\butta.txt", std::ios::app);
  os << s << std::endl;
  os.flush();
}
*/
}

//-----------------------------------------------------------------------------

TScannerEpson::TScannerEpson()
	: m_scannerIO(new TUSBScannerIO()), m_hasADF(false), m_isOpened(false)
{
	log("Created");
}

//-----------------------------------------------------------------------------

void TScannerEpson::closeIO()
{
	log("CloseIO.");
	if (m_scannerIO && m_isOpened)
		m_scannerIO->close();
	m_isOpened = false;
}

//-----------------------------------------------------------------------------

TScannerEpson::~TScannerEpson()
{
	closeIO();
	log("Destroyed");
}

//-----------------------------------------------------------------------------

void TScannerEpson::selectDevice()
{
	log(string("selectDevice; isOpened=") + (m_isOpened ? "true" : "false"));
	if (!m_scannerIO->open()) {
		log("open() failed");
		throw TException("unable to get handle to scanner");
	}
	m_isOpened = true;

	/*
  char lev0, lev1;
  unsigned short lowRes, hiRes, hMax,vMax;
  collectInformation(&lev0, &lev1, &lowRes, &hiRes, &hMax, &vMax);
  string version = toString(lev0) + "." + toString(lev1);
  */
	setName("Scanner EPSON (Internal driver)");
}

//-----------------------------------------------------------------------------

bool TScannerEpson::isDeviceAvailable()
{
	return true;
}

//-----------------------------------------------------------------------------

bool TScannerEpson::isDeviceSelected()
{
	return m_isOpened;
}

//-----------------------------------------------------------------------------

void TScannerEpson::updateParameters(TScannerParameters &parameters)
{
	log("updateParameters()");
	char lev0, lev1;
	unsigned short lowRes, hiRes, hMax, vMax;
	collectInformation(&lev0, &lev1, &lowRes, &hiRes, &hMax, &vMax);
	log("collected info. res = " + toString(lowRes) + "/" + toString(hiRes));

	// non supportiamo black & white
	parameters.setSupportedTypes(true, true, true);

	// param.m_scanArea = TRectD(0, 0, 0, 0);
	parameters.setMaxPaperSize((25.4 * hMax) / (float)hiRes, (25.4 * vMax) / (float)hiRes);
	parameters.updatePaperFormat();

	// cambio range, default, step, e supported. aggiorno, se necessario, il value (n.b. non dovrebbe succedere
	// mai, perche' i range sono sempre gli stessi su tutti gli scanner
	TScanParam defaultEpsonParam(0., 255., 128., 1.);
	parameters.m_brightness.update(defaultEpsonParam);
	parameters.m_contrast.update(defaultEpsonParam);
	parameters.m_threshold.update(defaultEpsonParam);

	if (m_hasADF) {
		TScanParam defaultPaperFeederParam(0., 1., 0., 1.);
		parameters.m_paperFeeder.update(defaultPaperFeederParam);
	} else
		parameters.m_paperFeeder.m_supported = false;

	// cerco un default per il dpi. il piu' piccolo possibile nella forma 100+k50
	float defaultDpi = 100;
	while (defaultDpi < lowRes)
		defaultDpi += 50;

	TScanParam defaultDpiParam(lowRes, hiRes, defaultDpi, hiRes > lowRes ? 1.F : 0.F);
	parameters.m_dpi.update(defaultDpiParam);

	//  parameters.m_twainVersion = "NATIVE";
	//  parameters.m_manufacturer = "EPSON";

	//  parameters.m_version = toString(lev0) + "." + toString(lev1);

	log("end updateParameters()");
}

//-----------------------------------------------------------------------------

void TScannerEpson::acquire(const TScannerParameters &params, int paperCount)
{
	log("acquire");
	TRectD scanArea = params.getScanArea();
	if (scanArea.isEmpty())
		throw TException("Scan area is empty, select a paper size");

	/*
  if ((scanArea.getSize().lx > params.m_maxPaperSize.lx) ||
      (scanArea.getSize().ly > params.m_maxPaperSize.ly))
    throw TException("Scan area too large, select a correct paper size");
*/

	for (int i = 0; i < paperCount; ++i) {
		log("paper " + toString(i));
#ifdef _DEBUG
		m_scannerIO->trace(true);
#endif
		doSettings(params, i == 0);
		unsigned int nTimes = 0;
		unsigned int bytes_to_read;
		bool rc;
		unsigned char stx;

#ifdef _DEBUG
		m_scannerIO->trace(false);
#endif

		TRasterP ras, rasBuffer;
		unsigned char *buffer = 0;
		log("Scan command");
		rc = ESCI_command('G', false); /* 'SCAN' command */
		if (!rc)
			throw TException("Start Scan failed");
		log("Scan command OK");

		unsigned short offsetx = 0, offsety = 0;
		unsigned short dimlx = 0, dimly = 0;

		TRectD scanArea = params.isPreview() ? params.getScanArea() : params.getCropBox();
		scanArea2pix(params, offsetx, offsety, dimlx, dimly, scanArea);
		tswap(dimlx, dimly);

		unsigned int bytes;

		switch (params.getScanType()) {
		case TScannerParameters::BW:
#ifdef BW_USES_GRAYTONES
			ras = TRasterGR8P(dimlx, dimly);
			bytes = dimlx * dimly;
			rasBuffer = TRasterGR8P(dimlx, dimly);
			buffer = rasBuffer->getRawData();
			break;
			break;
#else
			ras = TRasterGR8P(dimlx, dimly);
			bytes = tceil(dimlx / 8) * dimly;
			rasBuffer = TRasterGR8P(tceil(dimlx / 8), dimly);
			//bytes = (dimlx + 7) % 8 * dimly;
			//rasBuffer = TRasterGR8P((dimlx + 7) % 8,dimly);
			buffer = rasBuffer->getRawData();
			break;
#endif
		case TScannerParameters::GR8:

#ifdef GRAYTONES_USES_RGB
			ras = TRasterGR8P(dimlx, dimly);
			bytes = dimlx * dimly * 3;
			rasBuffer = TRasterGR8P(dimlx * 3, dimly);
			buffer = rasBuffer->getRawData();
			break;
#else
			ras = TRasterGR8P(dimlx, dimly);
			bytes = dimlx * dimly;
			rasBuffer = TRasterGR8P(dimlx, dimly);
			buffer = rasBuffer->getRawData();
			break;
#endif

		case TScannerParameters::RGB24:
			ras = TRaster32P(dimlx, dimly);
			bytes = dimlx * dimly * 3;
			rasBuffer = TRasterGR8P(dimlx * 3, dimly);
			buffer = rasBuffer->getRawData();
			break;

		default:
			throw TException("Unknown scanner mode");
			break;
		}

		log("sleep");
		TSystem::sleep(2000);
		log("reading data");
		bool areaEnd = false;
		unsigned int bytes_read = 0;
		while (!areaEnd) {
			unsigned short lines, counter;
			unsigned char status = 0;
			unsigned int retryCount = 0;
			do {
				ESCI_readLineData(stx, status, counter, lines, areaEnd);
				if (status & FatalError) {
					retryCount++;
					if (retryCount == 1)
						throw TException("Error scanning");
				}
			} while (status & FatalError);

			bytes_to_read = lines * counter;
			if (stx != 0x02) {
				ostrstream os;
				os << "header corrupted (" << std::hex << stx << ")" << '\0';
				throw TException(os.str());
			}
			unsigned long reqBytes = bytes_to_read;
			std::unique_ptr<unsigned char[]> readBuffer = ESCI_read_data2(reqBytes);
			if (!readBuffer)
				throw TException("Error reading image data");

			log("readline: OK");
			/*
      if (params.getScanType() == TScannerParameters::BW )
      {
        for (unsigned int i = 0; i< reqBytes;i++)
          readBuffer[i] = ~readBuffer[i];
      }
      */
			memcpy(buffer + bytes_read, readBuffer.get(), reqBytes);
			bytes_read += reqBytes;
			nTimes++;
			if (bytes_read == bytes)
				break; /* I've read enough bytes */
			if (!(stx & 0x20))
				sendACK();
		}
		log("read: OK");
		{ /*
    FILE *myFile = fopen("C:\\temp\\prova.dmp", "w+");
    fwrite(buffer, 1, bytes_to_read, myFile); 
    fclose(myFile);*/
		}
		if (params.m_paperFeeder.m_supported && (params.m_paperFeeder.m_value == 1.)) {
			log("Advance paper");
			if (m_settingsMode == OLD_STYLE)
				ESCI_doADF(0);
			log("Advance paper: OK");
		}
		switch (params.getScanType()) {
		case TScannerParameters::BW:
#ifdef BW_USES_GRAYTONES
			copyGR8BufferToTRasterBW(buffer, dimlx, dimly, ras, true, params.m_threshold.m_value);
			//copyGR8BufferToTRasterGR8(buffer, dimlx, dimly, ras, true);
			break;
#else
			copyBWBufferToTRasterGR8(buffer, dimlx, dimly, ras, true, true);
			break;
#endif
		case TScannerParameters::GR8:
#ifdef GRAYTONES_USES_RGB
			copyRGBBufferToTRasterGR8(buffer, dimlx, dimly, dimlx, ras);
			break;
#else
			copyGR8BufferToTRasterGR8(buffer, dimlx, dimly, ras, true);
			break;
#endif

		case TScannerParameters::RGB24:
			copyRGBBufferToTRaster32(buffer, dimlx, dimly, ras, true);
			break;

		default:
			throw TException("Unknown scanner mode");
			break;
		}

		if (params.getCropBox() != params.getScanArea() && !params.isPreview()) {
			TRect scanRect(TPoint(offsetx, offsety), TDimension(dimly, dimlx)); //dimLx and ly was has been swapped
			scanArea2pix(params, offsetx, offsety, dimlx, dimly, params.getScanArea());
			TRasterP app = ras->create(dimly, dimlx);
			TRaster32P app32(app);
			TRasterGR8P appGR8(app);
			if (app32)
				app32->fill(TPixel32::White);
			else if (appGR8)
				appGR8->fill(TPixelGR8::White);
			app->copy(ras, TPoint(dimly - scanRect.y1 - 1, dimlx - scanRect.x1 - 1));
			ras = app;
		}
		TRasterImageP rasImg(ras);
		if (ras) {
			rasImg->setDpi(params.m_dpi.m_value, params.m_dpi.m_value);
			rasImg->setSavebox(ras->getBounds());
		}
		log("notifying");
		notifyImageDone(rasImg);
		if (!(params.m_paperFeeder.m_value == 1.) || params.isPreview()) //feeder here!
		{
			if ((paperCount - i) > 1)
				notifyNextPaper();
		} else
			notifyAutomaticallyNextPaper();
		if (isScanningCanceled())
			break;
	}

	/*Unload Paper if need*/

	if ((m_settingsMode == NEW_STYLE) && params.m_paperFeeder.m_supported && (params.m_paperFeeder.m_value == 1.)) {
		if (!ESCI_command_1b('e', 1, true)) {
			vector<string> notFatal;
			throw TScannerExpection(notFatal, "Scanner error (un)loading paper");
		}
		unsigned char p = 0x0C;
		bool status = true;
		send(&p, 1);
		status = expectACK();
	}

	log("acquire OK");
}

//-----------------------------------------------------------------------------

bool TScannerEpson::isAreaSupported()
{
	return true;
}

//-----------------------------------------------------------------------------

inline unsigned char B0(const unsigned int v) { return (v & 0x000000ff); }
inline unsigned char B1(const unsigned int v) { return (v & 0x0000ff00) >> 8; }
inline unsigned char B2(const unsigned int v) { return (v & 0x00ff0000) >> 16; }
inline unsigned char B3(const unsigned int v) { return (v & 0xff000000) >> 24; }

void TScannerEpson::doSettings(const TScannerParameters &params, bool firstSheet)
{
	log("doSettings");
	int retryCount = 3;
	vector<string> notFatal;

	while (retryCount--) {
		if (m_settingsMode == NEW_STYLE) {
			if (firstSheet)
				if (!resetScanner()) {
					notFatal.push_back("Scanner error resetting");
					continue;
				}
		} else {
			if (!resetScanner()) {
				notFatal.push_back("Scanner error resetting");
				continue;
			}
		}

		unsigned char cmd[64];
		memset(&cmd, 0, 64);

		unsigned char brightness = 0x00;
		float bv = params.m_brightness.m_value;

		if (bv >= 0 && bv < 43)
			brightness = 0xFD;
		if (bv >= 43 && bv < 86)
			brightness = 0xFE;
		if (bv >= 86 && bv < 128)
			brightness = 0xFF;
		if (bv >= 128 && bv < 171)
			brightness = 0x00;
		if (bv >= 171 && bv < 214)
			brightness = 0x01;
		if (bv >= 214 && bv < 255)
			brightness = 0x02;
		if (bv == 255)
			brightness = 0x03;

		unsigned short dpi = (unsigned short)params.m_dpi.m_value;

		unsigned short offsetx, offsety;
		unsigned short sizelx, sizely;
		TRectD scanArea = params.isPreview() ? params.getScanArea() : params.getCropBox();
		scanArea2pix(params, offsetx, offsety, sizelx, sizely, scanArea);

		// the main scan resolution (ESC R)
		cmd[0] = B0(dpi);
		cmd[1] = B1(dpi);
		cmd[2] = B2(dpi);
		cmd[3] = B3(dpi);

		//the sub scan resolution (ESC R)
		cmd[4] = B0(dpi);
		cmd[5] = B1(dpi);
		cmd[6] = B2(dpi);
		cmd[7] = B3(dpi);

		//skipping length of main scan (ESC A)
		cmd[8] = B0(offsetx);
		cmd[9] = B1(offsetx);
		cmd[10] = B2(offsetx);
		cmd[11] = B3(offsetx);

		//skipping length of sub scan (ESC A)
		cmd[12] = B0(offsety);
		cmd[13] = B1(offsety);
		cmd[14] = B2(offsety);
		cmd[15] = B3(offsety);

		//the length of main scanning (ESC A)
		cmd[16] = B0(sizelx);
		cmd[17] = B1(sizelx);
		cmd[18] = B2(sizelx);
		cmd[19] = B3(sizelx);

		//the length of sub scanning (ESC A)
		cmd[20] = B0(sizely);
		cmd[21] = B1(sizely);
		cmd[22] = B2(sizely);
		cmd[23] = B3(sizely);

#ifdef GRAYTONES_USES_RGB
		cmd[24] = 0x13; //scanning color (ESC C)
		cmd[25] = 0x08; // data format (ESC D)
#else
		switch (params.getScanType()) {
		case TScannerParameters::BW:
			cmd[24] = 0x00; //scanning BW/Gray (ESC C)
#ifdef BW_USES_GRAYTONES
			cmd[25] = 0x08; // data format (ESC D)
#else
			cmd[25] = 0x01; // data format (ESC D)
			break;
#endif
		case TScannerParameters::GR8:
			cmd[24] = 0x00; //scanning BW/Gray (ESC C)
			cmd[25] = 0x08; // data format (ESC D)
			break;

		case TScannerParameters::RGB24:
			cmd[24] = 0x13; //scanning color (ESC C)
			cmd[25] = 0x08; // data format (ESC D)
			break;

		default:
			throw TException("Unknown scanner mode");
			break;
		}

//  cmd[24] = (params.getScanType() == TScannerParameters::RGB24)?0x13:0x00; //scanning color (ESC C)
//  cmd[25] = (params.getScanType() == TScannerParameters::RGB24)?0x08:0x08; // data format (ESC D)
#endif

		if (m_settingsMode == NEW_STYLE)
			cmd[26] = 0;
		else
			cmd[26] = (params.m_paperFeeder.m_supported && (params.m_paperFeeder.m_value == 1.)) ? 0x01 : 0x00; // option control (ESC e)
		cmd[27] = 0x00;																							// scanning mode (ESC g)
		cmd[28] = 0xFF;																							// the number of block line (ESC d)
		cmd[29] = 0x02;																							// gamma (ESC Z)
		cmd[30] = brightness;																					//  brightness (ESC L)
		cmd[31] = 0x80;																							// color collection (ESC M)
		cmd[32] = 0x01;																							// half toning processing (ESC B)
		cmd[33] = (unsigned char)params.m_threshold.m_value;													// threshold (ESC t)
		cmd[34] = 0x00;																							// separate of area (ESC s)
		cmd[35] = 0x01;																							// sharpness control (ESC Q)
		cmd[36] = 0x00;																							// mirroring (ESC K)
		cmd[37] = 0x00;																							// set film type (ESC N)
																												// other bytes should be set to 0x00 !

		if (m_settingsMode == NEW_STYLE)
			if (params.m_paperFeeder.m_supported && (params.m_paperFeeder.m_value == 1.)) {
				unsigned char v = (params.m_paperFeeder.m_value == 1.) ? 0x01 : 0x00;
				if (!ESCI_command_1b('e', v, true))
					throw TScannerExpection(notFatal, "Scanner error (un)loading paper");
				if (v) {
					unsigned char p = 0x19;
					bool status = true;
					send(&p, 1);
					status = expectACK();
				}
			}

		unsigned char setParamCmd[2] = {0x1C, 0x57};
		int wrote = send(&setParamCmd[0], 2);
		if (wrote != 2)
			throw TScannerExpection(notFatal, "Error setting scanner parameters - W -");

		if (!expectACK()) {
			notFatal.push_back("Error setting scanner parameters - NAK on W -");
			continue;
		}
		wrote = send(&cmd[0], 0x40);
		if (wrote != 0x40)
			throw TScannerExpection(notFatal, "Error setting scanner parameters - D -");

		if (!expectACK()) {
			notFatal.push_back("Error setting scanner parameters - NAK on D -");
			continue;
		}
		//if here, everything is ok, exit from loop
		break;
	}
	if (retryCount <= 0)
		throw TScannerExpection(notFatal, "Error setting scanner parameters, too many retries");
	log("doSettings:OK");
}

//-----------------------------------------------------------------------------

void TScannerEpson::collectInformation(char *lev0, char *lev1, unsigned short *lowRes, unsigned short *hiRes, unsigned short *hMax, unsigned short *vMax)
{
	log("collectInformation");
	unsigned char stx;
	int pos = 0;
	unsigned short counter;
	unsigned char status;

	/*
if (!resetScanner())
  throw TException("Scanner error resetting");
*/
	if (!ESCI_command('I', false))
		throw TException("Unable to get scanner info. Is it off ?");

	unsigned long s = 4; // 4 bytes cfr Identity Data Block on ESCI Manual!!!
	std::unique_ptr<unsigned char[]> buffer2 = ESCI_read_data2(s);
	if (!buffer2 || (s != 4))
		throw TException("Error reading scanner info");

	memcpy(&stx, buffer2.get(), 1);
	memcpy(&counter, &(buffer2[2]), 2);

#ifdef SWAPIT
	counter = swapUshort(counter);
#endif

#ifdef _DEBUG
	memcpy(&status, &(buffer2[1]), 1);
	ostrstream os;
	os.freeze(false);
	os << "stx = " << stx << " status = " << status << " counter=" << counter << '\n' << '\0';
#endif

	s = counter;
	std::unique_ptr<unsigned char[]> buffer = ESCI_read_data2(s);
	int len = strlen((const char *)buffer.get());

	/*printf("Level %c%c", buffer[0], buffer[1]);*/
	if (len > 1) {
		*lev0 = buffer[0];
		*lev1 = buffer[1];
	}
	pos = 2;
	/* buffer[pos] contains 'R' */
	if (len < 3 || buffer[pos] != 'R') {
		*lev0 = '0';
		*lev1 = '0';
		*lowRes = 0;
		*hiRes = 0;
		*vMax = 0;
		*hMax = 0;
		throw TException("unable to get information from scanner");
	}

	*lowRes = (buffer[pos + 2] * 256) + buffer[pos + 1];
	*hiRes = *lowRes;

	while (buffer[pos] == 'R') {
		*hiRes = (buffer[pos + 2] * 256) + buffer[pos + 1];
		/*  printf("Resolution %c  %d", buffer[pos], *hiRes);*/
		pos += 3;
	}

	if (buffer[pos] != 'A') {
		*lev0 = '0';
		*lev1 = '0';
		*lowRes = 0;
		*hiRes = 0;
		*vMax = 0;
		*hMax = 0;
		throw TException("unable to get information from scanner");
	}

	*hMax = (buffer[pos + 2] * 256) + buffer[pos + 1];
	*vMax = (buffer[pos + 4] * 256) + buffer[pos + 3];

	ESCI_command('f', false);

	ESCI_readLineData2(stx, status, counter);
	if (status & FatalError)
		throw TException("Fatal error reading information from scanner");

	s = counter;
	buffer = ESCI_read_data2(s);
	//name buffer+1A
	const char *name = (const char *)(buffer.get() + 0x1A);
	if (strncmp(name, "Perfection1640", strlen("Perfection1640"))) {
		m_settingsMode = NEW_STYLE;
	} else {
		m_settingsMode = OLD_STYLE;
	}
#if 0
scsi_new_d("ESCI_extended_status");
scsi_len(42);
/* main status*/
scsi_b00(0, "push_button_supported", 0);
scsi_b11(0, "warming_up", 0);
scsi_b33(0, "adf_load_sequence", 0);  /* 1 from first sheet; 0 from last or not supp */
scsi_b44(0, "both_sides_on_adf", 0);
scsi_b55(0, "adf_installed_main_status", 0);
scsi_b66(0, "NFlatbed", 0);   /*0 if scanner is flatbed else 1 */
scsi_b77(0, "system_error", 0);
/* adf status */
scsi_b77(1, "adf_installed",0);
/*... some other info.., refer to manual if needed*/
/**/
#endif
	m_hasADF = !!(buffer[1] & 0x80);
	log("collectInformation:OK");
}

//-----------------------------------------------------------------------------

bool TScannerEpson::resetScanner()
{
	log("resetScanner");
	bool ret = ESCI_command('@', true);
	log(string("resetScanner: ") + (ret ? "OK" : "FAILED"));
	return ret;
}

//-----------------------------------------------------------------------------

int TScannerEpson::receive(unsigned char *buffer, int size)
{
	return m_scannerIO->receive(buffer, size);
}

int TScannerEpson::send(unsigned char *buffer, int size)
{
	return m_scannerIO->send(buffer, size);
}

int TScannerEpson::sendACK()
{
	return send(&ACK, 1);
}

bool TScannerEpson::expectACK()
{
	log("expectACK");
	unsigned char ack = NAK;
	int nb = receive(&ack, 1);

#ifdef _DEBUG
	if (ack != ACK) {
		ostrstream os;
		os.freeze(false);
		os << "ack fails ret = 0x" << std::hex << (int)ack << '\n' << '\0';
		TSystem::outputDebug(os.str());
	}
#endif
	log(string("expectACK: ") + (ack == ACK ? "ACK" : "FAILED"));

	return (ack == ACK);
}

char *TScannerEpson::ESCI_inquiry(char cmd) /* returns 0 if failed */
{
	assert(0);
	return 0;
}

bool TScannerEpson::ESCI_command(char cmd, bool checkACK)
{
	unsigned char p[2];
	p[0] = ESC;
	p[1] = cmd;
	bool status;

	int count = send(&(p[0]), 2);
	status = count == 2;

	if (checkACK)
		status = expectACK();

	return status;
}

bool TScannerEpson::ESCI_command_1b(char cmd, unsigned char p0, bool checkACK)
{
	bool status = false;
	if (ESCI_command(cmd, checkACK)) {
		unsigned char p = p0;
		status = true;
		send(&p, 1);
		if (checkACK)
			status = expectACK();
	}

	return status;
}

bool TScannerEpson::ESCI_command_2b(char cmd, unsigned char p0, unsigned char p1, bool checkACK)
{
	bool status = false;
	if (ESCI_command(cmd, checkACK)) {
		status = true;
		unsigned char p[2];
		p[0] = p0;
		p[1] = p1;
		int timeout = 30000;
		send(&(p[0]), 2);
		if (checkACK)
			status = expectACK();
	}

	return status;
}

bool TScannerEpson::ESCI_command_2w(char cmd, unsigned short p0, unsigned short p1, bool checkACK)
{
	bool status = false;
	if (ESCI_command(cmd, checkACK)) {
		status = true;
		unsigned short p[2];
		p[0] = p0;
		p[1] = p1;
		const int len = 1;
		int timeout = 30000;
		send((unsigned char *)(&(p[0])), 4);
		if (checkACK)
			status = expectACK();
	}

	return status;
}

bool TScannerEpson::ESCI_command_4w(char cmd, unsigned short p0, unsigned short p1, unsigned short p2, unsigned short p3, bool checkACK)
{
	bool status = false;
	if (ESCI_command(cmd, checkACK)) {
		status = true;
		unsigned char p[8];
		p[0] = (unsigned char)p0;
		p[1] = (unsigned char)(p0 >> 8);
		p[2] = (unsigned char)p1;
		p[3] = (unsigned char)(p1 >> 8);
		p[4] = (unsigned char)p2;
		p[5] = (unsigned char)(p2 >> 8);
		p[6] = (unsigned char)(p3);
		p[7] = (unsigned char)(p3 >> 8);

		send((unsigned char *)&(p[0]), 8);

		if (checkACK)
			status = expectACK();
	}

	return status;
}

std::unique_ptr<unsigned char[]> TScannerEpson::ESCI_read_data2(unsigned long &size)
{
	std::unique_ptr<unsigned char[]> buffer(new unsigned char[size]);
	memset(buffer.get(), 0, size);
	unsigned long bytesToRead = size;
	size = receive(buffer.get(), bytesToRead);
	return buffer;
}

bool TScannerEpson::ESCI_doADF(bool on)
{
	//check ref esci documentation page 3-64
	unsigned char eject = 0x0c;
	int rc = send(&eject, 1);
	bool status1 = expectACK();
	return status1;

	return 1;
	if (!ESCI_command_1b('e', 0x01, true)) {
		if (on)
			throw TException("Scanner error loading paper");
		else
			throw TException("Scanner error unloading paper");
	}

	unsigned char p = on ? 0x19 : 0x0C;

	send(&p, 1);
	bool status = expectACK();
	return status;
}

void TScannerEpson::scanArea2pix(const TScannerParameters &params, unsigned short &offsetx, unsigned short &offsety,
								 unsigned short &sizelx, unsigned short &sizely, const TRectD &scanArea)
{
	const double f = 25.4;
	offsetx = (unsigned short)((scanArea.x0 * params.m_dpi.m_value) / f);
	offsety = (unsigned short)((scanArea.y0 * params.m_dpi.m_value) / f);
	sizelx = (unsigned short)(((scanArea.x1 - scanArea.x0) * params.m_dpi.m_value) / f);
	sizelx = (sizelx >> 3) << 3; //questo deve essere multiplo di 8
	sizely = (unsigned short)(((scanArea.y1 - scanArea.y0) * params.m_dpi.m_value) / f);
}

void TScannerEpson::ESCI_readLineData(unsigned char &stx, unsigned char &status, unsigned short &counter, unsigned short &lines, bool &areaEnd)
{
	unsigned long s = 6;
	std::unique_ptr<unsigned char[]> buffer = ESCI_read_data2(s);
	if (!buffer)
		throw TException("Error reading scanner info");
	/* PACKET DATA LEN = 6
	type offs  descr
	byte  0    STX
	b77   1    fatal_error
	b66   1    not_ready
	b55   1    area_end
	b44   1    option_unit
	b33   1    col_attrib_bit_3
	b22   1    col_attrib_bit_2
	b11   1    extended_commands
	drow  2,   counter
	drow  4    lines
	*/
	bool fatalError = !!(buffer[1] & 0x80);
	bool notReady = !!(buffer[1] & 0x40);
	areaEnd = !!(buffer[1] & 0x20);

	memcpy(&stx, buffer.get(), 1);
	memcpy(&counter, &(buffer[2]), 2);

#ifdef SWAPIT
	counter = swapUshort(counter);
#endif
	memcpy(&lines, &(buffer[4]), 2);
#ifdef SWAPIT
	lines = swapUshort(lines);
#endif

	status = buffer[1];

#ifdef _DEBUG
	ostrstream os;
	os.freeze(false);

	os << "fatal=" << fatalError;
	os << " notReady=" << notReady;
	os << " areaEnd=" << areaEnd;

	os << " stx=" << stx;
	os << " counter=" << counter;
	os << " lines=" << lines;
	os << '\n' << '\0';

	TSystem::outputDebug(os.str());
#endif
}

void TScannerEpson::ESCI_readLineData2(unsigned char &stx, unsigned char &status, unsigned short &counter)
{
	unsigned long s = 4;
	std::unique_ptr<unsigned char[]> buffer = ESCI_read_data2(s);
	if (!buffer)
		throw TException("Error reading scanner info");
	bool fatalError = !!(buffer[1] & 0x80);
	bool notReady = !!(buffer[1] & 0x40);

	memcpy(&stx, buffer.get(), 1);
	memcpy(&counter, &(buffer[2]), 2);
#ifdef SWAPIT
	counter = swapUshort(counter);
#endif

	status = buffer[1];

#ifdef _DEBUG
	ostrstream os;
	os.freeze(false);

	os << "fatal=" << fatalError;
	os << " notReady=" << notReady;

	os << " stx=" << stx;
	os << " counter=" << counter;
	os << '\n' << '\0';

	TSystem::outputDebug(os.str());
#endif
}