diff --git a/fhem/t/FHEM/98_Modbus/10_Timer.cfg b/fhem/t/FHEM/98_Modbus/10_Timer.cfg new file mode 100644 index 000000000..7ba6b0eaa --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/10_Timer.cfg @@ -0,0 +1,3 @@ +define M1 ModbusAttr 1 1 +attr M1 verbose 5 +define MS Modbus none diff --git a/fhem/t/FHEM/98_Modbus/10_Timer.t b/fhem/t/FHEM/98_Modbus/10_Timer.t new file mode 100644 index 000000000..e4d63d89d --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/10_Timer.t @@ -0,0 +1,22 @@ +############################################## +# test update timer +############################################## +use strict; +use warnings; +use Test::More; + +fhem('set M1 interval 0.2'); +is(FhemTestUtils_gotLog("changed interval to 0.2 seconds"), 1, "set interval in log"); +fhem('set M1 interval test'); +is(FhemTestUtils_gotLog("set interval test not valid"), 1, "invalid interval in log"); + + +InternalTimer(time()+1, sub() { + isnt(FhemTestUtils_gotLog("GetUpdate called"), 0, "GetUpdate in log"); + isnt(FhemTestUtils_gotLog("UpdateTimer called from.*GetUpdate"), 0, "UpdateTimer in log"); + + done_testing; + exit(0); +}, 0); + +1; diff --git a/fhem/t/FHEM/98_Modbus/12_Delays.cfg b/fhem/t/FHEM/98_Modbus/12_Delays.cfg new file mode 100644 index 000000000..00841e863 --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/12_Delays.cfg @@ -0,0 +1,234 @@ +define MS Modbus none +attr MS verbose 4 +attr MS clientSwitchDelay 0 +attr MS busDelay 0 + + +define M5 ModbusAttr 5 0 +attr M5 verbose 3 +attr M5 dev-timing-sendDelay 0 +attr M5 dev-timing-commDelay 0 +attr M5 nonPrioritizedGet 1 + +attr M5 obj-h256-reading TempWasserEin +attr M5 obj-h258-reading TempWasserAus + +attr M5 obj-h10-reading o1 +attr M5 obj-h10-map 0:off, 1:on +attr M5 obj-h10-set 1 +attr M5 obj-h11-reading o2 +attr M5 obj-h11-hint 1,2,3 + +attr M5 dev-h-defSet 1 +attr M5 dev-h-defShowGet 1 + + +define M1 ModbusAttr 1 0 +attr M1 verbose 3 +attr M1 dev-timing-sendDelay 0 +attr M1 dev-timing-commDelay 0 +attr M1 nonPrioritizedGet 1 + +attr M1 dev-c-defPoll 1 +attr M1 dev-h-combine 5 +attr M1 dev-h-defLen 2 +attr M1 dev-h-defPoll 1 +attr M1 dev-h-defRevRegs 1 +attr M1 dev-h-write 16 +attr M1 dev-i-defFormat %.1f +attr M1 dev-i-defLen 2 +attr M1 dev-i-defPoll 1 +attr M1 dev-i-defRevRegs 1 +attr M1 dev-i-defUnpack f> +attr M1 dev-type-VT_Date-expr sprintf("%02d.%02d",($val >> 8),($val & 0xff)) +attr M1 dev-type-VT_Date-len 1 +attr M1 dev-type-VT_Date-unpack n +attr M1 dev-type-VT_R4-format %.1f +attr M1 dev-type-VT_R4-len 2 +attr M1 dev-type-VT_R4-revRegs 1 +attr M1 dev-type-VT_R4-unpack f> +attr M1 dev-type-VT_String-bswapRegs 1 +attr M1 dev-type-VT_String-decode cp850 +attr M1 dev-type-VT_String-encode utf8 +attr M1 dev-type-VT_String-expr $val =~ s/[\00]+//gr +attr M1 dev-type-VT_String-len 8 +attr M1 dev-type-VT_String-revRegs 0 +attr M1 dev-type-VT_String-unpack a* +attr M1 dev-type-VT_Time-expr sprintf("%02d:%02d",($val >> 8),($val & 0xff)) +attr M1 dev-type-VT_Time-len 1 +attr M1 dev-type-VT_Time-unpack n + +attr M1 obj-c1009-map 0:false, 1:true +attr M1 obj-c1009-polldelay 3600 +attr M1 obj-c1009-reading HeatOff +attr M1 obj-c1329-map 0:false, 1:true +attr M1 obj-c1329-polldelay 300 +attr M1 obj-c1329-reading HeatIncreaseOff +attr M1 obj-c1409-map 0:0, 1:1=2, 2:1->2, 3:1->2->3 +attr M1 obj-c1409-polldelay 60 +attr M1 obj-c1409-reading AuxilaryModeHeating +attr M1 obj-c1457-map 0:false, 1:true +attr M1 obj-c1457-reading CoolOff +attr M1 obj-c1633-map 0:false, 1:true +attr M1 obj-c1633-reading DomesticWaterOff +attr M1 obj-h1-len 13 +attr M1 obj-h1-poll 0 +attr M1 obj-h1-reading RTCTime +attr M1 obj-h1-unpack H* +attr M1 obj-h1025-polldelay 86400 +attr M1 obj-h1025-reading HeatTimeOn +attr M1 obj-h1025-type VT_Time +attr M1 obj-h1041-polldelay 86400 +attr M1 obj-h1041-reading HeatTimeOff +attr M1 obj-h1041-type VT_Time +attr M1 obj-h1057-max 30 +attr M1 obj-h1057-min 10 +attr M1 obj-h1057-reading HeatCharacteristicSetPoint +attr M1 obj-h1057-type VT_R4 +attr M1 obj-h1089-max 65 +attr M1 obj-h1089-min 15 +attr M1 obj-h1089-reading HeatCharacteristicSetPointBaseTemp +attr M1 obj-h1089-set 1 +attr M1 obj-h1089-type VT_R4 +attr M1 obj-h1121-max 100 +attr M1 obj-h1121-min 0 +attr M1 obj-h1121-reading HeatCharacteristicGradient +attr M1 obj-h1121-type VT_R4 +attr M1 obj-h1153-max 72 +attr M1 obj-h1153-min 10 +attr M1 obj-h1153-reading HeatCharacteristicLimit +attr M1 obj-h1153-type VT_R4 +attr M1 obj-h1185-reading HeatReturnTemp +attr M1 obj-h1185-type VT_R4 +attr M1 obj-h1249-max 3 +attr M1 obj-h1249-min 1 +attr M1 obj-h1249-reading HeatTempHyst +attr M1 obj-h1249-type VT_R4 +attr M1 obj-h1281-reading RoomTempNominal +attr M1 obj-h1281-type VT_R4 +attr M1 obj-h1313-len 1 +attr M1 obj-h1313-max 200 +attr M1 obj-h1313-min 0 +attr M1 obj-h1313-reading RoomTempFactor +attr M1 obj-h1313-unpack S> +attr M1 obj-h1345-polldelay 86400 +attr M1 obj-h1345-reading HeatIncreaseTimeOn +attr M1 obj-h1345-type VT_Time +attr M1 obj-h1361-polldelay 86400 +attr M1 obj-h1361-reading HeatIncreaseTimeOff +attr M1 obj-h1361-type VT_Time +attr M1 obj-h1377-max 5 +attr M1 obj-h1377-min -5 +attr M1 obj-h1377-reading HeatIncreaseSetPtOffset +attr M1 obj-h1377-type VT_R4 +attr M1 obj-h1425-max 5 +attr M1 obj-h1425-min 0 +attr M1 obj-h1425-reading AuxilaryMaxDifference +attr M1 obj-h1425-type VT_R4 +attr M1 obj-h1473-polldelay 86400 +attr M1 obj-h1473-reading CoolTimeOn +attr M1 obj-h1473-type VT_Time +attr M1 obj-h1489-polldelay 86400 +attr M1 obj-h1489-reading CoolTimeOff +attr M1 obj-h1489-type VT_Time +attr M1 obj-h1505-max 30 +attr M1 obj-h1505-min 18 +attr M1 obj-h1505-reading CoolCharacteristicSetPoint +attr M1 obj-h1505-type VT_R4 +attr M1 obj-h1569-reading CoolReturnTempNominal +attr M1 obj-h1569-type VT_R4 +attr M1 obj-h1601-max 3 +attr M1 obj-h1601-min 1 +attr M1 obj-h1601-reading CoolReturnTempHyst +attr M1 obj-h1601-type VT_R4 +attr M1 obj-h1649-polldelay 86400 +attr M1 obj-h1649-reading DomesticWaterTimeOn +attr M1 obj-h1649-type VT_Time +attr M1 obj-h1665-polldelay 86400 +attr M1 obj-h1665-reading DomesticWaterTimeOff +attr M1 obj-h1665-type VT_Time +attr M1 obj-h1713-reading DomesticWaterTempNominal +attr M1 obj-h1713-set 1 +attr M1 obj-h1713-type VT_R4 +attr M1 obj-h1745-max 10 +attr M1 obj-h1745-min 5 +attr M1 obj-h1745-reading DomesticWaterTempHyst +attr M1 obj-h1745-type VT_R4 +attr M1 obj-h1777-len 16 +attr M1 obj-h1777-polldelay 86400 +attr M1 obj-h1777-reading LegionellaSchedule +attr M1 obj-h1777-unpack H* +attr M1 obj-h1793-polldelay 86400 +attr M1 obj-h1793-type VT_Time +attr M1 obj-h1809-polldelay 86400 +attr M1 obj-h1809-reading LegionellaTimeOff +attr M1 obj-h1809-type VT_Time +attr M1 obj-h209-len 13 +attr M1 obj-h209-poll 0 +attr M1 obj-h209-reading RTCDate +attr M1 obj-h209-unpack H* +attr M1 obj-h417-len 1 +attr M1 obj-h417-polldelay 86400 +attr M1 obj-h417-reading LngSelect +attr M1 obj-h4497-reading PElectric +attr M1 obj-h4497-type VT_R4 +attr M1 obj-h4529-reading PThermal +attr M1 obj-h4529-type VT_R4 +attr M1 obj-h4689-polldelay 86400 +attr M1 obj-h4689-reading FirmwareVersion +attr M1 obj-h4689-showGet 1 +attr M1 obj-h4689-type VT_String +attr M1 obj-h4689-unpack (a*) +attr M1 obj-h4817-polldelay 86400 +attr M1 obj-h4817-reading FirmwareDate +attr M1 obj-h4817-type VT_String +attr M1 obj-h4945-polldelay 86400 +attr M1 obj-h4945-reading ManufType +attr M1 obj-h4945-type VT_String +attr M1 obj-h5073-polldelay 86400 +attr M1 obj-h5073-reading ManufSerialNum +attr M1 obj-h5073-type VT_String +attr M1 obj-h5457-len 1 +attr M1 obj-h5457-map 0048:Kühlung, 0040:Idle, 0051:Warmwasser, 0052:Heizung +attr M1 obj-h5457-reading OperatingState +attr M1 obj-h5457-unpack H* +attr M1 obj-h5505-len 16 +attr M1 obj-h5505-reading ADC_Error +attr M1 obj-h5505-unpack H* +attr M1 obj-h5521-reading LCD_Display_Line_1 +attr M1 obj-h5521-type VT_String +attr M1 obj-h5649-reading LCD_Display_Line_2 +attr M1 obj-h5649-type VT_String +attr M1 obj-i1217-reading HeatReturnTempNominal +attr M1 obj-i1537-reading CoolReturnTemp +attr M1 obj-i1681-reading DomesticWaterTempActual +attr M1 obj-i2625-reading OHCompressor1 +attr M1 obj-i2657-reading OHCompressor2 +attr M1 obj-i2689-reading OHHeatingCompressor +attr M1 obj-i2721-reading OHHeatingAuxilary +attr M1 obj-i2753-reading OHCooling +attr M1 obj-i2785-reading OHDomesticWaterCompressor +attr M1 obj-i433-reading OutdoorTemp +attr M1 obj-i433-showGet 1 +attr M1 obj-i4561-reading COP +attr M1 obj-i4561-showGet 1 +attr M1 obj-i465-reading OutdoorTemp1h +attr M1 obj-i497-reading OutdoorTemp24h +attr M1 obj-i529-reading HeatSourceIn +attr M1 obj-i561-reading HeatSourceOut +attr M1 obj-i593-reading EvaporationTemp +attr M1 obj-i625-reading SuctionGasTemp +attr M1 obj-i657-reading EvaporationPress +attr M1 obj-i689-reading ReturnTempNominal +attr M1 obj-i721-reading ReturnTemp +attr M1 obj-i753-reading FlowTemp +attr M1 obj-i785-reading CondensationTemp +attr M1 obj-i817-reading CondensationPress +attr M1 obj-i849-reading RoomTemp +attr M1 obj-i881-reading RoomTemp1h +attr M1 obj-i913-reading DomesticWaterTemp +attr M1 obj-i945-reading PoolTemp +attr M1 obj-i977-reading SolarTemp + +attr M1 sortUpdate 1 + diff --git a/fhem/t/FHEM/98_Modbus/12_Delays.old b/fhem/t/FHEM/98_Modbus/12_Delays.old new file mode 100644 index 000000000..cd794022c --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/12_Delays.old @@ -0,0 +1,256 @@ +############################################## +# test request parsing +############################################## +use strict; +use warnings; +use Test::More; +use Time::HiRes qw( gettimeofday tv_interval); # return time as float, not just full seconds +use FHEM::HTTPMOD::Utils qw(:all); + +$logInform{'MS'} = \&ReactOnSendingLog; + +my %rData = ( +'010403d1000221b6' => '01040400000000fb84', +'010404c100022107' => '010404000041b4cba3', +'0104060100022083' => '0104045262419dbb1b', +'01040691000220ae' => '010404000042524ad9', +'01040a4100022207' => '010404533c458c19', +'01040a61000223cd' => '01040400000000fb84', +'01040a810002223b' => '0104049dff454cd77d', +'01040aa1000223f1' => '01040400000000fb84', +'01040ac1000223ef' => '0104044d6345282e78', +'01040ae100022225' => '010404b6f644980f54', +'010411d1000224ce' => '01040400000000fb84', + +'050303020001240a' => '0503020122c80d', +'05030309000155c8' => '05030200004984', +'0503010600016473' => '0503020106c816', +'05030100000585b1' => '05030a0137110001381100010dac7b', +'0503010000018472' => '050302013709c2', + +'0506030900005808' => '0506030900005808', # set hyst mode +'0506030201182850' => '0506030201182850' # set temp soll 28 +); + +my $testStep = 0; +my %results; +fhem 'attr global mseclog 1'; + +sub SimReadMS { + my $text = shift; + my $data = pack ('H*', $text); + my $hash = $defs{MS}; + my $name = 'MS'; + Log3 undef, 1, "SimReadMS: simulate reception of $text"; + $hash->{TestInput} = $data; + Modbus::ReadFn($hash); + #$hash->{READ}{BUFFER} = $data; + #$hash->{REMEMBER}{lrecv} = gettimeofday(); + #Modbus::ParseFrameStart($hash); + #Modbus::HandleResponse($hash); + Log3 undef, 1, "SimReadMS: look for next step"; + FINDSTEP: + while (1) { + $testStep++; + if ($testStep > 99) { + InternalTimer(gettimeofday(), "testStepLast", 0); + Log3 undef, 1, "SimReadMS: set timer to go to last step and finish testing"; + last FINDSTEP; + } + Log3 undef, 1, "SimReadMS: check step $testStep"; + next FINDSTEP if (!defined (&{"testStep$testStep"})); + InternalTimer(gettimeofday(), "testStep$testStep", 0); + Log3 undef, 1, "SimReadMS: set timer to call test $testStep"; + last FINDSTEP; + } + Log3 undef, 1, "SimReadMS: done."; + return; +} + + +sub ReactOnSendingLog { + my $name = shift; + my $line = shift; + #die "line got: $line"; + if ($line =~ /MS: Simulate sending to none: (.*)/) { + my $send = $1; + my $id = substr ($send, 0,2); + my $recv = $rData{$send} // (($id . '800041c0')); + Log3 undef, 1, "Test: saw sending $send, id $id, simulate receiving $recv"; + InternalTimer(gettimeofday() + 0.05, \&SimReadMS, $recv); + } + return; +} + + +sub findTimesInLog { + $results{'send'.$testStep} = FhemTestUtils_getLogTime('MS:\sSimulate\ssending', 'last'); + $results{'recv'.$testStep} = FhemTestUtils_getLogTime('ParseFrameStart\s\(RTU\)\sextracted\sid', 'last'); + Log3 undef, 1, "TEST$testStep: LogTime for last Sending is " . + ($results{'send'.$testStep} // 'unknown') . " converted: " . FmtTimeMs($results{'send'.$testStep}); + Log3 undef, 1, "TEST$testStep: LogTime for last Reception is " . + ($results{'recv'.$testStep} // 'unknown') . " converted: " . FmtTimeMs($results{'recv'.$testStep}); + return; +} + + +sub calcDelays { + my $commDelay = $results{'send' . $testStep} - $results{'recv' . ($testStep - 1)}; + my $sendDelay = $results{'send' . $testStep} - $results{'send' . ($testStep - 1)}; + Log3 undef, 1, "TEST$testStep: delay between receive in step " . ($testStep - 1) . " and send in step $testStep is $commDelay, send delay $sendDelay"; + return ($commDelay, $sendDelay); +} + + +fhem 'get M1 SolarTemp'; +InternalTimer(gettimeofday()+5, "testStepLast", 0); # last resort + + +sub testStep1 { + Log3 undef, 1, "TEST$testStep: initial step called"; + findTimesInLog(); + FhemTestUtils_resetLogs(); + + is(FhemTestUtils_gotEvent('M1:SolarTemp'), 1, "Event SolarTemp ..."); + + fhem 'get M1 HeatOff'; + # read simulation is triggered when sending is seen in the log. + # next step is called when read simulation is done. +} + + +sub testStep2 { + findTimesInLog(); + FhemTestUtils_resetLogs(); + my ($commDelay, $sendDelay) = calcDelays(); + + # check no delay between read (get SolarTemp) after Step 0 and send (get HeatOff) in step 1 + ok($commDelay < 0.1, 'normal delay from read solar temp to send get HeatOff smaller than 0.1'); + + fhem 'attr M1 dev-timing-sendDelay 0.2'; # send in step2 should be 0.2 after send in step1 + fhem 'get M1 HeatOff'; +} + + +sub testStep3 { + findTimesInLog(); + FhemTestUtils_resetLogs(); + my ($commDelay, $sendDelay) = calcDelays(); + + # check send delay between read (get HeatOff) after Step 1 and send (get HeatOff) in step 2 + ok($sendDelay >= 0.2, 'defined send delay from read HeatOff to next send get HeatOff big enough'); + ok($sendDelay < 0.22, 'defined send delay from read HeatOff to next send get HeatOff not too big'); + + fhem 'get M5 TempWasserEin'; +} + + +sub testStep4 { + findTimesInLog(); + FhemTestUtils_resetLogs(); + my ($commDelay, $sendDelay) = calcDelays(); + + # check no send delay between read (get HeatOff) after Step 2 and send (get TempWasserEin to id 5) in step 3 + ok($sendDelay < 0.1, 'defined send delay on id 1 from read HeatOff to send get TempWasserEin not used for id 5'); + + fhem 'attr MS busDelay 0.2'; + fhem 'get M5 TempWasserAus'; +} + + +sub testStep5 { + findTimesInLog(); + FhemTestUtils_resetLogs(); + my ($commDelay, $sendDelay) = calcDelays(); + + # check bus delay between read (get TempWasserEin) after Step 3 and send (get TempWasserAus) in step 4 + ok($commDelay >= 0.2, 'defined bus delay big enough'); + ok($commDelay < 0.22, 'defined bus delay not too big'); + + fhem 'attr MS busDelay 0'; + fhem 'attr M1 dev-timing-sendDelay 0'; + fhem 'attr MS clientSwitchDelay 0'; + fhem 'get M1 SolarTemp'; +} + + +sub testStep6 { + findTimesInLog(); + FhemTestUtils_resetLogs(); + my ($commDelay, $sendDelay) = calcDelays(); + + ok($sendDelay < 0.1, 'no delay'); + fhem 'attr MS clientSwitchDelay 0.2'; + fhem 'get M5 TempWasserEin'; +} + + +sub testStep7 { + findTimesInLog(); + FhemTestUtils_resetLogs(); + my ($commDelay, $sendDelay) = calcDelays(); + + ok($commDelay >= 0.2, 'defined clsw delay big enough'); + ok($commDelay < 0.22, 'defined clsw delay not too big'); + + fhem 'get M5 TempWasserAus'; +} + + +sub testStep8 { + findTimesInLog(); + FhemTestUtils_resetLogs(); + my ($commDelay, $sendDelay) = calcDelays(); + + ok($sendDelay < 0.1, 'no delay for same id'); + + fhem 'attr M5 dev-timing-commDelay 0.2'; + fhem 'get M5 TempWasserEin'; +} + + +sub testStep9 { + findTimesInLog(); + FhemTestUtils_resetLogs(); + my ($commDelay, $sendDelay) = calcDelays(); + + ok($commDelay >= 0.2, 'defined comm delay big enough'); + ok($commDelay < 0.22, 'defined comm delay not too big'); + + fhem 'attr M5 dev-timing-commDelay 0'; + fhem 'get M5 TempWasserEin'; +} + + +sub testStep10 { + findTimesInLog(); + FhemTestUtils_resetLogs(); + my ($commDelay, $sendDelay) = calcDelays(); + + ok($commDelay < 0.07, 'zero comm delay'); + + fhem 'attr M5 dev-timing-commDelay 0.2'; + fhem 'attr M5 verbose 4'; + fhem 'set M5 o1 on'; +} + + +sub testStep11 { + findTimesInLog(); + my ($commDelay, $sendDelay) = calcDelays(); + + is(FhemTestUtils_gotLog('commDelay not over.*sleep'), 1, "sleep message in log"); + ok($commDelay >0.2, 'forced comm delay big enough'); + ok($commDelay < 0.22, 'forced comm delay not too big'); + FhemTestUtils_resetLogs(); + testStepLast(); +} + +sub testStepLast { + done_testing; + exit(0); +}; + + + +1; diff --git a/fhem/t/FHEM/98_Modbus/12_Delays.t b/fhem/t/FHEM/98_Modbus/12_Delays.t new file mode 100644 index 000000000..137f7cb5a --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/12_Delays.t @@ -0,0 +1,203 @@ +############################################## +# test request parsing +############################################## +package main; +use strict; +use warnings; +use Test::More; +use Time::HiRes qw( gettimeofday tv_interval); # return time as float, not just full seconds +use FHEM::HTTPMOD::Utils qw(:all); +use FHEM::Modbus::TestUtils qw(:all); + +my %rData = ( +'010403d1000221b6' => '01040400000000fb84', +'010404c100022107' => '010404000041b4cba3', +'0104060100022083' => '0104045262419dbb1b', +'01040691000220ae' => '010404000042524ad9', +'01040a4100022207' => '010404533c458c19', +'01040a61000223cd' => '01040400000000fb84', +'01040a810002223b' => '0104049dff454cd77d', +'01040aa1000223f1' => '01040400000000fb84', +'01040ac1000223ef' => '0104044d6345282e78', +'01040ae100022225' => '010404b6f644980f54', +'010411d1000224ce' => '01040400000000fb84', + +'050303020001240a' => '0503020122c80d', +'05030309000155c8' => '05030200004984', +'0503010600016473' => '0503020106c816', +'05030100000585b1' => '05030a0137110001381100010dac7b', +'0503010000018472' => '050302013709c2', + +'0506030900005808' => '0506030900005808', # set hyst mode +'0506030201182850' => '0506030201182850' # set temp soll 28 +); + +fhem 'attr global mseclog 1'; + +SetTestOptions( + { IODevice => 'MS', # for loginform + RespondTo => 'MS: Simulate sending to none: (.*)', # auto reponder / go to next step at reception + ReadFn => \&Modbus::ReadFn, # call for reception + ReplyHash => \%rData, # to find the right response + + Time1Name => 'Sending', + Time1Regex => qr{MS:\sSimulate\ssending}, + Time2Name => 'Reception', + Time2Regex => qr{ParseFrameStart\s\(RTU\)\sextracted\sid}, + } +); + + +fhem 'get M1 SolarTemp'; + +sub testStep1 { + findTimesInLog(); + FhemTestUtils_resetLogs(); + + is(FhemTestUtils_gotEvent('M1:SolarTemp'), 1, "Event SolarTemp ..."); + + fhem 'get M1 HeatOff'; + # read simulation is triggered when sending is seen in the log. + # next step is called when read simulation is done. + return; +} + + +sub testStep2 { + findTimesInLog(); + FhemTestUtils_resetLogs(); + my ($commDelay, $sendDelay) = calcDelays(); + + # check no delay between read (get SolarTemp) after Step 0 and send (get HeatOff) in step 1 + ok($commDelay < 0.1, 'normal delay from read solar temp to send get HeatOff smaller than 0.1'); + + fhem 'attr M1 dev-timing-sendDelay 0.2'; # send in step2 should be 0.2 after send in step1 + fhem 'get M1 HeatOff'; + return; +} + + +sub testStep3 { + findTimesInLog(); + FhemTestUtils_resetLogs(); + my ($commDelay, $sendDelay) = calcDelays(); + + # check send delay between read (get HeatOff) after Step 1 and send (get HeatOff) in step 2 + ok($sendDelay >= 0.2, 'defined send delay from read HeatOff to next send get HeatOff big enough'); + ok($sendDelay < 0.22, 'defined send delay from read HeatOff to next send get HeatOff not too big'); + + fhem 'get M5 TempWasserEin'; + return; +} + + +sub testStep4 { + findTimesInLog(); + FhemTestUtils_resetLogs(); + my ($commDelay, $sendDelay) = calcDelays(); + + # check no send delay between read (get HeatOff) after Step 2 and send (get TempWasserEin to id 5) in step 3 + ok($sendDelay < 0.1, 'defined send delay on id 1 from read HeatOff to send get TempWasserEin not used for id 5'); + + fhem 'attr MS busDelay 0.2'; + fhem 'get M5 TempWasserAus'; + return; +} + + +sub testStep5 { + findTimesInLog(); + FhemTestUtils_resetLogs(); + my ($commDelay, $sendDelay) = calcDelays(); + + # check bus delay between read (get TempWasserEin) after Step 3 and send (get TempWasserAus) in step 4 + ok($commDelay >= 0.2, 'defined bus delay big enough'); + ok($commDelay < 0.3, 'defined bus delay not too big'); + + fhem 'attr MS busDelay 0'; + fhem 'attr M1 dev-timing-sendDelay 0'; + fhem 'attr MS clientSwitchDelay 0'; + fhem 'get M1 SolarTemp'; + return; +} + + +sub testStep6 { + findTimesInLog(); + FhemTestUtils_resetLogs(); + my ($commDelay, $sendDelay) = calcDelays(); + + ok($sendDelay < 0.2, 'no delay'); + fhem 'attr MS clientSwitchDelay 0.2'; + fhem 'get M5 TempWasserEin'; + return; +} + + +sub testStep7 { + findTimesInLog(); + FhemTestUtils_resetLogs(); + my ($commDelay, $sendDelay) = calcDelays(); + + ok($commDelay >= 0.2, 'defined clsw delay big enough'); + ok($commDelay < 0.3, 'defined clsw delay not too big'); + + fhem 'get M5 TempWasserAus'; + return; +} + + +sub testStep8 { + findTimesInLog(); + FhemTestUtils_resetLogs(); + my ($commDelay, $sendDelay) = calcDelays(); + + ok($sendDelay < 0.1, 'no delay for same id'); + + fhem 'attr M5 dev-timing-commDelay 0.2'; + fhem 'get M5 TempWasserEin'; + return; +} + + +sub testStep9 { + findTimesInLog(); + FhemTestUtils_resetLogs(); + my ($commDelay, $sendDelay) = calcDelays(); + + ok($commDelay >= 0.2, 'defined comm delay big enough'); + ok($commDelay < 0.22, 'defined comm delay not too big'); + + fhem 'attr M5 dev-timing-commDelay 0'; + fhem 'get M5 TempWasserEin'; + return; +} + + +sub testStep10 { + findTimesInLog(); + FhemTestUtils_resetLogs(); + my ($commDelay, $sendDelay) = calcDelays(); + + ok($commDelay < 0.07, 'zero comm delay'); + + fhem 'attr M5 dev-timing-commDelay 0.2'; + fhem 'attr M5 verbose 4'; + fhem 'set M5 o1 on'; + return; +} + + +sub testStep11 { + findTimesInLog(); + my ($commDelay, $sendDelay) = calcDelays(); + + is(FhemTestUtils_gotLog('commDelay not over.*sleep'), 1, "sleep message in log"); + ok($commDelay >0.2, 'forced comm delay big enough'); + ok($commDelay < 0.22, 'forced comm delay not too big'); + FhemTestUtils_resetLogs(); + return; +} + + +1; diff --git a/fhem/t/FHEM/98_Modbus/13_OpenDelays.cfg b/fhem/t/FHEM/98_Modbus/13_OpenDelays.cfg new file mode 100644 index 000000000..14330da85 --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/13_OpenDelays.cfg @@ -0,0 +1,123 @@ +attr global mseclog 1 + +define D1 dummy + +define Slave ModbusAttr 5 slave global:5501 +attr Slave obj-h256-reading TempWasserEin +attr Slave obj-h258-reading D1:TempWasserAus + +attr Slave obj-h100-reading Test1 +attr Slave obj-h100-setexpr $val * 4 + +attr Slave obj-h101-reading Test2 +attr Slave obj-h101-unpack f> +attr Slave obj-h101-len 2 + +attr Slave obj-h103-reading Test3 +attr Slave obj-h103-unpack a8 +attr Slave obj-h103-len 4 + +attr Slave obj-h120-reading Test4 +attr Slave obj-h120-unpack f> +attr Slave obj-h120-len 2 + +attr Slave obj-h130-reading Test5 +attr Slave obj-h130-unpack a* +attr Slave obj-h130-len 2 + +attr Slave obj-c400-reading c0 +attr Slave obj-c401-reading c1 +attr Slave obj-c402-reading c2 +attr Slave obj-c403-reading c3 +attr Slave obj-c404-reading c4 +attr Slave obj-c405-reading c5 +attr Slave obj-c406-reading c6 +attr Slave obj-c407-reading c7 +attr Slave obj-c408-reading c8 +attr Slave obj-c409-reading c9 +attr Slave obj-c410-reading c10 +attr Slave obj-c411-reading c11 +attr Slave obj-c412-reading c12 +attr Slave obj-c413-reading c13 +attr Slave obj-c414-reading c14 +attr Slave obj-c415-reading c15 +attr Slave obj-c416-reading c16 +attr Slave obj-c417-reading c17 +attr Slave obj-c418-reading c18 + +define Master ModbusAttr 5 0 localhost:5501 +attr Master disable 1 +attr Master verbose 3 +attr Master nonPrioritizedGet 1 +attr Master nonPrioritizedSet 1 + +attr Master dev-timing-sendDelay 0 +attr Master dev-timing-commDelay 0 + +attr Master obj-h256-reading TempWasserEin +attr Master obj-h258-reading TempWasserAus + +attr Master obj-h100-reading Test1 +attr Master obj-h100-expr $val + 2 +attr Master obj-h100-poll 1 + +attr Master obj-h101-reading Test2 +attr Master obj-h101-unpack f> +attr Master obj-h101-len 2 +attr Master obj-h101-format %.2f +attr Master obj-h101-poll 1 + +attr Master obj-h103-reading Test3 +attr Master obj-h103-unpack a8 +attr Master obj-h103-len 4 +attr Master obj-h103-poll 1 + +attr Master obj-h120-reading Test4 +attr Master obj-h120-unpack f> +attr Master obj-h120-len 2 +attr Master obj-h120-format %.2f +attr Master obj-h120-poll 1 +attr Master obj-h120-ignoreExpr $val > 10 + +attr Master obj-h130-reading Test5 +attr Master obj-h130-unpack a* +attr Master obj-h130-len 2 +attr Master obj-h130-encode utf8 + +attr Master obj-h10-reading o1 +attr Master obj-h10-map 0:off, 1:on + +attr Master obj-h11-reading o2 +attr Master obj-h11-min 1 +attr Master obj-h11-max 3 +attr Master dev-h-defSet 1 +attr Master dev-c-defSet 1 +attr Master dev-h-defShowGet 1 + +attr Master obj-c400-reading c0 +attr Master obj-c401-reading c1 +attr Master obj-c402-reading c2 +attr Master obj-c403-reading c3 +attr Master obj-c404-reading c4 +attr Master obj-c405-reading c5 +attr Master obj-c406-reading c6 +attr Master obj-c407-reading c7 +attr Master obj-c408-reading c8 +attr Master obj-c409-reading c9 +attr Master obj-c410-reading c10 +attr Master obj-c411-reading c11 +attr Master obj-c412-reading c12 +attr Master obj-c413-reading c13 +attr Master obj-c414-reading c14 +attr Master obj-c415-reading c15 +attr Master obj-c416-reading c16 +attr Master obj-c417-reading c17 +attr Master obj-c418-reading c18 + +attr Master obj-c400-poll 1 +attr Master obj-c405-poll 1 +attr Master obj-c406-poll 1 +attr Master obj-c417-poll 1 + +attr Master dev-h-combine 19 +attr Master dev-c-combine 32 \ No newline at end of file diff --git a/fhem/t/FHEM/98_Modbus/13_OpenDelays.t b/fhem/t/FHEM/98_Modbus/13_OpenDelays.t new file mode 100644 index 000000000..f7ca62bc1 --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/13_OpenDelays.t @@ -0,0 +1,219 @@ +############################################## +# test master slave end to end +# attr disable +# and set inactive / set active +############################################## +package main; +use strict; +use warnings; +use Test::More; +use Time::HiRes qw( gettimeofday tv_interval); # return time as float, not just full seconds +use FHEM::HTTPMOD::Utils qw(:all); +use Data::Dumper; + +my $closeTime; +my $openTime; +my $startTime; + +InternalTimer(gettimeofday() + 0.1, "testStep1", 0); + + +sub getLogTime { + my $regex = shift; + my $times = shift // 1; + is(FhemTestUtils_gotLog($regex), $times, "search $regex in log"); + my $time = FhemTestUtils_getLogTime($regex, 'last'); + Log3 undef, 1, "Test: found $regex in log at $startTime " . FmtTimeMs($time) if $time; + return $time; +} + + +sub testStep1 { # preparation of slave content, enable devices + Log3 undef, 1, "----------------"; + #is(FhemTestUtils_gotLog('attribute'), 0, "no unknown attributes"); # logs during init are not collected. + Log3 undef, 1, "TestStep1: enable Master and set value at Slave"; + fhem ('attr Master disable 0'); + fhem ('setreading Slave TempWasserEin 12'); + fhem ('setreading Slave Test1 1'); + fhem ('setreading Slave Test2 2.123'); + fhem ('setreading Slave Test3 abcdefg'); + fhem ('setreading Slave Test4 40'); + + InternalTimer(gettimeofday() + 0.1, "testStep2", 0); +} + +sub testStep2 { # get holding registers + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep2: get TempWasserEin"; + fhem ('attr Master verbose 5'); + fhem ('attr Slave verbose 3'); + fhem ('get Master TempWasserEin'); + InternalTimer(gettimeofday() + 0.1, "testStep3", 0); +} + +sub testStep3 { # check first result, disable and request again + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep3: check result, disable master and request again"; + is(FhemTestUtils_gotEvent(qr/Master:TempWasserEin:\s12/xms), 1, "Retrieve integer value from local slave"); + FhemTestUtils_resetEvents(); + FhemTestUtils_resetLogs(); + fhem ('attr Master disable 1'); + fhem ('get Master TempWasserEin'); + + InternalTimer(gettimeofday(), "testStep4", 0); +} + +sub testStep4 { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep4: check that master disable worked"; + is(FhemTestUtils_gotEvent(qr/Master:TempWasserEin:\s12/xms), 0, "no Retrieve for disabled"); + is(FhemTestUtils_gotEvent(qr/Master:disabled/xms), 1, "state disabled"); + fhem ('attr Master disable 0'); + FhemTestUtils_resetEvents(); + FhemTestUtils_resetLogs(); + InternalTimer(gettimeofday() + 0.1, "testStep5", 0); +} + +sub testStep5 { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep5: now set master inactive"; + $startTime = getLogTime ('Master device opened'); + fhem ('attr Master enableSetInactive 1'); + fhem ('set Master inactive'); + InternalTimer(gettimeofday(), "testStep6", 0); +} + +sub testStep6 { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep6: now try to get reading again"; + fhem ('get Master TempWasserEin'); + InternalTimer(gettimeofday(), "testStep7a", 0); +} + +sub testStep7a { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep7a: check if reading was not requested and then set master to active again"; + is(FhemTestUtils_gotEvent(qr/Master:TempWasserEin:\s12/xms), 0, "no Retrieve for inactive"); + FhemTestUtils_resetEvents(); + FhemTestUtils_resetLogs(); + fhem ('attr Master nextOpenDelay2 0'); # don't wait with open + fhem ('set Master active'); + InternalTimer(gettimeofday(), "testStep7b", 0); +} + +sub testStep7b { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep7b: try retrieve again"; + $openTime = getLogTime ('Master device opened'); + Log3 undef, 1, "TestStep7b: Time diff is " . sprintf ('%.3f', $openTime - $startTime); + ok($openTime - $startTime < 0.25, 'time between two open calls is smaller than 0.25'); + FhemTestUtils_resetEvents(); + FhemTestUtils_resetLogs(); + fhem ('get Master TempWasserEin'); + InternalTimer(gettimeofday() + 0.1, "testStep8", 0); +} + + +sub testStep8 { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep8: check result and then set slave to inactive and try again"; + is(FhemTestUtils_gotEvent(qr/Master:TempWasserEin:\s12/xms), 1, "Retrieve integer value again from local slave"); + fhem ('set Slave inactive'); + FhemTestUtils_resetEvents(); + FhemTestUtils_resetLogs(); + fhem ('attr Master dev-timing-timeout 0.2'); + fhem ('attr Master openTimeout 0.5'); # + fhem ('attr Master nextOpenDelay2 0.1'); # + fhem ('attr Master nextOpenDelay 1'); # can not be smaller than 1 + fhem ('get Master TempWasserEin'); # should run into timeout + InternalTimer(gettimeofday()+0.5, "testStep8b", 0); +} + +sub testStep8b { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep8b: check that request was not answered and get last open time"; + is(FhemTestUtils_gotEvent(qr/Master:TempWasserEin:\s12/xms), 0, "no Retrieve for inactive Slave"); + #is(FhemTestUtils_gotLog('Master: Timeout waiting for a modbus response'), 1, "saw timeout"); + $startTime = getLogTime ('HttpUtils url=http://localhost:5501'); # time of first try + fhem ('set Slave active'); + InternalTimer(gettimeofday()+1, "testStep8c", 0); +} + + +sub testStep8c { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep8c: "; + InternalTimer(gettimeofday()+1, "testStep9a", 0); +} + + +sub testStep9a { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep9a: check nextOpenDelay"; + $openTime = getLogTime ('5501 reappeared'); + Log3 undef, 1, "TestStep7b: Time diff is " . sprintf ('%.3f', $openTime - $startTime); + ok($openTime - $startTime >= 1, 'time between two open calls is bigger than 1'); + ok($openTime - $startTime < 2, 'time between two open calls is smaller than 2'); + InternalTimer(gettimeofday() + 0.1, "testStep9b", 0); +} + +sub testStep9b { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep9b: "; + InternalTimer(gettimeofday(), "testStep9c", 0); +} + +sub testStep9c { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep9b: retrieve value and then let Slave close after inactivity timeout"; + # now open should happen and event should come + fhem('attr Slave dev-timing-serverTimeout 1'); + fhem('attr Slave dev-timing-serverTimeout 1'); + FhemTestUtils_resetEvents(); + FhemTestUtils_resetLogs(); + fhem ('get Master TempWasserEin'); + InternalTimer(gettimeofday() + 1.1, "testStep10", 0); +} + +sub testStep10 { + # check that we now got the value + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep10: check successful retrieve after slave is active again and master did open connection"; + is(FhemTestUtils_gotEvent(qr/Master:TempWasserEin:\s12/xms), 1, "Retrieve integer value again from local slave"); + InternalTimer(gettimeofday() + 0.5, "testStep11", 0); +} + +sub testStep11 { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep11:"; + InternalTimer(gettimeofday() + 1, "testStep12", 0); +} + +sub testStep12 { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep12:"; + InternalTimer(gettimeofday() + 1, "testStep13", 0); +} + +sub testStep13 { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep13:"; + InternalTimer(gettimeofday(), "testStepEnd", 0); +} + + +sub testStepX { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStepX: "; + #fhem ('get '); + InternalTimer(gettimeofday() + 0.1, "testStepEnd", 0); +} + + +sub testStepEnd { + done_testing; + exit(0); +} + + +1; diff --git a/fhem/t/FHEM/98_Modbus/14_QDelay.cfg b/fhem/t/FHEM/98_Modbus/14_QDelay.cfg new file mode 100644 index 000000000..c8d2ead79 --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/14_QDelay.cfg @@ -0,0 +1,235 @@ +define MS Modbus none +attr MS verbose 5 +attr MS clientSwitchDelay 0 +attr MS busDelay 0 +attr MS queueDelay 0.4 + + +define M5 ModbusAttr 5 0 +attr M5 verbose 5 +attr M5 dev-timing-sendDelay 0 +attr M5 dev-timing-commDelay 0 +attr M5 nonPrioritizedGet 1 + +attr M5 obj-h256-reading TempWasserEin +attr M5 obj-h258-reading TempWasserAus + +attr M5 obj-h10-reading o1 +attr M5 obj-h10-map 0:off, 1:on +attr M5 obj-h10-set 1 +attr M5 obj-h11-reading o2 +attr M5 obj-h11-hint 1,2,3 + +attr M5 dev-h-defSet 1 +attr M5 dev-h-defShowGet 1 + + +define M1 ModbusAttr 1 0 +attr M1 verbose 5 +attr M1 dev-timing-sendDelay 0 +attr M1 dev-timing-commDelay 0 +attr M1 nonPrioritizedGet 1 + +attr M1 dev-c-defPoll 1 +attr M1 dev-h-combine 5 +attr M1 dev-h-defLen 2 +attr M1 dev-h-defPoll 1 +attr M1 dev-h-defRevRegs 1 +attr M1 dev-h-write 16 +attr M1 dev-i-defFormat %.1f +attr M1 dev-i-defLen 2 +attr M1 dev-i-defPoll 1 +attr M1 dev-i-defRevRegs 1 +attr M1 dev-i-defUnpack f> +attr M1 dev-type-VT_Date-expr sprintf("%02d.%02d",($val >> 8),($val & 0xff)) +attr M1 dev-type-VT_Date-len 1 +attr M1 dev-type-VT_Date-unpack n +attr M1 dev-type-VT_R4-format %.1f +attr M1 dev-type-VT_R4-len 2 +attr M1 dev-type-VT_R4-revRegs 1 +attr M1 dev-type-VT_R4-unpack f> +attr M1 dev-type-VT_String-bswapRegs 1 +attr M1 dev-type-VT_String-decode cp850 +attr M1 dev-type-VT_String-encode utf8 +attr M1 dev-type-VT_String-expr $val =~ s/[\00]+//gr +attr M1 dev-type-VT_String-len 8 +attr M1 dev-type-VT_String-revRegs 0 +attr M1 dev-type-VT_String-unpack a* +attr M1 dev-type-VT_Time-expr sprintf("%02d:%02d",($val >> 8),($val & 0xff)) +attr M1 dev-type-VT_Time-len 1 +attr M1 dev-type-VT_Time-unpack n + +attr M1 obj-c1009-map 0:false, 1:true +attr M1 obj-c1009-polldelay 3600 +attr M1 obj-c1009-reading HeatOff +attr M1 obj-c1329-map 0:false, 1:true +attr M1 obj-c1329-polldelay 300 +attr M1 obj-c1329-reading HeatIncreaseOff +attr M1 obj-c1409-map 0:0, 1:1=2, 2:1->2, 3:1->2->3 +attr M1 obj-c1409-polldelay 60 +attr M1 obj-c1409-reading AuxilaryModeHeating +attr M1 obj-c1457-map 0:false, 1:true +attr M1 obj-c1457-reading CoolOff +attr M1 obj-c1633-map 0:false, 1:true +attr M1 obj-c1633-reading DomesticWaterOff +attr M1 obj-h1-len 13 +attr M1 obj-h1-poll 0 +attr M1 obj-h1-reading RTCTime +attr M1 obj-h1-unpack H* +attr M1 obj-h1025-polldelay 86400 +attr M1 obj-h1025-reading HeatTimeOn +attr M1 obj-h1025-type VT_Time +attr M1 obj-h1041-polldelay 86400 +attr M1 obj-h1041-reading HeatTimeOff +attr M1 obj-h1041-type VT_Time +attr M1 obj-h1057-max 30 +attr M1 obj-h1057-min 10 +attr M1 obj-h1057-reading HeatCharacteristicSetPoint +attr M1 obj-h1057-type VT_R4 +attr M1 obj-h1089-max 65 +attr M1 obj-h1089-min 15 +attr M1 obj-h1089-reading HeatCharacteristicSetPointBaseTemp +attr M1 obj-h1089-set 1 +attr M1 obj-h1089-type VT_R4 +attr M1 obj-h1121-max 100 +attr M1 obj-h1121-min 0 +attr M1 obj-h1121-reading HeatCharacteristicGradient +attr M1 obj-h1121-type VT_R4 +attr M1 obj-h1153-max 72 +attr M1 obj-h1153-min 10 +attr M1 obj-h1153-reading HeatCharacteristicLimit +attr M1 obj-h1153-type VT_R4 +attr M1 obj-h1185-reading HeatReturnTemp +attr M1 obj-h1185-type VT_R4 +attr M1 obj-h1249-max 3 +attr M1 obj-h1249-min 1 +attr M1 obj-h1249-reading HeatTempHyst +attr M1 obj-h1249-type VT_R4 +attr M1 obj-h1281-reading RoomTempNominal +attr M1 obj-h1281-type VT_R4 +attr M1 obj-h1313-len 1 +attr M1 obj-h1313-max 200 +attr M1 obj-h1313-min 0 +attr M1 obj-h1313-reading RoomTempFactor +attr M1 obj-h1313-unpack S> +attr M1 obj-h1345-polldelay 86400 +attr M1 obj-h1345-reading HeatIncreaseTimeOn +attr M1 obj-h1345-type VT_Time +attr M1 obj-h1361-polldelay 86400 +attr M1 obj-h1361-reading HeatIncreaseTimeOff +attr M1 obj-h1361-type VT_Time +attr M1 obj-h1377-max 5 +attr M1 obj-h1377-min -5 +attr M1 obj-h1377-reading HeatIncreaseSetPtOffset +attr M1 obj-h1377-type VT_R4 +attr M1 obj-h1425-max 5 +attr M1 obj-h1425-min 0 +attr M1 obj-h1425-reading AuxilaryMaxDifference +attr M1 obj-h1425-type VT_R4 +attr M1 obj-h1473-polldelay 86400 +attr M1 obj-h1473-reading CoolTimeOn +attr M1 obj-h1473-type VT_Time +attr M1 obj-h1489-polldelay 86400 +attr M1 obj-h1489-reading CoolTimeOff +attr M1 obj-h1489-type VT_Time +attr M1 obj-h1505-max 30 +attr M1 obj-h1505-min 18 +attr M1 obj-h1505-reading CoolCharacteristicSetPoint +attr M1 obj-h1505-type VT_R4 +attr M1 obj-h1569-reading CoolReturnTempNominal +attr M1 obj-h1569-type VT_R4 +attr M1 obj-h1601-max 3 +attr M1 obj-h1601-min 1 +attr M1 obj-h1601-reading CoolReturnTempHyst +attr M1 obj-h1601-type VT_R4 +attr M1 obj-h1649-polldelay 86400 +attr M1 obj-h1649-reading DomesticWaterTimeOn +attr M1 obj-h1649-type VT_Time +attr M1 obj-h1665-polldelay 86400 +attr M1 obj-h1665-reading DomesticWaterTimeOff +attr M1 obj-h1665-type VT_Time +attr M1 obj-h1713-reading DomesticWaterTempNominal +attr M1 obj-h1713-set 1 +attr M1 obj-h1713-type VT_R4 +attr M1 obj-h1745-max 10 +attr M1 obj-h1745-min 5 +attr M1 obj-h1745-reading DomesticWaterTempHyst +attr M1 obj-h1745-type VT_R4 +attr M1 obj-h1777-len 16 +attr M1 obj-h1777-polldelay 86400 +attr M1 obj-h1777-reading LegionellaSchedule +attr M1 obj-h1777-unpack H* +attr M1 obj-h1793-polldelay 86400 +attr M1 obj-h1793-type VT_Time +attr M1 obj-h1809-polldelay 86400 +attr M1 obj-h1809-reading LegionellaTimeOff +attr M1 obj-h1809-type VT_Time +attr M1 obj-h209-len 13 +attr M1 obj-h209-poll 0 +attr M1 obj-h209-reading RTCDate +attr M1 obj-h209-unpack H* +attr M1 obj-h417-len 1 +attr M1 obj-h417-polldelay 86400 +attr M1 obj-h417-reading LngSelect +attr M1 obj-h4497-reading PElectric +attr M1 obj-h4497-type VT_R4 +attr M1 obj-h4529-reading PThermal +attr M1 obj-h4529-type VT_R4 +attr M1 obj-h4689-polldelay 86400 +attr M1 obj-h4689-reading FirmwareVersion +attr M1 obj-h4689-showGet 1 +attr M1 obj-h4689-type VT_String +attr M1 obj-h4689-unpack (a*) +attr M1 obj-h4817-polldelay 86400 +attr M1 obj-h4817-reading FirmwareDate +attr M1 obj-h4817-type VT_String +attr M1 obj-h4945-polldelay 86400 +attr M1 obj-h4945-reading ManufType +attr M1 obj-h4945-type VT_String +attr M1 obj-h5073-polldelay 86400 +attr M1 obj-h5073-reading ManufSerialNum +attr M1 obj-h5073-type VT_String +attr M1 obj-h5457-len 1 +attr M1 obj-h5457-map 0048:Kühlung, 0040:Idle, 0051:Warmwasser, 0052:Heizung +attr M1 obj-h5457-reading OperatingState +attr M1 obj-h5457-unpack H* +attr M1 obj-h5505-len 16 +attr M1 obj-h5505-reading ADC_Error +attr M1 obj-h5505-unpack H* +attr M1 obj-h5521-reading LCD_Display_Line_1 +attr M1 obj-h5521-type VT_String +attr M1 obj-h5649-reading LCD_Display_Line_2 +attr M1 obj-h5649-type VT_String +attr M1 obj-i1217-reading HeatReturnTempNominal +attr M1 obj-i1537-reading CoolReturnTemp +attr M1 obj-i1681-reading DomesticWaterTempActual +attr M1 obj-i2625-reading OHCompressor1 +attr M1 obj-i2657-reading OHCompressor2 +attr M1 obj-i2689-reading OHHeatingCompressor +attr M1 obj-i2721-reading OHHeatingAuxilary +attr M1 obj-i2753-reading OHCooling +attr M1 obj-i2785-reading OHDomesticWaterCompressor +attr M1 obj-i433-reading OutdoorTemp +attr M1 obj-i433-showGet 1 +attr M1 obj-i4561-reading COP +attr M1 obj-i4561-showGet 1 +attr M1 obj-i465-reading OutdoorTemp1h +attr M1 obj-i497-reading OutdoorTemp24h +attr M1 obj-i529-reading HeatSourceIn +attr M1 obj-i561-reading HeatSourceOut +attr M1 obj-i593-reading EvaporationTemp +attr M1 obj-i625-reading SuctionGasTemp +attr M1 obj-i657-reading EvaporationPress +attr M1 obj-i689-reading ReturnTempNominal +attr M1 obj-i721-reading ReturnTemp +attr M1 obj-i753-reading FlowTemp +attr M1 obj-i785-reading CondensationTemp +attr M1 obj-i817-reading CondensationPress +attr M1 obj-i849-reading RoomTemp +attr M1 obj-i881-reading RoomTemp1h +attr M1 obj-i913-reading DomesticWaterTemp +attr M1 obj-i945-reading PoolTemp +attr M1 obj-i977-reading SolarTemp + +attr M1 sortUpdate 1 + diff --git a/fhem/t/FHEM/98_Modbus/14_QDelay.t b/fhem/t/FHEM/98_Modbus/14_QDelay.t new file mode 100644 index 000000000..be2e428e7 --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/14_QDelay.t @@ -0,0 +1,52 @@ +############################################## +# test request parsing +############################################## +use strict; +use warnings; +use Test::More; +use Time::HiRes qw( gettimeofday tv_interval); # return time as float, not just full seconds +use FHEM::HTTPMOD::Utils qw(:all); +use FHEM::Modbus::TestUtils qw(:all); + + +fhem 'attr global mseclog 1'; + +SetTestOptions( + { IODevice => 'MS', # for loginform + #RespondTo => 'MS: Simulate sending to none: (.*)', # auto reponder / go to next step at reception + Time1Name => 'busy', + Time1Regex => qr{Fhem is still waiting}, + Time2Name => 'queue run', + Time2Regex => qr{ProcessRequestQueue called from Fhem internal timer as queue:MS}, + } +); + +NextStep(); + +sub testStep1 { + findTimesInLog(); + fhem 'get M1 SolarTemp'; + fhem 'get M5 TempWasserEin'; + return 0.5; +} + + +sub testStep2 { + findTimesInLog(); + FhemTestUtils_resetLogs(); + return 0.5; +} + + +sub testStep3 { + findTimesInLog(); + FhemTestUtils_resetLogs(); + my ($commDelay, $sendDelay, $lastDelay) = calcDelays(); + + # check no delay between read (get SolarTemp) after Step 0 and send (get HeatOff) in step 1 + ok($sendDelay < 0.5, 'queue delay not too big'); + ok($sendDelay > 0.3, 'queue delay not too small'); + +} + +1; diff --git a/fhem/t/FHEM/98_Modbus/20_Hints.cfg b/fhem/t/FHEM/98_Modbus/20_Hints.cfg new file mode 100644 index 000000000..f8c353261 --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/20_Hints.cfg @@ -0,0 +1,9 @@ +define M1 ModbusAttr 1 1 +attr M1 verbose 5 + +attr M1 obj-h10-reading o1 +attr M1 obj-h10-map 0:off, 1:on + +attr M1 obj-h11-reading o2 +attr M1 obj-h11-hint 1,2,3 +attr M1 dev-h-defSet 1 diff --git a/fhem/t/FHEM/98_Modbus/20_Hints.t b/fhem/t/FHEM/98_Modbus/20_Hints.t new file mode 100644 index 000000000..cc9530fad --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/20_Hints.t @@ -0,0 +1,18 @@ +############################################## +# test hints for set +############################################## +use strict; +use warnings; +use Test::More; + +fhem 'set M1 ?'; +is(FhemTestUtils_gotLog('choose one of .* o1:off,on o2:1,2,3'), 1, "hints in log"); + +InternalTimer(gettimeofday() + 0.2, "testStepEnd", 0); + +sub testStepEnd { + done_testing; + exit(0); +} + +1; diff --git a/fhem/t/FHEM/98_Modbus/31_Register.cfg b/fhem/t/FHEM/98_Modbus/31_Register.cfg new file mode 100644 index 000000000..9bf491342 --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/31_Register.cfg @@ -0,0 +1,7 @@ +define MS Modbus none +attr Master verbose 5 +attr MS clientSwitchDelay 0 +attr MS busDelay 0 +attr MS verbose 5 +attr global mseclog 1 + diff --git a/fhem/t/FHEM/98_Modbus/31_Register.t b/fhem/t/FHEM/98_Modbus/31_Register.t new file mode 100644 index 000000000..f679602f6 --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/31_Register.t @@ -0,0 +1,109 @@ +############################################## +# test master slave end to end +# attr disable +# and set inactive / set active +############################################## +use strict; +use warnings; +use Test::More; +use Time::HiRes qw(gettimeofday tv_interval); # return time as float, not just full seconds +use FHEM::HTTPMOD::Utils qw(:all); +use Data::Dumper; + +InternalTimer(gettimeofday() + 0.1, "testStep1", 0); + +sub testStep1 { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep1: define Master over MS"; + + fhem ('define Master ModbusAttr 5 0'); + InternalTimer(gettimeofday() + 0.1, "testStep2", 0); +} + +sub testStep2 { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep2: "; + is(FhemTestUtils_gotLog('registers Master at MS with id 5, MODE master, PROTOCOL RTU'), 1, "Master registered"); + fhem ('define Master2 ModbusAttr 4 0'); + InternalTimer(gettimeofday() + 0.1, "testStep3", 0); +} + +sub testStep3 { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep3: "; + is(FhemTestUtils_gotLog('registers Master2 at MS with id 4, MODE master, PROTOCOL RTU'), 1, "Master2 registered"); + fhem ('define Master3 ModbusAttr 4 0 ASCII'); + fhem ('attr Master3 enableSetInactive 1'); + fhem ('attr Master3 verbose 5'); + fhem ('attr Master3 obj-h100-reading test'); + fhem ('attr Master3 obj-h100-showGet 1'); + InternalTimer(gettimeofday() + 0.1, "testStep4", 0); +} + +sub testStep4 { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep4: "; + is(FhemTestUtils_gotLog('Master3: SetIODev found no usable physical modbus device'), 1, "No IODev for Master3 (MS already locked as RTU)"); + FhemTestUtils_resetLogs(); + fhem ('attr Master disable 1'); + fhem ('get Master3 test'); + InternalTimer(gettimeofday() + 0.1, "testStep5", 0); +} +sub testStep5 { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep5: "; + is(FhemTestUtils_gotLog('Master3: SetIODev found no usable physical modbus device'), 1, "No IODev for Master3 (MS still locked as RTU)"); + FhemTestUtils_resetLogs(); + fhem ('attr Master2 disable 1'); + fhem ('get Master3 test'); + InternalTimer(gettimeofday() + 0.1, "testStep6", 0); +} +sub testStep6 { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep6: "; + is(FhemTestUtils_gotLog('registers Master3 at MS with id 4, MODE master, PROTOCOL ASCII'), 1, "Now MS is locked as ASCII"); + FhemTestUtils_resetLogs(); + fhem ('define Slave1 ModbusAttr 10 slave ASCII'); + InternalTimer(gettimeofday() + 0.1, "testStep7", 0); +} +sub testStep7 { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep7: "; + is(FhemTestUtils_gotLog('Slave1: SetIODev found no usable physical modbus device'), 1, "no io device for slave"); + fhem ('delete Slave1'); + fhem ('attr Master3 disable 1'); + fhem ('define Slave1 ModbusAttr 10 slave ASCII'); + InternalTimer(gettimeofday() + 0.1, "testStep8", 0); +} +sub testStep8 { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep8: "; + is(FhemTestUtils_gotLog('registers Slave1 at MS with id 10, MODE slave, PROTOCOL ASCII'), 1, "now slave can use MS as IO Device"); + InternalTimer(gettimeofday() + 0.1, "testStep9", 0); +} +sub testStep9 { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep9: "; + InternalTimer(gettimeofday() + 0.1, "testStep10", 0); +} +sub testStep10 { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStep10: "; + InternalTimer(gettimeofday() + 0.1, "testStepEnd", 0); +} + +sub testStepX { + Log3 undef, 1, "----------------"; + Log3 undef, 1, "TestStepX: "; + #fhem ('get '); + InternalTimer(gettimeofday() + 0.1, "testStepEnd", 0); +} + + +sub testStepEnd { + done_testing; + exit(0); +} + + +1; diff --git a/fhem/t/FHEM/98_Modbus/41_Passive.cfg b/fhem/t/FHEM/98_Modbus/41_Passive.cfg new file mode 100644 index 000000000..4356d8eaa --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/41_Passive.cfg @@ -0,0 +1,250 @@ +define MS Modbus none +attr MS verbose 5 +attr MS clientSwitchDelay 0 +attr MS busDelay 0 +attr MS skipGarbage 1 + + +define M5 ModbusAttr 5 passive +attr M5 verbose 5 +attr M5 dev-timing-sendDelay 0 +attr M5 dev-timing-commDelay 0 +attr M5 nonPrioritizedGet 1 +attr M5 obj-h256-reading TempWasserEin +attr M5 obj-h258-reading TempWasserAus +attr M5 obj-h10-reading o1 +attr M5 obj-h10-map 0:off, 1:on +attr M5 obj-h10-set 1 +attr M5 obj-h11-reading o2 +attr M5 obj-h11-hint 1,2,3 + +attr M5 dev-h-defSet 1 +attr M5 dev-h-defShowGet 1 + + +define M254 ModbusAttr 254 passive +attr M254 verbose 5 +attr M254 dev-timing-sendDelay 0 +attr M254 dev-timing-commDelay 0 +attr M254 nonPrioritizedGet 1 +attr M254 obj-h256-reading TempWasserEin +attr M254 obj-h258-reading TempWasserAus +attr M254 obj-h10-reading o1 +attr M254 obj-h10-map 0:off, 1:on +attr M254 obj-h10-set 1 +attr M254 obj-h11-reading o2 +attr M254 obj-h11-hint 1,2,3 +attr M254 dev-h-defSet 1 +attr M254 dev-h-defShowGet 1 + + + +define M1 ModbusAttr 1 passive +attr M1 verbose 5 +attr M1 dev-timing-sendDelay 0 +attr M1 dev-timing-commDelay 0 +attr M1 nonPrioritizedGet 1 + +attr M1 dev-c-defPoll 1 +attr M1 dev-h-combine 5 +attr M1 dev-h-defLen 2 +attr M1 dev-h-defPoll 1 +attr M1 dev-h-defRevRegs 1 +attr M1 dev-h-write 16 +attr M1 dev-i-defFormat %.1f +attr M1 dev-i-defLen 2 +attr M1 dev-i-defPoll 1 +attr M1 dev-i-defRevRegs 1 +attr M1 dev-i-defUnpack f> +attr M1 dev-type-VT_Date-expr sprintf("%02d.%02d",($val >> 8),($val & 0xff)) +attr M1 dev-type-VT_Date-len 1 +attr M1 dev-type-VT_Date-unpack n +attr M1 dev-type-VT_R4-format %.1f +attr M1 dev-type-VT_R4-len 2 +attr M1 dev-type-VT_R4-revRegs 1 +attr M1 dev-type-VT_R4-unpack f> +attr M1 dev-type-VT_String-bswapRegs 1 +attr M1 dev-type-VT_String-decode cp850 +attr M1 dev-type-VT_String-encode utf8 +attr M1 dev-type-VT_String-expr $val =~ s/[\00]+//gr +attr M1 dev-type-VT_String-len 8 +attr M1 dev-type-VT_String-revRegs 0 +attr M1 dev-type-VT_String-unpack a* +attr M1 dev-type-VT_Time-expr sprintf("%02d:%02d",($val >> 8),($val & 0xff)) +attr M1 dev-type-VT_Time-len 1 +attr M1 dev-type-VT_Time-unpack n + +attr M1 obj-c1009-map 0:false, 1:true +attr M1 obj-c1009-polldelay 3600 +attr M1 obj-c1009-reading HeatOff +attr M1 obj-c1329-map 0:false, 1:true +attr M1 obj-c1329-polldelay 300 +attr M1 obj-c1329-reading HeatIncreaseOff +attr M1 obj-c1409-map 0:0, 1:1=2, 2:1->2, 3:1->2->3 +attr M1 obj-c1409-polldelay 60 +attr M1 obj-c1409-reading AuxilaryModeHeating +attr M1 obj-c1457-map 0:false, 1:true +attr M1 obj-c1457-reading CoolOff +attr M1 obj-c1633-map 0:false, 1:true +attr M1 obj-c1633-reading DomesticWaterOff +attr M1 obj-h1-len 13 +attr M1 obj-h1-poll 0 +attr M1 obj-h1-reading RTCTime +attr M1 obj-h1-unpack H* +attr M1 obj-h1025-polldelay 86400 +attr M1 obj-h1025-reading HeatTimeOn +attr M1 obj-h1025-type VT_Time +attr M1 obj-h1041-polldelay 86400 +attr M1 obj-h1041-reading HeatTimeOff +attr M1 obj-h1041-type VT_Time +attr M1 obj-h1057-max 30 +attr M1 obj-h1057-min 10 +attr M1 obj-h1057-reading HeatCharacteristicSetPoint +attr M1 obj-h1057-type VT_R4 +attr M1 obj-h1089-max 65 +attr M1 obj-h1089-min 15 +attr M1 obj-h1089-reading HeatCharacteristicSetPointBaseTemp +attr M1 obj-h1089-set 1 +attr M1 obj-h1089-type VT_R4 +attr M1 obj-h1121-max 100 +attr M1 obj-h1121-min 0 +attr M1 obj-h1121-reading HeatCharacteristicGradient +attr M1 obj-h1121-type VT_R4 +attr M1 obj-h1153-max 72 +attr M1 obj-h1153-min 10 +attr M1 obj-h1153-reading HeatCharacteristicLimit +attr M1 obj-h1153-type VT_R4 +attr M1 obj-h1185-reading HeatReturnTemp +attr M1 obj-h1185-type VT_R4 +attr M1 obj-h1249-max 3 +attr M1 obj-h1249-min 1 +attr M1 obj-h1249-reading HeatTempHyst +attr M1 obj-h1249-type VT_R4 +attr M1 obj-h1281-reading RoomTempNominal +attr M1 obj-h1281-type VT_R4 +attr M1 obj-h1313-len 1 +attr M1 obj-h1313-max 200 +attr M1 obj-h1313-min 0 +attr M1 obj-h1313-reading RoomTempFactor +attr M1 obj-h1313-unpack S> +attr M1 obj-h1345-polldelay 86400 +attr M1 obj-h1345-reading HeatIncreaseTimeOn +attr M1 obj-h1345-type VT_Time +attr M1 obj-h1361-polldelay 86400 +attr M1 obj-h1361-reading HeatIncreaseTimeOff +attr M1 obj-h1361-type VT_Time +attr M1 obj-h1377-max 5 +attr M1 obj-h1377-min -5 +attr M1 obj-h1377-reading HeatIncreaseSetPtOffset +attr M1 obj-h1377-type VT_R4 +attr M1 obj-h1425-max 5 +attr M1 obj-h1425-min 0 +attr M1 obj-h1425-reading AuxilaryMaxDifference +attr M1 obj-h1425-type VT_R4 +attr M1 obj-h1473-polldelay 86400 +attr M1 obj-h1473-reading CoolTimeOn +attr M1 obj-h1473-type VT_Time +attr M1 obj-h1489-polldelay 86400 +attr M1 obj-h1489-reading CoolTimeOff +attr M1 obj-h1489-type VT_Time +attr M1 obj-h1505-max 30 +attr M1 obj-h1505-min 18 +attr M1 obj-h1505-reading CoolCharacteristicSetPoint +attr M1 obj-h1505-type VT_R4 +attr M1 obj-h1569-reading CoolReturnTempNominal +attr M1 obj-h1569-type VT_R4 +attr M1 obj-h1601-max 3 +attr M1 obj-h1601-min 1 +attr M1 obj-h1601-reading CoolReturnTempHyst +attr M1 obj-h1601-type VT_R4 +attr M1 obj-h1649-polldelay 86400 +attr M1 obj-h1649-reading DomesticWaterTimeOn +attr M1 obj-h1649-type VT_Time +attr M1 obj-h1665-polldelay 86400 +attr M1 obj-h1665-reading DomesticWaterTimeOff +attr M1 obj-h1665-type VT_Time +attr M1 obj-h1713-reading DomesticWaterTempNominal +attr M1 obj-h1713-set 1 +attr M1 obj-h1713-type VT_R4 +attr M1 obj-h1745-max 10 +attr M1 obj-h1745-min 5 +attr M1 obj-h1745-reading DomesticWaterTempHyst +attr M1 obj-h1745-type VT_R4 +attr M1 obj-h1777-len 16 +attr M1 obj-h1777-polldelay 86400 +attr M1 obj-h1777-reading LegionellaSchedule +attr M1 obj-h1777-unpack H* +attr M1 obj-h1793-polldelay 86400 +attr M1 obj-h1793-type VT_Time +attr M1 obj-h1809-polldelay 86400 +attr M1 obj-h1809-reading LegionellaTimeOff +attr M1 obj-h1809-type VT_Time +attr M1 obj-h209-len 13 +attr M1 obj-h209-poll 0 +attr M1 obj-h209-reading RTCDate +attr M1 obj-h209-unpack H* +attr M1 obj-h417-len 1 +attr M1 obj-h417-polldelay 86400 +attr M1 obj-h417-reading LngSelect +attr M1 obj-h4497-reading PElectric +attr M1 obj-h4497-type VT_R4 +attr M1 obj-h4529-reading PThermal +attr M1 obj-h4529-type VT_R4 +attr M1 obj-h4689-polldelay 86400 +attr M1 obj-h4689-reading FirmwareVersion +attr M1 obj-h4689-showGet 1 +attr M1 obj-h4689-type VT_String +attr M1 obj-h4689-unpack (a*) +attr M1 obj-h4817-polldelay 86400 +attr M1 obj-h4817-reading FirmwareDate +attr M1 obj-h4817-type VT_String +attr M1 obj-h4945-polldelay 86400 +attr M1 obj-h4945-reading ManufType +attr M1 obj-h4945-type VT_String +attr M1 obj-h5073-polldelay 86400 +attr M1 obj-h5073-reading ManufSerialNum +attr M1 obj-h5073-type VT_String +attr M1 obj-h5457-len 1 +attr M1 obj-h5457-map 0048:Kühlung, 0040:Idle, 0051:Warmwasser, 0052:Heizung +attr M1 obj-h5457-reading OperatingState +attr M1 obj-h5457-unpack H* +attr M1 obj-h5505-len 16 +attr M1 obj-h5505-reading ADC_Error +attr M1 obj-h5505-unpack H* +attr M1 obj-h5521-reading LCD_Display_Line_1 +attr M1 obj-h5521-type VT_String +attr M1 obj-h5649-reading LCD_Display_Line_2 +attr M1 obj-h5649-type VT_String +attr M1 obj-i1217-reading HeatReturnTempNominal +attr M1 obj-i1537-reading CoolReturnTemp +attr M1 obj-i1681-reading DomesticWaterTempActual +attr M1 obj-i2625-reading OHCompressor1 +attr M1 obj-i2657-reading OHCompressor2 +attr M1 obj-i2689-reading OHHeatingCompressor +attr M1 obj-i2721-reading OHHeatingAuxilary +attr M1 obj-i2753-reading OHCooling +attr M1 obj-i2785-reading OHDomesticWaterCompressor +attr M1 obj-i433-reading OutdoorTemp +attr M1 obj-i433-showGet 1 +attr M1 obj-i4561-reading COP +attr M1 obj-i4561-showGet 1 +attr M1 obj-i465-reading OutdoorTemp1h +attr M1 obj-i497-reading OutdoorTemp24h +attr M1 obj-i529-reading HeatSourceIn +attr M1 obj-i561-reading HeatSourceOut +attr M1 obj-i593-reading EvaporationTemp +attr M1 obj-i625-reading SuctionGasTemp +attr M1 obj-i657-reading EvaporationPress +attr M1 obj-i689-reading ReturnTempNominal +attr M1 obj-i721-reading ReturnTemp +attr M1 obj-i753-reading FlowTemp +attr M1 obj-i785-reading CondensationTemp +attr M1 obj-i817-reading CondensationPress +attr M1 obj-i849-reading RoomTemp +attr M1 obj-i881-reading RoomTemp1h +attr M1 obj-i913-reading DomesticWaterTemp +attr M1 obj-i945-reading PoolTemp +attr M1 obj-i977-reading SolarTemp + +attr M1 sortUpdate 1 + diff --git a/fhem/t/FHEM/98_Modbus/41_Passive.t b/fhem/t/FHEM/98_Modbus/41_Passive.t new file mode 100644 index 000000000..6486197bb --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/41_Passive.t @@ -0,0 +1,147 @@ +############################################## +# test passive reception +############################################## + +package main; + +use strict; +use warnings; +use Test::More; +use Time::HiRes qw( gettimeofday tv_interval); # return time as float, not just full seconds +use FHEM::HTTPMOD::Utils qw(:all); +use FHEM::Modbus::TestUtils qw(:all); + +#$logInform{'MS'} = \&ReactOnSendingLog; + +my @rData = ( + + '05030100000585b1', # request h 256 - h 260 (TempWasserEin, TempWasserAus) + '05030a0137110001381100010dac7b', # response + + '0503010600016473', # request h 262 + '0503020106c816', # response + + '050303020001240a', # request h 770 + '0503020122c80d', # response + + '05030309000155c8', # request + '05030200004984', # response + + '0503010000018472', + '050302013709c2', # response + + '0506030900005808', # request set hyst mode + '0506030900005808', # response + + '0506030201182850', # request set temp soll 28 + '0506030201182850' # response +); +my $dataPtr = 0; + +fhem 'attr global mseclog 1'; +InternalTimer(gettimeofday()+5, "testStepLast", 0); # last resort +NextStep(); + + +sub testStep1 { + LogStep "send first request in parts"; + FhemTestUtils_resetLogs(); + SimRead('MS', \&Modbus::ReadFn, 'fe03'); # part of a request + return; +} + + +sub testStep2 { + FhemTestUtils_resetLogs(); + SimRead('MS', \&Modbus::ReadFn, '0164000810'); # part of a request + return; +} + + +sub testStep3 { + FhemTestUtils_resetLogs(); + SimRead('MS', \&Modbus::ReadFn, '20'); # final part of a request + return; +} + + +sub testStep4 { + LogStep "check reception of request and send another request"; + is(FhemTestUtils_gotLog('received valid request, now wait for the reponse'), 1, "first request reassembled correctly"); + FhemTestUtils_resetLogs(); + FhemTestUtils_resetEvents(); + SimRead('MS', \&Modbus::ReadFn, 'fe03016400081020'); # another request + return; +} + + +sub testStep5 { + LogStep "check reception of repeated request and send first reply"; + is(FhemTestUtils_gotLog('no valid response -> try interpretation as request instead'), 1, "invalid respone and switch to request"); + is(FhemTestUtils_gotLog('received valid request, now wait for the reponse'), 1, "second request interpreted"); + FhemTestUtils_resetLogs(); + FhemTestUtils_resetEvents(); + SimRead('MS', \&Modbus::ReadFn,'fe03100000000b000000400000011a00000167f378'); # the reply + return; +} + + +sub testStep6 { + LogStep "check reception of reply and send another repeated reply"; + is(FhemTestUtils_gotLog('ParseObj has no information about handling h356'), 1, "try parsing registers"); + FhemTestUtils_resetLogs(); + FhemTestUtils_resetEvents(); + SimRead('MS', \&Modbus::ReadFn,'fe03100000000b000000400000011a00000167f378'); # the reply repeated + return; +} + + +sub testStep7 { + LogStep "check reception of repeated reply"; + is(FhemTestUtils_gotLog('ParseObj has no information about handling'), 0, "no try parsing registers again since request is missing"); + is(FhemTestUtils_gotLog('HandleResponse got data but we don.t have a request'), 1, "next response without a request seen"); + FhemTestUtils_resetLogs(); + FhemTestUtils_resetEvents(); + SimRead('MS', \&Modbus::ReadFn, 'fe03064000810209'); # a broken frame + return; +} + + +sub testStep8 { + is(FhemTestUtils_gotLog('HandleRequest Done, error: '), 1, "invalid frame"); + FhemTestUtils_resetLogs(); + SimRead('MS', \&Modbus::ReadFn, 'fe03016400081020'); # another request + return; +} + +sub testStep9 { + is(FhemTestUtils_gotLog('received valid request, now wait for the reponse'), 1, "request after garbage interpreted"); + FhemTestUtils_resetLogs(); + return; +} + + +sub testStep10 { + LogStep "check broken frame with illegal fcode"; + FhemTestUtils_resetLogs(); + FhemTestUtils_resetEvents(); + SimRead('MS', \&Modbus::ReadFn, 'fe00064000810209'); # a broken frame + return; +} + + +sub testStep11 { + is(FhemTestUtils_gotLog('HandleRequest Done, error:'), 1, "invalid frame"); + FhemTestUtils_resetLogs(); + SimRead('MS', \&Modbus::ReadFn, 'fe03016400081020'); # another request + return; +} + +sub testStep12 { + is(FhemTestUtils_gotLog('received valid request, now wait for the reponse'), 1, "request after illegal fcode interpreted"); + FhemTestUtils_resetLogs(); + return; +} + + +1; diff --git a/fhem/t/FHEM/98_Modbus/42_MasterRTU.cfg b/fhem/t/FHEM/98_Modbus/42_MasterRTU.cfg new file mode 100644 index 000000000..7e9266ccf --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/42_MasterRTU.cfg @@ -0,0 +1,83 @@ +define MS Modbus none +attr MS verbose 5 +attr MS clientSwitchDelay 0 +attr MS busDelay 0 + +define PWP ModbusAttr 5 0 +attr PWP verbose 5 +attr PWP dev-timing-sendDelay 0 +attr PWP dev-timing-commDelay 0 +attr PWP dev-timing-timeout 1 +attr PWP dev-h-defSet 1 +attr PWP dev-h-defShowGet 1 +attr PWP dev-h-combine 16 + +attr M5 nonPrioritizedGet 1 + +attr PWP obj-h256-reading Temp_Wasser_Ein +attr PWP obj-h256-expr $val / 10 +attr PWP obj-h256-poll 1 +attr PWP obj-h256-polldelay 0 + +attr PWP obj-h258-reading Temp_Wasser_Aus +attr PWP obj-h258-expr $val / 10 +attr PWP obj-h258-poll 1 +attr PWP obj-h258-polldelay 0 + +attr PWP obj-h260-reading Temp_Verdampfer +attr PWP obj-h260-expr $val / 10 +attr PWP obj-h260-poll 1 +attr PWP obj-h260-polldelay 0 + +attr PWP obj-h262-reading Temp_Luft +attr PWP obj-h262-expr $val / 10 +attr PWP obj-h262-poll 0 + +attr PWP obj-h770-reading Temp_Soll +attr PWP obj-h770-expr $val / 10 +attr PWP obj-h770-hint 8,10,20,25,28,29,30,30.5,31,31.5,32 +attr PWP obj-h770-max 32 +attr PWP obj-h770-min 10 +attr PWP obj-h770-set 1 +attr PWP obj-h770-setexpr $val * 10 + +attr PWP obj-h771-reading Hysterese +attr PWP obj-h771-expr $val / 10 +attr PWP obj-h771-max 3 +attr PWP obj-h771-min 0.5 +attr PWP obj-h771-set 1 +attr PWP obj-h771-setexpr $val * 10 + +attr PWP obj-h777-reading Hyst_Mode +attr PWP obj-h777-map 0:mittig, 1:über, 2:unterhalb +attr PWP obj-h777-set 1 + +attr PWP obj-h801-reading Temp_Wasser_Ein_Off +attr PWP obj-h801-expr $val / 10 +attr PWP obj-h801-name CF24 +attr PWP obj-h801-poll 0 +attr PWP obj-h801-set 1 +attr PWP obj-h801-setexpr $val * 10 + +attr PWP obj-h802-reading Temp_Wasser_Aus_Off +attr PWP obj-h802-expr $val / 10 +attr PWP obj-h802-name CF25 +attr PWP obj-h802-poll 0 +attr PWP obj-h802-set 1 +attr PWP obj-h802-setexpr $val * 10 + +attr PWP obj-h803-reading Temp_Verdampfer_Off +attr PWP obj-h803-expr $val / 10 +attr PWP obj-h803-name CF26 +attr PWP obj-h803-poll 0 +attr PWP obj-h803-set 1 +attr PWP obj-h803-setexpr $val * 10 + +attr PWP obj-h804-reading Temp_Luft_Off +attr PWP obj-h804-expr $val / 10 +attr PWP obj-h804-name CF27 +attr PWP obj-h804-poll 0 +attr PWP obj-h804-set 1 +attr PWP obj-h804-setexpr $val * 10 + + diff --git a/fhem/t/FHEM/98_Modbus/42_MasterRTU.t b/fhem/t/FHEM/98_Modbus/42_MasterRTU.t new file mode 100644 index 000000000..88cc6e7b3 --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/42_MasterRTU.t @@ -0,0 +1,121 @@ +############################################## +# test modbus RTU Master +############################################## + +package main; + +use strict; +use warnings; +use Test::More; +use Time::HiRes qw( gettimeofday tv_interval); # return time as float, not just full seconds +use FHEM::HTTPMOD::Utils qw(:all); +use FHEM::Modbus::TestUtils qw(:all); + +fhem 'attr global mseclog 1'; +NextStep(); + + +sub testStep1 { + LogStep('start reread'); + FhemTestUtils_resetLogs(); + fhem('set PWP reread'); + return; +} + + +sub testStep2 { + LogStep('simulate normal reception'); + SimRead('MS', \&Modbus::ReadFn, '05030a0137110001381100010dac7b'); # normal response + return; +} + + +sub testStep3 { + LogStep('check reception and start second reread'); + is(FhemTestUtils_gotEvent(qr/PWP:Temp_Wasser_Ein:\s31\.1/xms), 1, "Parse TempEin"); + is(FhemTestUtils_gotEvent(qr/PWP:Temp_Wasser_Aus:\s31\.2/xms), 1, "Parse TempAus"); + is(FhemTestUtils_gotEvent(qr/PWP:Temp_Verdampfer:\s26\.9/xms), 1, "Parse TempVerdampfer"); + FhemTestUtils_resetLogs(); + FhemTestUtils_resetEvents(); + fhem('set PWP reread'); + return; +} + + +sub testStep4 { + LogStep('simulate short response'); + SimRead('MS', \&Modbus::ReadFn, '05030a013711000138110091a8'); # short response + return 1.1; # next step after 1.1 seconds +} + + +sub testStep5 { + LogStep('verify failed short response and then allow them and reread'); + is(FhemTestUtils_gotLog('frame that looks valid but is too short'), 1, "short frame"); + is(FhemTestUtils_gotEvent(qr/PWP:Temp_Wasser_Ein:\s31\.1/xms), 0, "No TempEin"); + + FhemTestUtils_resetLogs(); + FhemTestUtils_resetEvents(); + fhem('attr PWP dev-h-allowShortResponses 1'); + fhem('set PWP reread'); + return; +} + + +sub testStep6 { + LogStep('simulate another short response'); + SimRead('MS', \&Modbus::ReadFn, '05030a013711000138110091a8'); # short response + return; +} + + +sub testStep7 { + LogStep('verify valid short response reception and send another reread'); + is(FhemTestUtils_gotEvent(qr/PWP:Temp_Wasser_Ein:\s31\.1/xms), 1, "Parse TempEin"); + is(FhemTestUtils_gotEvent(qr/PWP:Temp_Wasser_Aus:\s31\.2/xms), 1, "Parse TempAus"); + is(FhemTestUtils_gotEvent(qr/PWP:Temp_Verdampfer:\s26\.9/xms), 0, "No Parse TempVerdampfer"); + + FhemTestUtils_resetLogs(); + FhemTestUtils_resetEvents(); + fhem('attr PWP dev-h-brokenFC3 1'); + fhem('set PWP reread'); + return; +} + + +sub testStep8 { + LogStep('simulate broken fc3 response'); + SimRead('MS', \&Modbus::ReadFn, '050301000137110001381100010dd04d'); # response type broken FC3 + return; +} + + +sub testStep9 { + LogStep('verify brokenfc3 reception and send another reread'); + is(FhemTestUtils_gotEvent(qr/PWP:Temp_Wasser_Ein:\s31\.1/xms), 1, "Parse TempEin"); + is(FhemTestUtils_gotEvent(qr/PWP:Temp_Wasser_Aus:\s31\.2/xms), 1, "Parse TempAus"); + is(FhemTestUtils_gotEvent(qr/PWP:Temp_Verdampfer:\s26\.9/xms), 1, "Parse TempVerdampfer"); + + FhemTestUtils_resetLogs(); + FhemTestUtils_resetEvents(); + fhem('attr PWP dev-h-brokenFC3 0'); + return 0.1; +} + +sub testStep10 { + LogStep('check polldelay'); + fhem('attr PWP obj-h256-polldelay 0'); + fhem('attr PWP obj-h258-polldelay 0'); + fhem('attr PWP obj-h260-polldelay 0.4'); + fhem('set PWP reread'); + return; +} + +sub testStep11 { + LogStep('check results'); + is(FhemTestUtils_gotLog('Simulate sending to none: 05030100000305b3'), 1, "request for 256 and 258 without 260 seen"); + return; +} + + +1; diff --git a/fhem/t/FHEM/98_Modbus/50_MasterSlave0.cfg b/fhem/t/FHEM/98_Modbus/50_MasterSlave0.cfg new file mode 100644 index 000000000..e21c6d6e2 --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/50_MasterSlave0.cfg @@ -0,0 +1,127 @@ +attr global mseclog 1 + +define D1 dummy + +define Slave ModbusAttr 5 slave global:5501 +attr Slave obj-h256-reading TempWasserEin +attr Slave obj-h258-reading D1:TempWasserAus + +attr Slave obj-h100-reading Test1 +attr Slave obj-h100-setexpr $val * 4 + +attr Slave obj-h101-reading Test2 +attr Slave obj-h101-unpack f> +attr Slave obj-h101-len 2 + +attr Slave obj-h103-reading Test3 +attr Slave obj-h103-unpack a8 +attr Slave obj-h103-len 4 + +attr Slave obj-h120-reading Test4 +attr Slave obj-h120-unpack f> +attr Slave obj-h120-len 2 + +attr Slave obj-h130-reading Test5 +attr Slave obj-h130-unpack a* +attr Slave obj-h130-len 2 + +attr Slave obj-c400-reading c0 +attr Slave obj-c401-reading c1 +attr Slave obj-c402-reading c2 +attr Slave obj-c403-reading c3 +attr Slave obj-c404-reading c4 +attr Slave obj-c405-reading c5 +attr Slave obj-c406-reading c6 +attr Slave obj-c407-reading c7 +attr Slave obj-c408-reading c8 +attr Slave obj-c409-reading c9 +attr Slave obj-c410-reading c10 +attr Slave obj-c411-reading c11 +attr Slave obj-c412-reading c12 +attr Slave obj-c413-reading c13 +attr Slave obj-c414-reading c14 +attr Slave obj-c415-reading c15 +attr Slave obj-c416-reading c16 +attr Slave obj-c417-reading c17 +attr Slave obj-c418-reading c18 + +define Master ModbusAttr 5 0 localhost:5501 +attr Master disable 1 +attr Master verbose 3 +attr Master nonPrioritizedGet 1 +attr Master nonPrioritizedSet 1 + +attr Master dev-timing-sendDelay 0 +attr Master dev-timing-commDelay 0 + +attr Master obj-h256-reading TempWasserEin +attr Master obj-h258-reading TempWasserAus + +attr Master obj-h100-reading Test1 +attr Master obj-h100-expr $val + 2 +attr Master obj-h100-poll 1 +attr Master obj-h100-polldelay 0 + +attr Master obj-h101-reading Test2 +attr Master obj-h101-unpack f> +attr Master obj-h101-len 2 +attr Master obj-h101-format %.2f +attr Master obj-h101-poll 1 +attr Master obj-h101-polldelay 0 + +attr Master obj-h103-reading Test3 +attr Master obj-h103-unpack a8 +attr Master obj-h103-len 4 +attr Master obj-h103-poll 1 +attr Master obj-h103-polldelay 0 + +attr Master obj-h120-reading Test4 +attr Master obj-h120-unpack f> +attr Master obj-h120-len 2 +attr Master obj-h120-format %.2f +attr Master obj-h120-poll 1 +attr Master obj-h120-ignoreExpr $val > 10 +attr Master obj-h120-polldelay 0 + +attr Master obj-h130-reading Test5 +attr Master obj-h130-unpack a* +attr Master obj-h130-len 2 +attr Master obj-h130-encode utf8 + +attr Master obj-h10-reading o1 +attr Master obj-h10-map 0:off, 1:on + +attr Master obj-h11-reading o2 +attr Master obj-h11-min 1 +attr Master obj-h11-max 3 +attr Master dev-h-defSet 1 +attr Master dev-c-defSet 1 +attr Master dev-h-defShowGet 1 + +attr Master obj-c400-reading c0 +attr Master obj-c401-reading c1 +attr Master obj-c402-reading c2 +attr Master obj-c403-reading c3 +attr Master obj-c404-reading c4 +attr Master obj-c405-reading c5 +attr Master obj-c406-reading c6 +attr Master obj-c407-reading c7 +attr Master obj-c408-reading c8 +attr Master obj-c409-reading c9 +attr Master obj-c410-reading c10 +attr Master obj-c411-reading c11 +attr Master obj-c412-reading c12 +attr Master obj-c413-reading c13 +attr Master obj-c414-reading c14 +attr Master obj-c415-reading c15 +attr Master obj-c416-reading c16 +attr Master obj-c417-reading c17 +attr Master obj-c418-reading c18 + +attr Master obj-c400-poll 1 +attr Master obj-c405-poll 1 +attr Master obj-c406-poll 1 +attr Master obj-c417-poll 1 + +attr Master dev-h-combine 19 +attr Master dev-c-combine 32 \ No newline at end of file diff --git a/fhem/t/FHEM/98_Modbus/50_MasterSlave0.t b/fhem/t/FHEM/98_Modbus/50_MasterSlave0.t new file mode 100644 index 000000000..a9682890d --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/50_MasterSlave0.t @@ -0,0 +1,231 @@ +############################################## +# test master slave end to end +############################################## + +package main; + +use strict; +use warnings; +use Test::More; +use Time::HiRes qw( gettimeofday tv_interval); # return time as float, not just full seconds +use FHEM::HTTPMOD::Utils qw(:all); +use FHEM::Modbus::TestUtils qw(:all); + +fhem 'attr global mseclog 1'; +InternalTimer(gettimeofday()+5, "testStepLast", 0); # last resort +NextStep(); + +sub testStep1 { # preparation of slave content, enable devices + is(FhemTestUtils_gotLog('attribute'), 0, "no unknown attributes"); # logs during init are not collected. + LogStep "enable Master and set value at Slave"; + fhem ('attr Master disable 0'); + fhem ('setreading Slave TempWasserEin 12'); + fhem ('setreading Slave Test1 1'); + fhem ('setreading Slave Test2 2.123'); + fhem ('setreading Slave Test3 abcdefg'); + fhem ('setreading Slave Test4 40'); + readingsSingleUpdate($defs{'Slave'}, 'Test5', pack('H*', 'e4f6fc'), 0); + + fhem ('setreading Slave c0 1'); + fhem ('setreading Slave c5 1'); + fhem ('setreading Slave c17 1'); + return 0.1; +} + +sub testStep2 { # get holding registers + LogStep "get TempWasserEin"; + fhem ('attr Master verbose 3'); + fhem ('attr Slave verbose 3'); + fhem ('get Master TempWasserEin'); + fhem ('get Master Test1'); + fhem ('get Master Test2'); + fhem ('get Master Test3'); + fhem ('get Master Test4'); + fhem ('get Master Test5'); + return 0.3; +} + +sub testStep3 { # check results + LogStep "check result"; + fhem ('attr Master verbose 3'); + fhem ('attr Slave verbose 3'); + is(FhemTestUtils_gotEvent(qr/Master:TempWasserEin:\s12/xms), 1, "Retrieve integer value from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:Test1: 6/), 1, "Retrieve another integer value with expressions on both sides from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:Test2: 2.12/), 1, "Retrieve float value from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:Test3: abcdefg/), 1, "Retrieve ascii value from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:Test4: 40/), 0, "ignoreExpr prohibits Test4 set to 40"); + is(FhemTestUtils_gotEvent(qr/Master:Test5: äöü/), 1, "encode worked for Test5"); + return; +} + +sub testStep4 { # set holding register without allowance at salve + LogStep "set TempWasserAus at Slave"; + fhem ('set Master TempWasserAus 20'); + fhem ('attr Master verbose 4'); + return 0.2; +} + +sub testStep5 { # check that write was forbidden + LogStep "Check error response"; + is(FhemTestUtils_gotLog('Master: HandleResponse got response with error code 86 / 01, illegal function'), 1, "disallow write by default"); + fhem ('attr Master verbose 3'); + return; +} + +sub testStep6 { # allow write at slave and try again to write + LogStep "allow write and try again"; + fhem ('attr Slave obj-h258-allowWrite 1'); + fhem ('set Master TempWasserAus 20'); + return 0.1; +} + +sub testStep7 { # check that write holding register did work + LogStep "check result"; + is(FhemTestUtils_gotEvent(qr/D1:TempWasserAus:\s20/xms), 1, "Write value to local slave"); + return 0.1; +} + +sub testStep8 { # check input validation at master and write + LogStep "set with map and min/max"; + fhem ('set Master o1 one'); + is(FhemTestUtils_gotLog('set Master o1 one : set value one did not match defined map'), 1, "map error message in log"); + fhem ('set Master o2 0'); + is(FhemTestUtils_gotLog('set Master o2 0 : value 0 is not within defined min/max range'), 1, "min error message in log"); + fhem ('set Master o2 4'); + is(FhemTestUtils_gotLog('set Master o2 4 : value 4 is not within defined min/max range'), 1, "max error message in log"); + + fhem ('attr Master verbose 4'); + fhem ('set Master o2 2'); + fhem ('set Master o1 on'); + return 0.2; +} + + +sub testStep9 { # check write data + LogStep "check log for map and set o2 2"; + is(FhemTestUtils_gotLog('0506000a0001698c'), 1, "set o1 on message in log"); + is(FhemTestUtils_gotLog('0506000b0002784d'), 1, "set O2 2 message in log"); + fhem ('attr Master verbose 3'); + return 0.1; +} + + +sub testStep10 { # check combined read of holding registers and coils + LogStep "getUpdate with combine"; + FhemTestUtils_resetEvents(); + fhem ('attr Master verbose 3'); + fhem ('attr Slave verbose 3'); + fhem ('set Master reread'); + return 0.1; +} + +sub testStep11 { # check results coming from slave and write coils to slave + is(FhemTestUtils_gotEvent(qr/Master:Test1: 6/), 1, "Combined retrieve integer value with expressions on both sides from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:Test2: 2.12/), 1, "Combined retrieve float value from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:Test3: abcdefg/), 1, "Combined Retrieve ascii value from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:c0: 1/), 1, "Combined Retrieve coil bit 0 from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:c1: 0/), 1, "Combined Retrieve coil bit 1 from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:c17: 1/), 1, "Combined Retrieve coil bit 17 from local slave"); + + fhem ('attr Slave obj-c402-allowWrite 1'); + fhem ('attr Master verbose 5'); + fhem ('set Master c2 1'); + return 0.1; +} + +sub testStep12 { + LogStep "check coil comm"; + + is(FhemTestUtils_gotLog('sending 05050192ff002daf'), 1, "set c2 1 sending message in log"); + is(FhemTestUtils_gotEvent(qr/Master:c2: 1/), 1, "fc5 response for coil shows 1 from local slave"); + + Log3 undef, 1, "TestStep12: try to write with fc16"; + fhem ('attr Master verbose 3'); + fhem ('attr Slave verbose 3'); + fhem ('attr Master dev-h-write 16'); + fhem ('set Master TempWasserAus 29'); + return 0.1; +} + +sub testStep13 { + LogStep "check write result of fc16"; + is(FhemTestUtils_gotEvent(qr/D1:TempWasserAus:\s29/xms), 1, "Write value with fc16 to local slave"); + return 0.1; +} + +sub testStep14 { + LogStep "closeAfterResponse"; + FhemTestUtils_resetEvents(); + FhemTestUtils_resetLogs(); + fhem ('attr Master closeAfterResponse 1'); + fhem ('attr Master verbose 4'); + fhem ('set Master reread'); + return 0.1; +} + +sub testStep15 { + is(FhemTestUtils_gotEvent(qr/Master:Test1: 6/), 1, "Retrieve Test1"); + is(FhemTestUtils_gotEvent(qr/Master:Test3: abcdefg/), 1, "Retrieve Test4"); + is(FhemTestUtils_gotLog('HandleResponse will close because closeAfterResponse is set and queue is empty'), 1, "closed"); + return 0.1; +} + +sub testStep16 { + LogStep "try get while closed"; + FhemTestUtils_resetEvents(); + FhemTestUtils_resetLogs(); + fhem ('get Master TempWasserEin'); + fhem ('attr Master queueDelay 0.3'); + return 0.1; +} + +sub testStep17 { + LogStep "check get result while connection closed"; + is(FhemTestUtils_gotLog('device opened'), 1, "device opened"); + is(FhemTestUtils_gotEvent(qr/Master:TempWasserEin:\s12/xms), 0, "No retrieve from local slave yet"); + return 0.3; +} + +sub testStep18 { + LogStep "check get result after another delay"; + is(FhemTestUtils_gotEvent(qr/Master:TempWasserEin:\s12/xms), 1, "retrieve from local slave after open and QueueDelay"); + is(FhemTestUtils_gotLog('close because closeAfterResponse'), 1, "device closed again"); + return 0.1; +} + + +sub testStep19 { + LogStep "now that the connection is closed again, try another prioritized get"; + FhemTestUtils_resetEvents(); + FhemTestUtils_resetLogs(); + fhem ('attr Master nonPrioritizedGet 0'); + fhem ('attr Master dev-timing-timeout 0.5'); + fhem ('attr Master verbose 5'); + fhem ('get Master TempWasserEin'); + return 0.1; +} + +sub testStep20 { + LogStep "check result after prio get"; + is(FhemTestUtils_gotLog('device opened'), 1, "device opened"); + is(FhemTestUtils_gotLog('Master: Timeout in Readanswer'), 1, "readanswer called but slave cannot answer while sitting in readanswer"); + return; +} + + +sub testStep21 { + LogStep "check result after prio get"; + is(FhemTestUtils_gotLog('Master: read buffer: 050302000c4981'), 1, "answer arrives after readanswer timeout"); + return; +} + + +# todo: different protocols (RTU, ASCII, TCP) +# data type definition +# swap bytes and similar +# unpack variations + +# then relay mode + + +1; diff --git a/fhem/t/FHEM/98_Modbus/53_Relay.cfg b/fhem/t/FHEM/98_Modbus/53_Relay.cfg new file mode 100644 index 000000000..137c3329f --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/53_Relay.cfg @@ -0,0 +1,135 @@ +attr global mseclog 1 + +define D1 dummy + +define Slave ModbusAttr 50 slave global:5501 +attr Slave obj-h256-reading TempWasserEin +attr Slave obj-h258-reading D1:TempWasserAus + +attr Slave obj-h100-reading Test1 +attr Slave obj-h100-setexpr $val * 4 + +attr Slave obj-h101-reading Test2 +attr Slave obj-h101-unpack f> +attr Slave obj-h101-len 2 + +attr Slave obj-h103-reading Test3 +attr Slave obj-h103-unpack a8 +attr Slave obj-h103-len 4 + +attr Slave obj-h120-reading Test4 +attr Slave obj-h120-unpack f> +attr Slave obj-h120-len 2 + +attr Slave obj-h130-reading Test5 +attr Slave obj-h130-unpack a* +attr Slave obj-h130-len 2 + +attr Slave obj-c400-reading c0 +attr Slave obj-c401-reading c1 +attr Slave obj-c402-reading c2 +attr Slave obj-c403-reading c3 +attr Slave obj-c404-reading c4 +attr Slave obj-c405-reading c5 +attr Slave obj-c406-reading c6 +attr Slave obj-c407-reading c7 +attr Slave obj-c408-reading c8 +attr Slave obj-c409-reading c9 +attr Slave obj-c410-reading c10 +attr Slave obj-c411-reading c11 +attr Slave obj-c412-reading c12 +attr Slave obj-c413-reading c13 +attr Slave obj-c414-reading c14 +attr Slave obj-c415-reading c15 +attr Slave obj-c416-reading c16 +attr Slave obj-c417-reading c17 +attr Slave obj-c418-reading c18 + +define RM ModbusAttr 50 0 localhost:5501 +attr RM dev-timing-sendDelay 0 +attr RM dev-timing-commDelay 0 +attr RM disable 1 + +define Relay ModbusAttr 5 relay localhost:5510 to RM +attr Relay disable 1 + +define Master ModbusAttr 5 0 localhost:5510 +attr Master disable 1 +attr Master verbose 3 +attr Master nonPrioritizedGet 1 +attr Master nonPrioritizedSet 1 + +attr Master dev-timing-sendDelay 0 +attr Master dev-timing-commDelay 0 + +attr Master obj-h256-reading TempWasserEin +attr Master obj-h258-reading TempWasserAus + +attr Master obj-h100-reading Test1 +attr Master obj-h100-expr $val + 2 +attr Master obj-h100-poll 1 +attr Master obj-h100-polldelay 0 + +attr Master obj-h101-reading Test2 +attr Master obj-h101-unpack f> +attr Master obj-h101-len 2 +attr Master obj-h101-format %.2f +attr Master obj-h101-poll 1 +attr Master obj-h101-polldelay 0 + +attr Master obj-h103-reading Test3 +attr Master obj-h103-unpack a8 +attr Master obj-h103-len 4 +attr Master obj-h103-poll 1 +attr Master obj-h103-polldelay 0 + +attr Master obj-h120-reading Test4 +attr Master obj-h120-unpack f> +attr Master obj-h120-len 2 +attr Master obj-h120-format %.2f +attr Master obj-h120-poll 1 +attr Master obj-h120-ignoreExpr $val > 10 +attr Master obj-h120-polldelay 0 + +attr Master obj-h130-reading Test5 +attr Master obj-h130-unpack a* +attr Master obj-h130-len 2 +attr Master obj-h130-encode utf8 + +attr Master obj-h10-reading o1 +attr Master obj-h10-map 0:off, 1:on + +attr Master obj-h11-reading o2 +attr Master obj-h11-min 1 +attr Master obj-h11-max 3 +attr Master dev-h-defSet 1 +attr Master dev-c-defSet 1 +attr Master dev-h-defShowGet 1 + +attr Master obj-c400-reading c0 +attr Master obj-c401-reading c1 +attr Master obj-c402-reading c2 +attr Master obj-c403-reading c3 +attr Master obj-c404-reading c4 +attr Master obj-c405-reading c5 +attr Master obj-c406-reading c6 +attr Master obj-c407-reading c7 +attr Master obj-c408-reading c8 +attr Master obj-c409-reading c9 +attr Master obj-c410-reading c10 +attr Master obj-c411-reading c11 +attr Master obj-c412-reading c12 +attr Master obj-c413-reading c13 +attr Master obj-c414-reading c14 +attr Master obj-c415-reading c15 +attr Master obj-c416-reading c16 +attr Master obj-c417-reading c17 +attr Master obj-c418-reading c18 + +attr Master obj-c400-poll 1 +attr Master obj-c405-poll 1 +attr Master obj-c406-poll 1 +attr Master obj-c417-poll 1 + +attr Master dev-h-combine 19 +attr Master dev-c-combine 32 \ No newline at end of file diff --git a/fhem/t/FHEM/98_Modbus/53_Relay.t b/fhem/t/FHEM/98_Modbus/53_Relay.t new file mode 100644 index 000000000..a2cb842cd --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/53_Relay.t @@ -0,0 +1,230 @@ +############################################## +# test master slave end to end +############################################## + +package main; + +use strict; +use warnings; +use Test::More; +use Time::HiRes qw( gettimeofday tv_interval); # return time as float, not just full seconds +use FHEM::HTTPMOD::Utils qw(:all); +use FHEM::Modbus::TestUtils qw(:all); + +fhem 'attr global mseclog 1'; +InternalTimer(gettimeofday()+5, "testStepLast", 0); # last resort +NextStep(); + +sub testStep1 { # preparation of slave content, enable devices + is(FhemTestUtils_gotLog('attribute'), 0, "no unknown attributes"); # logs during init are not collected. + LogStep "enable Master and set value at Slave"; + fhem ('attr RM disable 0'); + fhem ('attr Relay disable 0'); + fhem ('attr Master disable 0'); + fhem ('setreading Slave TempWasserEin 12'); + fhem ('setreading Slave Test1 1'); + fhem ('setreading Slave Test2 2.123'); + fhem ('setreading Slave Test3 abcdefg'); + fhem ('setreading Slave Test4 40'); + readingsSingleUpdate($defs{'Slave'}, 'Test5', pack('H*', 'e4f6fc'), 0); + + fhem ('setreading Slave c0 1'); + fhem ('setreading Slave c5 1'); + fhem ('setreading Slave c17 1'); + return 0.1; +} + +sub testStep2 { # get holding registers + LogStep "get TempWasserEin"; + fhem ('attr Master verbose 3'); + fhem ('attr Slave verbose 3'); + fhem ('attr Relay verbose 3'); + fhem ('attr RM verbose 3'); + fhem ('get Master TempWasserEin'); + fhem ('get Master Test1'); + fhem ('get Master Test2'); + fhem ('get Master Test3'); + fhem ('get Master Test4'); + fhem ('get Master Test5'); + return 1; +} + +sub testStep3 { # check results + LogStep "check result"; + is(FhemTestUtils_gotEvent(qr/Master:TempWasserEin:\s12/xms), 1, "Retrieve integer value from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:Test1: 6/), 1, "Retrieve another integer value with expressions on both sides from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:Test2: 2.12/), 1, "Retrieve float value from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:Test3: abcdefg/), 1, "Retrieve ascii value from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:Test4: 40/), 0, "ignoreExpr prohibits Test4 set to 40"); + is(FhemTestUtils_gotEvent(qr/Master:Test5: äöü/), 1, "encode worked for Test5"); + return; +} + +sub testStep4 { # set holding register without allowance at salve + LogStep "set TempWasserAus at Slave"; + fhem ('set Master TempWasserAus 20'); + fhem ('attr Master verbose 4'); + return 0.2; +} + +sub testStep5 { # check that write was forbidden + LogStep "Check error response"; + is(FhemTestUtils_gotLog('Master: HandleResponse got response with error code 86 / 01, illegal function'), 1, "disallow write by default"); + fhem ('attr Master verbose 3'); + return; +} + +sub testStep6 { # allow write at slave and try again to write + LogStep "allow write and try again"; + fhem ('attr Slave obj-h258-allowWrite 1'); + fhem ('set Master TempWasserAus 20'); + return 0.1; +} + +sub testStep7 { # check that write holding register did work + LogStep "check result"; + is(FhemTestUtils_gotEvent(qr/D1:TempWasserAus:\s20/xms), 1, "Write value to local slave"); + return 0.1; +} + +sub testStep8 { # check input validation at master and write + LogStep "set with map and min/max"; + fhem ('set Master o1 one'); + is(FhemTestUtils_gotLog('set Master o1 one : set value one did not match defined map'), 1, "map error message in log"); + fhem ('set Master o2 0'); + is(FhemTestUtils_gotLog('set Master o2 0 : value 0 is not within defined min/max range'), 1, "min error message in log"); + fhem ('set Master o2 4'); + is(FhemTestUtils_gotLog('set Master o2 4 : value 4 is not within defined min/max range'), 1, "max error message in log"); + + fhem ('attr Master verbose 4'); + fhem ('set Master o2 2'); + fhem ('set Master o1 on'); + return 0.2; +} + + +sub testStep9 { # check write data + LogStep "check log for map and set o2 2"; + is(FhemTestUtils_gotLog('0506000a0001698c'), 1, "set o1 on message in log"); + is(FhemTestUtils_gotLog('0506000b0002784d'), 1, "set O2 2 message in log"); + fhem ('attr Master verbose 3'); + return 0.1; +} + + +sub testStep10 { # check combined read of holding registers and coils + LogStep "getUpdate with combine"; + FhemTestUtils_resetEvents(); + fhem ('set Master reread'); + return 0.1; +} + +sub testStep11 { # check results coming from slave and write coils to slave + is(FhemTestUtils_gotEvent(qr/Master:Test1: 6/), 1, "Combined retrieve integer value with expressions on both sides from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:Test2: 2.12/), 1, "Combined retrieve float value from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:Test3: abcdefg/), 1, "Combined Retrieve ascii value from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:c0: 1/), 1, "Combined Retrieve coil bit 0 from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:c1: 0/), 1, "Combined Retrieve coil bit 1 from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:c17: 1/), 1, "Combined Retrieve coil bit 17 from local slave"); + + fhem ('attr Slave obj-c402-allowWrite 1'); + fhem ('attr Master verbose 5'); + fhem ('set Master c2 1'); + return 0.1; +} + +sub testStep12 { + LogStep "check coil comm"; + + is(FhemTestUtils_gotLog('sending 05050192ff002daf'), 1, "set c2 1 sending message in log"); + is(FhemTestUtils_gotEvent(qr/Master:c2: 1/), 1, "fc5 response for coil shows 1 from local slave"); + + Log3 undef, 1, "TestStep12: try to write with fc16"; + fhem ('attr Master verbose 3'); + fhem ('attr Slave verbose 3'); + fhem ('attr Master dev-h-write 16'); + fhem ('set Master TempWasserAus 29'); + return 0.1; +} + +sub testStep13 { + LogStep "check write result of fc16"; + is(FhemTestUtils_gotEvent(qr/D1:TempWasserAus:\s29/xms), 1, "Write value with fc16 to local slave"); + return 0.1; +} + +sub testStep14 { + LogStep "closeAfterResponse"; + FhemTestUtils_resetEvents(); + FhemTestUtils_resetLogs(); + fhem ('attr Master closeAfterResponse 1'); + fhem ('attr Master verbose 4'); + fhem ('set Master reread'); + return 0.1; +} + +sub testStep15 { + is(FhemTestUtils_gotEvent(qr/Master:Test1: 6/), 1, "Retrieve Test1"); + is(FhemTestUtils_gotEvent(qr/Master:Test4: 40/), 0, "Retrieve Test4"); + is(FhemTestUtils_gotLog('HandleResponse will close because closeAfterResponse is set and queue is empty'), 1, "closed"); + return 0.1; +} + +sub testStep16 { + LogStep "try get while closed"; + FhemTestUtils_resetEvents(); + FhemTestUtils_resetLogs(); + fhem ('get Master TempWasserEin'); + fhem ('attr Master queueDelay 0.3'); + return 0.1; +} + +sub testStep17 { + LogStep "check get result while connection closed"; + is(FhemTestUtils_gotLog('device opened'), 1, "device opened"); + is(FhemTestUtils_gotEvent(qr/Master:TempWasserEin:\s12/xms), 0, "No retrieve from local slave yet"); + return 0.3; +} + +sub testStep18 { + LogStep "check get result after another delay"; + is(FhemTestUtils_gotEvent(qr/Master:TempWasserEin:\s12/xms), 1, "retrieve from local slave after open and QueueDelay"); + is(FhemTestUtils_gotLog('close because closeAfterResponse'), 1, "device closed again"); + return 0.1; +} + + +sub testStep19 { + LogStep "now that the connection is closed again, try another prioritized get"; + FhemTestUtils_resetEvents(); + FhemTestUtils_resetLogs(); + fhem ('attr Master nonPrioritizedGet 0'); + fhem ('attr Master dev-timing-timeout 0.5'); + fhem ('attr Master verbose 5'); + fhem ('get Master TempWasserEin'); + return 0.1; +} + +sub testStep20 { + LogStep "check result after prio get"; + is(FhemTestUtils_gotLog('device opened'), 1, "device opened"); + is(FhemTestUtils_gotLog('Master: Timeout in Readanswer'), 1, "readanswer called but slave cannot answer while sitting in readanswer"); + return; +} + +sub testStep21 { + LogStep "check result after prio get"; + is(FhemTestUtils_gotLog('Master: read buffer: 050302000c4981'), 1, "answer arrives after readanswer timeout"); + return; +} + + +# todo: different protocols (RTU, ASCII, TCP) +# data type definition +# swap bytes and similar +# unpack variations + +# then relay mode + + +1; diff --git a/fhem/t/FHEM/98_Modbus/54_RelayProto.cfg b/fhem/t/FHEM/98_Modbus/54_RelayProto.cfg new file mode 100644 index 000000000..681941092 --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/54_RelayProto.cfg @@ -0,0 +1,135 @@ +attr global mseclog 1 + +define D1 dummy + +define Slave ModbusAttr 50 slave global:5501 ASCII +attr Slave obj-h256-reading TempWasserEin +attr Slave obj-h258-reading D1:TempWasserAus + +attr Slave obj-h100-reading Test1 +attr Slave obj-h100-setexpr $val * 4 + +attr Slave obj-h101-reading Test2 +attr Slave obj-h101-unpack f> +attr Slave obj-h101-len 2 + +attr Slave obj-h103-reading Test3 +attr Slave obj-h103-unpack a8 +attr Slave obj-h103-len 4 + +attr Slave obj-h120-reading Test4 +attr Slave obj-h120-unpack f> +attr Slave obj-h120-len 2 + +attr Slave obj-h130-reading Test5 +attr Slave obj-h130-unpack a* +attr Slave obj-h130-len 2 + +attr Slave obj-c400-reading c0 +attr Slave obj-c401-reading c1 +attr Slave obj-c402-reading c2 +attr Slave obj-c403-reading c3 +attr Slave obj-c404-reading c4 +attr Slave obj-c405-reading c5 +attr Slave obj-c406-reading c6 +attr Slave obj-c407-reading c7 +attr Slave obj-c408-reading c8 +attr Slave obj-c409-reading c9 +attr Slave obj-c410-reading c10 +attr Slave obj-c411-reading c11 +attr Slave obj-c412-reading c12 +attr Slave obj-c413-reading c13 +attr Slave obj-c414-reading c14 +attr Slave obj-c415-reading c15 +attr Slave obj-c416-reading c16 +attr Slave obj-c417-reading c17 +attr Slave obj-c418-reading c18 + +define RM ModbusAttr 50 0 localhost:5501 ASCII +attr RM dev-timing-sendDelay 0 +attr RM dev-timing-commDelay 0 +attr RM disable 1 + +define Relay ModbusAttr 5 relay localhost:5510 RTU to RM +attr Relay disable 1 + +define Master ModbusAttr 5 0 localhost:5510 RTU +attr Master disable 1 +attr Master verbose 3 +attr Master nonPrioritizedGet 1 +attr Master nonPrioritizedSet 1 + +attr Master dev-timing-sendDelay 0 +attr Master dev-timing-commDelay 0 + +attr Master obj-h256-reading TempWasserEin +attr Master obj-h258-reading TempWasserAus + +attr Master obj-h100-reading Test1 +attr Master obj-h100-expr $val + 2 +attr Master obj-h100-poll 1 +attr Master obj-h100-polldelay 0 + +attr Master obj-h101-reading Test2 +attr Master obj-h101-unpack f> +attr Master obj-h101-len 2 +attr Master obj-h101-format %.2f +attr Master obj-h101-poll 1 +attr Master obj-h101-polldelay 0 + +attr Master obj-h103-reading Test3 +attr Master obj-h103-unpack a8 +attr Master obj-h103-len 4 +attr Master obj-h103-poll 1 +attr Master obj-h103-polldelay 0 + +attr Master obj-h120-reading Test4 +attr Master obj-h120-unpack f> +attr Master obj-h120-len 2 +attr Master obj-h120-format %.2f +attr Master obj-h120-poll 1 +attr Master obj-h120-ignoreExpr $val > 10 +attr Master obj-h120-polldelay 0 + +attr Master obj-h130-reading Test5 +attr Master obj-h130-unpack a* +attr Master obj-h130-len 2 +attr Master obj-h130-encode utf8 + +attr Master obj-h10-reading o1 +attr Master obj-h10-map 0:off, 1:on + +attr Master obj-h11-reading o2 +attr Master obj-h11-min 1 +attr Master obj-h11-max 3 +attr Master dev-h-defSet 1 +attr Master dev-c-defSet 1 +attr Master dev-h-defShowGet 1 + +attr Master obj-c400-reading c0 +attr Master obj-c401-reading c1 +attr Master obj-c402-reading c2 +attr Master obj-c403-reading c3 +attr Master obj-c404-reading c4 +attr Master obj-c405-reading c5 +attr Master obj-c406-reading c6 +attr Master obj-c407-reading c7 +attr Master obj-c408-reading c8 +attr Master obj-c409-reading c9 +attr Master obj-c410-reading c10 +attr Master obj-c411-reading c11 +attr Master obj-c412-reading c12 +attr Master obj-c413-reading c13 +attr Master obj-c414-reading c14 +attr Master obj-c415-reading c15 +attr Master obj-c416-reading c16 +attr Master obj-c417-reading c17 +attr Master obj-c418-reading c18 + +attr Master obj-c400-poll 1 +attr Master obj-c405-poll 1 +attr Master obj-c406-poll 1 +attr Master obj-c417-poll 1 + +attr Master dev-h-combine 19 +attr Master dev-c-combine 32 \ No newline at end of file diff --git a/fhem/t/FHEM/98_Modbus/54_RelayProto.t b/fhem/t/FHEM/98_Modbus/54_RelayProto.t new file mode 100644 index 000000000..a2cb842cd --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/54_RelayProto.t @@ -0,0 +1,230 @@ +############################################## +# test master slave end to end +############################################## + +package main; + +use strict; +use warnings; +use Test::More; +use Time::HiRes qw( gettimeofday tv_interval); # return time as float, not just full seconds +use FHEM::HTTPMOD::Utils qw(:all); +use FHEM::Modbus::TestUtils qw(:all); + +fhem 'attr global mseclog 1'; +InternalTimer(gettimeofday()+5, "testStepLast", 0); # last resort +NextStep(); + +sub testStep1 { # preparation of slave content, enable devices + is(FhemTestUtils_gotLog('attribute'), 0, "no unknown attributes"); # logs during init are not collected. + LogStep "enable Master and set value at Slave"; + fhem ('attr RM disable 0'); + fhem ('attr Relay disable 0'); + fhem ('attr Master disable 0'); + fhem ('setreading Slave TempWasserEin 12'); + fhem ('setreading Slave Test1 1'); + fhem ('setreading Slave Test2 2.123'); + fhem ('setreading Slave Test3 abcdefg'); + fhem ('setreading Slave Test4 40'); + readingsSingleUpdate($defs{'Slave'}, 'Test5', pack('H*', 'e4f6fc'), 0); + + fhem ('setreading Slave c0 1'); + fhem ('setreading Slave c5 1'); + fhem ('setreading Slave c17 1'); + return 0.1; +} + +sub testStep2 { # get holding registers + LogStep "get TempWasserEin"; + fhem ('attr Master verbose 3'); + fhem ('attr Slave verbose 3'); + fhem ('attr Relay verbose 3'); + fhem ('attr RM verbose 3'); + fhem ('get Master TempWasserEin'); + fhem ('get Master Test1'); + fhem ('get Master Test2'); + fhem ('get Master Test3'); + fhem ('get Master Test4'); + fhem ('get Master Test5'); + return 1; +} + +sub testStep3 { # check results + LogStep "check result"; + is(FhemTestUtils_gotEvent(qr/Master:TempWasserEin:\s12/xms), 1, "Retrieve integer value from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:Test1: 6/), 1, "Retrieve another integer value with expressions on both sides from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:Test2: 2.12/), 1, "Retrieve float value from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:Test3: abcdefg/), 1, "Retrieve ascii value from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:Test4: 40/), 0, "ignoreExpr prohibits Test4 set to 40"); + is(FhemTestUtils_gotEvent(qr/Master:Test5: äöü/), 1, "encode worked for Test5"); + return; +} + +sub testStep4 { # set holding register without allowance at salve + LogStep "set TempWasserAus at Slave"; + fhem ('set Master TempWasserAus 20'); + fhem ('attr Master verbose 4'); + return 0.2; +} + +sub testStep5 { # check that write was forbidden + LogStep "Check error response"; + is(FhemTestUtils_gotLog('Master: HandleResponse got response with error code 86 / 01, illegal function'), 1, "disallow write by default"); + fhem ('attr Master verbose 3'); + return; +} + +sub testStep6 { # allow write at slave and try again to write + LogStep "allow write and try again"; + fhem ('attr Slave obj-h258-allowWrite 1'); + fhem ('set Master TempWasserAus 20'); + return 0.1; +} + +sub testStep7 { # check that write holding register did work + LogStep "check result"; + is(FhemTestUtils_gotEvent(qr/D1:TempWasserAus:\s20/xms), 1, "Write value to local slave"); + return 0.1; +} + +sub testStep8 { # check input validation at master and write + LogStep "set with map and min/max"; + fhem ('set Master o1 one'); + is(FhemTestUtils_gotLog('set Master o1 one : set value one did not match defined map'), 1, "map error message in log"); + fhem ('set Master o2 0'); + is(FhemTestUtils_gotLog('set Master o2 0 : value 0 is not within defined min/max range'), 1, "min error message in log"); + fhem ('set Master o2 4'); + is(FhemTestUtils_gotLog('set Master o2 4 : value 4 is not within defined min/max range'), 1, "max error message in log"); + + fhem ('attr Master verbose 4'); + fhem ('set Master o2 2'); + fhem ('set Master o1 on'); + return 0.2; +} + + +sub testStep9 { # check write data + LogStep "check log for map and set o2 2"; + is(FhemTestUtils_gotLog('0506000a0001698c'), 1, "set o1 on message in log"); + is(FhemTestUtils_gotLog('0506000b0002784d'), 1, "set O2 2 message in log"); + fhem ('attr Master verbose 3'); + return 0.1; +} + + +sub testStep10 { # check combined read of holding registers and coils + LogStep "getUpdate with combine"; + FhemTestUtils_resetEvents(); + fhem ('set Master reread'); + return 0.1; +} + +sub testStep11 { # check results coming from slave and write coils to slave + is(FhemTestUtils_gotEvent(qr/Master:Test1: 6/), 1, "Combined retrieve integer value with expressions on both sides from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:Test2: 2.12/), 1, "Combined retrieve float value from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:Test3: abcdefg/), 1, "Combined Retrieve ascii value from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:c0: 1/), 1, "Combined Retrieve coil bit 0 from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:c1: 0/), 1, "Combined Retrieve coil bit 1 from local slave"); + is(FhemTestUtils_gotEvent(qr/Master:c17: 1/), 1, "Combined Retrieve coil bit 17 from local slave"); + + fhem ('attr Slave obj-c402-allowWrite 1'); + fhem ('attr Master verbose 5'); + fhem ('set Master c2 1'); + return 0.1; +} + +sub testStep12 { + LogStep "check coil comm"; + + is(FhemTestUtils_gotLog('sending 05050192ff002daf'), 1, "set c2 1 sending message in log"); + is(FhemTestUtils_gotEvent(qr/Master:c2: 1/), 1, "fc5 response for coil shows 1 from local slave"); + + Log3 undef, 1, "TestStep12: try to write with fc16"; + fhem ('attr Master verbose 3'); + fhem ('attr Slave verbose 3'); + fhem ('attr Master dev-h-write 16'); + fhem ('set Master TempWasserAus 29'); + return 0.1; +} + +sub testStep13 { + LogStep "check write result of fc16"; + is(FhemTestUtils_gotEvent(qr/D1:TempWasserAus:\s29/xms), 1, "Write value with fc16 to local slave"); + return 0.1; +} + +sub testStep14 { + LogStep "closeAfterResponse"; + FhemTestUtils_resetEvents(); + FhemTestUtils_resetLogs(); + fhem ('attr Master closeAfterResponse 1'); + fhem ('attr Master verbose 4'); + fhem ('set Master reread'); + return 0.1; +} + +sub testStep15 { + is(FhemTestUtils_gotEvent(qr/Master:Test1: 6/), 1, "Retrieve Test1"); + is(FhemTestUtils_gotEvent(qr/Master:Test4: 40/), 0, "Retrieve Test4"); + is(FhemTestUtils_gotLog('HandleResponse will close because closeAfterResponse is set and queue is empty'), 1, "closed"); + return 0.1; +} + +sub testStep16 { + LogStep "try get while closed"; + FhemTestUtils_resetEvents(); + FhemTestUtils_resetLogs(); + fhem ('get Master TempWasserEin'); + fhem ('attr Master queueDelay 0.3'); + return 0.1; +} + +sub testStep17 { + LogStep "check get result while connection closed"; + is(FhemTestUtils_gotLog('device opened'), 1, "device opened"); + is(FhemTestUtils_gotEvent(qr/Master:TempWasserEin:\s12/xms), 0, "No retrieve from local slave yet"); + return 0.3; +} + +sub testStep18 { + LogStep "check get result after another delay"; + is(FhemTestUtils_gotEvent(qr/Master:TempWasserEin:\s12/xms), 1, "retrieve from local slave after open and QueueDelay"); + is(FhemTestUtils_gotLog('close because closeAfterResponse'), 1, "device closed again"); + return 0.1; +} + + +sub testStep19 { + LogStep "now that the connection is closed again, try another prioritized get"; + FhemTestUtils_resetEvents(); + FhemTestUtils_resetLogs(); + fhem ('attr Master nonPrioritizedGet 0'); + fhem ('attr Master dev-timing-timeout 0.5'); + fhem ('attr Master verbose 5'); + fhem ('get Master TempWasserEin'); + return 0.1; +} + +sub testStep20 { + LogStep "check result after prio get"; + is(FhemTestUtils_gotLog('device opened'), 1, "device opened"); + is(FhemTestUtils_gotLog('Master: Timeout in Readanswer'), 1, "readanswer called but slave cannot answer while sitting in readanswer"); + return; +} + +sub testStep21 { + LogStep "check result after prio get"; + is(FhemTestUtils_gotLog('Master: read buffer: 050302000c4981'), 1, "answer arrives after readanswer timeout"); + return; +} + + +# todo: different protocols (RTU, ASCII, TCP) +# data type definition +# swap bytes and similar +# unpack variations + +# then relay mode + + +1; diff --git a/fhem/t/FHEM/98_Modbus/58_Profiler.cfg b/fhem/t/FHEM/98_Modbus/58_Profiler.cfg new file mode 100644 index 000000000..25529ba35 --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/58_Profiler.cfg @@ -0,0 +1,128 @@ +attr global mseclog 1 + +define D1 dummy + +define Slave ModbusAttr 5 slave global:5501 +attr Slave obj-h256-reading TempWasserEin +attr Slave obj-h258-reading D1:TempWasserAus + +attr Slave obj-h100-reading Test1 +attr Slave obj-h100-setexpr $val * 4 + +attr Slave obj-h101-reading Test2 +attr Slave obj-h101-unpack f> +attr Slave obj-h101-len 2 + +attr Slave obj-h103-reading Test3 +attr Slave obj-h103-unpack a8 +attr Slave obj-h103-len 4 + +attr Slave obj-h120-reading Test4 +attr Slave obj-h120-unpack f> +attr Slave obj-h120-len 2 + +attr Slave obj-h130-reading Test5 +attr Slave obj-h130-unpack a* +attr Slave obj-h130-len 2 + +attr Slave obj-c400-reading c0 +attr Slave obj-c401-reading c1 +attr Slave obj-c402-reading c2 +attr Slave obj-c403-reading c3 +attr Slave obj-c404-reading c4 +attr Slave obj-c405-reading c5 +attr Slave obj-c406-reading c6 +attr Slave obj-c407-reading c7 +attr Slave obj-c408-reading c8 +attr Slave obj-c409-reading c9 +attr Slave obj-c410-reading c10 +attr Slave obj-c411-reading c11 +attr Slave obj-c412-reading c12 +attr Slave obj-c413-reading c13 +attr Slave obj-c414-reading c14 +attr Slave obj-c415-reading c15 +attr Slave obj-c416-reading c16 +attr Slave obj-c417-reading c17 +attr Slave obj-c418-reading c18 + +define Master ModbusAttr 5 0 localhost:5501 +attr Master disable 1 +attr Master verbose 3 +attr Master nonPrioritizedGet 1 +attr Master nonPrioritizedSet 1 +attr Master profileInterval 1 + +attr Master dev-timing-sendDelay 0 +attr Master dev-timing-commDelay 0 + +attr Master obj-h256-reading TempWasserEin +attr Master obj-h258-reading TempWasserAus + +attr Master obj-h100-reading Test1 +attr Master obj-h100-expr $val + 2 +attr Master obj-h100-poll 1 +attr Master obj-h100-polldelay 0 + +attr Master obj-h101-reading Test2 +attr Master obj-h101-unpack f> +attr Master obj-h101-len 2 +attr Master obj-h101-format %.2f +attr Master obj-h101-poll 1 +attr Master obj-h101-polldelay 0 + +attr Master obj-h103-reading Test3 +attr Master obj-h103-unpack a8 +attr Master obj-h103-len 4 +attr Master obj-h103-poll 1 +attr Master obj-h103-polldelay 0 + +attr Master obj-h120-reading Test4 +attr Master obj-h120-unpack f> +attr Master obj-h120-len 2 +attr Master obj-h120-format %.2f +attr Master obj-h120-poll 1 +attr Master obj-h120-ignoreExpr $val > 10 +attr Master obj-h120-polldelay 0 + +attr Master obj-h130-reading Test5 +attr Master obj-h130-unpack a* +attr Master obj-h130-len 2 +attr Master obj-h130-encode utf8 + +attr Master obj-h10-reading o1 +attr Master obj-h10-map 0:off, 1:on + +attr Master obj-h11-reading o2 +attr Master obj-h11-min 1 +attr Master obj-h11-max 3 +attr Master dev-h-defSet 1 +attr Master dev-c-defSet 1 +attr Master dev-h-defShowGet 1 + +attr Master obj-c400-reading c0 +attr Master obj-c401-reading c1 +attr Master obj-c402-reading c2 +attr Master obj-c403-reading c3 +attr Master obj-c404-reading c4 +attr Master obj-c405-reading c5 +attr Master obj-c406-reading c6 +attr Master obj-c407-reading c7 +attr Master obj-c408-reading c8 +attr Master obj-c409-reading c9 +attr Master obj-c410-reading c10 +attr Master obj-c411-reading c11 +attr Master obj-c412-reading c12 +attr Master obj-c413-reading c13 +attr Master obj-c414-reading c14 +attr Master obj-c415-reading c15 +attr Master obj-c416-reading c16 +attr Master obj-c417-reading c17 +attr Master obj-c418-reading c18 + +attr Master obj-c400-poll 1 +attr Master obj-c405-poll 1 +attr Master obj-c406-poll 1 +attr Master obj-c417-poll 1 + +attr Master dev-h-combine 19 +attr Master dev-c-combine 32 \ No newline at end of file diff --git a/fhem/t/FHEM/98_Modbus/58_Profiler.t b/fhem/t/FHEM/98_Modbus/58_Profiler.t new file mode 100644 index 000000000..2cb4a1907 --- /dev/null +++ b/fhem/t/FHEM/98_Modbus/58_Profiler.t @@ -0,0 +1,61 @@ +############################################## +# test master slave end to end +############################################## + +package main; + +use strict; +use warnings; +use Test::More; +use Time::HiRes qw( gettimeofday tv_interval); # return time as float, not just full seconds +use FHEM::HTTPMOD::Utils qw(:all); +use FHEM::Modbus::TestUtils qw(:all); + +fhem 'attr global mseclog 1'; +InternalTimer(gettimeofday()+5, "testStepLast", 0); # last resort +NextStep(); + +sub testStep1 { # preparation of slave content, enable devices + is(FhemTestUtils_gotLog('attribute'), 0, "no unknown attributes"); # logs during init are not collected. + LogStep "enable Master and set value at Slave"; + fhem ('attr Master disable 0'); + fhem ('setreading Slave TempWasserEin 12'); + fhem ('setreading Slave Test1 1'); + fhem ('setreading Slave Test2 2.123'); + fhem ('setreading Slave Test3 abcdefg'); + fhem ('setreading Slave Test4 40'); + readingsSingleUpdate($defs{'Slave'}, 'Test5', pack('H*', 'e4f6fc'), 0); + + fhem ('setreading Slave c0 1'); + fhem ('setreading Slave c5 1'); + fhem ('setreading Slave c17 1'); + return 0.1; +} + +sub testStep2 { # get holding registers + LogStep "get TempWasserEin"; + fhem ('attr Master verbose 5'); + fhem ('attr Slave verbose 5'); + fhem ('get Master TempWasserEin'); + fhem ('get Master Test1'); + fhem ('get Master Test2'); + fhem ('get Master Test3'); + fhem ('get Master Test4'); + fhem ('get Master Test5'); + return 0.3; +} + +sub testStep3 { + fhem 'set Master reread'; + return 0.3; +} + +sub testStep4 { + fhem 'set Master reread'; + return 0.3; +} + + + + +1;