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")
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
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")
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()
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()
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))
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")
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)
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)
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)
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) + ")")
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
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)
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