1
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-05-04 22:19:38 +00:00

50_SSFile.pm: contrib Version 0.5.0

git-svn-id: https://svn.fhem.de/fhem/trunk@23028 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
nasseeder1 2020-10-26 15:54:11 +00:00
parent f79391d3c2
commit a7bbf981b0

View File

@ -63,7 +63,7 @@ use FHEM::SynoModules::SMUtils qw( completeAPI
updQueueLength updQueueLength
trim trim
slurpFile slurpFile
jboolmap jboolmap
); # Hilfsroutinen Modul ); # Hilfsroutinen Modul
use FHEM::SynoModules::ErrCodes qw(expErrors); # Error Code Modul use FHEM::SynoModules::ErrCodes qw(expErrors); # Error Code Modul
@ -85,6 +85,9 @@ BEGIN {
qw( qw(
attr attr
AttrVal AttrVal
BlockingCall
BlockingKill
BlockingInformParent
CancelDelayedShutdown CancelDelayedShutdown
CommandSet CommandSet
CommandAttr CommandAttr
@ -93,6 +96,7 @@ BEGIN {
CommandGet CommandGet
CommandSetReading CommandSetReading
CommandTrigger CommandTrigger
Debug
data data
defs defs
devspec2array devspec2array
@ -133,7 +137,7 @@ BEGIN {
# Versions History intern # Versions History intern
my %vNotesIntern = ( my %vNotesIntern = (
"0.5.0" => "24.10.2020 new Setter Upload ", "0.5.0" => "26.10.2020 new Setter Upload and fillup upload queue asynchronously",
"0.4.0" => "18.10.2020 add reqtype to addSendqueue, new Setter prepareDownload ", "0.4.0" => "18.10.2020 add reqtype to addSendqueue, new Setter prepareDownload ",
"0.3.0" => "16.10.2020 create Reading Hash instead of Array ", "0.3.0" => "16.10.2020 create Reading Hash instead of Array ",
"0.2.0" => "16.10.2020 some changes in subroutines ", "0.2.0" => "16.10.2020 some changes in subroutines ",
@ -193,7 +197,7 @@ my $splitstr = "!_ESC_!"; # Split-String zur
my $queueStartFn = "FHEM::SSFile::getApiSites"; # Startfunktion zur Queue-Abarbeitung my $queueStartFn = "FHEM::SSFile::getApiSites"; # Startfunktion zur Queue-Abarbeitung
my $mbf = 1048576; # Divisionsfaktor für Megabytes my $mbf = 1048576; # Divisionsfaktor für Megabytes
my $kbf = 1024; # Divisionsfaktor für Kilobytes my $kbf = 1024; # Divisionsfaktor für Kilobytes
my $found; # Hash für Ergebnis File::find -> wanted my $bound = "wNWT9spu8GvTg4TJo1iN"; # Boundary for Multipart POST
################################################################ ################################################################
sub Initialize { sub Initialize {
@ -208,14 +212,15 @@ sub Initialize {
# Darstellung FHEMWEB # Darstellung FHEMWEB
# $hash->{FW_summaryFn} = \&FWsummaryFn; # $hash->{FW_summaryFn} = \&FWsummaryFn;
#$hash->{FW_addDetailToSummary} = 1 ; # zusaetzlich zu der Device-Summary auch eine Neue mit dem Inhalt von DetailFn angezeigt # $hash->{FW_addDetailToSummary} = 1 ; # zusaetzlich zu der Device-Summary auch eine Neue mit dem Inhalt von DetailFn angezeigt
#$hash->{FW_detailFn} = \&FWdetailFn; # $hash->{FW_detailFn} = \&FWdetailFn;
$hash->{FW_deviceOverview} = 1; $hash->{FW_deviceOverview} = 1;
$hash->{AttrList} = "additionalInfo:multiple-strict,real_path,size,owner,time,perm,mount_point_type,type,volume_status ". $hash->{AttrList} = "additionalInfo:multiple-strict,real_path,size,owner,time,perm,mount_point_type,type,volume_status ".
"disable:1,0 ". "disable:1,0 ".
"interval ". "interval ".
"loginRetries:1,2,3,4,5,6,7,8,9,10 ". "loginRetries:1,2,3,4,5,6,7,8,9,10 ".
"noAsyncFillQueue:1,0 ".
"showPassInLog:1,0 ". "showPassInLog:1,0 ".
"timeout ". "timeout ".
$readingFnAttributes; $readingFnAttributes;
@ -297,6 +302,7 @@ sub Undef {
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $type = $hash->{TYPE}; my $type = $hash->{TYPE};
BlockingKill($hash->{HELPER}{RUNNING_PID}) if($hash->{HELPER}{RUNNING_PID});
delete $data{$type}{$name}; delete $data{$type}{$name};
RemoveInternalTimer($name); RemoveInternalTimer($name);
@ -346,10 +352,14 @@ return;
} }
################################################################ ################################################################
sub Attr { ## no critic 'complexity' sub Attr {
my ($cmd,$name,$aName,$aVal) = @_; my $cmd = shift;
my $name = shift;
my $aName = shift;
my $aVal = shift;
my $hash = $defs{$name}; my $hash = $defs{$name};
my $model = $hash->{MODEL}; my $model = $hash->{MODEL};
my ($do,$val); my ($do,$val);
# $cmd can be "del" or "set" # $cmd can be "del" or "set"
@ -376,6 +386,16 @@ sub Attr { ## no critic 'complexity'
readingsEndUpdate ($hash, 1); readingsEndUpdate ($hash, 1);
} }
if ($cmd eq "set") {
if ($aName =~ m/interval/x && $aVal !~ /^[0-9]+$/x) {
return qq{The value of $aName is not valid. Use only integers 0-9 !};
}
if($aName =~ m/interval/x) {
RemoveInternalTimer($name,"FHEM::SSFile::periodicCall");
InternalTimer (gettimeofday()+1.0, "FHEM::SSFile::periodicCall", $name, 0);
}
}
return; return;
} }
@ -397,7 +417,7 @@ sub Set {
return if(IsDisabled($name)); return if(IsDisabled($name));
my $idxlist = join(",", sort keys %{$data{$type}{$name}{sendqueue}{entries}}); my $idxlist = join(",", sort{$a<=>$b} keys %{$data{$type}{$name}{sendqueue}{entries}});
$setlist = "Unknown argument $opt, choose one of "; $setlist = "Unknown argument $opt, choose one of ";
@ -503,6 +523,8 @@ sub _setDownload {
return qq{No source file or directory specified for download !} return qq{No source file or directory specified for download !}
} }
delReadings ($name, 0);
my @dld = split ",", $fp; my @dld = split ",", $fp;
for my $dl (@dld) { for my $dl (@dld) {
@ -560,7 +582,6 @@ sub _setUpload {
# method => auszuführende API-Methode, # method => auszuführende API-Methode,
# params => "spezifische API-Parameter> # params => "spezifische API-Parameter>
#$arg = smUrlEncode ($arg);
my ($a,$h) = parseParams ($arg); my ($a,$h) = parseParams ($arg);
my $fp = $a->[0]; my $fp = $a->[0];
@ -577,7 +598,6 @@ sub _setUpload {
my $ow = $h->{ow} // "true"; # Überschreiben Steuerbit my $ow = $h->{ow} // "true"; # Überschreiben Steuerbit
my $cdir = $h->{cdir} // "true"; # create Dir Steuerbit my $cdir = $h->{cdir} // "true"; # create Dir Steuerbit
my $bound = "wNWT9spu8GvTg4TJo1iN";
my @uld = split ",", $fp; my @uld = split ",", $fp;
@ -590,22 +610,57 @@ sub _setUpload {
push @all, $obj; push @all, $obj;
} }
undef $found; readingsSingleUpdate($hash, "state", "Wait for filling upload queue", 1);
for my $obj (@all) { # Objekte und Inhalte der Ordner auslesen für add Queue
find ( { wanted => \&wanted, no_chdir => 1 }, $obj );
}
Log3 ($name, 3, "$name - all explored files for upload:\n".Dumper $found); $paref->{allref} = \@all;
$paref->{remDir} = $remDir;
$paref->{ow} = $ow;
$paref->{cdir} = $cdir;
my $found = exploreFiles ($paref);
$paref->{found} = $found;
delReadings ($name, 0);
delete $paref->{allref};
if(AttrVal($name, "noAsyncFillQueue", 0)) { # kein BlockingCall verwenden
__fillUploadQueue ($paref);
}
else {
my $timeout = 1800;
$hash->{HELPER}{RUNNING_PID} = BlockingCall("FHEM::SSFile::__fillUploadQueue", $paref, "FHEM::SSFile::__fillUploadQueueFinish", $timeout, "FHEM::SSFile::blockingTimeout", $hash);
$hash->{HELPER}{RUNNING_PID}{loglevel} = 5 if($hash->{HELPER}{RUNNING_PID}); # Forum #77057
}
return;
}
######################################################################################
# extrahierte Filenamen für Upload in Queue eintragen
######################################################################################
sub __fillUploadQueue {
my $paref = shift;
my $name = $paref->{name};
my $remDir = $paref->{remDir};
my $opt = $paref->{opt};
my $ow = $paref->{ow};
my $cdir = $paref->{cdir};
my $found = $paref->{found};
my $hash = $defs{$name};
# Log3 ($name, 3, "$name - all explored files for upload:\n".Dumper $found);
for my $lcf (keys %{$found}) { for my $lcf (keys %{$found}) {
my $fname = (split "\/", $lcf)[-1]; my $fname = (split "\/", $lcf)[-1];
my $dir = $remDir.$found->{"$lcf"}; # zusammengesetztes Zielverzeichnis my $dir = $remDir.$found->{"$lcf"}; # zusammengesetztes Zielverzeichnis
my $dat; my $dat;
$dat .= addBodyPart ($bound, qq{content-disposition: form-data; name="path"}, $dir, "first"); $dat .= addBodyPart (qq{content-disposition: form-data; name="path"}, $dir, "first");
$dat .= addBodyPart ($bound, qq{content-disposition: form-data; name="create_parents"}, $cdir ); $dat .= addBodyPart (qq{content-disposition: form-data; name="create_parents"}, $cdir );
$dat .= addBodyPart ($bound, qq{content-disposition: form-data; name="overwrite"}, $ow ); $dat .= addBodyPart (qq{content-disposition: form-data; name="overwrite"}, $ow );
$dat .= addBodyPart ($bound, qq{content-disposition: form-data; name="file"; filename="$fname"\r\nContent-Type: application/octet-stream}, "<FILE>", "last" ); $dat .= addBodyPart (qq{content-disposition: form-data; name="file"; filename="$fname"\r\nContent-Type: application/octet-stream}, "<FILE>", "last" );
my $params = { my $params = {
name => $name, name => $name,
@ -618,16 +673,53 @@ sub _setUpload {
postdata => $dat postdata => $dat
}; };
addSendqueue ($params); if(AttrVal($name, "noAsyncFillQueue", 0)) {
addSendqueue ($params);
}
else {
my $json = encode_json ($params);
BlockingInformParent("FHEM::SSFile::__addQueueFromBlocking", [$json], 1);
}
} }
if(AttrVal($name, "noAsyncFillQueue", 0)) {
return __fillUploadQueueFinish ("$name|$opt");
}
return ("$name|$opt");
}
####################################################################################################
# Finishing Upload Queue
####################################################################################################
sub __fillUploadQueueFinish {
my $string = shift;
my ($name, $opt) = split "\\|", $string;
my $hash = $defs{$name};
readingsSingleUpdate($hash, "state", "Upload queue fill finished", 1);
if($opt ne "prepareUpload") { if($opt ne "prepareUpload") {
getApiSites ($name); # Queue starten getApiSites ($name); # Queue starten
} }
return; return;
} }
###################################################################
# SendQueue aus BlockingCall heraus füllen
###################################################################
sub __addQueueFromBlocking {
my $json = shift;
my $params = decode_json ($json);
addSendqueue ($params);
return 1;
}
###################################################################################### ######################################################################################
# Setter startQueue # Setter startQueue
###################################################################################### ######################################################################################
@ -1003,14 +1095,14 @@ sub periodicCall {
else { else {
$new = gettimeofday()+$interval; $new = gettimeofday()+$interval;
readingsBeginUpdate ($hash); readingsBeginUpdate ($hash);
readingsBulkUpdate ($hash, "nextUpdate", "Automatic - next polltime: ".FmtTime($new)); # Abrufmode initial auf "Manual" setzen readingsBulkUpdate ($hash, "nextUpdate", "Automatic - next start Queue time: ".FmtTime($new)); # Abrufmode initial auf "Manual" setzen
readingsEndUpdate ($hash,1); readingsEndUpdate ($hash,1);
$hash->{MODE} = "Automatic"; $hash->{MODE} = "Automatic";
} }
if($hash->{CREDENTIALS} && !IsDisabled($name)) { if(!IsDisabled($name) && ReadingsVal($name, "state", "running") ne "running") {
getApiSites($name); # Queue starten
} }
InternalTimer($new, "FHEM::SSFile::periodicCall", $name, 0); InternalTimer($new, "FHEM::SSFile::periodicCall", $name, 0);
@ -1732,8 +1824,7 @@ return;
# last - Ergänzung des letzten Boundary # last - Ergänzung des letzten Boundary
# #
############################################################################################# #############################################################################################
sub addBodyPart { sub addBodyPart {
my $bound = shift; # Boundary String
my $cdisp = shift; my $cdisp = shift;
my $val = shift; my $val = shift;
my $seq = shift // ""; # Sequence: first, last my $seq = shift // ""; # Sequence: first, last
@ -1753,19 +1844,47 @@ return $part;
} }
############################################################################################# #############################################################################################
# File::Find Auswertungsfunktion # File::Find Explore Files & Directories
# $found: Hash Referenz für file : dir Paare
############################################################################################# #############################################################################################
sub wanted { sub exploreFiles {
my $file = $File::Find::name; my $paref = shift;
my $dir = $File::Find::dir; my $allref = $paref->{allref};
$dir =~ s/^\.//x;
$dir =~ s/\/$//x;
if(-f $file && -r $file) {
$found->{"$file"} = "$dir";
}
my $found;
for my $obj (@$allref) { # Objekte und Inhalte der Ordner auslesen für add Queue
find ( { wanted => sub { my $file = $File::Find::name;
my $dir = $File::Find::dir;
$dir =~ s/^\.//x;
$dir =~ s/\/$//x;
if(-f $file && -r $file) {
$found->{"$file"} = "$dir";
}
},
no_chdir => 1
}, $obj
);
}
return $found;
}
####################################################################################################
# Abbruchroutine BlockingCall
####################################################################################################
sub blockingTimeout {
my ($hash,$cause) = @_;
my $name = $hash->{NAME};
$cause = $cause // "Timeout: process terminated";
Log3 ($name, 1, "$name -> BlockingCall $hash->{HELPER}{RUNNING_PID}{fn} pid:$hash->{HELPER}{RUNNING_PID}{pid} $cause");
setReadingErrorState ($hash, $cause);
delete($hash->{HELPER}{RUNNING_PID});
checkSendRetry ($name, 0, $queueStartFn);
return; return;
} }
@ -1945,7 +2064,7 @@ return;
<ul> <ul>
<a name="prepareUpload"></a> <a name="prepareUpload"></a>
<li><b> prepareUpload "&lt;File&gt;[,&lt;File&gt;,...]" &lt;args&gt;</b> <br> <li><b> prepareUpload "&lt;File&gt;[,&lt;File&gt;,...]" | "&lt;Ordner&gt;[,&lt;Ordner&gt;,...]" &lt;args&gt;</b> <br>
Identisch zum "Upload" Befehl. Die Übertragung der Files zur Synology Diskstation wird allerdings nicht sofort Identisch zum "Upload" Befehl. Die Übertragung der Files zur Synology Diskstation wird allerdings nicht sofort
gestartet, sondern die Einträge nur in die Sendequeue gestellt. gestartet, sondern die Einträge nur in die Sendequeue gestellt.
@ -2108,13 +2227,9 @@ return;
<a name="interval"></a> <a name="interval"></a>
<li><b>interval &lt;Sekunden&gt;</b> <br> <li><b>interval &lt;Sekunden&gt;</b> <br>
Automatisches Abrufintervall der Informationen in Sekunden. Ist "0" agegeben, wird kein automatischer Datenabruf Automatischer Start der internen Queue alle X Sekunden. Alle zum Startzeitpunkt in der Queue enthaltenen Einträge
ausgeführt. (default) <br> (z.B. mit prepareUpload eingefügt) werden abgearbeitet. Ist "0" angegeben, wird kein automatischer Start
Sollen z.B. jede Stunde Informationen abgerufen werden, wird das Attribut wie folgt gesetzt: <br><br> ausgeführt. (default)
<ul>
attr &lt;Name&gt; interval 3600 <br>
</ul>
</li><br> </li><br>
</ul> </ul>
@ -2129,6 +2244,19 @@ return;
</li><br> </li><br>
</ul> </ul>
<ul>
<a name="noAsyncFillQueue"></a>
<li><b>noAsyncFillQueue</b> <br>
Das Füllen der Upload-Queue kann in Abhängigkeit der Anzahl hochzuladender Dateien/Ordner sehr zeitaufwändig sein und FHEM
potentiell blockieren. Aus diesem Grund wird die Queue in einem nebenläufigen Prozess gefüllt. <br>
Sollen nur wenige Einträge verarbeitet werden oder bei sehr schnellen Systemen kann durch Setzen dieses Attributs auf die
Verwendung des zusätzlichen Prozesses verzichtet werden. <br>
(default: 0)
</li><br>
</ul>
<ul> <ul>
<a name="showPassInLog"></a> <a name="showPassInLog"></a>
<li><b>showPassInLog</b> <br> <li><b>showPassInLog</b> <br>
@ -2143,7 +2271,7 @@ return;
<a name="timeout"></a> <a name="timeout"></a>
<li><b>timeout &lt;Sekunden&gt;</b> <br> <li><b>timeout &lt;Sekunden&gt;</b> <br>
Timeout für den Datenabruf in Sekunden. <br> Timeout für die Kommunikation mit der File Station API in Sekunden. <br>
(default: 20) (default: 20)
</li><br> </li><br>