Aller au contenu
Franco268

net.TCPSocket

Recommended Posts

Hello,

 

Je cherche un peu d'aide pour reprendre un code HC2 et le mettre sur ma HC3.

 

Je souhaite faire 3 requetes pour obtenir le statut de mon UPS.

Le probleme est que je n'arrive pas à découper les réponses...

La réponse de mes 2 premières demandes se retrouve dans la première réponse et la dernière demande, dans la seconde réponse. J'ai fais des tentatives de sleep, d'imbriquer les "read" dans les "Write", mais rien n'y fait

Pouvez vous m'aider? J'ai beaucoup lu sur le forum et utiliser l'exemple Fibaro HC3. Mai je ne comprend pas comment synchroniser les réponses avec les demandes

 

-- Quick App template for handling the TCP device

debug = true

selfIp = "192.168.100.2"; -- the IP of this virtual device
selfPort = 3493; -- the dedicated port for this virtual device
tcpCommandVarGet = "GET VAR"; -- TCP command to get variable
tcpCommandUpsName = "UPS"; -- Name of UPS
timeOut = 5; -- Time-out attempts for setting up a connection
timeOutTime = 1; -- Time-out time for setting up a connection, in seconds
scrollDelaySec = 2; -- Raport scrolldelay in seconds
instanceValueWrite = 1; -- Instancevalue to write (must be an unique number per button in VD)
instanceValueComError = 8; -- Instancevalue when communication error is reported
instanceValueDecError = 9; -- Instancevalue when decoding received data error is reported
instanceValue = 0;
isOnline = 0;
enter = string.char(0x0d, 0x0a);
receivedTable = {};


function QuickApp:BtnMaj_onReleased(event)
if debug then self:debug("BtnMaj_onReleased(event)") end
	--self:send("Some command for on\n") -- sending data to the device. In a normal implementation it will be a code with an appropriate command.

	self:send(tcpCommandVarGet.." "..tcpCommandUpsName.." " .."ups.status"..enter)
	self:send(tcpCommandVarGet.." "..tcpCommandUpsName.." " .."battery.charge"..enter)
	self:send(tcpCommandVarGet.." "..tcpCommandUpsName.." " .."battery.runtime"..enter)
end

-- the method for sending data to the device
-- the method can be called from anywhere
function QuickApp:send(strToSend)
   self.sock:write(strToSend, {
        success = function() -- the function that will be triggered when the data is correctly sent
            self:debug("data sent: " ..strToSend)
        end,
        error = function(err) -- the function that will be triggered in the event of an error in data transmission
            self:debug("error while sending data")
        end
    })
end

-- method for reading data from the socket
-- since the method itself has been looped, it should not be called from other locations than QuickApp:connect
function QuickApp:waitForResponseFunction()
    self.sock:read({ -- reading a data package from the socket
        success = function(data)
            self:onDataReceived(data) -- handling of received data
            self:waitForResponseFunction() -- looping of data readout
        end,
        error = function() -- a function that will be called in case of an error when trying to receive data, e.g. disconnecting a socket
            self:debug("response error")
            self.sock:close() -- socket closed
            fibaro.setTimeout(5000, function() self:connect() end) -- re-connection attempt (every 5s)
        end
    })
end

-- a method to open a TCP connection.
-- if the connection is successful, the data readout loop will be called QuickApp:waitForResponseFunction()
function QuickApp:connect()
    self.sock:connect(self.ip, self.port, { -- connection to the device with the specified IP and port
        success = function() -- the function will be triggered if the connection is correct
            self:debug("connected")
            self:send("VER"..enter)
            self:waitForResponseFunction() -- launching a data readout "loop"
        end,
        error = function(err) -- a function that will be triggered in case of an incorrect connection, e.g. timeout
            self.sock:close() -- closing the socket
            self:debug("connection error")
            fibaro.setTimeout(5000, function() self:connect() end) -- re-connection attempt (every 5s)
        end,
    })
end

-- function handling the read data
-- normally this is where the data reported by the device will be handled
function QuickApp:onDataReceived(data)
	data = tostring(data)
	self:debug("onDataReceived", data)
	if data ~= "" then
		table.insert(receivedTable, data);
		if debug then self:debug("Table filled, row number: "..#receivedTable) end;
			
		if #receivedTable ~= 0   then  --and isOnline == 1 and instanceValue == instanceValueWrite
			if receivedTable[#receivedTable] ~= nil then
				textValueStart = tostring(string.find(receivedTable[#receivedTable], '"'));
				self:debug("textValueStart: ", textValueStart)
				if textValueStart ~= "nil" then
					textValueEnd = tostring(string.find(receivedTable[#receivedTable], '"', textValueStart+1));
					self:debug("textValueEnd: ", textValueEnd)
					if textValueEnd ~= "nil" then
						textValue = string.sub(receivedTable[#receivedTable], textValueStart+1, textValueEnd-1);
		
						self:debug("textValue: ", textValue)
						if string.find(receivedTable[#receivedTable], "ups.status") ~= nil then self:updateView("LblStatus", "text", "Statut: " ..textValue) end
						if string.find(receivedTable[#receivedTable], "battery.charge") ~= nil then self:updateView("LblBatLevel", "text", "Battery Level: " ..textValue .."%") end
						if string.find(receivedTable[#receivedTable], "battery.runtime") ~= nil then self:updateView("LblBatTime", "text", "Battery Time: " ..textValue .." sec") end
						self:updateView("upsReportLabel", "text", "Value "..textValue.." written.");

						hub.sleep(scrollDelaySec*1000);
					else
						instanceValue = instanceValueDecError
						if debug then hub.debug("Test Tag", "Error decoding value: missing second quotesign...") end;
					end
				else
					instanceValue = instanceValueDecError
					if debug then hub.debug("Test Tag", "Error decoding value: missing first quotesign...") end;
				end
			else
				instanceValue = instanceValueDecError
				if debug then hub.debug("Test Tag", "Error decoding value: not really found something...") end;
			end
		else
			instanceValue = instanceValueDecError
			if debug then hub.debug("Test Tag", "Error decoding value: no text to decode...") end;
		end
		if instanceValue == instanceValueDecError then
			self:updateView("upsReportLabel", "text", "Decoding error: value not written to GV");
			hub.sleep(scrollDelaySec*1000);
		end
	
	else
		self:updateView("LblStatus", "text", "Statut: No response!");
		self:updateView("LblBatLevel", "text", "Battery Level: No response!");
		self:updateView("LblBatTime", "text", "Battery Time: No response!");
  
		instanceValue = instanceValueComError
		if debug then self:debug("Error: No data received!") end;
		--hub.sleep(scrollDelaySec*1000);
	end
	
	
	
end


function QuickApp:onInit()
    self:debug("onInit")

	self.ip = self:getVariable("ip")
	self.port = tonumber(self:getVariable("port"))
	
    self.sock = net.TCPSocket() -- creation of a TCPSocket instance
    self:connect()
end

 

 

Partager ce message


Lien à poster
Partager sur d’autres sites

Alors, sans prendre le temps d'étudier ton code, il faut que tu comprennes la notion d'asynchronisme.

Le code en cours d'exécution se termine, puis un nouveau thread est démarré qui va prendre le relai, etc.
Donc il faut enchainer les appels de fonction dans le retour de la fonction success() à chaque appel. Etc.

 

Et surtout ne jamais utiliser de sleep()

 

Exemple ici pour des requêtes HTTP, mais c'est pareil pour le TCPsocket, sauf que tu dois faire plus d'enchainement connect => success => transmit => success => receive => success => disconnect...

 

 

Partager ce message


Lien à poster
Partager sur d’autres sites

J'ai tenté d'enchainé toutes mes étapes. Mais je ne comprends pas du tout le résultat.

Les 2 premières commandes sont envoyés. Mais pas la troisième.

Le truc de fou, c'est qu'en réponse, j'ai dans UN SEUL retour la réponse de la seconde requête ET de la TROISIEME! Celle qui n'a pas été envoyé!

 

J'ai essayé de mettre mon code au plus simple mais je ne comprends pas l'erreur.

-- Quick App template for handling the TCP device

debug = true

selfIp = "192.168.100.2"; -- the IP of this virtual device
selfPort = 3493; -- the dedicated port for this virtual device
tcpCommandVarGet = "GET VAR"; -- TCP command to get variable
tcpCommandUpsName = "UPS"; -- Name of UPS
enter = string.char(0x0d, 0x0a);

function QuickApp:BtnMaj_onReleased(event)
	self:debug("BtnMaj_onReleased(event)")

	self:Send(tcpCommandVarGet.." "..tcpCommandUpsName.." " .."ups.status"..enter)
	self:Send(tcpCommandVarGet.." "..tcpCommandUpsName.." " .."battery.charge"..enter)
	self:Send(tcpCommandVarGet.." "..tcpCommandUpsName.." " .."battery.runtime"..enter)
	
end

function QuickApp:Send(request)
    self.sock = net.TCPSocket() -- creation of a TCPSocket instance
    self.sock:connect(self.ip, self.port, { -- connection to the device with the specified IP and port
        success = function() -- the function will be triggered if the connection is correct
            self:debug("connected")
			self.sock:write(request, {
				success = function() -- the function that will be triggered when the data is correctly sent
					self:debug("data sent: " ..request)
					self.sock:read({ -- reading a data package from the socket
						success = function(data)
								data = tostring(data)
								self:debug("onDataReceived", data)
								self.sock:close() -- socket closed
						end,
						error = function() -- a function that will be called in case of an error when trying to receive data, e.g. disconnecting a socket
							self:debug("response error")
							self.sock:close() -- socket closed
						end
					})	
				end,
				error = function(err) -- the function that will be triggered in the event of an error in data transmission
					self:debug("error while sending data")
				end
			})	
        end,
        error = function(err) -- a function that will be triggered in case of an incorrect connection, e.g. timeout
            self.sock:close() -- closing the socket
            self:debug("connection error")
        end,
	})
end

function QuickApp:onInit()
    self:debug("onInit")
	self.ip = self:getVariable("ip")
	self.port = tonumber(self:getVariable("port"))
	
end

Résultat:

[03.02.2023] [15:15:02] [DEBUG] [QUICKAPP70]: onInit
[03.02.2023] [15:15:05] [TRACE] [QUICKAPP70]: UIEvent: {"elementName":"BtnMaj","deviceId":70,"eventType":"onReleased","values":[null]}
[03.02.2023] [15:15:05] [DEBUG] [QUICKAPP70]: BtnMaj_onReleased(event)
[03.02.2023] [15:15:05] [DEBUG] [QUICKAPP70]: connected
[03.02.2023] [15:15:05] [DEBUG] [QUICKAPP70]: connected
[03.02.2023] [15:15:05] [DEBUG] [QUICKAPP70]: connected
[03.02.2023] [15:15:05] [DEBUG] [QUICKAPP70]: data sent: GET VAR UPS ups.status
[03.02.2023] [15:15:05] [DEBUG] [QUICKAPP70]: data sent: GET VAR UPS battery.charge
[03.02.2023] [15:15:05] [DEBUG] [QUICKAPP70]: onDataReceived VAR UPS battery.runtime "3000" battery.charge "100"
[03.02.2023] [15:15:05] [DEBUG] [QUICKAPP70]: error while sending data
[03.02.2023] [15:15:05] [DEBUG] [QUICKAPP70]: onDataReceived

 

Partager ce message


Lien à poster
Partager sur d’autres sites

Dans le log on voit que 3 connect et 2 write ont effectués avant même que tu tentes de faire le 1er read. Celui-ci fait immédiatement un close de la socket, donc le 3ème write n'est plus possible.


Normal, car tout s'exécute en asynchrone (donc tu ne peux pas maitriser le timing), cependant dans le code de ton bouton tu as lancé 3 séquences (asynchrones donc) en simultané.
Ce n'est pas bon, il faut lancer uniquement la 1ère, puis quand elle est complètement terminée (dans le succes() du dernier read(), alors tu peux enchainer sur la suite)

 

Voir à ce sujet la discussion récente ici :

 

 

Modifié par Lazer

Partager ce message


Lien à poster
Partager sur d’autres sites

Merci, pour le lien. L'imbrication est acquis... mon petit bout de code tourne.

Prochaine étape le "join" pour du multi requête

Partager ce message


Lien à poster
Partager sur d’autres sites

×