WMBus: support frame type A with more than one block

git-svn-id: https://svn.fhem.de/fhem/trunk@30375 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
kaihs
2025-10-11 16:14:35 +00:00
parent 84e2196613
commit deddeeb8a3
2 changed files with 44 additions and 118 deletions

View File

@@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it
- bugfix: WMBus: support for frame type B with more than one block
- bugfix: 76_SolarForecast: V 1.59.3, minor fix in bat graphic
- change: 76_SolarForecast: minor fixes, optimal SoC consideration in SoC-FC
- change: 76_SolarForecast: adapt for new Chromium engine

View File

@@ -2318,36 +2318,57 @@ sub decodeLinkLayer($$)
return 0;
}
# first block
my $length = $self->{lfield} + 1; # 1 for lfield itself
my $length = 129;
if ($self->{lfield} < $length) {
$length = $self->{lfield};
}
if ($self->{crc_size} > 0) {
$length -= $self->{crc_size};
$length++; # for L field
#print "length: $length\n";
$self->{crc0} = unpack('n', substr($self->{msg}, $length, $self->{crc_size}));
my $lengthOfFirstBlock = $length;
my $crcoffset = $length - $self->{crc_size};
#print "length: $length crcoffset $crcoffset\n";
#printf "crc in msg %x crc calculated %x\n", $self->{crc0}, $self->calcCRC(substr($self->{msg}, 0, $length));
if ($self->{crc0} != $self->calcCRC(substr($self->{msg}, 0, $length))) {
my $maxLengthOfFirstBlock = TL_BLOCK_SIZE + 1 + 115 + $self->{crc_size};
if ($length > $maxLengthOfFirstBlock) {
$crcoffset = $maxLengthOfFirstBlock - $self->{crc_size};
$lengthOfFirstBlock = $maxLengthOfFirstBlock;
} else {
$lengthOfFirstBlock = $self->{lfield};
}
#print "length first block: $lengthOfFirstBlock crcoffset first block $crcoffset\n";
$self->{crc0} = unpack('n', substr($self->{msg}, $crcoffset, $self->{crc_size}));
#printf "crc in msg %x crc calculated %x\n", $self->{crc0}, $self->calcCRC(substr($self->{msg}, 0, $crcoffset));
if ($self->{crc0} != $self->calcCRC(substr($self->{msg}, 0, $crcoffset))) {
$self->{errormsg} = "CRC check failed on block 1";
$self->{errorcode} = ERR_CRC_FAILED;
return 0;
}
$self->{msglen} = $self->{lfield} + 1 - $self->{crc_size};
$self->{applicationlayer} = substr($self->{msg}, TL_BLOCK_SIZE, $length - TL_BLOCK_SIZE - $self->{crc_size});
if ($length > 128) {
#print "block 2\n";
$crcoffset = $length - $self->{crc_size};
$length -= 128;
#print "length second block: $length crcoffset second block $crcoffset\n";
$self->{crc0} = unpack('n', substr($self->{msg}, $crcoffset, $self->{crc_size}));
#printf "crc in msg %x crc calculated %x\n", $self->{crc0}, $self->calcCRC(substr($self->{msg}, 128, $length-2));
if ($self->{crc0} != $self->calcCRC(substr($self->{msg}, 128, $length - $self->{crc_size}))) {
$self->{errormsg} = "CRC check failed on block 2";
$self->{errorcode} = ERR_CRC_FAILED;
return 0;
}
$self->{datablocks} = int($self->{lfield} / 129);
$self->{datablocks}++ if $self->{lfield} % 129 != 0;
# header block is 10 bytes, following block
$self->{datalen} = $self->{lfield} - (TL_BLOCK_SIZE - 1) - ($self->{datablocks} * $self->{crc_size}) ; # this is with CRCs but without the lfield itself
$self->{msglen} = $self->{lfield};
if ($self->{datablocks} == 2) {
# TODO
$self->{applicationlayer} .= substr($self->{msg}, 128, $length - $self->{crc_size} );
}
} else {
$self->{applicationlayer} = substr($self->{msg}, TL_BLOCK_SIZE, $length - TL_BLOCK_SIZE); # - $self->{crc_size});
# no CRC
$self->{applicationlayer} = substr($self->{msg}, TL_BLOCK_SIZE);
}
}
if (length($self->{msg}) > $self->{msglen}) {
@@ -2363,102 +2384,6 @@ sub decodeLinkLayer($$)
return 1;
}
sub encodeLinkLayer($)
{
my $self = shift;
my $linklayer = pack('CCv', $self->{lfield}, $self->{cfield}, $self->{mfield});
($self->{lfield}, $self->{cfield}, $self->{mfield}) = unpack('CCv', $linklayer);
$self->{afield} = substr($linklayer,4,6);
$self->{afield_id} = sprintf("%08d", $self->decodeBCD(8,substr($linklayer,4,4)));
($self->{afield_ver}, $self->{afield_type}) = unpack('CC', substr($linklayer,8,2));
#printf("lfield %d\n", $self->{lfield});
if ($self->{frame_type} eq FRAME_TYPE_A) {
if ($self->{crc_size} > 0) {
$self->{crc0} = unpack('n', substr($linklayer,TL_BLOCK_SIZE, $self->{crc_size}));
#printf("crc0 %x calc %x\n", $self->{crc0}, $self->calcCRC(substr($linklayer,0,10)));
if ($self->{crc0} != $self->calcCRC(substr($linklayer,0,TL_BLOCK_SIZE))) {
$self->{errormsg} = "CRC check failed on link layer";
$self->{errorcode} = ERR_CRC_FAILED;
#print "CRC check failed on link layer\n";
return 0;
}
}
# header block is 10 bytes + 2 bytes CRC, each following block is 16 bytes + 2 bytes CRC, the last block may be smaller
$self->{datalen} = $self->{lfield} - (TL_BLOCK_SIZE - 1); # this is without CRCs and the lfield itself
$self->{datablocks} = int($self->{datalen} / LL_BLOCK_SIZE);
$self->{datablocks}++ if $self->{datalen} % LL_BLOCK_SIZE != 0;
$self->{msglen} = TL_BLOCK_SIZE + $self->{crc_size} + $self->{datalen} + $self->{datablocks} * $self->{crc_size};
#printf("calc len %d, actual %d\n", $self->{msglen}, length($self->{msg}));
$self->{applicationlayer} = $self->removeCRC(substr($self->{msg},TL_BLOCK_SIZE + $self->{crc_size}));
} else {
# FRAME TYPE B
# each block is at most 129 bytes long.
# first contains the header (TL_BLOCK), L field and trailing crc
# L field is included in crc calculation
# each following block contains only data and trailing crc
if (length($self->{msg}) < $self->{lfield}) {
$self->{errormsg} = "message too short, expected " . $self->{lfield} . ", got " . length($self->{msg}) . " bytes";
$self->{errorcode} = ERR_MSG_TOO_SHORT;
return 0;
}
my $length = 129;
if ($self->{lfield} < $length) {
$length = $self->{lfield};
}
if ($self->{crc_size} > 0) {
$length -= $self->{crc_size};
$length++; # for L field
#print "length: $length\n";
$self->{crc0} = unpack('n', substr($self->{msg}, $length, $self->{crc_size}));
#printf "crc in msg %x crc calculated %x\n", $self->{crc0}, $self->calcCRC(substr($self->{msg}, 0, $length));
if ($self->{crc0} != $self->calcCRC(substr($self->{msg}, 0, $length))) {
$self->{errormsg} = "CRC check failed on block 1";
$self->{errorcode} = ERR_CRC_FAILED;
return 0;
}
}
$self->{datablocks} = int($self->{lfield} / 129);
$self->{datablocks}++ if $self->{lfield} % 129 != 0;
# header block is 10 bytes, following block
$self->{datalen} = $self->{lfield} - (TL_BLOCK_SIZE - 1) - ($self->{datablocks} * $self->{crc_size}) ; # this is with CRCs but without the lfield itself
$self->{msglen} = $self->{lfield};
if ($self->{datablocks} == 2) {
# TODO
} else {
$self->{applicationlayer} = substr($self->{msg}, TL_BLOCK_SIZE, $length - TL_BLOCK_SIZE); # - $self->{crc_size});
}
}
if (length($self->{msg}) > $self->{msglen}) {
$self->{remainingData} = substr($self->{msg},$self->{msglen});
} elsif (length($self->{msg}) < $self->{msglen}) {
$self->{errormsg} = "message too short, expected " . $self->{msglen} . ", got " . length($self->{msg}) . " bytes";
$self->{errorcode} = ERR_MSG_TOO_SHORT;
return 0;
}
# according to the MBus spec only upper case letters are allowed.
# some devices send lower case letters none the less
# convert to upper case to make them spec conformant
$self->{manufacturer} = uc($self->manId2ascii($self->{mfield}));
$self->{typestring} = $validDeviceTypes{$self->{afield_type}} || 'unknown';
return 1;
}
sub setFrameType($$)
{