Fehlende Zeichen bei Übertragung - zu viele Threads?

Hallo,

ich stehe seit Jahren vor dem Problem, dass bei verschiedenen Datenübertragungen von externen Geräten zu Symcon, auf ganz unterschiedlichen Wegen (TCP, USB direkt am Host, USB über Silex USB Host) immer wieder Zeichen fehlen, mit gravierenden Folgen. Ich habe schon in die meisten Richtungen geforscht, aber mir erscheint eigentlich nur noch eines denkbar, nämlich dass immer dann, wenn kurzzeitig alle Threads „voll“ sind, ankommende Datenpakete nicht verarbeitet werden, unabhängig ob ich sie per Skript zusammenfüge oder mit entsprechenden Cutterinstanzen.

Frage an die Entwickler: Ist dies möglich oder ist eine andere Erklärung wahrscheinlicher?

Gibt es eine Möglichkeit, zu erforschen, welche Skripte zu viele Threads blockieren? Das blitzt ja in der Threadanzeige immer nur für Sekundenbruchteile auf. Sie sieht schon immer mal für kurze Momente „voll“ aus aber es ist kaum zu erkennen, womit.

Ich bin wirklich ratlos und mich plagt dieses Phänomen seit Jahren in meiner Heiminstallation.

Danke!
:banghead:

Hast du denn schon mal probiert, die Anzahl der Threads zu erhöhen? Der Default liegt meiner Meinung nach zur Zeit bei 30.

Ja, das verschiebt das Problem ja aber nur auf die Lange Bank. Irgendwo muss ein Fehler sein, der dafür sorgt, dass manchmal „irre viele“ Threads gleichzeitig belegt sind.

Screenshot 2020-10-03 203133.png

Es ist echt zum Mäuse melken.:frowning:

Habe die Threadanzahl und Queue jetzt zum Test weiter hochgeschraubt, bin mittlerweile bei 100. Scheint eventuell seltener aufzutreten. Im Log findet sich zu den Zeiten, wo der Fehler aufgetreten ist, nichts auffälliges.
:confused:

Cutter oder eigenes Script welche den String zusammenbaut?
Könnte ein Fehler im Script sein, oder auch das schon Daten am IO fehlen, bevor sie am Cutter/RegVar landen?
Sonst davon ein dump machen.
Michael

Es passiert sowohl bei Cuttern als auch im eigenen Skript. Wegen des sporadischen Auftretens kann ich nichts ausschließen, aber ich habe das weder in direkten Konsolen (Putty o.ä.) beobachtet, noch in der Debugansicht der entsprechenden IO-Instanzen.

Es passiert halt auch bei verschiedensten IO-Verbindungen, egal ob sie über USB kommen oder über TCP.

Servus, ich kann dir da leider nicht helfen, hattee aber schon lange mal angeregt, das man in die Thread Anzeige abhängig von gewissen Kriterien „einfrieren“ könnte. also zb.

  • Anzeige Stop wenn mehr als x Threads laufen,
  • oder Anzeige Stop wenn Thread x/y auftaucht

schöne Grüße
Bernhard

Auch ein Cutter hat ja dahinter eine RegVar und ein Script.
Wenn du es in den IOs siehst, dann schau dir die anderen Dumps der Ketten an, wo das auftritt.
Die kannst du jetzt auch auf die Platte schreiben lassen und musst nicht die ganze Zeit vor der Konsole verbringen.
Etwas grundlegendes kann es ja kaum sein, sonst würden ja auch mit bulit-in Instanzen Probleme geben, wie LCN oder Homematic.
Wenn es bis zur RegVar sauber durchläuft, dann musst du uns dein(e) Scripte zeigen.
Michael

Sind so einige, relativ verschiedene Skripte, die am Ende die Nachricht parsen. Die meisten haben mittlerweile Workarounds drin zur „Plausiblitätsprüfung“ denn wenn das mit dem Zeichenverlust bei numerischen Werten auftritt, die irgendwo in Berechnungen wieder auftauchen, dann kann es ziemlich nervige Folgen haben.

<?
$lock = true;

$rootId = $_IPS['SELF'];

if($_IPS['SENDER'] == 'RegisterVariable') {
	$bufStr = RegVar_GetBuffer($_IPS['INSTANCE']) . $_IPS['VALUE'];
} else if($_IPS['SENDER'] == 'Execute') {
	$bufStr = "{\"test\":true}
{\"Hallo\":\"Welt!\"}\r";
}

$bufStr = str_replace("
", "", $bufStr);
while(strpos($bufStr, "}") !== false)
{
	$msg = substr($bufStr, 0, strpos($bufStr, "}") + 1);
	$bufStr = substr($bufStr, strpos($bufStr, "}") + 1);
	
	if($msg != "") {
		if($_IPS['SENDER'] == 'Execute') {
			echo "Json-Nachricht: ";
			echo $msg . "\r";
		}
		handle_msg($msg);
	}
}

if($_IPS['SENDER'] == 'RegisterVariable') {
	if($bufStr === false) $bufStr = "";
	RegVar_SetBuffer($_IPS['INSTANCE'], $bufStr);
}

function handle_msg($json) {
	$arr = json_decode($json, true);
	if($arr) {
		foreach($arr as $key => $value) {
			set_var($key, $value);
		}
	}
}

function set_var($name, $value) {
	global $lock, $rootId;
	switch(gettype($value)) {
		case "boolean": $ips_type = 0; break;
		case "integer": $ips_type = 1; break;
		case "double": $ips_type = 2; break;
		case "string": $ips_type = 3; break;
		default:
			IPS_LogMessage($_IPS['SELF'], "Data type " .
				gettype($value) . " of variable " .
				$name . " not supported.");
			return;
	}
	$var_id = @IPS_GetObjectIDByIdent($name, $rootId);
	if($var_id === false) {
		if($lock) return; // WORKAROUND damit keine sinnlosen Variablen erzeugt werden!
		// durch einen unbekannten Datenfehler kommem manchmal einzelne Zeichen
		// der Verbindung nicht an
		$var_id = IPS_CreateVariable($ips_type);
		IPS_SetName($var_id, $name);
		IPS_SetIdent($var_id, $name);
		IPS_SetParent($var_id, $rootId);
	}
	
	if($lock) {
		// WORKAROUND discard implausible water meter readouts
		if($name == "water" || $name == "water1" || $name == "water2") {
			$oldvalue = GetValue($var_id);
			if($value < $oldvalue) return; // can't be lower than the old value
			if($value > $oldvalue + 1) return; // max increment 1m3
			//if($value > $oldvalue + 0.1) return;
		}
	}
	
	SetValue($var_id, $value);
}

?>

In diesem Beispiel würde ich einem Cutter einsetzen welche auf den Zeilenumbruch schneidet und dann im Script direkt mit $arr = json_decode($_IPS[‚VALUE‘], true); anfangen.
Warum die Mühe machen und selber da mit substr und strpos das zu zerlegen?
Das kostet bei jedem empfangen Teilpaket des json dich einem php-slot. Der Cutter belegt keinen und schon somit Ressourcen.
Michael

Da ist ein Cutter davor, der entsprechende Code ist noch von vorher (funktioniert aber so auch weiter). Sicherlich kann ich ihn mal überarbeiten und optimieren, aber davon allein kann doch nicht der beschriebene Effekt kommen.

Ich habe den Code jetzt nicht getestet oder komplett geprüft.
Unabhängig davon, wenn da ein Cutter vor ist, warum dann noch im Script schneiden, stückeln und ersetzen?
Ist ja ineffizient wenn es dadurch x-mal ausgeführt wird bis ein json komplett ist.
Michael

Nein, das ist ein Relikt. Der Cutter gibt nur noch das komplette Paket weiter. Früher hatte ich es ohne Cutter am laufen.

Meinst du denn, das Phänomen ist auf die Weise zu erklären, also durch zu viele Threads?

Oder Scripte wo eventuell nicht gut auf den Buffer der RegVar zugegriffen wird, so das bei bestimmten Umständen etwas verloren geht.
Michael

Hast du im Cutter mal geschaut, ob dort alle Zeichen immer drin sind? PHP Threads können nicht das Problem sein - es wird alles schön nacheinander abgearbeitet.

paresy

Wie prüfe ich denn das?

Das Problem tritt ja nur sporadisch auf, so zwischen 0 und ca. 10 mal am Tag. Das einzige worauf ich reagieren könnte ist, wenn ich am Ende in dem RegVar-Handlerskript korrupte (oder grob unplausible) Daten habe. Aber dann kann ich ja vermutlich nicht sowas wie ein „Stacktrace“ von dem Datenfluss machen, bei dem ich das dann sehe, wo der Fehler zustande kam, oder?

Das wirklich sehr seltsame ist, dass das bei vielen unterschiedlichen und unterschiedlich angebundenen Geräten auftritt, die mir serielle Textdaten schicken. Ob es auch bei anderen (LCN, HomeMatic) auftritt weiß ich nicht, dort würde vermutlich irgendeine Fehlerkorrektur diese Dinge herausfiltern(?)

Aber am deutlichsten merke ich es bei den Gas- und Wasserzählern, wenn diese plötzlich „zurückspringen“. Das erzeugt jedes Mal ein Datenchaos.

Du kannst das debug in Datei Feature aktivieren. Dann speichern wir alles in debug Reiter auf die Platte und du kannst hinterher in Ruhe alle Werte durchgehen.

paresy

Das mache ich, bin mal gespannt.

Erwischt! Aus der IO-Instanz:

08.10.2020 07:53:52 | TXT | RECEIVED | ,"ma
08.10.2020 07:53:52 | HEX | RECEIVED | 2C226D61
08.10.2020 07:53:52 | TXT | RECEIVED | ilbo
08.10.2020 07:53:52 | HEX | RECEIVED | 696C626F
08.10.2020 07:53:52 | TXT | RECEIVED | x":"mpty","gas_meter":17522.00,"water_meter":0.000,"power":true,"heating":"on"}<CR><LF>
08.10.2020 07:53:52 | HEX | RECEIVED | 78223A226D707479222C226761735F6D65746572223A31373532322E30302C2277617465725F6D65746572223A302E3030302C22706F776572223A747275652C2268656174696E67223A226F6E227D0D0A

Der rot markierte String muss eigentlich „empty“ heißen.

:confused:

Edit: Einzig auffälliges Merkmal: Sonst kommen meist kleinere „Häppchen“ von einigen wenigen Zeichen auf einmal (siehe Logeinträge davor), aber an der Stelle wo der Fehler aufgetreten ist, ist es ein längerer String am Stück… aber einen Reim kann ich mir darauf nicht machen. Irgendwo läuft wohl irgendein Puffer voll? Aber wo? Wie gesagt habe ich das Phänomen nicht nur bei USB-Serial- sondern auch bei bspw. TCP-Verbindungen.