| EAFDSS documentation | Contained in the EAFDSS distribution. |
EAFDSS::Base - EAFDSS Base Class Driver for all other drivers
Read EAFDSS on how to use the module. This manual page is only of use if you want to find out what it needs to develop a driver for a new EAFDSS device. This Base class is to be inherited by any new driver.
This the constructor, were we make sure we get the correct parameters to handle the initialization of device object. Things like the signatures directory, the serial number of the device. Also parameters special to the type of the device, like ip address, or serial port, or baud rate, etc.
The main job of an EAFDSS device is to produce signatures. Signatures of text files (invoices) or text streams. So in that function we make sure to read the text from the caller of the function in whatever format. Then we feed the text to the device which in return he gives us the signature of that text. The function at tha level handles the saving of the text in the "A file" and of the signature in the "B file", according to the rules set by the law for the filenames of the files.
What this function return is a single line containing the values of the following: serial number, the index of the last Z, the total signatures, the daily signatures, the last signature's data size, remaining signatures until the device will force a Z.
GetTime will return the time in "DD/MM/YY HH:MM:SS" format.
Use this method to set the date/time on the device. Provide the date/time in the "DD/MM/YY HH:MM:SS" format.
This method will return information about the name of the device and version of it's firmware.
This method should Query to find available devicess [NOT IMPLEMENTED]
This method will return the printing headers of the device. The returned array contains 6 couples of values. One for the type of the printing line, and one for the actual printing message.
This method will set the printing headers on the device. The headers are to be provided in the following format
Style1/Line1/Style2/Line2/Style3/Line3/Style4/Line4/Style5/Line5/Style6/Line6
The second most used function is Z report issuing function. At the end of the day ask for the device to close the fiscal day by issuing the Z report. It will return the signature of the day. The function will also take care to save the signature in the "C file"
This is our handy debuging function
This is version 0.80.
Hasiotis Nikos, <hasiotis@gmail.com>
Copyright (C) 2008 by Hasiotis Nikos
This library is free software; you can redistribute it and/or modify it under the terms of the LGPL or the same terms as Perl itself, either Perl version 5.8.8 or, at your option, any later version of Perl 5 you may have available.
| EAFDSS documentation | Contained in the EAFDSS distribution. |
# EAFDSS - Electronic Fiscal Signature Devices Library # Îιδική ÎÏÏÎ±Î»Î®Ï Î¦Î¿Ïολογική ÎιάÏαξη ΣήμανÏÎ·Ï (ÎÎΦÎΣΣ) # # Copyright (C) 2008 Hasiotis Nikos # # ID: $Id: Base.pm 105 2009-05-18 10:52:03Z hasiotis $ package EAFDSS::Base;
use 5.006_000; use strict; use warnings; use Carp; use Class::Base; use base qw ( Class::Base ); our($VERSION) = '0.80';
sub init { my($self, $config) = @_; if (! exists $config->{DIR}) { return $self->error("You need to provide the DIR to save the signnatures!"); } else { $self->{DIR} = $config->{DIR}; } if (! exists $config->{SN}) { return $self->error("You need to provide the Serial Number of the device!"); } else { $self->{SN} = $config->{SN}; } return $self; }
sub Sign { my($self) = shift @_; my($fname) = shift @_; my($reply, $totalSigns, $dailySigns, $date, $time, $nextZ, $sign, $fullSign); $self->debug("Sign operation"); if ( ($fname eq '-') || (-e $fname) ) { my($replySignDir, $deviceDir) = $self->_createSignDir(); if ($replySignDir != 0) { return $self->error($replySignDir); } # Slurping the invoice open(FH, $fname); my($invoice) = do { local($/); <FH> }; close(FH); $self->debug( " Checking file [%s] for invalid characters", $fname); my($invalid) = $self->_checkCharacters($invoice); if ($invalid) { $self->debug(" File contains invalid characters [%s]", $fname); return $self->error(64+0x10); } $self->debug( " Signing file [%s]", $fname); ($reply, $totalSigns, $dailySigns, $date, $time, $nextZ, $sign) = $self->PROTO_GetSign($invoice); if ($reply == 0) { $fullSign = sprintf("%s %04d %08d %s%s %s", $sign, $dailySigns, $totalSigns, $self->UTIL_date6ToHost($date), substr($time, 0, 4), $self->{SN}); $self->_createFileA($invoice, $deviceDir, $date, $dailySigns, $nextZ); $self->_createFileB($fullSign, $deviceDir, $date, $dailySigns, $nextZ); return $fullSign; } else { return $self->error($reply); } } else { $self->debug( " No such file [%s]", $fname); return $self->error(64+2); } }
sub Status { my($self) = shift @_; $self->debug("Status operation"); my($reply, $status1, $status2, $lastZ, $total, $daily, $signBlock, $remainDaily) = $self->PROTO_ReadSummary(); if ($reply == 0) { my($statusLine) = sprintf("%s %d %d %d %d %d", $self->{SN}, $lastZ, $total, $daily, $signBlock, $remainDaily); return $statusLine; } else { return $self->error($reply); } }
sub GetTime { my($self) = shift @_; $self->debug("Read time operation"); my($reply, $time) = $self->PROTO_ReadTime(); if ($reply == 0) { return $time; } else { return $self->error($reply); } }
sub SetTime { my($self) = shift @_; my($time) = shift @_; $self->debug("Set time operation"); my($reply) = $self->PROTO_SetTime($time); if ($reply == 0) { return 0; } else { return $self->error($reply); } }
sub Info { my($self) = shift @_; $self->debug("Read Info operation"); my($reply, $version) = $self->PROTO_VersionInfo(); if ($reply == 0) { return $version; } else { return $self->error($reply); } }
sub Query { my($self) = shift @_; $self->debug("Query for devices"); my($reply, $devices) = $self->PROTO_Query(); if ($reply == 0) { if ($devices) { return $devices; } else { return $self->error(64+0x05); } } else { return $self->error($reply); } }
sub GetHeaders { my($self) = shift @_; $self->debug("Read Headers operation"); my($reply, @headers) = $self->PROTO_GetHeader(); if ($reply == 0) { return @headers; } else { return $self->error($reply); } }
sub SetHeaders { my($self) = shift @_; my($headers) = shift @_; $self->debug("Set Headers operation"); my($reply) = $self->PROTO_SetHeader($headers); if ($reply == 0) { return 0; } else { return $self->error($reply); } }
sub Report { my($self) = shift @_; my($replySignDir, $deviceDir) = $self->_createSignDir(); if ($replySignDir != 0) { return $self->error($replySignDir); } $self->_validateFilesB(); $self->_validateFilesC(); $self->debug("Issue Report operation"); my($reply1) = $self->PROTO_IssueReport(); if ($reply1 != 0) { return $self->error($reply1); } my($reply2, $status1, $status2, $totalSigns, $dailySigns, $date, $time, $z, $sn, $closure) = $self->PROTO_ReadClosure(0); $self->_createFileC($z, $deviceDir, $date, $time, $closure); return $z; } sub _RecoveryReport { my($self) = shift @_; my($deviceDir) = sprintf("%s/%s", $self->{DIR}, $self->{SN}); $self->_validateFilesB(); $self->_validateFilesC(); $self->debug("Issue Recovery Report operation"); my($reply1) = $self->PROTO_IssueReport(); if ($reply1 != 0) { return $self->error($reply1); } my($reply2, $status1, $status2, $totalSigns, $dailySigns, $date, $time, $z, $sn, $closure) = $self->PROTO_ReadClosure(0); $self->_createFileC($z, $deviceDir, $date, $time, $closure); return $z; } sub _checkCharacters { my($self) = shift @_; my($invoice) = shift @_; my($c); foreach $c (unpack('C*', $invoice)) { if (grep $_ == ord($c), qw/0 1 2 3 4 5 6 7 8 11 14 15 16 17 18 19 20 21 22 23 24 25 27 28 29 30 31 127 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 173 210 255/ ) { $self->debug(" Found invalid character [%d]", ord($c)); return 1; } } return 0; } sub _createSignDir { my($self) = shift @_; my($result) = $self->_Recover(); if ($result != 0) { return ($result, undef); } # Create The signs Dir if (! -d $self->{DIR} ) { $self->debug(" Creating Base Dir [%s]", $self->{DIR}); mkdir($self->{DIR}); } my($deviceDir) = sprintf("%s/%s", $self->{DIR}, $self->{SN}); if (! -d $deviceDir ) { $self->debug(" Creating Device Dir [%s]", $deviceDir); mkdir($deviceDir); } return (0, $deviceDir); } sub _Recover { my($self) = shift @_; my($reply, $status1, $status2, $lastZ, $total, $daily, $signBlock, $remainDaily); ($reply, $status1, $status2) = $self->PROTO_GetStatus(); if ($reply ne "0") { return $reply }; my($busy, $fatal, $paper, $cmos, $printer, $user, $fiscal, $battery) = $self->UTIL_devStatus($status1); if ($cmos != 1) { return 0 }; my($day, $signature, $recovery, $fiscalWarn, $dailyFull, $fiscalFull) = $self->UTIL_appStatus($status1); $self->debug(" CMOS is set, going for recovery!"); ($reply, $status1, $status2, $lastZ, $total, $daily, $signBlock, $remainDaily) = $self->PROTO_ReadSummary(0); if ($reply != 0) { $self->debug(" Aborting recovery because of ReadClosure reply [%d]", $reply); return $reply }; my($regexA) = sprintf("%s\\d{6}%04d\\d{4}_a.txt", $self->{SN}, $lastZ + 1); my($deviceDir) = sprintf("%s/%s", $self->{DIR}, $self->{SN}); opendir(DIR, $deviceDir) || croak "can't opendir $deviceDir: $!"; my(@afiles) = grep { /$regexA/ } readdir(DIR); closedir(DIR); foreach my $curA (@afiles) { $self->debug(" Checking [%s]", $curA); my($curFileA) = sprintf("%s/%s", $deviceDir, $curA); my($curFileB) = $curFileA; $curFileB =~ s/_a/_b/; my($curB) = $curA; $curB =~ s/_a/_b/; my($curIndex) = substr($curA, 21, 4); $curIndex =~ s/^0*//; $self->debug(" Resigning file A [%s]", $curA); open(FH, $curFileA); my($reply, $totalSigns, $dailySigns, $date, $time, $nextZ, $sign) = $self->PROTO_GetSign(*FH); my($fullSign) = sprintf("%s %04d %08d %s%s %s", $sign, $dailySigns, $totalSigns, $self->UTIL_date6ToHost($date), substr($time, 0, 4), $self->{SN}); close(FH); $self->debug(" Updating file B [%s] -- Index [%d]", $curB, $curIndex); open(FB, ">>", $curFileB) || croak "Error: $!"; print(FB "\n" . $fullSign); close(FB); } my($z) = $self->_RecoveryReport(); if ($z) { return(0); } else { my($errNo) = $self->error(); return $self->error($errNo); } } sub _createFileA { my($self) = shift @_; my($invoice) = shift @_; my($dir) = shift @_; my($date) = shift @_; my($ds) = shift @_; my($curZ) = shift @_; my($fnA) = sprintf("%s/%s%s%04d%04d_a.txt", $dir, $self->{SN}, $self->UTIL_date6ToHost($date), $curZ, $ds); $self->debug(" Creating File A [%s]", $fnA); open(FA, ">", $fnA) || croak "Error: $!"; print(FA $invoice); close(FA); } sub _createFileB { my($self) = shift @_; my($fullSign) = shift @_; my($dir) = shift @_; my($date) = shift @_; my($ds) = shift @_; my($curZ) = shift @_; my($fnB) = sprintf("%s/%s%s%04d%04d_b.txt", $dir, $self->{SN}, $self->UTIL_date6ToHost($date), $curZ, $ds); $self->debug(" Creating File B [%s]", $fnB); open(FB, ">", $fnB) || croak "Error: $!"; print(FB $fullSign); close(FB); } sub _createFileC { my($self) = shift @_; my($z) = shift @_; my($dir) = shift @_; my($date) = shift @_; my($time) = shift @_; my($closure) = shift @_; my($fnC) = sprintf("%s/%s%s%s%04d_c.txt", $dir, $self->{SN}, $date, $self->UTIL_time6toHost($time), $closure); $self->debug( " Creating File C [%s]", $fnC); open(FC, ">", $fnC) || croak "Error: $!"; print(FC $z); close(FC); } sub _validateFilesB { my($self) = shift @_; my($reply, $status1, $status2, $lastZ, $total, $daily, $signBlock, $remainDaily) = $self->PROTO_ReadSummary(); if ($reply != 0) { return $reply}; my($regexA) = sprintf("%s\\d{6}%04d\\d{4}_a.txt", $self->{SN}, $lastZ + 1); $self->debug( " Validating B Files for #%d Z with regex [%s]", $lastZ + 1 , $regexA); my($deviceDir) = sprintf("%s/%s", $self->{DIR}, $self->{SN}); opendir(DIR, $deviceDir) || croak "can't opendir $deviceDir: $!"; my(@afiles) = grep { /$regexA/ } readdir(DIR); closedir(DIR); foreach my $curA (@afiles) { $self->debug( " Checking [%s]", $curA); my($curFileA) = sprintf("%s/%s", $deviceDir, $curA); my($curFileB) = $curFileA; $curFileB =~ s/_a/_b/; if (! -e $curFileB) { # TODO: Add size Check my($curB) = $curA; $curB =~ s/_a/_b/; my($curIndex) = substr($curA, 21, 4); $curIndex =~ s/^0*//; $self->debug( " Recreating file B [%s] -- Index [%d]", $curB, $curIndex); my($replyCode, $status1, $status2, $totalSigns, $dailySigns, $date, $time, $sign, $sn, $closure) = $self->PROTO_ReadSignEntry($curIndex); my($fullSign) = sprintf("%s %04d %08d %s%s %s", $sign, $dailySigns, $totalSigns, $self->UTIL_date6ToHost($date), substr($time, 0, 4), $self->{SN}); open(FB, ">", $curFileB) || croak "Error: $!"; print(FB $fullSign); close(FB); } } return; } sub _validateFilesC { my($self) = shift @_; my($reply, $status1, $status2, $lastZ, $total, $daily, $signBlock, $remainDaily) = $self->PROTO_ReadSummary(); if ($reply != 0) { return $reply }; my($curClosure, $curFileC, $matched); my($regexC) = sprintf("%s.*_c.txt", $self->{SN}, $lastZ + 1); $self->debug( " Validating C Files for, total of [%d]", $lastZ); my($deviceDir) = sprintf("%s/%s", $self->{DIR}, $self->{SN}); opendir(DIR, $deviceDir) || croak "can't opendir $deviceDir: $!"; my(@cfiles) = grep { /$regexC/ } readdir(DIR); closedir(DIR); for ($curClosure = 1; $curClosure <= $lastZ; $curClosure++) { $self->debug( " Searching for [%d]", $curClosure); $matched = 0; foreach (@cfiles) { if (/${curClosure}_c\.txt$/) { $curFileC = $_; $matched = 1; last; } } if ($matched) { $self->debug( " Keeping file C [%s] -- Index [%d]", $curFileC, $curClosure); } else { my($replyCode, $status1, $status2, $totalSigns, $dailySigns, $date, $time, $z, $sn, $closure) = $self->PROTO_ReadClosure($curClosure); my($fnC) = sprintf("%s%s%s%04d_c.txt", $sn, $date, $time, $curClosure); $self->debug( " Recreating file C [%s] -- Index [%d]", $fnC, $curClosure); open(FC, ">", $deviceDir . "/" . $fnC) || croak "Error: $!"; print(FC $z); close(FC); } } } sub DESTROY { my($self) = shift; #printfv("Destroying %s %s", $self, $self->name ); }
sub debug { my($self) = shift; my($flag); if (ref $self && defined $self->{ _DEBUG }) { $flag = $self->{ _DEBUG }; } else { # go looking for package variable no strict 'refs'; $self = ref $self || $self; $flag = ${"$self\::DEBUG"}; } return unless $flag; printf(STDERR "[%s] %s\n", $self->id, sprintf(shift @_, @_)); } # Preloaded methods go here. 1; __END__