Hallo Rudi,
Für die Arrays hätte ich noch eine Idee:
Da EvalSpecials ja einen String mit mehreren "my varxy" erzeugt, der dann von AnalyzePerlCommand vor den eigentlichen Perl-String gehängt wird, ist es schwer etwas komplexeres als Scalars auf diesem Weg zu übergeben.
Auch mit Referenzen auf Arrays oder Hashes klappt es so offenbar nicht.
Das wäre aber kein Problem, wenn die Werte nicht erst über EvalSpecials gehen müssten, sondern direkt als Referenzen an AnalyzePerlCommand übergeben werden könnten.
Ich habe daher mal überlegt, wie man die Features, die ich für eine zentrale Eval-Funktion von HTTPMOD und Modbus bräuchte, doch noch in AnalyzePerlCommand bekommen könnte.
Grundidee ist dabei dass man eine Hash-Referenz für die bereitzustellenden Variablen direkt an AnalyzePerlCommand übergeben kann.
Mir ist klar, dass das nicht ganz so schöne Überschneidungen zu EvalSpecials mit sich bringt, aber ich wollte die Idee dennoch mal weitergeben. Vielleicht passt es ja doch.
Im Prinzip reichen ein paar zusätzliche Zeilen (mit ### new ### gekennzeichnet):
#####################################
sub
AnalyzePerlCommand($$;$)
{
my ($cl, $cmd, $calledFromChain) = @_; # third parmeter is deprecated
return "Forbidden command $cmd." if($cl && !Authorized($cl, "cmd", "perl"));
$cmd =~ s/\\ *\n/ /g; # Multi-line. Probably not needed anymore
# Make life easier for oneliners:
if($featurelevel <= 5.6) {
%value = ();
foreach my $d (keys %defs) {
$value{$d} = $defs{$d}{STATE}
}
}
my ($sec,$min,$hour,$mday,$month,$year,$wday,$yday,$isdst) =
localtime(gettimeofday());
$month++; $year+=1900;
my $today = sprintf('%04d-%02d-%02d', $year,$month,$mday);
my $hms = sprintf("%02d:%02d:%02d", $hour, $min, $sec);
my $we = IsWe(undef, $wday);
if($evalSpecials) {
$cmd = join("", map { my $n = substr($_,1); # ignore the %
my $v = $evalSpecials->{$_};
$v =~ s/(['\\])/\\$1/g;
"my \$$n='$v';";
} keys %{$evalSpecials})
. $cmd;
# Normally this is deleted in AnalyzeCommandChain, but ECMDDevice calls us
# directly, and combining perl with something else isnt allowed anyway.
$evalSpecials = undef if(!$calledFromChain);
}
### new ###
# new block to allow arrays and hashes to be passed to AnalyzePerlCommand as third parameter
my $arg_ref = $calledFromChain; # better rename above already
if (ref ($arg_ref) eq 'HASH') {
my $assign = '';
foreach my $key (keys %{$arg_ref}) {
my $type = ref $arg_ref->{$key};
my $vName = substr($key,1);
if ($type eq 'SCALAR') {
$assign .= "my \$$vName = \${\$arg_ref->{'$key'}};";
} elsif ($type eq 'ARRAY') {
$assign .= "my \@$vName = \@{\$arg_ref->{'$key'}};";
} elsif ($type eq 'HASH') {
$assign .= "my \%$vName = \%{\$arg_ref->{'$key'}};";
}
}
$cmd = $assign . $cmd;
}
### end new feature block ###
$cmdFromAnalyze = $cmd;
local $SIG{__WARN__} = sub { Log 1, "$arg_ref->{action} with expresion $cmd created warning: @_"; }
if ((ref ($arg_ref) eq 'HASH') && ($arg_ref->{action}));
my $ret = eval $cmd;
if($@) {
$ret = $@;
Log 1, ($arg_ref->{action} ? "$arg_ref->{action} created " : '') . "ERROR evaluating $cmd: $ret";
}
$cmdFromAnalyze = undef;
return $ret;
}
der Aufruf in HTTPMOD würde dann z.B. so aussehen:
my $erg = AnalyzePerlCommand(undef, $code, {'@val2' => \@val2, '$val' => \$val, '%testHash' => \%testHash});
Falls das soweit nicht abwegig ist, würde ich gerne noch ein zweites Feature behalten:
Bisher Logge ich in HTTPMOD, Modbus und FReplacer bei Errors und Warnings aus Perl-Expressions auch einen Kontext als Hinweis, in welchem Attribut / Feature die fehlerhafte Expression zu finden ist. Das hat mir bisher im Support / bei der Fehlersuche sehr geholfen.
Um das in AnalyzePerlCommand auch unter zu bekommen reicht eigentlich eine Anweisung vor dem eval (auch auf obiger Erweiterung basierend) und eine entsprechende Ergänzung bei der Fehler-Protokollierung:
local $SIG{__WARN__} = sub { Log 1, "$arg_ref->{action} with expresion $cmd created warning: @_"; }
if ((ref ($arg_ref) eq 'HASH') && ($arg_ref->{action}));
Den Kontext würde man dann in dem neuen Hash einfach als weiteren Eintrag hinzufügen:
action => 'reading53Expr'
ich habe auch kein Problem damit, eine solche Funktion nur für HTTPMOD, Modbus und FReplacer in ein Modul zu packen, aber schöner wäre es natürlich wenn AnalyzePerlCommand auch Arrays oder Hashes für eine Expression bereitstellen könnte.
Was meinst Du?
Gruss
Stefan