mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-05-01 20:20:10 +00:00
98_Modbus: added template and Test utils
git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@23487 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
ae01ad22fd
commit
aa4e4ed7da
223
lib/FHEM/Modbus/TestUtils.pm
Normal file
223
lib/FHEM/Modbus/TestUtils.pm
Normal file
@ -0,0 +1,223 @@
|
||||
#########################################################################
|
||||
# $Id$
|
||||
# Utility functions for testing Modbus that can be uses by other Fhem modules
|
||||
#
|
||||
# This file is part of fhem.
|
||||
#
|
||||
# Fhem is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fhem is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with fhem. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
package FHEM::Modbus::TestUtils;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use GPUtils qw(:all);
|
||||
use Time::HiRes qw(gettimeofday);
|
||||
|
||||
use Exporter ('import');
|
||||
our @EXPORT_OK = qw(
|
||||
CallStep
|
||||
NextStep
|
||||
LogStep
|
||||
SimRead
|
||||
findTimesInLog
|
||||
calcDelays
|
||||
SetTestOptions
|
||||
);
|
||||
|
||||
our %EXPORT_TAGS = (all => [@EXPORT_OK]);
|
||||
|
||||
BEGIN {
|
||||
GP_Import( qw(
|
||||
fhem
|
||||
Log3
|
||||
RemoveInternalTimer
|
||||
InternalTimer
|
||||
gettimeofday
|
||||
|
||||
FmtDateTime
|
||||
FmtTimeMs
|
||||
ReadingsVal
|
||||
ReadingsTimestamp
|
||||
AttrVal
|
||||
InternalVal
|
||||
featurelevel
|
||||
|
||||
FhemTestUtils_getLogTime
|
||||
|
||||
defs
|
||||
modules
|
||||
attr
|
||||
done_testing
|
||||
logInform
|
||||
));
|
||||
};
|
||||
|
||||
|
||||
our $testStep = 0;
|
||||
our %testOptions;
|
||||
our %results;
|
||||
our $nextStepMode = 'auto';
|
||||
|
||||
sub SimRead {
|
||||
my $name = shift;
|
||||
my $readFn = shift;
|
||||
my $text = shift;
|
||||
my $option = shift;
|
||||
my $hash = $defs{$name};
|
||||
my $data;
|
||||
Log3 undef, 1, "Test simulate reception of $text";
|
||||
if ($option && $option eq 'ASCII') {
|
||||
$data = $text; # ascii
|
||||
} else {
|
||||
$data = pack ('H*', $text); # hex coded binary
|
||||
}
|
||||
$hash->{TestInput} = $data;
|
||||
eval { &{$readFn}($hash) };
|
||||
if ($@) {
|
||||
Log3 undef, 1, "Test step $testStep call to readFn created error: $@";
|
||||
} else {
|
||||
Log3 undef, 1, "Test step $testStep readFn done.";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub GetNextStep {
|
||||
#Log3 undef, 1, "Test GetNextStep: look for next step";
|
||||
my $next = $testStep;
|
||||
FINDSTEP:
|
||||
while (1) {
|
||||
$next++;
|
||||
return 0 if ($next > 99);
|
||||
#Log3 undef, 1, "Test GetNextStep: check step $next";
|
||||
next FINDSTEP if (!defined (&{"main::testStep$next"}));
|
||||
return $next;
|
||||
}
|
||||
return; # never reached
|
||||
}
|
||||
|
||||
|
||||
sub NextStep {
|
||||
my $delay = shift // 0;
|
||||
my $next = GetNextStep();
|
||||
|
||||
InternalTimer(gettimeofday() + $delay, \&CallStep, "main::testStep$next");
|
||||
#Log3 undef, 1, "Test NextStep: done.";
|
||||
$testStep = $next;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
sub CallStep {
|
||||
my $func = shift;
|
||||
Log3 undef, 1, "----------------------------------------------------";
|
||||
Log3 undef, 1, "Test step $testStep";
|
||||
|
||||
no strict "refs"; ## no critic - function name needs to be string
|
||||
my $delay = eval { &{$func}() };
|
||||
if ($@) {
|
||||
Log3 undef, 1, "Test step $testStep call created error: $@";
|
||||
} else {
|
||||
Log3 undef, 1, "Test step $testStep done.";
|
||||
}
|
||||
|
||||
my $next = GetNextStep();
|
||||
if (!$next) { # done if no more steps
|
||||
Log3 undef, 1, "Test NextStep: no more steps found - exiting";
|
||||
done_testing;
|
||||
exit(0);
|
||||
}
|
||||
if ($nextStepMode eq 'auto') {
|
||||
NextStep($delay); # set timer to go to next step with delay returned by last
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
sub LogStep {
|
||||
my $msg = shift // '';
|
||||
Log3 undef, 1, "Test step $testStep: $msg";
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
sub ReactOnSendingLog {
|
||||
my $name = shift;
|
||||
my $line = shift;
|
||||
#die "line got: $line";
|
||||
if ($line =~ /$testOptions{RespondTo}/) {
|
||||
my $send = $1;
|
||||
my $id = substr ($send, 0, 2);
|
||||
my $recv = $testOptions{ReplyHash}{$send} // (($id . '800041c0')); # simulate broken error response by default
|
||||
my $delay = $testOptions{ResponseDelay} // 0.05;
|
||||
Log3 undef, 1, "------------------------------------------------------------------------";
|
||||
Log3 undef, 1, "Test saw sending $send, id $id, set timer to simulate receiving $recv in $delay";
|
||||
InternalTimer(gettimeofday() + $delay, \&SimResponseRead, $recv);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
sub SimResponseRead {
|
||||
my $data = shift;
|
||||
Log3 undef, 1, "Test now simulate reception of response and then call next step";
|
||||
SimRead($testOptions{IODevice}, $testOptions{ReadFn}, $data);
|
||||
NextStep();
|
||||
}
|
||||
|
||||
|
||||
sub SetTestOptions {
|
||||
my $opt = shift;
|
||||
foreach my $k (keys %{$opt}) {
|
||||
$testOptions{$k} = $opt->{$k};
|
||||
}
|
||||
if ($testOptions{RespondTo}) {
|
||||
$nextStepMode = 'reception';
|
||||
$logInform{$testOptions{IODevice}} = \&ReactOnSendingLog;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub findTimesInLog {
|
||||
$results{$testOptions{Time1Name}.$testStep} = FhemTestUtils_getLogTime($testOptions{Time1Regex});
|
||||
$results{$testOptions{Time2Name}.$testStep} = FhemTestUtils_getLogTime($testOptions{Time2Regex});
|
||||
Log3 undef, 1, "Test step $testStep: LogTime for last $testOptions{Time1Name} is " .
|
||||
($results{$testOptions{Time1Name}.$testStep} ? FmtTimeMs($results{$testOptions{Time1Name}.$testStep}) : 'unknown');
|
||||
Log3 undef, 1, "Test step $testStep: LogTime for last $testOptions{Time2Name} is " .
|
||||
($results{$testOptions{Time2Name}.$testStep} ? FmtTimeMs($results{$testOptions{Time2Name}.$testStep}) : 'unknown');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
sub calcDelays {
|
||||
my ($lastDelay, $commDelay, $sendDelay);
|
||||
if (defined ($results{$testOptions{Time1Name} . $testStep}) &&
|
||||
defined ($results{$testOptions{Time2Name} . $testStep})) {
|
||||
$lastDelay = sprintf '%.3f', ($results{$testOptions{Time2Name} . $testStep} // 0) - ($results{$testOptions{Time1Name} . ($testStep)} // 0);
|
||||
Log3 undef, 1, "Test step $testStep: delay between $testOptions{Time1Name} in step " . ($testStep) . " and $testOptions{Time2Name} in step $testStep is $lastDelay";
|
||||
}
|
||||
if (defined ($results{$testOptions{Time1Name} . ($testStep - 1)}) &&
|
||||
defined ($results{$testOptions{Time2Name} . ($testStep - 1)})) {
|
||||
$commDelay = sprintf '%.3f', ($results{$testOptions{Time1Name} . $testStep} // 0) - ($results{$testOptions{Time2Name} . ($testStep - 1)} // 0);
|
||||
$sendDelay = sprintf '%.3f', ($results{$testOptions{Time1Name} . $testStep} // 0) - ($results{$testOptions{Time1Name} . ($testStep - 1)} // 0);
|
||||
|
||||
Log3 undef, 1, "Test step $testStep: delay between $testOptions{Time2Name} in step " . ($testStep - 1) . " and $testOptions{Time1Name} in step $testStep is $commDelay, between each $testOptions{Time1Name} $sendDelay";
|
||||
}
|
||||
return ($commDelay, $sendDelay, $lastDelay);
|
||||
}
|
||||
|
||||
|
||||
1;
|
42
lib/FHEM/Modbus/modTemplate
Normal file
42
lib/FHEM/Modbus/modTemplate
Normal file
@ -0,0 +1,42 @@
|
||||
##############################################
|
||||
# $Id: 98_ModbusGen${fName}.pm automatically generated by ModbusAttr $
|
||||
# module generated automatically by ModbusAttr
|
||||
|
||||
package ModbusGen${fName};
|
||||
use strict;
|
||||
use warnings;
|
||||
use GPUtils qw(:all);
|
||||
|
||||
use Exporter ('import');
|
||||
our @EXPORT_OK = qw();
|
||||
our %EXPORT_TAGS = (all => [@EXPORT_OK]);
|
||||
|
||||
BEGIN {
|
||||
GP_Import( qw(
|
||||
LoadModule
|
||||
));
|
||||
GP_Export( qw(
|
||||
Initialize
|
||||
));
|
||||
};
|
||||
|
||||
|
||||
my %ModbusGen${fName}parseInfo = (
|
||||
${t}
|
||||
|
||||
|
||||
#####################################
|
||||
sub Initialize {
|
||||
my $modHash = shift;
|
||||
$modHash->{parseInfo} = \%ModbusGen${fName}parseInfo; # defines registers, inputs, coils etc. for this Modbus Defive
|
||||
$modHash->{deviceInfo} = \%ModbusGen${fName}deviceInfo; # defines properties of the device like defaults and supported function codes
|
||||
|
||||
LoadModule "Modbus";
|
||||
Modbus::InitializeLD($modHash); # Generic function of the Modbus module does the rest
|
||||
|
||||
$modHash->{AttrList} = $modHash->{AttrList} . " " . # Standard Attributes like IODEv etc
|
||||
$modHash->{ObjAttrList} . " " . # Attributes to add or overwrite parseInfo definitions
|
||||
$modHash->{DevAttrList} . " " . # Attributes to add or overwrite devInfo definitions
|
||||
"poll-.* " . # overwrite poll with poll-ReadingName
|
||||
"polldelay-.* "; # overwrite polldelay with polldelay-ReadingName
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user