Esempio n. 1
0
class Database:
    def __init__(self, dbfilename):
        try:
            self.name = dbfilename
            self.dbsemaphore = QSemaphore(
                1)  # to control concurrent write access to db
            self.engine = create_engine('sqlite:///{dbFileName}'.format(
                dbFileName=dbfilename))  #, poolclass=SingletonThreadPool)
            self.session = scoped_session(sessionmaker())
            self.session.configure(bind=self.engine, autoflush=False)
            self.metadata = Base.metadata
            self.metadata.create_all(self.engine)
            self.metadata.echo = True
            self.metadata.bind = self.engine
        except Exception as e:
            log.info('Could not create database. Please try again.')
            log.info(e)

    def openDB(self, dbfilename):
        try:
            self.name = dbfilename
            self.dbsemaphore = QSemaphore(
                1)  # to control concurrent write access to db
            self.engine = create_engine('sqlite:///{dbFileName}'.format(
                dbFileName=dbfilename))  #, poolclass=SingletonThreadPool)
            self.session = scoped_session(sessionmaker())
            self.session.configure(bind=self.engine, autoflush=False)
            self.metadata = Base.metadata
            self.metadata.create_all(self.engine)
            self.metadata.echo = True
            self.metadata.bind = self.engine
        except:
            log.info('Could not open database file. Is the file corrupted?')

    def commit(self):
        self.dbsemaphore.acquire()
        log.info("DB lock aquired")
        try:
            session = self.session()
            rnd = float(randint(1, 99)) / 100.00
            log.info("Waiting {0}s before commit...".format(str(rnd)))
            time.sleep(rnd)
            session.commit()
        except Exception as e:
            log.error("DB Commit issue")
            log.error(str(e))
            try:
                rnd = float(randint(1, 99)) / 100.00
                time.sleep(rnd)
                log.info("Waiting {0}s before commit...".format(str(rnd)))
                session.commit()
            except Exception as e:
                log.error("DB Commit issue on retry")
                log.error(str(e))
                pass
        self.dbsemaphore.release()
        log.info("DB lock released")
Esempio n. 2
0
 def read(self, *data):
     id = self.__getRandomId()
     sem = QSemaphore(1)
     sem.acquire()
     self.queue.put({'type': 'read', 'data': list(data), 'id': id, 'sem': sem})
     # wait for release the semaphore
     sem.acquire()
     state = self.ids[id]
     del self.ids[id]
     return state
Esempio n. 3
0
class Database:
    def __init__(self, dbfilename):
        from db.database import Base
        self.log = getDbLogger()
        self.base = Base
        try:
            self.establishSqliteConnection(dbfilename)
        except Exception as e:
            self.log.error('Could not create SQLite database. Please try again.')
            self.log.info(e)

    def openDB(self, dbfilename):
        try:
            self.establishSqliteConnection(dbfilename)
        except:
            self.log.error('Could not open SQLite database file. Is the file corrupted?')

    def establishSqliteConnection(self, dbFileName: str):
        self.name = dbFileName
        self.dbsemaphore = QSemaphore(1)  # to control concurrent write access to db
        self.engine = create_engine(
            'sqlite:///{dbFileName}'.format(dbFileName=dbFileName))
        self.session = scoped_session(sessionmaker())
        self.session.configure(bind=self.engine, autoflush=False)
        self.metadata = self.base.metadata
        self.metadata.create_all(self.engine)
        self.metadata.echo = True
        self.metadata.bind = self.engine
        self.log.info(f"Established SQLite connection on file '{dbFileName}'")

    def commit(self):
        self.dbsemaphore.acquire()
        self.log.debug("DB lock acquired")
        try:
            session = self.session()
            rnd = float(randint(1, 99)) / 100.00
            self.log.debug("Waiting {0}s before commit...".format(str(rnd)))
            time.sleep(rnd)
            session.commit()
        except Exception as e:
            self.log.error("DB Commit issue")
            self.log.error(str(e))
            try:
                rnd = float(randint(1, 99)) / 100.00
                time.sleep(rnd)
                self.log.debug("Waiting {0}s before commit...".format(str(rnd)))
                session.commit()
            except Exception as e:
                self.log.error("DB Commit issue on retry")
                self.log.error(str(e))
                pass
        self.dbsemaphore.release()
        self.log.debug("DB lock released")
Esempio n. 4
0
 def delay_method_call(self, *args, **kwargs):
     if self.thread() != QThread.currentThread():
         semaphore = QSemaphore()
     else:
         semaphore = None
     event = QueuedCallEvent(method, (self, ) + args, kwargs, semaphore)
     QCoreApplication.postEvent(self, event)
     if semaphore is None:
         QCoreApplication.sendPostedEvents()
     else:
         # Wait until the other thread's event loop processes the event
         semaphore.acquire()
     return event.result()
Esempio n. 5
0
class Database:
    def __init__(self, dbfilename):

        try:
            self.connect(dbfilename)
            #setup_all()
            #create_all()

        except Exception as e:
            print('[-] Could not create database. Please try again.')
            print(e)

    def openDB(self, dbfilename):

        try:
            self.connect(dbfilename)
            #setup_all()

        except Exception as e:
            print('[-] Could not open database file. Is the file corrupted?')
            print(e)

    def connect(self, dbfilename):
        self.name = dbfilename
        self.dbsemaphore = QSemaphore(
            1)  # to control concurrent write access to db
        self.engine = create_engine('sqlite:///' + dbfilename,
                                    connect_args={"check_same_thread": False})
        self.session = scoped_session(sessionmaker())
        self.session.configure(bind=self.engine, autoflush=False)
        self.metadata = Base.metadata
        self.metadata.create_all(self.engine)
        self.metadata.echo = True
        self.metadata.bind = self.engine

    # this function commits any modified data to the db, ensuring no concurrent write access to the DB (within the same thread)
    # if you code a thread that writes to the DB, make sure you acquire/release at the beginning/end of the thread (see nmap importer)
    def commit(self):
        self.dbsemaphore.acquire()

        try:
            session = self.session
            session.commit()

        except Exception as e:
            print("[-] Could not commit to DB.")
            print(e)

        self.dbsemaphore.release()
Esempio n. 6
0
class s7port(object):
    def __init__(self, aw):
        self.aw = aw

        self.readRetries = 1
        self.channels = 10  # maximal number of S7 channels
        self.host = '127.0.0.1'  # the TCP host
        self.port = 102  # the TCP port
        self.rack = 0  # 0,..,7
        self.slot = 0  # 0,..,31

        self.lastReadResult = 0  # this is set by eventaction following some custom button/slider S/ actions with "read" command

        self.area = [0] * self.channels
        self.db_nr = [1] * self.channels
        self.start = [0] * self.channels
        self.type = [
            0
        ] * self.channels  # type 0 => int, type 1 => float, type 2 => intFloat
        #  type 3 => Bool(0), type 4 => Bool(1), type 5 => Bool(2), type 6 => Bool(3), type 7 => Bool(4), type 8 => Bool(5), type 9 => Bool(6), type 10 => Bool(7)
        self.mode = [
            0
        ] * self.channels  # temp mode is an int here, 0:__,1:C,2:F (this is different than other places)
        self.div = [0] * self.channels

        self.optimizer = True  # if set, values of consecutive register addresses are requested in single requests
        self.fetch_max_blocks = False  # if set, the optimizer fetches only one sequence per area from the minimum to the maximum register ignoring gaps
        # S7 areas associated to dicts associating S7 DB numbers to start registers in use
        # for optimized read of full register segments with single requests
        # this dict is re-computed on each connect() by a call to updateActiveRegisters()
        # NOTE: for registers of type float (32bit = 2x16bit) also the succeeding register is registered here
        self.activeRegisters = {}
        # the readings cache that is filled by requesting sequences of values in blocks
        self.readingsCache = {}

        self.PID_area = 0
        self.PID_db_nr = 0
        self.PID_SV_register = 0
        self.PID_p_register = 0
        self.PID_i_register = 0
        self.PID_d_register = 0
        self.PID_ON_action = ""
        self.PID_OFF_action = ""
        self.PIDmultiplier = 0
        self.SVtype = 0
        self.SVmultiplier = 0

        self.COMsemaphore = QSemaphore(1)

        self.areas = [
            0x81,  # PE
            0x82,  # PA
            0x83,  # MK
            0x1C,  # CT
            0x1D,  # TM
            0x84,  # DB
        ]

        self.plc = None
        self.commError = False  # True after a communication error was detected and not yet cleared by receiving proper data
        self.libLoaded = False

################
# conversion methods copied from s7:util.py

    def get_bool(self, _bytearray, byte_index, bool_index):
        """
        Get the boolean value from location in bytearray
        """
        index_value = 1 << bool_index
        byte_value = _bytearray[byte_index]
        current_value = byte_value & index_value
        return current_value == index_value

    def set_bool(self, _bytearray, byte_index, bool_index, value):
        """
        Set boolean value on location in bytearray
        """
        assert value in [0, 1, True, False]
        current_value = self.get_bool(_bytearray, byte_index, bool_index)
        index_value = 1 << bool_index

        # check if bool already has correct value
        if current_value == value:
            return

        if value:
            # make sure index_v is IN current byte
            _bytearray[byte_index] += index_value
        else:
            # make sure index_v is NOT in current byte
            _bytearray[byte_index] -= index_value

    def set_int(self, _bytearray, byte_index, _int):
        """
        Set value in bytearray to int
        """
        # make sure were dealing with an int
        _int = int(_int)
        _bytes = struct.unpack('2B', struct.pack('>h', _int))
        _bytearray[byte_index:byte_index + 2] = _bytes

    def get_int(self, _bytearray, byte_index):
        """
        Get int value from bytearray.
    
        int are represented in two bytes
        """
        data = _bytearray[byte_index:byte_index + 2]
        data[1] = data[
            1] & 0xFF  # added to fix a conversion problem: see https://github.com/gijzelaerr/python-snap7/issues/101
        value = struct.unpack('>h', struct.pack('2B', *data))[0]
        return value

    def set_real(self, _bytearray, byte_index, real):
        """
        Set Real value
    
        make 4 byte data from real
    
        """
        real = float(real)
        real = struct.pack('>f', real)
        _bytes = struct.unpack('4B', real)
        for i, b in enumerate(_bytes):
            _bytearray[byte_index + i] = b

    def get_real(self, _bytearray, byte_index):
        """
        Get real value. create float from 4 bytes
        """
        x = _bytearray[byte_index:byte_index + 4]
        real = struct.unpack('>f', struct.pack('4B', *x))[0]
        return real

################

    def setPID(self, p, i, d, PIDmultiplier):
        if self.PID_area and not (self.PID_p_register == self.PID_i_register ==
                                  self.PID_d_register == 0):
            multiplier = 1.
            if PIDmultiplier == 1:
                PIDmultiplier = 10.
            elif PIDmultiplier == 2:
                multiplier = 100.
            self.writeInt(self.PID_area - 1, self.PID_db_nr,
                          self.PID_p_register, p * multiplier)
            self.writeInt(self.PID_area - 1, self.PID_area, self.PID_db_nr,
                          self.PID_i_register, i * multiplier)
            self.writeInt(self.PID_area - 1, self.PID_area, self.PID_db_nr,
                          self.PID_d_register, d * multiplier)

    def setTarget(self, sv, SVmultiplier):
        if self.PID_area:
            multiplier = 1.
            if SVmultiplier == 1:
                multiplier = 10.
            elif SVmultiplier == 2:
                multiplier = 100.
            if self.SVtype == 1:
                self.writeFloat(self.PID_area - 1, self.PID_db_nr,
                                self.PID_SV_register, sv * multiplier)
            else:
                self.writeInt(self.PID_area - 1,
                              self.PID_db_nr, self.PID_SV_register,
                              int(round(sv * multiplier)))

    def isConnected(self):
        # the check on the CPU state is needed as get_connected() still returns True if the connect got terminated from the peer due to a bug in snap7
        # disconnects and clears the S7 plc objects if get_connected() but not str(self.plc.get_cpu_state()) == "S7CpuStatusRun" to force a clear restart
        #        return self.plc is not None and self.plc.get_connected() and str(self.plc.get_cpu_state()) == "S7CpuStatusRun"
        if self.plc is not None and self.plc.get_connected():
            return True
#            if str(self.plc.get_cpu_state()) == "S7CpuStatusRun":
#                return True
#            else:
#                self.disconnect()
#                return False
        else:
            return False

    def disconnect(self):
        try:
            self.plc.plc_stop()
        except:
            pass
        try:
            self.plc.disconnect()
        except:
            pass
        try:
            self.plc.destroy()
        except:
            pass
        self.plc = None

    def connect(self):
        if not self.libLoaded:
            #from artisanlib.s7client import S7Client
            from snap7.common import load_library as load_snap7_library
            # first load shared lib if needed
            platf = str(platform.system())
            if platf in ['Windows', 'Linux'] and artisanlib.util.appFrozen():
                libpath = os.path.dirname(sys.executable)
                if platf == 'Linux':
                    snap7dll = os.path.join(libpath, "libsnap7.so")
                else:  # Windows:
                    snap7dll = os.path.join(libpath, "snap7.dll")
                load_snap7_library(
                    snap7dll)  # will ensure to load it only once
            self.libLoaded = True

        if self.libLoaded and self.plc is None:
            # create a client instance
            from artisanlib.s7client import S7Client
            self.plc = S7Client()

        # next reset client instance if not yet connected to ensure a fresh start
        if not self.isConnected():
            try:
                if self.plc is None:
                    from artisanlib.s7client import S7Client
                    self.plc = S7Client()
                else:
                    self.plc.disconnect()
            except:
                pass
            with suppress_stdout_stderr():
                time.sleep(0.4)
                try:
                    self.plc.connect(self.host, self.rack, self.slot,
                                     self.port)
                    time.sleep(0.4)
                except:
                    pass

            if self.isConnected():
                self.aw.sendmessage(
                    QApplication.translate("Message", "S7 connected", None))
                self.clearReadingsCache()
                time.sleep(0.4)
            else:
                time.sleep(0.6)
                try:
                    if self.plc is None:
                        from artisanlib.s7client import S7Client
                        self.plc = S7Client()
                    else:
                        self.plc.disconnect()
                except:
                    pass
                # we try a second time
                with suppress_stdout_stderr():
                    time.sleep(0.4)
                    self.plc.connect(self.host, self.rack, self.slot,
                                     self.port)
                    time.sleep(0.4)

                    if self.isConnected():
                        self.clearReadingsCache()
                        self.aw.sendmessage(
                            QApplication.translate("Message", "S7 Connected",
                                                   None) + " (2)")
                        time.sleep(0.4)
            self.updateActiveRegisters()

########## S7 optimizer for fetching register data in batches

# S7 area => db_nr => [start registers]

    def updateActiveRegisters(self):
        self.activeRegisters = {}
        for c in range(self.channels):
            area = self.area[c] - 1
            if area != -1:
                db_nr = self.db_nr[c]
                register = self.start[c]
                registers = [register]  # BOOL
                if self.type[c] in [1, 2]:  # FLOAT (or FLOAT2INT)
                    registers.append(register + 1)
                    registers.append(register + 2)
                    registers.append(register + 3)
                elif self.type[c] == 0:  # INT
                    registers.append(register + 1)
                if not (area in self.activeRegisters):
                    self.activeRegisters[area] = {}
                if db_nr in self.activeRegisters[area]:
                    self.activeRegisters[area][db_nr].extend(registers)
                else:
                    self.activeRegisters[area][db_nr] = registers

    def clearReadingsCache(self):
        self.readingsCache = {}

    def cacheReadings(self, area, db_nr, register, results):
        if not (area in self.readingsCache):
            self.readingsCache[area] = {}
        if not db_nr in self.readingsCache[area]:
            self.readingsCache[area][db_nr] = {}
        try:
            for i, v in enumerate(results):
                self.readingsCache[area][db_nr][register + i] = v
        except:
            pass

    def readActiveRegisters(self):
        if not self.optimizer:
            return
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.clearReadingsCache()
            self.connect()
            if self.isConnected():
                for area in self.activeRegisters:
                    for db_nr in self.activeRegisters[area]:
                        registers = sorted(self.activeRegisters[area][db_nr])
                        if self.fetch_max_blocks:
                            sequences = [[registers[0], registers[-1]]]
                        else:
                            # split in successive sequences
                            gaps = [[s, e]
                                    for s, e in zip(registers, registers[1:])
                                    if s + 1 < e]
                            edges = iter(registers[:1] + sum(gaps, []) +
                                         registers[-1:])
                            sequences = list(
                                zip(edges, edges)
                            )  # list of pairs of the form (start-register,end-register)
                        just_send = False
                        for seq in sequences:
                            retry = self.readRetries
                            register = seq[0]
                            count = seq[1] - seq[0] + 1
                            res = None
                            while True:
                                try:
                                    if just_send:
                                        time.sleep(0.03)
                                    just_send = True
                                    res = self.plc.read_area(
                                        self.areas[area], db_nr, register,
                                        count)
                                except:
                                    res = None
                                if res is None:
                                    if retry > 0:
                                        retry = retry - 1
                                    else:
                                        raise Exception(
                                            "read_area({},{},{},{})".format(
                                                area, db_nr, register, count))
                                else:
                                    break
                            if res is not None:
                                if self.commError:  # we clear the previous error and send a message
                                    self.commError = False
                                    self.aw.qmc.adderror(
                                        QApplication.translate(
                                            "Error Message",
                                            "S7 Communication Resumed", None))
                                self.cacheReadings(area, db_nr, register, res)

                            #note: logged chars should be unicode not binary
                            if self.aw.seriallogflag:
                                self.aw.addserial(
                                    "S7 read_area({},{},{},{})".format(
                                        area, db_nr, register, count))
        except Exception as e:  # as ex:
            #            self.disconnect()
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            _, _, exc_tb = sys.exc_info()
            #            self.aw.qmc.adderror((QApplication.translate("Error Message","S7 Error:",None) + " readSingleRegister() {0}").format(str(ex)),exc_tb.tb_lineno)
            _, _, exc_tb = sys.exc_info()
            self.aw.qmc.adderror(
                QApplication.translate(
                    "Error Message",
                    "readActiveRegisters() S7 Communication Error", None) +
                ": " + str(e), exc_tb.tb_lineno)
            if self.aw.seriallogflag:
                self.aw.addserial(
                    "S7 readActiveRegisters() => S7 Communication Error: {}".
                    format(str(e)))
            self.commError = True
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

##########

    def writeFloat(self, area, dbnumber, start, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if self.isConnected():
                with suppress_stdout_stderr():
                    ba = self.plc.read_area(self.areas[area], dbnumber, start,
                                            4)
                    self.set_real(ba, 0, float(value))
                    self.plc.write_area(self.areas[area], dbnumber, start, ba)

            else:
                self.aw.qmc.adderror(
                    (QApplication.translate("Error Message", "S7 Error:", None)
                     + " connecting to PLC failed"))
        except Exception as e:
            if self.aw.qmc.flagon:
                _, _, exc_tb = sys.exc_info()
                self.aw.qmc.adderror(
                    QApplication.translate("Error Message",
                                           "S7 Communication Error", None) +
                    " writeFloat: " + str(e), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            if self.aw.seriallogflag:
                self.aw.addserial("S7 writeFloat({},{},{},{})".format(
                    area, dbnumber, start, value))

    def writeInt(self, area, dbnumber, start, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if self.isConnected():
                with suppress_stdout_stderr():
                    ba = self.plc.read_area(self.areas[area], dbnumber, start,
                                            2)
                    self.set_int(ba, 0, int(value))
                    self.plc.write_area(self.areas[area], dbnumber, start, ba)

            else:
                self.aw.qmc.adderror(
                    (QApplication.translate("Error Message", "S7 Error:", None)
                     + " connecting to PLC failed"))
        except Exception as e:
            if self.aw.qmc.flagon:
                _, _, exc_tb = sys.exc_info()
                self.aw.qmc.adderror(
                    QApplication.translate("Error Message",
                                           "S7 Communication Error", None) +
                    " writeInt: " + str(e), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            if self.aw.seriallogflag:
                self.aw.addserial("S7 writeInt({},{},{},{})".format(
                    area, dbnumber, start, value))

    def maskWriteInt(self, area, dbnumber, start, and_mask, or_mask, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if self.isConnected():
                with suppress_stdout_stderr():
                    ba = self.plc.read_area(self.areas[area], dbnumber, start,
                                            2)
                    new_val = (int(round(value))
                               & and_mask) | (or_mask & (and_mask ^ 0xFFFF))
                    self.set_int(ba, 0, int(new_val))
                    self.plc.write_area(self.areas[area], dbnumber, start, ba)
            else:
                self.aw.qmc.adderror(
                    (QApplication.translate("Error Message", "S7 Error:", None)
                     + " connecting to PLC failed"))
        except Exception as e:
            if self.aw.qmc.flagon:
                _, _, exc_tb = sys.exc_info()
                self.aw.qmc.adderror(
                    QApplication.translate("Error Message",
                                           "S7 Communication Error", None) +
                    " maskWriteInt: " + str(e), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            if self.aw.seriallogflag:
                self.aw.addserial("S7 writeInt({},{},{},{})".format(
                    area, dbnumber, start, value))

    def writeBool(self, area, dbnumber, start, index, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if self.isConnected():
                with suppress_stdout_stderr():
                    ba = self.plc.read_area(self.areas[area], dbnumber, start,
                                            1)
                    self.set_bool(ba, 0, int(index), bool(value))
                    self.plc.write_area(self.areas[area], dbnumber, start, ba)
            else:
                self.aw.qmc.adderror(
                    (QApplication.translate("Error Message", "S7 Error:", None)
                     + " connecting to PLC failed"))
        except Exception as e:
            if self.aw.qmc.flagon:
                _, _, exc_tb = sys.exc_info()
                self.aw.qmc.adderror(
                    QApplication.translate("Error Message",
                                           "S7 Communication Error", None) +
                    " writeBool: " + str(e), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            if self.aw.seriallogflag:
                self.aw.addserial("S7 writeBool({},{},{},{},{})".format(
                    area, dbnumber, start, index, value))

    # if force the readings cache is ignored and fresh readings are requested
    def readFloat(self, area, dbnumber, start, force=False):
        if area == 0:
            return
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            if not force and area in self.readingsCache and dbnumber in self.readingsCache[area] and start in self.readingsCache[area][dbnumber] \
                and start+1 in self.readingsCache[area][dbnumber] and start+2 in self.readingsCache[area][dbnumber] \
                and start+3 in self.readingsCache[area][dbnumber]:
                # cache hit
                res = bytearray([
                    self.readingsCache[area][dbnumber][start],
                    self.readingsCache[area][dbnumber][start + 1],
                    self.readingsCache[area][dbnumber][start + 2],
                    self.readingsCache[area][dbnumber][start + 3]
                ])
                r = self.get_real(res, 0)
                if self.aw.seriallogflag and not self.commError:
                    self.aw.addserial(
                        "S7 readFloat_cached({},{},{},{}) => {}".format(
                            area, dbnumber, start, force, r))
                return r
            else:
                self.connect()
                if self.isConnected():
                    retry = self.readRetries
                    res = None
                    while True:
                        try:
                            with suppress_stdout_stderr():
                                res = self.plc.read_area(
                                    self.areas[area], dbnumber, start, 4)

                        except:
                            res = None
                        if res is None:
                            if retry > 0:
                                retry = retry - 1
                            else:
                                raise Exception("result None")
                        else:
                            break
                    if res is None:
                        return
                    else:
                        if self.commError:  # we clear the previous error and send a message
                            self.commError = False
                            self.aw.qmc.adderror(
                                QApplication.translate(
                                    "Error Message",
                                    "S7 Communication Resumed", None))
                        r = self.get_real(res, 0)
                        if self.aw.seriallogflag and not self.commError:
                            self.aw.addserial(
                                "S7 readFloat({},{},{},{}) => {}".format(
                                    area, dbnumber, start, force, r))
                        return r
                else:
                    self.commError = True
                    self.aw.qmc.adderror((QApplication.translate(
                        "Error Message", "S7 Error:", None) +
                                          " connecting to PLC failed"))
        except Exception as e:
            if self.aw.qmc.flagon:
                _, _, exc_tb = sys.exc_info()
                self.aw.qmc.adderror(
                    QApplication.translate("Error Message",
                                           "S7 Communication Error", None) +
                    " readFloat({},{},{},{}): {}".format(
                        area, dbnumber, start, force, str(e)),
                    exc_tb.tb_lineno)
                if self.aw.seriallogflag:
                    self.aw.addserial(
                        "S7 readFloat({},{},{},{}) => S7 Communication Error: {}"
                        .format(area, dbnumber, start, force, str(e)))
            self.commError = True
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # as readFloat, but does not retry nor raise and error and returns a None instead
    # also does not reserve the port via a semaphore nor uses the cache!
    def peakFloat(self, area, dbnumber, start):
        if area == 0:
            return
        try:
            self.connect()
            if self.isConnected():
                with suppress_stdout_stderr():
                    res = self.plc.read_area(self.areas[area], dbnumber, start,
                                             4)
                return self.get_real(res, 0)
            else:
                return
        except:
            return

    # if force the readings cache is ignored and fresh readings are requested
    def readInt(self, area, dbnumber, start, force=False):
        if area == 0:
            return
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            if not force and area in self.readingsCache and dbnumber in self.readingsCache[area] and start in self.readingsCache[area][dbnumber] \
                and start+1 in self.readingsCache[area][dbnumber]:
                # cache hit
                res = bytearray([
                    self.readingsCache[area][dbnumber][start],
                    self.readingsCache[area][dbnumber][start + 1]
                ])
                r = self.get_int(res, 0)
                if self.aw.seriallogflag:
                    self.aw.addserial(
                        "S7 readInt_cached({},{},{},{}) => {}".format(
                            area, dbnumber, start, force, r))
                return r
            else:
                self.connect()
                if self.isConnected():
                    retry = self.readRetries
                    res = None
                    while True:
                        try:
                            with suppress_stdout_stderr():
                                res = self.plc.read_area(
                                    self.areas[area], dbnumber, start, 2)
                        except Exception:
                            res = None
                        if dbnumber == 2 and start == 48:
                            raise Exception("result None")
                        if res is None:
                            if retry > 0:
                                retry = retry - 1
                            else:
                                raise Exception("result None")
                        else:
                            break
                    if res is None:
                        return
                    else:
                        if self.commError:  # we clear the previous error and send a message
                            self.commError = False
                            self.aw.qmc.adderror(
                                QApplication.translate(
                                    "Error Message",
                                    "S7 Communication Resumed", None))
                        r = self.get_int(res, 0)
                        if self.aw.seriallogflag and not self.commError:
                            self.aw.addserial(
                                "S7 readInt({},{},{},{}) => {}".format(
                                    area, dbnumber, start, force, r))
                        return r
                else:
                    self.commError = True
                    self.aw.qmc.adderror((QApplication.translate(
                        "Error Message", "S7 Error:", None) +
                                          " connecting to PLC failed"))
        except Exception as e:
            if self.aw.qmc.flagon:
                _, _, exc_tb = sys.exc_info()
                self.aw.qmc.adderror(
                    QApplication.translate("Error Message",
                                           "S7 Communication Error", None) +
                    " readInt({},{},{},{}): {}".format(area, dbnumber, start,
                                                       force, str(e)),
                    exc_tb.tb_lineno)
                if self.aw.seriallogflag:
                    self.aw.addserial(
                        "S7 readInt({},{},{},{}) => S7 Communication Error: {}"
                        .format(area, dbnumber, start, force, str(e)))
            self.commError = True
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # as readInt, but does not retry nor raise and error and returns a None instead
    # also does not reserve the port via a semaphore nor uses the cache!
    def peekInt(self, area, dbnumber, start):
        if area == 0:
            return
        try:
            self.connect()
            if self.isConnected():
                with suppress_stdout_stderr():
                    res = self.plc.read_area(self.areas[area], dbnumber, start,
                                             2)
                return self.get_int(res, 0)
            else:
                return
        except:
            return

    # if force the readings cache is ignored and fresh readings are requested
    def readBool(self, area, dbnumber, start, index, force=False):
        if area == 0:
            return
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            if not force and area in self.readingsCache and dbnumber in self.readingsCache[
                    area] and start in self.readingsCache[area][dbnumber]:
                # cache hit
                res = bytearray([self.readingsCache[area][dbnumber][start]])
                r = self.get_bool(res, 0, index)
                if self.aw.seriallogflag:
                    self.aw.addserial(
                        "S7 readBool_cached({},{},{},{},{}) => {}".format(
                            area, dbnumber, start, index, force, r))
                return r
            else:
                self.connect()
                if self.isConnected():
                    retry = self.readRetries
                    res = None
                    while True:
                        try:
                            with suppress_stdout_stderr():
                                res = self.plc.read_area(
                                    self.areas[area], dbnumber, start, 1)

                        except Exception:
                            res = None
                        if res is None:
                            if retry > 0:
                                retry = retry - 1
                            else:
                                raise Exception("result None")
                        else:
                            break
                    if res is None:
                        return
                    else:
                        if self.commError:  # we clear the previous error and send a message
                            self.commError = False
                            self.aw.qmc.adderror(
                                QApplication.translate(
                                    "Error Message",
                                    "S7 Communication Resumed", None))
                        r = self.get_bool(res, 0, index)
                        if self.aw.seriallogflag and not self.commError:
                            self.aw.addserial(
                                "S7 readBool({},{},{},{},{}) => {}".format(
                                    area, dbnumber, start, index, force, r))
                        return r
                else:
                    self.commError = True
                    self.aw.qmc.adderror((QApplication.translate(
                        "Error Message", "S7 Error:", None) +
                                          " connecting to PLC failed"))
        except Exception as e:
            if self.aw.qmc.flagon:
                _, _, exc_tb = sys.exc_info()
                self.aw.qmc.adderror(
                    QApplication.translate("Error Message",
                                           "S7 Communication Error", None) +
                    " readBool({},{},{},{},{}): {}".format(
                        area, dbnumber, start, index, force, str(e)),
                    exc_tb.tb_lineno)
                if self.aw.seriallogflag:
                    self.aw.addserial(
                        "S7 readBool({},{},{},{},{}) => S7 Communication Error: {}"
                        .format(area, dbnumber, start, index, force, str(e)))
            self.commError = True
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            if self.aw.seriallogflag:
                self.aw.addserial("S7 readBool({},{},{},{})".format(
                    area, dbnumber, start, index))
Esempio n. 7
0
class s7port(object):
    def __init__(self, sendmessage, adderror, addserial):
        self.sendmessage = sendmessage  # function to create an Artisan message to the user in the message line
        self.adderror = adderror  # signal an error to the user
        self.addserial = addserial  # add to serial log

        self.readRetries = 1
        self.channels = 8  # maximal number of S7 channels
        self.host = '127.0.0.1'  # the TCP host
        self.port = 102  # the TCP port
        self.rack = 0  # 0,..,7
        self.slot = 0  # 0,..,31

        self.lastReadResult = 0  # this is set by eventaction following some custom button/slider S/ actions with "read" command

        self.area = [0] * self.channels
        self.db_nr = [1] * self.channels
        self.start = [0] * self.channels
        self.type = [0] * self.channels
        self.mode = [
            0
        ] * self.channels  # temp mode is an int here, 0:__,1:C,2:F (this is different than other places)
        self.div = [0] * self.channels

        self.PID_area = 0
        self.PID_db_nr = 0
        self.PID_SV_register = 0
        self.PID_p_register = 0
        self.PID_i_register = 0
        self.PID_d_register = 0
        self.PID_ON_action = ""
        self.PID_OFF_action = ""
        self.SVmultiplier = 0
        self.PIDmultiplier = 0

        self.COMsemaphore = QSemaphore(1)

        self.areas = [
            0x81,  # PE
            0x82,  # PA
            0x83,  # MK
            0x1C,  # CT
            0x1D,  # TM
            0x84,  # DB
        ]

        self.plc = None
        self.commError = False  # True after a communication error was detected and not yet cleared by receiving proper data

    def setPID(self, p, i, d, PIDmultiplier):
        if self.PID_area and not (self.PID_p_register == self.PID_i_register ==
                                  self.PID_d_register == 0):
            multiplier = 1.
            if PIDmultiplier == 1:
                PIDmultiplier = 10.
            elif PIDmultiplier == 2:
                multiplier = 100.
            self.writeInt(self.PID_area - 1, self.PID_db_nr,
                          self.PID_p_register, p * multiplier)
            self.writeInt(self.PID_area - 1, self.PID_area, self.PID_db_nr,
                          self.PID_i_register, i * multiplier)
            self.writeInt(self.PID_area - 1, self.PID_area, self.PID_db_nr,
                          self.PID_d_register, d * multiplier)

    def setTarget(self, sv, SVmultiplier):
        if self.PID_area:
            multiplier = 1.
            if SVmultiplier == 1:
                multiplier = 10.
            elif SVmultiplier == 2:
                multiplier = 100.
            self.writeInt(self.PID_area - 1, self.PID_db_nr,
                          self.PID_SV_register, int(round(sv * multiplier)))

    def isConnected(self):
        return not (self.plc is None) and self.plc.get_connected()

    def disconnect(self):
        if self.isConnected():
            try:
                self.plc.disconnect()
                self.plc.destroy()
                self.plc = None
            except Exception:
                pass

    def connect(self):
        from artisanlib.s7client import S7Client
        from snap7.common import load_library as load_snap7_library
        # first load shared lib if needed
        platf = str(platform.system())
        if platf in ['Windows', 'Linux'] and util.appFrozen():
            libpath = os.path.dirname(sys.executable)
            if platf == 'Linux':
                snap7dll = os.path.join(libpath, "libsnap7.so")
            else:  # Windows:
                snap7dll = os.path.join(libpath, "snap7.dll")
            load_snap7_library(snap7dll)  # will ensure to load it only once
        # next reset client instance if not yet connected to ensure a fresh start
        if self.plc and not self.plc.get_connected():
            self.plc = None
        # connect if not yet connected
        if self.plc is None:
            self.plc = S7Client()
            with suppress_stdout_stderr():
                time.sleep(0.3)
                self.plc.connect(self.host, self.rack, self.slot, self.port)
            if self.plc.get_connected():
                self.sendmessage(
                    QApplication.translate("Message", "S7 Connected", None))
                time.sleep(0.7)
            else:
                time.sleep(0.5)
                self.plc = S7Client()
                # we try a second time
                with suppress_stdout_stderr():
                    time.sleep(0.3)
                    self.plc.connect(self.host, self.rack, self.slot,
                                     self.port)
                if self.plc.get_connected():
                    self.sendmessage(
                        QApplication.translate("Message", "S7 Connected",
                                               None))
                    time.sleep(0.7)

    def writeFloat(self, area, dbnumber, start, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if self.plc is not None and self.plc.get_connected():
                with suppress_stdout_stderr():
                    ba = self.plc.read_area(self.areas[area], dbnumber, start,
                                            4)
                    from snap7.util import set_real
                    set_real(ba, 0, float(value))
                    self.plc.write_area(self.areas[area], dbnumber, start, ba)
            else:
                self.adderror(
                    (QApplication.translate("Error Message", "S7 Error:", None)
                     + " connecting to PLC failed"))
        except Exception as ex:
            self.adderror(
                QApplication.translate("Error Message",
                                       "S7 Communication Error", None))
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    def writeInt(self, area, dbnumber, start, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if self.plc is not None and self.plc.get_connected():
                with suppress_stdout_stderr():
                    ba = self.plc.read_area(self.areas[area], dbnumber, start,
                                            2)
                    from snap7.util import set_int
                    set_int(ba, 0, int(value))
                    self.plc.write_area(self.areas[area], dbnumber, start, ba)
            else:
                self.adderror(
                    (QApplication.translate("Error Message", "S7 Error:", None)
                     + " connecting to PLC failed"))
        except Exception:
            self.adderror(
                QApplication.translate("Error Message",
                                       "S7 Communication Error", None))
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    def readFloat(self, area, dbnumber, start):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if self.plc is not None and self.plc.get_connected():
                retry = self.readRetries
                res = None
                while True:
                    try:
                        with suppress_stdout_stderr():
                            res = self.plc.read_area(self.areas[area],
                                                     dbnumber, start, 4)
                    except:
                        res = None
                    if res is None:
                        if retry > 0:
                            retry = retry - 1
                        else:
                            raise Exception("Communication error")
                    else:
                        break
                if res is None:
                    return -1
                else:
                    if self.commError:  # we clear the previous error and send a message
                        self.commError = False
                        self.adderror(
                            QApplication.translate("Error Message",
                                                   "S7 Communication Resumed",
                                                   None))
                    from snap7.util import get_real
                    return get_real(res, 0)
            else:
                self.commError = True
                self.adderror(
                    (QApplication.translate("Error Message", "S7 Error:", None)
                     + " connecting to PLC failed"))
                return -1
        except Exception:
            self.adderror(
                QApplication.translate("Error Message",
                                       "S7 Communication Error", None))
            self.commError = True
            return -1
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            self.addserial("S7 readFloat")

    def readInt(self, area, dbnumber, start):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if self.plc is not None and self.plc.get_connected():
                retry = self.readRetries
                res = None
                while True:
                    try:
                        with suppress_stdout_stderr():
                            res = self.plc.read_area(self.areas[area],
                                                     dbnumber, start, 2)
                    except Exception as e:
                        res = None
                    if res is None:
                        if retry > 0:
                            retry = retry - 1
                        else:
                            raise Exception("Communication error")
                    else:
                        break
                if res is None:
                    return -1
                else:
                    if self.commError:  # we clear the previous error and send a message
                        self.commError = False
                        self.adderror(
                            QApplication.translate("Error Message",
                                                   "S7 Communication Resumed",
                                                   None))
                    from snap7.util import get_int
                    return get_int(res, 0)
            else:
                self.commError = True
                self.adderror(
                    (QApplication.translate("Error Message", "S7 Error:", None)
                     + " connecting to PLC failed"))
                return -1
        except Exception:
            self.adderror(
                QApplication.translate("Error Message",
                                       "S7 Communication Error", None))
            self.commError = True
            return -1
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            self.addserial("S7 readInt")
Esempio n. 8
0
class modbusport(object):
    """ this class handles the communications with all the modbus devices"""
    def __init__(self, sendmessage, adderror, addserial, aw):
        self.sendmessage = sendmessage  # function to create an Artisan message to the user in the message line
        self.adderror = adderror  # signal an error to the user
        self.addserial = addserial  # add to serial log
        self.aw = aw

        # retries
        self.readRetries = 1
        #default initial settings. They are changed by settingsload() at initiation of program acording to the device chosen
        self.comport = "COM5"  #NOTE: this string should not be translated.
        self.baudrate = 115200
        self.bytesize = 8
        self.parity = 'N'
        self.stopbits = 1
        self.timeout = 1.0
        self.PID_slave_ID = 0
        self.PID_SV_register = 0
        self.PID_p_register = 0
        self.PID_i_register = 0
        self.PID_d_register = 0
        self.PID_ON_action = ""
        self.PID_OFF_action = ""

        self.channels = 8
        self.inputSlaves = [0] * self.channels
        self.inputRegisters = [0] * self.channels
        self.inputFloats = [False] * self.channels
        self.inputBCDs = [False] * self.channels
        self.inputCodes = [3] * self.channels
        self.inputDivs = [0] * self.channels  # 0: none, 1: 1/10, 2:1/100
        self.inputModes = ["C"] * self.channels

        self.optimizer = True  # if set, values of consecutive register addresses are requested in single requests
        # MODBUS functions associated to dicts associating MODBUS slave ids to registers in use
        # for optimized read of full register segments with single requests
        # this dict is re-computed on each connect() by a call to updateActiveRegisters()
        # NOTE: for registers of type float and BCD (32bit = 2x16bit) also the succeeding register is registered here
        self.activeRegisters = {}
        # the readings cache that is filled by requesting sequences of values in blocks
        self.readingsCache = {}

        self.SVmultiplier = 0
        self.PIDmultiplier = 0
        self.byteorderLittle = False
        self.wordorderLittle = True
        self.master = None
        self.COMsemaphore = QSemaphore(1)
        self.host = '127.0.0.1'  # the TCP/UDP host
        self.port = 502  # the TCP/UDP port
        self.type = 0
        # type =
        #    0: Serial RTU
        #    1: Serial ASCII
        #    2: Serial Binary
        #    3: TCP
        #    4: UDP
        self.lastReadResult = 0  # this is set by eventaction following some custom button/slider Modbus actions with "read" command

        self.commError = False  # True after a communication error was detected and not yet cleared by receiving proper data

    # this garantees a minimum of 30 miliseconds between readings and 80ms between writes (according to the Modbus spec) on serial connections
    # this sleep delays between requests seems to be beneficial on slow RTU serial connections like those of the FZ-94
    def sleepBetween(self, write=False):
        if write:
            #            if self.type in [3,4]: # TCP or UDP
            #                time.sleep(0.040)
            pass  # handled in MODBUS lib
            #            else:
            time.sleep(0.035)
        else:
            if self.type in [
                    3, 4
            ]:  # delay between writes only on serial connections
                pass
            else:
                time.sleep(0.035)

    def address2register(self, addr, code=3):
        if code == 3 or code == 6:
            return addr - 40001
        else:
            return addr - 30001

    def isConnected(self):
        return not (self.master is None) and self.master.socket

    def disconnect(self):
        try:
            self.master.close()
        except Exception:
            pass
        self.master = None

    def connect(self):
        #        if self.master and not self.master.socket:
        #            self.master = None
        if self.master is None:
            self.commError = False
            try:
                # as in the following the port is None, no port is opened on creation of the (py)serial object
                if self.type == 1:  # Serial ASCII
                    from pymodbus.client.sync import ModbusSerialClient
                    self.master = ModbusSerialClient(method='ascii',
                                                     port=self.comport,
                                                     baudrate=self.baudrate,
                                                     bytesize=self.bytesize,
                                                     parity=self.parity,
                                                     stopbits=self.stopbits,
                                                     retry_on_empty=True,
                                                     timeout=self.timeout)
                elif self.type == 2:  # Serial Binary
                    from pymodbus.client.sync import ModbusSerialClient  # @Reimport
                    self.master = ModbusSerialClient(method='binary',
                                                     port=self.comport,
                                                     baudrate=self.baudrate,
                                                     bytesize=self.bytesize,
                                                     parity=self.parity,
                                                     stopbits=self.stopbits,
                                                     retry_on_empty=True,
                                                     timeout=self.timeout)
                elif self.type == 3:  # TCP
                    from pymodbus.client.sync import ModbusTcpClient
                    try:
                        self.master = ModbusTcpClient(
                            host=self.host,
                            port=self.port,
                            retry_on_empty=True,
                            retries=1,
                            timeout=0.9,  #self.timeout
                        )
                        self.readRetries = 0
                    except:
                        self.master = ModbusTcpClient(
                            host=self.host,
                            port=self.port,
                        )
                elif self.type == 4:  # UDP
                    from pymodbus.client.sync import ModbusUdpClient
                    try:
                        self.master = ModbusUdpClient(
                            host=self.host,
                            port=self.port,
                            retry_on_empty=True,
                            retries=3,
                            timeout=0.7,  #self.timeout
                        )
                    except:  # older versions of pymodbus don't support the retries, timeout nor the retry_on_empty arguments
                        self.master = ModbusUdpClient(
                            host=self.host,
                            port=self.port,
                        )
                else:  # Serial RTU
                    from pymodbus.client.sync import ModbusSerialClient  # @Reimport
                    self.master = ModbusSerialClient(
                        method='rtu',
                        port=self.comport,
                        baudrate=self.baudrate,
                        bytesize=self.bytesize,
                        parity=self.parity,
                        stopbits=self.stopbits,
                        retry_on_empty=False,
                        strict=
                        False,  # settings this to False disables the inter char timeout restriction
                        timeout=self.timeout)
                    #                    self.master.inter_char_timeout = 0.05
                    self.readRetries = 1
                self.master.connect()
                self.updateActiveRegisters()
                time.sleep(.5)  # avoid possible hickups on startup
                if self.isConnected() != None:
                    self.sendmessage(
                        QApplication.translate("Message",
                                               "Connected via MODBUS", None))
            except Exception as ex:
                _, _, exc_tb = sys.exc_info()
                self.adderror(
                    (QApplication.translate("Error Message", "Modbus Error:",
                                            None) + " connect() {0}").format(
                                                str(ex)), exc_tb.tb_lineno)

########## MODBUS optimizer for fetching register data in batches

# MODBUS code => slave => [registers]

    def updateActiveRegisters(self):
        self.activeRegisters = {}
        for c in range(self.channels):
            slave = self.inputSlaves[c]
            if slave != 0:
                register = self.inputRegisters[c]
                code = self.inputCodes[c]
                registers = [register]
                if self.inputFloats[c] or self.inputBCDs[c]:
                    registers.append(register + 1)
                if not (code in self.activeRegisters):
                    self.activeRegisters[code] = {}
                if slave in self.activeRegisters[code]:
                    self.activeRegisters[code][slave].extend(registers)
                else:
                    self.activeRegisters[code][slave] = registers

    def clearReadingsCache(self):
        self.readingsCache = {}

    def cacheReadings(self, code, slave, register, results):
        if not (code in self.readingsCache):
            self.readingsCache[code] = {}
        if not slave in self.readingsCache[code]:
            self.readingsCache[code][slave] = {}
        for i, v in enumerate(results):
            self.readingsCache[code][slave][register + i] = v

    def readActiveRegisters(self):
        if not self.optimizer:
            return
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.clearReadingsCache()
            for code in self.activeRegisters:
                for slave in self.activeRegisters[code]:
                    registers = sorted(self.activeRegisters[code][slave])
                    # split in successive sequences
                    gaps = [[s, e] for s, e in zip(registers, registers[1:])
                            if s + 1 < e]
                    edges = iter(registers[:1] + sum(gaps, []) +
                                 registers[-1:])
                    sequences = list(
                        zip(edges, edges)
                    )  # list of pairs of the form (start-register,end-register)
                    just_send = False
                    for seq in sequences:
                        retry = self.readRetries
                        register = seq[0]
                        count = seq[1] - seq[0] + 1
                        res = None
                        if just_send:
                            self.sleepBetween(
                            )  # we start with a sleep, as it could be that just a send command happend before the semaphore was catched
                        just_send = True
                        while True:
                            try:
                                # we cache only MODBUS function 3 and 4 (not 1 and 2!)
                                if code == 3:
                                    res = self.master.read_holding_registers(
                                        register, count, unit=slave)
                                elif code == 4:
                                    res = self.master.read_input_registers(
                                        register, count, unit=slave)
                            except Exception:
                                res = None
                            if res is None or res.isError(
                            ):  # requires pymodbus v1.5.1
                                if retry > 0:
                                    retry = retry - 1
                                    time.sleep(0.020)
                                else:
                                    raise Exception("Exception response")
                            else:
                                break
                        if res is not None:
                            if self.commError:  # we clear the previous error and send a message
                                self.commError = False
                                self.adderror(
                                    QApplication.translate(
                                        "Error Message",
                                        "Modbus Communication Resumed", None))
                            self.cacheReadings(code, slave, register,
                                               res.registers)

                        #note: logged chars should be unicode not binary
                        if self.aw.seriallogflag:
                            ser_str = "MODBUS readSingleRegister : {},{},{},{},{},{} || Slave = {} || Register = {} || Code = {} || Rx = {}".format(
                                self.comport, self.baudrate, self.bytesize,
                                self.parity, self.stopbits, self.timeout,
                                slave, register, code, res)
                            self.addserial(ser_str)

        except Exception:  # as ex:
            #            self.disconnect()
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            _, _, exc_tb = sys.exc_info()
            #            self.adderror((QApplication.translate("Error Message","Modbus Error:",None) + " readSingleRegister() {0}").format(str(ex)),exc_tb.tb_lineno)
            self.adderror(
                QApplication.translate("Error Message",
                                       "Modbus Communication Error", None))
            self.commError = True
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

##########

# function 15 (Write Multiple Coils)

    def writeCoils(self, slave, register, values):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.master.write_coils(int(register),
                                    list(values),
                                    unit=int(slave))
            time.sleep(.3)  # avoid possible hickups on startup
        except Exception as ex:
            #            self.disconnect()
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeCoils() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # function 5 (Write Single Coil)
    def writeCoil(self, slave, register, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.master.write_coil(int(register), value, unit=int(slave))
            time.sleep(.3)  # avoid possible hickups on startup
        except Exception as ex:
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeCoil() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # write value to register on slave (function 6 for int or function 16 for float)
    # value can be one of string (containing an int or float), an int or a float
    def writeRegister(self, slave, register, value):
        if stringp(value):
            if "." in value:
                self.writeWord(slave, register, value)
            else:
                self.writeSingleRegister(slave, register, value)
        elif isinstance(value, int):
            self.writeSingleRegister(slave, register, value)
        elif isinstance(value, float):
            self.writeWord(slave, register, value)

    # function 6 (Write Single Holding Register)
    def writeSingleRegister(self, slave, register, value):
        #        _logger.debug("writeSingleRegister(%d,%d,%d)" % (slave,register,value))
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.master.write_register(int(register),
                                       int(value),
                                       unit=int(slave))
            time.sleep(.03)  # avoid possible hickups on startup
        except Exception as ex:
            #            _logger.debug("writeSingleRegister exception: %s" % str(ex))
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeSingleRegister() {0}").format(str(ex)),
                exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # function 22 (Mask Write Register)
    # bits to be modified are "masked" with a 0 in the and_mask (not and_mask)
    # new bit values to be written are taken from the or_mask
    def maskWriteRegister(self, slave, register, and_mask, or_mask):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.master.mask_write_register(int(register),
                                            int(and_mask),
                                            int(or_mask),
                                            unit=int(slave))
            time.sleep(.03)
        except Exception as ex:
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeMask() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # a local variant of function 22 (Mask Write Register)
    # the masks are evaluated locally on the given integer value and the result is send via
    # using function 6
    def localMaskWriteRegister(self, slave, register, and_mask, or_mask,
                               value):
        new_val = (int(round(value)) & and_mask) | (or_mask &
                                                    (and_mask ^ 0xFFFF))
        self.writeSingleRegister(slave, register, int(new_val))

    # function 16 (Write Multiple Holding Registers)
    # values is a list of integers or one integer
    def writeRegisters(self, slave, register, values):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.master.write_registers(int(register), values, unit=int(slave))
            time.sleep(.03)
        except Exception as ex:
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeRegisters() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # function 16 (Write Multiple Holding Registers)
    # value=int or float
    # writes a single precision 32bit float (2-registers)
    def writeWord(self, slave, register, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            builder = getBinaryPayloadBuilder(self.byteorderLittle,
                                              self.wordorderLittle)
            builder.add_32bit_float(float(value))
            payload = builder.build()  # .tolist()
            self.master.write_registers(int(register),
                                        payload,
                                        unit=int(slave),
                                        skip_encode=True)
            time.sleep(.03)
        except Exception as ex:
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeWord() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # translates given int value int a 16bit BCD and writes it into one register
    def writeBCD(self, slave, register, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            builder = getBinaryPayloadBuilder(self.byteorderLittle,
                                              self.wordorderLittle)
            r = convert_to_bcd(int(value))
            builder.add_16bit_uint(r)
            payload = builder.build()  # .tolist()
            self.master.write_registers(int(register),
                                        payload,
                                        unit=int(slave),
                                        skip_encode=True)
            time.sleep(.03)
        except Exception as ex:
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeWord() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # function 3 (Read Multiple Holding Registers) and 4 (Read Input Registers)
    def readFloat(self, slave, register, code=3):
        r = None
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if code in self.readingsCache and slave in self.readingsCache[code] and register in self.readingsCache[code][slave] \
                and register+1 in self.readingsCache[code][slave]:
                # cache hit
                res = [
                    self.readingsCache[code][slave][register],
                    self.readingsCache[code][slave][register + 1]
                ]
                decoder = getBinaryPayloadDecoderFromRegisters(
                    res, self.byteorderLittle, self.wordorderLittle)
                return decoder.decode_32bit_float()
            else:
                retry = self.readRetries
                while True:
                    if code == 3:
                        res = self.master.read_holding_registers(
                            int(register), 2, unit=int(slave))
                    else:
                        res = self.master.read_input_registers(int(register),
                                                               2,
                                                               unit=int(slave))
                    if res is None or res.isError(
                    ):  # requires pymodbus v1.5.1
                        if retry > 0:
                            retry = retry - 1
                            #time.sleep(0.020)
                        else:
                            raise Exception("Exception response")
                    else:
                        break
                decoder = getBinaryPayloadDecoderFromRegisters(
                    res.registers, self.byteorderLittle, self.wordorderLittle)
                r = decoder.decode_32bit_float()
                if self.commError:  # we clear the previous error and send a message
                    self.commError = False
                    self.adderror(
                        QApplication.translate("Error Message",
                                               "Modbus Communication Resumed",
                                               None))
                return r
        except Exception:  # as ex:
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            self.disconnect()
            #            _, _, exc_tb = sys.exc_info()
            #            self.adderror((QApplication.translate("Error Message","Modbus Error:",None) + " readFloat() {0}").format(str(ex)),exc_tb.tb_lineno)
            self.adderror(
                QApplication.translate("Error Message",
                                       "Modbus Communication Error", None))
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            #note: logged chars should be unicode not binary
            if self.aw.seriallogflag:
                ser_str = "MODBUS readFloat :{},{},{},{},{},{} || Slave = {} || Register = {} || Code = {} || Rx = {}".format(
                    self.comport, self.baudrate, self.bytesize, self.parity,
                    self.stopbits, self.timeout, slave, register, code, r)
                self.addserial(ser_str)

    # function 3 (Read Multiple Holding Registers) and 4 (Read Input Registers)
    def readBCD(self, slave, register, code=3):
        r = None
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if code in self.readingsCache and slave in self.readingsCache[code] and register in self.readingsCache[code][slave] \
                and register+1 in self.readingsCache[code][slave]:
                # cache hit
                res = [
                    self.readingsCache[code][slave][register],
                    self.readingsCache[code][slave][register + 1]
                ]
                decoder = getBinaryPayloadDecoderFromRegisters(
                    [res], self.byteorderLittle, self.wordorderLittle)
                r = decoder.decode_32bit_uint()
                return convert_from_bcd(r)
            else:
                retry = self.readRetries
                while True:
                    if code == 3:
                        res = self.master.read_holding_registers(
                            int(register), 2, unit=int(slave))
                    else:
                        res = self.master.read_input_registers(int(register),
                                                               2,
                                                               unit=int(slave))
                    if res is None or res.isError(
                    ):  # requires pymodbus v1.5.1
                        if retry > 0:
                            retry = retry - 1
                            #time.sleep(0.020)
                        else:
                            raise Exception("Exception response")
                    else:
                        break
                decoder = getBinaryPayloadDecoderFromRegisters(
                    res.registers, self.byteorderLittle, self.wordorderLittle)
                r = decoder.decode_32bit_uint()
                if self.commError:  # we clear the previous error and send a message
                    self.commError = False
                    self.adderror(
                        QApplication.translate("Error Message",
                                               "Modbus Communication Resumed",
                                               None))
                time.sleep(
                    0.020
                )  # we add a small sleep between requests to help out the slow Loring electronic
                return convert_from_bcd(r)
        except Exception:  # as ex:
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            self.disconnect()
            #            _, _, exc_tb = sys.exc_info()
            #            self.adderror((QApplication.translate("Error Message","Modbus Error:",None) + " readBCD() {0}").format(str(ex)),exc_tb.tb_lineno)
            self.adderror(
                QApplication.translate("Error Message",
                                       "Modbus Communication Error", None))
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            #note: logged chars should be unicode not binary
            if self.aw.seriallogflag:
                ser_str = "MODBUS readBCD : {},{},{},{},{},{} || Slave = {} || Register = {} || Code = {} || Rx = {}".format(
                    self.comport, self.baudrate, self.bytesize, self.parity,
                    self.stopbits, self.timeout, slave, register, code, r)
                self.addserial(ser_str)

    # as readSingleRegister, but does not retry nor raise and error and returns a None instead
    # also does not reserve the port via a semaphore!
    def peekSingleRegister(self, slave, register, code=3):
        try:
            if code == 1:
                res = self.master.read_coils(int(register), 1, unit=int(slave))
            elif code == 2:
                res = self.master.read_discrete_inputs(int(register),
                                                       1,
                                                       unit=int(slave))
            elif code == 4:
                res = self.master.read_input_registers(int(register),
                                                       1,
                                                       unit=int(slave))
            else:  # code==3
                res = self.master.read_holding_registers(int(register),
                                                         1,
                                                         unit=int(slave))
        except Exception:
            res = None
        if res is not None and not res.isError():  # requires pymodbus v1.5.1
            if code in [1, 2]:
                if res is not None and res.bits[0]:
                    return 1
                else:
                    return 0
            else:
                decoder = getBinaryPayloadDecoderFromRegisters(
                    res.registers, self.byteorderLittle, self.wordorderLittle)
                r = decoder.decode_16bit_uint()
                return r
        else:
            return None

    # function 1 (Read Coil)
    # function 2 (Read Discrete Input)
    # function 3 (Read Multiple Holding Registers) and
    # function 4 (Read Input Registers)
    def readSingleRegister(self, slave, register, code=3):
        #        import logging
        #        logging.basicConfig()
        #        log = logging.getLogger()
        #        log.setLevel(logging.DEBUG)
        r = None
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if code in self.readingsCache and slave in self.readingsCache[
                    code] and register in self.readingsCache[code][slave]:
                # cache hit
                res = self.readingsCache[code][slave][register]
                decoder = getBinaryPayloadDecoderFromRegisters(
                    [res], self.byteorderLittle, self.wordorderLittle)
                return decoder.decode_16bit_uint()
            else:
                retry = self.readRetries
                while True:
                    try:
                        if code == 1:
                            res = self.master.read_coils(int(register),
                                                         1,
                                                         unit=int(slave))
                        elif code == 2:
                            res = self.master.read_discrete_inputs(
                                int(register), 1, unit=int(slave))
                        elif code == 4:
                            res = self.master.read_input_registers(
                                int(register), 1, unit=int(slave))
                        else:  # code==3
                            res = self.master.read_holding_registers(
                                int(register), 1, unit=int(slave))
                    except Exception:
                        res = None
                    if res is None or res.isError(
                    ):  # requires pymodbus v1.5.1
                        if retry > 0:
                            retry = retry - 1
                            time.sleep(0.020)
                        else:
                            raise Exception("Exception response")
                    else:
                        break
                if code in [1, 2]:
                    if res is not None and res.bits[0]:
                        r = 1
                    else:
                        r = 0
                    if self.commError:  # we clear the previous error and send a message
                        self.commError = False
                        self.adderror(
                            QApplication.translate(
                                "Error Message",
                                "Modbus Communication Resumed", None))
                    return r
                else:
                    decoder = getBinaryPayloadDecoderFromRegisters(
                        res.registers, self.byteorderLittle,
                        self.wordorderLittle)
                    r = decoder.decode_16bit_uint()
                    if self.commError:  # we clear the previous error and send a message
                        self.commError = False
                        self.adderror(
                            QApplication.translate(
                                "Error Message",
                                "Modbus Communication Resumed", None))
                    return r
        except Exception:  # as ex:
            #            self.disconnect()
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            _, _, exc_tb = sys.exc_info()
            #            self.adderror((QApplication.translate("Error Message","Modbus Error:",None) + " readSingleRegister() {0}").format(str(ex)),exc_tb.tb_lineno)
            self.adderror(
                QApplication.translate("Error Message",
                                       "Modbus Communication Error", None))
            self.commError = True
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            #note: logged chars should be unicode not binary
            if self.aw.seriallogflag:
                ser_str = "MODBUS readSingleRegister : {},{},{},{},{},{} || Slave = {} || Register = {} || Code = {} || Rx = {}".format(
                    self.comport, self.baudrate, self.bytesize, self.parity,
                    self.stopbits, self.timeout, slave, register, code, r)
                self.addserial(ser_str)

    def setTarget(self, sv):
        if self.PID_slave_ID:
            multiplier = 1.
            if self.SVmultiplier == 1:
                multiplier = 10.
            elif self.SVmultiplier == 2:
                multiplier = 100.
            self.writeSingleRegister(self.PID_slave_ID, self.PID_SV_register,
                                     int(round(sv * multiplier)))

    def setPID(self, p, i, d):
        if self.PID_slave_ID and not (self.PID_p_register ==
                                      self.PID_i_register ==
                                      self.PID_d_register == 0):
            multiplier = 1.
            if self.PIDmultiplier == 1:
                multiplier = 10.
            elif self.PIDmultiplier == 2:
                multiplier = 100.
            self.writeSingleRegister(self.PID_slave_ID, self.PID_p_register,
                                     p * multiplier)
            self.writeSingleRegister(self.PID_slave_ID, self.PID_i_register,
                                     i * multiplier)
            self.writeSingleRegister(self.PID_slave_ID, self.PID_d_register,
                                     d * multiplier)
Esempio n. 9
0
class modbusport(object):
    """ this class handles the communications with all the modbus devices"""
    def __init__(self, sendmessage, adderror, addserial):
        self.sendmessage = sendmessage  # function to create an Artisan message to the user in the message line
        self.adderror = adderror  # signal an error to the user
        self.addserial = addserial  # add to serial log

        # retries
        self.readRetries = 1
        #default initial settings. They are changed by settingsload() at initiation of program acording to the device chosen
        self.comport = "COM5"  #NOTE: this string should not be translated.
        self.baudrate = 115200
        self.bytesize = 8
        self.parity = 'N'
        self.stopbits = 1
        self.timeout = 1.0
        self.PID_slave_ID = 0
        self.PID_SV_register = 0
        self.PID_p_register = 0
        self.PID_i_register = 0
        self.PID_d_register = 0
        self.PID_ON_action = ""
        self.PID_OFF_action = ""
        self.input1slave = 0
        self.input1register = 0
        self.input1float = False
        self.input1bcd = False
        self.input1code = 3
        self.input1div = 0  # 0: none, 1: 1/10, 2:1/100
        self.input1mode = "C"
        self.input2slave = 0
        self.input2register = 0
        self.input2float = False
        self.input2bcd = False
        self.input2code = 3
        self.input2div = 0
        self.input2mode = "C"
        self.input3slave = 0
        self.input3register = 0
        self.input3float = False
        self.input3bcd = False
        self.input3code = 3
        self.input3div = 0
        self.input3mode = "C"
        self.input4slave = 0
        self.input4register = 0
        self.input4float = False
        self.input4bcd = False
        self.input4code = 3
        self.input4div = 0
        self.input4mode = "C"
        self.input5slave = 0
        self.input5register = 0
        self.input5float = False
        self.input5bcd = False
        self.input5code = 3
        self.input5div = 0
        self.input5mode = "C"
        self.input6slave = 0
        self.input6register = 0
        self.input6float = False
        self.input6bcd = False
        self.input6code = 3
        self.input6div = 0
        self.input6mode = "C"
        self.SVmultiplier = 0
        self.PIDmultiplier = 0
        self.byteorderLittle = False
        self.wordorderLittle = True
        self.master = None
        self.COMsemaphore = QSemaphore(1)
        self.host = '127.0.0.1'  # the TCP/UDP host
        self.port = 502  # the TCP/UDP port
        self.type = 0
        # type =
        #    0: Serial RTU
        #    1: Serial ASCII
        #    2: Serial Binary
        #    3: TCP
        #    4: UDP
        self.lastReadResult = 0  # this is set by eventaction following some custom button/slider Modbus actions with "read" command

        self.commError = False  # True after a communication error was detected and not yet cleared by receiving proper data

    # this garantees a minimum of 30 miliseconds between readings and 80ms between writes (according to the Modbus spec) on serial connections
    # this sleep delays between requests seems to be beneficial on slow RTU serial connections like those of the FZ-94
    def sleepBetween(self, write=False):
        if write:
            #            if self.type in [3,4]: # TCP or UDP
            #                time.sleep(0.040)
            pass  # handled in MODBUS lib
            #            else:
            time.sleep(0.035)
        else:
            if self.type in [
                    3, 4
            ]:  # delay between writes only on serial connections
                pass
            else:
                time.sleep(0.035)

    def address2register(self, addr, code=3):
        if code == 3 or code == 6:
            return addr - 40001
        else:
            return addr - 30001

    def isConnected(self):
        return not (self.master is None) and self.master.socket

    def disconnect(self):
        try:
            self.master.close()
        except Exception:
            pass
        self.master = None

    def connect(self):
        #        if self.master and not self.master.socket:
        #            self.master = None
        if self.master is None:
            self.commError = False
            try:
                # as in the following the port is None, no port is opened on creation of the (py)serial object
                if self.type == 1:  # Serial ASCII
                    from pymodbus.client.sync import ModbusSerialClient
                    self.master = ModbusSerialClient(method='ascii',
                                                     port=self.comport,
                                                     baudrate=self.baudrate,
                                                     bytesize=self.bytesize,
                                                     parity=self.parity,
                                                     stopbits=self.stopbits,
                                                     retry_on_empty=True,
                                                     timeout=self.timeout)
                elif self.type == 2:  # Serial Binary
                    from pymodbus.client.sync import ModbusSerialClient
                    self.master = ModbusSerialClient(method='binary',
                                                     port=self.comport,
                                                     baudrate=self.baudrate,
                                                     bytesize=self.bytesize,
                                                     parity=self.parity,
                                                     stopbits=self.stopbits,
                                                     retry_on_empty=True,
                                                     timeout=self.timeout)
                elif self.type == 3:  # TCP
                    from pymodbus.client.sync import ModbusTcpClient
                    try:
                        self.master = ModbusTcpClient(
                            host=self.host,
                            port=self.port,
                            retry_on_empty=True,
                            retries=1,
                            timeout=0.9,  #self.timeout
                        )
                        self.readRetries = 0
                    except:
                        self.master = ModbusTcpClient(
                            host=self.host,
                            port=self.port,
                        )
                elif self.type == 4:  # UDP
                    from pymodbus.client.sync import ModbusUdpClient
                    try:
                        self.master = ModbusUdpClient(
                            host=self.host,
                            port=self.port,
                            retry_on_empty=True,
                            retries=3,
                            timeout=0.7,  #self.timeout
                        )
                    except:  # older versions of pymodbus don't support the retries, timeout nor the retry_on_empty arguments
                        self.master = ModbusUdpClient(
                            host=self.host,
                            port=self.port,
                        )
                else:  # Serial RTU
                    from pymodbus.client.sync import ModbusSerialClient
                    self.master = ModbusSerialClient(method='rtu',
                                                     port=self.comport,
                                                     baudrate=self.baudrate,
                                                     bytesize=self.bytesize,
                                                     parity=self.parity,
                                                     stopbits=self.stopbits,
                                                     retry_on_empty=False,
                                                     timeout=self.timeout)
                    self.readRetries = 1
                self.master.connect()
                self.adderror(
                    QApplication.translate("Error Message",
                                           "Connected via MODBUS", None))
                time.sleep(.5)  # avoid possible hickups on startup
            except Exception as ex:
                _, _, exc_tb = sys.exc_info()
                self.adderror(
                    (QApplication.translate("Error Message", "Modbus Error:",
                                            None) + " connect() {0}").format(
                                                str(ex)), exc_tb.tb_lineno)

    # function 15 (Write Multiple Coils)
    def writeCoils(self, slave, register, values):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.master.write_coils(int(register),
                                    list(values),
                                    unit=int(slave))
            time.sleep(.3)  # avoid possible hickups on startup
        except Exception as ex:
            #            self.disconnect()
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeCoils() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # function 5 (Write Single Coil)
    def writeCoil(self, slave, register, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.master.write_coil(int(register), value, unit=int(slave))
            time.sleep(.3)  # avoid possible hickups on startup
        except Exception as ex:
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeCoil() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # write value to register on slave (function 6 for int or function 16 for float)
    # value can be one of string (containing an int or float), an int or a float
    def writeRegister(self, slave, register, value):
        #        print("writeRegister",slave,register,value)
        if stringp(value):
            if "." in value:
                self.writeWord(slave, register, value)
            else:
                self.writeSingleRegister(slave, register, value)
        elif isinstance(value, int):
            self.writeSingleRegister(slave, register, value)
        elif isinstance(value, float):
            self.writeWord(slave, register, value)

    # function 6 (Write Single Holding Register)
    def writeSingleRegister(self, slave, register, value):
        #        _logger.debug("writeSingleRegister(%d,%d,%d)" % (slave,register,value))
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.master.write_register(int(register),
                                       int(value),
                                       unit=int(slave))
            time.sleep(.03)  # avoid possible hickups on startup
        except Exception as ex:
            #            _logger.debug("writeSingleRegister exception: %s" % str(ex))
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeSingleRegister() {0}").format(str(ex)),
                exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # function 22 (Mask Write Register)
    def maskWriteRegister(self, slave, register, and_mask, or_mask):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.master.mask_write_register(int(register),
                                            int(and_mask),
                                            int(or_mask),
                                            unit=int(slave))
            time.sleep(.03)
        except Exception as ex:
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeMask() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # function 16 (Write Multiple Holding Registers)
    # values is a list of integers or one integer
    def writeRegisters(self, slave, register, values):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.master.write_registers(int(register), values, unit=int(slave))
            time.sleep(.03)
        except Exception as ex:
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeRegisters() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # function 16 (Write Multiple Holding Registers)
    # value=int or float
    # writes a single precision 32bit float (2-registers)
    def writeWord(self, slave, register, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            builder = getBinaryPayloadBuilder(self.byteorderLittle,
                                              self.wordorderLittle)
            builder.add_32bit_float(float(value))
            payload = builder.build()  # .tolist()
            self.master.write_registers(int(register),
                                        payload,
                                        unit=int(slave),
                                        skip_encode=True)
            time.sleep(.03)
        except Exception as ex:
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeWord() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # translates given int value int a 16bit BCD and writes it into one register
    def writeBCD(self, slave, register, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            builder = getBinaryPayloadBuilder(self.byteorderLittle,
                                              self.wordorderLittle)
            r = convert_to_bcd(int(value))
            builder.add_16bit_uint(r)
            payload = builder.build()  # .tolist()
            self.master.write_registers(int(register),
                                        payload,
                                        unit=int(slave),
                                        skip_encode=True)
            time.sleep(.03)
        except Exception as ex:
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeWord() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # function 3 (Read Multiple Holding Registers) and 4 (Read Input Registers)
    def readFloat(self, slave, register, code=3):
        r = None
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            retry = self.readRetries
            while True:
                if code == 3:
                    res = self.master.read_holding_registers(int(register),
                                                             2,
                                                             unit=int(slave))
                else:
                    res = self.master.read_input_registers(int(register),
                                                           2,
                                                           unit=int(slave))
                if res is None or res.isError():  # requires pymodbus v1.5.1
                    if retry > 0:
                        retry = retry - 1
                        #time.sleep(0.020)
                    else:
                        raise Exception("Exception response")
                else:
                    break
            decoder = getBinaryPayloadDecoderFromRegisters(
                res.registers, self.byteorderLittle, self.wordorderLittle)
            r = decoder.decode_32bit_float()
            if self.commError:  # we clear the previous error and send a message
                self.commError = False
                self.adderror(
                    QApplication.translate("Error Message",
                                           "Modbus Communication Resumed",
                                           None))
            return r
        except Exception:  # as ex:
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            self.disconnect()
            #            _, _, exc_tb = sys.exc_info()
            #            self.adderror((QApplication.translate("Error Message","Modbus Error:",None) + " readFloat() {0}").format(str(ex)),exc_tb.tb_lineno)
            self.adderror(
                QApplication.translate("Error Message",
                                       "Modbus Communication Error", None))
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            #note: logged chars should be unicode not binary
            settings = str(self.comport) + "," + str(
                self.baudrate) + "," + str(self.bytesize) + "," + str(
                    self.parity) + "," + str(self.stopbits) + "," + str(
                        self.timeout)
            ser_str = "MODBUS readFloat :" + settings + " || Slave = " + str(
                slave) + " || Register = " + str(
                    register) + " || Code = " + str(code)
            if r is not None:
                ser_str = ser_str + " || Rx = " + str(r)
            self.addserial(ser_str)

    # function 3 (Read Multiple Holding Registers) and 4 (Read Input Registers)
    def readBCD(self, slave, register, code=3):
        r = None
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            retry = self.readRetries
            while True:
                if code == 3:
                    res = self.master.read_holding_registers(int(register),
                                                             1,
                                                             unit=int(slave))
                else:
                    res = self.master.read_input_registers(int(register),
                                                           1,
                                                           unit=int(slave))
                if res is None or res.isError():  # requires pymodbus v1.5.1
                    if retry > 0:
                        retry = retry - 1
                        #time.sleep(0.020)
                    else:
                        raise Exception("Exception response")
                else:
                    break
            decoder = getBinaryPayloadDecoderFromRegisters(
                res.registers, self.byteorderLittle, self.wordorderLittle)
            r = decoder.decode_16bit_uint()
            if self.commError:  # we clear the previous error and send a message
                self.commError = False
                self.adderror(
                    QApplication.translate("Error Message",
                                           "Modbus Communication Resumed",
                                           None))
            time.sleep(
                0.020
            )  # we add a small sleep between requests to help out the slow Loring electronic
            return convert_from_bcd(r)
        except Exception:  # as ex:
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            self.disconnect()
            #            _, _, exc_tb = sys.exc_info()
            #            self.adderror((QApplication.translate("Error Message","Modbus Error:",None) + " readBCD() {0}").format(str(ex)),exc_tb.tb_lineno)
            self.adderror(
                QApplication.translate("Error Message",
                                       "Modbus Communication Error", None))
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            #note: logged chars should be unicode not binary
            settings = str(self.comport) + "," + str(
                self.baudrate) + "," + str(self.bytesize) + "," + str(
                    self.parity) + "," + str(self.stopbits) + "," + str(
                        self.timeout)
            ser_str = "MODBUS readBCD :" + settings + " || Slave = " + str(
                slave) + " || Register = " + str(
                    register) + " || Code = " + str(code)
            if r is not None:
                ser_str = ser_str + " || Rx = " + str(r)
            self.addserial(ser_str)

    # as readSingleRegister, but does not retry nor raise and error and returns a None instead
    # also does not reserve the port via a semaphore!
    def peekSingleRegister(self, slave, register, code=3):
        try:
            if code == 1:
                res = self.master.read_coils(int(register), 1, unit=int(slave))
            elif code == 2:
                res = self.master.read_discrete_inputs(int(register),
                                                       1,
                                                       unit=int(slave))
            elif code == 4:
                res = self.master.read_input_registers(int(register),
                                                       1,
                                                       unit=int(slave))
            else:  # code==3
                res = self.master.read_holding_registers(int(register),
                                                         1,
                                                         unit=int(slave))
        except Exception:
            res = None
        if res is not None and not res.isError():  # requires pymodbus v1.5.1
            if code in [1, 2]:
                if res is not None and res.bits[0]:
                    return 1
                else:
                    return 0
            else:
                decoder = getBinaryPayloadDecoderFromRegisters(
                    res.registers, self.byteorderLittle, self.wordorderLittle)
                r = decoder.decode_16bit_uint()
                return r
        else:
            return None

    # function 1 (Read Coil)
    # function 2 (Read Discrete Input)
    # function 3 (Read Multiple Holding Registers) and
    # function 4 (Read Input Registers)
    def readSingleRegister(self, slave, register, code=3):
        #        import logging
        #        logging.basicConfig()
        #        log = logging.getLogger()
        #        log.setLevel(logging.DEBUG)
        r = None
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            retry = self.readRetries
            while True:
                try:
                    if code == 1:
                        res = self.master.read_coils(int(register),
                                                     1,
                                                     unit=int(slave))
                    elif code == 2:
                        res = self.master.read_discrete_inputs(int(register),
                                                               1,
                                                               unit=int(slave))
                    elif code == 4:
                        res = self.master.read_input_registers(int(register),
                                                               1,
                                                               unit=int(slave))
                    else:  # code==3
                        res = self.master.read_holding_registers(
                            int(register), 1, unit=int(slave))
                except Exception:
                    res = None
                if res is None or res.isError():  # requires pymodbus v1.5.1
                    if retry > 0:
                        retry = retry - 1
                        time.sleep(0.020)
                    else:
                        raise Exception("Exception response")
                else:
                    break
            if code in [1, 2]:
                if res is not None and res.bits[0]:
                    r = 1
                else:
                    r = 0
                return r
            else:
                decoder = getBinaryPayloadDecoderFromRegisters(
                    res.registers, self.byteorderLittle, self.wordorderLittle)
                r = decoder.decode_16bit_uint()
                if self.commError:  # we clear the previous error and send a message
                    self.commError = False
                    self.adderror(
                        QApplication.translate("Error Message",
                                               "Modbus Communication Resumed",
                                               None))
                return r
        except Exception:  # as ex:
            #            self.disconnect()
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            _, _, exc_tb = sys.exc_info()
            #            self.adderror((QApplication.translate("Error Message","Modbus Error:",None) + " readSingleRegister() {0}").format(str(ex)),exc_tb.tb_lineno)
            self.adderror(
                QApplication.translate("Error Message",
                                       "Modbus Communication Error", None))
            self.commError = True
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            #note: logged chars should be unicode not binary
            settings = str(self.comport) + "," + str(
                self.baudrate) + "," + str(self.bytesize) + "," + str(
                    self.parity) + "," + str(self.stopbits) + "," + str(
                        self.timeout)
            ser_str = "MODBUS readSingleRegister :" + settings + " || Slave = " + str(
                slave) + " || Register = " + str(
                    register) + " || Code = " + str(code)
            if r is not None:
                ser_str = ser_str + " || Rx = " + str(r)
            self.addserial(ser_str)

    def setTarget(self, sv):
        if self.PID_slave_ID:
            multiplier = 1.
            if self.SVmultiplier == 1:
                multiplier = 10.
            elif self.SVmultiplier == 2:
                multiplier = 100.
            self.writeSingleRegister(self.PID_slave_ID, self.PID_SV_register,
                                     int(round(sv * multiplier)))

    def setPID(self, p, i, d):
        if self.PID_slave_ID and not (self.PID_p_register ==
                                      self.PID_i_register ==
                                      self.PID_d_register == 0):
            multiplier = 1.
            if self.PIDmultiplier == 1:
                multiplier = 10.
            elif self.PIDmultiplier == 2:
                multiplier = 100.
            self.writeSingleRegister(self.PID_slave_ID, self.PID_p_register,
                                     p * multiplier)
            self.writeSingleRegister(self.PID_slave_ID, self.PID_i_register,
                                     i * multiplier)
            self.writeSingleRegister(self.PID_slave_ID, self.PID_d_register,
                                     d * multiplier)
Esempio n. 10
0
class PhidgetManager():

    def __init__(self):
        # a dictionary associating all physical attached Phidget channels
        # to their availablility state:
        #    True: available for attach to a software channel
        #    False: occupied and connected to a software channel
        # access to this dict is protected by the managersemaphore and
        # should happen only via the methods addChannel and deleteChannel
        self.attachedPhidgetChannels = {}
        self.managersemaphore = QSemaphore(1)
        self.manager = Manager()
        self.manager.setOnAttachHandler(self.attachHandler)
        self.manager.setOnDetachHandler(self.detachHandler)
        self.manager.open()
        
    def close(self):
        self.manager.close()
        self.attachedPhidgetChannels.clear()
        
    def attachHandler(self,_,attachedChannel):
        try:
            if attachedChannel.getParent().getDeviceClass() != DeviceClass.PHIDCLASS_HUB:
                # we do not add the hub itself
                self.addChannel(attachedChannel)
        except:
            pass
    
    def detachHandler(self,_,attachedChannel):
        try:
            self.deleteChannel(attachedChannel)
        except:
            pass
    
    def addChannel(self,channel):
        try:
            self.managersemaphore.acquire(1)
            state = True
            try:
                # reserve all channels with the same hubport on the same hub
                hub = channel.getHub()
                hubport = channel.getHubPort()
                hupportdevice = bool(channel.getIsHubPortDevice() == 0) # it is not a direct hubport channel
                for k, _ in self.attachedPhidgetChannels.items():
                    try:
                        khub = k.getHub()
                        khubport = k.getHubPort()
                        if khub == hub and khubport == hubport:
                            if hupportdevice:
                                if k.getIsHubPortDevice() != 0:
                                    self.attachedPhidgetChannels[k] = False
                                #else:
                                #  other is also a VINT device. Do nothing
                            else:
                                if k.getIsHubPortDevice() == 0:
                                    # there is a port registered with connected VINT device we deactivate this hubport channel
                                    state = False
                                #else:
                                #   do nothing
                    except:
                        pass
            except:
                pass
            self.attachedPhidgetChannels[channel] = state
        except Exception:
            pass
        finally:
            if self.managersemaphore.available() < 1:
                self.managersemaphore.release(1)

    def deleteChannel(self,channel):
        try:
            self.managersemaphore.acquire(1)
            # if channel is a VINT device, release all HUBport channels that were blocked by this VINT device
            try:
                hub = channel.getHub()
                hubport = channel.getHubPort()
                hupportdevice = bool(channel.getIsHubPortDevice() == 0) # it is not a direct hubport channel
                if hupportdevice:
                    for k, _ in self.attachedPhidgetChannels.items():
                        if k != channel:
                            try:
                                khub = k.getHub()
                                khubport = k.getHubPort()
                                if khub == hub and khubport == hubport:                                    
                                    self.attachedPhidgetChannels[k] = True
                            except:
                                pass
            except:
                pass
            self.attachedPhidgetChannels.pop(channel, None)
        except Exception:
            pass
        finally:
            if self.managersemaphore.available() < 1:
                self.managersemaphore.release(1)
                
    def getChannel(self,serial,port,channel,phidget_class_name,device_id,remote,remoteOnly):
        try:
            self.managersemaphore.acquire(1)
            if device_id in [DeviceID.PHIDID_HUB0000]:
                # we are looking for HUB ports
                hub = 1
            else:
                hub = 0
            for k, _ in self.attachedPhidgetChannels.items():
                if k.getIsHubPortDevice() or k.getDeviceClass() == DeviceClass.PHIDCLASS_VINT:
                    kport = k.getHubPort()
                else:
                    kport = None
                if k.getDeviceSerialNumber() == serial and (port is None or kport == port) and \
                    (hub or (k.getDeviceID() == device_id)) and \
                    ((remote and not remoteOnly) or (not remote and k.getIsLocal()) or (remote and remoteOnly and not k.getIsLocal())) and \
                    k.getChannelClassName() == phidget_class_name and \
                    k.getChannel() == channel:
                    return k
            return None
        except Exception:
            return None
        finally:
            if self.managersemaphore.available() < 1:
                self.managersemaphore.release(1)
                
    def reserveSerialPort(self,serial,port,channel,phidget_class_name,device_id,remote=False,remoteOnly=False):
        chnl = self.getChannel(serial,port,channel,phidget_class_name,device_id,remote,remoteOnly)
        self.reserveChannel(chnl)
        
    def releaseSerialPort(self,serial,port,channel,phidget_class_name,device_id,remote=False,remoteOnly=False):
        chnl = self.getChannel(serial,port,channel,phidget_class_name,device_id,remote,remoteOnly)
        self.releaseChannel(chnl)

    # should be called from the attach handler that binds this hardware channel to a software channel
    def reserveChannel(self,channel):
        try:
            self.managersemaphore.acquire(1)
            if channel is not None and channel in self.attachedPhidgetChannels:
                self.attachedPhidgetChannels[channel] = False
                if channel.getIsHubPortDevice():
                    hub = channel.getHub()
                    port = channel.getHubPort()
                    # reserve also all other channels with that hub/port combination
                    for k, _ in self.attachedPhidgetChannels.items():
                        try:
                            if k.getHub() == hub and k.getHubPort() == port:
                                self.attachedPhidgetChannels[k] = False
                        except:
                            pass
                #else:
                #  not a HUB port
        except Exception:
            pass
        finally:
            if self.managersemaphore.available() < 1:
                self.managersemaphore.release(1)

    # should be called from the detach handler that releases this hardware channel fron a software channel
    def releaseChannel(self,channel):
        try:
            self.managersemaphore.acquire(1)
            if channel is not None and channel in self.attachedPhidgetChannels:
                self.attachedPhidgetChannels[channel] = True
                if channel.getIsHubPortDevice():
                    hub = channel.getHub()
                    port = channel.getHubPort()
                    # enable also all other channels with that hub/port combination
                    for k, _ in self.attachedPhidgetChannels.items():
                        try:
                            if k.getHub() == hub and k.getHubPort() == port:
                                self.attachedPhidgetChannels[k] = True
                        except:
                            pass                
        except Exception:
            pass
        finally:
            if self.managersemaphore.available() < 1:
                self.managersemaphore.release(1)
                
#    def print_list(self,items):
#        for k,v in items:
#            print(v,k.getDeviceSerialNumber(),k.getDeviceClass(),k.getDeviceClassName(),k.getDeviceName(),k.getDeviceSKU(),k.getChannelClassName(),k.getDeviceID(),k.getIsHubPortDevice(),"port: ",k.getHubPort(),"ch: ", k.getChannel(), "local: ", k.getIsLocal())
#
#    def print_list2(self,items):
#        for k in items:
#            print(k.getDeviceSerialNumber(),k.getChannelClassName(),k.getDeviceID(),k.getIsHubPortDevice(),"port: ", k.getHubPort(),"ch: ",k.getChannel(), "local: ", k.getIsLocal())

    # returns the first matching Phidget channel and reserves it
    def getFirstMatchingPhidget(self,phidget_class_name,device_id,channel=None,remote=False,remoteOnly=False):
        try:
            self.managersemaphore.acquire(1)
            if device_id in [
                    DeviceID.PHIDID_HUB0000,
                    DeviceID.PHIDID_DIGITALINPUT_PORT,
                    DeviceID.PHIDID_DIGITALOUTPUT_PORT,
                    DeviceID.PHIDID_VOLTAGEINPUT_PORT,
                    DeviceID.PHIDID_VOLTAGERATIOINPUT_PORT]:
                # we are looking for HUB ports
                hub = 1
            else:
                hub = 0

#            self.print_list(self.attachedPhidgetChannels.items())

            # get list of all matching phidget channels
            matching_channels = [k for k, v in self.attachedPhidgetChannels.items() if v and \
                (hub or (k.getDeviceID() == device_id)) and \
                ((remote and not remoteOnly) or (not remote and k.getIsLocal()) or (remote and remoteOnly and not k.getIsLocal())) and \
                k.getChannelClassName() == phidget_class_name and \
                (channel is None or (not hub and channel == k.getChannel()) or (hub and k.getIsHubPortDevice() and k.getHubPort() == channel))]

#            self.print_list2(matching_channels)

            # sort by serial number (local first)
            matching_channels.sort(key=lambda x:x.getDeviceSerialNumber())
            # return smallest / first item
            if len(matching_channels) > 0:
                p = matching_channels[0]
                if p.getIsHubPortDevice() or p.getDeviceClass() == DeviceClass.PHIDCLASS_VINT:
                    port = p.getHubPort()
                else:
                    port = None
                return p.getDeviceSerialNumber(), port
            else:
                return None, None
        except Exception:
            return None, None
        finally:
            if self.managersemaphore.available() < 1:
                self.managersemaphore.release(1)
Esempio n. 11
0
class s7port(object):
    def __init__(self, sendmessage, adderror, addserial):
        self.sendmessage = sendmessage  # function to create an Artisan message to the user in the message line
        self.adderror = adderror  # signal an error to the user
        self.addserial = addserial  # add to serial log

        self.readRetries = 1
        self.channels = 8  # maximal number of S7 channels
        self.host = '127.0.0.1'  # the TCP host
        self.port = 102  # the TCP port
        self.rack = 0  # 0,..,7
        self.slot = 0  # 0,..,31

        self.lastReadResult = 0  # this is set by eventaction following some custom button/slider S/ actions with "read" command

        self.area = [0] * self.channels
        self.db_nr = [1] * self.channels
        self.start = [0] * self.channels
        self.type = [0] * self.channels
        self.mode = [
            0
        ] * self.channels  # temp mode is an int here, 0:__,1:C,2:F (this is different than other places)
        self.div = [0] * self.channels

        self.PID_area = 0
        self.PID_db_nr = 0
        self.PID_SV_register = 0
        self.PID_p_register = 0
        self.PID_i_register = 0
        self.PID_d_register = 0
        self.PID_ON_action = ""
        self.PID_OFF_action = ""
        self.SVmultiplier = 0
        self.PIDmultiplier = 0

        self.COMsemaphore = QSemaphore(1)

        self.areas = [
            0x81,  # PE
            0x82,  # PA
            0x83,  # MK
            0x1C,  # CT
            0x1D,  # TM
            0x84,  # DB
        ]

        self.plc = None
        self.commError = False  # True after a communication error was detected and not yet cleared by receiving proper data

################
# conversion methods copied from s7:util.py

    def set_int(self, _bytearray, byte_index, _int):
        """
        Set value in bytearray to int
        """
        # make sure were dealing with an int
        _int = int(_int)
        _bytes = struct.unpack('2B', struct.pack('>h', _int))
        _bytearray[byte_index:byte_index + 2] = _bytes

    def get_int(self, _bytearray, byte_index):
        """
        Get int value from bytearray.
    
        int are represented in two bytes
        """
        data = _bytearray[byte_index:byte_index + 2]
        data[1] = data[
            1] & 0xFF  # added to fix a conversion problem: see https://github.com/gijzelaerr/python-snap7/issues/101
        value = struct.unpack('>h', struct.pack('2B', *data))[0]
        return value

    def set_real(self, _bytearray, byte_index, real):
        """
        Set Real value
    
        make 4 byte data from real
    
        """
        real = float(real)
        real = struct.pack('>f', real)
        _bytes = struct.unpack('4B', real)
        for i, b in enumerate(_bytes):
            _bytearray[byte_index + i] = b

    def get_real(self, _bytearray, byte_index):
        """
        Get real value. create float from 4 bytes
        """
        x = _bytearray[byte_index:byte_index + 4]
        real = struct.unpack('>f', struct.pack('4B', *x))[0]
        return real

################

    def setPID(self, p, i, d, PIDmultiplier):
        if self.PID_area and not (self.PID_p_register == self.PID_i_register ==
                                  self.PID_d_register == 0):
            multiplier = 1.
            if PIDmultiplier == 1:
                PIDmultiplier = 10.
            elif PIDmultiplier == 2:
                multiplier = 100.
            self.writeInt(self.PID_area - 1, self.PID_db_nr,
                          self.PID_p_register, p * multiplier)
            self.writeInt(self.PID_area - 1, self.PID_area, self.PID_db_nr,
                          self.PID_i_register, i * multiplier)
            self.writeInt(self.PID_area - 1, self.PID_area, self.PID_db_nr,
                          self.PID_d_register, d * multiplier)

    def setTarget(self, sv, SVmultiplier):
        if self.PID_area:
            multiplier = 1.
            if SVmultiplier == 1:
                multiplier = 10.
            elif SVmultiplier == 2:
                multiplier = 100.
            self.writeInt(self.PID_area - 1, self.PID_db_nr,
                          self.PID_SV_register, int(round(sv * multiplier)))

    def isConnected(self):
        return not (self.plc is None) and self.plc.get_connected()

    def disconnect(self):
        if self.isConnected():
            try:
                self.plc.disconnect()
                self.plc.destroy()
                self.plc = None
            except Exception:
                pass

    def connect(self):
        from artisanlib.s7client import S7Client
        from snap7.common import load_library as load_snap7_library
        # first load shared lib if needed
        platf = str(platform.system())
        if platf in ['Windows', 'Linux'] and artisanlib.util.appFrozen():
            libpath = os.path.dirname(sys.executable)
            if platf == 'Linux':
                snap7dll = os.path.join(libpath, "libsnap7.so")
            else:  # Windows:
                snap7dll = os.path.join(libpath, "snap7.dll")
            load_snap7_library(snap7dll)  # will ensure to load it only once
        # next reset client instance if not yet connected to ensure a fresh start
        if self.plc and not self.plc.get_connected():
            self.plc = None
        # connect if not yet connected
        if self.plc is None:
            self.plc = S7Client()
            with suppress_stdout_stderr():
                time.sleep(0.4)
                self.plc.connect(self.host, self.rack, self.slot, self.port)
                time.sleep(0.4)
            if self.plc.get_connected():
                self.sendmessage(
                    QApplication.translate("Message", "S7 Connected", None))
                time.sleep(0.7)
            else:
                time.sleep(0.6)
                self.plc = S7Client()
                # we try a second time
                with suppress_stdout_stderr():
                    time.sleep(0.4)
                    self.plc.connect(self.host, self.rack, self.slot,
                                     self.port)
                    time.sleep(0.4)
                if self.plc.get_connected():
                    self.sendmessage(
                        QApplication.translate("Message", "S7 Connected", None)
                        + " (2)")
                    time.sleep(0.7)

    def writeFloat(self, area, dbnumber, start, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if self.plc is not None and self.plc.get_connected():
                with suppress_stdout_stderr():
                    ba = self.plc.read_area(self.areas[area], dbnumber, start,
                                            4)
                    self.set_real(ba, 0, float(value))
                    self.plc.write_area(self.areas[area], dbnumber, start, ba)
            else:
                self.adderror(
                    (QApplication.translate("Error Message", "S7 Error:", None)
                     + " connecting to PLC failed"))
        except Exception as e:
            self.adderror(
                QApplication.translate("Error Message",
                                       "S7 Communication Error", None) +
                " writeFloat: " + str(e))
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            self.addserial("S7 writeFloat(" + str(area) + "," + str(dbnumber) +
                           "," + str(start) + "," + str(value) + ")")

    def writeInt(self, area, dbnumber, start, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if self.plc is not None and self.plc.get_connected():
                with suppress_stdout_stderr():
                    ba = self.plc.read_area(self.areas[area], dbnumber, start,
                                            2)
                    self.set_int(ba, 0, int(value))
                    self.plc.write_area(self.areas[area], dbnumber, start, ba)
            else:
                self.adderror(
                    (QApplication.translate("Error Message", "S7 Error:", None)
                     + " connecting to PLC failed"))
        except Exception as e:
            self.adderror(
                QApplication.translate("Error Message",
                                       "S7 Communication Error", None) +
                " writeInt: " + str(e))
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            self.addserial("S7 writeInt(" + str(area) + "," + str(dbnumber) +
                           "," + str(start) + "," + str(value) + ")")

    def readFloat(self, area, dbnumber, start):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if self.plc is not None and self.plc.get_connected():
                retry = self.readRetries
                res = None
                while True:
                    try:
                        with suppress_stdout_stderr():
                            res = self.plc.read_area(self.areas[area],
                                                     dbnumber, start, 4)
                    except:
                        res = None
                    if res is None:
                        if retry > 0:
                            retry = retry - 1
                        else:
                            raise Exception("Communication error")
                    else:
                        break
                if res is None:
                    return -1
                else:
                    if self.commError:  # we clear the previous error and send a message
                        self.commError = False
                        self.adderror(
                            QApplication.translate("Error Message",
                                                   "S7 Communication Resumed",
                                                   None))
                    return self.get_real(res, 0)
            else:
                self.commError = True
                self.adderror(
                    (QApplication.translate("Error Message", "S7 Error:", None)
                     + " connecting to PLC failed"))
                return -1
        except Exception as e:
            self.adderror(
                QApplication.translate("Error Message",
                                       "S7 Communication Error", None) +
                " readFloat: " + str(e))
            self.commError = True
            return -1
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            self.addserial("S7 readFloat(" + str(area) + "," + str(dbnumber) +
                           "," + str(start) + ")")

    def readInt(self, area, dbnumber, start):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if self.plc is not None and self.plc.get_connected():
                retry = self.readRetries
                res = None
                while True:
                    try:
                        with suppress_stdout_stderr():
                            res = self.plc.read_area(self.areas[area],
                                                     dbnumber, start, 2)
                    except Exception:
                        res = None
                    if res is None:
                        if retry > 0:
                            retry = retry - 1
                        else:
                            raise Exception("Communication error")
                    else:
                        break
                if res is None:
                    return -1
                else:
                    if self.commError:  # we clear the previous error and send a message
                        self.commError = False
                        self.adderror(
                            QApplication.translate("Error Message",
                                                   "S7 Communication Resumed",
                                                   None))
                    return self.get_int(res, 0)
            else:
                self.commError = True
                self.adderror(
                    (QApplication.translate("Error Message", "S7 Error:", None)
                     + " connecting to PLC failed"))
                return -1
        except Exception as e:
            self.adderror(
                QApplication.translate("Error Message",
                                       "S7 Communication Error", None) +
                " readInt: " + str(e))
            self.commError = True
            return -1
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            self.addserial("S7 readInt(" + str(area) + "," + str(dbnumber) +
                           "," + str(start) + ")")
Esempio n. 12
0
class Buffer(object):
    def __init__(self, buffer_size=5):
        self.buffer_size = buffer_size
        self.free_slots = QSemaphore(self.buffer_size)
        self.used_slots = QSemaphore(0)
        self.clear_buffer_add = QSemaphore(1)
        self.clear_buffer_get = QSemaphore(1)
        self.queue_mutex = QMutex()
        self.queue = Queue(self.buffer_size)

    def add(self, data, drop_if_full=False):
        self.clear_buffer_add.acquire()
        if drop_if_full:
            if self.free_slots.tryAcquire():
                self.queue_mutex.lock()
                self.queue.put(data)
                self.queue_mutex.unlock()
                self.used_slots.release()
        else:
            self.free_slots.acquire()
            self.queue_mutex.lock()
            self.queue.put(data)
            self.queue_mutex.unlock()
            self.used_slots.release()

        self.clear_buffer_add.release()

    def get(self):
        # acquire semaphores
        self.clear_buffer_get.acquire()
        self.used_slots.acquire()
        self.queue_mutex.lock()
        data = self.queue.get()
        self.queue_mutex.unlock()
        # release semaphores
        self.free_slots.release()
        self.clear_buffer_get.release()
        # return item to caller
        return data

    def clear(self):
        # check if buffer contains items
        if self.queue.qsize() > 0:
            # stop adding items to buffer (will return false if an item is currently being added to the buffer)
            if self.clear_buffer_add.tryAcquire():
                # stop taking items from buffer (will return false if an item is currently being taken from the buffer)
                if self.clear_buffer_get.tryAcquire():
                    # release all remaining slots in queue
                    self.free_slots.release(self.queue.qsize())
                    # acquire all queue slots
                    self.free_slots.acquire(self.buffer_size)
                    # reset used_slots to zero
                    self.used_slots.acquire(self.queue.qsize())
                    # clear buffer
                    for _ in range(self.queue.qsize()):
                        self.queue.get()
                    # release all slots
                    self.free_slots.release(self.buffer_size)
                    # allow get method to resume
                    self.clear_buffer_get.release()
                else:
                    return False
                # allow add method to resume
                self.clear_buffer_add.release()
                return True
            else:
                return False
        else:
            return False

    def size(self):
        return self.queue.qsize()

    def maxsize(self):
        return self.buffer_size

    def isfull(self):
        return self.queue.qsize() == self.buffer_size

    def isempty(self):
        return self.queue.qsize() == 0
Esempio n. 13
0
    def run_process_conformity(self, progress_bar, params=None):
        results = []
        nc = cpu_count() - 1
        sem = QSemaphore(nc)
        bar_sem = QSemaphore(1)
        qDebug("CPU count " + str(nc + 1))
        parameters = params
        self.attached_progess_bar = progress_bar

        th_list = []
        n = len(self.case_event_model.cases)
        progress_bar.setMaximum(n)
        qDebug("Process Conformity -- Run")
        for (case, eventSeq) in self.case_event_model.cases.items():
            dummyCase = (case, eventSeq)
            #qDebug("Start -- Case %s" % str(case))
            th = ReplayThread(self.process_model, dummyCase, progress_bar,
                              bar_sem, results, sem, parameters)
            th.updateProgessbarSignal.connect(self.update_progress_bar_slot)
            th_list.append((th, case))
            th.start()
            sem.acquire()

        qDebug("All threads Created")

        for (th, case) in th_list:
            if th and th.isFinished():
                pass
            else:
                sem.acquire()
            th.updateProgessbarSignal.disconnect(self.update_progress_bar_slot)

        n = 0
        nCase = CaseAttributeModel(name="Conformity Process Result")
        i_itens = []
        legend = ["ID"]
        if bool(parameters["conformity"]):
            legend.append(["Conformity"])
        if bool(parameters["types"]):
            legend.append(["NonConformity Type"])
        if parameters["notes"] == "add":
            legend.append(["Notes"])

        for i in range(len(legend)):
            i_itens.append(i)
        nCase.add_legend(legend)

        string = "Params:"
        for key, value in parameters.items():
            string += "\t" + str(key) + " : " + str(value) + "\n"

        string += "==============Report===========\n"
        for item in results:
            liste = []
            string += str(item[0]) + " : "
            if parameters["conformity"]:
                liste.append(item[1])
                string += str(item[1]) + "\t"
            if parameters["types"]:
                if parameters["style"] == "int":
                    liste.append(item[2])
                    string += str(item[2]) + "\t"
                else:
                    if item[2] == 0:
                        liste.append("Conform")
                        string += format("%110s" % "Conform")
                    elif item[2] == 1:
                        liste.append("Non Conform: Inexistent Activity")
                        string += format("%110s" %
                                         "Non Conform: Inexistent Activity")
                    elif item[2] == 2:
                        liste.append("Non Conform: Inexistent Transition")
                        string += format("%110s" %
                                         "Non Conform: Inexistent Transition")
                    elif item[2] == 3:
                        liste.append(
                            "Non Conform: Inexistent Activity and Inexistent Transition"
                        )
                        string += format(
                            "%110s" %
                            "Non Conform: Inexistent Activity and Inexistent Transition"
                        )
                    elif item[2] == 4:
                        liste.append(
                            "Non Conform: Transition with Innapropriate Duration"
                        )
                        string += format(
                            "%110s" %
                            "Non Conform: Transition with Innapropriate Duration"
                        )
                    elif item[2] == 5:
                        liste.append(
                            "Non Conform: Transition with Innapropriate Duration and Inexistent Activity"
                        )
                        string += format(
                            "%110s" %
                            "Non Conform: Transition with Innapropriate Duration and Inexistent Activity"
                        )
                    elif item[2] == 6:
                        liste.append(
                            "Non Conform: Transition with Innapropriate Duration and Inexistent Transition"
                        )
                        string += format(
                            "%110s" %
                            "Non Conform: Transition with Innapropriate Duration and Inexistent Transition"
                        )
                    elif item[2] == 7:
                        liste.append(
                            "Non Conform: Transition with Innapropriate Duration, Inexistent Activity and Inexistent Transition"
                        )
                        string += format(
                            "%110s" %
                            "Non Conform: Transition with Innapropriate Duration, Inexistent Activity and Inexistent Transition"
                        )

            if parameters["notes"] == "add":
                liste.append(str(item[3]))
            if parameters["notes"] != "ignore":
                string += str(item[3]) + "\t"
            nCase.add_case(item[0], liste)
            string += "\n"

        if parameters["result_group"] == "merge":
            self.case_attribute_model[0].merge(nCase, i_itens)

        self.attached_progess_bar.setValue(0)
        self.attached_progess_bar = None

        self.signal_project_has_changed.emit()
        self.signal_conformity_algorithm_finished.emit(string)
Esempio n. 14
0
class Buffer(object):
    def __init__(self, size):
        # Save buffer size
        self.bufferSize = size
        # Create semaphores
        self.freeSlots = QSemaphore(self.bufferSize)
        self.usedSlots = QSemaphore(0)
        self.clearBuffer_add = QSemaphore(1)
        self.clearBuffer_get = QSemaphore(1)
        # Create mutex
        self.queueProtect = QMutex()
        # Create queue
        self.queue = Queue(self.bufferSize)

    def add(self, data, dropIfFull=False):
        # Acquire semaphore
        self.clearBuffer_add.acquire()
        # If dropping is enabled, do not block if buffer is full
        if dropIfFull:
            # Try and acquire semaphore to add item

            # Drop new frame
            # if self.freeSlots.tryAcquire():
            #     # Add item to queue
            #     self.queueProtect.lock()
            #     self.queue.put(data)
            #     self.queueProtect.unlock()
            #     # Release semaphore
            #     self.usedSlots.release()

            # Drop oldest frame
            ret = self.freeSlots.tryAcquire()
            self.queueProtect.lock()
            if not ret:
                self.queue.get()
            else:
                # Release semaphore
                self.usedSlots.release()
            self.queue.put(data)
            self.queueProtect.unlock()
        # If buffer is full, wait on semaphore
        else:
            # Acquire semaphore
            self.freeSlots.acquire()
            # Add item to queue
            self.queueProtect.lock()
            self.queue.put(data)
            self.queueProtect.unlock()
            # Release semaphore
            self.usedSlots.release()
        # Release semaphore
        self.clearBuffer_add.release()

    def get(self):
        # Acquire semaphores
        self.clearBuffer_get.acquire()
        self.usedSlots.acquire()
        # Take item from queue
        self.queueProtect.lock()
        data = self.queue.get()
        self.queueProtect.unlock()
        # Release semaphores
        self.freeSlots.release()
        self.clearBuffer_get.release()
        # Return item to caller
        return data

    def clear(self):
        # Check if buffer contains items
        if self.queue.qsize() > 0:
            # Stop adding items to buffer (will return false if an item is currently being added to the buffer)
            if self.clearBuffer_add.tryAcquire():
                # Stop taking items from buffer (will return false if an item is currently being taken from the buffer)
                if self.clearBuffer_get.tryAcquire():
                    # Release all remaining slots in queue
                    self.freeSlots.release(self.queue.qsize())
                    # Acquire all queue slots
                    self.freeSlots.acquire(self.bufferSize)
                    # Reset usedSlots to zero
                    self.usedSlots.acquire(self.queue.qsize())
                    # Clear buffer
                    for _ in range(self.queue.qsize()):
                        self.queue.get()
                    # Release all slots
                    self.freeSlots.release(self.bufferSize)
                    # Allow get method to resume
                    self.clearBuffer_get.release()
                else:
                    return False
                # Allow add method to resume
                self.clearBuffer_add.release()
                return True
            else:
                return False
        else:
            return False

    def size(self):
        return self.queue.qsize()

    def maxSize(self):
        return self.bufferSize

    def isFull(self):
        return self.queue.qsize() == self.bufferSize

    def isEmpty(self):
        return self.queue.qsize() == 0