You can subscribe to this list here.
| 2010 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
(1) |
Nov
(121) |
Dec
(58) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2011 |
Jan
(38) |
Feb
(36) |
Mar
(7) |
Apr
(2) |
May
(32) |
Jun
(24) |
Jul
(16) |
Aug
(21) |
Sep
(17) |
Oct
(62) |
Nov
(60) |
Dec
(70) |
| 2012 |
Jan
(54) |
Feb
(41) |
Mar
(21) |
Apr
(38) |
May
(76) |
Jun
(47) |
Jul
(25) |
Aug
(72) |
Sep
(29) |
Oct
(64) |
Nov
(93) |
Dec
(97) |
| 2013 |
Jan
(100) |
Feb
(168) |
Mar
(115) |
Apr
(59) |
May
(37) |
Jun
(32) |
Jul
(45) |
Aug
(42) |
Sep
(24) |
Oct
(73) |
Nov
(64) |
Dec
(4) |
| 2014 |
Jan
(14) |
Feb
(57) |
Mar
(58) |
Apr
(10) |
May
(18) |
Jun
(12) |
Jul
(7) |
Aug
(12) |
Sep
(15) |
Oct
(6) |
Nov
(32) |
Dec
(17) |
| 2015 |
Jan
(50) |
Feb
(5) |
Mar
(1) |
Apr
(26) |
May
(10) |
Jun
(3) |
Jul
(3) |
Aug
(2) |
Sep
(3) |
Oct
(18) |
Nov
(18) |
Dec
(8) |
| 2016 |
Jan
(33) |
Feb
(35) |
Mar
(50) |
Apr
(20) |
May
(25) |
Jun
(17) |
Jul
(8) |
Aug
(73) |
Sep
(64) |
Oct
(51) |
Nov
(20) |
Dec
(14) |
| 2017 |
Jan
(41) |
Feb
(57) |
Mar
(44) |
Apr
(136) |
May
(32) |
Jun
(39) |
Jul
(2) |
Aug
(12) |
Sep
(32) |
Oct
(103) |
Nov
(12) |
Dec
(4) |
| 2018 |
Jan
(9) |
Feb
(1) |
Mar
(60) |
Apr
(24) |
May
(15) |
Jun
(1) |
Jul
(2) |
Aug
(23) |
Sep
(15) |
Oct
(57) |
Nov
(21) |
Dec
(77) |
| 2019 |
Jan
(62) |
Feb
(99) |
Mar
(98) |
Apr
(49) |
May
(6) |
Jun
(3) |
Jul
(6) |
Aug
(18) |
Sep
(9) |
Oct
(15) |
Nov
(30) |
Dec
(6) |
| 2020 |
Jan
(14) |
Feb
(2) |
Mar
(22) |
Apr
(33) |
May
(47) |
Jun
(12) |
Jul
|
Aug
|
Sep
(4) |
Oct
(2) |
Nov
(5) |
Dec
(5) |
| 2021 |
Jan
(4) |
Feb
(101) |
Mar
(13) |
Apr
(32) |
May
(40) |
Jun
|
Jul
(3) |
Aug
|
Sep
|
Oct
(25) |
Nov
(12) |
Dec
|
| 2022 |
Jan
(154) |
Feb
(82) |
Mar
(63) |
Apr
(27) |
May
(26) |
Jun
(5) |
Jul
(12) |
Aug
(23) |
Sep
(17) |
Oct
(37) |
Nov
(13) |
Dec
(21) |
| 2023 |
Jan
(43) |
Feb
(43) |
Mar
(15) |
Apr
(8) |
May
(3) |
Jun
(25) |
Jul
(6) |
Aug
(38) |
Sep
(5) |
Oct
(20) |
Nov
(9) |
Dec
(28) |
| 2024 |
Jan
(15) |
Feb
(2) |
Mar
(12) |
Apr
(2) |
May
(8) |
Jun
(10) |
Jul
(10) |
Aug
(2) |
Sep
(3) |
Oct
(15) |
Nov
(6) |
Dec
(20) |
| 2025 |
Jan
|
Feb
(2) |
Mar
(6) |
Apr
(2) |
May
(2) |
Jun
|
Jul
|
Aug
(1) |
Sep
(1) |
Oct
(11) |
Nov
(2) |
Dec
|
|
From: <pf...@us...> - 2012-05-10 10:15:36
|
Revision: 805
http://openautomation.svn.sourceforge.net/openautomation/?rev=805&view=rev
Author: pfry
Date: 2012-05-10 10:15:25 +0000 (Thu, 10 May 2012)
Log Message:
-----------
eibd_backend_address konfigurierbar gemacht, zur Vermeidung von Zirkellogiken
Modified Paths:
--------------
wiregate/plugin/generic/Logikprozessor.pl
wiregate/plugin/generic/conf.d/Logikprozessor.conf
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-05-10 08:54:24 UTC (rev 804)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-10 10:15:25 UTC (rev 805)
@@ -48,6 +48,7 @@
my $systemtime=time();
# Konfigurationsfile einlesen
+my $eibd_backend_address='1.1.254';
my %logic=();
my $conf="/etc/wiregate/plugin/generic/conf.d/$plugname";
$conf.='.conf' unless $conf=~s/\.pl$/.conf/;
@@ -90,11 +91,11 @@
for my $t (keys %logic)
{
+ next if $t eq 'debug';
+
# Debuggingflag gesetzt
my $debug = $logic{debug} || $logic{$t}{debug};
- next if $t eq 'debug';
-
# Eintrag pruefen
if(defined $logic{$t}{receive} && ref $logic{$t}{receive} && ref $logic{$t}{receive} ne 'ARRAY')
{
@@ -170,6 +171,8 @@
# welche translate-Logik ist aufgerufen?
for my $t (keys %logic)
{
+ next if $t eq 'debug';
+
my $transmit=groupaddress($logic{$t}{transmit});
my $transmit_ga = ($ga eq $transmit);
@@ -230,7 +233,7 @@
# (Logik antwortet auf sich selbst in einer Endlosschleife)
# war Wiregate der Sender des Telegramms?
- my $sender_is_wiregate=int($msg{src})==0;
+ my $sender_is_wiregate = $msg{src} eq $eibd_backend_address;
# Aufruf der Logik-Engine
my $result=execute_logic($t, $receive, $ga, $in);
@@ -263,12 +266,13 @@
if($logic{$t}{delay})
{
$plugin_info{$plugname.'__'.$t.'_timer'}=$systemtime+$logic{$t}{delay};
- plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result, zu senden in ".$logic{$t}{delay}."s");
+ plugin_log($plugname, "$msg{src} $ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result, zu senden in ".$logic{$t}{delay}."s")
+ if $debug;
}
else
{
knx_write($transmit, $result);
- plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result gesendet") if $debug;
+ plugin_log($plugname, "$msg{src} $ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result gesendet") if $debug;
}
}
Modified: wiregate/plugin/generic/conf.d/Logikprozessor.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-10 08:54:24 UTC (rev 804)
+++ wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-10 10:15:25 UTC (rev 805)
@@ -3,6 +3,8 @@
# Logikprozessor.pl - Konfiguration
#
+$eibd_backend_address='1.1.254'; # eigene Adresse zur Vermeidung von Zirkellogiken
+
%logic=(
# 1. Alle Werte, die auf einer GA gesendet werden, werden mit 2 multipliziert auf einer anderen GA weitergegeben
mal2 => { receive=>'9/5/201', transmit=>'9/5/202', translate => sub { 2*$input; }, },
@@ -38,7 +40,7 @@
req => { receive=>'1/2/9', transmit=>'1/2/9', transmit_on_request=>1, translate => sub { 2*$input; }, },
# 6. Eine Logik, die einem Lichtanschalten gleich einen Dimmwert hinterherfeuert, und zwar tags und nachts einen verschiedenen:
- dim => { receive=>'2/2/9', transmit=>'2/3/9', translate => sub { return unless $input; $day ? 80 : 5; }, },
+ dim => { receive=>'2/2/9', transmit=>'2/3/9', translate => sub { return unless $input; $day ? 80 : 3; }, },
# Die Variablen $day_of_week (Mo...So), $day_of_month (01-31), $month (01-12), $year (2012),
# $weekend (0 oder 1), $weekday (= nicht $weekend), $time_of_day ("08:34:02"),
# $hour ("08"), $day (1 falls zwischen 7 und 23 Uhr, 0 sonst) und $night (entsprechend umgekehrt)
@@ -88,3 +90,73 @@
# in einen absoluten Wert umgewandelt
);
+
+# In meinem (Fry) speziellen GA-Schema kann ich mir die obige Einzelkonfiguration sparen, weil aus meinem
+# GA-Namensschema mit sprechenden Kuerzeln alle Zuweisungen folgen; das Konfigurationshash wird in folgender
+# Schleife automatisch erzeugt.
+%logic=(
+ wecker => { debug=>1, transmit=>'LI_SZ_Decke_1', timer=>{ time=>'22:23', day_of_month=>[(7..14)], day_of_week=>'Mi' }, translate => 1 },
+ wecker2 => { debug=>1, transmit=>'LI_SZ_Decke_1', timer=>{ time=>'22:24', day_of_month=>[(7..14)], day_of_week=>'Mi' }, translate => 0 },
+ lichtaus => { debug=>1, receive=>'LD_SZ_Decke_1', transmit=>'LD_SZ_Decke_1', translate => 0 },
+# temperatur => { receive=>['KR_E_Fermax_Oeffner','TA_A2'], transmit=>'WA_Temperatur',
+# translate => sub { return if $input->[0]; $input->[1]; },
+# },
+);
+
+# Memory-Funktion f\xFCr alle Aktoren, die kein KO fuer die Abfrage des eingestellten Wertes anbieten
+for my $rec (grep /^(LD|JP|JW)_\S*$/, keys %eibgaconf)
+{
+ $logic{$rec.'_mem'}={ transmit=>$rec, transmit_only_on_request=>1 };
+}
+
+# Wenn unter 3% gedimmt wird -> ausschalten
+for my $rec (grep /^LD_\S*$/, keys %eibgaconf)
+{
+ my $trm=$rec; $trm=~s/^LD/LI/;
+ $logic{$rec.'_off'}={ receive=>$rec, transmit=>$trm, translate => sub { $input<3 ? 0 : undef; } };
+}
+
+# Szenenbestaetigung: LEDs der Taster blinken nach Szenenspeicherung, diese 1s spaeter wieder ausschalten
+for my $rec (grep /^ZC_\S*$/, keys %eibgaconf)
+{
+ $logic{$rec.'_led_off'}={ debug=>1, receive=>$rec, transmit=>$rec, delay=>1, translate=>0 };
+}
+
+# nachts gedaempftes Licht. Auf einen Einschaltbefehl bei Nacht direkt einen Dimmwert hinterherschicken
+for my $rec (grep /^LI_\S*$/, keys %eibgaconf)
+{
+ my $trm=$rec; $trm=~s/^LI/LK/;
+ $logic{$rec.'_dim'}={ receive=>$rec, transmit=>$trm,
+ translate => sub { return undef unless $input;
+ return 3 if $night;
+ return 50; }
+ };
+}
+
+# Umwandlung relativer Dimmwerte in absolute Dimmwerte
+#for my $rec (grep /^LR_\S*$/, keys %eibgaconf)
+#{
+# next unless $eibgaconf{$rec}{name}=~/Dali/;
+# $rec=$eibgaconf{$rec}{short};
+# my $trm=$rec; $trm=~s/^LR/LK/;
+#
+# $logic{$rec}={
+# receive=>$rec, transmit=>$trm,
+# translate => sub { my ($state,$input)=@_; limit(0,(1+0.2*$input)*$state+5*$input,100); }
+# };
+#}
+
+# Umwandlung von Schaltbefehlen in absolute Dimmwerte
+#for my $rec (grep /^LI_\S*$/, keys %eibgaconf)
+#{
+# next unless $eibgaconf{$rec}{name}=~/Dali/;
+# $rec=$eibgaconf{$rec}{short};
+# my $trm=$rec; $trm=~s/^LI/LK/;
+#
+# $logic{$rec}={
+# receive=>$rec, transmit=>$trm,
+# translate => sub { my ($state,$input)=@_; 100*(1-$input); }
+# };
+#}
+
+
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-10 08:54:34
|
Revision: 804
http://openautomation.svn.sourceforge.net/openautomation/?rev=804&view=rev
Author: pfry
Date: 2012-05-10 08:54:24 +0000 (Thu, 10 May 2012)
Log Message:
-----------
KLeinerer Bugfix betr. debug-Flag und Name des Konfigfiles, wenn ".pl" im Skriptnamen fehlt
Modified Paths:
--------------
wiregate/plugin/generic/Logikprozessor.pl
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-05-10 07:49:18 UTC (rev 803)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-10 08:54:24 UTC (rev 804)
@@ -49,7 +49,8 @@
# Konfigurationsfile einlesen
my %logic=();
-my $conf="/etc/wiregate/plugin/generic/conf.d/$plugname"; $conf=~s/\.pl$/.conf/;
+my $conf="/etc/wiregate/plugin/generic/conf.d/$plugname";
+$conf.='.conf' unless $conf=~s/\.pl$/.conf/;
open FILE, "<$conf" || return "no config found";
$/=undef;
my $lines = <FILE>;
@@ -92,6 +93,8 @@
# Debuggingflag gesetzt
my $debug = $logic{debug} || $logic{$t}{debug};
+ next if $t eq 'debug';
+
# Eintrag pruefen
if(defined $logic{$t}{receive} && ref $logic{$t}{receive} && ref $logic{$t}{receive} ne 'ARRAY')
{
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-10 07:49:29
|
Revision: 803
http://openautomation.svn.sourceforge.net/openautomation/?rev=803&view=rev
Author: pfry
Date: 2012-05-10 07:49:18 +0000 (Thu, 10 May 2012)
Log Message:
-----------
Mini-Patch, betrifft m?\195?\182gliche Zirkellogiken (Logiken, die sich selbst wieder aufrufen und damit zu Endlosschleifen f?\195?\188hren)
Modified Paths:
--------------
wiregate/plugin/generic/Logikprozessor.pl
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-05-09 20:59:51 UTC (rev 802)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-10 07:49:18 UTC (rev 803)
@@ -232,15 +232,8 @@
# Aufruf der Logik-Engine
my $result=execute_logic($t, $receive, $ga, $in);
- # In bestimmten Sonderfaellen nichts schicken
- unless(defined $result) # Resultat undef => nichts senden
- {
- plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Logik) -> nichts zu senden") if $debug;
- next;
- }
-
# Zirkelaufruf ausschliessen
- if($sender_is_wiregate && $in eq $result)
+ if($sender_is_wiregate && $in == $result)
{
# kommt transmit-GA unter den receive-GAs vor?
# Wenn transmit_ga gesetzt ist, ist das schon mal der Fall
@@ -249,9 +242,16 @@
next if ref $receive && grep /^$transmit$/, @{$receive};
}
+ # In bestimmten Sonderfaellen nichts schicken
+ unless(defined $result) # Resultat undef => nichts senden
+ {
+ plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Logik) -> nichts zu senden") if $debug;
+ next;
+ }
+
if($logic{$t}{transmit_only_on_request})
{
- plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result, wird erst auf spaeteres Lesetelegramm versendet")
+ plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result gespeichert")
if $debug;
next;
}
@@ -312,10 +312,10 @@
# Timer loeschen bzw. neu setzen
set_next_call($t, $debug);
- next unless defined $result;
- next if $toor;
-
- knx_write($transmit, $result);
+ if(defined $result && !$toor)
+ {
+ knx_write($transmit, $result);
+ }
}
else # noch nicht faelliger Timer
{
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-09 20:59:58
|
Revision: 802
http://openautomation.svn.sourceforge.net/openautomation/?rev=802&view=rev
Author: pfry
Date: 2012-05-09 20:59:51 +0000 (Wed, 09 May 2012)
Log Message:
-----------
Logeintrag minimal gekuerzt
Modified Paths:
--------------
wiregate/plugin/generic/Logikprozessor.pl
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-05-09 20:26:38 UTC (rev 801)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-09 20:59:51 UTC (rev 802)
@@ -235,7 +235,7 @@
# In bestimmten Sonderfaellen nichts schicken
unless(defined $result) # Resultat undef => nichts senden
{
- plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Logik) -> nichts zu senden");
+ plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Logik) -> nichts zu senden") if $debug;
next;
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-09 20:26:45
|
Revision: 801
http://openautomation.svn.sourceforge.net/openautomation/?rev=801&view=rev
Author: pfry
Date: 2012-05-09 20:26:38 +0000 (Wed, 09 May 2012)
Log Message:
-----------
Debuggingflag hinzugefuegt. (debug=>1 in %logic oder auch in einzelnen Logikdefinitionen)
Modified Paths:
--------------
wiregate/plugin/generic/Logikprozessor.pl
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-05-08 20:18:22 UTC (rev 800)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-09 20:26:38 UTC (rev 801)
@@ -7,7 +7,7 @@
#$plugin_info{$plugname.'_cycle'}=0; return 'deaktiviert';
-my $use_short_names=0; # 1 fuer GA-Kuerzel (erstes Wort des GA-Namens), 0 fuer die "nackte" Gruppenadresse
+my $use_short_names=1; # 1 fuer GA-Kuerzel (erstes Wort des GA-Namens), 0 fuer die "nackte" Gruppenadresse
# eibgaconf fixen falls nicht komplett indiziert
if($use_short_names && !exists $eibgaconf{ZV_Uhrzeit})
@@ -89,6 +89,9 @@
for my $t (keys %logic)
{
+ # Debuggingflag gesetzt
+ my $debug = $logic{debug} || $logic{$t}{debug};
+
# Eintrag pruefen
if(defined $logic{$t}{receive} && ref $logic{$t}{receive} && ref $logic{$t}{receive} ne 'ARRAY')
{
@@ -116,14 +119,16 @@
# transmit-Adresse abonnieren
my $transmit=groupaddress($logic{$t}{transmit});
$plugin_subscribe{$transmit}{$plugname}=1;
+ plugin_log($plugname, "\$logic{$t}: Transmit-GA $transmit abonniert") if $debug;
+ # Zaehlen und Logeintrag
$count++;
# Timer-Logiken reagieren nicht auf Bustraffic auf den receive-Adressen
# fuer Timer-Logiken: ersten Call berechnen
if($logic{$t}{timer})
{
- set_next_call($t);
+ set_next_call($t, $debug);
next;
}
@@ -135,6 +140,7 @@
unless(ref $receive)
{
$plugin_subscribe{$receive}{$plugname}=1;
+ plugin_log($plugname, "\$logic{$t}: Receive-GA $receive abonniert") if $debug;
}
else
{
@@ -142,6 +148,7 @@
{
$plugin_subscribe{$rec}{$plugname}=1;
}
+ plugin_log($plugname, "\$logic{$t}: Receive-GAs (".join(",",@{$receive}).") abonniert") if $debug;
}
}
@@ -182,6 +189,9 @@
$keep_subscription=1;
+ # Debuggingflag gesetzt
+ my $debug = $logic{debug} || $logic{$t}{debug};
+
# Sonderfall: Read- und Write-Telegramme auf der Transmit-Adresse?
if($transmit_ga)
{
@@ -192,6 +202,7 @@
my $result=$plugin_info{$plugname.'_'.$t.'_result'};
if(defined $result)
{
+ plugin_log($plugname, "$ga:Lesetelegramm -> \$logic{$t}{transmit}(memory) -> $ga:$result gesendet") if $debug;
knx_write($ga, $result);
}
next;
@@ -222,8 +233,11 @@
my $result=execute_logic($t, $receive, $ga, $in);
# In bestimmten Sonderfaellen nichts schicken
- next unless defined $result; # Resultat undef => nichts senden
- next if $logic{$t}{transmit_only_on_request};
+ unless(defined $result) # Resultat undef => nichts senden
+ {
+ plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Logik) -> nichts zu senden");
+ next;
+ }
# Zirkelaufruf ausschliessen
if($sender_is_wiregate && $in eq $result)
@@ -235,14 +249,23 @@
next if ref $receive && grep /^$transmit$/, @{$receive};
}
+ if($logic{$t}{transmit_only_on_request})
+ {
+ plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result, wird erst auf spaeteres Lesetelegramm versendet")
+ if $debug;
+ next;
+ }
+
# Falls delay spezifiziert, wird ein "Wecker" gestellt, um in einem spaeteren Aufruf den Wert zu senden
if($logic{$t}{delay})
{
$plugin_info{$plugname.'__'.$t.'_timer'}=$systemtime+$logic{$t}{delay};
+ plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result, zu senden in ".$logic{$t}{delay}."s");
}
else
{
knx_write($transmit, $result);
+ plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result gesendet") if $debug;
}
}
@@ -261,25 +284,36 @@
# Relevanten Eintrag von %logic ermitteln
$timer=~/$plugname\__(.*)_timer/;
my $t=$1;
+
+ # Debuggingflag gesetzt
+ my $debug = $logic{debug} || $logic{$t}{debug};
- # Timer loeschen bzw. neu setzen
- set_next_call($t);
+ # Transmit-GA
+ my $transmit=groupaddress($logic{$t}{transmit});
+ my $toor=$logic{$t}{transmit_only_on_request};
+ my $result=undef;
- # zu sendendes Resultat = zuletzt berechnetes Ergebnis der Logik
- my $result=$plugin_info{$plugname.'_'.$t.'_result'};
-
- # ...es sei denn, es ist eine timer-Logik. Die muss jetzt ausgefuehrt werden
- if($logic{$t}{timer})
+ unless($logic{$t}{timer})
{
+ # zu sendendes Resultat = zuletzt berechnetes Ergebnis der Logik
+ $result=$plugin_info{$plugname.'_'.$t.'_result'};
+ plugin_log($plugname, "\$logic{$t} -> $transmit:".
+ (defined $result?$result.($toor?" gespeichert":" gesendet"):"nichts zu senden")." (delayed)") if $debug;
+ }
+ else
+ {
+ # ...es sei denn, es ist eine timer-Logik. Die muss jetzt ausgefuehrt werden
# Aufruf der Logik-Engine
$result=execute_logic($t, groupaddress($logic{$t}{receive}), undef, undef);
+ plugin_log($plugname, "\$logic{$t} -> $transmit:".
+ (defined $result?$result.($toor?" gespeichert":" gesendet"):"nichts zu senden")." (Timer)") if $debug;
}
- # Transmit-GA
- my $transmit=groupaddress($logic{$t}{transmit});
-
+ # Timer loeschen bzw. neu setzen
+ set_next_call($t, $debug);
+
next unless defined $result;
- next if $logic{$t}{transmit_only_on_request};
+ next if $toor;
knx_write($transmit, $result);
}
@@ -292,13 +326,14 @@
# Cycle auf naechsten Aufruf setzen
unless(defined $nexttimer)
{
- $plugin_info{$plugname."_cycle"}=0; # kein Aufruf noetig
+ $plugin_info{$plugname."_cycle"}=0; # kein Aufruf noetig
}
else
{
my $cycle=$nexttimer-time();
$cycle=1 if $cycle<1;
$plugin_info{$plugname."_cycle"}=$cycle;
+ plugin_log($plugname, "Cycle (Timer) gestellt auf ".$cycle."s") if $logic{debug};
}
return unless $retval;
@@ -347,7 +382,7 @@
sub set_next_call
{
- my $t=shift; # der relevante Eintrag in %logic
+ my ($t,$debug)=@_; # der relevante Eintrag in %logic, und das Debugflag
my $nextcall=undef;
my $days_until_nextcall=0;
@@ -431,7 +466,8 @@
if(defined $nextcall)
{
- plugin_log($plugname, "Naechster Aufruf der Logik '$t' um $nextcall".($days_until_nextcall?" in $days_until_nextcall Tagen.":"."));
+ plugin_log($plugname, "Naechster Aufruf der Logik '$t' um $nextcall".($days_until_nextcall?" in $days_until_nextcall Tagen.":"."))
+ if $debug;
# Zeitdelta zu jetzt berechnen
my $seconds=3600*(substr($nextcall,0,2)-substr($time_of_day,0,2))
@@ -441,7 +477,8 @@
}
else
{
- plugin_log($plugname, "Logik '$t' wird nicht mehr aufgerufen (alle in time=>... festgelegten Termine sind verstrichen).");
+ plugin_log($plugname, "Logik '$t' wird nicht mehr aufgerufen (alle in time=>... festgelegten Termine sind verstrichen).")
+ if $debug && $logic{$t}{timer};
delete $plugin_info{$plugname.'__'.$t.'_timer'};
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <ma...@us...> - 2012-05-08 20:18:28
|
Revision: 800
http://openautomation.svn.sourceforge.net/openautomation/?rev=800&view=rev
Author: makki1
Date: 2012-05-08 20:18:22 +0000 (Tue, 08 May 2012)
Log Message:
-----------
remove SegV in daemon-mode
Modified Paths:
--------------
tools/knxdmxd/src/knxdmxd.cpp
Modified: tools/knxdmxd/src/knxdmxd.cpp
===================================================================
--- tools/knxdmxd/src/knxdmxd.cpp 2012-05-08 20:17:03 UTC (rev 799)
+++ tools/knxdmxd/src/knxdmxd.cpp 2012-05-08 20:18:22 UTC (rev 800)
@@ -552,7 +552,7 @@
//char *p;
char pidstr[255];
- while ((c = getopt (argc, argv, "d:p:u:c:")) != -1)
+ while ((c = getopt (argc, argv, "dp:u:c:")) != -1)
switch (c) {
case 'd':
daemonize = 1;
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <ma...@us...> - 2012-05-08 20:17:13
|
Revision: 799
http://openautomation.svn.sourceforge.net/openautomation/?rev=799&view=rev
Author: makki1
Date: 2012-05-08 20:17:03 +0000 (Tue, 08 May 2012)
Log Message:
-----------
Add debian-packaging, change to autoconf/make - NO source-changes
Added Paths:
-----------
tools/knxdmxd/Makefile.am
tools/knxdmxd/Makefile.org
tools/knxdmxd/configure.ac
tools/knxdmxd/debian/
tools/knxdmxd/debian/README
tools/knxdmxd/debian/README.Debian
tools/knxdmxd/debian/changelog
tools/knxdmxd/debian/compat
tools/knxdmxd/debian/control
tools/knxdmxd/debian/copyright
tools/knxdmxd/debian/docs
tools/knxdmxd/debian/files
tools/knxdmxd/debian/init.d
tools/knxdmxd/debian/knxdmxd.default
tools/knxdmxd/debian/knxdmxd.install
tools/knxdmxd/debian/rules
tools/knxdmxd/debian/source/
tools/knxdmxd/debian/source/format
tools/knxdmxd/src/
tools/knxdmxd/src/Makefile.am
tools/knxdmxd/src/knxdmxd.cpp
Removed Paths:
-------------
tools/knxdmxd/Makefile
tools/knxdmxd/knxdmxd.c
Deleted: tools/knxdmxd/Makefile
===================================================================
--- tools/knxdmxd/Makefile 2012-05-08 20:07:12 UTC (rev 798)
+++ tools/knxdmxd/Makefile 2012-05-08 20:17:03 UTC (rev 799)
@@ -1,7 +0,0 @@
-CC = g++
-CFLAGS = -Wall -Os
-LIBS = -L. -L/usr/lib -L/home/jan/ola-0.8.18/ola -lpthread -leibclient -lolacommon -lola -ljson
-
-knxdmxd: knxdmxd.c
- $(CC) $(CFLAGS) knxdmxd.c $(LIBS) -o knxdmxd
-
Added: tools/knxdmxd/Makefile.am
===================================================================
--- tools/knxdmxd/Makefile.am (rev 0)
+++ tools/knxdmxd/Makefile.am 2012-05-08 20:17:03 UTC (rev 799)
@@ -0,0 +1,2 @@
+AUTOMAKE_OPTIONS = foreign
+SUBDIRS = src
\ No newline at end of file
Copied: tools/knxdmxd/Makefile.org (from rev 797, tools/knxdmxd/Makefile)
===================================================================
--- tools/knxdmxd/Makefile.org (rev 0)
+++ tools/knxdmxd/Makefile.org 2012-05-08 20:17:03 UTC (rev 799)
@@ -0,0 +1,7 @@
+CC = g++
+CFLAGS = -Wall -Os
+LIBS = -L. -L/usr/lib -L/home/jan/ola-0.8.18/ola -lpthread -leibclient -lolacommon -lola -ljson
+
+knxdmxd: knxdmxd.c
+ $(CC) $(CFLAGS) knxdmxd.c $(LIBS) -o knxdmxd
+
Added: tools/knxdmxd/configure.ac
===================================================================
--- tools/knxdmxd/configure.ac (rev 0)
+++ tools/knxdmxd/configure.ac 2012-05-08 20:17:03 UTC (rev 799)
@@ -0,0 +1,38 @@
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+AC_PREREQ([2.61])
+AC_INIT(knxdmxd)
+AM_INIT_AUTOMAKE(knxdmxd, 0.01)
+
+AC_CONFIG_SRCDIR([src])
+#AC_CONFIG_HEADERS([config.h:config.h.in])
+
+# Checks for programs.
+AC_PROG_CXX
+
+# Checks for libraries.
+AC_CHECK_LIB([eibclient], [EIBClose], ,[AC_MSG_ERROR(
+[Couldn't find libeibclient...try installing the eibclient-dev package] )])
+AC_CHECK_LIB([pthread], [pthread_create])
+AC_CHECK_LIB([json], [json_object_get], ,[AC_MSG_ERROR(
+[Couldn't find libjson0...try installing the libjson0-dev package] )])
+
+dnl ************************************
+dnl Check for libola
+dnl ************************************
+PKG_CHECK_MODULES([OLA], [libola], ,[AC_MSG_ERROR(
+[Couldn't find libola...try installing the ola package] )])
+AC_SUBST(OLA_LIBS)
+
+
+# Checks for header files.
+AC_CHECK_HEADERS([arpa/inet.h fcntl.h stdlib.h string.h syslog.h unistd.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_PID_T
+
+# Checks for library functions.
+AC_FUNC_FORK
+
+AC_OUTPUT(src/Makefile Makefile)
+
Added: tools/knxdmxd/debian/README
===================================================================
--- tools/knxdmxd/debian/README (rev 0)
+++ tools/knxdmxd/debian/README 2012-05-08 20:17:03 UTC (rev 799)
@@ -0,0 +1,6 @@
+The Debian Package knxdmxd
+----------------------------
+
+Comments regarding the Package
+
+ -- Michael Markstaller <de...@wi...> Wed, 07 Sep 2011 18:04:18 +0200
Added: tools/knxdmxd/debian/README.Debian
===================================================================
--- tools/knxdmxd/debian/README.Debian (rev 0)
+++ tools/knxdmxd/debian/README.Debian 2012-05-08 20:17:03 UTC (rev 799)
@@ -0,0 +1,6 @@
+knxdmxd for Debian
+-----------------------
+
+control startup settings through /etc/default/knxdmxd
+
+ -- Michael Markstaller <de...@wi...> Di 08 Mai 2012 16:52:30 CEST
Added: tools/knxdmxd/debian/changelog
===================================================================
--- tools/knxdmxd/debian/changelog (rev 0)
+++ tools/knxdmxd/debian/changelog 2012-05-08 20:17:03 UTC (rev 799)
@@ -0,0 +1,5 @@
+knxdmxd (0.1) unstable; urgency=low
+
+ * Initial Release.
+
+ -- Michael Markstaller <de...@wi...> Tue, 08 May 2012 16:53:19 +0200
Added: tools/knxdmxd/debian/compat
===================================================================
--- tools/knxdmxd/debian/compat (rev 0)
+++ tools/knxdmxd/debian/compat 2012-05-08 20:17:03 UTC (rev 799)
@@ -0,0 +1 @@
+5
Added: tools/knxdmxd/debian/control
===================================================================
--- tools/knxdmxd/debian/control (rev 0)
+++ tools/knxdmxd/debian/control 2012-05-08 20:17:03 UTC (rev 799)
@@ -0,0 +1,14 @@
+Source: knxdmxd
+Section: unknown
+Priority: extra
+Maintainer: Michael Markstaller <de...@wi...>
+Build-Depends: debhelper (>= 5.0), autotools-dev, libeibclient-dev, libjson0-dev
+Standards-Version: 3.9.1
+Homepage: http://sourceforge.net/projects/openautomation/
+
+Package: knxdmxd
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: simple daemon to connect EIB/KNX to DMX using OLA
+ simple daemon to connect EIB/KNX to DMX using OLA
+
Added: tools/knxdmxd/debian/copyright
===================================================================
--- tools/knxdmxd/debian/copyright (rev 0)
+++ tools/knxdmxd/debian/copyright 2012-05-08 20:17:03 UTC (rev 799)
@@ -0,0 +1,39 @@
+Format: http://dep.debian.net/deps/dep5
+Upstream-Name: knxdmxd
+Source: <http://sourceforge.net/projects/openautomation/>
+
+Files: *
+Copyright: 2012 Jan N. Klug 2012 <jan...@ru...>
+License: GPL-3.0+
+
+Files: debian/*
+Copyright: 2011 Michael Markstaller <de...@wi...>
+License: GPL-3.0+
+
+License: GPL-3.0+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+ .
+ This package is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ .
+ On Debian systems, the complete text of the GNU General
+ Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
+
+# Please choose a license for your packaging work. If the program you package
+# uses a mainstream license, using the same license is the safest choice.
+# Please avoid to pick license terms that are more restrictive than the
+# packaged work, as it may make Debian's contributions unacceptable upstream.
+# If you just want it to be GPL version 3, leave the following line in.
+
+and is licensed under the GPL version 3, see above.
+
+# Please also look if there are files or directories which have a
+# different copyright/license attached and list them here.
Added: tools/knxdmxd/debian/docs
===================================================================
--- tools/knxdmxd/debian/docs (rev 0)
+++ tools/knxdmxd/debian/docs 2012-05-08 20:17:03 UTC (rev 799)
@@ -0,0 +1,2 @@
+README
+
Added: tools/knxdmxd/debian/files
===================================================================
--- tools/knxdmxd/debian/files (rev 0)
+++ tools/knxdmxd/debian/files 2012-05-08 20:17:03 UTC (rev 799)
@@ -0,0 +1 @@
+knxdmxd_0.1_i386.deb unknown extra
Added: tools/knxdmxd/debian/init.d
===================================================================
--- tools/knxdmxd/debian/init.d (rev 0)
+++ tools/knxdmxd/debian/init.d 2012-05-08 20:17:03 UTC (rev 799)
@@ -0,0 +1,152 @@
+#!/bin/sh
+### BEGIN INIT INFO
+# Provides: knxdmxd
+# Required-Start: $network $local_fs
+# Required-Stop:
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: <Enter a short description of the sortware>
+# Description: <Enter a long description of the software>
+# <...>
+# <...>
+### END INIT INFO
+
+# PATH should only include /usr/* if it runs after the mountnfs.sh script
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+DESC=knxdmxd # Introduce a short description here
+NAME=knxdmxd # Introduce the short server's name here
+DAEMON=/usr/bin/knxdmxd # Introduce the server's location here
+DAEMON_ARGS="" # Arguments to run the daemon with
+PIDFILE=/var/run/$NAME.pid
+SCRIPTNAME=/etc/init.d/$NAME
+
+# Exit if the package is not installed
+[ -x $DAEMON ] || exit 0
+
+# Read configuration variable file if it is present
+[ -r /etc/default/$NAME ] && . /etc/default/$NAME
+
+# Load the VERBOSE setting and other rcS variables
+. /lib/init/vars.sh
+
+# Define LSB log_* functions.
+# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
+. /lib/lsb/init-functions
+
+#
+# Function that starts the daemon/service
+#
+do_start()
+{
+ # Return
+ # 0 if daemon has been started
+ # 1 if daemon was already running
+ # 2 if daemon could not be started
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
+ || return 1
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
+ $DAEMON_ARGS \
+ || return 2
+ # Add code here, if necessary, that waits for the process to be ready
+ # to handle requests from services started subsequently which depend
+ # on this one. As a last resort, sleep for some time.
+}
+
+#
+# Function that stops the daemon/service
+#
+do_stop()
+{
+ # Return
+ # 0 if daemon has been stopped
+ # 1 if daemon was already stopped
+ # 2 if daemon could not be stopped
+ # other if a failure occurred
+ start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
+ RETVAL="$?"
+ [ "$RETVAL" = 2 ] && return 2
+ # Wait for children to finish too if this is a daemon that forks
+ # and if the daemon is only ever run from this initscript.
+ # If the above conditions are not satisfied then add some other code
+ # that waits for the process to drop all resources that could be
+ # needed by services started subsequently. A last resort is to
+ # sleep for some time.
+ start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
+ [ "$?" = 2 ] && return 2
+ # Many daemons don't delete their pidfiles when they exit.
+ rm -f $PIDFILE
+ return "$RETVAL"
+}
+
+#
+# Function that sends a SIGHUP to the daemon/service
+#
+do_reload() {
+ #
+ # If the daemon can reload its configuration without
+ # restarting (for example, when it is sent a SIGHUP),
+ # then implement that here.
+ #
+ start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
+ return 0
+}
+
+case "$1" in
+ start)
+ [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME"
+ do_start
+ case "$?" in
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+ esac
+ ;;
+ stop)
+ [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
+ do_stop
+ case "$?" in
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+ esac
+ ;;
+ status)
+ status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
+ ;;
+ #reload|force-reload)
+ #
+ # If do_reload() is not implemented then leave this commented out
+ # and leave 'force-reload' as an alias for 'restart'.
+ #
+ #log_daemon_msg "Reloading $DESC" "$NAME"
+ #do_reload
+ #log_end_msg $?
+ #;;
+ restart|force-reload)
+ #
+ # If the "reload" option is implemented then remove the
+ # 'force-reload' alias
+ #
+ log_daemon_msg "Restarting $DESC" "$NAME"
+ do_stop
+ case "$?" in
+ 0|1)
+ do_start
+ case "$?" in
+ 0) log_end_msg 0 ;;
+ 1) log_end_msg 1 ;; # Old process is still running
+ *) log_end_msg 1 ;; # Failed to start
+ esac
+ ;;
+ *)
+ # Failed to stop
+ log_end_msg 1
+ ;;
+ esac
+ ;;
+ *)
+ #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
+ echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
+ exit 3
+ ;;
+esac
+
+:
Added: tools/knxdmxd/debian/knxdmxd.default
===================================================================
--- tools/knxdmxd/debian/knxdmxd.default (rev 0)
+++ tools/knxdmxd/debian/knxdmxd.default 2012-05-08 20:17:03 UTC (rev 799)
@@ -0,0 +1,17 @@
+# Defaults for knxdmxd initscript
+# sourced by /etc/init.d/knxdmxd
+# installed at /etc/default/knxdmxd by the maintainer scripts
+
+#
+# This is a POSIX shell fragment
+#
+
+#Usage: ./knxdmxd
+# -d Run as daemon/No debug output
+# -p <pidfile> PID-filename
+# -u <eib url> URL to contact eibd like local:/tmp/eib or ip:192.168.0.101
+# -c <config-file> Config-File
+
+# Additional options that are passed to the Daemon.
+DAEMON_ARGS="-d -c /etc/knxdmxd.conf -u local:/tmp/eib -p /var/run/knxdmxd.pid"
+
Added: tools/knxdmxd/debian/knxdmxd.install
===================================================================
--- tools/knxdmxd/debian/knxdmxd.install (rev 0)
+++ tools/knxdmxd/debian/knxdmxd.install 2012-05-08 20:17:03 UTC (rev 799)
@@ -0,0 +1,2 @@
+knxdmxd.conf etc/
+
Added: tools/knxdmxd/debian/rules
===================================================================
--- tools/knxdmxd/debian/rules (rev 0)
+++ tools/knxdmxd/debian/rules 2012-05-08 20:17:03 UTC (rev 799)
@@ -0,0 +1,13 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+%:
+ dh $@
Property changes on: tools/knxdmxd/debian/rules
___________________________________________________________________
Added: svn:executable
+ *
Added: tools/knxdmxd/debian/source/format
===================================================================
--- tools/knxdmxd/debian/source/format (rev 0)
+++ tools/knxdmxd/debian/source/format 2012-05-08 20:17:03 UTC (rev 799)
@@ -0,0 +1 @@
+3.0 (native)
Deleted: tools/knxdmxd/knxdmxd.c
===================================================================
--- tools/knxdmxd/knxdmxd.c 2012-05-08 20:07:12 UTC (rev 798)
+++ tools/knxdmxd/knxdmxd.c 2012-05-08 20:17:03 UTC (rev 799)
@@ -1,653 +0,0 @@
-/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
-/*
- * knxdmxd.c
- * Copyright (C) Jan N. Klug 2012 <jan...@ru...>
- * Daemon skeleton by Michael Markstaller 2011 <de...@wi...>
- *
- * knxdmxd is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * knxdmxd is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <unistd.h>
-#include <syslog.h>
-#include <string.h>
-#include <signal.h>
-#include <getopt.h>
-#include <json/json.h>
-
-#include <ola/Logging.h>
-#include <ola/DmxBuffer.h>
-#include <ola/StreamingClient.h>
-#include <ola/StringUtils.h>
-
-//#include <assert.h>
-
-#include <arpa/inet.h>
-//#include <netinet/in.h>
-//#include <sys/socket.h>
-
-#include <pthread.h>
-#include <eibclient.h>
-
-#include <iostream>
-#include <fstream>
-#include <map>
-
-/*#include <boost/property_tree/ptree.hpp>
-#include <boost/property_tree/xml_parser.hpp>
-#include <boost/foreach.hpp>*/
-
-#define DEBUG 1
-#define DAEMON_NAME "knxdmxd"
-#define USAGESTRING "\n"\
- "\t-d Run as daemon/No debug output\n"\
- "\t-p <pidfile> PID-filename\n"\
- "\t-u <eib url> URL to contact eibd like local:/tmp/eib or ip:192.168.0.101\n"\
- "\t-c <config-file> Config-File\n"
-#define NUM_THREADS 4
-#define MAX_ZONES 31
-#define RETRY_TIME 5
-#define BUFLEN 1024
-#define POLLING_INTERVAL 10
-#define FADING_INTERVAL 50000
-
-using namespace std;
-//namespace bpt = boost::property_tree;
-typedef vector<string> svector;
-
-void daemonShutdown();
-
-eibaddr_t readgaddr (const char *addr) {
- int a, b, c;
- if (sscanf (addr, "%d/%d/%d", &a, &b, &c) == 3)
- return ((a & 0x01f) << 11) | ((b & 0x07) << 8) | ((c & 0xff));
- if (sscanf (addr, "%d/%d", &a, &b) == 2)
- return ((a & 0x01f) << 11) | ((b & 0x7FF));
- if (sscanf (addr, "%x", &a) == 1)
- return a & 0xffff;
- syslog(LOG_WARNING,"invalid group address format %s", addr);
- daemonShutdown();
- return 0;
-}
-
-int readdaddr (const string addr) {
- int universe, channel;
- sscanf( (char*)addr.c_str(), "%d.%d", &universe, &channel);
- if (channel==-1) { // default universe is 1
- channel = universe;
- universe = 1;
- }
- return (universe << 9) + channel;
-}
-
-namespace knxdmxd {
-
-class Fixture {
- public:
- Fixture() {};
- Fixture(const string str);
-
- void Patch(map<int, svector >& patchMap, const string channel, const int DMX, const int KNX);
- void Patch(map<int, svector >& patchMap, const string channel, const string DMX, const string KNX);
- void SetFadeTime(const float t);
- void PatchFadeTime(const int KNX);
- void Update(const int KNX, const int val);
- void Refresh(map<int, ola::DmxBuffer>& output);
- private:
- string name;
- map <string, int> channelKNX;
- map <string, int> channelDMX;
- map <string, int> channelValue;
- map <string, float> channelFloatValue;
- float fadeTime, fadeStep;
- int fadeTimeKNX;
-};
-
-Fixture::Fixture(const string str) {
- name=str;
- syslog(LOG_DEBUG, "Created Fixture '%s'", (char *)str.c_str());
-}
-
-void Fixture::Patch(map<int, svector>& patchMap, const string channel, const int DMX, const int KNX) {
- channelKNX[channel] = KNX;
- channelDMX[channel] = DMX;
-
- syslog(LOG_DEBUG, "Fixture '%s': Patched channel '%s' (KNX %d to DMX %d) ", (char *)name.c_str(), (char *)channel.c_str(), KNX, DMX);
-
- svector assignedChannels = patchMap[KNX]; // check if already patched
- for (vector<string>::iterator i = assignedChannels.begin(); i != assignedChannels.end(); ++i) {
- if ((*i)==name) { // already patched;
- return;
- }
- }
-
- patchMap[KNX].push_back(name); // no, add to patchMap
-
-}
-
-void Fixture::Patch(map<int, svector>& patchMap, const string channel, const string DMX, const string KNX) {
- Patch(patchMap, channel, readdaddr(DMX), readgaddr((char *) KNX.c_str()));
-}
-
-void Fixture::SetFadeTime(const float t) {
- fadeTime = t;
- fadeStep = (t<=0) ? 256 : 256/(t*1e6/FADING_INTERVAL);
- syslog(LOG_DEBUG, "Fixture '%s': Set Fadetime to %.2f s'(%.2f steps/interval)", (char *)name.c_str(), fadeTime, fadeStep);
-}
-
-void Fixture::PatchFadeTime(const int KNX) {
- fadeTimeKNX = KNX;
-}
-
-void Fixture::Update(const int KNX, const int val) {
- for(std::map<string, int>::const_iterator i = channelKNX.begin(); i != channelKNX.end(); ++i) {
- if (i->second == KNX) {
- channelValue[i->first] = val;
- syslog(LOG_DEBUG, "Updated channel '%s' to %d", (char *)i->first.c_str(), val);
- }
- }
-}
-
-void Fixture::Refresh(map<int, ola::DmxBuffer>& output) {
- for(std::map<string, int>::const_iterator i = channelDMX.begin(); i != channelDMX.end(); ++i) {
- int dmxuniverse = (int) (i->second / 512) , dmxchannel = i->second % 512;
- int oldValue = output[dmxuniverse].Get(dmxchannel);
- int newValue = channelValue[i->first];
- if (oldValue<newValue) {
- channelFloatValue[i->first] += fadeStep;
- if (channelFloatValue[i->first]>newValue) {
- channelFloatValue[i->first] = newValue;
- }
- output[dmxuniverse].SetChannel(dmxchannel, (int) channelFloatValue[i->first]);
- }
- if (oldValue>newValue) {
- channelFloatValue[i->first] -= fadeStep;
- if (channelFloatValue[i->first]<newValue) {
- channelFloatValue[i->first] = newValue;
- }
- output[dmxuniverse].SetChannel(dmxchannel, (int) channelFloatValue[i->first]);
- }
- }
-}
-
-}
-
-pthread_mutex_t zonelock = PTHREAD_MUTEX_INITIALIZER;
-pthread_mutex_t initlock = PTHREAD_MUTEX_INITIALIZER;
-pthread_mutex_t standbylock = PTHREAD_MUTEX_INITIALIZER;
-
-string eibd_url = "local:/tmp/eib";
-string conf_file = "knxdmxd.conf";
-int pidFilehandle;
-string pidfilename = "/var/run/dmxknxd.pid";
-
-map<int, ola::DmxBuffer> dmxWriteBuffer;
-map<string, knxdmxd::Fixture> fixtureList;
-map<int, svector> patchMap;
-
-void daemonShutdown() {
- //FIXME: clean exit pthread_exit(NULL); pthread_cancel(..);
- //close(udpSocket);
- syslog(LOG_INFO, "%s daemon exiting", DAEMON_NAME);
- fprintf(stderr, "%s daemon exiting", DAEMON_NAME);
- close(pidFilehandle);
- unlink((char *)pidfilename.c_str());
- exit(EXIT_SUCCESS);
-}
-
-void signal_handler(int sig) {
- switch(sig) {
- case SIGHUP:
- syslog(LOG_WARNING, "Received SIGHUP signal.");
- break;
- case SIGTERM:
- syslog(LOG_WARNING, "Received SIGTERM signal.");
- daemonShutdown();
- break;
- case SIGINT:
- syslog(LOG_WARNING, "Received SIGINT signal.");
- daemonShutdown();
- break;
- default:
- syslog(LOG_WARNING, "Unhandled signal (%d) %s", sig, strsignal(sig));
- break;
- }
-}
-
-/*void *sendKNXdgram(int type, int dpt, eibaddr_t dest, unsigned char val) {
- int len = 0;
- EIBConnection *con;
- unsigned char buf[255] = { 0, 0x80 };
- buf[1] = type; //0x40 response, 0x80 write
- syslog(LOG_DEBUG,"Send KNX dgram Type %d DPT %d dest %d val %d",type,dpt,dest,val);
- con = EIBSocketURL ((char *)eibd_url.c_str());
- if (!con)
- syslog(LOG_WARNING,"sendknxdgram: Open failed");
- if (EIBOpenT_Group (con, dest, 1) == -1)
- syslog(LOG_WARNING,"sendknxdgram: Connect failed");
- switch (dpt)
- {
- case 0:
- len=1;
- //for(i = 0; i < strlen(val)/2; i++)
- //raw value: nothing for now
- break;
- case 1:
- buf[1] |= val & 0x3f;
- len=2;
- break;
- case 3:
- // EIS2/DPT3 4bit dim
- buf[1] |= val & 0x3f;
- len=2;
- break;
- case 5:
- buf[2] = val*255/100;
- len=3;
- break;
- case 51:
- case 5001:
- buf[2] = val;
- len=3;
- break;
- case 6:
- case 6001:
- //FIXME: This is basically wrong but as we get a uchar we expect the sender to know..
- buf[2] = val;
- len=3;
- break;
- }
-
- len = EIBSendAPDU (con, len, buf);
- if (len == -1)
- syslog (LOG_WARNING,"sendknxdram: Request failed");
- EIBClose (con);
- usleep(50*1000); //throttle a little (50ms/max 20tps)
- return 0;
-} */
-
-void sigtime(int signo)
-{
- for(std::map<string, knxdmxd::Fixture>::const_iterator i = fixtureList.begin(); i != fixtureList.end(); ++i) {
- fixtureList[i->first].Refresh(dmxWriteBuffer);
- }
- signal(SIGALRM, sigtime);
-}
-
-void *worker(void *) {
- syslog(LOG_DEBUG, "Internal worker thread started");
-
- signal(SIGALRM, sigtime);
-
- itimerval itm;
- itm.it_interval.tv_sec=0;
- itm.it_value.tv_sec = 0;
- itm.it_interval.tv_usec = FADING_INTERVAL; // 20 ms is enough
- itm.it_value.tv_usec = FADING_INTERVAL;
- setitimer(ITIMER_REAL,&itm,0);
-
- while (1) {
- sleep(POLLING_INTERVAL);
- }
-
- pthread_exit(NULL);
-}
-
-/*void *sendKNXresponse(eibaddr_t dest, int zone, int func) {
- syslog(LOG_DEBUG, "KNX response %d zone %d func %d", dest,zone,func);
- pthread_mutex_lock (&zonelock);
- switch (func) {
- case 0:
- case 20:
- sendKNXdgram (0x40,1,dest,zones[zone].zonepower);
- break;
- case 1:
- case 21:
- sendKNXdgram (0x40,51,dest,zones[zone].srcid);
- break;
- case 2:
- case 22:
- sendKNXdgram (0x40,5,dest,zones[zone].volume*2);
- break;
- case 3:
- case 23:
- if (zones[zone].bass<10)
- sendKNXdgram (0x40,51,dest,zones[zone].bass-10+256);
- else
- sendKNXdgram (0x40,51,dest,zones[zone].bass-10);
- break;
- case 4:
- case 24:
- if (zones[zone].treble<10)
- sendKNXdgram (0x40,51,dest,zones[zone].treble-10+256);
- else
- sendKNXdgram (0x40,51,dest,zones[zone].treble-10);
- break;
- case 5:
- case 25:
- sendKNXdgram (0x40,1,dest,zones[zone].loudness);
- break;
- case 6:
- case 26:
- if (zones[zone].balance<10)
- sendKNXdgram (0x40,51,dest,zones[zone].balance-10+256);
- else
- sendKNXdgram (0x40,51,dest,zones[zone].balance-10);
- break;
- case 27:
- sendKNXdgram (0x40,1,dest,zones[zone].partymode);
- break;
- case 28:
- sendKNXdgram (0x40,1,dest,zones[zone].dnd);
- break;
- case 29:
- sendKNXdgram (0x40,5,dest,zones[zone].onvolume*2);
- break;
- }
- pthread_mutex_unlock (&zonelock);
- return 0;
-}
-*/
-void *handleKNXdgram(eibaddr_t dest, unsigned char* buf, int len){
- unsigned char val;
- switch (buf[1] & 0xC0) {
- case 0x00:
-// sendKNXresponse (dest,zone+(controller*ZONES_PER_CONTROLLER),func);
- break;
- case 0x40:
- //FIXME: response dunno
- break;
- case 0x80:
- if (buf[1] & 0xC0) {
- if (len == 2)
- val = buf[1] & 0x3F;
- else
- val = buf[2];
- svector uFixtures = patchMap.find(dest)->second;
- for (unsigned int i=0; i < uFixtures.size(); i++) {
- fixtureList[uFixtures[i]].Update(dest, val);
- }
- syslog(LOG_DEBUG, "Received %d @ %d, updated %d fixtures", val, dest, (int) uFixtures.size());
- }
- break;
- }
- return 0;
-}
-
-
-void *olahandler(void *) { // thread just reliably sends the data to DMX via OLA
- syslog(LOG_DEBUG, "OLA sender thread started");
-
- ola::InitLogging(ola::OLA_LOG_WARN, ola::OLA_LOG_STDERR);
-
- while (1) { // retry forever
-
- ola::StreamingClient ola_client;
-
- if (!ola_client.Setup()) { // setup client
- syslog(LOG_WARNING, "OLA: Client setup failed");
- sleep(RETRY_TIME);
- continue;
- }
-
- syslog(LOG_INFO, "OLA: Client connected");
-
- while (1) { // loop forever
- int error_flag=0;
- for(std::map<int, ola::DmxBuffer>::const_iterator i = dmxWriteBuffer.begin(); i != dmxWriteBuffer.end(); ++i) {
- int universe = i->first;
- if (!ola_client.SendDmx(universe, i->second)) { // send all universes
- syslog(LOG_WARNING, "OLA: failed to send universe %d", universe);
- error_flag = 1;
- break; // something went wrong
- }
- }
- if (error_flag==1) {
- sleep(RETRY_TIME);
- break;
- }
-
- usleep(20000);
- }
-
- ola_client.Stop(); // close the client
- }
-
- pthread_exit(NULL);
-}
-
-
-void *knxhandler(void *) {
- syslog(LOG_DEBUG, "KNX reader thread started");
- int len;
- EIBConnection *con;
- eibaddr_t dest;
- eibaddr_t src;
- unsigned char buf[255];
-
- while (1) //retry infinite
- {
- con = EIBSocketURL ((char *)eibd_url.c_str());
- if (!con) {
- syslog(LOG_WARNING, "eibd: Open failed");
- sleep(RETRY_TIME);
- continue;
- }
-
- if (EIBOpen_GroupSocket (con, 0) == -1) {
- syslog(LOG_WARNING, "eibd: Connect failed");
- sleep(RETRY_TIME);
- continue;
- }
-
- while (1)
- {
- len = EIBGetGroup_Src (con, sizeof (buf), buf, &src, &dest);
- if (len == -1) {
- syslog(LOG_WARNING, "eibd: Read failed");
- sleep(RETRY_TIME);
- break;
- }
- if (len < 2) {
- syslog(LOG_WARNING, "eibd: Invalid Packet");
- break;
- }
- if (buf[0] & 0x3 || (buf[1] & 0xC0) == 0xC0) {
- syslog(LOG_WARNING, "eibd: Unknown APDU from %d to %d",src,dest);
- break;
- } else {
- if ( patchMap.count(dest)<=0 ) //not for us
- continue;
- handleKNXdgram(dest,buf,len);
- }
- }
- syslog(LOG_WARNING,"eibd: closed connection"); //break in read-loop
- EIBClose (con);
- }
- pthread_exit(NULL);
-}
-
-void load_config() {
-
-/* bpt::ptree config;
-
- read_xml(conf_file, config);
-
- BOOST_FOREACH( bpt::ptree::value_type const& e, config) {
- if( e.first == "fixture" ) {
- string name = e.second.get<std::string>("<xmlattr>.name");
- knxdmxd::Fixture f(name);
- BOOST_FOREACH( bpt::ptree::value_type const& c, e.second) {
- if (c.first == "channel") {
- f.Patch(patchMap, c.second.data(), c.second.get<std::string>("<xmlattr>.dmx"), c.second.get<std::string>("<xmlattr>.knx"));
- }
- if (c.first == "fading") {
- float t;
- string content = c.second.data();
- sscanf((char*) content.c_str(), "%f", &t);
- f.SetFadeTime((int) (t*1.e6));
- }
- }
- fixtureList[name] = f;
- }
-
- } */
- struct json_object *config;
-
- config = json_object_from_file((char *)conf_file.c_str());
- // first all fixtures
- struct json_object *fixtures = json_object_object_get(config, "fixtures");
-
- int fixturenum = json_object_array_length(fixtures);
-
- for (int i=0; i<fixturenum; i++) { // read all
- // get fixture
- struct json_object *fixture = json_object_array_get_idx(fixtures, i);
-
- // get name & create
- string fname(json_object_get_string(json_object_object_get(fixture, "name")));
- knxdmxd::Fixture f(fname);
-
- // get channels & patch them
- struct json_object *channels = json_object_object_get(fixture, "channels");
- int channelnum = json_object_array_length(channels);
- for (int j=0; j<channelnum; j++) { // read all
- // get channel
- struct json_object *channel = json_object_array_get_idx(channels, j);
- string cname(json_object_get_string(json_object_object_get(channel, "name")));
- string cdmx(json_object_get_string(json_object_object_get(channel, "dmx")));
- string cknx(json_object_get_string(json_object_object_get(channel, "knx")));
- f.Patch(patchMap, cname, cdmx, cknx);
- }
-
- // get fading
- struct json_object *fading = json_object_object_get(fixture, "fading");
- float ftime = (fading) ? json_object_get_double(json_object_object_get(fading, "time")) : 0;
- f.SetFadeTime(ftime);
-
- fixtureList[fname] = f;
- }
-
- return;
-}
-
-int main(int argc, char **argv) {
- int daemonize = 0;
- int c;
- //char *p;
- char pidstr[255];
-
- while ((c = getopt (argc, argv, "d:p:u:c:")) != -1)
- switch (c) {
- case 'd':
- daemonize = 1;
- break;
- case 'p':
- pidfilename.assign(optarg);
- break;
- case 'u':
- eibd_url.assign(optarg);
- break;
- case 'c':
- conf_file.assign(optarg);
- break;
- case '?':
- //FIXME: check arguments better, print_usage
- fprintf (stderr, "Unknown option `-%c'.\nUsage: %s %s", optopt, argv[0], USAGESTRING);
- return 1;
- default:
- abort ();
- }
-
- //FIXME: clean shutdown in sub-thread with signals?
- signal(SIGHUP, signal_handler);
- signal(SIGTERM, signal_handler);
- signal(SIGINT, signal_handler);
- signal(SIGQUIT, signal_handler);
-
- if (!daemonize) {
- setlogmask(LOG_UPTO(LOG_DEBUG));
- openlog(DAEMON_NAME, LOG_CONS | LOG_NDELAY | LOG_PERROR | LOG_PID, LOG_USER);
- syslog(LOG_DEBUG, "startup with debug; pidfile: %s, eibd: %s",
- (char *)pidfilename.c_str(), (char *)eibd_url.c_str());
- } else {
- setlogmask(LOG_UPTO(LOG_INFO));
- openlog(DAEMON_NAME, LOG_CONS, LOG_USER);
- }
-
- syslog(LOG_INFO, "using config-file %s", (char *)conf_file.c_str());
-
- load_config();
-
- pid_t pid, sid;
-
- if (daemonize) {
- syslog(LOG_INFO, "starting daemon");
-
- pid = fork();
- if (pid < 0) {
- exit(EXIT_FAILURE);
- }
- if (pid > 0) {
- exit(EXIT_SUCCESS);
- }
- umask(0);
- sid = setsid();
- if (sid < 0) {
- exit(EXIT_FAILURE);
- }
- if ((chdir("/")) < 0) {
- exit(EXIT_FAILURE);
- }
- close(STDIN_FILENO);
- close(STDOUT_FILENO);
- close(STDERR_FILENO);
- }
- //FIXME: output errors to stderr, change order
- pidFilehandle = open((char *)pidfilename.c_str(), O_RDWR|O_CREAT, 0600);
- if (pidFilehandle == -1 )
- {
- syslog(LOG_INFO, "Could not open pidfile %s, exiting", (char *)pidfilename.c_str());
- fprintf(stderr, "Could not open pidfile %s, exiting", (char *)pidfilename.c_str());
- exit(EXIT_FAILURE);
- }
- if (lockf(pidFilehandle,F_TLOCK,0) == -1)
- {
- syslog(LOG_INFO, "Could not lock pidfile %s, exiting", (char *)pidfilename.c_str());
- fprintf(stderr, "Could not lock pidfile %s, exiting", (char *)pidfilename.c_str());
- exit(EXIT_FAILURE);
- }
- sprintf(pidstr,"%d\n",getpid());
- c = write(pidFilehandle, pidstr, strlen(pidstr));
-
- int knxthread, olathread, workerthread;
- pthread_t threads[NUM_THREADS];
- // PTHREAD_CREATE_DETACHED?
- knxthread = pthread_create(&threads[1], NULL, knxhandler, NULL); //id, thread attributes, subroutine, arguments
- olathread = pthread_create(&threads[2], NULL, olahandler, NULL); //id, thread attributes, subroutine, arguments
- workerthread = pthread_create(&threads[3], NULL, worker, NULL); //id, thread attributes, subroutine, arguments
- syslog(LOG_DEBUG,"Threads created: %d %d %d",knxthread, olathread, workerthread);
- //TODO: Maybe another console/TCP-server/Logging thread?
- while (1) {
-// syslog(LOG_DEBUG, "%s daemon running", DAEMON_NAME);
- sleep(POLLING_INTERVAL);
- }
- syslog(LOG_INFO, "%s daemon exiting", DAEMON_NAME);
- // TODO: Free any allocated resources before exiting - we never get here though -> signal handler
- exit(EXIT_SUCCESS);
-}
Added: tools/knxdmxd/src/Makefile.am
===================================================================
--- tools/knxdmxd/src/Makefile.am (rev 0)
+++ tools/knxdmxd/src/Makefile.am 2012-05-08 20:17:03 UTC (rev 799)
@@ -0,0 +1,11 @@
+# what flags you want to pass to the C compiler & linker
+#CPPFLAGS = --pedantic -Wall -std=c99 -O2
+#LDFLAGS =
+
+knxdmxd_CXXFLAGS = $(OLA_CFLAGS) $(AM_CXXFLAGS)
+knxdmxd_LDADD = $(OLA_LIBS)
+
+# this lists the binaries to produce, the (non-PHONY, binary) targets in
+# the previous manual Makefile
+bin_PROGRAMS = knxdmxd
+knxdmxd_SOURCES = knxdmxd.cpp
Copied: tools/knxdmxd/src/knxdmxd.cpp (from rev 797, tools/knxdmxd/knxdmxd.c)
===================================================================
--- tools/knxdmxd/src/knxdmxd.cpp (rev 0)
+++ tools/knxdmxd/src/knxdmxd.cpp 2012-05-08 20:17:03 UTC (rev 799)
@@ -0,0 +1,653 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * knxdmxd.c
+ * Copyright (C) Jan N. Klug 2012 <jan...@ru...>
+ * Daemon skeleton by Michael Markstaller 2011 <de...@wi...>
+ *
+ * knxdmxd is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * knxdmxd is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <signal.h>
+#include <getopt.h>
+#include <json/json.h>
+
+#include <ola/Logging.h>
+#include <ola/DmxBuffer.h>
+#include <ola/StreamingClient.h>
+#include <ola/StringUtils.h>
+
+//#include <assert.h>
+
+#include <arpa/inet.h>
+//#include <netinet/in.h>
+//#include <sys/socket.h>
+
+#include <pthread.h>
+#include <eibclient.h>
+
+#include <iostream>
+#include <fstream>
+#include <map>
+
+/*#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/xml_parser.hpp>
+#include <boost/foreach.hpp>*/
+
+#define DEBUG 1
+#define DAEMON_NAME "knxdmxd"
+#define USAGESTRING "\n"\
+ "\t-d Run as daemon/No debug output\n"\
+ "\t-p <pidfile> PID-filename\n"\
+ "\t-u <eib url> URL to contact eibd like local:/tmp/eib or ip:192.168.0.101\n"\
+ "\t-c <config-file> Config-File\n"
+#define NUM_THREADS 4
+#define MAX_ZONES 31
+#define RETRY_TIME 5
+#define BUFLEN 1024
+#define POLLING_INTERVAL 10
+#define FADING_INTERVAL 50000
+
+using namespace std;
+//namespace bpt = boost::property_tree;
+typedef vector<string> svector;
+
+void daemonShutdown();
+
+eibaddr_t readgaddr (const char *addr) {
+ int a, b, c;
+ if (sscanf (addr, "%d/%d/%d", &a, &b, &c) == 3)
+ return ((a & 0x01f) << 11) | ((b & 0x07) << 8) | ((c & 0xff));
+ if (sscanf (addr, "%d/%d", &a, &b) == 2)
+ return ((a & 0x01f) << 11) | ((b & 0x7FF));
+ if (sscanf (addr, "%x", &a) == 1)
+ return a & 0xffff;
+ syslog(LOG_WARNING,"invalid group address format %s", addr);
+ daemonShutdown();
+ return 0;
+}
+
+int readdaddr (const string addr) {
+ int universe, channel;
+ sscanf( (char*)addr.c_str(), "%d.%d", &universe, &channel);
+ if (channel==-1) { // default universe is 1
+ channel = universe;
+ universe = 1;
+ }
+ return (universe << 9) + channel;
+}
+
+namespace knxdmxd {
+
+class Fixture {
+ public:
+ Fixture() {};
+ Fixture(const string str);
+
+ void Patch(map<int, svector >& patchMap, const string channel, const int DMX, const int KNX);
+ void Patch(map<int, svector >& patchMap, const string channel, const string DMX, const string KNX);
+ void SetFadeTime(const float t);
+ void PatchFadeTime(const int KNX);
+ void Update(const int KNX, const int val);
+ void Refresh(map<int, ola::DmxBuffer>& output);
+ private:
+ string name;
+ map <string, int> channelKNX;
+ map <string, int> channelDMX;
+ map <string, int> channelValue;
+ map <string, float> channelFloatValue;
+ float fadeTime, fadeStep;
+ int fadeTimeKNX;
+};
+
+Fixture::Fixture(const string str) {
+ name=str;
+ syslog(LOG_DEBUG, "Created Fixture '%s'", (char *)str.c_str());
+}
+
+void Fixture::Patch(map<int, svector>& patchMap, const string channel, const int DMX, const int KNX) {
+ channelKNX[channel] = KNX;
+ channelDMX[channel] = DMX;
+
+ syslog(LOG_DEBUG, "Fixture '%s': Patched channel '%s' (KNX %d to DMX %d) ", (char *)name.c_str(), (char *)channel.c_str(), KNX, DMX);
+
+ svector assignedChannels = patchMap[KNX]; // check if already patched
+ for (vector<string>::iterator i = assignedChannels.begin(); i != assignedChannels.end(); ++i) {
+ if ((*i)==name) { // already patched;
+ return;
+ }
+ }
+
+ patchMap[KNX].push_back(name); // no, add to patchMap
+
+}
+
+void Fixture::Patch(map<int, svector>& patchMap, const string channel, const string DMX, const string KNX) {
+ Patch(patchMap, channel, readdaddr(DMX), readgaddr((char *) KNX.c_str()));
+}
+
+void Fixture::SetFadeTime(const float t) {
+ fadeTime = t;
+ fadeStep = (t<=0) ? 256 : 256/(t*1e6/FADING_INTERVAL);
+ syslog(LOG_DEBUG, "Fixture '%s': Set Fadetime to %.2f s'(%.2f steps/interval)", (char *)name.c_str(), fadeTime, fadeStep);
+}
+
+void Fixture::PatchFadeTime(const int KNX) {
+ fadeTimeKNX = KNX;
+}
+
+void Fixture::Update(const int KNX, const int val) {
+ for(std::map<string, int>::const_iterator i = channelKNX.begin(); i != channelKNX.end(); ++i) {
+ if (i->second == KNX) {
+ channelValue[i->first] = val;
+ syslog(LOG_DEBUG, "Updated channel '%s' to %d", (char *)i->first.c_str(), val);
+ }
+ }
+}
+
+void Fixture::Refresh(map<int, ola::DmxBuffer>& output) {
+ for(std::map<string, int>::const_iterator i = channelDMX.begin(); i != channelDMX.end(); ++i) {
+ int dmxuniverse = (int) (i->second / 512) , dmxchannel = i->second % 512;
+ int oldValue = output[dmxuniverse].Get(dmxchannel);
+ int newValue = channelValue[i->first];
+ if (oldValue<newValue) {
+ channelFloatValue[i->first] += fadeStep;
+ if (channelFloatValue[i->first]>newValue) {
+ channelFloatValue[i->first] = newValue;
+ }
+ output[dmxuniverse].SetChannel(dmxchannel, (int) channelFloatValue[i->first]);
+ }
+ if (oldValue>newValue) {
+ channelFloatValue[i->first] -= fadeStep;
+ if (channelFloatValue[i->first]<newValue) {
+ channelFloatValue[i->first] = newValue;
+ }
+ output[dmxuniverse].SetChannel(dmxchannel, (int) channelFloatValue[i->first]);
+ }
+ }
+}
+
+}
+
+pthread_mutex_t zonelock = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t initlock = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t standbylock = PTHREAD_MUTEX_INITIALIZER;
+
+string eibd_url = "local:/tmp/eib";
+string conf_file = "knxdmxd.conf";
+int pidFilehandle;
+string pidfilename = "/var/run/dmxknxd.pid";
+
+map<int, ola::DmxBuffer> dmxWriteBuffer;
+map<string, knxdmxd::Fixture> fixtureList;
+map<int, svector> patchMap;
+
+void daemonShutdown() {
+ //FIXME: clean exit pthread_exit(NULL); pthread_cancel(..);
+ //close(udpSocket);
+ syslog(LOG_INFO, "%s daemon exiting", DAEMON_NAME);
+ fprintf(stderr, "%s daemon exiting", DAEMON_NAME);
+ close(pidFilehandle);
+ unlink((char *)pidfilename.c_str());
+ exit(EXIT_SUCCESS);
+}
+
+void signal_handler(int sig) {
+ switch(sig) {
+ case SIGHUP:
+ syslog(LOG_WARNING, "Received SIGHUP signal.");
+ break;
+ case SIGTERM:
+ syslog(LOG_WARNING, "Received SIGTERM signal.");
+ daemonShutdown();
+ break;
+ case SIGINT:
+ syslog(LOG_WARNING, "Received SIGINT signal.");
+ daemonShutdown();
+ break;
+ default:
+ syslog(LOG_WARNING, "Unhandled signal (%d) %s", sig, strsignal(sig));
+ break;
+ }
+}
+
+/*void *sendKNXdgram(int type, int dpt, eibaddr_t dest, unsigned char val) {
+ int len = 0;
+ EIBConnection *con;
+ unsigned char buf[255] = { 0, 0x80 };
+ buf[1] = type; //0x40 response, 0x80 write
+ syslog(LOG_DEBUG,"Send KNX dgram Type %d DPT %d dest %d val %d",type,dpt,dest,val);
+ con = EIBSocketURL ((char *)eibd_url.c_str());
+ if (!con)
+ syslog(LOG_WARNING,"sendknxdgram: Open failed");
+ if (EIBOpenT_Group (con, dest, 1) == -1)
+ syslog(LOG_WARNING,"sendknxdgram: Connect failed");
+ switch (dpt)
+ {
+ case 0:
+ len=1;
+ //for(i = 0; i < strlen(val)/2; i++)
+ //raw value: nothing for now
+ break;
+ case 1:
+ buf[1] |= val & 0x3f;
+ len=2;
+ break;
+ case 3:
+ // EIS2/DPT3 4bit dim
+ buf[1] |= val & 0x3f;
+ len=2;
+ break;
+ case 5:
+ buf[2] = val*255/100;
+ len=3;
+ break;
+ case 51:
+ case 5001:
+ buf[2] = val;
+ len=3;
+ break;
+ case 6:
+ case 6001:
+ //FIXME: This is basically wrong but as we get a uchar we expect the sender to know..
+ buf[2] = val;
+ len=3;
+ break;
+ }
+
+ len = EIBSendAPDU (con, len, buf);
+ if (len == -1)
+ syslog (LOG_WARNING,"sendknxdram: Request failed");
+ EIBClose (con);
+ usleep(50*1000); //throttle a little (50ms/max 20tps)
+ return 0;
+} */
+
+void sigtime(int signo)
+{
+ for(std::map<string, knxdmxd::Fixture>::const_iterator i = fixtureList.begin(); i != fixtureList.end(); ++i) {
+ fixtureList[i->first].Refresh(dmxWriteBuffer);
+ }
+ signal(SIGALRM, sigtime);
+}
+
+void *worker(void *) {
+ syslog(LOG_DEBUG, "Internal worker thread started");
+
+ signal(SIGALRM, sigtime);
+
+ itimerval itm;
+ itm.it_interval.tv_sec=0;
+ itm.it_value.tv_sec = 0;
+ itm.it_interval.tv_usec = FADING_INTERVAL; // 20 ms is enough
+ itm.it_value.tv_usec = FADING_INTERVAL;
+ setitimer(ITIMER_REAL,&itm,0);
+
+ while (1) {
+ sleep(POLLING_INTERVAL);
+ }
+
+ pthread_exit(NULL);
+}
+
+/*void *sendKNXresponse(eibaddr_t dest, int zone, int func) {
+ syslog(LOG_DEBUG, "KNX response %d zone %d func %d", dest,zone,func);
+ pthread_mutex_lock (&zonelock);
+ switch (func) {
+ case 0:
+ case 20:
+ sendKNXdgram (0x40,1,dest,zones[zone].zonepower);
+ break;
+ case 1:
+ case 21:
+ sendKNXdgram (0x40,51,dest,zones[zone].srcid);
+ break;
+ case 2:
+ case 22:
+ sendKNXdgram (0x40,5,dest,zones[zone].volume*2);
+ break;
+ case 3:
+ case 23:
+ if (zones[zone].bass<10)
+ sendKNXdgram (0x40,51,dest,zones[zone].bass-10+256);
+ else
+ sendKNXdgram (0x40,51,dest,zones[zone].bass-10);
+ break;
+ case 4:
+ case 24:
+ if (zones[zone].treble<10)
+ sendKNXdgram (0x40,51,dest,zones[zone].treble-10+256);
+ else
+ sendKNXdgram (0x40,51,dest,zones[zone].treble-10);
+ break;
+ case 5:
+ case 25:
+ sendKNXdgram (0x40,1,dest,zones[zone].loudness);
+ break;
+ case 6:
+ case 26:
+ if (zones[zone].balance<10)
+ sendKNXdgram (0x40,51,dest,zones[zone].balance-10+256);
+ else
+ sendKNXdgram (0x40,51,dest,zones[zone].balance-10);
+ break;
+ case 27:
+ sendKNXdgram (0x40,1,dest,zones[zone].partymode);
+ break;
+ case 28:
+ sendKNXdgram (0x40,1,dest,zones[zone].dnd);
+ break;
+ case 29:
+ sendKNXdgram (0x40,5,dest,zones[zone].onvolume*2);
+ break;
+ }
+ pthread_mutex_unlock (&zonelock);
+ return 0;
+}
+*/
+void *handleKNXdgram(eibaddr_t dest, unsigned char* buf, int len){
+ unsigned char val;
+ switch (buf[1] & 0xC0) {
+ case 0x00:
+// sendKNXresponse (dest,zone+(controller*ZONES_PER_CONTROLLER),func);
+ break;
+ case 0x40:
+ //FIXME: response dunno
+ break;
+ case 0x80:
+ if (buf[1] & 0xC0) {
+ if (len == 2)
+ val = buf[1] & 0x3F;
+ else
+ val = buf[2];
+ svector uFixtures = patchMap.find(dest)->second;
+ for (unsigned int i=0; i < uFixtures.size(); i++) {
+ fixtureList[uFixtures[i]].Update(dest, val);
+ }
+ syslog(LOG_DEBUG, "Received %d @ %d, updated %d fixtures", val, dest, (int) uFixtures.size());
+ }
+ break;
+ }
+ return 0;
+}
+
+
+void *olahandler(void *) { // thread just reliably sends the data to DMX via OLA
+ syslog(LOG_DEBUG, "OLA sender thread started");
+
+ ola::InitLogging(ola::OLA_LOG_WARN, ola::OLA_LOG_STDERR);
+
+ while (1) { // retry forever
+
+ ola::StreamingClient ola_client;
+
+ if (!ola_client.Setup()) { // setup client
+ syslog(LOG_WARNING, "OLA: Client setup failed");
+ sleep(RETRY_TIME);
+ continue;
+ }
+
+ syslog(LOG_INFO, "OLA: Client connected");
+
+ while (1) { // loop forever
+ int error_flag=0;
+ for(std::map<int, ola::DmxBuffer>::const_iterator i = dmxWriteBuffer.begin(); i != dmxWriteBuffer.end(); ++i) {
+ int universe = i->first;
+ if (!ola_client.SendDmx(universe, i->second)) { // send all universes
+ syslog(LOG_WARNING, "OLA: failed to send universe %d", universe);
+ error_flag = 1;
+ break; // something went wrong
+ }
+ }
+ if (error_flag==1) {
+ sleep(RETRY_TIME);
+ break;
+ }
+
+ usleep(20000);
+ }
+
+ ola_client.Stop(); // close the client
+ }
+
+ pthread_exit(NULL);
+}
+
+
+void *knxhandler(void *) {
+ syslog(LOG_DEBUG, "KNX reader thread started");
+ int len;
+ EIBConnection *con;
+ eibaddr_t dest;
+ eibaddr_t src;
+ unsigned char buf[255];
+
+ while (1) //retry infinite
+ {
+ con = EIBSocketURL ((char *)eibd_url.c_str());
+ if (!con) {
+ syslog(LOG_WARNING, "eibd: Open failed");
+ sleep(RETRY_TIME);
+ continue;
+ }
+
+ if (EIBOpen_GroupSocket (con, 0) == -1) {
+ syslog(LOG_WARNING, "eibd: Connect failed");
+ sleep(RETRY_TIME);
+ continue;
+ }
+
+ while (1)
+ {
+ len = EIBGetGroup_Src (con, sizeof (buf), buf, &src, &dest);
+ if (len == -1) {
+ syslog(LOG_WARNING, "eibd: Read failed");
+ sleep(RETRY_TIME);
+ break;
+ }
+ if (len < 2) {
+ syslog(LOG_WARNING, "eibd: Invalid Packet");
+ break;
+ }
+ if (buf[0] & 0x3 || (buf[1] & 0xC0) == 0xC0) {
+ syslog(LOG_WARNING, "eibd: Unknown APDU from %d to %d",src,dest);
+ break;
+ } else {
+ if ( patchMap.count(dest)<=0 ) //not for us
+ continue;
+ handleKNXdgram(dest,buf,len);
+ }
+ }
+ syslog(LOG_WARNING,"eibd: closed connection"); //break in read-loop
+ EIBClose (con);
+ }
+ pthread_exit(NULL);
+}
+
+void load_config() {
+
+/* bpt::ptree config;
+
+ read_xml(conf_file, config);
+
+ BOOST_FOREACH( bpt::ptree::value_type const& e, config) {
+ if( e.first == "fixture" ) {
+ string name = e.second.get<std::string>("<xmlattr>.name");
+ knxdmxd::Fixture f(name);
+ BOOST_FOREACH( bpt::ptree::value_type const& c, e.second) {
+ if (c.first == "channel") {
+ f.Patch(patchMap, c.second.data(), c.second.get<std::string>("<xmlattr>.dmx"), c.second.get<std::string>("<xmlattr>.knx"));
+ }
+ if (c.first == "fading") {
+ float t;
+ string content = c.second.data();
+ sscanf((char*) content.c_str(), "%f", &t);
+ f.SetFadeTime((int) (t*1.e6));
+ }
+ }
+ fixtureList[name] = f;
+ }
+
+ } */
+ struct json_object *config;
+
+ config = json_object_from_file((char *)conf_file.c_str());
+ // first all fixtures
+ struct json_object *fixtures = json_object_object_get(config, "fixtures");
+
+ int fixturenum = json_object_array_length(fixtures);
+
+ for (int i=0; i<fixturenum; i++) { // read all
+ // get fixture
+ struct json_object *fixture = json_object_array_get_idx(fixtures, i);
+
+ // get name & create
+ string fname(json_object_get_string(json_object_object_get(fixture, "name")));
+ knxdmxd::Fixture f(fname);
+
+ // get channels & patch them
+ struct json_object *channels = json_object_object_get(fixture, "channels");
+ int channelnum = json_object_array_length(channels);
+ for (int j=0; j<channelnum; j++) { // read all
+ // get channel
+ struct json_object *channel = json_object_array_get_idx(channels, j);
+ string cname(json_object_get_string(json_object_object_get(channel, "name")));
+ string cdmx(json_object_get_string(json_object_object_get(channel, "dmx")));
+ string cknx(json_object_get_string(json_object_object_get(channel, "knx")));
+ f.Patch(patchMap, cname, cdmx, cknx);
+ }
+
+ // get fading
+ struct json_object *fading = json_object_object_get(fixture, "fading");
+ float ftime = (fading) ? json_object_get_double(json_object_object_get(fading, "time")) : 0;
+ f.SetFadeTime(ftime);
+
+ fixtureList[fname] = f;
+ }
+
+ return;
+}
+
+int main(int argc, char **argv) {
+ int daemonize = 0;
+ int c;
+ //char *p;
+ char pidstr[255];
+
+ while ((c = getopt (argc, argv, "d:p:u:c:")) != -1)
+ switch (c) {
+ case 'd':
+ daemonize = 1;
+ break;
+ case 'p':
+ pidfilename.assign(optarg);
+ break;
+ case 'u':
+ eibd_url.assign(optarg);
+ break;
+ case 'c':
+ conf_file.assign(optarg);
+ break;
+ case '?':
+ //FIXME: check arguments better, print_usage
+ fprintf (stderr, "Unknown option `-%c'.\nUsage: %s %s", optopt, argv[0], USAGESTRING);
+ return 1;
+ default:
+ abort ();
+ }
+
+ //FIXME: clean shutdown in sub-thread with signals?
+ signal(SIGHUP, signal_handler);
+ signal(SIGTERM, signal_handler);
+ signal(SIGINT, signal_handler);
+ signal(SIGQUIT, signal_handler);
+
+ if (!daemonize) {
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+ openlog(DAEMON_NAME, LOG_CONS | LOG_NDELAY | LOG_PERROR | LOG_PID, LOG_USER);
+ syslog(LOG_DEBUG, "startup with debug; pidfile: %s, eibd: %s",
+ (char *)pidfilename.c_str(), (char *)eibd_url.c_str());
+ } else {
+ setlogmask(LOG_UPTO(LOG_INFO));
+ openlog(DAEMON_NAME, LOG_CONS, LOG_USER);
+ }
+
+ syslog(LOG_INFO, "using config-file %s", (char *)conf_file.c_str());
+
+ load_config();
+
+ pid_t pid, sid;
+
+ if (daemonize) {
+ syslog(LOG_INFO, "starting daemon");
+
+ pid = fork();
+ if (pid < 0) {
+ exit(EXIT_FAILURE);
+ }
+ if (pid > 0) {
+ exit(EXIT_SUCCESS);
+ }
+ umask(0);
+ sid = setsid();
+ if (sid < 0) {
+ exit(EXIT_FAILURE);
+ }
+ if ((chdir("/")) < 0) {
+ exit(EXIT_FAILURE);
+ }
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ }
+ //FIXME: output errors to stderr, change order
+ pidFilehandle = open((char *)pidfilename.c_str(), O_RDWR|O_CREAT, 0600);
+ if (pidFilehandle == -1 )
+ {
+ syslog(LOG_INFO, "Could not open pidfile %s, exiting", (char *)pidfilename.c_str());
+ fprintf(stderr, "Could not open pidfile %s, exiting", (char *)pidfilename.c_str());
+ exit(EXIT_FAILURE);
+ }
+ if (lockf(pidFilehandle,F_TLOCK,0) == -1)
+ {
+ syslog(LOG_INFO, "Could not lock pidfile %s, exiting", (char *)pidfilename.c_str());
+ fprintf(stderr, "Could not lock pidfile %s, exiting", (char *)pidfilename.c_str());
+ exit(EXIT_FAILURE);
+ }
+ sprintf(pidstr,"%d\n",getpid());
+ c = write(pidFilehandle, pidstr, strlen(pidstr));
+
+ int knxthread, olathread, workerthread;
+ pthread_t threads[NUM_THREADS];
+ // PTHREAD_CREATE_DETACHED?
+ knxthread = pthread_create(&threads[1], NULL, knxhandler, NULL); //id, thread attributes, subroutine, arguments
+ olathread = pthread_create(&threads[2], NULL, olahandler, NULL); //id, thread attributes, subroutine, arguments
+ workerthread = pthread_create(&threads[3], NULL, worker, NULL); //id, thread attributes, subroutine, arguments
+ syslog(LOG_DEBUG,"Threads created: %d %d %d",knxthread, olathread, workerthread);
+ //TODO: Maybe another console/TCP-server/Logging thread?
+ while (1) {
+// syslog(LOG_DEBUG, "%s daemon running", DAEMON_NAME);
+ sleep(POLLING_INTERVAL);
+ }
+ syslog(LOG_INFO, "%s daemon exiting", DAEMON_NAME);
+ // TODO: Free any allocated resources before exiting - we never get here though -> signal handler
+ exit(EXIT_SUCCESS);
+}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-08 20:07:18
|
Revision: 798
http://openautomation.svn.sourceforge.net/openautomation/?rev=798&view=rev
Author: pfry
Date: 2012-05-08 20:07:12 +0000 (Tue, 08 May 2012)
Log Message:
-----------
Modified Paths:
--------------
wiregate/plugin/generic/conf.d/Heizungsregler.conf
Modified: wiregate/plugin/generic/conf.d/Heizungsregler.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Heizungsregler.conf 2012-05-08 04:36:03 UTC (rev 797)
+++ wiregate/plugin/generic/conf.d/Heizungsregler.conf 2012-05-08 20:07:12 UTC (rev 798)
@@ -76,15 +76,15 @@
# Raumtemperaturfuehler (Liste oder nur einer)
# sensor => ['1/2/3','1/2/3','1/2/3'],
- sensor => $eibgaconf{TA_A2}{ga},
+ sensor => $eibgaconf{TL_WZ_Sofa}{ga},
# Vorlauftemperatur, kann auch pro Kreislauf definiert sein
-# inflow => $eibgaconf{TO_Zentralvorlauf}{ga},
+ inflow => $eibgaconf{TO_Zentralvorlauf}{ga},
# Fensterkontaktabfrage
window => [$eibgaconf{KF_WZ_West_L}{ga}, $eibgaconf{KF_WZ_West_R}{ga}],
- actuator => $eibgaconf{KR_E_Fermax_Licht}{ga}, # GA zum Setzen der Solltemperatur
+ actuator => $eibgaconf{VS_WZ_Mitte}{ga}, # GA zum Setzen der Stellventils
# Heizkreislaeufe, aktuell werden alle gleich gesteuert
# (Ausgleich fuer gleiche Estrichtemp. noch nicht implementiert)
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-08 04:36:10
|
Revision: 797
http://openautomation.svn.sourceforge.net/openautomation/?rev=797&view=rev
Author: pfry
Date: 2012-05-08 04:36:03 +0000 (Tue, 08 May 2012)
Log Message:
-----------
Added Paths:
-----------
wiregate/plugin/generic/Heizungsregler.pl
wiregate/plugin/generic/conf.d/Heizungsregler.conf
Removed Paths:
-------------
wiregate/plugin/generic/Translator.pl
wiregate/plugin/generic/conf.d/Translator.conf
Added: wiregate/plugin/generic/Heizungsregler.pl
===================================================================
--- wiregate/plugin/generic/Heizungsregler.pl (rev 0)
+++ wiregate/plugin/generic/Heizungsregler.pl 2012-05-08 04:36:03 UTC (rev 797)
@@ -0,0 +1,764 @@
+##################
+# Heizungsregler #
+##################
+# Wiregate-Plugin
+# (c) 2012 Fry under the GNU Public License
+
+# $plugin_info{$plugname.'_cycle'}=0; return "deaktiviert";
+
+use POSIX qw(floor);
+use Math::Round qw(nearest);
+
+my $use_short_names=0; # 1 fuer GA-Kuerzel (erstes Wort des GA-Namens), 0 fuer die "nackte" Gruppenadresse
+
+# eibgaconf fixen falls nicht komplett indiziert
+if($use_short_names && !exists $eibgaconf{ZV_Uhrzeit})
+{
+ for my $ga (grep /^[0-9\/]+$/, keys %eibgaconf)
+ {
+ $eibgaconf{$ga}{ga}=$ga;
+ my $name=$eibgaconf{$ga}{name};
+ next unless defined $name;
+ $eibgaconf{$name}=$eibgaconf{$ga};
+
+ next unless $name=~/^\s*(\S+)/;
+ my $short=$1;
+ $short='ZV_'.$1 if $eibgaconf{$ga}{name}=~/^Zeitversand.*(Uhrzeit|Datum)/;
+
+ $eibgaconf{$ga}{short}=$short;
+ $eibgaconf{$short}=$eibgaconf{$ga};
+ }
+}
+
+# Aufrufgrund ermitteln
+my $event=undef;
+if (!$plugin_initflag)
+{ $event='restart'; } # Restart des daemons / Reboot
+#elsif ((stat('/etc/wiregate/plugin/generic/' . $plugname))[9] > time()-2)
+# ab PL30:
+elsif ($plugin_info{$plugname.'_lastsaved'} > $plugin_info{$plugname.'_last'})
+{ $event='modified'; } # Plugin modifiziert
+elsif (%msg) { $event='bus'; } # Bustraffic
+#elsif ($fh) { $event='socket'; } # Netzwerktraffic
+else { $event='cycle'; } # Zyklus
+
+# Konfigurationsfile einlesen
+my $conf=$plugname; $conf=~s/\.pl$/.conf/;
+$conf="/etc/wiregate/plugin/generic/conf.d/$conf";
+my %house=();
+my $err=read_from_house_config();
+return $err if $err;
+
+# Initialisierung
+my %dyn=();
+my $retval='';
+my $t=time();
+
+if($event=~/restart|modified/)
+{
+ # Cleanup aller Variablen
+ for my $k (grep /^$plugname\_/, keys %plugin_info)
+ {
+ delete $plugin_info{$k};
+ }
+
+ # Alle Controller-GAs abonnieren, Reglerstati initialisieren
+ for my $r (grep ref($house{$_}), keys %house)
+ {
+ $plugin_subscribe{$house{$r}{control}}{$plugname}=1;
+ RESET($r);
+ }
+
+ $plugin_info{$plugname.'_cycle'}=$house{cycle};
+
+ store_to_plugin_info(\%dyn);
+
+ $event='cycle';
+}
+
+%dyn=recall_from_plugin_info();
+
+# Zyklischer Aufruf - Regelung
+if($event=~/cycle/)
+{
+ my $Vreq=undef;
+ my $anynews=0;
+ for my $r (grep ref($house{$_}), keys %house)
+ {
+ if($dyn{$r}{mode} eq 'ON')
+ {
+ # PID-Regler
+ my ($T,$T0,$U,$Vr)=PID($r);
+ $retval.=sprintf "$r\->%.1f(%.1f)%d%% ", $T?$T:"T?", $T0, 100*$U;
+ $anynews=1;
+ $Vreq=$Vr if defined $Vr && (!defined $Vreq || $Vr>$Vreq);
+ }
+ elsif($dyn{$r}{mode} eq 'OPTIMIZE')
+ {
+ # Optimierung der PID-Parameter durch Ermittlung der Sprungantwort
+ $retval.="$r\->".OPTIMIZE($r);
+ $anynews=1;
+ $Vreq=$house{inflow_max} if defined $house{inflow_max};
+ }
+ elsif($dyn{$r}{mode} eq 'OFF')
+ {
+ $retval.="$r\->OFF ";
+ }
+ }
+
+ if(defined $Vreq)
+ {
+ $Vreq=$house{inflow_max} if defined $house{inflow_max} && $Vreq>$house{inflow_max};
+ knx_write($house{inflow_control},$Vreq,9.001) if defined $house{inflow_control};
+ $retval.=sprintf "Vreq=%d", $Vreq;
+ $anynews=1;
+ }
+
+ $retval=~s/\s*$//; # Space am Ende entfernen
+
+ return unless $anynews;
+}
+elsif($event=~/bus/)
+{
+ return if $msg{apci} eq 'A_GroupValue_Response';
+
+ # Aufruf durch GA - neue Wunschtemperatur
+ my $ga=$msg{dst};
+
+ # erstmal den betroffenen Raum finden
+ my @rms=(grep ref($house{$_}) && $house{$_}{control} eq $ga, keys %house);
+ my $r=shift @rms;
+
+ # $r ist undef falls obige Schleife fertig durchlaufen wurde
+ if(defined $r)
+ {
+ my $T0=0;
+ $T0 = $msg{value} if defined $msg{value};
+ my $mode=$dyn{$r}{mode};
+
+ # Jemand moechte einen Sollwert wissen
+ if($msg{apci} eq 'A_GroupValue_Read')
+ {
+ $T0=$dyn{$r}{T0};
+ $T0=$dyn{$r}{T0old} if $dyn{mode} eq 'OPTIMIZE';
+ knx_write($ga,$T0,9.001);
+ return;
+ }
+
+ # spezielle Temperaturwerte sind 0=>OFF und -1=>OPTIMIZE
+ if($T0==0)
+ {
+ RESET($r);
+ writeactuators($r,0);
+ $dyn{$r}{mode}='OFF';
+ $retval.="$r\->OFF";
+ }
+ elsif($T0==-1)
+ {
+ return if $dyn{$r}{mode} eq 'OPTIMIZE'; # Entprellen
+
+ # Initialisierung der Optimierungsfunktion
+ $dyn{$r}{mode}='OPTIMIZE';
+ $dyn{$r}{T0old}=$dyn{$r}{T0};
+ writeactuators($r,0);
+ my ($T,$V,$E,$R,$spread,$window)=readsensors($r);
+
+ $retval.=sprintf "$r\->OPT", $T;
+ }
+ else # neue Wunschtemperatur
+ {
+ return if $dyn{$r}{T0} == $T0; # Entprellen
+
+ RESET($r) if $mode eq 'OPTIMIZE'; # Optimierung unterbrochen
+ $dyn{$r}{mode}='ON'; # ansonsten uebrige Werte behalten
+ $dyn{$r}{T0}=$T0;
+ my ($T,$T0,$U,$Vr)=PID($r);
+ $retval.=sprintf "$r\->%.1f(%.1f)%d%%", $T, $T0, 100*$U;
+ }
+ }
+ else
+ {
+ # GA-Abonnement loeschen
+ delete $plugin_subscribe{$ga}{$plugname};
+ }
+}
+
+# Speichere Statusvariablen aller Regler
+store_to_plugin_info(\%dyn);
+
+return $retval eq '' ? undef : $retval;
+
+
+########## Datenpersistenz - Speichern und Einlesen ###############
+
+sub store_to_plugin_info
+{
+ my $dyn=shift;
+
+ # Alle Laufzeitvariablen im Hash %{$dyn}
+ # in das (flache) Hash plugin_info schreiben
+ for my $r (keys %{$dyn})
+ {
+ for my $k (grep /^$plugname\_$r\_(temps|times|Uvals)_/, keys %plugin_info)
+ {
+ delete $plugin_info{$k};
+ }
+
+ for my $v (keys %{$dyn->{$r}})
+ {
+ next if $v=~/^(temps|times|Uvals)$/; # Arrays
+ $plugin_info{$plugname.'_'.$r.'_'.$v}=$dyn->{$r}{$v};
+ }
+
+ for my $array (qw(temps times Uvals))
+ {
+ my $arr=$dyn->{$r}{$array};
+ next unless $arr;
+ for my $i (0..$#{$arr})
+ {
+ $plugin_info{$plugname.'_'.$r.'_'.$array.'_'.$i}=$arr->[$i];
+ }
+ }
+ }
+}
+
+sub recall_from_plugin_info
+{
+ my %dyn=();
+
+ for my $k (grep /^$plugname\_/, keys %plugin_info)
+ {
+ next unless $k=~/^$plugname\_([^_]+)\_(\S+)$/;
+ my $r=$1; my $v=$2;
+
+ unless($v=~/^(temps|times|Uvals)_([0-9]+)$/)
+ {
+ $dyn{$r}{$v}=$plugin_info{$k};
+ }
+ else
+ {
+ my $array=$1; my $i=$2;
+ $dyn{$r}{$array}[$i]=$plugin_info{$k};
+ }
+ }
+
+ return %dyn;
+}
+
+sub read_from_house_config
+{
+ open CONFIG, "<$conf" || return "no config found";
+ my @lines = <CONFIG>;
+ close CONFIG;
+ eval("@lines");
+ return "config error" if $@;
+}
+
+sub store_to_house_config
+{
+ my $r=shift; # der betreffende Raum im Haus
+
+ open CONFIG, ">>$conf";
+ print CONFIG "\$house{$r}{pid}={";
+ for my $k (sort keys %{$house{$r}{pid}})
+ {
+ print CONFIG sprintf "$k=>%.2f, ", $house{$r}{pid}{$k} unless $k eq 'date';
+ print CONFIG "$k=>'$house{$r}{pid}{date}'," if $k eq 'date';
+ }
+ print CONFIG "};\n";
+ close CONFIG;
+}
+
+########## Kommunikation mit Sensoren und Aktoren ###############
+
+sub readsensors
+{
+ my $r=shift; # interessierender Raum
+ my @substructures=();
+
+ push @substructures, values %{$house{$r}->{circ}} if defined $house{$r}->{circ};
+ push @substructures, $house{$r};
+
+ my %T=();
+ my %R=();
+
+ for my $type (qw(sensor inflow floor outflow window))
+ {
+ my $dpt=$type eq 'window' ? 1 : 9;
+
+ # Alle Sensoren eines Typs im gesamten Raum einlesen
+ for my $ss (@substructures)
+ {
+ if(defined $ss->{$type})
+ {
+ my $sensorlist=(ref $ss->{$type})?$ss->{$type}:[$ss->{$type}];
+ for my $s (@{$sensorlist})
+ {
+ unless(defined $T{$type}{$s})
+ {
+ $T{$type}{$s}=knx_read($s,$house{cycle},$dpt);
+ delete $T{$type}{$s} unless defined $T{$type}{$s};
+ }
+ }
+ }
+ }
+
+ # Ueber alle Sensoren mitteln, dabei wird jeder Sensor genau einmal
+ # beruecksichtigt, auch wenn er in der Konfiguration mehrfach steht
+ my $n=0;
+ for my $k (keys %{$T{$type}})
+ {
+ if(defined $T{$type}{$k})
+ {
+ if($type eq 'window')
+ {
+ $R{$type}=1 if int($T{$type}{$k})==1;
+ }
+ else
+ {
+ $R{$type}+=$T{$type}{$k};
+ $n++;
+ }
+ }
+ }
+ $R{$type}/=$n if defined $R{$type} && $type ne 'window';
+ }
+
+ # Falls Fensterkontakte nicht lesbar -> Fenster als geschlossen annehmen
+ $R{window}=0 unless defined $R{window};
+
+ # outflow (Ruecklauf) und floor (Estrich) nehmen wir als gleich an,
+ # falls nicht beide Werte vorhanden sind. Das sollte immer noch besser
+ # sein als der globale Hauswert, um danach den Spread zu berechnen.
+ unless(defined $R{outflow} && defined $R{floor})
+ {
+ $R{outflow}=$R{floor} if defined $R{floor};
+ $R{floor}=$R{outflow} if defined $R{outflow};
+ }
+
+ # Falls Vor- oder Ruecklauf nicht fuer den Raum definiert,
+ # nehmen wir die Hauswerte - falls diese verfuegbar sind
+ for my $type (qw(inflow outflow))
+ {
+ if(!defined $R{$type} && defined $house{$type})
+ {
+ $R{$type} = knx_read($house{$type},$house{cycle},9);
+ delete $R{$type} unless $R{$type};
+ }
+ }
+
+ # Jetzt Spread (Spreizung) berechnen, falls alle Daten verfuegbar
+ if(defined $R{inflow} && defined $R{outflow})
+ {
+ $R{spread}=$R{inflow}-$R{outflow};
+ }
+
+ # und wenn alle Stricke reissen, bleibt der vorkonfigurierte Wert
+ $R{spread}=$house{spread} unless defined $R{spread};
+
+ return @R{qw(sensor inflow floor outflow spread window)};
+}
+
+sub writeactuators
+{
+ my $r=shift; # Raum mit Substruktur
+ my $U=shift; # Ventileinstellung
+
+# plugin_log($plugname, "Trying to write $r, $U");
+
+ my @substructures=values %{$house{$r}->{circ}} if defined $house{$r}->{circ};
+ push @substructures, $house{$r};
+
+ for my $ss (@substructures)
+ {
+ if(defined $ss->{actuator})
+ {
+ unless(ref $ss->{actuator})
+ {
+ knx_write($ss->{actuator},100*$U,5.001); # DPT NOCH UNKLAR ########
+ }
+ else
+ {
+ for my $s (@{$ss->{actuator}})
+ {
+ knx_write($s,100*$U,5.001);
+ }
+ }
+ }
+ }
+# plugin_log($plugname, "Done trying to write $r, $U");
+}
+
+########## PID-Regler #####################
+
+sub RESET
+{
+ my $r=shift; # zu regelnder Raum im Haus
+
+ $dyn{$r} = {
+ mode=>'OFF', T0=>20, Told=>0, told=>$t, IS=>0, DF=>0,
+ temps=>[], times=>[], Uvals=>[], U=>0
+ };
+}
+
+sub PID
+{
+ my $r=shift; # zu regelnder Raum im Haus
+ my ($T,$V,$E,$R,$spread,$window)=readsensors($r);
+
+ # Ohne Temperaturmessung und Spread keine Regelung -> aufgeben
+ my ($mode,$T0,$Told,$told,$IS,$DF,$temps,$times,$Uvals,$U)
+ = @{$dyn{$r}}{qw(mode T0 Told told IS DF temps times Uvals U)};
+
+ return ($T,$T0,$U,0) unless $T && $spread;
+
+ # Regelparameter einlesen
+ my ($Tv,$Tn,$lim,$prop,$refspread)=(5,30,1,1,10); # Defaults
+
+ ($Tv,$Tn,$lim,$prop,$refspread)
+ =@{$house{$r}{pid}}{qw(Tv Tn lim prop refspread)}
+ if defined $house{$r}{pid};
+
+ $Tv*=60; $Tn*=60; # in Sekunden umrechnen
+
+ # Anzahl Datenpunkte fuer Steigungsberechnung
+ my $S1=12; $S1=$house{mindata} if defined $house{mindata};
+
+ # Anzahl Datenpunkte fuer Ermittlung neuer Vorhaltetemperatur
+ my $S2=$S1;
+
+ push @{$temps},$T; shift @{$temps} while @{$temps}>$S1;
+ push @{$times},$t; shift @{$times} while @{$times}>$S1;
+
+ if($window)
+ {
+ $U=0; # Heizung aus falls Fenster offen
+ push @{$Uvals}, $U; shift @{$Uvals} while @{$Uvals}>$S2;
+
+ $dyn{$r} = {
+ mode=>$mode, T0=>$T0, Told=>$T, told=>$t, IS=>$IS, DF=>$DF,
+ temps=>$temps, times=>$times, Uvals=>$Uvals, U=>$U
+ };
+
+ writeactuators($r,$U);
+ return ($T,$T0,$U,0);
+ }
+
+ # Skalierung fuer aktuellen Spread
+ my $coeff = $refspread/($spread*$prop);
+
+ # Proportionalteil (P)
+ my $P = $T0 - $T;
+
+ # Integralteil (I)
+ $IS += $P * ($t - $told) / $Tn;
+
+ # kein negativer I-Anteil bei reiner Heizung (nur fuer Klimaanlage erforderlich)
+ $IS=0 if $IS<0;
+
+ # Begrenzung des I-Anteils zur Vermeidung von Ueberschwingern ("wind-up")
+ $IS=+$lim/$coeff if $IS>+$lim/$coeff;
+
+ # Differentialteil (D) - gemittelt wegen moeglichem Sensorrauschen
+ $S1=scalar(@{$times});
+ if($S1>=2)
+ {
+ my ($SX,$SX2,$SY,$SXY)=(0,0,0,0);
+ for my $i (0..$S1-1)
+ {
+ my $time=$times->[$i]-$times->[0];
+ $SX+=$time;
+ $SX2+=$time*$time;
+ $SY+=$temps->[$i];
+ $SXY+=$time*$temps->[$i];
+ }
+ $DF = - $Tv * ($S1*$SXY - $SX*$SY)/($S1*$SX2 - $SX*$SX);
+ }
+# Fuer den Fall S1==2 fuehrt die obige Regression zum gleichen Ergebnis wie:
+# $DF = - $Tv * ($T - $Told) / ($t - $told);
+
+ # und alles zusammen, skaliert mit der aktuellen Spreizung
+ $U = ($P + $IS + $DF) * $coeff;
+
+ # Stellwert begrenzen auf 0-1
+ $U=1 if $U>1;
+ $U=0 if $U<0;
+ push @{$Uvals}, $U; shift @{$Uvals} while @{$Uvals}>$S2;
+
+ # Wunsch-Vorlauftemperatur ermitteln
+ my $Vr=$V;
+ if(defined $Vr)
+ {
+ $Vr=$T0+3 if $Vr<$T0+3;
+ my $Uavg=0; $Uavg+=$_ foreach (@{$Uvals}); $Uavg/=scalar(@{$Uvals});
+
+ $Vr+=1 if $Uavg>0.9;
+ $Vr-=1 if $Uavg<0.6 && $V>$T0+6 && $spread>6;
+ $Vr-=1 if $Uavg<0.75 && $V>$T0+5 && $spread>5;
+ $Vr-=1 if $Uavg<0.7 && $V>$T0+4 && $spread>4;
+ $Vr-=1 if $Uavg<0.6 && $V>$T0+3 && $spread>3;
+ }
+
+ # Variablen zurueckschreiben
+ $dyn{$r} = {
+ mode=>$mode, T0=>$T0, Told=>$T, told=>$t, IS=>$IS, DF=>$DF,
+ temps=>$temps, times=>$times, Uvals=>$Uvals, U=>$U
+ };
+
+ # Ventil einstellen
+ writeactuators($r,$U);
+
+ # Ist, Soll, Stellwert, Spread, Wunsch-Vorlauftemp.
+ return ($T,$T0,$U,$Vr);
+}
+
+########## Optimierungsroutine #####################
+
+sub OPTIMIZE
+{
+ my $r=shift;
+ my ($T,$V,$E,$R,$spread,$window)=readsensors($r);
+
+ # Ohne Temperaturmessung und Spread keine Regelung -> aufgeben
+ return "(OPT) " unless defined $T && defined $spread;
+
+ # Praktische Abkuerzungen fuer Statusvariablen
+ my ($mode,$phase,$T0old) = @{$dyn{$r}}{qw(mode phase T0old)};
+
+ # Falls Fenster offen -> Abbruch, Heizung aus und Regler resetten
+ if($window)
+ {
+ if($phase ne 'COOL')
+ {
+ RESET($r);
+ $dyn{$r}{mode}='ON';
+ $dyn{$r}{T0}=$T0old;
+ return "FAILED:WINDOW ";
+ }
+ else
+ {
+ # Tn, Tv, prop und refspread wurden am Ende der HEAT-Periode bereits berechnet
+ # Wir nutzen die "cooling"-Periode sowieso nicht.
+ # Also Parameter ins Konfig-File schreiben.
+ my ($Tn, $Tv, $prop, $refspread) = @{$dyn{$r}}{qw(Tn Tv prop refspread)};
+ my $date=`/bin/date +"%F %X"`; chomp $date;
+ my $lim=0.5;
+ $house{$r}{pid}={Tv=>$Tv, Tn=>$Tn, lim=>$lim, prop=>$prop, refspread=>$refspread, date=>$date};
+ store_to_house_config($r);
+ }
+ }
+
+ # Warte bis Therme voll aufgeheizt
+ # das Aufheizen der Therme geschieht in der Hauptschleife
+ $phase='WAIT' unless defined $phase;
+
+ if($phase eq 'WAIT')
+ {
+ if(defined $V && defined $house{inflow_max} && $V<$house{inflow_max}-3)
+ {
+ writeactuators($r,0); # noch nicht heizen
+ return "WAIT(V=$V) ";
+ }
+
+ # Falls Heizung noch nicht voll an, jetzt starten
+ writeactuators($r,1); # maximal heizen
+
+ # Temperaturaufzeichnung beginnen
+ $dyn{$r} = {
+ mode=>$mode, phase=>'HEAT',
+ T0old=>$T0old, told=>0, optstart=>$t,
+ maxpos=>0, maxslope=>0,
+ sumspread=>$spread, temps=>[0], times=>[$T]
+ };
+
+ return sprintf("%.1f(HEAT)%.1f ",$T,$spread);
+ }
+
+ my ($optstart, $sumspread, $told, $temps, $times)
+ = @{$dyn{$r}}{qw(optstart sumspread told temps times)};
+
+ my $tp=$t-$optstart;
+
+ # falls aus irgendeinem Grund zu frueh aufgerufen, tu nichts
+ return sprintf("%.1f(", $T).'SKP'.sprintf(")%.1f ",$spread)
+ if $tp-$told<$house{cycle}/2;
+
+ # Temperaturkurve aufzeichnen
+ push @{$times}, $tp;
+ push @{$temps}, $T;
+ $sumspread+=$spread;
+
+ # Anzahl Datenpunkte fuer Steigungsberechnung. Hier verdoppelt, weil wir
+ # mehr Praezision brauchen.
+ my $S1=25; $S1=2*$house{mindata} if defined $house{mindata};
+
+ if(scalar(@{$temps})<=$S1)
+ {
+ $dyn{$r} = {
+ mode=>$mode, phase=>$phase,
+ T0old=>$T0old, told=>$tp, optstart=>$optstart,
+ maxpos=>0, maxslope=>0,
+ sumspread=>$sumspread, temps=>$temps, times=>$times
+ };
+
+ return sprintf("%.1f(", $T).'OPT'.sprintf(")%.1f ",$spread);
+ }
+
+ # Steigung der Temperaturkurve durch Regression bestimmen
+ my ($SX,$SY,$SY2,$SXY)=(0,0,0,0);
+
+ for my $i (-$S1..-1)
+ {
+ $SX+=$temps->[$i];
+ $SY+=$times->[$i];
+ $SY2+=$times->[$i]*$times->[$i];
+ $SXY+=$times->[$i]*$temps->[$i];
+ }
+
+ my $slope = ($S1*$SXY - $SX*$SY)/($S1*$SY2 - $SY*$SY);
+
+ if($phase eq 'HEAT')
+ {
+ my ($maxpos, $maxslope) = @{$dyn{$r}}{qw(maxpos maxslope)};
+
+ if($slope<=0 || $maxslope<=0 || $slope>=0.7*$maxslope)
+ {
+ my $retval='';
+
+ if($slope>$maxslope)
+ {
+ $maxslope = $slope;
+ $maxpos = nearest(1,$#{$temps}-$S1/2);
+ $retval=sprintf "%.2fKph",$slope*60*60;
+ }
+ elsif($slope>0)
+ {
+ $retval=sprintf "%.2fKph=%d%%", $slope*60*60, 100*$slope/$maxslope;
+ }
+ else
+ {
+ $retval=sprintf "%.2fKph",$slope*60*60;
+ }
+
+ # Statusvariablen zurueckschreiben
+ $dyn{$r} = {
+ mode=>$mode, phase=>'HEAT',
+ T0old=>$T0old, told=>$tp, optstart=>$optstart,
+ maxpos=>$maxpos, maxslope=>$maxslope,
+ sumspread=>$sumspread, temps=>$temps, times=>$times
+ };
+
+ return sprintf("%.1f(", $T).$retval.sprintf(")%.1f ",$spread);
+ }
+
+ # Erwaermung deutlich verlangsamt -> Optimierung berechnen
+ # Abschaetzung des finalen Plateauniveaus durch Annahme
+ # exponentieller Thermalisierung
+
+ # Position maximaler Steigung
+ my $pos1 = nearest(1,$maxpos-$S1/2);
+ my $t1 = $times->[$maxpos];
+
+ # Endpunkt
+ my $t3 = $times->[nearest(1,-1-$S1/2)];
+
+ # Punkt in der Mitte zwischen max. Steigung und Endpunkt
+ my $pos2 = undef;
+ for my $p ($maxpos..$#{$times})
+ {
+ if($times->[$p]>=($t1+$t3)/2) { $pos2=$p; last; }
+ }
+ unless(defined $pos2)
+ {
+ RESET($r);
+ $dyn{$r}{mode}='ON'; # ansonsten uebrige Werte behalten
+ $dyn{$r}{T0}=$T0old;
+ return "FAILED:POS2";
+ }
+ $pos2 = nearest(1,$pos2-$S1/2);
+
+ # Temperaturen an den Punkten t=0, maxtime, (maxtime+t)/2, t
+ # gemittelt ueber S1 Werte
+ my ($X0,$X1,$X2,$X3)=(0,0,0,0);
+ for my $i (0..($S1-1))
+ {
+ $X0+=$temps->[$i];
+ $X1+=$temps->[$i+$pos1];
+ $X2+=$temps->[$i+$pos2];
+ $X3+=$temps->[-$i-1];
+ }
+ $X0/=$S1; $X1/=$S1; $X2/=$S1; $X3/=$S1;
+
+ # Berechnung des Plateauwertes bei exponentieller Thermalisierung
+ my $Xplateau=($X1*$X3 - $X2*$X2)/($X1 - 2*$X2 + $X3);
+
+ # Analyse der Sprungantwort
+ my $refspread = $sumspread/scalar(@{$times});
+ my $DX = $Xplateau - $X0;
+ my $Ks = $DX/$refspread;
+ my $Tu = $t1 - 2*($tp-$told) - ($X1-$X0)/$maxslope;
+ my $Tg = $DX/$maxslope;
+
+ # Optimierung der PID-Parameter nach Chien/Hrones/Reswick
+ # (siehe zB Wikipedia). Wir nehmen aber etwas andere Koeffizienten,
+ # das fuehrt zu ruhigerem Regelverhalten...
+
+ # Proportionalbereich prop=1/Kp, kleineres prop ist aggressiver
+ my $prop = $maxslope*$Tu/(0.3*$refspread);
+
+ # Nachstellzeit des Integralteils, kleiner ist aggressiver
+ my $Tn = $Tg/60;
+
+ # Vorhaltezeit des Differentialteils, groesser ist aggressiver
+ my $Tv = $Tu/60;
+
+ # alle drei Parameter muessen positiv sein, sonst Fehler
+ unless($prop>=0 && $Tn>=0 && $Tv>=0)
+ {
+ RESET($r);
+ $dyn{$r}{mode}='ON';
+ $dyn{$r}{T0}=$T0old;
+ $dyn{$r}{Told}=$T;
+
+ return "FAILED:NEG";
+ }
+
+ # Statusvariablen zurueckschreiben
+ $dyn{$r} = {
+ mode=>$mode, phase=>'COOL',
+ T0old=>$T0old, told=>$tp, optstart=>$optstart,
+ Tn=>$Tn, Tv=>$Tv, prop=>$prop, refspread=>$refspread, tcool=>$t3,
+ sumspread=>$sumspread, temps=>$temps, times=>$times
+ };
+
+ # Abkuehlung einleiten
+ writeactuators($r, 0);
+
+ return sprintf("%.1f(COOL) ",$T);
+ }
+
+ if($phase eq 'COOL' && $slope>0)
+ {
+ return sprintf("%.1f(%.2fKph) ",$T,$slope*60*60);
+ }
+
+ # Abspeichern der optimierten Parameter im Konfigurationsfile
+ # aus der Laenge der "cooling"-Periode bis zum Maximum koennte man noch was berechnen,
+ # aber wir setzen $lim hier als Konstante
+ my ($Tn, $Tv, $prop, $refspread, $tcool)
+ = @{$dyn{$r}}{qw(Tn Tv prop refspread tcool)};
+ my $date=`/bin/date +"%F %X"`; chomp $date;
+ my $lim=0.5;
+ $house{$r}{pid}={Tv=>$Tv, Tn=>$Tn, lim=>$lim, prop=>$prop, refspread=>$refspread, date=>$date};
+ store_to_house_config($r);
+
+ # Regelung starten
+ RESET($r);
+ $dyn{$r}{mode}='ON';
+ $dyn{$r}{T0}=$T0old;
+ $dyn{$r}{Told}=$T;
+
+ # Info an den User
+ return sprintf "t=%dh:%02dmin Tv=%.1fmin Tn=%dmin lim=%.1f prop=%.1f spread=%.1f ", $tp/3600,($tp/60)%60,$Tv,$Tn,$lim,$prop,$refspread;
+}
+
Deleted: wiregate/plugin/generic/Translator.pl
===================================================================
--- wiregate/plugin/generic/Translator.pl 2012-05-07 21:12:24 UTC (rev 796)
+++ wiregate/plugin/generic/Translator.pl 2012-05-08 04:36:03 UTC (rev 797)
@@ -1,218 +0,0 @@
-#!/usr/bin/perl -w
-##############
-# Translator #
-##############
-# Wiregate-Plugin
-# (c) 2012 Fry under the GNU Public License
-
-#$plugin_info{$plugname.'_cycle'}=0; return 'deaktiviert';
-
-my $use_short_names=0; # 1 fuer GA-Kuerzel (erstes Wort des GA-Namens), 0 fuer die "nackte" Gruppenadresse
-
-# eibgaconf fixen falls nicht komplett indiziert
-if($use_short_names && !exists $eibgaconf{ZV_Uhrzeit})
-{
- for my $ga (grep /^[0-9\/]+$/, keys %eibgaconf)
- {
- $eibgaconf{$ga}{ga}=$ga;
- my $name=$eibgaconf{$ga}{name};
- next unless defined $name;
- $eibgaconf{$name}=$eibgaconf{$ga};
-
- next unless $name=~/^\s*(\S+)/;
- my $short=$1;
- $short='ZV_'.$1 if $eibgaconf{$ga}{name}=~/^Zeitversand.*(Uhrzeit|Datum)/;
-
- $eibgaconf{$ga}{short}=$short;
- $eibgaconf{$short}=$eibgaconf{$ga};
- }
-}
-
-sub limit { my ($lo,$x,$hi)=@_; return $x<$lo?$lo:($x>$hi?$hi:$x); }
-
-# Konfigurationsfile einlesen
-my %trans=();
-my $conf=$plugname; $conf=~s/\.pl$/.conf/;
-open FILE, "</etc/wiregate/plugin/generic/conf.d/$conf" || return "no config found";
-my @lines = <FILE>;
-close FILE;
-eval("@lines");
-return "config error: $@" if $@;
-
-# Aufrufgrund ermitteln
-my $event=undef;
-if (!$plugin_initflag)
-{ $event='restart'; } # Restart des daemons / Reboot
-elsif ($plugin_info{$plugname.'_lastsaved'} > $plugin_info{$plugname.'_last'})
-{ $event='modified'; } # Plugin modifiziert
-elsif (%msg) { $event='bus'; } # Bustraffic
-elsif ($fh) { $event='socket'; } # Netzwerktraffic
-else { $event='cycle'; } # Zyklus
-
-# Plugin-Code
-if($event=~/restart|modified/)
-{
- # alle Variablen loeschen und neu initialisieren, alle GAs abonnieren
- for my $k (grep /^$plugname\_/, keys %plugin_info)
- {
- delete $plugin_info{$k};
- }
-
- my $count=0;
- my $rxtx_lookup='';
-
- for my $t (sort keys %trans)
- {
- my $receive=$trans{$t}{receive};
- my $transmit=$trans{$t}{transmit};
-
- $receive=$eibgaconf{$receive}{ga} if $receive!~/^[0-9\/]+$/ && defined $eibgaconf{$receive};
- $transmit=$eibgaconf{$transmit}{ga} if $transmit!~/^[0-9\/]+$/ && defined $eibgaconf{$transmit};
-
- next unless defined $receive && defined $transmit;
-
- $rxtx_lookup.="Rx($receive)=>'$t', Tx($transmit)=>'$t', ";
-
- $plugin_subscribe{$receive}{$plugname}=1;
- $plugin_subscribe{$transmit}{$plugname}=1;
-
- $count++;
-
- delete $plugin_info{$plugname.'_'.$t.'_result'};
-
- if(ref $trans{$t}{state})
- {
- for my $v (keys %{$trans{$t}{state}})
- {
- $plugin_info{$plugname.'_'.$t.'_'.$v}=$trans{$t}{state}{$v};
- }
- }
- elsif(defined $trans{$t}{state})
- {
- $plugin_info{$plugname.'_'.$t.'_result'}=$trans{$t}{state};
- }
- }
-
- $plugin_info{$plugname.'__RxTxLookup'}=$rxtx_lookup;
- $plugin_info{$plugname.'_cycle'}=0;
-
- return $count." initialisiert";
-}
-
-# Bustraffic bedienen - nur Schreibzugriffe der iButtons interessieren
-if($event=~/bus/)
-{
- return if $msg{apci} eq "A_GroupValue_Response";
-
- my $ga=$msg{dst};
-
- unless($plugin_info{$plugname.'__RxTxLookup'}=~/(Tx|Rx)\($ga\)=>\'(.+?)\',/)
- {
- delete $plugin_subscribe{$ga}{$plugname}; # unbekannte GA
- return;
- }
-
- my $cmd=$1; chop $cmd;
- my $t=$2;
-
- if($msg{apci} eq "A_GroupValue_Read")
- {
- # Ein Read-Request auf einer Transmit-GA wird mit dem letzten Ergebnis beantwortet
- if($cmd eq 'T')
- {
- my $transmit=$ga;
- $transmit=$eibgaconf{$ga}{short} if $use_short_names;
- my $result=$plugin_info{$plugname.'_'.$t.'_result'};
- knx_write($ga, $result);
- return "Memory: $result ($transmit)";
- }
- return;
- }
- elsif($msg{apci} eq "A_GroupValue_Write")
- {
- my $input=$msg{value};
-
- # Write-Telegramm auf unserer Transmit-Adresse?
- # Vorsicht! - das koennten wir selbst gewesen sein, also nicht antworten!
- if($cmd eq 'T')
- {
- if(defined $input)
- {
- $plugin_info{$plugname.'_'.$t.'_result'}=$input; # einfach Input ablegen
- }
- else
- {
- delete $plugin_info{$plugname.'_'.$t.'_result'};
- }
- return;
- }
-
- my $result=undef;
-
- # Ein Write-Request auf einer Receive-GA wird uebersetzt und das Resultat auf der Transmit-GA uebertragen
- if(ref $trans{$t}{state})
- {
- # Komplexer state-Hash: Basis sind die Werte im Configfile
- my $state=$trans{$t}{state};
-
- # Nun die dynamischen Variablen aus plugin_info hinzufuegen
- for my $k (keys %plugin_info)
- {
- next unless $k=~/^$plugname\_$t\_(.*)$/;
- my $v=$1;
- $state->{$v}=$plugin_info{$plugname.'_'.$t.'_'.$v};
- }
-
- # Funktionsaufruf, das Ergebnis vom letzten Mal steht in $state->{result}
- $result=$trans{$t}{translate}($state,$input);
-
- # Alle dynamischen Variablen wieder nach plugin_info schreiben
- # Damit plugin_info nicht durch Konfigurationsfehler vollgemuellt wird,
- # erlauben wir nur vorhandene Eintraege
- for my $v (keys %{$state})
- {
- if(defined $state->{$v})
- {
- $plugin_info{$plugname.'_'.$t.'_'.$v}=$state->{$v};
- }
- else
- {
- delete $plugin_info{$plugname.'_'.$t.'_'.$v};
- }
- }
- }
- else # Einfacher Fall - skalare state-Variable
- {
- # Einfache state-Skalar: Ergebnis des letzten Aufrufs
- my $state=$plugin_info{$plugname.'_'.$t.'_result'};
-
- # Funktionsaufruf, das Ergebnis vom letzten Mal steht in $state
- $result=$trans{$t}{translate}($state,$input);
- }
-
- # Ergebnis des letzten Aufrufs zurueckschreiben
- if(defined $result)
- {
- $plugin_info{$plugname.'_'.$t.'_result'}=$result;
- }
- else
- {
- delete $plugin_info{$plugname.'_'.$t.'_result'};
- }
-
- # Uebersetzung auf Bus schreiben, ausser im Sonderfall receive==transmit, dann nur auf Anfrage senden
- my $receive=$trans{$t}{receive};
- my $transmit=$trans{$t}{transmit};
-
- $receive=$eibgaconf{$receive}{ga} if $receive!~/^[0-9\/]+$/ && defined $eibgaconf{$receive};
- $transmit=$eibgaconf{$transmit}{ga} if $transmit!~/^[0-9\/]+$/ && defined $eibgaconf{$transmit};
-
- unless($transmit eq $receive)
- {
- knx_write($transmit, $result);
- return "$input ($receive) -> $result ($transmit)";
- }
- }
-}
-
-return;
Added: wiregate/plugin/generic/conf.d/Heizungsregler.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Heizungsregler.conf (rev 0)
+++ wiregate/plugin/generic/conf.d/Heizungsregler.conf 2012-05-08 04:36:03 UTC (rev 797)
@@ -0,0 +1,114 @@
+#!/usr/bin/perl
+#
+# Konfiguration Heizungsregler ##########################################
+#
+# Raeume, Messfuehler und Heizkreislaeufe definieren
+# Jeder Raum hat einen eigenen Regler. Die Bezeichnungen der Raeume sind
+# beliebig, sie duerfen aber keinen Underscore ("_") enthalten.
+#
+# Ueber mehrere Sensoren eines Raumes inklusive aller seiner Heizkreislaeufe
+# wird gemittelt (egal wie oft eine GA in der Struktur auftaucht, sie wird
+# nur einfach gewichtet).
+
+# Falls kein Sensor im Raum definiert ist oder antwortet, wird der Haussensor
+# genommen.
+#
+# Falls inflow und outflow weder im Raum noch zentral im Haus definiert
+# oder verfuegbar sind, wird der global definierte fixe Spread angenommen.
+# (Spread = Vorlauf- minus Ruecklauftemperatur = inflow-outflow). Fehlt auch
+# dieser, so faellt die Regelung aus, sobald der Spread mit den vorhandenen
+# Werten nicht ermittelt werden kann.
+#
+# Eine komplette, minimale Konfiguration fuer zwei Zimmer sieht daher aus:
+#
+#%house=(
+# cycle=>60,
+# spread=>20,
+# Zimmer1=>{control=>'1/2/4', sensor=>'1/2/3'},
+# Zimmer2=>{control=>'1/2/14', sensor=>'1/2/13'},
+# );
+#
+# Hierbei gibt es genau eine Wunschtemperatur und einen Messfuehler pro Raum.
+# Der Spread ist dabei fix mit 20K angenommen.
+
+%house=(
+
+# Taktung des Reglers, Anzahl Sekunden zwischen Regleraufrufen
+# Es macht durchaus Sinn, den Regler haeufiger laufen zu lassen als die
+# Sensoren ausgelesen werden. Das sollte zu einem ruhigeren Regler fuehren.
+ cycle => 60,
+
+# Anzahl der Datenpunkte fuer Trendberechnung
+ mindata => 15,
+
+# Falls die Heizung ueber eine einstellbare Vorlauftemperatur verfuegt,
+# so kann hier eine GA fuer die Wunsch-Vorlauftemperatur festgelegt werden,
+# sowie eine maximale Vorlauftemperatur. Vorsicht! Bitte nur benutzen, wenn
+# die tatsaechliche Vorlauftemperatur auch gemessen werden kann!
+
+# inflow_control => '1/2/3', # GA zum Setzen der Soll-Vorlauftemperatur
+ inflow_max => 40, # maximale Soll-Vorlauftemperatur
+
+# Der Spread, oder die Spreizung, ist die Differenz zwischen Vorlauftemperatur
+# und Ruecklauftemperatur. Falls entsprechende Messfuehler entweder nicht
+# konfiguriert oder nicht auslesbar sind, wird der hausuebergreifende Wert
+# genommen, der hier steht. Falls in einem solchen Fall auch hausueber-
+# greifend kein Wert konfiguriert ist steht, faellt die Heizung aus.
+# Faustregel: Thermentemperatur minus Raumtemperatur
+# inflow => '1/2/3', # hausweite Vorlauftemp, falls nicht raumweise messbar
+# outflow => '1/2/3', # hausweite Ruecklauftemp, falls nicht raumweise messbar
+ spread => 20, # hausweite Spreizung, falls alle Stricke reissen
+
+# Die Gruppenadressen fuer
+# sensor (Ist-Temperatur im Raum),
+# window (Fensterkontakte, dpt-Typ 1, Wert 1 bedeutet offen)
+# floor (Estrichtemperatur),
+# inflow (Vorlauftemperatur) und
+# outflow (Ruecklauftemperatur)
+# koennen jeweils auf Raum- und/oder Heizkreislaufebene definiert
+# sein und koennen einzelne GAs oder Listen von GAs sein. Wenn alle diese
+# Messfuehler fehlen, so werden die hausweiten Sensoren oben genommen.
+# Falls die auch fehlen, wird der fixe Spread weiter oben genommen.
+# Wenn der auch noch fehlt, arbeitet der Regler nicht.
+
+ WZ => {
+ control => $eibgaconf{TS_WZ}{ga}, # GA zum Setzen der Solltemperatur
+
+ # Raumtemperaturfuehler (Liste oder nur einer)
+# sensor => ['1/2/3','1/2/3','1/2/3'],
+ sensor => $eibgaconf{TA_A2}{ga},
+
+ # Vorlauftemperatur, kann auch pro Kreislauf definiert sein
+# inflow => $eibgaconf{TO_Zentralvorlauf}{ga},
+
+ # Fensterkontaktabfrage
+ window => [$eibgaconf{KF_WZ_West_L}{ga}, $eibgaconf{KF_WZ_West_R}{ga}],
+
+ actuator => $eibgaconf{KR_E_Fermax_Licht}{ga}, # GA zum Setzen der Solltemperatur
+
+ # Heizkreislaeufe, aktuell werden alle gleich gesteuert
+ # (Ausgleich fuer gleiche Estrichtemp. noch nicht implementiert)
+# circ => {
+# WZ_Nord => {
+# actuator => $eibgaconf{VS_WZ_Mitte}{ga}, # Stellventil
+# floor => $eibgaconf{TE_WZ_Mitte}{ga}, # Estrichfuehler
+# outflow => '1/2/3', # Ruecklauf
+# },
+# WZ_Mitte => {
+# actuator => '1/2/3', # Stellventil
+# outflow => '1/2/3', # Ruecklauf
+# },
+# WZ_Sued => {
+# actuator => '1/2/3', # Stellventil
+# outflow => '1/2/3', # Ruecklauf
+# },
+# },
+ },
+ );
+
+# Ende der Konfiguration ##################################
+
+$house{WZ}{pid}={Tn=>485.62, Tv=>39.87, date=>'2012-04-20 22:34:06',lim=>1.00, prop=>0.31, refspread=>11.42, };
+$house{WZ}{pid}={Tn=>485.62, Tv=>19.93, date=>'2012-04-20 22:51:50',lim=>1.00, prop=>0.31, refspread=>11.42, };
+$house{WZ}{pid}={Tn=>485.62, Tv=>19.93, date=>'2012-04-20 23:30:49',lim=>0.20, prop=>0.31, refspread=>11.42, };
+$house{WZ}{pid}={Tn=>485.62, Tv=>19.93, date=>'2012-04-21 08:54:14',lim=>0.50, prop=>0.31, refspread=>11.42, };
Deleted: wiregate/plugin/generic/conf.d/Translator.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Translator.conf 2012-05-07 21:12:24 UTC (rev 796)
+++ wiregate/plugin/generic/conf.d/Translator.conf 2012-05-08 04:36:03 UTC (rev 797)
@@ -1,39 +0,0 @@
-#!/usr/bin/perl
-#
-# Translator.pl - Konfiguration
-
-%trans=(
-
- # 1. Alle Werte, die auf einer GA gesendet werden, werden mit 2 multipliziert auf einer anderen GA weitergegeben
- mal2 => { receive=>'9/5/201', transmit=>'9/5/202', translate => sub { my ($state,$input)=@_; 2*$input; }, },
-
- # 2. Die Werte auf der ersten GA werden aufsummiert, das Ergebis auf der anderen GA gesendet.
- # Damit kann man bspw aus einem relativen Dimmwert einen absoluten Dimmwert machen.
- sum => { receive=>'9/5/207', transmit=>'9/5/208', translate => sub { my ($state,$input)=@_; $state+$input; }, },
- # $state enthaelt das jeweils letzte Ergebnis.
-
- # 3. Wie oben, aber das Ergebnis limitiert auf den Bereich 0-100
- lsum => { receive=>'1/2/7', transmit=>'1/2/8', translate => sub { my ($state,$input)=@_; limit(0,$state+$input,100); }, },
-
- # 4. Schliesslich eine Memory-Funktion: wenn receive==transmit, so wird nur auf eine Leseanfrage gesendet.
- # Hier speichert Translator also den Input ab und sendet den errechneten (=gespeicherten) Wert NUR AUF ANFRAGE
- memory => { receive=>'1/2/9', transmit=>'1/2/10', translate => sub { my ($state,$input)=@_; $input; }, },
-
- # 5. Ein komplexerer Fall: hier besteht der Status des Translators aus mehreren Werten.
- # Es wird immer die Summe aus letztem, vorletztem und aktuellem Wert gesendet.
- complex => { receive=>'9/5/205', transmit=>'9/5/206', state => {val1=>0, val2=>0},
- translate => sub { my ($state,$input)=@_;
- $state->{val2}=$state->{val1}; $state->{val1}=$state->{result};
- $state->{val2}+$state->{val1}+$input; }, },
- # Wenn state ein Hash ist, wird der letzte gesendete Wert in $state->{result} gespeichert.
-
- # 6. Schlussendlich wieder mal Werbung fuer die GA-Kurznamen. Setzt man im Skript Translator.pl $use_short_names=1
- # und verwendet GA-Namen mit eindeutigem Kuerzel (=erstes Wort des Namens), so funktioniert auch das folgende:
- D_SZ_Decke => { receive=>'LR_SZ_Decke', transmit=>'LK_SZ_Decke',
- translate => sub { my ($state,$input)=@_; limit(0,$state+20*$input,100); }, },
- # ist doch leserlicher, oder? Hier wird ein relativer Dimmwert durch Skalierung und Summierung
- # in einen absoluten Wert umgewandelt
-
- );
-
-
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-07 21:12:31
|
Revision: 796
http://openautomation.svn.sourceforge.net/openautomation/?rev=796&view=rev
Author: pfry
Date: 2012-05-07 21:12:24 +0000 (Mon, 07 May 2012)
Log Message:
-----------
Modified Paths:
--------------
wiregate/plugin/generic/Logikprozessor.pl
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-05-07 20:46:56 UTC (rev 795)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-07 21:12:24 UTC (rev 796)
@@ -7,7 +7,7 @@
#$plugin_info{$plugname.'_cycle'}=0; return 'deaktiviert';
-my $use_short_names=1; # 1 fuer GA-Kuerzel (erstes Wort des GA-Namens), 0 fuer die "nackte" Gruppenadresse
+my $use_short_names=0; # 1 fuer GA-Kuerzel (erstes Wort des GA-Namens), 0 fuer die "nackte" Gruppenadresse
# eibgaconf fixen falls nicht komplett indiziert
if($use_short_names && !exists $eibgaconf{ZV_Uhrzeit})
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-07 20:47:02
|
Revision: 795
http://openautomation.svn.sourceforge.net/openautomation/?rev=795&view=rev
Author: pfry
Date: 2012-05-07 20:46:56 +0000 (Mon, 07 May 2012)
Log Message:
-----------
Modified Paths:
--------------
wiregate/plugin/generic/Logikprozessor.pl
wiregate/plugin/generic/conf.d/Logikprozessor.conf
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-05-07 18:51:21 UTC (rev 794)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-07 20:46:56 UTC (rev 795)
@@ -30,21 +30,32 @@
# Tools und vorbesetzte Variablen fue die Logiken
sub limit { my ($lo,$x,$hi)=@_; return $x<$lo?$lo:($x>$hi?$hi:$x); }
-my $day_of_week=`/bin/date +"%a"`;
-my $weekend=($day_of_week=~/Sa|So/);
-my $time_of_day=`/bin/date +"%X"`;
-my $hour_of_day=substr($time_of_day,0,2);
-my $day=($hour_of_day>7 && $hour_of_day<23);
+my $date=`/bin/date +"%W,%a,%u,%m,%d,%Y,%H,%M,%X"`;
+plugin_log($plugname, "Datum/Uhrzeit konnte nicht lesbar.") unless $date=~/^(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+)$/;
+my $calendar_week=$1;
+my $day_of_week=$2;
+my $day_of_week_no=$3;
+my $month=int($4);
+my $day_of_month=int($5);
+my $year=int($6);
+my $hour=int($7);
+my $minute=int($8);
+my $time_of_day=$9; # '08:30:43'
+my $weekend=($day_of_week_no>=6);
+my $weekday=!$weekend;
+my $day=($hour>7 && $hour<23);
my $night=!$day;
+my $systemtime=time();
# Konfigurationsfile einlesen
-my %devices=(0=>'Wiregate');
my %logic=();
my $conf="/etc/wiregate/plugin/generic/conf.d/$plugname"; $conf=~s/\.pl$/.conf/;
open FILE, "<$conf" || return "no config found";
-my @lines = <FILE>;
+$/=undef;
+my $lines = <FILE>;
+$lines =~ s/((?:translate|\'translate\'|\"translate\")\s*=>\s*sub\s*\{)/$1 my \(\$state,\$input\)=\@\_;/sg;
close FILE;
-eval("@lines");
+eval($lines);
return "config error: $@" if $@;
# Aufrufgrund ermitteln
@@ -97,13 +108,26 @@
next;
}
+ if(defined $logic{$t}{timer} && defined $logic{$t}{delay})
+ {
+ plugin_log($plugname, "Config err: \$logic{$t}: delay und timer festgelegt, ignoriere delay");
+ }
+
# transmit-Adresse abonnieren
my $transmit=groupaddress($logic{$t}{transmit});
$plugin_subscribe{$transmit}{$plugname}=1;
$count++;
- # alle receive-Adressen abonnieren (eine oder mehrere)
+ # Timer-Logiken reagieren nicht auf Bustraffic auf den receive-Adressen
+ # fuer Timer-Logiken: ersten Call berechnen
+ if($logic{$t}{timer})
+ {
+ set_next_call($t);
+ next;
+ }
+
+ # fuer Nicht-Timer-Logiken: alle receive-Adressen abonnieren (eine oder mehrere)
my $receive=groupaddress($logic{$t}{receive});
next unless $receive;
@@ -121,7 +145,6 @@
}
}
- $plugin_info{$plugname.'_cycle'}=0;
$retval.=$count." initialisiert";
}
@@ -143,7 +166,7 @@
my $receive=groupaddress($logic{$t}{receive});
my $receive_ga=0;
- if(defined $receive)
+ if(defined $receive && !$logic{$t}{timer})
{
unless(ref $logic{$t}{receive})
{
@@ -173,7 +196,7 @@
}
next;
}
- elsif(!$receive_ga) # Receive geht vor!
+ elsif(!$receive_ga) # Receive geht vor - bei Timer-Logiken ist receive_ga immer 0
{
if(defined $in) # Write-Telegramm: das waren moeglicherweise wir selbst, also nicht antworten
{
@@ -192,109 +215,30 @@
# Nebenbei berechnen wir noch zwei Flags, die Zirkelkommunikation verhindern sollen
# (Logik antwortet auf sich selbst in einer Endlosschleife)
- # kommt transmit-GA unter den receive-GAs vor?
- # Wenn transmit_ga gesetzt ist, ist das schon mal der Fall
- my $possible_circle=$transmit_ga;
-
# war Wiregate der Sender des Telegramms?
my $sender_is_wiregate=int($msg{src})==0;
- # Es folgt die eigentliche Logik-Engine - als erstes definiere das Input-Array fuer die Logik
- my $input=$in; # Skalarer Fall (der haeufigste)
+ # Aufruf der Logik-Engine
+ my $result=execute_logic($t, $receive, $ga, $in);
- # Array-Fall: bereite Input-Array fuer Logik vor
- if(ref $receive)
+ # In bestimmten Sonderfaellen nichts schicken
+ next unless defined $result; # Resultat undef => nichts senden
+ next if $logic{$t}{transmit_only_on_request};
+
+ # Zirkelaufruf ausschliessen
+ if($sender_is_wiregate && $in eq $result)
{
- $input=();
- for my $rec (@{$receive})
- {
- my $rec=groupaddress($rec);
-
- $possible_circle=1 if $transmit eq $rec;
-
- if($ga eq $rec)
- {
- push @{$input}, $in;
- }
- else
- {
- push @{$input}, knx_read($rec, 300);
- }
- }
+ # kommt transmit-GA unter den receive-GAs vor?
+ # Wenn transmit_ga gesetzt ist, ist das schon mal der Fall
+ next if $transmit_ga;
+ next if !ref $receive && ($transmit eq $receive);
+ next if ref $receive && grep /^$transmit$/, @{$receive};
}
- # ab hier liegt $input komplett vor, und nun muss die Logik ausgewertet
- # und das Resultat auf der Transmit-GA uebertragen
- my $result=undef;
-
- unless(ref $logic{$t}{translate})
- {
- # Trivialer Fall: translate enthaelt einen fixen Rueckgabewert
- $plugin_info{$plugname.'_'.$t.'_result'}=$result=$logic{$t}{translate};
- }
- elsif(!ref $logic{$t}{state})
- {
- # Einfacher aber haeufiger Fall: skalarer $state
- # $state mit Ergebnis des letzten Aufrufs vorbesetzen
- my $state=$plugin_info{$plugname.'_'.$t.'_result'};
-
- # Funktionsaufruf, das Ergebnis vom letzten Mal steht in $state
- $result=$logic{$t}{translate}($state,$input);
-
- # Ergebnis des letzten Aufrufs zurueckschreiben
- if(defined $result)
- {
- $plugin_info{$plugname.'_'.$t.'_result'}=$result;
- }
- else
- {
- delete $plugin_info{$plugname.'_'.$t.'_result'};
- }
- }
- else
- {
- # Komplexer Fall: $state-Hash aus %logic initialisieren
- my $state=$logic{$t}{state};
- my @vars=keys %{$state};
- push @vars, 'result';
-
- # Nun die dynamischen Variablen aus plugin_info hinzufuegen
- for my $v (@vars)
- {
- $state->{$v}=$plugin_info{$plugname.'_'.$t.'_'.$v} if defined $plugin_info{$plugname.'_'.$t.'_'.$v};
- }
-
- # Funktionsaufruf, das Ergebnis vom letzten Mal steht in $state->{result}
- $result=$state->{result}=$logic{$t}{translate}($state,$input);
-
- # Alle dynamischen Variablen wieder nach plugin_info schreiben
- # Damit plugin_info nicht durch Konfigurationsfehler vollgemuellt wird,
- # erlauben wir nur Eintraege mit defined-Werten
- for my $v (@vars)
- {
- if(defined $state->{$v})
- {
- $plugin_info{$plugname.'_'.$t.'_'.$v}=$state->{$v};
- }
- else
- {
- # wenn die Logik den Wert undef in eine state-Variable schreibt,
- # wird beim naechsten Aufruf wieder der Startwert aus %logic genommen,
- delete $plugin_info{$plugname.'_'.$t.'_'.$v};
- }
- }
- }
-
- # In bestimmten Sonderfaellen nichts schicken
- next unless defined $result; # Resultat undef => nichts senden
- next if $logic{$t}{transmit_only_on_request};
- next if $possible_circle && $sender_is_wiregate && $in eq $result;
-
# Falls delay spezifiziert, wird ein "Wecker" gestellt, um in einem spaeteren Aufruf den Wert zu senden
if($logic{$t}{delay})
{
- $plugin_info{$plugname.'__'.$t.'_timer'}=time()+$logic{$t}{delay};
- set_timer();
+ $plugin_info{$plugname.'__'.$t.'_timer'}=$systemtime+$logic{$t}{delay};
}
else
{
@@ -308,58 +252,301 @@
}
}
-# Evtl. faellige Timer finden
+# Evtl. faellige Timer finden, gleichzeitig Timer fuer nachste Aktion setzen
+my $nexttimer=undef;
for my $timer (grep /$plugname\__.*_timer/, keys %plugin_info) # alle Timer
{
- next if time()<$plugin_info{$timer}; # weiter falls noch nicht faellig
-
- # Timer loeschen
- delete $plugin_info{$timer};
-
- # Relevanten Eintrag von %logic ermitteln
- $timer=~/$plugname\__(.*)_timer/;
- my $t=$1;
-
- # Transmit-GA
- my $transmit=groupaddress($logic{$t}{transmit});
-
- # zu sendendes Resultat
- my $result=$plugin_info{$plugname.'_'.$t.'_result'};
- next unless defined $result;
-
- knx_write($transmit, $result);
+ if(time()>=$plugin_info{$timer}) # Timer faellig? -> dann ausfuehren bzw. Resultat senden
+ {
+ # Relevanten Eintrag von %logic ermitteln
+ $timer=~/$plugname\__(.*)_timer/;
+ my $t=$1;
+
+ # Timer loeschen bzw. neu setzen
+ set_next_call($t);
+
+ # zu sendendes Resultat = zuletzt berechnetes Ergebnis der Logik
+ my $result=$plugin_info{$plugname.'_'.$t.'_result'};
+
+ # ...es sei denn, es ist eine timer-Logik. Die muss jetzt ausgefuehrt werden
+ if($logic{$t}{timer})
+ {
+ # Aufruf der Logik-Engine
+ $result=execute_logic($t, groupaddress($logic{$t}{receive}), undef, undef);
+ }
+
+ # Transmit-GA
+ my $transmit=groupaddress($logic{$t}{transmit});
+
+ next unless defined $result;
+ next if $logic{$t}{transmit_only_on_request};
+
+ knx_write($transmit, $result);
+ }
+ else # noch nicht faelliger Timer
+ {
+ $nexttimer=$plugin_info{$timer} if !defined $nexttimer || $plugin_info{$timer}<$nexttimer;
+ }
}
-# Timer fuer nachste Aktion setzen
-set_timer();
+# Cycle auf naechsten Aufruf setzen
+unless(defined $nexttimer)
+{
+ $plugin_info{$plugname."_cycle"}=0; # kein Aufruf noetig
+}
+else
+{
+ my $cycle=$nexttimer-time();
+ $cycle=1 if $cycle<1;
+ $plugin_info{$plugname."_cycle"}=$cycle;
+}
return unless $retval;
return $retval;
-# Zeit bis zum naechsten Aufruf dieses Plugins berechnen
+# Fuer Logiken mit timer-Klausel: Zeit des naechsten Aufrufs bestimmen
-sub set_timer
+sub next_day
{
- # optimalen Wert fuer Aufrufzyklus finden, um beim naechsten Aufruf was zu senden
- my $nexttimer=undef;
- for my $timer (grep /$plugname\__.*_timer/, keys %plugin_info) # alle Timer
+ my $d=shift;
+
+ my $leapyear = ($d->{year} % 4)==0 && ($d->{year} % 100!=0 || $d->{year} % 400==0);
+ my @days_in_month=(0,31,28+$leapyear,31,30,31,30,31,31,30,31,30,31);
+
+ $d->{day_of_week} = ($d->{day_of_week} % 7)+1;
+ $d->{calendar_week} += $d->{day_of_week}==1;
+ $d->{day_of_month} = ($d->{day_of_month} % $days_in_month[$d->{month}])+1;
+ $d->{month} += $d->{day_of_month}==1;
+ $d->{month}=1 if $d->{month}==13;
+ $d->{year} += ($d->{day_of_month}==1 && $d->{month}==1);
+
+ return $d;
+}
+
+sub schedule_matches_day
+{
+ my ($schedule,$day)=@_;
+ # Beide Argumente sind Hash-Referenzen, wobei schedule jeweils auf Listen zeigt, aber $day alle Kategorien enthaelt
+ # zB $day={year=>2012,month=>4,day_of_month=>13,calendar_week=>16,day_of_week=>4,...};
+
+ my $match=1;
+
+ for my $c (keys %{$schedule})
{
- $nexttimer=$plugin_info{$timer} if !defined $nexttimer || $plugin_info{$timer}<$nexttimer;
+ next if $c eq 'time'; # es geht nur um Tage
+ unless(grep /^$day->{$c}$/, @{$schedule->{$c}})
+ {
+ $match=0;
+ last;
+ }
}
- unless(defined $nexttimer)
+ return $match;
+}
+
+sub set_next_call
+{
+ my $t=shift; # der relevante Eintrag in %logic
+ my $nextcall=undef;
+ my $days_until_nextcall=0;
+
+ # $logic{$t}{timer} ist eine Liste oder ein einzelner Eintrag
+ # jeder solche Eintrag ist ein Hash im Format
+ # {day_of_month=>[(1..7)],day_of_week=>'Mo',time=>['08:30','09:20']}
+ # das gerade genannte Beispiel bedeutet "jeden Monat jeweils der erster Montag, 8:30 oder 9:20"
+ # verwendbare Klauseln sind year, month, day_of_month, calendar_week, day_of_week und time
+ # Pflichtfeld ist lediglich time, die anderen duerfen auch entfallen.
+ # Jeder Wert darf ein Einzelwert oder eine Liste sein.
+ my $schedule=$logic{$t}{timer};
+ my $today={year=>$year,month=>$month,day_of_month=>$day_of_month,calendar_week=>$calendar_week,day_of_week=>$day_of_week_no};
+ my $time_of_day=`/bin/date +"%X"`;
+
+ # Schedule-Form standardisieren (alle Eintraege in Listenform setzen und Wochentage durch Zahlen ersetzen)
+ # dabei gleich schauen, ob HEUTE noch ein Termin ansteht
+ $schedule=[$schedule] if ref $schedule eq 'HASH';
+ my %weekday=(Mo=>1,Di=>2,Mi=>3,Do=>4,Fr=>5,Sa=>6,So=>7);
+
+ for my $s (@{$schedule})
{
- $plugin_info{$plugname."_cycle"}=0;
+ unless(ref $s eq 'HASH')
+ {
+ plugin_log($plugname, "Logiktimer zu Logik '$t' ist kein Hash oder Liste von Hashes");
+ next;
+ }
+ unless(defined $s->{time})
+ {
+ plugin_log($plugname, "Logiktimer zu Logik '$t' enthaelt mindestens einen Eintrag ohne Zeitangabe (time=>...)");
+ next;
+ }
+
+ for my $k (keys %{$s})
+ {
+ unless($k=~/^(year|month|calendar_week|day_of_month|day_of_week|time)$/)
+ {
+ plugin_log($plugname, "Logiktimer zu Logik '$t': Unerlaubter Eintrag '$k'; erlaubt sind year, month, calendar_week, day_of_month, day_of_week, und Pflichteintrag ist time");
+ next;
+ }
+
+ unless(!ref $s->{$k} || ref $s->{$k} eq 'ARRAY')
+ {
+ plugin_log($plugname, "Logiktimer zu Logik '$t': '$k' muss auf Skalar oder Array ($k=>[...]) verweisen");
+ next;
+ }
+
+ $s->{$k}=[$s->{$k}] unless ref $s->{$k} eq 'ARRAY'; # alle Kategorien in Listenform
+ map $_=$weekday{$_}, @{$s->{$k}} if $k eq 'day_of_week'; # Wochentage in Zahlenform
+ @{$s->{$k}}=sort @{$s->{$k}}; # alle Listen sortieren
+ }
+
+ # Steht heute aus diesem Schedule noch ein Termin an?
+ next unless schedule_matches_day($s,$today) && $s->{time}[-1] gt $time_of_day;
+
+ # Heute steht tatsaechlich noch ein Termin an! Welcher ist der naechste?
+ # Rueckwaerts durch die Liste $s->{time} suchen
+ my $nc=undef;
+ for(my $i=$#{$s->{time}}; $i>=0 && $s->{time}[$i] gt $time_of_day; $i--) { $nc=$s->{time}[$i]; }
+ $nextcall=$nc unless defined $nextcall && $nextcall lt $nc;
}
+
+ # Wenn $nextcall hier bereits definiert, enthaelt es die naechste Aufrufzeit des Timers im Format "08:30"
+ my $schedules_done=0;
+
+ # falls nextcall noch nicht definiert, geht es jetzt um den naechsten Tag mit Termin
+ until($schedules_done || defined $nextcall)
+ {
+ $schedules_done=1;
+ $days_until_nextcall++;
+ $today=next_day($today);
+
+ for my $s (@{$schedule})
+ {
+ $schedules_done=0 if !defined $s->{year} || $s->{year}[-1]>=$today->{year};
+ next unless schedule_matches_day($s,$today);
+
+ # an diesem Tag gibt es einen Termin! Wann ist der erste?
+ $nextcall=$s->{time}[0] unless defined $nextcall && $nextcall lt $s->{time}[0];
+ }
+ }
+
+ if(defined $nextcall)
+ {
+ plugin_log($plugname, "Naechster Aufruf der Logik '$t' um $nextcall".($days_until_nextcall?" in $days_until_nextcall Tagen.":"."));
+
+ # Zeitdelta zu jetzt berechnen
+ my $seconds=3600*(substr($nextcall,0,2)-substr($time_of_day,0,2))
+ +60*(substr($nextcall,3,2)-substr($time_of_day,3,2))-substr($time_of_day,6,2);
+ $nextcall=$systemtime+$seconds+3600*24*$days_until_nextcall;
+ $plugin_info{$plugname.'__'.$t.'_timer'}=$nextcall;
+ }
else
{
- my $cycle=$nexttimer-time();
- $cycle=1 if $cycle<1;
- $plugin_info{$plugname."_cycle"}=$cycle;
+ plugin_log($plugname, "Logik '$t' wird nicht mehr aufgerufen (alle in time=>... festgelegten Termine sind verstrichen).");
+
+ delete $plugin_info{$plugname.'__'.$t.'_timer'};
}
}
+
+# Es folgt die eigentliche Logik-Engine
+sub execute_logic
+{
+ my ($t, $receive, $ga, $in)=@_; # Logikindex $t, Bustelegramm erhalten auf $ga mit Inhalt $in
+
+ # als erstes definiere das Input-Array fuer die Logik
+ my $input=$in;
+
+ # Array-Fall: bereite Input-Array fuer Logik vor
+ if(!ref $receive)
+ {
+ # wenn ga gesetzt, steht der Input-Wert in $in
+ # wenn receive undefiniert, gibt es keine receive-GA
+ $in=$input=knx_read($receive, 300) if !$ga && $receive;
+ }
+ else
+ {
+ $input=();
+ for my $rec (@{$receive})
+ {
+ my $rec=groupaddress($rec);
+
+ if($ga eq $rec)
+ {
+ push @{$input}, $in;
+ }
+ else
+ {
+ push @{$input}, knx_read($rec, 300);
+ }
+ }
+ }
+
+ # ab hier liegt $input komplett vor, und nun muss die Logik ausgewertet
+ # und das Resultat auf der Transmit-GA uebertragen
+ my $result=undef;
+
+ unless(ref $logic{$t}{translate})
+ {
+ # Trivialer Fall: translate enthaelt einen fixen Rueckgabewert
+ $plugin_info{$plugname.'_'.$t.'_result'}=$result=$logic{$t}{translate};
+ }
+ elsif(!ref $logic{$t}{state})
+ {
+ # Einfacher aber haeufiger Fall: skalarer $state
+ # $state mit Ergebnis des letzten Aufrufs vorbesetzen
+ my $state=$plugin_info{$plugname.'_'.$t.'_result'};
+
+ # Funktionsaufruf, das Ergebnis vom letzten Mal steht in $state
+ $result=$logic{$t}{translate}($state,$input);
+
+ # Ergebnis des letzten Aufrufs zurueckschreiben
+ if(defined $result)
+ {
+ $plugin_info{$plugname.'_'.$t.'_result'}=$result;
+ }
+ else
+ {
+ delete $plugin_info{$plugname.'_'.$t.'_result'};
+ }
+ }
+ else
+ {
+ # Komplexer Fall: $state-Hash aus %logic initialisieren
+ my $state=$logic{$t}{state};
+ my @vars=keys %{$state};
+ push @vars, 'result';
+
+ # Nun die dynamischen Variablen aus plugin_info hinzufuegen
+ for my $v (@vars)
+ {
+ $state->{$v}=$plugin_info{$plugname.'_'.$t.'_'.$v} if defined $plugin_info{$plugname.'_'.$t.'_'.$v};
+ }
+
+ # Funktionsaufruf, das Ergebnis vom letzten Mal steht in $state->{result}
+ $result=$state->{result}=$logic{$t}{translate}($state,$input);
+
+ # Alle dynamischen Variablen wieder nach plugin_info schreiben
+ # Damit plugin_info nicht durch Konfigurationsfehler vollgemuellt wird,
+ # erlauben wir nur Eintraege mit defined-Werten
+ for my $v (@vars)
+ {
+ if(defined $state->{$v})
+ {
+ $plugin_info{$plugname.'_'.$t.'_'.$v}=$state->{$v};
+ }
+ else
+ {
+ # wenn die Logik den Wert undef in eine state-Variable schreibt,
+ # wird beim naechsten Aufruf wieder der Startwert aus %logic genommen,
+ delete $plugin_info{$plugname.'_'.$t.'_'.$v};
+ }
+ }
+ }
+
+ return $result;
+}
+
+
# Umgang mit GA-Kurznamen und -Adressen
sub groupaddress
Modified: wiregate/plugin/generic/conf.d/Logikprozessor.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-07 18:51:21 UTC (rev 794)
+++ wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-07 20:46:56 UTC (rev 795)
@@ -21,21 +21,43 @@
# translate-Routine. Neu ist hier der "delay"-Parameter, ausserdem der Spezialfall, dass translate einfach eine Konstante als
# Rueckgabewert spezifiziert.
stair => { receive=>'1/2/9', transmit=>'1/2/9', delay=>600, translate => 0, },
- # Damit im Fall transmit != receive der Logikprozessor nicht auf sein eigenes Schreibtelegramm immer wieder antwortet, wird nur dann gesendet,
- # wenn Ergebnis != Input oder Sender des empfangenen Telegramms!=0 (Wiregate).
+ # Verzoegert wird uebrigens nur das Senden, nicht das Ausfuehren der translate-Routine.
+ # Neu ist hier der "delay"-Parameter, ausserdem der Spezialfall, dass translate einfach eine Konstante
+ # als Rueckgabewert spezifiziert.
- # 5. Memory-Funktion. Wenn ein Write-Telegramm auf die Transmit-Adresse kommt, speichert der Logikprozessor den Wert ab.
- # Das Flag "transmit_on_request" bewirkt, dass nichts gesendet wird, jedoch wird eine Leseanfrage auf der transmit-GA immer
- # mit dem letzten Wert (hier also dem gespeicherten) beantwortet. Damit laesst sich eine Speicherfunktion realisieren.
- # Hier speichert Logikprozessor also den Input ab und sendet den errechneten (=gespeicherten) Wert NUR AUF ANFRAGE.
- memory => { transmit=>'1/2/9', transmit_only_on_request=>1 },
- # Eine receive-Adresse oder translate-Logik werden hier gar nicht gebraucht.
+ # Weitere Bemerkungen:
+ # * translate darf nur entweder eine Konstante oder ausf\xFChrbarer Code (sub {...}) sein.
+ # * Damit im Fall transmit==receive der Translator nicht auf sein eigenes Schreibtelegramm immer wieder antwortet,
+ # wird nur dann gesendet, wenn Ergebnis!=Input oder Sender des empfangenen Telegramms!=0 (Wiregate).
+
+ # Damit im Fall transmit != receive der Logikprozessor nicht auf sein eigenes Schreibtelegramm immer wieder antwortet,
+ # wird nur dann gesendet, wenn Ergebnis != Input oder Sender des empfangenen Telegramms!=0 (Wiregate).
- # 6. Eine einfache UND-Logik mit zwei Eingaengen. Falls ein Telegramm auf einer der beiden receive-GAs empfangen wird,
+ # 5. Hier eine Logik, die den Input bei Eintreffen mit 2 multipliziert, das Resultat aber nur speichert und erst
+ # spaeter auf ein explizites Lesetelegramm hin auf der transmit-Adresse sendet.
+ req => { receive=>'1/2/9', transmit=>'1/2/9', transmit_on_request=>1, translate => sub { 2*$input; }, },
+
+ # 6. Eine Logik, die einem Lichtanschalten gleich einen Dimmwert hinterherfeuert, und zwar tags und nachts einen verschiedenen:
+ dim => { receive=>'2/2/9', transmit=>'2/3/9', translate => sub { return unless $input; $day ? 80 : 5; }, },
+ # Die Variablen $day_of_week (Mo...So), $day_of_month (01-31), $month (01-12), $year (2012),
+ # $weekend (0 oder 1), $weekday (= nicht $weekend), $time_of_day ("08:34:02"),
+ # $hour ("08"), $day (1 falls zwischen 7 und 23 Uhr, 0 sonst) und $night (entsprechend umgekehrt)
+ # sind f\xFCr diese Logiken vorbesetzt (bitte nicht darauf schreiben, koennte unverhergesehene Auswirkungen
+ # auf andere Logiken haben).
+
+ # 7. Memory-Funktion. Fuer KNX-Ger\xE4te, die kein Auslesen ihres Statuswerts zulassen (z.B. MDT DaliControl
+ # bei Einzel-EVG-Ansteuerung). Sehr einfach:
+ memory => { transmit=>'1/2/9' },
+ # Hier wird folgende Eigenschaft der Logik ausgenutzt: Wenn ein Write-Telegramm auf die Transmit-Adresse kommt,
+ # speichert der Logikprozessor den Wert immer automatisch ab. Eine Leseanfrage auf der transmit-GA hingegen wird
+ # immer mit dem letzten Wert (hier also dem gespeicherten) beantwortet. Eine receive-Adresse oder translate-Logik
+ # werden hier gar nicht gebraucht.
+
+ # 8. Eine einfache UND-Logik mit zwei Eingaengen. Falls ein Telegramm auf einer der beiden receive-GAs empfangen wird,
# wird die andere Adresse noch ausgelesen, die Logik angewendet und das Ergebnis auf der transmit-GA uebermittelt
und => { receive=>['1/2/12','1/2/13'], transmit=>'1/2/14', translate => sub { $input->[0] && $input->[1]; }, },
- # 7. Ein komplexerer Fall nur zur Demonstration: hier besteht der Status des Logikprozessors aus mehreren Werten.
+ # 9. Ein komplexerer Fall nur zur Demonstration: hier besteht der Status des Logikprozessors aus mehreren Werten.
# Es wird immer die Summe aus letztem, vorletztem und aktuellem Wert gesendet, und zwar mit 30s Verzoegerung.
complex => { receive=>'9/5/205',
transmit=>'9/5/206',
@@ -46,7 +68,19 @@
},
# Wenn state ein Hash ist, wird der letzte gesendete Wert in $state->{result} gespeichert.
- # 8. Schlussendlich wieder mal Werbung fuer die GA-Kurznamen. Setzt man im Skript Logikprozessor.pl $use_short_names=1
+ # 10. Eine Timer-Funktion. Hier eine einfache Zeitschaltuhr, die immer um 8 Uhr und um 10:00 am jeweils zweiten
+ # Dienstag jedes Monats eine 1 auf Transmit sendet
+ wecker => { transmit=>'10/1/15', timer=>{ time=>['08:00','10:00'], day_of_month=>[(8..14)], day_of_week=>'Di' }, translate => 1 },
+ # Logiken mit timer-Klausel weichen in mehreren Punkten von den bisherigen Logiken ab:
+ # * sie ignorieren die delay-Klausel und senden sofort, aber transmit_only_on_request funktioniert
+ # * jeglicher Bustraffic auf receive-Adressen wird ignoriert. (Diese werden aber beim Timer-Aufruf abgefragt,
+ # um das input-Array vorzubesetzen).
+ # Als timer-Eintrag geht entweder ein einzelnes Hash timer=>{...} wie oben oder eine Liste solcher Eintraege
+ # time=>[{...},{...},{...},...]. Jeder Eintrag MUSS eine Spezifikation time=>'XX:XX' enthalten (auch das darf wieder
+ # eine Liste sein) und DARF zusaetzliche, die Geltungstage einschraenkende Klauseln wie year, month, day_of_month,
+ # day_of_week, calendar_week enthalten.
+
+ # 11. Schlussendlich wieder mal Werbung fuer die GA-Kurznamen. Setzt man im Skript Logikprozessor.pl $use_short_names=1
# und verwendet GA-Namen mit eindeutigem Kuerzel (=erstes Wort des Namens), so funktioniert auch das folgende:
D_SZ_Decke => { receive=>'LR_SZ_Decke_1', transmit=>'LK_SZ_Decke_1',
translate => sub { limit(0,$state+20*$input,100); }, },
@@ -54,5 +88,3 @@
# in einen absoluten Wert umgewandelt
);
-
-
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <j-...@us...> - 2012-05-07 18:51:28
|
Revision: 794
http://openautomation.svn.sourceforge.net/openautomation/?rev=794&view=rev
Author: j-n-k
Date: 2012-05-07 18:51:21 +0000 (Mon, 07 May 2012)
Log Message:
-----------
Inital commit for knxdmxd - A KNX-DMX controller using OLA
Added Paths:
-----------
tools/knxdmxd/
tools/knxdmxd/Makefile
tools/knxdmxd/knxdmxd
tools/knxdmxd/knxdmxd.c
tools/knxdmxd/knxdmxd.conf
Added: tools/knxdmxd/Makefile
===================================================================
--- tools/knxdmxd/Makefile (rev 0)
+++ tools/knxdmxd/Makefile 2012-05-07 18:51:21 UTC (rev 794)
@@ -0,0 +1,7 @@
+CC = g++
+CFLAGS = -Wall -Os
+LIBS = -L. -L/usr/lib -L/home/jan/ola-0.8.18/ola -lpthread -leibclient -lolacommon -lola -ljson
+
+knxdmxd: knxdmxd.c
+ $(CC) $(CFLAGS) knxdmxd.c $(LIBS) -o knxdmxd
+
Added: tools/knxdmxd/knxdmxd
===================================================================
(Binary files differ)
Property changes on: tools/knxdmxd/knxdmxd
___________________________________________________________________
Added: svn:executable
+ *
Added: svn:mime-type
+ application/octet-stream
Added: tools/knxdmxd/knxdmxd.c
===================================================================
--- tools/knxdmxd/knxdmxd.c (rev 0)
+++ tools/knxdmxd/knxdmxd.c 2012-05-07 18:51:21 UTC (rev 794)
@@ -0,0 +1,653 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * knxdmxd.c
+ * Copyright (C) Jan N. Klug 2012 <jan...@ru...>
+ * Daemon skeleton by Michael Markstaller 2011 <de...@wi...>
+ *
+ * knxdmxd is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * knxdmxd is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <signal.h>
+#include <getopt.h>
+#include <json/json.h>
+
+#include <ola/Logging.h>
+#include <ola/DmxBuffer.h>
+#include <ola/StreamingClient.h>
+#include <ola/StringUtils.h>
+
+//#include <assert.h>
+
+#include <arpa/inet.h>
+//#include <netinet/in.h>
+//#include <sys/socket.h>
+
+#include <pthread.h>
+#include <eibclient.h>
+
+#include <iostream>
+#include <fstream>
+#include <map>
+
+/*#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/xml_parser.hpp>
+#include <boost/foreach.hpp>*/
+
+#define DEBUG 1
+#define DAEMON_NAME "knxdmxd"
+#define USAGESTRING "\n"\
+ "\t-d Run as daemon/No debug output\n"\
+ "\t-p <pidfile> PID-filename\n"\
+ "\t-u <eib url> URL to contact eibd like local:/tmp/eib or ip:192.168.0.101\n"\
+ "\t-c <config-file> Config-File\n"
+#define NUM_THREADS 4
+#define MAX_ZONES 31
+#define RETRY_TIME 5
+#define BUFLEN 1024
+#define POLLING_INTERVAL 10
+#define FADING_INTERVAL 50000
+
+using namespace std;
+//namespace bpt = boost::property_tree;
+typedef vector<string> svector;
+
+void daemonShutdown();
+
+eibaddr_t readgaddr (const char *addr) {
+ int a, b, c;
+ if (sscanf (addr, "%d/%d/%d", &a, &b, &c) == 3)
+ return ((a & 0x01f) << 11) | ((b & 0x07) << 8) | ((c & 0xff));
+ if (sscanf (addr, "%d/%d", &a, &b) == 2)
+ return ((a & 0x01f) << 11) | ((b & 0x7FF));
+ if (sscanf (addr, "%x", &a) == 1)
+ return a & 0xffff;
+ syslog(LOG_WARNING,"invalid group address format %s", addr);
+ daemonShutdown();
+ return 0;
+}
+
+int readdaddr (const string addr) {
+ int universe, channel;
+ sscanf( (char*)addr.c_str(), "%d.%d", &universe, &channel);
+ if (channel==-1) { // default universe is 1
+ channel = universe;
+ universe = 1;
+ }
+ return (universe << 9) + channel;
+}
+
+namespace knxdmxd {
+
+class Fixture {
+ public:
+ Fixture() {};
+ Fixture(const string str);
+
+ void Patch(map<int, svector >& patchMap, const string channel, const int DMX, const int KNX);
+ void Patch(map<int, svector >& patchMap, const string channel, const string DMX, const string KNX);
+ void SetFadeTime(const float t);
+ void PatchFadeTime(const int KNX);
+ void Update(const int KNX, const int val);
+ void Refresh(map<int, ola::DmxBuffer>& output);
+ private:
+ string name;
+ map <string, int> channelKNX;
+ map <string, int> channelDMX;
+ map <string, int> channelValue;
+ map <string, float> channelFloatValue;
+ float fadeTime, fadeStep;
+ int fadeTimeKNX;
+};
+
+Fixture::Fixture(const string str) {
+ name=str;
+ syslog(LOG_DEBUG, "Created Fixture '%s'", (char *)str.c_str());
+}
+
+void Fixture::Patch(map<int, svector>& patchMap, const string channel, const int DMX, const int KNX) {
+ channelKNX[channel] = KNX;
+ channelDMX[channel] = DMX;
+
+ syslog(LOG_DEBUG, "Fixture '%s': Patched channel '%s' (KNX %d to DMX %d) ", (char *)name.c_str(), (char *)channel.c_str(), KNX, DMX);
+
+ svector assignedChannels = patchMap[KNX]; // check if already patched
+ for (vector<string>::iterator i = assignedChannels.begin(); i != assignedChannels.end(); ++i) {
+ if ((*i)==name) { // already patched;
+ return;
+ }
+ }
+
+ patchMap[KNX].push_back(name); // no, add to patchMap
+
+}
+
+void Fixture::Patch(map<int, svector>& patchMap, const string channel, const string DMX, const string KNX) {
+ Patch(patchMap, channel, readdaddr(DMX), readgaddr((char *) KNX.c_str()));
+}
+
+void Fixture::SetFadeTime(const float t) {
+ fadeTime = t;
+ fadeStep = (t<=0) ? 256 : 256/(t*1e6/FADING_INTERVAL);
+ syslog(LOG_DEBUG, "Fixture '%s': Set Fadetime to %.2f s'(%.2f steps/interval)", (char *)name.c_str(), fadeTime, fadeStep);
+}
+
+void Fixture::PatchFadeTime(const int KNX) {
+ fadeTimeKNX = KNX;
+}
+
+void Fixture::Update(const int KNX, const int val) {
+ for(std::map<string, int>::const_iterator i = channelKNX.begin(); i != channelKNX.end(); ++i) {
+ if (i->second == KNX) {
+ channelValue[i->first] = val;
+ syslog(LOG_DEBUG, "Updated channel '%s' to %d", (char *)i->first.c_str(), val);
+ }
+ }
+}
+
+void Fixture::Refresh(map<int, ola::DmxBuffer>& output) {
+ for(std::map<string, int>::const_iterator i = channelDMX.begin(); i != channelDMX.end(); ++i) {
+ int dmxuniverse = (int) (i->second / 512) , dmxchannel = i->second % 512;
+ int oldValue = output[dmxuniverse].Get(dmxchannel);
+ int newValue = channelValue[i->first];
+ if (oldValue<newValue) {
+ channelFloatValue[i->first] += fadeStep;
+ if (channelFloatValue[i->first]>newValue) {
+ channelFloatValue[i->first] = newValue;
+ }
+ output[dmxuniverse].SetChannel(dmxchannel, (int) channelFloatValue[i->first]);
+ }
+ if (oldValue>newValue) {
+ channelFloatValue[i->first] -= fadeStep;
+ if (channelFloatValue[i->first]<newValue) {
+ channelFloatValue[i->first] = newValue;
+ }
+ output[dmxuniverse].SetChannel(dmxchannel, (int) channelFloatValue[i->first]);
+ }
+ }
+}
+
+}
+
+pthread_mutex_t zonelock = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t initlock = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t standbylock = PTHREAD_MUTEX_INITIALIZER;
+
+string eibd_url = "local:/tmp/eib";
+string conf_file = "knxdmxd.conf";
+int pidFilehandle;
+string pidfilename = "/var/run/dmxknxd.pid";
+
+map<int, ola::DmxBuffer> dmxWriteBuffer;
+map<string, knxdmxd::Fixture> fixtureList;
+map<int, svector> patchMap;
+
+void daemonShutdown() {
+ //FIXME: clean exit pthread_exit(NULL); pthread_cancel(..);
+ //close(udpSocket);
+ syslog(LOG_INFO, "%s daemon exiting", DAEMON_NAME);
+ fprintf(stderr, "%s daemon exiting", DAEMON_NAME);
+ close(pidFilehandle);
+ unlink((char *)pidfilename.c_str());
+ exit(EXIT_SUCCESS);
+}
+
+void signal_handler(int sig) {
+ switch(sig) {
+ case SIGHUP:
+ syslog(LOG_WARNING, "Received SIGHUP signal.");
+ break;
+ case SIGTERM:
+ syslog(LOG_WARNING, "Received SIGTERM signal.");
+ daemonShutdown();
+ break;
+ case SIGINT:
+ syslog(LOG_WARNING, "Received SIGINT signal.");
+ daemonShutdown();
+ break;
+ default:
+ syslog(LOG_WARNING, "Unhandled signal (%d) %s", sig, strsignal(sig));
+ break;
+ }
+}
+
+/*void *sendKNXdgram(int type, int dpt, eibaddr_t dest, unsigned char val) {
+ int len = 0;
+ EIBConnection *con;
+ unsigned char buf[255] = { 0, 0x80 };
+ buf[1] = type; //0x40 response, 0x80 write
+ syslog(LOG_DEBUG,"Send KNX dgram Type %d DPT %d dest %d val %d",type,dpt,dest,val);
+ con = EIBSocketURL ((char *)eibd_url.c_str());
+ if (!con)
+ syslog(LOG_WARNING,"sendknxdgram: Open failed");
+ if (EIBOpenT_Group (con, dest, 1) == -1)
+ syslog(LOG_WARNING,"sendknxdgram: Connect failed");
+ switch (dpt)
+ {
+ case 0:
+ len=1;
+ //for(i = 0; i < strlen(val)/2; i++)
+ //raw value: nothing for now
+ break;
+ case 1:
+ buf[1] |= val & 0x3f;
+ len=2;
+ break;
+ case 3:
+ // EIS2/DPT3 4bit dim
+ buf[1] |= val & 0x3f;
+ len=2;
+ break;
+ case 5:
+ buf[2] = val*255/100;
+ len=3;
+ break;
+ case 51:
+ case 5001:
+ buf[2] = val;
+ len=3;
+ break;
+ case 6:
+ case 6001:
+ //FIXME: This is basically wrong but as we get a uchar we expect the sender to know..
+ buf[2] = val;
+ len=3;
+ break;
+ }
+
+ len = EIBSendAPDU (con, len, buf);
+ if (len == -1)
+ syslog (LOG_WARNING,"sendknxdram: Request failed");
+ EIBClose (con);
+ usleep(50*1000); //throttle a little (50ms/max 20tps)
+ return 0;
+} */
+
+void sigtime(int signo)
+{
+ for(std::map<string, knxdmxd::Fixture>::const_iterator i = fixtureList.begin(); i != fixtureList.end(); ++i) {
+ fixtureList[i->first].Refresh(dmxWriteBuffer);
+ }
+ signal(SIGALRM, sigtime);
+}
+
+void *worker(void *) {
+ syslog(LOG_DEBUG, "Internal worker thread started");
+
+ signal(SIGALRM, sigtime);
+
+ itimerval itm;
+ itm.it_interval.tv_sec=0;
+ itm.it_value.tv_sec = 0;
+ itm.it_interval.tv_usec = FADING_INTERVAL; // 20 ms is enough
+ itm.it_value.tv_usec = FADING_INTERVAL;
+ setitimer(ITIMER_REAL,&itm,0);
+
+ while (1) {
+ sleep(POLLING_INTERVAL);
+ }
+
+ pthread_exit(NULL);
+}
+
+/*void *sendKNXresponse(eibaddr_t dest, int zone, int func) {
+ syslog(LOG_DEBUG, "KNX response %d zone %d func %d", dest,zone,func);
+ pthread_mutex_lock (&zonelock);
+ switch (func) {
+ case 0:
+ case 20:
+ sendKNXdgram (0x40,1,dest,zones[zone].zonepower);
+ break;
+ case 1:
+ case 21:
+ sendKNXdgram (0x40,51,dest,zones[zone].srcid);
+ break;
+ case 2:
+ case 22:
+ sendKNXdgram (0x40,5,dest,zones[zone].volume*2);
+ break;
+ case 3:
+ case 23:
+ if (zones[zone].bass<10)
+ sendKNXdgram (0x40,51,dest,zones[zone].bass-10+256);
+ else
+ sendKNXdgram (0x40,51,dest,zones[zone].bass-10);
+ break;
+ case 4:
+ case 24:
+ if (zones[zone].treble<10)
+ sendKNXdgram (0x40,51,dest,zones[zone].treble-10+256);
+ else
+ sendKNXdgram (0x40,51,dest,zones[zone].treble-10);
+ break;
+ case 5:
+ case 25:
+ sendKNXdgram (0x40,1,dest,zones[zone].loudness);
+ break;
+ case 6:
+ case 26:
+ if (zones[zone].balance<10)
+ sendKNXdgram (0x40,51,dest,zones[zone].balance-10+256);
+ else
+ sendKNXdgram (0x40,51,dest,zones[zone].balance-10);
+ break;
+ case 27:
+ sendKNXdgram (0x40,1,dest,zones[zone].partymode);
+ break;
+ case 28:
+ sendKNXdgram (0x40,1,dest,zones[zone].dnd);
+ break;
+ case 29:
+ sendKNXdgram (0x40,5,dest,zones[zone].onvolume*2);
+ break;
+ }
+ pthread_mutex_unlock (&zonelock);
+ return 0;
+}
+*/
+void *handleKNXdgram(eibaddr_t dest, unsigned char* buf, int len){
+ unsigned char val;
+ switch (buf[1] & 0xC0) {
+ case 0x00:
+// sendKNXresponse (dest,zone+(controller*ZONES_PER_CONTROLLER),func);
+ break;
+ case 0x40:
+ //FIXME: response dunno
+ break;
+ case 0x80:
+ if (buf[1] & 0xC0) {
+ if (len == 2)
+ val = buf[1] & 0x3F;
+ else
+ val = buf[2];
+ svector uFixtures = patchMap.find(dest)->second;
+ for (unsigned int i=0; i < uFixtures.size(); i++) {
+ fixtureList[uFixtures[i]].Update(dest, val);
+ }
+ syslog(LOG_DEBUG, "Received %d @ %d, updated %d fixtures", val, dest, (int) uFixtures.size());
+ }
+ break;
+ }
+ return 0;
+}
+
+
+void *olahandler(void *) { // thread just reliably sends the data to DMX via OLA
+ syslog(LOG_DEBUG, "OLA sender thread started");
+
+ ola::InitLogging(ola::OLA_LOG_WARN, ola::OLA_LOG_STDERR);
+
+ while (1) { // retry forever
+
+ ola::StreamingClient ola_client;
+
+ if (!ola_client.Setup()) { // setup client
+ syslog(LOG_WARNING, "OLA: Client setup failed");
+ sleep(RETRY_TIME);
+ continue;
+ }
+
+ syslog(LOG_INFO, "OLA: Client connected");
+
+ while (1) { // loop forever
+ int error_flag=0;
+ for(std::map<int, ola::DmxBuffer>::const_iterator i = dmxWriteBuffer.begin(); i != dmxWriteBuffer.end(); ++i) {
+ int universe = i->first;
+ if (!ola_client.SendDmx(universe, i->second)) { // send all universes
+ syslog(LOG_WARNING, "OLA: failed to send universe %d", universe);
+ error_flag = 1;
+ break; // something went wrong
+ }
+ }
+ if (error_flag==1) {
+ sleep(RETRY_TIME);
+ break;
+ }
+
+ usleep(20000);
+ }
+
+ ola_client.Stop(); // close the client
+ }
+
+ pthread_exit(NULL);
+}
+
+
+void *knxhandler(void *) {
+ syslog(LOG_DEBUG, "KNX reader thread started");
+ int len;
+ EIBConnection *con;
+ eibaddr_t dest;
+ eibaddr_t src;
+ unsigned char buf[255];
+
+ while (1) //retry infinite
+ {
+ con = EIBSocketURL ((char *)eibd_url.c_str());
+ if (!con) {
+ syslog(LOG_WARNING, "eibd: Open failed");
+ sleep(RETRY_TIME);
+ continue;
+ }
+
+ if (EIBOpen_GroupSocket (con, 0) == -1) {
+ syslog(LOG_WARNING, "eibd: Connect failed");
+ sleep(RETRY_TIME);
+ continue;
+ }
+
+ while (1)
+ {
+ len = EIBGetGroup_Src (con, sizeof (buf), buf, &src, &dest);
+ if (len == -1) {
+ syslog(LOG_WARNING, "eibd: Read failed");
+ sleep(RETRY_TIME);
+ break;
+ }
+ if (len < 2) {
+ syslog(LOG_WARNING, "eibd: Invalid Packet");
+ break;
+ }
+ if (buf[0] & 0x3 || (buf[1] & 0xC0) == 0xC0) {
+ syslog(LOG_WARNING, "eibd: Unknown APDU from %d to %d",src,dest);
+ break;
+ } else {
+ if ( patchMap.count(dest)<=0 ) //not for us
+ continue;
+ handleKNXdgram(dest,buf,len);
+ }
+ }
+ syslog(LOG_WARNING,"eibd: closed connection"); //break in read-loop
+ EIBClose (con);
+ }
+ pthread_exit(NULL);
+}
+
+void load_config() {
+
+/* bpt::ptree config;
+
+ read_xml(conf_file, config);
+
+ BOOST_FOREACH( bpt::ptree::value_type const& e, config) {
+ if( e.first == "fixture" ) {
+ string name = e.second.get<std::string>("<xmlattr>.name");
+ knxdmxd::Fixture f(name);
+ BOOST_FOREACH( bpt::ptree::value_type const& c, e.second) {
+ if (c.first == "channel") {
+ f.Patch(patchMap, c.second.data(), c.second.get<std::string>("<xmlattr>.dmx"), c.second.get<std::string>("<xmlattr>.knx"));
+ }
+ if (c.first == "fading") {
+ float t;
+ string content = c.second.data();
+ sscanf((char*) content.c_str(), "%f", &t);
+ f.SetFadeTime((int) (t*1.e6));
+ }
+ }
+ fixtureList[name] = f;
+ }
+
+ } */
+ struct json_object *config;
+
+ config = json_object_from_file((char *)conf_file.c_str());
+ // first all fixtures
+ struct json_object *fixtures = json_object_object_get(config, "fixtures");
+
+ int fixturenum = json_object_array_length(fixtures);
+
+ for (int i=0; i<fixturenum; i++) { // read all
+ // get fixture
+ struct json_object *fixture = json_object_array_get_idx(fixtures, i);
+
+ // get name & create
+ string fname(json_object_get_string(json_object_object_get(fixture, "name")));
+ knxdmxd::Fixture f(fname);
+
+ // get channels & patch them
+ struct json_object *channels = json_object_object_get(fixture, "channels");
+ int channelnum = json_object_array_length(channels);
+ for (int j=0; j<channelnum; j++) { // read all
+ // get channel
+ struct json_object *channel = json_object_array_get_idx(channels, j);
+ string cname(json_object_get_string(json_object_object_get(channel, "name")));
+ string cdmx(json_object_get_string(json_object_object_get(channel, "dmx")));
+ string cknx(json_object_get_string(json_object_object_get(channel, "knx")));
+ f.Patch(patchMap, cname, cdmx, cknx);
+ }
+
+ // get fading
+ struct json_object *fading = json_object_object_get(fixture, "fading");
+ float ftime = (fading) ? json_object_get_double(json_object_object_get(fading, "time")) : 0;
+ f.SetFadeTime(ftime);
+
+ fixtureList[fname] = f;
+ }
+
+ return;
+}
+
+int main(int argc, char **argv) {
+ int daemonize = 0;
+ int c;
+ //char *p;
+ char pidstr[255];
+
+ while ((c = getopt (argc, argv, "d:p:u:c:")) != -1)
+ switch (c) {
+ case 'd':
+ daemonize = 1;
+ break;
+ case 'p':
+ pidfilename.assign(optarg);
+ break;
+ case 'u':
+ eibd_url.assign(optarg);
+ break;
+ case 'c':
+ conf_file.assign(optarg);
+ break;
+ case '?':
+ //FIXME: check arguments better, print_usage
+ fprintf (stderr, "Unknown option `-%c'.\nUsage: %s %s", optopt, argv[0], USAGESTRING);
+ return 1;
+ default:
+ abort ();
+ }
+
+ //FIXME: clean shutdown in sub-thread with signals?
+ signal(SIGHUP, signal_handler);
+ signal(SIGTERM, signal_handler);
+ signal(SIGINT, signal_handler);
+ signal(SIGQUIT, signal_handler);
+
+ if (!daemonize) {
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+ openlog(DAEMON_NAME, LOG_CONS | LOG_NDELAY | LOG_PERROR | LOG_PID, LOG_USER);
+ syslog(LOG_DEBUG, "startup with debug; pidfile: %s, eibd: %s",
+ (char *)pidfilename.c_str(), (char *)eibd_url.c_str());
+ } else {
+ setlogmask(LOG_UPTO(LOG_INFO));
+ openlog(DAEMON_NAME, LOG_CONS, LOG_USER);
+ }
+
+ syslog(LOG_INFO, "using config-file %s", (char *)conf_file.c_str());
+
+ load_config();
+
+ pid_t pid, sid;
+
+ if (daemonize) {
+ syslog(LOG_INFO, "starting daemon");
+
+ pid = fork();
+ if (pid < 0) {
+ exit(EXIT_FAILURE);
+ }
+ if (pid > 0) {
+ exit(EXIT_SUCCESS);
+ }
+ umask(0);
+ sid = setsid();
+ if (sid < 0) {
+ exit(EXIT_FAILURE);
+ }
+ if ((chdir("/")) < 0) {
+ exit(EXIT_FAILURE);
+ }
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ }
+ //FIXME: output errors to stderr, change order
+ pidFilehandle = open((char *)pidfilename.c_str(), O_RDWR|O_CREAT, 0600);
+ if (pidFilehandle == -1 )
+ {
+ syslog(LOG_INFO, "Could not open pidfile %s, exiting", (char *)pidfilename.c_str());
+ fprintf(stderr, "Could not open pidfile %s, exiting", (char *)pidfilename.c_str());
+ exit(EXIT_FAILURE);
+ }
+ if (lockf(pidFilehandle,F_TLOCK,0) == -1)
+ {
+ syslog(LOG_INFO, "Could not lock pidfile %s, exiting", (char *)pidfilename.c_str());
+ fprintf(stderr, "Could not lock pidfile %s, exiting", (char *)pidfilename.c_str());
+ exit(EXIT_FAILURE);
+ }
+ sprintf(pidstr,"%d\n",getpid());
+ c = write(pidFilehandle, pidstr, strlen(pidstr));
+
+ int knxthread, olathread, workerthread;
+ pthread_t threads[NUM_THREADS];
+ // PTHREAD_CREATE_DETACHED?
+ knxthread = pthread_create(&threads[1], NULL, knxhandler, NULL); //id, thread attributes, subroutine, arguments
+ olathread = pthread_create(&threads[2], NULL, olahandler, NULL); //id, thread attributes, subroutine, arguments
+ workerthread = pthread_create(&threads[3], NULL, worker, NULL); //id, thread attributes, subroutine, arguments
+ syslog(LOG_DEBUG,"Threads created: %d %d %d",knxthread, olathread, workerthread);
+ //TODO: Maybe another console/TCP-server/Logging thread?
+ while (1) {
+// syslog(LOG_DEBUG, "%s daemon running", DAEMON_NAME);
+ sleep(POLLING_INTERVAL);
+ }
+ syslog(LOG_INFO, "%s daemon exiting", DAEMON_NAME);
+ // TODO: Free any allocated resources before exiting - we never get here though -> signal handler
+ exit(EXIT_SUCCESS);
+}
Added: tools/knxdmxd/knxdmxd.conf
===================================================================
--- tools/knxdmxd/knxdmxd.conf (rev 0)
+++ tools/knxdmxd/knxdmxd.conf 2012-05-07 18:51:21 UTC (rev 794)
@@ -0,0 +1,50 @@
+{ "fixtures": [
+ { "name" : "Treppe",
+ "channels" : [
+ { "name": "oben",
+ "knx" : "1/7/170",
+ "dmx" : "1.0"
+ },
+ { "name": "mitte",
+ "knx" : "1/7/170",
+ "dmx" : "1.1"
+ },
+ { "name": "unten",
+ "knx" : "1/7/170",
+ "dmx" : "1.2"
+ }
+ ],
+ "fading" : {
+ "knx" : "1/6/170",
+ "time": "1.7"
+ }
+ },
+ { "name" : "Kueche",
+ "channels" : [
+ { "name": "R",
+ "knx" : "1/7/141",
+ "dmx" : "1.10"
+ },
+ { "name": "G",
+ "knx" : "1/7/142",
+ "dmx" : "1.11"
+ },
+ { "name": "B",
+ "knx" : "1/7/143",
+ "dmx" : "1.12"
+ }
+ ],
+ "fading" : {
+ "knx" : "1/6/141",
+ "time": "3.0"
+ }
+ },
+ { "name" : "Test",
+ "channels" : [
+ { "name": "TestC",
+ "knx": "1/7/170",
+ "dmx" : "1.100"
+ }
+ ]
+ } ]
+}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-06 19:15:59
|
Revision: 793
http://openautomation.svn.sourceforge.net/openautomation/?rev=793&view=rev
Author: pfry
Date: 2012-05-06 19:15:53 +0000 (Sun, 06 May 2012)
Log Message:
-----------
Modified Paths:
--------------
wiregate/plugin/generic/Logikprozessor.pl
wiregate/plugin/generic/conf.d/Logikprozessor.conf
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-05-06 12:19:51 UTC (rev 792)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-06 19:15:53 UTC (rev 793)
@@ -328,11 +328,11 @@
next unless defined $result;
knx_write($transmit, $result);
-
- # Timer fuer nachste Aktion setzen
- set_timer();
}
+# Timer fuer nachste Aktion setzen
+set_timer();
+
return unless $retval;
return $retval;
Modified: wiregate/plugin/generic/conf.d/Logikprozessor.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-06 12:19:51 UTC (rev 792)
+++ wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-06 19:15:53 UTC (rev 793)
@@ -5,16 +5,16 @@
%logic=(
# 1. Alle Werte, die auf einer GA gesendet werden, werden mit 2 multipliziert auf einer anderen GA weitergegeben
- mal2 => { receive=>'9/5/201', transmit=>'9/5/202', translate => sub { my (undef,$input)=@_; 2*$input; }, },
+ mal2 => { receive=>'9/5/201', transmit=>'9/5/202', translate => sub { 2*$input; }, },
# das "undef" steht da einfach, weil uns der letzte Ergebniswert nicht interessiert
# 2. Die Werte auf der ersten GA werden aufsummiert, das Ergebis auf der anderen GA gesendet.
# Damit kann man bspw aus einem relativen Dimmwert einen absoluten Dimmwert machen.
- sum => { receive=>'9/5/207', transmit=>'9/5/208', translate => sub { my ($state,$input)=@_; $state+$input; }, },
+ sum => { receive=>'9/5/207', transmit=>'9/5/208', translate => sub { $state+$input; }, },
# $state enthaelt das jeweils letzte Ergebnis.
# 3. Wie oben, aber das Ergebnis limitiert auf den Bereich 0-100
- lsum => { receive=>'1/2/7', transmit=>'1/2/8', translate => sub { my ($state,$input)=@_; limit(0,$state+$input,100); }, },
+ lsum => { receive=>'1/2/7', transmit=>'1/2/8', translate => sub { limit(0,$state+$input,100); }, },
# 4. Hier eine "Treppenlichtfunktion". Auf jeden Schreibzugriff auf die receive-Adresse wird 10min spaeter eine 0 an
# die transmit-Adresse (hier gleich) geschickt. Verzoegert wird uebrigens nur das Senden, nicht das Ausfuehren der
@@ -33,7 +33,7 @@
# 6. Eine einfache UND-Logik mit zwei Eingaengen. Falls ein Telegramm auf einer der beiden receive-GAs empfangen wird,
# wird die andere Adresse noch ausgelesen, die Logik angewendet und das Ergebnis auf der transmit-GA uebermittelt
- und => { receive=>['1/2/12','1/2/13'], transmit=>'1/2/14', translate => sub { my (undef,$input)=@_; $input->[0] && $input->[1]; }, },
+ und => { receive=>['1/2/12','1/2/13'], transmit=>'1/2/14', translate => sub { $input->[0] && $input->[1]; }, },
# 7. Ein komplexerer Fall nur zur Demonstration: hier besteht der Status des Logikprozessors aus mehreren Werten.
# Es wird immer die Summe aus letztem, vorletztem und aktuellem Wert gesendet, und zwar mit 30s Verzoegerung.
@@ -41,8 +41,7 @@
transmit=>'9/5/206',
delay=>30,
state => {val1=>0, val2=>0},
- translate => sub { my ($state,$input)=@_;
- $state->{val2}=$state->{val1}; $state->{val1}=$state->{result};
+ translate => sub { $state->{val2}=$state->{val1}; $state->{val1}=$state->{result};
$state->{val2}+$state->{val1}+$input; },
},
# Wenn state ein Hash ist, wird der letzte gesendete Wert in $state->{result} gespeichert.
@@ -50,7 +49,7 @@
# 8. Schlussendlich wieder mal Werbung fuer die GA-Kurznamen. Setzt man im Skript Logikprozessor.pl $use_short_names=1
# und verwendet GA-Namen mit eindeutigem Kuerzel (=erstes Wort des Namens), so funktioniert auch das folgende:
D_SZ_Decke => { receive=>'LR_SZ_Decke_1', transmit=>'LK_SZ_Decke_1',
- translate => sub { my ($state,$input)=@_; limit(0,$state+20*$input,100); }, },
+ translate => sub { limit(0,$state+20*$input,100); }, },
# ist doch leserlicher, oder? Hier wird ein relativer Dimmwert durch Skalierung und Summierung
# in einen absoluten Wert umgewandelt
);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <ma...@us...> - 2012-05-06 12:19:58
|
Revision: 792
http://openautomation.svn.sourceforge.net/openautomation/?rev=792&view=rev
Author: mayerch
Date: 2012-05-06 12:19:51 +0000 (Sun, 06 May 2012)
Log Message:
-----------
New feature: allow full JavaScript formulas for a mapping
Modified Paths:
--------------
CometVisu/trunk/visu/lib/templateengine.js
CometVisu/trunk/visu/visu_config_demo.xml
Modified: CometVisu/trunk/visu/lib/templateengine.js
===================================================================
--- CometVisu/trunk/visu/lib/templateengine.js 2012-05-06 11:12:01 UTC (rev 791)
+++ CometVisu/trunk/visu/lib/templateengine.js 2012-05-06 12:19:51 UTC (rev 792)
@@ -106,15 +106,23 @@
function map( value, element ) {
var map = element.data('mapping');
- if( map && mappings[map] && (mappings[map][value] || mappings[map]['range']) ) {
- if( mappings[map]['range'] ) value = parseFloat( value );
- if( mappings[map][value] ) return mappings[map][value];
-
- var range = mappings[map]['range'];
- for( var min in range ) {
- if( min > value ) continue;
- if( range[min][0] < value ) continue; // check max
- return range[min][1];
+ if( map && mappings[map] )
+ {
+ var m = mappings[map];
+
+ if( m.formula ) {
+ return m.formula( value );
+ } else if( m[value] ) {
+ return m[value];
+ } else if( m['range'] ) {
+ var valueFloat = parseFloat( value );
+
+ var range = m['range'];
+ for( var min in range ) {
+ if( min > valueFloat ) continue;
+ if( range[min][0] < valueFloat ) continue; // check max
+ return range[min][1];
+ }
}
}
return value;
@@ -230,18 +238,27 @@
// then the mappings
$( 'meta > mappings mapping', xml ).each( function(i){
- var name = $(this).attr('name');
+ var $this = $(this);
+ var name = $this.attr('name');
mappings[ name ] = {};
- $(this).find('entry').each( function(){
- if( $(this).attr('value') )
- {
- mappings[ name ][ $(this).attr('value') ] = $(this).text();
- } else {
- if( ! mappings[ name ][ 'range' ] ) mappings[ name ][ 'range' ] = {};
- mappings[ name ][ 'range' ][ parseFloat($(this).attr('range_min')) ] =
- [ parseFloat( $(this).attr('range_max') ), $(this).text() ];
- }
- });
+ var formula = $this.find('formula');
+ if( formula.length > 0 )
+ {
+ eval( 'var func = function(x){' + formula.text() + '; return y;}' );
+ mappings[ name ][ 'formula' ] = func;
+ } else {
+ $this.find('entry').each( function(){
+ var $localThis = $(this);
+ if( $localThis.attr('value') )
+ {
+ mappings[ name ][ $localThis.attr('value') ] = $localThis.text();
+ } else {
+ if( ! mappings[ name ][ 'range' ] ) mappings[ name ][ 'range' ] = {};
+ mappings[ name ][ 'range' ][ parseFloat($localThis.attr('range_min')) ] =
+ [ parseFloat( $localThis.attr('range_max') ), $localThis.text() ];
+ }
+ });
+ }
});
// then the stylings
Modified: CometVisu/trunk/visu/visu_config_demo.xml
===================================================================
--- CometVisu/trunk/visu/visu_config_demo.xml 2012-05-06 11:12:01 UTC (rev 791)
+++ CometVisu/trunk/visu/visu_config_demo.xml 2012-05-06 12:19:51 UTC (rev 792)
@@ -47,6 +47,9 @@
<entry value="4">Sat</entry>
<entry value="5">Kino</entry>
</mapping>
+ <mapping name="One1000th">
+ <formula>y = x/1000;</formula>
+ </mapping>
</mappings>
<stylings>
<styling name="Red_Green">
@@ -365,6 +368,11 @@
<label>Format "%+.2f":</label>
<address transform="DPT:9">12/7/9</address>
</info>
+ <line/>
+ <info format="%.4f" mapping="One1000th">
+ <label>Format "%.4f + Formel":</label>
+ <address transform="DPT:9">12/7/9</address>
+ </info>
</page>
<page name="Stylings Test" align="center">
<switch mapping="On_Off" styling="Green_Red">
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <ma...@us...> - 2012-05-06 11:12:07
|
Revision: 791
http://openautomation.svn.sourceforge.net/openautomation/?rev=791&view=rev
Author: mayerch
Date: 2012-05-06 11:12:01 +0000 (Sun, 06 May 2012)
Log Message:
-----------
New feature: initial implementation of <pagejump> widget
Modified Paths:
--------------
CometVisu/trunk/visu/index.html
CometVisu/trunk/visu/visu_config_demo.xml
Added Paths:
-----------
CometVisu/trunk/visu/structure/pure/pagejump.js
Modified: CometVisu/trunk/visu/index.html
===================================================================
--- CometVisu/trunk/visu/index.html 2012-05-06 10:41:10 UTC (rev 790)
+++ CometVisu/trunk/visu/index.html 2012-05-06 11:12:01 UTC (rev 791)
@@ -31,6 +31,7 @@
<script src="structure/pure/multitrigger.js" type="text/javascript"></script>
<script src="structure/pure/navbar.js" type="text/javascript"></script>
<script src="structure/pure/page.js" type="text/javascript"></script>
+ <script src="structure/pure/pagejump.js" type="text/javascript"></script>
<script src="structure/pure/slide.js" type="text/javascript"></script>
<script src="structure/pure/switch.js" type="text/javascript"></script>
<script src="structure/pure/text.js" type="text/javascript"></script>
Added: CometVisu/trunk/visu/structure/pure/pagejump.js
===================================================================
--- CometVisu/trunk/visu/structure/pure/pagejump.js (rev 0)
+++ CometVisu/trunk/visu/structure/pure/pagejump.js 2012-05-06 11:12:01 UTC (rev 791)
@@ -0,0 +1,68 @@
+/* trigger.js (c) 2012 by Christian Mayer [CometVisu at ChristianMayer dot de]
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+basicdesign.addCreator('pagejump', {
+ create: function( element, path ) {
+ var $e = $(element);
+ var layout = $e.children('layout')[0];
+ var style = layout ? 'style="' + extractLayout( layout ) + '"' : '';
+ var target = $e.attr('target') ? $e.attr('target') : '0';
+ var ret_val = $('<div class="widget clearfix switch" ' + style + ' />');
+ ret_val.setWidgetLayout($e);
+ var labelElement = $e.find('label')[0];
+ var label = labelElement ? '<div class="label">' + labelElement.textContent + '</div>' : '';
+ var address = makeAddressList($e);
+ var actor = '<div class="actor switchUnpressed ';
+ if ( $e.attr( 'align' ) )
+ actor += $e.attr( 'align' );
+ actor += '">';
+ if( $e.attr( 'name' ) )
+ actor += '<div class="value">' + $e.attr( 'name' ) + '</div>';
+ else
+ actor += '<div class="value">' + target + '</div>';
+ actor += '</div>';
+ var $actor = $(actor).data( {
+ 'styling' : $(element).attr('styling'),
+ 'type' : 'pagejump',
+ 'align' : $e.attr('align'),
+ 'target' : target
+ } ).bind( 'click', this.action ).bind( 'mousedown', function(){
+ $(this).removeClass('switchUnpressed').addClass('switchPressed');
+ } ).bind( 'mouseup mouseout', function(){ // not perfect but simple
+ $(this).removeClass('switchPressed').addClass('switchUnpressed');
+ } ).setWidgetStyling(target);
+ ret_val.append( label ).append( $actor );
+ return ret_val;
+ },
+ action: function() {
+ var data = $(this).data();
+ scrollToPage( data.target );
+ },
+ attributes: {
+ traget: { type: 'string' , required: true },
+ name: { type: 'string' , required: false },
+ styling: { type: 'styling', required: false },
+ align: { type: 'string' , required: false },
+ colspan: { type: 'numeric', required: false },
+ rowspan: { type: 'numeric', required: false }
+ },
+ elements: {
+ label: { type: 'string' , required: true , multi: false },
+ layout: { type: 'layout' , required: false, multi: false }
+ },
+ content: false
+});
\ No newline at end of file
Modified: CometVisu/trunk/visu/visu_config_demo.xml
===================================================================
--- CometVisu/trunk/visu/visu_config_demo.xml 2012-05-06 10:41:10 UTC (rev 790)
+++ CometVisu/trunk/visu/visu_config_demo.xml 2012-05-06 11:12:01 UTC (rev 791)
@@ -108,7 +108,15 @@
<navbar position="left" dynamic="true" width="200px">
<text>Test 1</text>
<line/>
- <text>Test 2</text>
+ <pagejump target="0_33_7" name="Lithium">
+ <label>Flavour</label>
+ </pagejump>
+ <pagejump target="0_33_8" name="Potassium">
+ <label>Flavour</label>
+ </pagejump>
+ <pagejump target="0_36">
+ <label>2D</label>
+ </pagejump>
</navbar>
<text align="center">Herzlich willkommen bei dem CometVisu Widget Demo!</text>
<text><b>Hinweis:</b><br/>Alle Widgets verwenden KNX-Addressen mit der Haupt- und Mittelgruppe 12/7, da diese meist unbenutzt sind und so beim Testen oft keine Nebenwirkungen auftreten können. Sollten diese Gruppe bei Ihnen verwendet werden, so sollten Sie diese Widgets hier nicht betätigen!</text>
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <ma...@us...> - 2012-05-06 10:41:16
|
Revision: 790
http://openautomation.svn.sourceforge.net/openautomation/?rev=790&view=rev
Author: mayerch
Date: 2012-05-06 10:41:10 +0000 (Sun, 06 May 2012)
Log Message:
-----------
Fix wrong month name
Modified Paths:
--------------
CometVisu/trunk/visu/plugins/jqclock/jqclock.min.js
Modified: CometVisu/trunk/visu/plugins/jqclock/jqclock.min.js
===================================================================
--- CometVisu/trunk/visu/plugins/jqclock/jqclock.min.js 2012-05-06 10:36:37 UTC (rev 789)
+++ CometVisu/trunk/visu/plugins/jqclock/jqclock.min.js 2012-05-06 10:41:10 UTC (rev 790)
@@ -1 +1 @@
-(function($){$.clock={version:"2.0.1",locale:{}};t=[];$.fn.clock=function(d){var c={it:{weekdays:["Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato"],months:["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"]},en:{weekdays:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],months:["January","February","March","April","May","June","July","August","September","October","November","December"]},es:{weekdays:["Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado"],months:["Enero","Febrero","Marzo","Abril","May","junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"]},de:{weekdays:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],months:["Januar","Februar","März","April","könnte","Juni","Juli","August","September","Oktober","November","Dezember"]},fr:{weekdays:["Dimanche","Lundi","Mardi","Mercredi","Jeudi","Vendredi","Samedi"],months:["Janvier","Février","Mars","Avril","May","Juin","Juillet","Août","Septembre","Octobre","Novembre","Décembre"]},ru:{weekdays:["Воскресенье","Понедельник","Вторник","Среда","Четверг","Пятница","Суббота"],months:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"]}};return this.each(function(){$.extend(c,$.clock.locale);d=d||{};d.timestamp=d.timestamp||"z";y=new Date().getTime();d.sysdiff=0;if(d.timestamp!="z"){d.sysdiff=d.timestamp-y}d.langSet=d.langSet||"en";d.format=d.format||((d.langSet!="en")?"24":"12");d.calendar=d.calendar||"true";if(!$(this).hasClass("jqclock")){$(this).addClass("jqclock")}var e=function(g){if(g<10){g="0"+g}return g},f=function(j,n){var r=$(j).attr("id");if(n=="destroy"){clearTimeout(t[r])}else{m=new Date(new Date().getTime()+n.sysdiff);var p=m.getHours(),l=m.getMinutes(),v=m.getSeconds(),u=m.getDay(),i=m.getDate(),k=m.getMonth(),q=m.getFullYear(),o="",z="",w=n.langSet;if(n.format=="12"){o=" AM";if(p>11){o=" PM"}if(p>12){p=p-12}if(p==0){p=12}}p=e(p);l=e(l);v=e(v);if(n.calendar!="false"){z=((w=="en")?"<span class='clockdate'>"+c[w].weekdays[u]+", "+c[w].months[k]+" "+i+", "+q+"</span>":"<span class='clockdate'>"+c[w].weekdays[u]+", "+i+" "+c[w].months[k]+" "+q+"</span>")}$(j).html(z+"<span class='clocktime'>"+p+":"+l+":"+v+o+"</span>");t[r]=setTimeout(function(){f($(j),n)},1000)}};f($(this),d)})};return this})(jQuery);
+(function($){$.clock={version:"2.0.1",locale:{}};t=[];$.fn.clock=function(d){var c={it:{weekdays:["Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato"],months:["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"]},en:{weekdays:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],months:["January","February","March","April","May","June","July","August","September","October","November","December"]},es:{weekdays:["Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado"],months:["Enero","Febrero","Marzo","Abril","May","junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"]},de:{weekdays:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],months:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"]},fr:{weekdays:["Dimanche","Lundi","Mardi","Mercredi","Jeudi","Vendredi","Samedi"],months:["Janvier","Février","Mars","Avril","May","Juin","Juillet","Août","Septembre","Octobre","Novembre","Décembre"]},ru:{weekdays:["Воскресенье","Понедельник","Вторник","Среда","Четверг","Пятница","Суббота"],months:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"]}};return this.each(function(){$.extend(c,$.clock.locale);d=d||{};d.timestamp=d.timestamp||"z";y=new Date().getTime();d.sysdiff=0;if(d.timestamp!="z"){d.sysdiff=d.timestamp-y}d.langSet=d.langSet||"en";d.format=d.format||((d.langSet!="en")?"24":"12");d.calendar=d.calendar||"true";if(!$(this).hasClass("jqclock")){$(this).addClass("jqclock")}var e=function(g){if(g<10){g="0"+g}return g},f=function(j,n){var r=$(j).attr("id");if(n=="destroy"){clearTimeout(t[r])}else{m=new Date(new Date().getTime()+n.sysdiff);var p=m.getHours(),l=m.getMinutes(),v=m.getSeconds(),u=m.getDay(),i=m.getDate(),k=m.getMonth(),q=m.getFullYear(),o="",z="",w=n.langSet;if(n.format=="12"){o=" AM";if(p>11){o=" PM"}if(p>12){p=p-12}if(p==0){p=12}}p=e(p);l=e(l);v=e(v);if(n.calendar!="false"){z=((w=="en")?"<span class='clockdate'>"+c[w].weekdays[u]+", "+c[w].months[k]+" "+i+", "+q+"</span>":"<span class='clockdate'>"+c[w].weekdays[u]+", "+i+" "+c[w].months[k]+" "+q+"</span>")}$(j).html(z+"<span class='clocktime'>"+p+":"+l+":"+v+o+"</span>");t[r]=setTimeout(function(){f($(j),n)},1000)}};f($(this),d)})};return this})(jQuery);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <ma...@us...> - 2012-05-06 10:36:43
|
Revision: 789
http://openautomation.svn.sourceforge.net/openautomation/?rev=789&view=rev
Author: mayerch
Date: 2012-05-06 10:36:37 +0000 (Sun, 06 May 2012)
Log Message:
-----------
New feature: initial implementation of navbars
Added Paths:
-----------
CometVisu/trunk/visu/structure/pure/navbar.js
Added: CometVisu/trunk/visu/structure/pure/navbar.js
===================================================================
--- CometVisu/trunk/visu/structure/pure/navbar.js (rev 0)
+++ CometVisu/trunk/visu/structure/pure/navbar.js 2012-05-06 10:36:37 UTC (rev 789)
@@ -0,0 +1,61 @@
+/* unknown.js (c) 2012 by Christian Mayer [CometVisu at ChristianMayer dot de]
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+basicdesign.addCreator('navbar', {
+ create: function( navbar, path, flavour ) {
+ var $n = $(navbar);
+ var childs = $n.children();
+ var container = $( '<div class="clearfix"/>' );
+ if( $n.attr('name') ) container.append( '<h2 ' + hstyle + '>' + $n.attr('name') + '</h2>' );
+ $( childs ).each( function(i){
+ container.append( create_pages( childs[i], path + '_' + i, flavour ) );
+ } );
+
+ var dynamic = $n.attr('dynamic') ? true : false;
+ var position = $n.attr('position') || 'left';
+ switch( position )
+ {
+ case 'top':
+ $('#navbarTop').append( container );
+ break;
+
+ case 'left':
+ $('#navbarLeft').append( container );
+ navbarSetSize( 'left', 300 ); // FIXME - only a temporal solution
+ break;
+
+ case 'right':
+ $('#navbarRight').append( container );
+ navbarSetSize( 'right', 300 ); // FIXME - only a temporal solution
+ break;
+
+ case 'bottom':
+ $('#navbarBottom').append( container );
+ break;
+ }
+ navbars[position].dynamic |= dynamic;
+
+ var ret_val = $('');
+ return ret_val;
+ },
+ attributes: {
+ name: { type: 'string', required: false },
+ position: { type: 'list' , required: true , list: { top: 'top', left: 'left', right: 'right', bottom: 'bottom' } },
+ dynamic: { type: 'list' , required: true , list: { 'false': 'Fixed size', 'true': 'Dynamic' } }
+ },
+ content: true
+});
\ No newline at end of file
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <ma...@us...> - 2012-05-05 21:39:53
|
Revision: 788
http://openautomation.svn.sourceforge.net/openautomation/?rev=788&view=rev
Author: mayerch
Date: 2012-05-05 21:39:46 +0000 (Sat, 05 May 2012)
Log Message:
-----------
New feature: initial implementation of navbars
Modified Paths:
--------------
CometVisu/trunk/visu/designs/designglobals.css
CometVisu/trunk/visu/index.html
CometVisu/trunk/visu/lib/templateengine.js
CometVisu/trunk/visu/visu_config_demo.xml
Modified: CometVisu/trunk/visu/designs/designglobals.css
===================================================================
--- CometVisu/trunk/visu/designs/designglobals.css 2012-05-05 20:03:52 UTC (rev 787)
+++ CometVisu/trunk/visu/designs/designglobals.css 2012-05-05 21:39:46 UTC (rev 788)
@@ -2,6 +2,36 @@
* global CSS declarations for all designs
*/
+/*
+ * create a three column layout in the middle of the page to allow navbars
+ * inspired by http://www.alistapart.com/articles/holygrail
+ */
+#centerContainer {
+ padding-left: 0px; /* left bar width */
+ padding-right: 0px; /* right bar width */
+}
+#centerContainer > div {
+ position: relative;
+ float: left;
+}
+#main {
+ width: 100%;
+}
+#navbarLeft {
+ width: 0px; /* left bar width */
+ right: 0px; /* left bar width */
+ margin-left: -100%;
+}
+#navbarRight {
+ width: 0px; /* right bar width */
+ margin-right: 0px; /* right bar width */
+}
+#navbarTop {
+}
+#navbarBottom {
+ clear: both;
+}
+
/*
* colspans, each design needs to define a colspan0
* class, this class is the default widget size
Modified: CometVisu/trunk/visu/index.html
===================================================================
--- CometVisu/trunk/visu/index.html 2012-05-05 20:03:52 UTC (rev 787)
+++ CometVisu/trunk/visu/index.html 2012-05-05 21:39:46 UTC (rev 788)
@@ -29,6 +29,7 @@
<script src="structure/pure/infotrigger.js" type="text/javascript"></script>
<script src="structure/pure/line.js" type="text/javascript"></script>
<script src="structure/pure/multitrigger.js" type="text/javascript"></script>
+ <script src="structure/pure/navbar.js" type="text/javascript"></script>
<script src="structure/pure/page.js" type="text/javascript"></script>
<script src="structure/pure/slide.js" type="text/javascript"></script>
<script src="structure/pure/switch.js" type="text/javascript"></script>
@@ -53,10 +54,17 @@
<div id="top" class="loading">
<div class="nav_path">-</div>
</div>
- <div id="main" style="width:900px;position:relative; overflow: hidden;" class="loading">
- <div id="pages" class="clearfix" style="width:20000em; position:relative;clear:both;"><!-- all pages will be inserted here -->
+ <div id="navbarTop" class="loading"></div>
+ <div id="centerContainer">
+ <div id="main" style="width:900px;position:relative; overflow: hidden;" class="loading">
+ <div id="pages" class="clearfix" style="width:20000em; position:relative;clear:both;">
+ <!-- all pages will be inserted here -->
+ </div>
</div>
+ <div id="navbarLeft" class="loading page"></div>
+ <div id="navbarRight" class="loading page"></div>
</div>
+ <div id="navbarBottom" class="loading"></div>
<div id="bottom" class="loading">
<hr />
<div class="footer"></div>
Modified: CometVisu/trunk/visu/lib/templateengine.js
===================================================================
--- CometVisu/trunk/visu/lib/templateengine.js 2012-05-05 20:03:52 UTC (rev 787)
+++ CometVisu/trunk/visu/lib/templateengine.js 2012-05-05 21:39:46 UTC (rev 788)
@@ -22,6 +22,12 @@
var mappings = {}; // store the mappings
var stylings = {}; // store the stylings
+var navbars = { // store informations about the nav bars
+ top: { dynamic: false },
+ left: { dynamic: false },
+ right: { dynamic: false },
+ bottom: { dynamic: false }
+};
var ga_list = [];
@@ -120,15 +126,20 @@
*/
function handleResize() {
var uagent = navigator.userAgent.toLowerCase();
-
+ var widthNavbarLeft = $( '#navbarLeft' ).width();
+ var widthNavbarRight = $( '#navbarRight' ).width();
+ var width = $( window ).width() - widthNavbarLeft - widthNavbarRight;
+
if (/(android|blackberry|iphone|ipod|series60|symbian|windows ce|palm)/i.test(uagent)) {
- var width = $( window ).width();
$( '#main' ).css( 'width', width );
$( 'head' ).append( '<style type="text/css">.page{width:' + (width-0) + 'px;}</style>' );
// do nothing
} else {
- var width = $( window ).width();
- var height = $( window ).height() - $( '#top' ).outerHeight(true) - $( '#bottom' ).outerHeight(true) - 2;
+ var height = $( window ).height()
+ - $( '#top' ).outerHeight(true)
+ - $( '#navbarTop' ).outerHeight(true)
+ - $( '#navbarBottom' ).outerHeight(true)
+ - $( '#bottom' ).outerHeight(true) - 2;
$( '#main' ).css( 'width', width ).css( 'height', height );
$( 'head' ).append( '<style type="text/css">.page{width:' + (width-0) + 'px;height:' + height + 'px;}</style>' );
}
@@ -537,4 +548,27 @@
})
})
-}
\ No newline at end of file
+}
+
+/**
+ * Change the size of the selected navbar
+ *
+ * currently only "left" and "right" are implemented
+ */
+function navbarSetSize( position, size )
+{
+ var cssSize = size + 'px';
+ switch( position )
+ {
+ case 'left':
+ $( '#centerContainer' ).css( 'padding-left', cssSize );
+ $( '#navbarLeft' ).css( { width: cssSize, right: cssSize } );
+ break;
+
+ case 'right':
+ $( '#centerContainer' ).css( 'padding-right', cssSize );
+ $( '#navbarRight' ).css( { width: cssSize, 'margin-right': '-' + cssSize } );
+ break;
+ }
+ handleResize();
+}
Modified: CometVisu/trunk/visu/visu_config_demo.xml
===================================================================
--- CometVisu/trunk/visu/visu_config_demo.xml 2012-05-05 20:03:52 UTC (rev 787)
+++ CometVisu/trunk/visu/visu_config_demo.xml 2012-05-05 21:39:46 UTC (rev 788)
@@ -105,6 +105,11 @@
</statusbar>
</meta>
<page name="CometVisu Widget Demo">
+ <navbar position="left" dynamic="true" width="200px">
+ <text>Test 1</text>
+ <line/>
+ <text>Test 2</text>
+ </navbar>
<text align="center">Herzlich willkommen bei dem CometVisu Widget Demo!</text>
<text><b>Hinweis:</b><br/>Alle Widgets verwenden KNX-Addressen mit der Haupt- und Mittelgruppe 12/7, da diese meist unbenutzt sind und so beim Testen oft keine Nebenwirkungen auftreten können. Sollten diese Gruppe bei Ihnen verwendet werden, so sollten Sie diese Widgets hier nicht betätigen!</text>
<line/>
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-05 20:03:58
|
Revision: 787
http://openautomation.svn.sourceforge.net/openautomation/?rev=787&view=rev
Author: pfry
Date: 2012-05-05 20:03:52 +0000 (Sat, 05 May 2012)
Log Message:
-----------
Modified Paths:
--------------
wiregate/plugin/generic/conf.d/Logikprozessor.conf
Modified: wiregate/plugin/generic/conf.d/Logikprozessor.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-05 19:33:18 UTC (rev 786)
+++ wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-05 20:03:52 UTC (rev 787)
@@ -21,21 +21,21 @@
# translate-Routine. Neu ist hier der "delay"-Parameter, ausserdem der Spezialfall, dass translate einfach eine Konstante als
# Rueckgabewert spezifiziert.
stair => { receive=>'1/2/9', transmit=>'1/2/9', delay=>600, translate => 0, },
- # Damit im Fall transmit != receive der Translator nicht auf sein eigenes Schreibtelegramm immer wieder antwortet, wird nur dann gesendet,
+ # Damit im Fall transmit != receive der Logikprozessor nicht auf sein eigenes Schreibtelegramm immer wieder antwortet, wird nur dann gesendet,
# wenn Ergebnis != Input oder Sender des empfangenen Telegramms!=0 (Wiregate).
# 5. Memory-Funktion. Wenn ein Write-Telegramm auf die Transmit-Adresse kommt, speichert der Logikprozessor den Wert ab.
# Das Flag "transmit_on_request" bewirkt, dass nichts gesendet wird, jedoch wird eine Leseanfrage auf der transmit-GA immer
# mit dem letzten Wert (hier also dem gespeicherten) beantwortet. Damit laesst sich eine Speicherfunktion realisieren.
- # Hier speichert Translator also den Input ab und sendet den errechneten (=gespeicherten) Wert NUR AUF ANFRAGE.
- memory => { transmit=>'1/2/9', transmit_on_request=>1 },
+ # Hier speichert Logikprozessor also den Input ab und sendet den errechneten (=gespeicherten) Wert NUR AUF ANFRAGE.
+ memory => { transmit=>'1/2/9', transmit_only_on_request=>1 },
# Eine receive-Adresse oder translate-Logik werden hier gar nicht gebraucht.
# 6. Eine einfache UND-Logik mit zwei Eingaengen. Falls ein Telegramm auf einer der beiden receive-GAs empfangen wird,
# wird die andere Adresse noch ausgelesen, die Logik angewendet und das Ergebnis auf der transmit-GA uebermittelt
und => { receive=>['1/2/12','1/2/13'], transmit=>'1/2/14', translate => sub { my (undef,$input)=@_; $input->[0] && $input->[1]; }, },
- # 7. Ein komplexerer Fall nur zur Demonstration: hier besteht der Status des Translators aus mehreren Werten.
+ # 7. Ein komplexerer Fall nur zur Demonstration: hier besteht der Status des Logikprozessors aus mehreren Werten.
# Es wird immer die Summe aus letztem, vorletztem und aktuellem Wert gesendet, und zwar mit 30s Verzoegerung.
complex => { receive=>'9/5/205',
transmit=>'9/5/206',
@@ -47,7 +47,7 @@
},
# Wenn state ein Hash ist, wird der letzte gesendete Wert in $state->{result} gespeichert.
- # 8. Schlussendlich wieder mal Werbung fuer die GA-Kurznamen. Setzt man im Skript Translator.pl $use_short_names=1
+ # 8. Schlussendlich wieder mal Werbung fuer die GA-Kurznamen. Setzt man im Skript Logikprozessor.pl $use_short_names=1
# und verwendet GA-Namen mit eindeutigem Kuerzel (=erstes Wort des Namens), so funktioniert auch das folgende:
D_SZ_Decke => { receive=>'LR_SZ_Decke_1', transmit=>'LK_SZ_Decke_1',
translate => sub { my ($state,$input)=@_; limit(0,$state+20*$input,100); }, },
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-05 19:33:25
|
Revision: 786
http://openautomation.svn.sourceforge.net/openautomation/?rev=786&view=rev
Author: pfry
Date: 2012-05-05 19:33:18 +0000 (Sat, 05 May 2012)
Log Message:
-----------
Modified Paths:
--------------
wiregate/plugin/generic/Szenencontroller.pl
wiregate/plugin/generic/conf.d/Szenencontroller.conf
Added Paths:
-----------
wiregate/plugin/generic/Logikprozessor.pl
wiregate/plugin/generic/conf.d/Logikprozessor.conf
Added: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl (rev 0)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-05 19:33:18 UTC (rev 786)
@@ -0,0 +1,434 @@
+#!/usr/bin/perl -w
+##################
+# Logikprozessor #
+##################
+# Wiregate-Plugin
+# (c) 2012 Fry under the GNU Public License
+
+#$plugin_info{$plugname.'_cycle'}=0; return 'deaktiviert';
+
+my $use_short_names=1; # 1 fuer GA-Kuerzel (erstes Wort des GA-Namens), 0 fuer die "nackte" Gruppenadresse
+
+# eibgaconf fixen falls nicht komplett indiziert
+if($use_short_names && !exists $eibgaconf{ZV_Uhrzeit})
+{
+ for my $ga (grep /^[0-9\/]+$/, keys %eibgaconf)
+ {
+ $eibgaconf{$ga}{ga}=$ga;
+ my $name=$eibgaconf{$ga}{name};
+ next unless defined $name;
+ $eibgaconf{$name}=$eibgaconf{$ga};
+
+ next unless $name=~/^\s*(\S+)/;
+ my $short=$1;
+ $short='ZV_'.$1 if $eibgaconf{$ga}{name}=~/^Zeitversand.*(Uhrzeit|Datum)/;
+
+ $eibgaconf{$ga}{short}=$short;
+ $eibgaconf{$short}=$eibgaconf{$ga};
+ }
+}
+
+# Tools und vorbesetzte Variablen fue die Logiken
+sub limit { my ($lo,$x,$hi)=@_; return $x<$lo?$lo:($x>$hi?$hi:$x); }
+my $day_of_week=`/bin/date +"%a"`;
+my $weekend=($day_of_week=~/Sa|So/);
+my $time_of_day=`/bin/date +"%X"`;
+my $hour_of_day=substr($time_of_day,0,2);
+my $day=($hour_of_day>7 && $hour_of_day<23);
+my $night=!$day;
+
+# Konfigurationsfile einlesen
+my %devices=(0=>'Wiregate');
+my %logic=();
+my $conf="/etc/wiregate/plugin/generic/conf.d/$plugname"; $conf=~s/\.pl$/.conf/;
+open FILE, "<$conf" || return "no config found";
+my @lines = <FILE>;
+close FILE;
+eval("@lines");
+return "config error: $@" if $@;
+
+# Aufrufgrund ermitteln
+my $event=undef;
+if (!$plugin_initflag)
+{ $event='restart'; } # Restart des daemons / Reboot
+elsif ($plugin_info{$plugname.'_lastsaved'} > $plugin_info{$plugname.'_last'})
+{ $event='modified'; } # Plugin modifiziert
+elsif (%msg) { $event='bus'; } # Bustraffic
+elsif ($fh) { $event='socket'; } # Netzwerktraffic
+else { $event='cycle'; } # Zyklus
+
+# Konfigfile seit dem letzten Mal geaendert?
+my $config_modified = (24*60*60*(-M $conf)-time()) > $plugin_info{$plugname.'_configtime'};
+
+# Plugin-Code
+my $retval='';
+
+if($event=~/restart|modified/ || $config_modified)
+{
+ $plugin_info{$plugname.'_configtime'}=(24*60*60*(-M $conf)-time());
+
+ # alle Variablen loeschen und neu initialisieren, alle GAs abonnieren
+ for my $k (grep /^$plugname\_/, keys %plugin_info)
+ {
+ delete $plugin_info{$k};
+ }
+
+ my $count=0;
+ my $err=0;
+
+ for my $t (keys %logic)
+ {
+ # Eintrag pruefen
+ if(defined $logic{$t}{receive} && ref $logic{$t}{receive} && ref $logic{$t}{receive} ne 'ARRAY')
+ {
+ plugin_log($plugname, "Config err: \$logic{$t}{receive} ist weder Skalar noch ARRAY-Referenz ([...]).");
+ next;
+ }
+
+ if(defined $logic{$t}{translate} && ref $logic{$t}{translate} && ref $logic{$t}{translate} ne 'CODE')
+ {
+ plugin_log($plugname, "Config err: \$logic{$t}{translate} ist weder Skalar noch CODE-Referenz (sub {...}).");
+ next;
+ }
+
+ if(defined $logic{$t}{state} && ref $logic{$t}{state} && ref $logic{$t}{state} ne 'HASH')
+ {
+ plugin_log($plugname, "Config err: \$logic{$t}{state} ist weder Skalar noch HASH-Referenz ({...}).");
+ next;
+ }
+
+ # transmit-Adresse abonnieren
+ my $transmit=groupaddress($logic{$t}{transmit});
+ $plugin_subscribe{$transmit}{$plugname}=1;
+
+ $count++;
+
+ # alle receive-Adressen abonnieren (eine oder mehrere)
+ my $receive=groupaddress($logic{$t}{receive});
+
+ next unless $receive;
+
+ unless(ref $receive)
+ {
+ $plugin_subscribe{$receive}{$plugname}=1;
+ }
+ else
+ {
+ for my $rec (@{$receive})
+ {
+ $plugin_subscribe{$rec}{$plugname}=1;
+ }
+ }
+ }
+
+ $plugin_info{$plugname.'_cycle'}=0;
+ $retval.=$count." initialisiert";
+}
+
+if($event=~/bus/)
+{
+ # Bustraffic bedienen - nur Schreibzugriffe der iButtons interessieren
+ return unless $msg{apci}=~/A_GroupValue_(Write|Read)/;
+
+ my $ga=$msg{dst};
+ my $in=$msg{value};
+ my $keep_subscription=0; # falls am Ende immer noch Null, die GA stornieren
+
+ # welche translate-Logik ist aufgerufen?
+ for my $t (keys %logic)
+ {
+ my $transmit=groupaddress($logic{$t}{transmit});
+ my $transmit_ga = ($ga eq $transmit);
+
+ my $receive=groupaddress($logic{$t}{receive});
+ my $receive_ga=0;
+
+ if(defined $receive)
+ {
+ unless(ref $logic{$t}{receive})
+ {
+ $receive_ga=1 if $ga eq $receive;
+ }
+ else
+ {
+ $receive_ga=1 if grep /^$ga$/, @{$receive};
+ }
+ }
+
+ next unless $receive_ga || $transmit_ga; # diese Logik nicht anwendbar
+
+ $keep_subscription=1;
+
+ # Sonderfall: Read- und Write-Telegramme auf der Transmit-Adresse?
+ if($transmit_ga)
+ {
+ # Ein Read-Request auf einer Transmit-GA wird mit dem letzten Ergebnis beantwortet
+ # Read-Requests auf die receive-Adressen werden gar nicht beantwortet
+ if($msg{apci} eq "A_GroupValue_Read")
+ {
+ my $result=$plugin_info{$plugname.'_'.$t.'_result'};
+ if(defined $result)
+ {
+ knx_write($ga, $result);
+ }
+ next;
+ }
+ elsif(!$receive_ga) # Receive geht vor!
+ {
+ if(defined $in) # Write-Telegramm: das waren moeglicherweise wir selbst, also nicht antworten
+ {
+ $plugin_info{$plugname.'_'.$t.'_result'}=$in; # einfach Input ablegen
+ }
+ else
+ {
+ delete $plugin_info{$plugname.'_'.$t.'_result'};
+ }
+ next;
+ }
+ }
+
+ # Wir wissen ab hier: Es liegt ein Write-Telegramm auf einer der receive-Adressen vor
+
+ # Nebenbei berechnen wir noch zwei Flags, die Zirkelkommunikation verhindern sollen
+ # (Logik antwortet auf sich selbst in einer Endlosschleife)
+
+ # kommt transmit-GA unter den receive-GAs vor?
+ # Wenn transmit_ga gesetzt ist, ist das schon mal der Fall
+ my $possible_circle=$transmit_ga;
+
+ # war Wiregate der Sender des Telegramms?
+ my $sender_is_wiregate=int($msg{src})==0;
+
+ # Es folgt die eigentliche Logik-Engine - als erstes definiere das Input-Array fuer die Logik
+ my $input=$in; # Skalarer Fall (der haeufigste)
+
+ # Array-Fall: bereite Input-Array fuer Logik vor
+ if(ref $receive)
+ {
+ $input=();
+ for my $rec (@{$receive})
+ {
+ my $rec=groupaddress($rec);
+
+ $possible_circle=1 if $transmit eq $rec;
+
+ if($ga eq $rec)
+ {
+ push @{$input}, $in;
+ }
+ else
+ {
+ push @{$input}, knx_read($rec, 300);
+ }
+ }
+ }
+
+ # ab hier liegt $input komplett vor, und nun muss die Logik ausgewertet
+ # und das Resultat auf der Transmit-GA uebertragen
+ my $result=undef;
+
+ unless(ref $logic{$t}{translate})
+ {
+ # Trivialer Fall: translate enthaelt einen fixen Rueckgabewert
+ $plugin_info{$plugname.'_'.$t.'_result'}=$result=$logic{$t}{translate};
+ }
+ elsif(!ref $logic{$t}{state})
+ {
+ # Einfacher aber haeufiger Fall: skalarer $state
+ # $state mit Ergebnis des letzten Aufrufs vorbesetzen
+ my $state=$plugin_info{$plugname.'_'.$t.'_result'};
+
+ # Funktionsaufruf, das Ergebnis vom letzten Mal steht in $state
+ $result=$logic{$t}{translate}($state,$input);
+
+ # Ergebnis des letzten Aufrufs zurueckschreiben
+ if(defined $result)
+ {
+ $plugin_info{$plugname.'_'.$t.'_result'}=$result;
+ }
+ else
+ {
+ delete $plugin_info{$plugname.'_'.$t.'_result'};
+ }
+ }
+ else
+ {
+ # Komplexer Fall: $state-Hash aus %logic initialisieren
+ my $state=$logic{$t}{state};
+ my @vars=keys %{$state};
+ push @vars, 'result';
+
+ # Nun die dynamischen Variablen aus plugin_info hinzufuegen
+ for my $v (@vars)
+ {
+ $state->{$v}=$plugin_info{$plugname.'_'.$t.'_'.$v} if defined $plugin_info{$plugname.'_'.$t.'_'.$v};
+ }
+
+ # Funktionsaufruf, das Ergebnis vom letzten Mal steht in $state->{result}
+ $result=$state->{result}=$logic{$t}{translate}($state,$input);
+
+ # Alle dynamischen Variablen wieder nach plugin_info schreiben
+ # Damit plugin_info nicht durch Konfigurationsfehler vollgemuellt wird,
+ # erlauben wir nur Eintraege mit defined-Werten
+ for my $v (@vars)
+ {
+ if(defined $state->{$v})
+ {
+ $plugin_info{$plugname.'_'.$t.'_'.$v}=$state->{$v};
+ }
+ else
+ {
+ # wenn die Logik den Wert undef in eine state-Variable schreibt,
+ # wird beim naechsten Aufruf wieder der Startwert aus %logic genommen,
+ delete $plugin_info{$plugname.'_'.$t.'_'.$v};
+ }
+ }
+ }
+
+ # In bestimmten Sonderfaellen nichts schicken
+ next unless defined $result; # Resultat undef => nichts senden
+ next if $logic{$t}{transmit_only_on_request};
+ next if $possible_circle && $sender_is_wiregate && $in eq $result;
+
+ # Falls delay spezifiziert, wird ein "Wecker" gestellt, um in einem spaeteren Aufruf den Wert zu senden
+ if($logic{$t}{delay})
+ {
+ $plugin_info{$plugname.'__'.$t.'_timer'}=time()+$logic{$t}{delay};
+ set_timer();
+ }
+ else
+ {
+ knx_write($transmit, $result);
+ }
+ }
+
+ unless($keep_subscription)
+ {
+ delete $plugin_subscribe{$ga}{$plugname};
+ }
+}
+
+# Evtl. faellige Timer finden
+for my $timer (grep /$plugname\__.*_timer/, keys %plugin_info) # alle Timer
+{
+ next if time()<$plugin_info{$timer}; # weiter falls noch nicht faellig
+
+ # Timer loeschen
+ delete $plugin_info{$timer};
+
+ # Relevanten Eintrag von %logic ermitteln
+ $timer=~/$plugname\__(.*)_timer/;
+ my $t=$1;
+
+ # Transmit-GA
+ my $transmit=groupaddress($logic{$t}{transmit});
+
+ # zu sendendes Resultat
+ my $result=$plugin_info{$plugname.'_'.$t.'_result'};
+ next unless defined $result;
+
+ knx_write($transmit, $result);
+
+ # Timer fuer nachste Aktion setzen
+ set_timer();
+}
+
+return unless $retval;
+return $retval;
+
+
+# Zeit bis zum naechsten Aufruf dieses Plugins berechnen
+
+sub set_timer
+{
+ # optimalen Wert fuer Aufrufzyklus finden, um beim naechsten Aufruf was zu senden
+ my $nexttimer=undef;
+ for my $timer (grep /$plugname\__.*_timer/, keys %plugin_info) # alle Timer
+ {
+ $nexttimer=$plugin_info{$timer} if !defined $nexttimer || $plugin_info{$timer}<$nexttimer;
+ }
+
+ unless(defined $nexttimer)
+ {
+ $plugin_info{$plugname."_cycle"}=0;
+ }
+ else
+ {
+ my $cycle=$nexttimer-time();
+ $cycle=1 if $cycle<1;
+ $plugin_info{$plugname."_cycle"}=$cycle;
+ }
+}
+
+# Umgang mit GA-Kurznamen und -Adressen
+
+sub groupaddress
+{
+ my $short=shift;
+
+ return unless defined $short;
+
+ if(ref $short)
+ {
+ my $ga=[];
+ for my $sh (@{$short})
+ {
+ if($sh!~/^[0-9\/]+$/ && defined $eibgaconf{$sh}{ga})
+ {
+ push @{$ga}, $eibgaconf{$sh}{ga};
+ }
+ else
+ {
+ push @{$ga}, $sh;
+ }
+ }
+ return $ga;
+ }
+ else
+ {
+ my $ga=$short;
+
+ if($short!~/^[0-9\/]+$/ && defined $eibgaconf{$short}{ga})
+ {
+ $ga=$eibgaconf{$short}{ga};
+ }
+
+ return $ga;
+ }
+}
+
+sub shortname
+{
+ my $gas=shift;
+
+ return unless defined $gas;
+ return $gas unless $use_short_names;
+
+ if(ref $gas)
+ {
+ my $sh=[];
+ for my $ga (@{$gas})
+ {
+ if($ga=~/^[0-9\/]+$/ && defined $eibgaconf{$ga}{short})
+ {
+ push @{$sh}, $eibgaconf{$ga}{short};
+ }
+ else
+ {
+ push @{$sh}, $ga;
+ }
+ }
+ return $sh;
+ }
+ else
+ {
+ my $sh=$gas;
+
+ if($gas=~/^[0-9\/]+$/ && defined $eibgaconf{$gas}{short})
+ {
+ $sh=$eibgaconf{$gas}{short};
+ }
+
+ return $sh;
+ }
+}
Modified: wiregate/plugin/generic/Szenencontroller.pl
===================================================================
--- wiregate/plugin/generic/Szenencontroller.pl 2012-05-05 15:56:45 UTC (rev 785)
+++ wiregate/plugin/generic/Szenencontroller.pl 2012-05-05 19:33:18 UTC (rev 786)
@@ -5,9 +5,9 @@
# Wiregate-Plugin
# (c) 2012 Fry under the GNU Public License
-# $plugin_info{$plugname.'_cycle'}=0; return "deaktiviert";
+#$plugin_info{$plugname.'_cycle'}=0; return "deaktiviert";
-my $use_short_names=0; # 1 fuer GA-Kuerzel (erstes Wort des GA-Namens), 0 fuer die "nackte" Gruppenadresse
+my $use_short_names=1; # 1 fuer GA-Kuerzel (erstes Wort des GA-Namens), 0 fuer die "nackte" Gruppenadresse
# eibgaconf fixen falls nicht komplett indiziert
if($use_short_names && !exists $eibgaconf{ZV_Uhrzeit})
@@ -41,17 +41,24 @@
else { $event='cycle'; } # Zyklus
# Konfigurationsfile einlesen
-my $conf=$plugname; $conf=~s/\.pl$/.conf/;
-$conf="/etc/wiregate/plugin/generic/conf.d/$conf";
my %scene=();
+my $conf="/etc/wiregate/plugin/generic/conf.d/$plugname"; $conf=~s/\.pl$/.conf/;
my $err=read_from_config();
return $err if $err;
+# Konfigfile seit dem letzten Mal geaendert?
+my $config_modified = ($scene{storage} ne 'configfile' && (24*60*60*(-M $conf)-time()) > $plugin_info{$plugname.'_configtime'});
+
# Dynamisch definierte Szenen aus plugin_info einlesen
recall_from_plugin_info();
-if($event=~/restart|modified/)
+# Plugin-Code
+my $retval='';
+
+if($event=~/restart|modified/ || $config_modified)
{
+ $plugin_info{$plugname.'_configtime'}=(24*60*60*(-M $conf)-time());
+
# Cleanup aller Szenenvariablen in %plugin_info
for my $k (grep /^$plugname\_/, keys %plugin_info)
{
@@ -64,7 +71,7 @@
for my $sc (sort keys %scene)
{
- next if $sc eq 'storage';
+ next if $sc=~/^(storage|debug)$/;
my $store=$scene{$sc}{store};
my $recall=$scene{$sc}{recall};
@@ -85,12 +92,9 @@
$plugin_info{$plugname.'__SceneLookup'}=$scene_lookup;
$plugin_info{$plugname.'_cycle'}=0;
- return $count." initialisiert";
+ $retval.=$count." initialisiert";
}
-
-my $retval='';
-
-if($event=~/bus/)
+elsif($event=~/bus/)
{
# nur auf Write-Telegramme reagieren
return if $msg{apci} ne 'A_GroupValue_Write';
@@ -127,11 +131,15 @@
}
# Szenencode
- my $z="$sc\__$n";
+ my $z="$sc\#$n";
if($cmd eq 'S') # Szene speichern
{
- $retval.="Szene $z speichern: ";
+ $retval.="Szene $z speichern: " if $scene{debug};
+
+ my $confirm_store=$scene{$sc}{confirm_store};
+ $confirm_store=$eibgaconf{$confirm_store}{ga} if $confirm_store!~/^[0-9\/]+$/ && defined $eibgaconf{$confirm_store};
+ knx_write($confirm_store,1); # Translator feuert spaeter dann eine Null hinterher, um die LED auszuschalten
delete $scene{$z};
@@ -139,15 +147,18 @@
{
my $wga=$scene{$sc}{gas}{$ga}; # auf diese GA muss spaeter geschrieben werden
$wga=$eibgaconf{$wga}{short} if $wga=~/^[0-9\/]+$/ && $use_short_names && defined $eibgaconf{$wga}{short};
+
$ga=$eibgaconf{$ga}{ga} if $ga!~/^[0-9\/]+$/ && defined $eibgaconf{$ga};
- $scene{$z}{$wga}=knx_read($ga,300);
+ $scene{$z}{$wga}=knx_read($ga,300); # KOSTET ZEIT FALLS GERAETE NICHT ANTWORTEN!
+
if(defined $scene{$z}{$wga})
{
- $retval.=$wga.'->'.$scene{$z}{$wga}.' ';
+ $retval.=$wga.'->'.$scene{$z}{$wga}.' ' if $scene{debug};
}
else
{
+ $retval.=$wga.'? ' if $scene{debug};
delete $scene{$z}{$wga};
}
}
@@ -163,14 +174,12 @@
}
else # Szene abrufen
{
- $retval.="Szene $z abrufen: ";
+ $retval.="Szene $z abrufen: " if $scene{debug};
for my $v (keys %{$scene{$z}})
{
- my $ga=$v;
- $ga=$eibgaconf{$ga}{ga} if $ga!~/^[0-9\/]+$/ && defined $eibgaconf{$ga};
- knx_write($ga,$scene{$z}{$v});
- $retval.=$ga.'->'.$scene{$z}{$v}.' ';
+ knx_write(groupaddress($v),$scene{$z}{$v});
+ $retval.=$v.'->'.$scene{$z}{$v}.' ' if $scene{debug};
}
}
}
@@ -226,9 +235,81 @@
{
for my $k (grep /^$plugname\__/, keys %plugin_info)
{
- next unless($k=~/^$plugname\__([A-Z0-9 ]+__[0-9]+)__(.*)$/i);
+ next unless($k=~/^$plugname\__(.*\#.*)__(.*)$/);
my ($z,$v)=($1,$2);
$scene{$z}{$v}=$plugin_info{$k};
}
}
+# Umgang mit GA-Kurznamen und -Adressen
+sub groupaddress
+{
+ my $short=shift;
+
+ return unless defined $short;
+
+ if(ref $short)
+ {
+ my $ga=[];
+ for my $sh (@{$short})
+ {
+ if($sh!~/^[0-9\/]+$/ && defined $eibgaconf{$sh}{ga})
+ {
+ push @{$ga}, $eibgaconf{$sh}{ga};
+ }
+ else
+ {
+ push @{$ga}, $sh;
+ }
+ }
+ return $ga;
+ }
+ else
+ {
+ my $ga=$short;
+
+ if($short!~/^[0-9\/]+$/ && defined $eibgaconf{$short}{ga})
+ {
+ $ga=$eibgaconf{$short}{ga};
+ }
+
+ return $ga;
+ }
+}
+
+sub shortname
+{
+ my $gas=shift;
+
+ return unless defined $gas;
+ return $gas unless $use_short_names;
+
+ if(ref $gas)
+ {
+ my $sh=[];
+ for my $ga (@{$gas})
+ {
+ if($ga=~/^[0-9\/]+$/ && defined $eibgaconf{$ga}{short})
+ {
+ push @{$sh}, $eibgaconf{$ga}{short};
+ }
+ else
+ {
+ push @{$sh}, $ga;
+ }
+ }
+ return $sh;
+ }
+ else
+ {
+ my $sh=$gas;
+
+ if($gas=~/^[0-9\/]+$/ && defined $eibgaconf{$gas}{short})
+ {
+ $sh=$eibgaconf{$gas}{short};
+ }
+
+ return $sh;
+ }
+}
+
Added: wiregate/plugin/generic/conf.d/Logikprozessor.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Logikprozessor.conf (rev 0)
+++ wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-05 19:33:18 UTC (rev 786)
@@ -0,0 +1,59 @@
+#!/usr/bin/perl
+#
+# Logikprozessor.pl - Konfiguration
+#
+
+%logic=(
+ # 1. Alle Werte, die auf einer GA gesendet werden, werden mit 2 multipliziert auf einer anderen GA weitergegeben
+ mal2 => { receive=>'9/5/201', transmit=>'9/5/202', translate => sub { my (undef,$input)=@_; 2*$input; }, },
+ # das "undef" steht da einfach, weil uns der letzte Ergebniswert nicht interessiert
+
+ # 2. Die Werte auf der ersten GA werden aufsummiert, das Ergebis auf der anderen GA gesendet.
+ # Damit kann man bspw aus einem relativen Dimmwert einen absoluten Dimmwert machen.
+ sum => { receive=>'9/5/207', transmit=>'9/5/208', translate => sub { my ($state,$input)=@_; $state+$input; }, },
+ # $state enthaelt das jeweils letzte Ergebnis.
+
+ # 3. Wie oben, aber das Ergebnis limitiert auf den Bereich 0-100
+ lsum => { receive=>'1/2/7', transmit=>'1/2/8', translate => sub { my ($state,$input)=@_; limit(0,$state+$input,100); }, },
+
+ # 4. Hier eine "Treppenlichtfunktion". Auf jeden Schreibzugriff auf die receive-Adresse wird 10min spaeter eine 0 an
+ # die transmit-Adresse (hier gleich) geschickt. Verzoegert wird uebrigens nur das Senden, nicht das Ausfuehren der
+ # translate-Routine. Neu ist hier der "delay"-Parameter, ausserdem der Spezialfall, dass translate einfach eine Konstante als
+ # Rueckgabewert spezifiziert.
+ stair => { receive=>'1/2/9', transmit=>'1/2/9', delay=>600, translate => 0, },
+ # Damit im Fall transmit != receive der Translator nicht auf sein eigenes Schreibtelegramm immer wieder antwortet, wird nur dann gesendet,
+ # wenn Ergebnis != Input oder Sender des empfangenen Telegramms!=0 (Wiregate).
+
+ # 5. Memory-Funktion. Wenn ein Write-Telegramm auf die Transmit-Adresse kommt, speichert der Logikprozessor den Wert ab.
+ # Das Flag "transmit_on_request" bewirkt, dass nichts gesendet wird, jedoch wird eine Leseanfrage auf der transmit-GA immer
+ # mit dem letzten Wert (hier also dem gespeicherten) beantwortet. Damit laesst sich eine Speicherfunktion realisieren.
+ # Hier speichert Translator also den Input ab und sendet den errechneten (=gespeicherten) Wert NUR AUF ANFRAGE.
+ memory => { transmit=>'1/2/9', transmit_on_request=>1 },
+ # Eine receive-Adresse oder translate-Logik werden hier gar nicht gebraucht.
+
+ # 6. Eine einfache UND-Logik mit zwei Eingaengen. Falls ein Telegramm auf einer der beiden receive-GAs empfangen wird,
+ # wird die andere Adresse noch ausgelesen, die Logik angewendet und das Ergebnis auf der transmit-GA uebermittelt
+ und => { receive=>['1/2/12','1/2/13'], transmit=>'1/2/14', translate => sub { my (undef,$input)=@_; $input->[0] && $input->[1]; }, },
+
+ # 7. Ein komplexerer Fall nur zur Demonstration: hier besteht der Status des Translators aus mehreren Werten.
+ # Es wird immer die Summe aus letztem, vorletztem und aktuellem Wert gesendet, und zwar mit 30s Verzoegerung.
+ complex => { receive=>'9/5/205',
+ transmit=>'9/5/206',
+ delay=>30,
+ state => {val1=>0, val2=>0},
+ translate => sub { my ($state,$input)=@_;
+ $state->{val2}=$state->{val1}; $state->{val1}=$state->{result};
+ $state->{val2}+$state->{val1}+$input; },
+ },
+ # Wenn state ein Hash ist, wird der letzte gesendete Wert in $state->{result} gespeichert.
+
+ # 8. Schlussendlich wieder mal Werbung fuer die GA-Kurznamen. Setzt man im Skript Translator.pl $use_short_names=1
+ # und verwendet GA-Namen mit eindeutigem Kuerzel (=erstes Wort des Namens), so funktioniert auch das folgende:
+ D_SZ_Decke => { receive=>'LR_SZ_Decke_1', transmit=>'LK_SZ_Decke_1',
+ translate => sub { my ($state,$input)=@_; limit(0,$state+20*$input,100); }, },
+ # ist doch leserlicher, oder? Hier wird ein relativer Dimmwert durch Skalierung und Summierung
+ # in einen absoluten Wert umgewandelt
+ );
+
+
+
Modified: wiregate/plugin/generic/conf.d/Szenencontroller.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Szenencontroller.conf 2012-05-05 15:56:45 UTC (rev 785)
+++ wiregate/plugin/generic/conf.d/Szenencontroller.conf 2012-05-05 19:33:18 UTC (rev 786)
@@ -5,6 +5,9 @@
%scene=(
+ # Debugflag fuer Kontrollmeldungen auf /var/log/wiregate_plugin.log auf 1 setzen
+ debug=>1,
+
# Storage auf 'configfile' setzen, wenn die Szenen hier im Konfigfile gespeichert werden sollen.
# Sie ueberleben in diesem Fall sogar das Loeschen des plugin_info und gelten somit als "fest verdrahtet".
# Ansonsten 'plugin_info' setzen, dann werden die Szenen im %plugin_info gespeichert.
@@ -48,10 +51,10 @@
#
# 4. Schlussendlich wieder mal Werbung fuer die GA-Kurznamen. Setzt man im Skript Translator.pl $use_short_names=1
# und verwendet GA-Namen mit eindeutigem Kuerzel (=erstes Wort des Namens), so funktioniert auch das folgende:
- Schlafzimmer2 => {store=>'ZS_SZ', recall=>'ZA_SZ', gas=>{'LI_SZ'=>'LI_SZ', 'JX_SZ'=>'JW_SZ', 'JQ_SZ'=>'JP_SZ'}},
+ Schlafzimmer2 => {store=>'ZS_SZ', recall=>'ZA_SZ', confirm_store=>'ZC_SZ', gas=>{'LI_SZ'=>'LI_SZ', 'JX_SZ'=>'JW_SZ', 'JQ_SZ'=>'JP_SZ'}},
# ist doch leserlicher, oder? SZ=Schlafzimmer, ZA=Szene abrufen, ZS=Szene speichern, LI=Licht,
# JX=Jalousiewinkel abfragen, JW=Jalousiewinkel einstellen, JQ=Jalousieposition abfragen, JP=Jalousie positionieren
-
+ # confirm_store ist uebrigens eine GA (Trigger), die gesendet wird, um ein Szenenabspeichern zu bestaetigen
);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <ma...@us...> - 2012-05-05 15:56:52
|
Revision: 785
http://openautomation.svn.sourceforge.net/openautomation/?rev=785&view=rev
Author: mayerch
Date: 2012-05-05 15:56:45 +0000 (Sat, 05 May 2012)
Log Message:
-----------
3D mode: Initial support of filters (only working for floors at the moment)
Modified Paths:
--------------
CometVisu/trunk/visu/structure/pure/_common.js
CometVisu/trunk/visu/structure/pure/page.js
CometVisu/trunk/visu/visu_config_2d3d.xml
Modified: CometVisu/trunk/visu/structure/pure/_common.js
===================================================================
--- CometVisu/trunk/visu/structure/pure/_common.js 2012-05-03 09:27:17 UTC (rev 784)
+++ CometVisu/trunk/visu/structure/pure/_common.js 2012-05-05 15:56:45 UTC (rev 785)
@@ -366,7 +366,10 @@
var pos = data.building2screen( new THREE.Vector3( l.x, l.y, l.z ) );
e.data.element.css( 'left', pos.x + 'px' );
e.data.element.css( 'top' , pos.y + 'px' );
- //console.log( e, data, e.data, pos.x, pos.y );
+
+ var floorFilter = true;
+ if( l.floorFilter) floorFilter = data.getState('showFloor') == data.buildingProperties.floorNames[ l.floorFilter ];
+ e.data.element.css( 'display', floorFilter ? '' : 'none' );
}
function extractLayout( layout, defaultValues )
@@ -396,6 +399,8 @@
if( layout.getAttribute('y' ) ) ret_val.y = layout.getAttribute('y' );
if( layout.getAttribute('z' ) ) ret_val.z = layout.getAttribute('z' );
if( layout.getAttribute('floor') ) ret_val.floor = layout.getAttribute('floor');
+ if( layout.getAttribute('floorFilter') ) ret_val.floorFilter = layout.getAttribute('floorFilter');
+ if( layout.getAttribute('roomFilter') ) ret_val.roomFilter = layout.getAttribute('roomFilter' );
return ret_val;
}
Modified: CometVisu/trunk/visu/structure/pure/page.js
===================================================================
--- CometVisu/trunk/visu/structure/pure/page.js 2012-05-03 09:27:17 UTC (rev 784)
+++ CometVisu/trunk/visu/structure/pure/page.js 2012-05-05 15:56:45 UTC (rev 785)
@@ -89,6 +89,19 @@
address[ '_' + $p.attr('floor') ] = [ 'DPT:5.004', 0, 'floor' ];
container.bind( '_' + $p.attr('floor'), this.update );
};
+
+ $( childs ).each( function(i,a){
+ if( this.tagName == 'filter' )
+ {
+ var floorFilter = $(this).attr('floor');
+ var roomFilter = $(this).attr('room');
+ childs.splice( i, 1 ); // remove filter element
+ $(this).children().each( function(j){ // and add it's children
+ $(this).find('layout').attr({floorFilter:floorFilter,roomFilter:roomFilter});
+ childs.splice( i+j, 0, this );
+ });
+ }
+ });
}
container.data( 'address', address );
$( childs ).each( function(i){
Modified: CometVisu/trunk/visu/visu_config_2d3d.xml
===================================================================
--- CometVisu/trunk/visu/visu_config_2d3d.xml 2012-05-03 09:27:17 UTC (rev 784)
+++ CometVisu/trunk/visu/visu_config_2d3d.xml 2012-05-05 15:56:45 UTC (rev 785)
@@ -83,6 +83,22 @@
<layout x="3.5" y="3.7" z="1.0"/>
<address transform="DPT:5.001" type="">12/7/53</address>
</info>
+ <filter floor="Underground" room="Room2">
+ <info format="%.2f">
+ <layout x="7.5" y="3.7" z="1.0"/>
+ <address transform="DPT:5.001" type="">12/7/53</address>
+ </info>
+ </filter>
+ <filter floor="Underground">
+ <info format="%.2f">
+ <layout x="3.5" y="7.7" z="1.0"/>
+ <address transform="DPT:5.001" type="">12/7/53</address>
+ </info>
+ <info format="%.2f">
+ <layout x="7.5" y="7.7" z="2.0"/>
+ <address transform="DPT:5.001" type="">12/7/53</address>
+ </info>
+ </filter>
<slide min="0" max="6.283185307179586" step="0.01">
<layout x="0px" y="570px" width="600px" />
<label>Azimut</label>
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-03 09:27:27
|
Revision: 784
http://openautomation.svn.sourceforge.net/openautomation/?rev=784&view=rev
Author: pfry
Date: 2012-05-03 09:27:17 +0000 (Thu, 03 May 2012)
Log Message:
-----------
Modified Paths:
--------------
wiregate/plugin/generic/Szenencontroller.pl
wiregate/plugin/generic/Translator.pl
Modified: wiregate/plugin/generic/Szenencontroller.pl
===================================================================
--- wiregate/plugin/generic/Szenencontroller.pl 2012-05-02 23:37:31 UTC (rev 783)
+++ wiregate/plugin/generic/Szenencontroller.pl 2012-05-03 09:27:17 UTC (rev 784)
@@ -45,7 +45,7 @@
$conf="/etc/wiregate/plugin/generic/conf.d/$conf";
my %scene=();
my $err=read_from_config();
-return "config err" if $err;
+return $err if $err;
# Dynamisch definierte Szenen aus plugin_info einlesen
recall_from_plugin_info();
@@ -88,6 +88,8 @@
return $count." initialisiert";
}
+my $retval='';
+
if($event=~/bus/)
{
# nur auf Write-Telegramme reagieren
@@ -129,18 +131,25 @@
if($cmd eq 'S') # Szene speichern
{
- # Debugging
- plugin_log($plugname, "Szene $z speichern: ".join(',', (sort keys %{$scene{$sc}{gas}})));
+ $retval.="Szene $z speichern: ";
delete $scene{$z};
- for my $ga (keys %{$scene{$sc}{gas}})
+ for my $ga (sort keys %{$scene{$sc}{gas}})
{
my $wga=$scene{$sc}{gas}{$ga}; # auf diese GA muss spaeter geschrieben werden
$wga=$eibgaconf{$wga}{short} if $wga=~/^[0-9\/]+$/ && $use_short_names && defined $eibgaconf{$wga}{short};
$ga=$eibgaconf{$ga}{ga} if $ga!~/^[0-9\/]+$/ && defined $eibgaconf{$ga};
$scene{$z}{$wga}=knx_read($ga,300);
- delete $scene{$z}{$wga} unless defined $scene{$z}{$wga};
+
+ if(defined $scene{$z}{$wga})
+ {
+ $retval.=$wga.'->'.$scene{$z}{$wga}.' ';
+ }
+ else
+ {
+ delete $scene{$z}{$wga};
+ }
}
if($scene{storage} eq 'configfile')
@@ -154,19 +163,20 @@
}
else # Szene abrufen
{
- # Debugging
- plugin_log($plugname, "Szene $z abrufen: ".join(',', (sort values %{$scene{$sc}{gas}})));
+ $retval.="Szene $z abrufen: ";
for my $v (keys %{$scene{$z}})
{
my $ga=$v;
$ga=$eibgaconf{$ga}{ga} if $ga!~/^[0-9\/]+$/ && defined $eibgaconf{$ga};
knx_write($ga,$scene{$z}{$v});
+ $retval.=$ga.'->'.$scene{$z}{$v}.' ';
}
}
}
-return;
+return unless $retval;
+return $retval;
########## Datenpersistenz - Speichern und Einlesen ###############
@@ -176,7 +186,7 @@
my @lines = <CONFIG>;
close CONFIG;
eval("@lines");
- return "config error" if $@;
+ return "config error: $@" if $@;
}
sub store_to_config
Modified: wiregate/plugin/generic/Translator.pl
===================================================================
--- wiregate/plugin/generic/Translator.pl 2012-05-02 23:37:31 UTC (rev 783)
+++ wiregate/plugin/generic/Translator.pl 2012-05-03 09:27:17 UTC (rev 784)
@@ -78,7 +78,7 @@
$count++;
- $plugin_info{$plugname.'_'.$t.'_result'}=undef;
+ delete $plugin_info{$plugname.'_'.$t.'_result'};
if(ref $trans{$t}{state})
{
@@ -123,8 +123,8 @@
my $transmit=$ga;
$transmit=$eibgaconf{$ga}{short} if $use_short_names;
my $result=$plugin_info{$plugname.'_'.$t.'_result'};
- plugin_log($plugname, "memory: $result ($transmit)");
knx_write($ga, $result);
+ return "Memory: $result ($transmit)";
}
return;
}
@@ -136,7 +136,14 @@
# Vorsicht! - das koennten wir selbst gewesen sein, also nicht antworten!
if($cmd eq 'T')
{
- $plugin_info{$plugname.'_'.$t.'_result'}=$input; # einfach Input ablegen
+ if(defined $input)
+ {
+ $plugin_info{$plugname.'_'.$t.'_result'}=$input; # einfach Input ablegen
+ }
+ else
+ {
+ delete $plugin_info{$plugname.'_'.$t.'_result'};
+ }
return;
}
@@ -164,8 +171,14 @@
# erlauben wir nur vorhandene Eintraege
for my $v (keys %{$state})
{
- next unless exists $plugin_info{$plugname.'_'.$t.'_'.$v};
- $plugin_info{$plugname.'_'.$t.'_'.$v}=$state->{$v};
+ if(defined $state->{$v})
+ {
+ $plugin_info{$plugname.'_'.$t.'_'.$v}=$state->{$v};
+ }
+ else
+ {
+ delete $plugin_info{$plugname.'_'.$t.'_'.$v};
+ }
}
}
else # Einfacher Fall - skalare state-Variable
@@ -178,7 +191,14 @@
}
# Ergebnis des letzten Aufrufs zurueckschreiben
- $plugin_info{$plugname.'_'.$t.'_result'}=$result;
+ if(defined $result)
+ {
+ $plugin_info{$plugname.'_'.$t.'_result'}=$result;
+ }
+ else
+ {
+ delete $plugin_info{$plugname.'_'.$t.'_result'};
+ }
# Uebersetzung auf Bus schreiben, ausser im Sonderfall receive==transmit, dann nur auf Anfrage senden
my $receive=$trans{$t}{receive};
@@ -187,11 +207,10 @@
$receive=$eibgaconf{$receive}{ga} if $receive!~/^[0-9\/]+$/ && defined $eibgaconf{$receive};
$transmit=$eibgaconf{$transmit}{ga} if $transmit!~/^[0-9\/]+$/ && defined $eibgaconf{$transmit};
- # Debugging
unless($transmit eq $receive)
{
- plugin_log($plugname, "$input ($receive) -> $result ($transmit)");
knx_write($transmit, $result);
+ return "$input ($receive) -> $result ($transmit)";
}
}
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-02 23:37:37
|
Revision: 783
http://openautomation.svn.sourceforge.net/openautomation/?rev=783&view=rev
Author: pfry
Date: 2012-05-02 23:37:31 +0000 (Wed, 02 May 2012)
Log Message:
-----------
Modified Paths:
--------------
wiregate/plugin/generic/Szenencontroller.pl
wiregate/plugin/generic/Translator.pl
Modified: wiregate/plugin/generic/Szenencontroller.pl
===================================================================
--- wiregate/plugin/generic/Szenencontroller.pl 2012-05-02 23:05:18 UTC (rev 782)
+++ wiregate/plugin/generic/Szenencontroller.pl 2012-05-02 23:37:31 UTC (rev 783)
@@ -62,19 +62,19 @@
my $count=0;
my $scene_lookup='';
- for my $room (keys %scene)
+ for my $sc (sort keys %scene)
{
- next if $room eq 'storage';
+ next if $sc eq 'storage';
- my $store=$scene{$room}{store};
- my $recall=$scene{$room}{recall};
+ my $store=$scene{$sc}{store};
+ my $recall=$scene{$sc}{recall};
- $store=$scene{$room}{store}=$eibgaconf{$store}{ga} if $store!~/^[0-9\/]+$/ && defined $eibgaconf{$store};
- $recall=$scene{$room}{recall}=$eibgaconf{$recall}{ga} if $recall!~/^[0-9\/]+$/ && defined $eibgaconf{$recall};
+ $store=$scene{$sc}{store}=$eibgaconf{$store}{ga} if $store!~/^[0-9\/]+$/ && defined $eibgaconf{$store};
+ $recall=$scene{$sc}{recall}=$eibgaconf{$recall}{ga} if $recall!~/^[0-9\/]+$/ && defined $eibgaconf{$recall};
next unless defined $store && defined $recall;
- $scene_lookup.="St($store)=>'$room', Rc($recall)=>'$room', ";
+ $scene_lookup.="St($store)=>'$sc', Rc($recall)=>'$sc', ";
$plugin_subscribe{$store}{$plugname}=1;
$plugin_subscribe{$recall}{$plugname}=1;
@@ -105,7 +105,7 @@
}
my $cmd=$1; chop $cmd;
- my $room=$2;
+ my $sc=$2;
if($eibgaconf{$ga}{DPTSubId} eq '1.017')
{
@@ -113,7 +113,7 @@
return unless $msg{src}=~/[0-9]+\.[0-9]+\.([0-9]+)/;
$n=$1;
}
- elsif($scene{$room}{store} eq $scene{$room}{recall})
+ elsif($scene{$sc}{store} eq $scene{$sc}{recall})
{
# Speichern oder Abrufen? Falls beides die gleiche GA, aus 7. Bit der Szenennummer ableiten
$cmd = ($n & 0x80)?'S':'R';
@@ -125,26 +125,18 @@
}
# Szenencode
- my $z="$room\__$n";
+ my $z="$sc\__$n";
- # Debugging
-
- if($cmd eq 'S')
- {
- plugin_log($plugname, "Szene $z speichern: ".join(',', (keys %{$scene{$room}{gas}})));
- }
- else
- {
- plugin_log($plugname, "Szene $z abrufen: ".join(',', (values %{$scene{$room}{gas}})));
- }
-
if($cmd eq 'S') # Szene speichern
{
+ # Debugging
+ plugin_log($plugname, "Szene $z speichern: ".join(',', (sort keys %{$scene{$sc}{gas}})));
+
delete $scene{$z};
- for my $ga (keys %{$scene{$room}{gas}})
+ for my $ga (keys %{$scene{$sc}{gas}})
{
- my $wga=$scene{$room}{gas}{$ga}; # auf diese GA muss spaeter geschrieben werden
+ my $wga=$scene{$sc}{gas}{$ga}; # auf diese GA muss spaeter geschrieben werden
$wga=$eibgaconf{$wga}{short} if $wga=~/^[0-9\/]+$/ && $use_short_names && defined $eibgaconf{$wga}{short};
$ga=$eibgaconf{$ga}{ga} if $ga!~/^[0-9\/]+$/ && defined $eibgaconf{$ga};
$scene{$z}{$wga}=knx_read($ga,300);
@@ -162,6 +154,9 @@
}
else # Szene abrufen
{
+ # Debugging
+ plugin_log($plugname, "Szene $z abrufen: ".join(',', (sort values %{$scene{$sc}{gas}})));
+
for my $v (keys %{$scene{$z}})
{
my $ga=$v;
Modified: wiregate/plugin/generic/Translator.pl
===================================================================
--- wiregate/plugin/generic/Translator.pl 2012-05-02 23:05:18 UTC (rev 782)
+++ wiregate/plugin/generic/Translator.pl 2012-05-02 23:37:31 UTC (rev 783)
@@ -61,7 +61,7 @@
my $count=0;
my $rxtx_lookup='';
- for my $t (keys %trans)
+ for my $t (sort keys %trans)
{
my $receive=$trans{$t}{receive};
my $transmit=$trans{$t}{transmit};
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-02 23:05:24
|
Revision: 782
http://openautomation.svn.sourceforge.net/openautomation/?rev=782&view=rev
Author: pfry
Date: 2012-05-02 23:05:18 +0000 (Wed, 02 May 2012)
Log Message:
-----------
Modified Paths:
--------------
wiregate/plugin/generic/Szenencontroller.pl
wiregate/plugin/generic/Translator.pl
Modified: wiregate/plugin/generic/Szenencontroller.pl
===================================================================
--- wiregate/plugin/generic/Szenencontroller.pl 2012-05-02 22:36:50 UTC (rev 781)
+++ wiregate/plugin/generic/Szenencontroller.pl 2012-05-02 23:05:18 UTC (rev 782)
@@ -128,8 +128,16 @@
my $z="$room\__$n";
# Debugging
- plugin_log($plugname, "Szene $z ".($cmd eq 'S'?'speichern':'abrufen'));
-
+
+ if($cmd eq 'S')
+ {
+ plugin_log($plugname, "Szene $z speichern: ".join(',', (keys %{$scene{$room}{gas}})));
+ }
+ else
+ {
+ plugin_log($plugname, "Szene $z abrufen: ".join(',', (values %{$scene{$room}{gas}})));
+ }
+
if($cmd eq 'S') # Szene speichern
{
delete $scene{$z};
Modified: wiregate/plugin/generic/Translator.pl
===================================================================
--- wiregate/plugin/generic/Translator.pl 2012-05-02 22:36:50 UTC (rev 781)
+++ wiregate/plugin/generic/Translator.pl 2012-05-02 23:05:18 UTC (rev 782)
@@ -118,7 +118,14 @@
if($msg{apci} eq "A_GroupValue_Read")
{
# Ein Read-Request auf einer Transmit-GA wird mit dem letzten Ergebnis beantwortet
- knx_write($ga, $plugin_info{$plugname.'_'.$t.'_result'}) if $cmd eq 'T';
+ if($cmd eq 'T')
+ {
+ my $transmit=$ga;
+ $transmit=$eibgaconf{$ga}{short} if $use_short_names;
+ my $result=$plugin_info{$plugname.'_'.$t.'_result'};
+ plugin_log($plugname, "memory: $result ($transmit)");
+ knx_write($ga, $result);
+ }
return;
}
elsif($msg{apci} eq "A_GroupValue_Write")
@@ -177,13 +184,15 @@
my $receive=$trans{$t}{receive};
my $transmit=$trans{$t}{transmit};
- # Debugging
- plugin_log($plugname, "$input ($receive) -> $result ($transmit)");
-
$receive=$eibgaconf{$receive}{ga} if $receive!~/^[0-9\/]+$/ && defined $eibgaconf{$receive};
$transmit=$eibgaconf{$transmit}{ga} if $transmit!~/^[0-9\/]+$/ && defined $eibgaconf{$transmit};
- knx_write($transmit, $result) unless($transmit eq $receive);
+ # Debugging
+ unless($transmit eq $receive)
+ {
+ plugin_log($plugname, "$input ($receive) -> $result ($transmit)");
+ knx_write($transmit, $result);
+ }
}
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-02 22:36:56
|
Revision: 781
http://openautomation.svn.sourceforge.net/openautomation/?rev=781&view=rev
Author: pfry
Date: 2012-05-02 22:36:50 +0000 (Wed, 02 May 2012)
Log Message:
-----------
Modified Paths:
--------------
wiregate/plugin/generic/Szenencontroller.pl
wiregate/plugin/generic/Translator.pl
Modified: wiregate/plugin/generic/Szenencontroller.pl
===================================================================
--- wiregate/plugin/generic/Szenencontroller.pl 2012-05-02 22:03:43 UTC (rev 780)
+++ wiregate/plugin/generic/Szenencontroller.pl 2012-05-02 22:36:50 UTC (rev 781)
@@ -1,3 +1,4 @@
+#!/usr/bin/perl -w
####################
# Szenencontroller #
####################
@@ -127,7 +128,7 @@
my $z="$room\__$n";
# Debugging
-# plugin_log($plugname, "Szene $z ".($cmd eq 'S'?'speichern':'abrufen'));
+ plugin_log($plugname, "Szene $z ".($cmd eq 'S'?'speichern':'abrufen'));
if($cmd eq 'S') # Szene speichern
{
Modified: wiregate/plugin/generic/Translator.pl
===================================================================
--- wiregate/plugin/generic/Translator.pl 2012-05-02 22:03:43 UTC (rev 780)
+++ wiregate/plugin/generic/Translator.pl 2012-05-02 22:36:50 UTC (rev 781)
@@ -1,3 +1,4 @@
+#!/usr/bin/perl -w
##############
# Translator #
##############
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|