2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-08 19:32:42 +00:00

Adapted to DevIo

git-svn-id: https://svn.fhem.de/fhem/trunk@1013 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
rudolfkoenig 2011-09-23 08:50:34 +00:00
parent 71ea4d044e
commit ad9285cd3d
4 changed files with 63 additions and 681 deletions

View File

@ -15,11 +15,7 @@ sub CUL_ReadAnswer($$$$);
sub CUL_Ready($);
sub CUL_Write($$$);
sub CUL_OpenDev($$);
sub CUL_CloseDev($);
sub CUL_SimpleWrite(@);
sub CUL_SimpleRead($);
sub CUL_Disconnected($);
my %gets = ( # Name, Data to send to the CUL, Regexp for the answer
"ccconf" => 1,
@ -79,6 +75,8 @@ CUL_Initialize($)
{
my ($hash) = @_;
require "$attr{global}{modpath}/FHEM/DevIo.pm";
# Provider
$hash->{ReadFn} = "CUL_Read";
$hash->{WriteFn} = "CUL_Write";
@ -96,6 +94,7 @@ CUL_Initialize($)
"fhtsoftbuffer:1,0 sendpool addvaltrigger " .
"rfmode:SlowRF,HomeMatic hmId hmProtocolEvents";
$hash->{ShutdownFn} = "CUL_Shutdown";
}
#####################################
@ -112,7 +111,7 @@ CUL_Define($$)
return $msg;
}
CUL_CloseDev($hash);
DevIo_CloseDev($hash);
my $name = $a[0];
my $dev = $a[2];
@ -145,7 +144,7 @@ CUL_Define($$)
}
$hash->{DeviceName} = $dev;
my $ret = CUL_OpenDev($hash, 0);
my $ret = DevIo_OpenDev($hash, 0, "CUL_DoInit");
return $ret;
}
@ -169,7 +168,7 @@ CUL_Undef($$)
}
CUL_SimpleWrite($hash, "X00"); # Switch reception off, it may hang up the CUL
CUL_CloseDev($hash);
DevIo_CloseDev($hash);
return undef;
}
@ -450,7 +449,7 @@ READEND:
CUL_SimpleWrite($hash, $gets{$a[1]}[0] . $arg);
($err, $msg) = CUL_ReadAnswer($hash, $a[1], 0, $gets{$a[1]}[1]);
if(!defined($msg)) {
CUL_Disconnected($hash);
DevIo_Disconnected($hash);
$msg = "No answer";
} elsif($a[1] eq "uptime") { # decode it
@ -540,7 +539,7 @@ CUL_DoInit($)
CUL_SimpleWrite($hash, "T01" . $hash->{FHTID});
}
$hash->{STATE} = "Initialized" if(!$hash->{STATE});
$hash->{STATE} = "Initialized";
# Reset the counter
delete($hash->{XMIT_TIME});
@ -586,12 +585,12 @@ CUL_ReadAnswer($$$$)
if($nfound < 0) {
next if ($! == EAGAIN() || $! == EINTR() || $! == 0);
my $err = $!;
CUL_Disconnected($hash);
DevIo_Disconnected($hash);
return("CUL_ReadAnswer $arg: $err", undef);
}
return ("Timeout reading answer for get $arg", undef)
if($nfound == 0);
$buf = CUL_SimpleRead($hash);
$buf = DevIo_SimpleRead($hash);
return ("No data", undef) if(!defined($buf));
}
@ -784,20 +783,10 @@ CUL_Read($)
{
my ($hash) = @_;
my $buf = CUL_SimpleRead($hash);
my $buf = DevIo_SimpleRead($hash);
return "" if(!defined($buf));
my $name = $hash->{NAME};
###########
# Lets' try again: Some drivers return len(0) on the first read...
if(defined($buf) && length($buf) == 0) {
$buf = CUL_SimpleRead($hash);
}
if(!defined($buf) || length($buf) == 0) {
CUL_Disconnected($hash);
return "";
}
my $culdata = $hash->{PARTIAL};
Log 5, "CUL/RAW: $culdata/$buf";
$culdata .= $buf;
@ -842,8 +831,7 @@ CUL_Parse($$$$$)
my $len = length($dmsg);
if($fn eq "F" && $len >= 9) { # Reformat for 10_FS20.pm
CUL_AddFS20Queue($iohash, ""); # Block immediate replies
CUL_AddFS20Queue($iohash, ""); # Delay immediate replies
$dmsg = sprintf("81%02x04xx0101a001%s00%s",
$len/2+7, substr($dmsg,1,6), substr($dmsg,7));
$dmsg = lc($dmsg);
@ -922,7 +910,7 @@ CUL_Ready($)
{
my ($hash) = @_;
return CUL_OpenDev($hash, 1)
return DevIo_OpenDev($hash, 1, "CUL_DoInit")
if($hash->{STATE} eq "disconnected");
# This is relevant for windows/USB only
@ -956,221 +944,6 @@ CUL_SimpleWrite(@)
select(undef, undef, undef, 0.001);
}
########################
sub
CUL_SimpleRead($)
{
my ($hash) = @_;
my ($buf, $res);
if($hash->{USBDev}) {
$buf = $hash->{USBDev}->input();
} elsif($hash->{DIODev}) {
$res = sysread($hash->{DIODev}, $buf, 256);
$buf = undef if(!defined($res));
} elsif($hash->{TCPDev}) {
$res = sysread($hash->{TCPDev}, $buf, 256);
$buf = undef if(!defined($res));
}
return $buf;
}
########################
sub
CUL_CloseDev($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
my $dev = $hash->{DeviceName};
return if(!$dev);
if($hash->{TCPDev}) {
$hash->{TCPDev}->close();
delete($hash->{TCPDev});
} elsif($hash->{USBDev}) {
$hash->{USBDev}->close() ;
delete($hash->{USBDev});
} elsif($hash->{DIODev}) {
close($hash->{DIODev});
delete($hash->{DIODev});
}
($dev, undef) = split("@", $dev); # Remove the baudrate
delete($selectlist{"$name.$dev"});
delete($readyfnlist{"$name.$dev"});
delete($hash->{FD});
}
########################
sub
CUL_OpenDev($$)
{
my ($hash, $reopen) = @_;
my $dev = $hash->{DeviceName};
my $name = $hash->{NAME};
my $po;
my $baudrate;
($dev, $baudrate) = split("@", $dev);
$hash->{PARTIAL} = "";
Log 3, "CUL opening $name device $dev"
if(!$reopen);
if($dev =~ m/^(.+):([0-9]+)$/) { # host:port
# This part is called every time the timeout (5sec) is expired _OR_
# somebody is communicating over another TCP connection. As the connect
# for non-existent devices has a delay of 3 sec, we are sitting all the
# time in this connect. NEXT_OPEN tries to avoid this problem.
if($hash->{NEXT_OPEN} && time() < $hash->{NEXT_OPEN}) {
return;
}
my $conn = IO::Socket::INET->new(PeerAddr => $dev);
if($conn) {
delete($hash->{NEXT_OPEN})
} else {
Log(3, "Can't connect to $dev: $!") if(!$reopen);
$readyfnlist{"$name.$dev"} = $hash;
$hash->{STATE} = "disconnected";
$hash->{NEXT_OPEN} = time()+60;
return "";
}
$hash->{TCPDev} = $conn;
$hash->{FD} = $conn->fileno();
delete($readyfnlist{"$name.$dev"});
$selectlist{"$name.$dev"} = $hash;
} elsif($baudrate && lc($baudrate) eq "directio") { # Without Device::SerialPort
if(!open($po, "+<$dev")) {
return undef if($reopen);
Log(3, "Can't open $dev: $!");
$readyfnlist{"$name.$dev"} = $hash;
$hash->{STATE} = "disconnected";
return "";
}
$hash->{DIODev} = $po;
if( $^O =~ /Win/ ) {
$readyfnlist{"$name.$dev"} = $hash;
} else {
$hash->{FD} = fileno($po);
delete($readyfnlist{"$name.$dev"});
$selectlist{"$name.$dev"} = $hash;
}
} else { # USB/Serial device
if ($^O=~/Win/) {
require Win32::SerialPort;
$po = new Win32::SerialPort ($dev);
} else {
require Device::SerialPort;
$po = new Device::SerialPort ($dev);
}
if(!$po) {
return undef if($reopen);
Log(3, "Can't open $dev: $!");
$readyfnlist{"$name.$dev"} = $hash;
$hash->{STATE} = "disconnected";
return "";
}
$hash->{USBDev} = $po;
if( $^O =~ /Win/ ) {
$readyfnlist{"$name.$dev"} = $hash;
} else {
$hash->{FD} = $po->FILENO;
delete($readyfnlist{"$name.$dev"});
$selectlist{"$name.$dev"} = $hash;
}
if($baudrate) {
$po->reset_error();
Log 3, "CUL setting $name baudrate to $baudrate";
$po->baudrate($baudrate);
$po->databits(8);
$po->parity('none');
$po->stopbits(1);
$po->handshake('none');
# This part is for some Linux kernel versions whih has strange default
# settings. Device::SerialPort is nice: if the flag is not defined for your
# OS then it will be ignored.
$po->stty_icanon(0);
#$po->stty_parmrk(0); # The debian standard install does not have it
$po->stty_icrnl(0);
$po->stty_echoe(0);
$po->stty_echok(0);
$po->stty_echoctl(0);
# Needed for some strange distros
$po->stty_echo(0);
$po->stty_icanon(0);
$po->stty_isig(0);
$po->stty_opost(0);
$po->stty_icrnl(0);
}
$po->write_settings;
}
if($reopen) {
Log 1, "CUL $dev reappeared ($name)";
} else {
Log 3, "CUL device opened";
}
$hash->{STATE}=""; # Allow InitDev to set the state
my $ret = CUL_DoInit($hash);
if($ret) {
CUL_CloseDev($hash);
Log 1, "Cannot init $dev, ignoring it";
}
DoTrigger($name, "CONNECTED") if($reopen);
return $ret;
}
sub
CUL_Disconnected($)
{
my $hash = shift;
my $dev = $hash->{DeviceName};
my $name = $hash->{NAME};
my $baudrate;
($dev, $baudrate) = split("@", $dev);
return if(!defined($hash->{FD})); # Already deleted or RFR
Log 1, "$dev disconnected, waiting to reappear";
CUL_CloseDev($hash);
$readyfnlist{"$name.$dev"} = $hash; # Start polling
$hash->{STATE} = "disconnected";
# Without the following sleep the open of the device causes a SIGSEGV,
# and following opens block infinitely. Only a reboot helps.
sleep(5);
DoTrigger($name, "DISCONNECTED");
}
sub
CUL_Attr(@)
{

View File

@ -11,11 +11,7 @@ sub HMLAN_Write($$$);
sub HMLAN_ReadAnswer($$$);
sub HMLAN_uptime($);
sub HMLAN_OpenDev($$);
sub HMLAN_CloseDev($);
sub HMLAN_SimpleWrite(@);
sub HMLAN_SimpleRead($);
sub HMLAN_Disconnected($);
my %sets = (
"hmPairForSec" => "HomeMatic",
@ -27,6 +23,8 @@ HMLAN_Initialize($)
{
my ($hash) = @_;
require "$attr{global}{modpath}/FHEM/DevIo.pm";
# Provider
$hash->{ReadFn} = "HMLAN_Read";
$hash->{WriteFn} = "HMLAN_Write";
@ -58,11 +56,11 @@ HMLAN_Define($$)
Log 2, $msg;
return $msg;
}
HMLAN_CloseDev($hash);
DevIo_CloseDev($hash);
my $name = $a[0];
my $dev = $a[2];
$dev .= ":1000" if($dev !~ m/:/);
$dev .= ":1000" if($dev !~ m/:/ && $dev ne "none" && $dev !~ m/\@/);
$attr{$name}{hmId} = sprintf("%06X", time() % 0xffffff); # Will be overwritten
if($dev eq "none") {
@ -71,7 +69,7 @@ HMLAN_Define($$)
return undef;
}
$hash->{DeviceName} = $dev;
my $ret = HMLAN_OpenDev($hash, 0);
my $ret = DevIo_OpenDev($hash, 0, "HMLAN_DoInit");
return $ret;
}
@ -94,7 +92,7 @@ HMLAN_Undef($$)
}
}
HMLAN_CloseDev($hash);
DevIo_CloseDev($hash);
return undef;
}
@ -168,12 +166,12 @@ HMLAN_ReadAnswer($$$)
if($nfound < 0) {
next if ($! == EAGAIN() || $! == EINTR() || $! == 0);
my $err = $!;
HMLAN_Disconnected($hash);
DevIo_Disconnected($hash);
return("HMLAN_ReadAnswer $arg: $err", undef);
}
return ("Timeout reading answer for get $arg", undef)
if($nfound == 0);
$buf = HMLAN_SimpleRead($hash);
$buf = DevIo_SimpleRead($hash);
return ("No data", undef) if(!defined($buf));
if($buf) {
@ -223,14 +221,10 @@ HMLAN_Read($)
{
my ($hash) = @_;
my $buf = HMLAN_SimpleRead($hash);
my $buf = DevIo_SimpleRead($hash);
return "" if(!defined($buf));
my $name = $hash->{NAME};
if(!defined($buf) || length($buf) == 0) {
HMLAN_Disconnected($hash);
return "";
}
my $hmdata = $hash->{PARTIAL};
Log 5, "HMLAN/RAW: $hmdata/$buf";
$hmdata .= $buf;
@ -325,7 +319,7 @@ HMLAN_Ready($)
{
my ($hash) = @_;
return HMLAN_OpenDev($hash, 1);
return DevIo_OpenDev($hash, 1, "HMLAN_DoInit");
}
########################
@ -345,88 +339,10 @@ HMLAN_SimpleWrite(@)
########################
sub
HMLAN_SimpleRead($)
{
my ($hash) = @_;
if($hash->{TCPDev}) {
my $buf;
if(!defined(sysread($hash->{TCPDev}, $buf, 256))) {
HMLAN_Disconnected($hash);
return undef;
}
return $buf;
}
return undef;
}
########################
sub
HMLAN_CloseDev($)
HMLAN_DoInit($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
my $dev = $hash->{DeviceName};
return if(!$dev);
if($hash->{TCPDev}) {
$hash->{TCPDev}->close();
delete($hash->{TCPDev});
}
delete($selectlist{"$name.$dev"});
delete($readyfnlist{"$name.$dev"});
delete($hash->{FD});
}
########################
sub
HMLAN_OpenDev($$)
{
my ($hash, $reopen) = @_;
my $dev = $hash->{DeviceName};
my $name = $hash->{NAME};
$hash->{PARTIAL} = "";
Log 3, "HMLAN opening $name device $dev"
if(!$reopen);
if($dev =~ m/^.+:\d+$/) { # host:port
# This part is called every time the timeout (5sec) is expired _OR_
# somebody is communicating over another TCP connection. As the connect
# for non-existent devices has a delay of 3 sec, we are sitting all the
# time in this connect. NEXT_OPEN tries to avoid this problem.
if($hash->{NEXT_OPEN} && time() < $hash->{NEXT_OPEN}) {
return;
}
my $conn = IO::Socket::INET->new(PeerAddr => $dev);
if($conn) {
delete($hash->{NEXT_OPEN})
} else {
Log(3, "Can't connect to $dev: $!") if(!$reopen);
$readyfnlist{"$name.$dev"} = $hash;
$hash->{STATE} = "disconnected";
$hash->{NEXT_OPEN} = time()+60;
return "";
}
$hash->{TCPDev} = $conn;
$hash->{FD} = $conn->fileno();
delete($readyfnlist{"$name.$dev"});
$selectlist{"$name.$dev"} = $hash;
InternalTimer(gettimeofday()+25, "HMLAN_KeepAlive", $hash, 0);
}
if($reopen) {
Log 1, "HMLAN $dev reappeared ($name)";
} else {
Log 3, "HMLAN device opened";
}
my $id = AttrVal($name, "hmId", undef);
my $key = AttrVal($name, "hmKey", ""); # 36(!) hex digits
@ -440,10 +356,8 @@ HMLAN_OpenDev($$)
HMLAN_SimpleWrite($hash, "Y03,00,");
HMLAN_SimpleWrite($hash, "T$s2000,04,00,00000000");
$hash->{STATE}="Initialized";
DoTrigger($name, "CONNECTED") if($reopen);
return "";
InternalTimer(gettimeofday()+25, "HMLAN_KeepAlive", $hash, 0);
return undef;
}
#####################################
@ -456,27 +370,4 @@ HMLAN_KeepAlive($)
InternalTimer(gettimeofday()+25, "HMLAN_KeepAlive", $hash, 1);
}
sub
HMLAN_Disconnected($)
{
my $hash = shift;
my $dev = $hash->{DeviceName};
my $name = $hash->{NAME};
return if(!defined($hash->{FD})); # Already deleted or RFR
Log 1, "$dev disconnected, waiting to reappear";
RemoveInternalTimer($hash);
HMLAN_CloseDev($hash);
$readyfnlist{"$name.$dev"} = $hash; # Start polling
$hash->{STATE} = "disconnected";
# Without the following sleep the open of the device causes a SIGSEGV,
# and following opens block infinitely. Only a reboot helps.
sleep(5);
DoTrigger($name, "DISCONNECTED");
}
1;

View File

@ -13,10 +13,6 @@ use Time::HiRes qw(gettimeofday);
sub KM271_Read($);
sub KM271_Ready($);
sub KM271_OpenDev($);
sub KM271_CloseDev($);
sub KM271_SimpleWrite(@);
sub KM271_SimpleRead($);
sub KM271_crc($);
sub KM271_setbits($$);
sub KM271_GetReading($$);
@ -180,6 +176,8 @@ KM271_Initialize($)
{
my ($hash) = @_;
require "$attr{global}{modpath}/FHEM/DevIo.pm";
$hash->{ReadFn} = "KM271_Read";
$hash->{ReadyFn} = "KM271_Ready";
@ -208,7 +206,7 @@ KM271_Define($$)
return "wrong syntax: define <name> KM271 [devicename|none]"
if(@a != 3);
KM271_CloseDev($hash);
DevIo_CloseDev($hash);
my $name = $a[0];
my $dev = $a[2];
@ -218,7 +216,7 @@ KM271_Define($$)
}
$hash->{DeviceName} = $dev;
my $ret = KM271_OpenDev($hash);
my $ret = DevIo_OpenDev($hash, 0, "KM271_DoInit");
return $ret;
}
@ -228,7 +226,7 @@ sub
KM271_Undef($$)
{
my ($hash, $arg) = @_;
KM271_CloseDev($hash);
DevIo_CloseDev($hash);
return undef;
}
@ -269,7 +267,7 @@ KM271_Set($@)
my $data = ($val ? sprintf($fmt, $val) : $fmt);
push @{$hash->{SENDBUFFER}}, $data;
KM271_SimpleWrite($hash, "02") if(!$hash->{WAITING});
DevIo_SimpleWrite($hash, "02") if(!$hash->{WAITING});
return undef;
}
@ -284,45 +282,40 @@ KM271_Read($)
my $name = $hash->{NAME};
my ($data, $crc);
my $buf = KM271_SimpleRead($hash);
Log 5, "KM271RAW: " . unpack('H*', $buf);
if(!defined($buf)) {
Log 1, "$name: EOF";
KM271_CloseDev($hash);
return;
}
my $buf = DevIo_SimpleRead($hash);
return "" if(!defined($buf));
$buf = unpack('H*', $buf);
Log 5, "KM271RAW: $buf";
if(@{$hash->{SENDBUFFER}} || $hash->{DATASENT}) { # Send data
if($buf eq "02") { # KM271 Wants to send, override
KM271_SimpleWrite($hash, "02");
DevIo_SimpleWrite($hash, "02");
return;
}
if($buf eq "10") {
if($hash->{DATASENT}) {
delete($hash->{DATASENT});
KM271_SimpleWrite($hash, "02") if(@{$hash->{SENDBUFFER}});
DevIo_SimpleWrite($hash, "02") if(@{$hash->{SENDBUFFER}});
return;
}
$data = pop @{ $hash->{SENDBUFFER} };
$data =~ s/10/1010/g;
$crc = KM271_crc($data);
KM271_SimpleWrite($hash, $data."1003$crc"); # Send the data
DevIo_SimpleWrite($hash, $data."1003$crc"); # Send the data
}
if($buf eq "15") { # NACK from the KM271
Log 1, "$name: NACK!";
delete($hash->{DATASENT});
KM271_SimpleWrite($hash, "02") if(@{$hash->{SENDBUFFER}});
DevIo_SimpleWrite($hash, "02") if(@{$hash->{SENDBUFFER}});
return;
}
} elsif($buf eq "02") { # KM271 Wants to send
KM271_SimpleWrite($hash, "10"); # We are ready
DevIo_SimpleWrite($hash, "10"); # We are ready
$hash->{PARTIAL} = "";
$hash->{WAITING} = 1;
return;
@ -339,12 +332,12 @@ KM271_Read($)
if(KM271_crc($data) ne $crc) {
Log 1, "Wrong CRC in $hash->{PARTIAL}: $crc vs. ". KM271_crc($data);
KM271_SimpleWrite($hash, "15"); # NAK
KM271_SimpleWrite($hash, "02") if(@{$hash->{SENDBUFFER}}); # want to send
DevIo_SimpleWrite($hash, "15"); # NAK
DevIo_SimpleWrite($hash, "02") if(@{$hash->{SENDBUFFER}}); # want to send
return;
}
KM271_SimpleWrite($hash, "10"); # ACK, Data received ok
DevIo_SimpleWrite($hash, "10"); # ACK, Data received ok
$data =~ s/1010/10/g;
@ -406,97 +399,21 @@ KM271_Ready($)
{
my ($hash) = @_;
return DevIo_OpenDev($hash, 1, undef)
if($hash->{STATE} eq "disconnected");
# This is relevant for windows/USB only
my $po = $hash->{Dev};
my $po = $hash->{USBDev};
my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status;
return ($InBytes>0);
}
########################
sub
KM271_SimpleWrite(@)
{
my ($hash, $msg) = @_;
Log 3, "KM271 SimpleWrite $msg" if(length($msg) != 2);
$hash->{Dev}->write(pack('H*',$msg)) if($hash->{DeviceName});
}
########################
sub
KM271_SimpleRead($)
KM271_DoInit($)
{
my ($hash) = @_;
return $hash->{Dev}->input() if($hash->{Dev});
return undef;
}
########################
sub
KM271_CloseDev($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
my $dev = $hash->{DeviceName};
return if(!$dev); # "none"
if($hash->{Dev}) {
$hash->{Dev}->close() ;
delete($hash->{Dev});
}
delete($selectlist{"$name.$dev"});
delete($readyfnlist{"$name.$dev"});
delete($hash->{FD});
delete($hash->{DeviceName});
}
########################
sub
KM271_OpenDev($)
{
my ($hash) = @_;
my $dev = $hash->{DeviceName};
my $name = $hash->{NAME};
my $po;
$hash->{PARTIAL} = "";
Log 3, "KM271 opening $name device $dev";
if ($^O=~/Win/) {
require Win32::SerialPort;
$po = new Win32::SerialPort ($dev);
} else {
require Device::SerialPort;
$po = new Device::SerialPort ($dev);
}
if(!$po) {
Log(3, "Can't open $dev: $!");
return "";
}
$hash->{Dev} = $po;
if( $^O =~ /Win/ ) {
$readyfnlist{"$name.$dev"} = $hash;
} else {
$hash->{FD} = $po->FILENO;
delete($readyfnlist{"$name.$dev"});
$selectlist{"$name.$dev"} = $hash;
}
$po->reset_error();
$po->baudrate(2400);
$po->databits(8);
$po->parity('none');
$po->stopbits(1);
$po->handshake('none');
$hash->{STATE} = "Initialized";
push @{$hash->{SENDBUFFER}}, "EE0000";
KM271_SimpleWrite($hash, "02"); # STX
Log 3, "$dev opened";
DevIo_SimpleWrite($hash, "02"); # STX
return undef;
}

View File

@ -26,11 +26,6 @@ sub TCM_ReadAnswer($$);
sub TCM_Ready($);
sub TCM_Write($$$);
sub TCM_OpenDev($$);
sub TCM_CloseDev($);
sub TCM_SimpleWrite($$);
sub TCM_SimpleRead($);
sub TCM_Disconnected($);
sub TCM_Parse120($$$);
sub TCM_CRC8($);
sub TCM_CSUM($);
@ -40,6 +35,8 @@ TCM_Initialize($)
{
my ($hash) = @_;
require "$attr{global}{modpath}/FHEM/DevIo.pm";
# Provider
$hash->{ReadFn} = "TCM_Read";
$hash->{WriteFn} = "TCM_Write";
@ -70,7 +67,7 @@ TCM_Define($$)
"{devicename[\@baudrate]|ip:port}"
if(@a != 4 || $model !~ m/^(120|310)$/);
TCM_CloseDev($hash);
DevIo_CloseDev($hash);
my $dev = $a[3];
if($dev eq "none") {
@ -81,7 +78,7 @@ TCM_Define($$)
$hash->{DeviceName} = $dev;
$hash->{MODEL} = $model;
my $ret = TCM_OpenDev($hash, 0);
my $ret = DevIo_OpenDev($hash, 0, undef);
return $ret;
}
@ -114,7 +111,7 @@ TCM_Write($$$)
}
Log $ll5, "$hash->{NAME} sending $bstring";
TCM_SimpleWrite($hash, $bstring);
DevIo_SimpleWrite($hash, $bstring);
}
#####################################
@ -180,22 +177,13 @@ TCM_Read($)
{
my ($hash) = @_;
my $buf = TCM_SimpleRead($hash);
my $buf = DevIo_SimpleRead($hash);
return "" if(!defined($buf));
my $name = $hash->{NAME};
my $ll5 = GetLogLevel($name,5);
my $ll2 = GetLogLevel($name,2);
###########
# Lets' try again: Some drivers return len(0) on the first read...
if(defined($buf) && length($buf) == 0) {
$buf = TCM_SimpleRead($hash);
}
if(!defined($buf) || length($buf) == 0) {
TCM_Disconnected($hash);
return "";
}
my $data = $hash->{PARTIAL} . uc(unpack('H*', $buf));
Log $ll5, "$name/RAW: $data";
@ -401,7 +389,7 @@ TCM_Ready($)
{
my ($hash) = @_;
return TCM_OpenDev($hash, 1)
return DevIo_OpenDev($hash, 1, undef)
if($hash->{STATE} eq "disconnected");
# This is relevant for windows/USB only
@ -410,193 +398,6 @@ TCM_Ready($)
return ($InBytes>0);
}
########################
# Input is HEX, with header and CRC
sub
TCM_SimpleWrite($$)
{
my ($hash, $msg) = @_;
return if(!$hash);
#Log 1, "SW: $msg";
$msg = pack('H*', $msg);
$hash->{USBDev}->write($msg) if($hash->{USBDev});
syswrite($hash->{TCPDev}, $msg) if($hash->{TCPDev});
select(undef, undef, undef, 0.001);
}
########################
sub
TCM_SimpleRead($)
{
my ($hash) = @_;
my $buf;
$buf = $hash->{USBDev}->input() if($hash->{USBDev});
$buf = sysread($hash->{TCPDev}, $buf, 256) if($hash->{TCPDev});
return $buf;
}
########################
sub
TCM_CloseDev($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
my $dev = $hash->{DeviceName};
return if(!$dev);
if($hash->{TCPDev}) {
$hash->{TCPDev}->close();
delete($hash->{TCPDev});
} elsif($hash->{USBDev}) {
$hash->{USBDev}->close() ;
delete($hash->{USBDev});
}
($dev, undef) = split("@", $dev); # Remove the baudrate
delete($selectlist{"$name.$dev"});
delete($readyfnlist{"$name.$dev"});
delete($hash->{FD});
}
########################
sub
TCM_OpenDev($$)
{
my ($hash, $reopen) = @_;
my $dev = $hash->{DeviceName};
my $name = $hash->{NAME};
my $po;
my $baudrate;
($dev, $baudrate) = split("@", $dev);
if(!$baudrate) {
$baudrate = 9600 if($hash->{MODEL} == 120);
$baudrate = 57600 if($hash->{MODEL} == 310);
}
$hash->{PARTIAL} = "";
Log 3, "TCM opening $name device $dev"
if(!$reopen);
if($dev =~ m/^(.+):([0-9]+)$/) { # host:port
# This part is called every time the timeout (5sec) is expired _OR_
# somebody is communicating over another TCP connection. As the connect
# for non-existent devices has a delay of 3 sec, we are sitting all the
# time in this connect. NEXT_OPEN tries to avoid this problem.
if($hash->{NEXT_OPEN} && time() < $hash->{NEXT_OPEN}) {
return;
}
my $conn = IO::Socket::INET->new(PeerAddr => $dev);
if($conn) {
delete($hash->{NEXT_OPEN})
} else {
Log(3, "Can't connect to $dev: $!") if(!$reopen);
$readyfnlist{"$name.$dev"} = $hash;
$hash->{STATE} = "disconnected";
$hash->{NEXT_OPEN} = time()+60;
return "";
}
$hash->{TCPDev} = $conn;
$hash->{FD} = $conn->fileno();
delete($readyfnlist{"$name.$dev"});
$selectlist{"$name.$dev"} = $hash;
} else { # USB/Serial device
if ($^O=~/Win/) {
require Win32::SerialPort;
$po = new Win32::SerialPort ($dev);
} else {
require Device::SerialPort;
$po = new Device::SerialPort ($dev);
}
if(!$po) {
return undef if($reopen);
Log(3, "Can't open $dev: $!");
$readyfnlist{"$name.$dev"} = $hash;
$hash->{STATE} = "disconnected";
return "";
}
$hash->{USBDev} = $po;
if( $^O =~ /Win/ ) {
$readyfnlist{"$name.$dev"} = $hash;
} else {
$hash->{FD} = $po->FILENO;
delete($readyfnlist{"$name.$dev"});
$selectlist{"$name.$dev"} = $hash;
}
$po->reset_error();
$po->baudrate($baudrate);
$po->databits(8);
$po->parity('none');
$po->stopbits(1);
$po->handshake('none');
# This part is for some Linux kernel versions whih has strange default
# settings. Device::SerialPort is nice: if the flag is not defined for your
# OS then it will be ignored.
$po->stty_icanon(0);
#$po->stty_parmrk(0); # The debian standard install does not have it
$po->stty_icrnl(0);
$po->stty_echoe(0);
$po->stty_echok(0);
$po->stty_echoctl(0);
# Needed for some strange distros
$po->stty_echo(0);
$po->stty_icanon(0);
$po->stty_isig(0);
$po->stty_opost(0);
$po->stty_icrnl(0);
$po->write_settings;
}
if($reopen) {
Log 1, "TCM $dev reappeared ($name)";
} else {
Log 3, "TCM device opened";
}
$hash->{STATE}="connected";
DoTrigger($name, "CONNECTED") if($reopen);
return "";
}
sub
TCM_Disconnected($)
{
my $hash = shift;
my $dev = $hash->{DeviceName};
my $name = $hash->{NAME};
my $baudrate;
($dev, $baudrate) = split("@", $dev);
return if(!defined($hash->{FD})); # Already deleted or RFR
Log 1, "$dev disconnected, waiting to reappear";
TCM_CloseDev($hash);
$readyfnlist{"$name.$dev"} = $hash; # Start polling
$hash->{STATE} = "disconnected";
# Without the following sleep the open of the device causes a SIGSEGV,
# and following opens block infinitely. Only a reboot helps.
sleep(5);
DoTrigger($name, "DISCONNECTED");
}
my %gets120 = (
"sensitivity" => "AB48",
"idbase" => "AB58",
@ -711,7 +512,7 @@ TCM_Set($@)
##############################
if($hash->{MODEL} eq "120") {
if($cmdHex eq "") { # wake is very special
TCM_SimpleWrite($hash, "AA");
DevIo_SimpleWrite($hash, "AA");
return "";
}
@ -767,12 +568,12 @@ TCM_ReadAnswer($$)
if($nfound < 0) {
next if ($! == EAGAIN() || $! == EINTR() || $! == 0);
my $err = $!;
TCM_Disconnected($hash);
DevIo_Disconnected($hash);
return("TCM_ReadAnswer $err", undef);
}
return ("Timeout reading answer for $arg", undef)
if($nfound == 0);
$buf = TCM_SimpleRead($hash);
$buf = DevIo_SimpleRead($hash);
return ("No data", undef) if(!defined($buf));
}