Problem mit multithreading -_- Python Websocket Server -_-

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Soulpilot
User
Beiträge: 63
Registriert: Sonntag 24. April 2022, 12:19

Moin,

ich habe ein Problem oder besser gesagt zu wenig Ahnung, um in mein vorliegendes Websocket_Server Skript Multithreading zu implementieren. Vielleicht könnte mir jemand helfen oder ein paar Tipps geben, wie man das am besten machen könnte?

Zum eigentlichen Problem:
Der Websocket Server läuft zwar, allerdings wurde dort jetzt eine While Schleife integriert (siehe def set_temperature1), was dazu führt, dass das gesamte Skript blockiert wird. Also keine weitere I/O mehr möglich ist. Mein Wunsch wäre, dass der while Loop nebeher läuft und I/O dennoch möglich sind. Auch ein weiterer Subprozess ist geplant aber erstmal wäre es schön, wenn das Programm hier korrekt läuft.

Nach meiner Recherche sollte Multithreading in Kombination mit Websockets funktionieren, allerdings habe ich auch gelesen, dass bei Websockets wohl eher asycio zum Einsatz kommt, weil das bei I/O Vorteile bringen soll. Allerdings stehe ich was asyncio und threading betrifft, komplett am Anfang und weiß nicht so recht, wie das ordentlich implementiert wird.

Vielleicht könnte mir jemand weiter helfen, ich hänge die Files mit dran.



websocket_server_multithreading.py

Code: Alles auswählen

import signal, sys, json
import os
import shutil
import logging
import time
import sqlite3
from sqlite3 import Error
from functools import partial

# import thread module
from _thread import *
import threading

from pymodbus.client.sync import ModbusTcpClient as ModbusClient
from SimpleWebSocketServer import WebSocket, SimpleWebSocketServer
logger = logging.getLogger(__name__)

# Adressen
UNIT = 0x1

HOST = '192.168.0.24'
#HOST = '192.168.0.50'

PORT = 5020
#PORT = 502

# Coil Output Base Address
COILBASE = 512
# Portnummer listen
PORTNUM = 8001

# Websocket class to echo received data
class Echo(WebSocket):

    def __init__(self, client, server, sock, address, threading):
        try:
            super(Echo, self).__init__(server, sock, address)

            self.modbus = client
            self.functions = {
                "relais1": partial(self.simple_switch, COILBASE + 16),
                "relais2": partial(self.simple_switch, COILBASE + 1),
                "relais3": partial(self.simple_switch, COILBASE + 2),
                "set_temp1": threading.Thread(target=self.set_temperature1, args=(524)),
#                "set_temp1": partial(self.set_temperature1, 524),
                "temperatur1": partial(self.read_temperature, 2),
                "temperatur2": partial(self.read_temperature, 3),
                "heizen1": partial(self.heating, 524),
                "heizen2": partial(self.heating, 525),
                "heatread": partial(self.simple_read, 524),
                "systemp": self.systemp
            }
        except Exception as exception:
            logger.exception("constructor")
            raise


    def simple_switch(self, address, value):
        """ value can be one of "on", "off" or "get" """
        if value in ['on', 'off']:
            rq = self.modbus.write_coil(address, value == 'on', unit=UNIT)
            time.sleep(0.01)
            print("rq = self.modbus.write_coil(address, value == on, unit=UNIT)")
        elif value != 'get':
            raise ValueError(value)
        # Relaisregister reading
        rp = self.modbus.read_coils(address, unit=UNIT)
        time.sleep(0.01)
        return "on" if rp.bits[0] else "off"


    def set_temperature1(self, address, value):
        print("set_temperature1 was triggerd")

        # Loop for temperature controll
        print("database_reading")
        print(self.database_reading()[1][0])
		
        if (self.database_reading()[0][1]) == 524 and (self.database_reading()[1][1]) == 'on':
            print("DB-Reading from set_temperature1 'heizen1': ", self.database_reading()[1][1])

            while True:

                print("Loop!!!!!!!")

                rp = self.modbus.read_coils(address, unit=UNIT)
                print(rp.bits[0])
                time.sleep(0.01)

                if rp.bits[0] == False:
                    rq = self.modbus.write_coil(address, value == "on", unit=UNIT)
                    time.sleep(0.01)
                    print("Heating is active!!!!")

                print(address)
                time.sleep(0.01)

                print(type(self.read_temperature( address == 2, 'get')))
                print(type(value))

                if float(self.read_temperature( address == 2, 'get')) >= float(value):
                    print("Heating is on!!!!!!!")
                    time.sleep(0.01)

                    print(address)
                    rq = self.modbus.write_coil(address, value == "off", unit=UNIT)
                    time.sleep(0.01)

                    print("Heating Temperture is achieved! -> deactivate heating")
                    break
# Breake müsste bei Heizen1 aus kommen, nicht wenn nur die Soll Temp erreicht wurde

#        return NULL


    def database_reading(self):

        def dict_factory(cursor, row):
            d = {}
            for idx, col in enumerate(cursor.description):
                d[col[0]] = row[idx]
            return d

        # Database state request | If heating button was pressed
        sqliteConnection = sqlite3.connect("state")
        sqliteConnection.row_factory = dict_factory
        zeiger = sqliteConnection.cursor()
        zeiger.execute("SELECT * FROM heating_relais_state")
#        zeiger.execute("SELECT heating_relais_address = 524 FROM heating_relais_state")
        row =  zeiger.fetchone()
        rowDict = dict(zip([c[0] for c in zeiger.description], row))

#        row = json.dumps(row)
#        row1 = json.loads(row)

        print(type(row))

        print("row1")

#        print(row1)
#        row1 = {}
#        print(row[0])

        # Convert a dict Array into List[]
        result = row.items()
        row1 = list(result)
        print(row1)
		
#        print(list(row.keya())[1])
#        print(list(row.items())[1])
#        print(list(row.values())[1])

        print(row1[1][1])

#        self.sendMessage(json.dumps(row))

#        row =  zeiger.fetchone()
#        print("row2")
#        print(row)

#        self.sendMessage(json.dumps(row))

#            print(rowDict)
#            inhalt = zeiger.fetchall()

        sqliteConnection.commit()

        if sqliteConnection:
            sqliteConnection.close()
            print("The SQLite connection is closed")
#        return json.dumps(row)
        return row1


    def simple_read(self, address, value):
        try:
            if value != 'get':
                raise ValueError(value)

            # Relaisregister reading | If heating wire is physically on
            rp = self.modbus.read_coils(address, unit=UNIT)
            time.sleep(0.01)

            def dict_factory(cursor, row):
                d = {}
                for idx, col in enumerate(cursor.description):
                    d[col[0]] = row[idx]
                return d

            # Database state request | If heating button was pressed
            sqliteConnection = sqlite3.connect("state")
            sqliteConnection.row_factory = dict_factory
            zeiger = sqliteConnection.cursor()
            zeiger.execute("SELECT * FROM heating_relais_state")
#            zeiger.execute("SELECT heating_relais_address = 524 FROM heating_relais_state")
            row =  zeiger.fetchone()
            rowDict = dict(zip([c[0] for c in zeiger.description], row))
            print("row12")
            print(row)
            self.sendMessage(json.dumps(row))
            # Jump to the next row
            row =  zeiger.fetchone()
            self.sendMessage(json.dumps(row))

#            print(rowDict)

#            inhalt = zeiger.fetchall()

            sqliteConnection.commit()

            if sqliteConnection:
                sqliteConnection.close()
                print("The SQLite connection is closed")

#            json_object = json.dumps(inhalt)
#            print(json_object)

            time.sleep(0.01)
            return "on" if rp.bits[0] else "off"
        except AttributeError:
            pass

    # Send temperature data to client
    def read_temperature(self, address, value):
        if value != "get":
            raise ValueError(value)
        response = self.modbus.read_holding_registers(0x00, 8, unit=UNIT)
        time.sleep(0.01)
        t = response.registers[address]
        time.sleep(0.01)
        z = t/10

        return z

#        return response.registers[address]
#        return response.registers[address]

    # Save heating_state in database
	# Get the heating_state with 'get'
    def heating(self, address, value):
        # Heizen an | Modul an dem der Heizdraht geschaltet wird
        if value in ["on", "off"]:
#            rq = self.modbus.write_coil(524, value == "on", unit=UNIT)
#            time.sleep(0.05)
#            rq = self.modbus.write_coil(address, value == "on", unit=UNIT)
#            time.sleep(0.01)

            # Database Handling | Heating_State = true does not mean Relaise_State = True | this depends on the temperature
            sqliteConnection = sqlite3.connect("state")
            zeiger = sqliteConnection.cursor()

            sql_anweisung = ("""CREATE TABLE IF NOT EXISTS heating_relais_state (
            heating_relais_address INTEGER, 
            state TEXT)""")

            zeiger.execute(sql_anweisung)

#            print(address)
#            zeiger.execute("INSERT INTO heating_relais_state VALUES (?,?)", (address, value))

#            zeiger.execute("INSERT INTO heating_relais_state VALUES (?,?)", (524, value))
#            zeiger.execute("INSERT INTO heating_relais_state VALUES (?,?)", (525, value))

#            zeiger.execute(sql_anweisung)

#            if value == "on":

            zeiger.execute("UPDATE heating_relais_state SET state =?  WHERE heating_relais_address =?", (value, address))
#            zeiger.execute(sql_anweisung)

            zeiger.execute("SELECT * FROM heating_relais_state")
            inhalt = zeiger.fetchall()
            for inhalt in inhalt:
                print(inhalt)

            sqliteConnection.commit()

            if sqliteConnection:
                sqliteConnection.close()
                print("The SQLite connection is closed")


        elif value != 'get':
            raise ValueError(value)

        rp = self.modbus.read_coils(address, unit=UNIT)
        time.sleep(0.01)
        return "on" if rp.bits[0] else "off"


    def systemp(self, value):
#        result = subprocess.run(["vcgencmd", "measure_temp"], stdout=subprocess.PIPE)
#        temperature = result.stdout.partition("temp=")[-1].partition("'C\n")[0]
        res = os.popen("vcgencmd measure_temp").readline()
        temp = (res.replace("temp=","").replace("'C\n",""))
        time.sleep(0.01)
        return temp



    def handleMessage(self):
        try:
#            print("Echoing '%s'" % self.data)
#            print("Echoing '%s'" % json.loads(self.data))

            commands = json.loads(self.data)
            new_state = {}

            print("Echoing '%s'" % commands)
#            print(commands['relais1'])

            for key, value in commands.items():
                function = self.functions[key]
                print("function '%s'" % function)
                new_state[key] = function(value)
                print("new_state '%s'" % new_state)
                print("key '%s'" % key)
                print("value '%s'" % value)
            print(json.dumps(new_state))
            self.sendMessage(json.dumps(new_state))

            print("Echoing '%s'" % commands)



#            print(commands['relais1'])
#            self.sendMessage(json.dumps(commands))

#            sampleDict = {'name': 'John', 'age': 30, 'data': 'New York'}
#            jsonData = json.dumps(sampleDict)
#            self.sendMessage(json.dumps(sampleDict))


        except Exception as exception:
            logger.exception("handle message")
            raise

    def handleConnected(self):
        print("Connected")

    def handleClose(self):
        print("Disconnected")


def main():
    logging.basicConfig()
    with ModbusClient(host=HOST, port=PORT) as client:
        client.connect()
        time.sleep(0.02)

        print("Websocket server on port %s" % PORTNUM)
#        server = SimpleWebSocketServer('', PORTNUM, Echo)
        server = SimpleWebSocketServer('', PORTNUM, partial(Echo, client))
        try:
            server.serveforever()
        finally:
            server.close()



if __name__ == "__main__":
    main()
client.html (JS)

Code: Alles auswählen

<!DOCTYPE html>
<head>
<link href="/favicon.png" rel="icon" type="image/png" />
</head>
<meta charset="utf-8"/>
<title>WebSocket</title>
<script language="javascript" type="text/javascript">

  // Client for Python SimpleWebsocketServer
  const portnum = 8001;
  var host, server, connected = false, relais1 = false, relais2 = false, relais3 = false, temp1 = false, temp2 = false, temperatur1 = 0, temperatur2 = 0, button_temp1 = false, button_temp2 = false, heating = false, feedpump = false, tempinput =0;

  // Display the given text
  function display(s)
  {
    document.myform.text.value += s;
    document.myform.text.scrollTop = document.myform.text.scrollHeight;
  }

  // Initialisation
  function init()
  {
//    host = "192.168.0.24";
//    host = "192.168.0.54";
//    host = location.host ? String(location.host) : "unknown";
//    host = host.replace("127.0.0.1", "localhost");
//    server = host.replace(/:\d*\b/, ":" + portnum);
    server = "192.168.0.24:8001";
//    server = "192.168.0.54:8001";
    document.myform.text.value = "Host " + host + "\n";
    window.setInterval(timer_tick, 2600);
    window.setInterval(timer_tick2, 1600);
    window.setInterval(timer_tick3, 10000);
    window.setInterval(timer_tick4, 10000);

    if (connected === false)
    {
      connect();
      connected = true;
    }

    //Timeout() initialisiert nur einmal im Gegensatz zu Intervall()
    setTimeout(() =>
    {
    if (connected === true)
    {

      image0()
	  
      websock.send(JSON.stringify({heatread: 'get'}));

      websock.send(JSON.stringify({relais1: 'get'}));
      websock.send(JSON.stringify({relais2: 'get'}));
      websock.send(JSON.stringify({relais3: 'get'}));


//      websock.send('abfrage_aller_register');
//      websock.send('temp1off');
//      websock.send('temp2off');

      button_temp1 = false;
      temp1 = false;

      button_temp2 = false;
      temp2 = false;

      heating = false;

    }
    }, 100);

  }

  // Open a Websocket connection
  function connect()
  {
    var url = "ws://" + server + "/";
    display("Opening websocket " + url + "\n");
    websock = new WebSocket(url);
    websock.onopen    = function(evt) {sock_open(evt)};
    websock.onclose   = function(evt) {sock_close(evt)};
    websock.onmessage = function(evt) {sock_message(evt)};
    websock.onerror   = function(evt) {sock_error(evt)};
    connected = true;
  }
  // Close a Websocket connection
  function disconnect()
  {
    connected = false;
    websock.close();   
  }

  // Timer tick handler
  function timer_tick()
  {
    if (connected)
    {
      websock.send(JSON.stringify({heatread: 'get'}));

//      websock.send(JSON.stringify({heatread: 'get'}));

//      console.log(temp_input);
//      window.alert(tempinput);

      websock.send(JSON.stringify({set_temp1: tempinput}));

      websock.send(JSON.stringify({relais1: 'get'}));
      websock.send(JSON.stringify({relais2: 'get'}));
      websock.send(JSON.stringify({relais3: 'get'}));

      websock.send(JSON.stringify({temperatur1: 'get'}));
//      websock.send(JSON.stringify({temperatur2: 'get'}));
      websock.send(JSON.stringify({systemp: 'get'}));

    }

//      websock.send('*');
//      websock.send('abfrage_aller_register');
//      websock.send('temperatur1');
//      websock.send('temperatur2');
//      websock.send('heatread');
//      websock.send('systemp');

  }

  function timer_tick2()
  {

    image0()
    image4()
    image5()

//    if (connected)
//      websock.send('*');
//      if (temp1 == true)

//        if ((button_temp1 == true && button_temp2 == false) && (temp1 === true && temp2 === false))
//        {
//          if (temperatur1 < 28)
//          {
           //if (temp1 === false)
//            {
//              websock.send('temp1');
//              image4()
//            }
//          }
//          else
//          {
            //if (temp1 === true)
//            {
//              websock.send('temp1off');
//              image4()
//            }
//          }
//        } 
//        if ((button_temp2 === true && button_temp1 === false) && (temp2 === true && temp1 === false))
//        {
//          if (temperatur1 < 35)
//          {
            //if (temp2 === false)
//            {
//              websock.send('temp2');
//              image5()
//            }
//          }
//          else
//          {
            //if (temp2 === true)
//            {
//            websock.send('temp2off');
//            image5()
//            }
//          }
//        }
  }

  function timer_tick3()
  {
//    if (connected)
//      websock.send('*');
//      websock.send('abfrage_aller_register');
//      websock.send('temp1state');
//      websock.send('temp2state');
//      websock.send('temp3state');
//      websock.send('temp4state');
  }

  function timer_tick4()
  {
//    if (connected)
//    {
//      if (feedpump === true)
//      {
//        setTimeout(() =>
//        {
        // Code, der erst nach 0.1 Sekunden ausgeführt wird
//        websock.send('relais3');
//        }, 100);

//        setTimeout(() =>
//        {
        // Code, der erst nach 2 Sekunden ausgeführt wird
//        websock.send('relais3off');
//        }, 3400);
//      }
//    }
  }


  // Incoming Data processing
  function sock_message(evt)
  {
//    display(evt.data);

    received_msg = JSON.parse(evt.data);

    const data_array = []

    for (const [key, value] of Object.entries(received_msg))
    {
        data_array.push([`${key}`, `${value}`]);
    }
    console.log(data_array)

    display(data_array[0] [0]);
	display(data_array[0] [1]);


    // Incoming data of relais hardware state
    if ( data_array[0] [0] === 'relais1' )
	{
      if ( data_array[0] [1] === 'on' )
      {
//       display("data_array[1] = on");
	relais1 = true;
        image1()
      }
      if ( data_array[0] [1] === 'off' )
      {
//       display("data_array[1] = off");
	relais1 = false;
        image1()
      }
    }

    if ( data_array[0] [0] === 'relais2' )
	{
      if ( data_array[0] [1] === 'on' )
      {
//       display("data_array[1] = on");
	relais2 = true;
        image2()
      }
      if ( data_array[0] [1] === 'off' )
      {
//       display("data_array[1] = off");
	relais2 = false;
        image2()
      }
    }

    if ( data_array[0] [0] === 'relais3' )
	{
      if ( data_array[0] [1] === 'on' )
      {
//       display("data_array[1] = on");
        relais3 = true;
        feedpump = true;
        image3()
      }
      if ( data_array[0] [1] === 'off' )
      {
//       display("data_array[1] = off");
        relais3 = false;
        feedpump = false;
        image3()
      }
    }


    // Fetch Heating Temperature
    if ( data_array[0] [0] === 'temperatur1' )
	{
	  let temper1 = data_array[0] [1];
      display(temper1);
      temperatur1 = temper1;
      output_temp1(temperatur1)
	}

    // Fetch Water Temperature
    if ( data_array[0] [0] === 'temperatur2' )
	{
	  let temper2 = data_array[0] [1];
      display(temper2);
      temperatur2 = temper2;
      output_temp2(temperatur2)
	}

    // System temperature of raspberry pi | "Modbuscontroller"
    if ( data_array[0] [0] === 'systemp' )
	{
	  let temper_pi = data_array[0] [1];
      display(temper_pi);
      temperatur8 = temper_pi;
      output_systemp(temperatur8)
	}

    // Red signal | Heating wire is heating | This depends on the temperature
    if ( data_array[0] [0] === 'heatread' )
	{
	  if ( data_array[0] [1] === 'on' )
	  {
	    heating = true;
          image6()
	  }
	  if ( data_array[0] [1] === 'off' )
	  {
	    heating = false;
          image6()
	  }
	}

    // Incoming database data of heating state | This depends of button heating was pressed
    if ( data_array[0] [1] === '524' )
    {
	  if ( data_array[1] [1] === 'on' )
	  {
	    display("524_on");
	    temp1 = true;
		button_temp1 = true;
		  image4()
		  image6()

		display("524 = on");
	  }
	  if ( data_array[1] [1] === 'off' )
	  {
	    temp1 = false;
		button_temp1 = false;
		  image4()
		  if ( temp2 === false)
		  {
		    image6()
		  }
	  }
	}

    if ( data_array[0] [1] === '525' )
    {
	  if ( data_array[1] [1] === 'on' )
	  {
	    display("525_on");
	    temp2 = true;
		button_temp2 = true;
		  image5()
		  image6()

		display("524 = on");
	  }
	  if ( data_array[1] [1] === 'off' )
	  {
	    temp2 = false;
		button_temp2 = false;
		  image5()
		  if ( temp1 === false)
		  {
		    image6()
		  }
	  }
	}





    //Old version of incomoming data processing

    //Temperatur von Heizmantel, Websocket übergabe von Websocketserver
    if (evt.data.includes(1020))
    {
      const myArray = evt.data.split(" ");
      let temper1 = myArray[1];
      display(temper1);
      temperatur1 = temper1;     
      output_temp1(temperatur1)
    }

    if (evt.data.includes(1024))
    {
      const myArray2 = evt.data.split(" ");
      let temper2 = myArray2[1];
      display(temper2);
      temperatur2 = temper2;
      output_temp2(temperatur2)
    }

    //Temperatur von Raspberry, Websocket übergabe von Websocketserver
    if (evt.data.includes(8888))
    {
      const myArray8 = evt.data.split(" ");
      let systemp = myArray8[1];
      display(systemp);
      temperatur8 = systemp;
      output_systemp(temperatur8)
    }


//    data = evt.data;
    if (evt.data === "528True")
    {
      relais1 = true;
      image1()
//      alert("528True");
    }
    if (evt.data === "528False") 
    {
      relais1 = false;
        image1()
//      alert("528False");
    }

    if (evt.data === "513True") 
    {
      relais2 = true;
      image2()
//      alert("513True");
    }
    if (evt.data === "513False")
    {
      relais2 = false;
      image2()
//      alert("513False");
    }

//    if (evt.data === "514True")
    if (evt.data === "514FeedpumpTrue")
    {
//      relais3 = true;
      feedpump = true;
      image3()
//      alert("514True");
    }
//    if (evt.data === "514False")
    if (evt.data === "514FeedpumpFalse")
    {
      feedpump = false;
//      relais3 = false;
      image3()
//      alert("514False");
    }

    if (evt.data === "1025True")
    {

      if (temp2 === false || button_temp2 === false)

        temp1 = true;
//        button_temp1 = true;

      image4()
//      alert("524True" + button_temp2 + button_temp1);
    }

    if (evt.data === "1026False")
    {

      temp1 = false;
//      button_temp1 = false;
      image4()
//      alert("524False");
    }

    if (evt.data === "1028True")
    {
      if (temp1 === false || button_temp1 === false)

        temp2 = true;
//        button_temp2 = true;

      image5()
//      alert("524True");
    }

    if (evt.data === "1029False")
    {

      temp2 = false;
//      button_temp2 = false;
      image5()
//      alert("524False");
    }

    if (evt.data === "1025True" || evt.data === "1028True")
    {
        heating = true;
        image6()
    }
    if (evt.data === "1026False" || evt.data === "1029False")
    {
        heating = false;
        image6()
    }

  }


   //functions to control the io
  function relais1_on()
  {
    relais1 = true;

      if (relais1 === true)
	  
	    websock.send(JSON.stringify({relais1: 'on'}));

//        websock.send('relais1');
//        alert("button pressd" + relais1);
  }

  function relais2_on()
  {
    relais2 = true;
    //alert("button pressd" + relais2);

      if (relais2 === true)
        websock.send(JSON.stringify({relais2: 'on'}));
//        alert("button pressd" + relais2);
  }

  function relais3_on()
  {
    relais3 = true;
    feedpump = true;

//    alert("button pressd" + relais2);

//      if (relais3 === true)
//        websock.send('relais3');
      if (feedpump === true)
        websock.send(JSON.stringify({relais3: 'on'}));
//        alert("button pressd" + relais3);
  }

  function temp1_on()
  {
    temp1 = true;
    button_temp1 = true;

    if (temp1 === true)
	{
	  websock.send(JSON.stringify({heizen1: 'on'}));
	}
//    temp2 = false;
//    button_temp2 = false;

//    websock.send('temp2off');
  }

  function temp2_on()
  {
    temp2 = true;
    button_temp2 = true;

    temp1 = false;
    button_temp1 = false;
	
    if (temp2 === true)
	{
	  websock.send(JSON.stringify({heizen2: 'on'}));
	}
	
  }



  function relais1_off()
  {
    relais1 = false;

      if (relais1 === false)
	  
	    websock.send(JSON.stringify({relais1: 'off'}));

//        websock.send('relais1off');
//        alert("button pressd" + relais1);
  }

  function relais2_off()
  {
    relais2 = false;

     if (relais2 === false)
       websock.send(JSON.stringify({relais2: 'off'}));
//       alert("button pressd" + relais2);
  }

  function relais3_off()
  {
    relais3 = false;
    feedpump = false;

//      if (relais3 === false)
//        websock.send('relais3off');

      if (feedpump === false)
        websock.send(JSON.stringify({relais3: 'off'}));
//        alert("button pressd" + relais3);
  }

//Temp1 Heizen off
  function temp1_off()
  {
    temp1 = false;
    button_temp1 = false;

      if (temp1 === false)
	  {
	    websock.send(JSON.stringify({heizen1: 'off'}));
	  }

//        websock.send('temp1off');

//      if (button_temp1 === false)
//        websock.send('button_temp1off');
  }

//Temp1 Heizen off
  function temp2_off()
  {
    temp2 = false;
    button_temp2 = false;

      if (temp2 === false)
	  {
	    websock.send(JSON.stringify({heizen2: 'off'}));
	  }
	  
  }



  // Display incoming data
//  function sock_message(evt)
  {
//    display(evt.data);
//    alert("relais1True");
//    return evt.data;
  }

  // Handlers for other Websocket events
  function sock_open(evt)
  {
    display("Connected\n");
  }
  function sock_close(evt)
  {
    display("\nDisconnected\n");
  }
  function sock_error(evt)
  {
    display("Socket error\n");
    websock.close();
  }

  // Do initialisation when page is loaded
  window.addEventListener("load", init, false);

//  function state()
//  {
//  if (relais1 === true)
//  {
//    document.getElementById('on').src="on.png";
//  }else
//  {
//    document.getElementById('off').src="off.png";
//  }
//  }

</script>
<form name="myform">
  </div>
  <img id="BEE"  src="BEE.png" width="40" height="40" align="left">
  <h2>&nbsp Websocket</h2>
  </div>
  <p>
  <textarea name="text" rows="10" cols="60">
  </textarea>
  </p>
  <p>
  <p>
  <input type="button" value="Connect" onClick="connect();">
  <input type="button" value="Disconnect" onClick="disconnect();">

  </p>
  <img id="myImage0" src="RI.png" align="middle" alt="Plant Image" style="position:absolute; top:5%; left:30%; height=”150”; border:double;"  />
  <p>
  <script>
  function image0()
  {

//Relais1 = Cooling
//Relais2 = Gas
//Relais3 = Feed

    var image = document.getElementById("myImage0");

//    if ((button_temp1 === true || button_temp2 === true) && (relais1 === true && relais2 === true))
//    {
//      image.src = "RI_hot5_cool_gas.png";
//    }

    if ((button_temp1 == true || button_temp2 == true) && (relais1 === false && relais2 === false && feedpump === false))
    {
      image.src = "RI_hot3.png";
    }
    else if ((relais2 === true && relais1 === true) && feedpump == false && (button_temp1 == false && button_temp2 == false))
    {
      image.src = "RI_cool_gas.png";
    }
    else if ((relais2 === true && relais1 === false) && feedpump == true && (button_temp1 == false && button_temp2 == false))
    {
      image.src = "RI_feed_gas.png";
    }
    else if ((relais2 === false && relais1 === true) && feedpump == true && (button_temp1 == false && button_temp2 == false))
    {
      image.src = "RI_cool_feed.png";
    }
    else if ((relais2 === true && relais1 === true) && feedpump == true && (button_temp1 == false && button_temp2 == false))
    {
      image.src = "RI_cool_feed_gas.png";
    }
    else if ((button_temp1 == true || button_temp2 == true) && (relais1 === true && relais2 === true && feedpump === false))
    {
      image.src = "RI_hot5_cool_gas.png";
    }
    else if ((button_temp1 == true || button_temp2 == true) && (relais1 === true && relais2 === true && feedpump === true))
    {
      image.src = "RI_hot5_feed_cool_gas.png";
    }
    else if ((button_temp1 === true || button_temp2 === true) && (relais1 === true && feedpump === true) && relais2 === false)
    {
      image.src = "RI_hot5_feed_cool.png";
    }
    else if ((button_temp1 === true || button_temp2 === true) && (relais1 === false && feedpump === true) && relais2 === true)
    {
      image.src = "RI_hot5_feed_gas.png";
    }
    else if ((button_temp1 === true || button_temp2 === true) && relais1 === true)
    {
      image.src = "RI_hot5_cool.png";
    }
    else if ((button_temp1 === true || button_temp2 === true) && relais2 === true)
    {
      image.src = "RI_hot5_gas.png";
    }
    else if ((button_temp1 === true || button_temp2 === true) && feedpump === true)
    {
      image.src = "RI_hot5_feed.png";
    }
    else if (relais1 === true)
    {
      image.src = "RI_cool.png";
    }
    else if (relais2 === true)
    {
      image.src = "RI_gas.png";
    }
    else if (feedpump === true)
    {
      image.src = "RI_feed.png";
    }
    else
    {
      image.src = "RI.png";
    }


  }

  </script>
  


  <input type="button" value="Cooling System on" onClick="relais1_on();">
  <img id="myImage1"  src="off.png" width="20" height="20"> 
  <script>
  function image1()
  {
    var image = document.getElementById("myImage1");

    if (relais1 === true) 
    {
      image.src = "on.png";
    }else 
    {
      image.src = "off.png";
    }   
  }
  </script>

  </p>
  <input type="button" value="Cooling System off" onClick="relais1_off();">
  </p>

  <input type="button" value="Gas Valve on" onClick="relais2_on();">
  <img id="myImage2"  src="off.png" width="20" height="20">
  <script>
  function image2()
  {
    var image = document.getElementById("myImage2");

    if (relais2 === true)
    {
      image.src = "on.png";
    }else
    {
      image.src = "off.png";
    }
  }
  </script>

  </p>
  <input type="button" value="Gas Valve off" onClick="relais2_off();">
  </p>

  <input type="button" value="Feed Pump on" onClick="relais3_on();">
  <img id="myImage3"  src="off.png" width="20" height="20">
  <script>
  function image3()
  {
    var image = document.getElementById("myImage3");

    if (feedpump === true)
    {
      image.src = "on.png";
    }else
    {
      image.src = "off.png";
    }
  }
  </script>

  </p>
  <input type="button" value="Feed Pump off" onClick="relais3_off();">
  </p>

  </p>


<!--  <p></p>


  <h2>Temperature Controll</h2>
  <label for="Temp">Temperatur:</label>
  </p>
  <input type="range" id="temp_input" value="50" min="1" max="500">
  <output id="output" for="temp_input">50</output>


  <script>

  function ausgeben(ev)
  {
      document.getElementById('output').value = ev.target.value;
  }

  document.getElementById('temp_input').addEventListener('input', ausgeben);

  </script>
//-->


<!--  <p></p>-->


  <h2>Temperature Controll</h2>

  <div class="slidecontainer">
    <input autocomplete="off" type="range" min="0" max="500" value="0" class="slider" id="temp_input">
    <p>Temp_Value: <span id="temp_output"></span></p>
  </div>

  <script>
    var slider = document.getElementById("temp_input");
    var output = document.getElementById("temp_output");

    output.innerHTML = slider.value;

    slider.oninput = function()
    {
      output.innerHTML = this.value;
      //tempinput = slider;
    }

    slider.addEventListener("input", function(e)
    {
    tempinput = slider.value;
    });

  </script>


  <script>
  </script>


<!--  <p></p>-->


 </p>

  </p>
  <h2>Heating Status</h2>
  
  <img id="myImage6"  src="red-off.png" width="20" height="20">
  <script>
  function image6()
  {
    var image = document.getElementById("myImage6");

    if (button_temp2 === true || button_temp1 === true)
//    if (temp1 === true || temp2 === true)
    {
      image.src = "red-on.png";
    }else
    {
      image.src = "red-off.png";
    }
  }
  </script>


  </p>
  <h2>Heating Controll</h2>
  <!--  <p>Heat 350 °C</p>-->
  <input type="button" value="temp1_on" onClick="temp1_on();">

  <img id="myImage4"  src="off.png" width="20" height="20">
  <script>
  function image4()
  {
    var image = document.getElementById("myImage4");

    if (temp1 === true)
//    if (button_temp1 === true && temp1 === true)
//    if (button_temp1 === true)
    {
      image.src = "on.png";
    }else
    {
      image.src = "off.png";
    }
  }
  </script>

  </p>
  <input type="button" value="temp1_off" onClick="temp1_off();">
  </p>
 

<!--
  </p>
  <p>Heat 400 °C</p>
  <input type="button" value="temp2_on" onClick="temp2_on();">

  <img id="myImage5"  src="off.png" width="20" height="20">
  <script>
  function image5()
  {
    var image = document.getElementById("myImage5");

    if (temp2 === true)
//    if (button_temp2 === true && temp2 === true)
//    if (button_temp2 === true)
    {
      image.src = "on.png";
    }else
    {
      image.src = "off.png";
    }
  }
  </script>

  </p>
  <input type="button" value="temp2_off" onClick="temp2_off();">
  </p>
//-->



  </p>
  </p>

  <script>
  function output_temp1(temperatur1)
  {
//    var temperatur1;
    document.getElementById('temper1').innerHTML = temperatur1;
//    display(temperatur1);
  }
  </script>
  <p> Temperatur Reaktor: <span id="temper1"></span>.</p> 

  </p>

  </p>
  </p>

  <script>
  function output_temp2(temperatur2)
  {
//    var temperatur1;
    document.getElementById('temper2').innerHTML = temperatur2;
//    display(temperatur1);
  }
  </script>
  <p> Temperatur Water: <span id="temper2"></span>.</p>

  </p>


  
  <script>
  function output_systemp(temperatur8)
  {

    document.getElementById('systemp').innerHTML = temperatur8;

  }
  </script>
  <p> Temperatur System: <span id="systemp"></span>.</p>

  </p>



  </p>

</form>
</html> 
Falls das jemanden Testen will, sind wohl kleine Modifikationen Notwendig. Aber hier wäre noch ein Modbus Server Skript, das als dummy dient, wenn keine Hardware zur Verfügung steht.

modbus_server

Code: Alles auswählen

#!/usr/bin/env python
"""
Pymodbus Synchronous Server Example
--------------------------------------------------------------------------

The synchronous server is implemented in pure python without any third
party libraries (unless you need to use the serial protocols which require
pyserial). This is helpful in constrained or old environments where using
twisted is just not feasible. What follows is an example of its use:
"""
# --------------------------------------------------------------------------- #
# import the various server implementations
# --------------------------------------------------------------------------- #
from pymodbus.version import version
from pymodbus.server.sync import StartTcpServer
from pymodbus.server.sync import StartTlsServer
from pymodbus.server.sync import StartUdpServer
from pymodbus.server.sync import StartSerialServer

from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock, ModbusSparseDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext

from pymodbus.transaction import ModbusRtuFramer, ModbusBinaryFramer
# --------------------------------------------------------------------------- #
# configure the service logging
# --------------------------------------------------------------------------- #
import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s'
          ' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)


def run_server():
    # ----------------------------------------------------------------------- #
    # initialize your data store
    # ----------------------------------------------------------------------- #
    # The datastores only respond to the addresses that they are initialized to
    # Therefore, if you initialize a DataBlock to addresses of 0x00 to 0xFF, a
    # request to 0x100 will respond with an invalid address exception. This is
    # because many devices exhibit this kind of behavior (but not all)::
    #
    #     block = ModbusSequentialDataBlock(0x00, [0]*0xff)
    #
    # Continuing, you can choose to use a sequential or a sparse DataBlock in
    # your data context.  The difference is that the sequential has no gaps in
    # the data while the sparse can. Once again, there are devices that exhibit
    # both forms of behavior::
    #
    #block = ModbusSparseDataBlock({0x00: 0, 0x05: 1})
    block = ModbusSequentialDataBlock(0x00, [0]*5)
    #
    # Alternately, you can use the factory methods to initialize the DataBlocks
    # or simply do not pass them to have them initialized to 0x00 on the full
    # address range::
    #
    #store = ModbusSlaveContext(di = ModbusSequentialDataBlock.create())
    #store = ModbusSlaveContext()
    #
    # Finally, you are allowed to use the same DataBlock reference for every
    # table or you may use a separate DataBlock for each table.
    # This depends if you would like functions to be able to access and modify
    # the same data or not::
    #
    #    block = ModbusSequentialDataBlock(0x00, [0]*0xff)
    #    store = ModbusSlaveContext(di=block, co=block, hr=block, ir=block)
    #
    # The server then makes use of a server context that allows the server to
    # respond with different slave contexts for different unit ids. By default
    # it will return the same context for every unit id supplied (broadcast
    # mode).
    # However, this can be overloaded by setting the single flag to False and
    # then supplying a dictionary of unit id to context mapping::
    #
    #     slaves  = {
    #         0x01: ModbusSlaveContext(...),
    #         0x02: ModbusSlaveContext(...),
    #         0x03: ModbusSlaveContext(...),
    #     }
    #     context = ModbusServerContext(slaves=slaves, single=False)
    #
    # The slave context can also be initialized in zero_mode which means that a
    # request to address(0-7) will map to the address (0-7). The default is
    # False which is based on section 4.4 of the specification, so address(0-7)
    # will map to (1-8)::
    #
    #     store = ModbusSlaveContext(..., zero_mode=True)
    # ----------------------------------------------------------------------- #
    store = ModbusSlaveContext(

#        co=ModbusSequentialDataBlock(0, [0, 650]),
#        hr=ModbusSequentialDataBlock(0, [0, 350]),

        di=ModbusSequentialDataBlock(0, [0, 350]),
        ir=ModbusSequentialDataBlock(0, [0, 650]), zero_mode=True)


    context = ModbusServerContext(slaves=store, single=True)

    # ----------------------------------------------------------------------- #
    # initialize the server information
    # ----------------------------------------------------------------------- #
    # If you don't set this or any fields, they are defaulted to empty strings.
    # ----------------------------------------------------------------------- #
    identity = ModbusDeviceIdentification()
    identity.VendorName = 'Pymodbus'
    identity.ProductCode = 'PM'
    identity.VendorUrl = 'http://github.com/riptideio/pymodbus/'
    identity.ProductName = 'Pymodbus Server'
    identity.ModelName = 'Pymodbus Server'
    identity.MajorMinorRevision = version.short()

    # ----------------------------------------------------------------------- #
    # run the server you want
    # ----------------------------------------------------------------------- #
    # Tcp:
    StartTcpServer(context, identity=identity, address=("", 5020))
    #
    # TCP with different framer
    # StartTcpServer(context, identity=identity,
    #                framer=ModbusRtuFramer, address=("0.0.0.0", 5020))

    # TLS
    # StartTlsServer(context, identity=identity, certfile="server.crt",
    #                keyfile="server.key", address=("0.0.0.0", 8020))

    # Udp:
    # StartUdpServer(context, identity=identity, address=("0.0.0.0", 5020))

    # socat -d -d PTY,link=/tmp/ptyp0,raw,echo=0,ispeed=9600 PTY,link=/tmp/ttyp0,raw,echo=0,ospeed=9600
    # Ascii:
    # StartSerialServer(context, identity=identity,
    #                    port='/dev/ttyp0', timeout=1)

    # RTU:
    # StartSerialServer(context, framer=ModbusRtuFramer, identity=identity,
    #                   port='/tmp/ttyp0', timeout=.005, baudrate=9600)

    # Binary
    # StartSerialServer(context,
    #                   identity=identity,
    #                   framer=ModbusBinaryFramer,
    #                   port='/dev/ttyp0',
    #                   timeout=1)


if __name__ == "__main__":
    run_server()
Soulpilot
User
Beiträge: 63
Registriert: Sonntag 24. April 2022, 12:19

Ich glaube, das Problem gelöst zu haben.

Hier ein Ausschnitt von meiner Lösung, ich starte für den While Loop über eine Funktion, die einfach mittels eines neuen Thread aufgerufen wird. Der Websocket nimmt noch I/O entgegen und sendet weiter Daten, während die While Schleife ausgeführt wird. "Yeah" genau das was ich wollte... Ich hänge den Code mal an, falls es doch noch jemanden interessiert. Eure Meinung dazu wäre mir aber dennoch wichtig, vielleicht kann dazu jemand seine Meinung abgeben? =)

Code: Alles auswählen

    def set_temperature1(self, address, value):
        print("set_temperature1 was triggerd")
        t2 = threading.Thread(target=self.temp_controll, args=(address, value)) #, daemon= True)
        t2.start()
        for thread in threading.enumerate(): 
            print(thread.name)


    def temp_controll(self, address, value):
        #while True:
        #    print("thread 2 = ok")

        # Loop for temperature controll
        print("database_reading")
        print(self.database_reading()[1][0])

        if (self.database_reading()[0][1]) == 524 and (self.database_reading()[1][1]) == 'on':
            print("DB-Reading from set_temperature1 'heizen1': ", self.database_reading()[1][1])

            while True:

                print("Loop!!!!!!!")

                rp = self.modbus.read_coils(address, unit=UNIT)
                print(rp.bits[0])
                time.sleep(0.01)

                if rp.bits[0] == False:
                    rq = self.modbus.write_coil(address, value == "on", unit=UNIT)
                    time.sleep(0.01)
                    print("Heating is active!!!!")

                print(address)
                time.sleep(0.01)

                print(type(self.read_temperature( address == 2, 'get')))
                print(type(value))

                if float(self.read_temperature( address == 2, 'get')) >= float(value):
                    print("Heating is on!!!!!!!")
                    time.sleep(0.01)

                    print(address)
                    rq = self.modbus.write_coil(address, value == "off", unit=UNIT)
                    time.sleep(0.01)

                    print("Heating Temperture is achieved! -> deactivate heating")

                if (self.database_reading()[0][1]) == 524 and (self.database_reading()[1][1]) == 'off':
                    break
Gestartet wird der Server auf Thread #1-

Code: Alles auswählen

        server = SimpleWebSocketServer('', PORTNUM, partial(Echo, client))
        t1 = threading.Thread(target=server.serveforever)
#        t.setDaemon(True)
        t1.start()
        for thread in threading.enumerate(): 
            print(thread.name)
Was denkt Ihr, ist die Vorgehensweise gut, oder gibt es noch bessere Möglichkeiten. @Sirius3 würde mich mal interessieren ob es noch effizientere Lösungen zu dem Problem gäbe, vielleicht hast du ne Minute xD

Also was auch noch interessant zu wissen wäre, muss ich hier beim Threading irgendetwas Besonderes beachten, also kann das zu irgendwelche Problemen führen? Was weiß ich Bufferoverflow oder ähnliches?

Grüße
Soulpilot
User
Beiträge: 63
Registriert: Sonntag 24. April 2022, 12:19

Moin

Ok, nach einigen Tests habe ich festgestellt, dass man aus einem Thread keinen Thread starten sollte. Allerdings, auch wenn ich nur die While Schleife in einen separaten Thread starten, kommt es schon nach kurzer Zeit zu komischen Nebenwirkungen, also das läuft dann nicht mal mehr annähernd synchron. (Synchon muss es auch nicht sein aber so extrem asynchron macht dann auch keinen Spaß *lol*)

Was kann man da machen, also gibt es fürs threading Einstellungen bzw. Parameter, mit welchen ein etwas synchroner Programmverlauf möglich ist? Ansonsten schaue ich mir mal Multiprocessing an aber das ist was ich gelesen habe, noch komplizierter, wegen der Speicherbereiche etc...

Würde mich über ein paar Tipps freuen.

Grüße
Soulpilot
User
Beiträge: 63
Registriert: Sonntag 24. April 2022, 12:19

Moin,

ok ich habe eine Lösung gefunden, mit der sich bei jeder neuen Verbindung ein neuer Thread für die While Schleife spawned und beim disconnecten wieder gestoppt wird.

Laut meiner Recherche sollte Threading für I/O in Python passabel sein, auch meine Tests mit dieser Implementierung, unter anderem habe ich mit verschiedenen Rechnern gleichzeitig zugegriffen und es lief besser als erwartet, auch wenn dann pro Verbindung (und das verstehe ich nicht so ganz) zwei Threads gestartet werden. Das muss ich mir noch mal genauer anschauen.

Ich werde auch mal testen, ob ich die Schleife außerhalb der Klasse mit einem neuen Thread gestartet bekomme und wie sich das auf die Bedienbarkeit und Performance auswirkt. Normalerweise (was ich so bisher gelesen habe) sollte I/O besser mit asyncio implementiert werden, da dabei die Ein- und Ausgabe besser getaktet werden kann, vor allem wenn von mehreren Parteien zugegriffen wird. Außerdem will ich einige Schlampereien mit den Datenbank Handling Code technisch noch optimieren, irgendwie passen da die Formate bzw. Datentypen nicht so ganz, selbst wenn ich die DB auslese und direkt ins json dumpe kommt etwas anderes heraus, als ich erwarten würde, weswegen ich das "unschön" händisch mache.


Vielleicht klingt sich hier doch noch jemand mit ein? Ansonsten ist das hier vielleicht doch hilfreich für den einen oder anderen, falls jemand auf ähnliche Probleme trifft, auch wenn mein Code natürlich nicht der Beste ist, aber mein Lernprozess ist lange noch nicht abgeschlossen :lol:


Hier eine vereinfachte Version, wie das umgesetzt wurde.

Code: Alles auswählen

import threading
from threading import Thread

from SimpleWebSocketServer import WebSocket, SimpleWebSocketServer
logger = logging.getLogger(__name__)

wss = []
PORTNUM = 8001

class Echo(WebSocket):

    def __init__(self, state):
        threading.Thread.__init__(self)
        self.thread_stop = False

    def controll(self, state):
        # Loop
        while not self.thread_stop:
            self.do_something(state)
            time.sleep(5)

    def do_something(self, state)
        for ws in wss:
            ws.sendMessage(sendMessage(calculatation_something(state) #Broadcast of messane

    def handleMessage(self):
        if self.data is None:
            self.data = ''

         data = (self.data)
         func(data)
         print("Echoing check received data: '%s'" % state)

    def handleConnected(self):
        self.thread_stop = False
        print(self.address, 'connected')
        if self not in wss:
            wss.append(self)
            t2 = threading.Thread(target=self.controll, args=('on'))
            t2.start()
            for thread in threading.enumerate():
                print(thread.name)
        print("Connected")

    def handleClose(self):
        self.thread_stop = True
        print('thread stop')
        wss.remove(self)
        print(self.address, 'closed')
        print("Disconnected")

      
def main():
    print("Websocket server on port %s" % PORTNUM)
    server = SimpleWebSocketServer('', PORTNUM, Echo)
    try:
        server.serveforever()
    finally:
        server.close()
if __name__ == "__main__":
    main()`
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Schon eine Klasse zu haben, aber dann eine globale Variable zur Verwaltung der eingegangenen Verbindungen zu benutzen, ist schon etwas sehr speziell. Und du startest nicht zwei Threads, sondern machst was ganz anderes falsch: statt einen Thread pro Socket zu haben (was IMHO ueberfluessig ist, weil du ja gar nix mit denen machst), schickst du einen Thread pro Verbindung in dein "controll" (schreibt man wenn mit einem l), um dann in do_something aber einfach immer ueber alle Sockets zu laufen. Statt zweimal soviel also quadratisch so viel Nachrichten. Das ist natuerlich grober Unfug.
Soulpilot
User
Beiträge: 63
Registriert: Sonntag 24. April 2022, 12:19

__deets__ hat geschrieben: Montag 7. November 2022, 15:22 Schon eine Klasse zu haben, aber dann eine globale Variable zur Verwaltung der eingegangenen Verbindungen zu benutzen, ist schon etwas sehr speziell. Und du startest nicht zwei Threads, sondern machst was ganz anderes falsch: statt einen Thread pro Socket zu haben (was IMHO ueberfluessig ist, weil du ja gar nix mit denen machst), schickst du einen Thread pro Verbindung in dein "controll" (schreibt man wenn mit einem l), um dann in do_something aber einfach immer ueber alle Sockets zu laufen. Statt zweimal soviel also quadratisch so viel Nachrichten. Das ist natuerlich grober Unfug.
Hi,

danke vielmals, ja da hast du recht. Wie sollte ich das mit der While Schleife am besten machen? Wäre es möglich, die While schleife in einem Thread außerhalb der Klasse zu starten? Das Problem ist nur, dass von der While Schleife auf Memberfunktionen der Klasse zugegriffen wird. Wenn es sinnvoll und machbar ist, den While Loop von außerhalb aufzurufen, könntest du mir bitte zeigen, wie das geht? Ich bin auch für alternative Lösungsvorschläge offen... ich würde nur gerne verstehen wie man das am besten macht, das man parallel noch etwas laufen lassen kann^^
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wieso sollte denn die Lösung zu dem von mir geschilderten Problemen eine while-Schleife außerhalb deiner Klasse sein? Das hat damit doch keinerlei Zusammenhang.

Was die Frage nach dem richtig machen angeht - ich weiß gar nicht, was du machen willst. Also auch nicht, wie das richtig geht. Eine while-Schleife ist ja nun kein Selbstzweck.
Soulpilot
User
Beiträge: 63
Registriert: Sonntag 24. April 2022, 12:19

__deets__ hat geschrieben: Montag 7. November 2022, 22:12 Wieso sollte denn die Lösung zu dem von mir geschilderten Problemen eine while-Schleife außerhalb deiner Klasse sein? Das hat damit doch keinerlei Zusammenhang.

Was die Frage nach dem richtig machen angeht - ich weiß gar nicht, was du machen willst. Also auch nicht, wie das richtig geht. Eine while-Schleife ist ja nun kein Selbstzweck.
Die While Schleife soll schon einen Zweck erfüllen. In meinem Fall eine permanente Kontrolle, ob die Soll-Temperatur erreicht wurde, wenn ja, wird das entsprechende Relais abgeschaltet. Darüber hinaus wollte ich noch weitere ähnliche kontinuierliche Programmroutinen einbauen.

Ah, ich meinte nicht die while schleife außerhalb der Klasse, sondern die While schleife mit dem Programmstart direkt zu starten, also von außerhalb der Klasse im Main Teil per Thread aufrufen.

Ok einerseits werden zwar von der Klasse Methoden verwendet, aber die Temperatur abfrage über Modbus könnte auch unabhängig von der Klasse gemacht werden. Die Soll-Temperatur wird in eine SQLithe Datenbank geschrieben.

Also so in etwa: (das hat aber nicht funktioniert?)

Code: Alles auswählen

    def handleConnected(self):
        self.thread_stop = False
        print(self.address, 'connected')
        if self not in wss:
            wss.append(self)
#            t2 = threading.Thread(target=self.controll, args=('on'))
#            t2.start()
#            for thread in threading.enumerate():
#                print(thread.name)
        print("Connected")

    def handleClose(self):
        self.thread_stop = True
        print('thread stop')
        wss.remove(self)
        print(self.address, 'closed')
        print("Disconnected")

      
def main():
    print("Websocket server on port %s" % PORTNUM)

    t2 = threading.Thread(target=Echo.controll, args=('on'))
    t2.start()

    server = SimpleWebSocketServer('', PORTNUM, Echo)
    try:
        server.serveforever()
    finally:
        server.close()
if __name__ == "__main__":
    main()`
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich habe gestern deine Frage nur im Kontext des letzten Posts verstanden. Hätte weiter oben lesen sollen.

Warum muss diese Heizungssteuerung in den Echo-Service gehämmert werden? Warum kann das keine eigene, halbwegs saubere Klasse sein?
Soulpilot
User
Beiträge: 63
Registriert: Sonntag 24. April 2022, 12:19

__deets__ hat geschrieben: Dienstag 8. November 2022, 10:19 Ich habe gestern deine Frage nur im Kontext des letzten Posts verstanden. Hätte weiter oben lesen sollen.

Warum muss diese Heizungssteuerung in den Echo-Service gehämmert werden? Warum kann das keine eigene, halbwegs saubere Klasse sein?
Das hatte ich mir auch schon überlegt, allerdings komme ich dann auch nicht um eine While Schleife herum. Also die Klasse wäre ja relativ schnelle gebastelt, aber wie geht es dann weiter? Da benötige ich doch auch ein kontinuierlich laufende Routine? Wenn du mir da bitte Verständnis technisch auf die Sprünge helfen könntest?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Entweder threading, oder timer events braucht es, ja. Das problem hier und jetzt ist, das du alles ineinander quirlst. Und es damit für dich, und jeden anderen, völlig unnachvollziehbar machst. Darum eben der Vorschlag, eine Klasse zur Temperaturregelung zu bauen, die dieses eine Problem löst. Und die einfach zu benutzen.
Soulpilot
User
Beiträge: 63
Registriert: Sonntag 24. April 2022, 12:19

__deets__ hat geschrieben: Dienstag 8. November 2022, 14:35 Entweder threading, oder timer events braucht es, ja. Das problem hier und jetzt ist, das du alles ineinander quirlst. Und es damit für dich, und jeden anderen, völlig unnachvollziehbar machst. Darum eben der Vorschlag, eine Klasse zur Temperaturregelung zu bauen, die dieses eine Problem löst. Und die einfach zu benutzen.

Vielen Dank! Das ist eine gute Idee und ich werde das in den nächsten Tagen versuchen zu realisieren und mich noch mal melden.

Meinst Du, ich kann die Klasse so starten: (stark vereinfachter Code)

Code: Alles auswählen


class Temperture_Control

    def __init__(self):
        super(self).__init__()
  
    def control
        while True:
            #self.do_somthing() 


def main():

        t2 = threading.Thread(target=Temperture_Control, args=('value'))
        t2.start()


        server = SimpleWebSocketServer('', PORTNUM, partial(Echo, client))
        try:
            server.serveforever()
        finally:
            server.close()

Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Soulpilot: Nein. Alleine die Formulierung „eine Klasse starten“ ist schon problematisch. Klassen sind ja nichts was ”läuft”. Das tun Funktionen und Methoden.

Das `self` als Argument beim Aufruf von `super()` ist schräg. Das ist nämlich weder Python 3 (gar kein Argument) noch Python 2, wo man zwei Argumente braucht.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Soulpilot
User
Beiträge: 63
Registriert: Sonntag 24. April 2022, 12:19

__blackjack__ hat geschrieben: Dienstag 8. November 2022, 16:36 @Soulpilot: Nein. Alleine die Formulierung „eine Klasse starten“ ist schon problematisch. Klassen sind ja nichts was ”läuft”. Das tun Funktionen und Methoden.

Das `self` als Argument beim Aufruf von `super()` ist schräg. Das ist nämlich weder Python 3 (gar kein Argument) noch Python 2, wo man zwei Argumente braucht.
Danke für den Hinweis!
Soulpilot
User
Beiträge: 63
Registriert: Sonntag 24. April 2022, 12:19

Vollständigkeitshalber will ich mein Projektfortschritt hier posten. So habe ich die Instanziierung einer neuen Klasse und den Aufruf der Methode für die While Schleife implementiert, scheint jedenfalls so zu laufen wie ich es mir vorgestellt habe. (Auch wenn's eine schwere Geburt war)^^

Der Websocket wird dabei über thread 1 gestarten und die Klasse Echo instanziiert, die Klasse Temperature_Control wird separat instanziiert und die Methode control über einen neuen Thread 2 ausgeführt. Die While Schleife führt nun temp_control innerhalb der Echo Klasse aus. Hat ziemlich lange gedauert und auch nur mit etwas Unterstützung geklappt, bis ich kapiert habe, welche Klasse eigentlich von welcher erbt und wie man Klassen überhaupt richtig instanziiert. Auch wird bei Thread 2 eine lambda Funktion benötigt, sonst wird die variable client blockiert und t1 wird nicht ausgeführt.

Wahnsinns Lernprozess, die Erkenntnis kommt erst, wenn man es tatsächlich versucht, das Programm zu implementieren. Jetzt bin ich in der Lage, Prozesse und Routinen serverseitig auszuführen und diese auch direkt zentral zu überwachen, auch wenn kein Client mit dem Server verbunden ist, das bringt gewisse Vorteile mit sich. Mittels Broadcast Messages können an alle Clients Informationen gesendet werden, das könnte Traffic einsparen, wenn nicht jede Information gepullt werden muss. Der code siehe weiter unten.


Ich habe allerdings noch die eine oder andere Frage und würde mich über eine einigermaßen detaillierte Beantwortung freuen, da ich die Software noch weiter entwickeln und auch noch verbessern möchte. Danke im Voraus!

1. Was denkt Ihr von meiner Implementierung, also ist das so eine gute Basis oder ginge das noch effizienter oder besser?

2. Was ist eigentlich eine Schnittstelle und wie könnte man theoretisch einen Prozess auslagern? Könnte dafür Websocket verwendet werden oder gibt es noch andere Möglichkeiten, damit Programme miteinander kommunizieren können. Also was gibt es da konkret für Möglichkeiten und welche könntet Ihr mir empfehlen für mein Projekt?

3. Wäre es möglich Daten wie Temperatur zu visualisieren, über Graphen. Sollte das direkt Clientseitig implementiert werden oder könnte der Server die Daten aufbereiten, zum beispiel in einer SQL-Datenbank? Wie könnte man das relativ einfach umsetzen, aber doch professionell?

4. Wofür werden eigentlich generell Sessions benötigt?

5. Wäre asyncio ebenfalls notwendig irgendwann, also wenn das Programm noch erweitert wird, um zum Beispiel die I/O effizienter zu machen? Jedenfalls konnte ich in den Tests, die ich durchgeführt habe, keine Blockaden erkennen. Also dass wenn relativ gleichzeitig Eingaben getätigt wurden, hat es zu keinen Fehlern oder sonst was geführt, Zufall?



Hier der Code Ausschnitt, noch minimalistisch gehalten.

Code: Alles auswählen


class Echo(WebSocket):

    def __init__(self, client, server, sock, address):
        super().__init__(server, sock, address)
        threading.Thread.__init__(self)
        self.thread_stop = False
        self.modbus = client
        self.functions = {
                "relais1": partial(self.simple_switch, COILBASE + 16)
            }

    def simple_switch(self, address, value):
        do_somthing(address, value)

    def temp_control(self, address, value):
        #self.database_reading()[0][1]
        for ws in wss:
        ws.sendMessage(sendMessage(calculatation_something(state) #Broadcast message


class Temperature_Control(Echo):

    def __init__(self, client):
        self.modbus = client

    def control(self, value):
        while True:
            self.temp_control(524, 'get')
            time.sleep(1)

def main():
    with ModbusClient(host=HOST, port=PORT) as client:
        client.connect()
        time.sleep(0.01)

        print("Websocket server on port %s" % PORTNUM)
        server = SimpleWebSocketServer('', PORTNUM, partial(Echo, client))

        control = Temperature_Control(client)
        t2 = threading.Thread(target=lambda:control.control('get')) #value = get
        t2.start()

        try:
            t1 = threading.Thread(target=server.serveforever())
            t1.start()
        finally:
            server.close()

if __name__ == "__main__":
    main()

Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Soulpilot: Du hast Klassen nicht verstanden. Das `Temperature_Control` von `Echo` erbt ist vollkommen verquer. In der `Echo.__init__()` ist der `threading.Thread.__init__()`-Aufruf unsinnig, und in `Temperature_Control` wird in der `__init__()` die `__init__()` der Basisklasse nicht aufgerufen, was falsch ist, weil dort ja was passieren würde, was so jetzt nicht passiert.

``lambda`` braucht man so bei `Thread` nicht, weil `Thread` selbst schon einen Weg bereit stellt Argumente für `target` mitzugeben.

`t1` macht so keinen Sinn weil da gar kein Thread gestartet wird, beziehungsweise erst wenn der `serveforever()`-Aufruf zurück kehrt, und dessen *Rückgabewert* ist sicher kein passender Wert für `target`, womit `t1` keinen Unterschied dazu macht einfach `serveforever()` aufzurufen. Ein Thread würde an der Stelle ja auch gar keinen Sinn machen. Was würde in dem Fall denn dann der Hauptthread machen?

Du solltest Dir bei `Thread` auch mal das `daemon`-Argument anschauen und was es für Auswirkungen hat das (nicht) zu setzen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Diese Klasse macht genau nicht, was ich angeregt habe - nur ein Ding, die Temperatur zu kontrollieren.

Stattdessen ist das wieder ein riesen Verhau von vermischten Dingen - thread starten. Websocket bedienen. Modbus ebenfalls.

Ich würde wirklich nach einem anderen Framework suchen. NodeRed vielleicht. Das solche Abstraktionen wie quasi-Parallele Ausführung und messages zwischen Komponenten schon mitbringt. Das alles selbst zu bauen ist zu ambitioniert.
Soulpilot
User
Beiträge: 63
Registriert: Sonntag 24. April 2022, 12:19

__blackjack__ hat geschrieben: Montag 21. November 2022, 05:44 @Soulpilot: Du hast Klassen nicht verstanden. Das `Temperature_Control` von `Echo` erbt ist vollkommen verquer.
Erkläre es mir bitte was du genau damit meinst?
__blackjack__ hat geschrieben: Montag 21. November 2022, 05:44 In der `Echo.__init__()` ist der `threading.Thread.__init__()`-Aufruf unsinnig, und in `Temperature_Control` wird in der `__init__()` die `__init__()` der Basisklasse nicht aufgerufen, was falsch ist, weil dort ja was passieren würde, was so jetzt nicht passiert.
Das threading.Thread.__init__() ist noch von der vorherigen Version, kann aber herausgemacht werden, stimmt.
__blackjack__ hat geschrieben: Montag 21. November 2022, 05:44 ``lambda`` braucht man so bei `Thread` nicht, weil `Thread` selbst schon einen Weg bereit stellt Argumente für `target` mitzugeben.
Mh, das Lambda steht dort, damit client nicht blockiert wird. Der aufruf funktioniert auch ohne lambda ja nur kann dann client nicht für websocket verwendet werden.
Soulpilot
User
Beiträge: 63
Registriert: Sonntag 24. April 2022, 12:19

__deets__ hat geschrieben: Montag 21. November 2022, 09:42 Diese Klasse macht genau nicht, was ich angeregt habe - nur ein Ding, die Temperatur zu kontrollieren.

Stattdessen ist das wieder ein riesen Verhau von vermischten Dingen - thread starten. Websocket bedienen. Modbus ebenfalls.
Das kann sie aber, ich habe es nur testweise so gemacht, weil def temp_controll noch oben in class Echo stand und ich sehen wollte, ob das so überhaupt läuft, aber du hast recht, ich habe vor die Steuerung komplett dort einzubauen. ;-) Für die Temperatursteuerung brauche ich aber Modbus, wie soll den sonst ausgelesen werden oder das Relais geschaltet werden?

Wie hättest du es den gemacht, ein minimalbespiel könnte mir helfen, ich bin froh über ernstzunehmende Krititik, aber sie sollte schon auch konstruktiv für Beginner sein.

Ich verstehe noch nicht genau, aber es wäre schöner, wenn das alles voneinander getrennt wäre. Ich weiß nur noch nicht, wie man das trennen könnte?
Soulpilot
User
Beiträge: 63
Registriert: Sonntag 24. April 2022, 12:19

__blackjack__ hat geschrieben: Montag 21. November 2022, 05:44 `t1` macht so keinen Sinn weil da gar kein Thread gestartet wird, beziehungsweise erst wenn der `serveforever()`-Aufruf zurück kehrt, und dessen *Rückgabewert* ist sicher kein passender Wert für `target`, womit `t1` keinen Unterschied dazu macht einfach `serveforever()` aufzurufen. Ein Thread würde an der Stelle ja auch gar keinen Sinn machen. Was würde in dem Fall denn dann der Hauptthread machen?
Das ist richtig, denke ich, das ist eh schon der MainTread der dann noch mal in einem neuen gestartet wird... Danke für den Hinweis!
Antworten