예제 #1
0
	def __validate_dispense_both_response( response_frame, template ):
		logger = Logger()
		logger.debug('Dispenser_LCDM4000: Validate response_frame to dispense_both : %s' % response_frame)

		template['status']				  = response_frame[12] in [0x30, 0x31]
		template['error_cause']			 = response_frame[12]

		template['upper_chk_requested_10']  = chr(response_frame[4])
		template['upper_chk_requested_1']   = chr(response_frame[5])
		template['upper_exit_requested_10'] = chr(response_frame[6])
		template['upper_exit_requested_1']  = chr(response_frame[7])

		template['lower_chk_requested_10']  = chr(response_frame[8])
		template['lower_chk_requested_1']   = chr(response_frame[9])
		template['lower_exit_requested_10'] = chr(response_frame[10])
		template['lower_exit_requested_1']  = chr(response_frame[11])

		template['upper_rejects_10']		= chr(response_frame[15])
		template['upper_rejects_1']		 = chr(response_frame[16])
		template['upper_status']			= chr(response_frame[13])

		template['lower_rejects_10']		= chr(response_frame[17])
		template['lower_rejects_1']		 = chr(response_frame[18])
		template['lower_status']			= chr(response_frame[14])

		return template
예제 #2
0
	def command_dispense_several(self, cassets_info, cassets_dispense):
		logger = Logger()
		logger.debug('Dispenser_LCDM2000: Amount will be dispensed from several cassets')

		# check limits
		upper_count_10  = min( int(cassets_dispense[0] * 1. / 10),		  self.dispense_limit_both_10 )
		upper_count_1   = min( cassets_dispense[0] - upper_count_10 * 10,   self.dispense_limit_both_1 )

		lower_count_10  = min( int(cassets_dispense[1] * 1. / 10),		  self.dispense_limit_both_10 )
		lower_count_1   = min( cassets_dispense[1] - lower_count_10 * 10,   self.dispense_limit_both_1 )

		logger.debug('Dispenser_LCDM2000: upper_count_10 : %d'  % upper_count_10)
		logger.debug('Dispenser_LCDM2000: upper_count_1  : %d'  % upper_count_1)
		logger.debug('Dispenser_LCDM2000: lower_count_10 : %d'  % lower_count_10)
		logger.debug('Dispenser_LCDM2000: lower_count_1  : %d'  % lower_count_1)

		upper_count_10  = ord("%s" % upper_count_10)
		upper_count_1   = ord("%s" % upper_count_1)
		lower_count_10  = ord("%s" % lower_count_10)
		lower_count_1   = ord("%s" % lower_count_1)

		return [
			0x04,0x50,0x02,0x56,upper_count_10,upper_count_1,lower_count_10,lower_count_1,0x03,
			0x04^0x50^0x02^0x56^upper_count_10^upper_count_1^lower_count_10^lower_count_1^0x03
		]
예제 #3
0
파일: Db.py 프로젝트: w1r2p1/creten
    def init(engineType, user, password, host, port, db, debug=False):
        Db.engineType = engineType
        Db.user = user
        Db.password = password
        Db.port = port
        Db.host = host
        Db.db = db

        log = Logger()

        if engineType == 'sqlite':
            engineConnStr = engineType + ':///' + db
            engineConnStrLog = engineConnStr
        else:
            engineConnStr = engineType + '://' + user + ':' + password + '@' + host + ':' + str(
                port) + '/' + db
            engineConnStrLog = engineType + '://' + user + '@' + host + ':' + str(
                port) + '/' + db

        log.info('Db connection: ' + engineConnStrLog)
        log.debug('Db connection full: ' + engineConnStr)

        engine = create_engine(engineConnStr, echo=debug)
        session_factory = sessionmaker(bind=engine)
        Db.Session = scoped_session(session_factory)

        Db.Base = declarative_base(bind=engine)
        Db.metadata = Db.Base.metadata
예제 #4
0
    def __validate_dispense_both_response(response_frame, template):
        logger = Logger()
        logger.debug(
            'Dispenser_LCDM4000: Validate response_frame to dispense_both : %s'
            % response_frame)

        template['status'] = response_frame[12] in [0x30, 0x31]
        template['error_cause'] = response_frame[12]

        template['upper_chk_requested_10'] = chr(response_frame[4])
        template['upper_chk_requested_1'] = chr(response_frame[5])
        template['upper_exit_requested_10'] = chr(response_frame[6])
        template['upper_exit_requested_1'] = chr(response_frame[7])

        template['lower_chk_requested_10'] = chr(response_frame[8])
        template['lower_chk_requested_1'] = chr(response_frame[9])
        template['lower_exit_requested_10'] = chr(response_frame[10])
        template['lower_exit_requested_1'] = chr(response_frame[11])

        template['upper_rejects_10'] = chr(response_frame[15])
        template['upper_rejects_1'] = chr(response_frame[16])
        template['upper_status'] = chr(response_frame[13])

        template['lower_rejects_10'] = chr(response_frame[17])
        template['lower_rejects_1'] = chr(response_frame[18])
        template['lower_status'] = chr(response_frame[14])

        return template
예제 #5
0
	def validate_status_response(self, status_frame):
		logger = Logger()
		logger.debug('Dispenser_LCDM1000: Validate status_frame %s. Len is %d' % (status_frame, len(status_frame)))

		# Pop ACK
		status_frame.pop(0)

		response	= {
			'status'		: True,
			'error_cause'   : "%X" % status_frame[5] ,
			'sensor_0'	  : "%X" % status_frame[6],
			'sensor_1'	  : "%X" % status_frame[7]
		}
		return response
예제 #6
0
	def command_dispense_single(self, cassets_info, id_cash_box, count):
		logger   = Logger()
		count_10 = min(int(count * 1. / 10), self.dispense_limit_single_10)
		count_1  = min(count - count_10 * 10, self.dispense_limit_single_1)
		
		logger.debug( 'Corrected counts: 10 (%d), 1 (%d)' % (count_10, count_1) )

		count_10 = ord("%s" % count_10)
		count_1 = ord("%s" % count_1)

		return [
			0x04,0x50,0x02,0x45,count_10,count_1,0x03,
			0x04^0x50^0x02^0x45^count_10^count_1^0x03
		]
예제 #7
0
    def command_dispense_several(self, cassets_info, cassets_dispense):
        logger = Logger()
        logger.debug(
            'Dispenser_LCDM4000: Amount will be dispensed from several cassettes'
        )

        # check limits
        upper_count = int(cassets_dispense.get(0, 0))
        m_upper_count = int(cassets_dispense.get(4, 0))
        m_lower_count = int(cassets_dispense.get(5, 0))
        lower_count = int(cassets_dispense.get(1, 0))

        logger.debug('Dispenser_LCDM4000: upper_count   : %d' % upper_count)
        logger.debug('Dispenser_LCDM4000: m_upper_count : %d' % m_upper_count)
        logger.debug('Dispenser_LCDM4000: m_lower_count : %d' % m_lower_count)
        logger.debug('Dispenser_LCDM4000: lower_count   : %d' % lower_count)
        '''
			The number of bills to be dispensed from cassette + 0x20
		'''
        upper_count += 0x20
        m_upper_count += 0x20
        m_lower_count += 0x20
        lower_count += 0x20

        return [
            0x04, 0x30, 0x02, 0x52, upper_count, m_upper_count, m_lower_count,
            lower_count, 0x20, 0x20, 0x20, 0x03,
            0x04 ^ 0x30 ^ 0x02 ^ 0x52 ^ upper_count ^ m_upper_count
            ^ m_lower_count ^ lower_count ^ 0x20 ^ 0x20 ^ 0x20 ^ 0x03
        ]
예제 #8
0
	def validate_status_response(self, status_frame):
		logger = Logger()
		logger.debug('Dispenser_LCDM2000: Validate status_frame %s. Len is %d' % (status_frame, len(status_frame)))

		# Pop ACK
		status_frame.pop(0)

		response	= {
			'status'		: True,
			'error_cause'   : "%X" % status_frame[5] ,
			'sensor_0'	  : "%X" % status_frame[6],
			'sensor_1'	  : "%X" % status_frame[7]
		}
		return response
예제 #9
0
	def command_dispense_several(self, cassets_info, cassets_dispense):
		logger = Logger()
		logger.debug('Dispenser_LCDM4000: Amount will be dispensed from several cassettes')

		# check limits
		upper_count   = int(cassets_dispense.get(0,0))
		m_upper_count = int(cassets_dispense.get(4,0))
		m_lower_count = int(cassets_dispense.get(5,0))
		lower_count   = int(cassets_dispense.get(1,0))

		logger.debug('Dispenser_LCDM4000: upper_count   : %d'  % upper_count)
		logger.debug('Dispenser_LCDM4000: m_upper_count : %d'  % m_upper_count)
		logger.debug('Dispenser_LCDM4000: m_lower_count : %d'  % m_lower_count)
		logger.debug('Dispenser_LCDM4000: lower_count   : %d'  % lower_count)

		'''
			The number of bills to be dispensed from cassette + 0x20
		'''
		upper_count	+= 0x20
		m_upper_count  += 0x20
		m_lower_count  += 0x20
		lower_count	+= 0x20

		return [
			0x04,0x30,0x02,0x52,upper_count,m_upper_count,m_lower_count,lower_count,0x20,0x20,0x20,0x03,
			0x04^0x30^0x02^0x52^upper_count^m_upper_count^m_lower_count^lower_count^0x20^0x20^0x20^0x03
		]
예제 #10
0
    def command_dispense_single(self, cassets_info, id_cash_box, count):
        logger = Logger()
        count_10 = min(int(count * 1. / 10), self.dispense_limit_single_10)
        count_1 = min(count - count_10 * 10, self.dispense_limit_single_1)

        logger.debug('Corrected counts: 10 (%d), 1 (%d)' % (count_10, count_1))

        count_10 = ord("%s" % count_10)
        count_1 = ord("%s" % count_1)

        return [
            0x04, 0x50, 0x02, 0x45, count_10, count_1, 0x03,
            0x04 ^ 0x50 ^ 0x02 ^ 0x45 ^ count_10 ^ count_1 ^ 0x03
        ]
예제 #11
0
	def validate_status_response(self, status_frame):
		logger = Logger()
		logger.debug('Dispenser_LCDM4000: Validate status_frame %s. Len is %d' % (status_frame, len(status_frame)))

		# Pop ACK
		status_frame.pop(0)

		response	= {
			'status'		: True,
			'error_cause'   : "%X" % status_frame[5],
			'sensor_0'	  : "%X" % status_frame[6],  # Upper
			'sensor_1'	  : "%X" % status_frame[9],  # Middle upper
			'sensor_2'	  : "%X" % status_frame[12], # Middle lower
			'sensor_3'	  : "%X" % status_frame[15], # Lower
		}
		return response
예제 #12
0
class StrategyExecutor(object):
    def __init__(self):
        self.strategies = []

        self.log = Logger()

    def addStrategy(self, strategy):
        self.strategies.append(strategy)

    def getStrategies(self):
        return self.strategies

    def execute(self, candle):
        self.log.debug('Processing candle: ' + str(candle))

        # TODO should catch exceptions otherwise one faulty strategy will break all the remaining ones
        for strategy in self.strategies:
            strategy.execute(candle)
예제 #13
0
    def validate_status_response(self, status_frame):
        logger = Logger()
        logger.debug(
            'Dispenser_LCDM4000: Validate status_frame %s. Len is %d' %
            (status_frame, len(status_frame)))

        # Pop ACK
        status_frame.pop(0)

        response = {
            'status': True,
            'error_cause': "%X" % status_frame[5],
            'sensor_0': "%X" % status_frame[6],  # Upper
            'sensor_1': "%X" % status_frame[9],  # Middle upper
            'sensor_2': "%X" % status_frame[12],  # Middle lower
            'sensor_3': "%X" % status_frame[15],  # Lower
        }
        return response
예제 #14
0
	def command_dispense_single(self, cassets_info, id_cash_box, count):

		logger = Logger()
		logger.debug('Dispenser_LCDM4000: Request to dispense will be converted from single to multi dispense')

		CASSETE_ID_UPPER		= 0
		CASSETE_ID_LOWER		= 1
		CASSETE_ID_MIDDLE_UPPER = 4
		CASSETE_ID_MIDDLE_LOWER = 5

		cassets_dispense = {
			CASSETE_ID_UPPER		: 0,
			CASSETE_ID_LOWER		: 0,
			CASSETE_ID_MIDDLE_UPPER : 0,
			CASSETE_ID_MIDDLE_LOWER : 0,
		}

		cassets_dispense[id_cash_box] = count
		return self.command_dispense_several(cassets_info, cassets_dispense)
예제 #15
0
	def __validate_dispense_single_response(response_frame, template):
		logger = Logger()
		response	= {}

		if response_frame[3] == 0x45:
			logger.debug('Dispenser_LCDM4000: upper cassete validator')
			chk_sensor_exit_10  = 'upper_chk_requested_10'
			chk_sensor_exit_1   = 'upper_chk_requested_1'
			exit_requested_10   = 'upper_exit_requested_10'
			exit_requested_1	= 'upper_exit_requested_1'
			rejects_10		  = 'upper_rejects_10'
			rejects_1		   = 'upper_rejects_1'
			status			  = 'upper_status'

		elif response_frame[3] == 0x55:
			logger.debug('Dispenser_LCDM4000: lower cassete validator')
			chk_sensor_exit_10  = 'lower_chk_requested_10'
			chk_sensor_exit_1   = 'lower_chk_requested_1'
			exit_requested_10   = 'lower_exit_requested_10'
			exit_requested_1	= 'lower_exit_requested_1'
			rejects_10		  = 'lower_rejects_10'
			rejects_1		   = 'lower_rejects_1'
			status			  = 'lower_status'
		else:
			logger.debug('Dispenser_LCDM4000: UNKNOWN cassete')
			logger.debug('Dispenser_LCDM4000: response_frame[3] is %s' % response_frame[3])
			logger.debug('Dispenser_LCDM4000: response_frame[3] is %X' % response_frame[3])


		template[chk_sensor_exit_10]	= chr(response_frame[4])
		template[chk_sensor_exit_1]	 = chr(response_frame[5])
		template[exit_requested_10]	 = chr(response_frame[6])
		template[exit_requested_1]	  = chr(response_frame[7])
		template[rejects_10]			= chr(response_frame[10])
		template[rejects_1]			 = chr(response_frame[11])
		template['error_cause']		 = response_frame[8]
		template[status]				= response_frame[9]

		logger.debug('Dispenser_LCDM4000: Validate response_frame to dispense_single : %s' % response_frame)

		return template
예제 #16
0
    def command_dispense_single(self, cassets_info, id_cash_box, count):

        logger = Logger()
        logger.debug(
            'Dispenser_LCDM4000: Request to dispense will be converted from single to multi dispense'
        )

        CASSETE_ID_UPPER = 0
        CASSETE_ID_LOWER = 1
        CASSETE_ID_MIDDLE_UPPER = 4
        CASSETE_ID_MIDDLE_LOWER = 5

        cassets_dispense = {
            CASSETE_ID_UPPER: 0,
            CASSETE_ID_LOWER: 0,
            CASSETE_ID_MIDDLE_UPPER: 0,
            CASSETE_ID_MIDDLE_LOWER: 0,
        }

        cassets_dispense[id_cash_box] = count
        return self.command_dispense_several(cassets_info, cassets_dispense)
예제 #17
0
    def validate_dispense_response(self, response_frame):
        logger = Logger()
        logger.debug(
            'Dispenser_LCDM1000: validate "Dispense" response frame %s' %
            response_frame)

        template = {
            'status': False,
            'error_cause': 0,
            'upper_chk_requested_10': 0,
            'upper_chk_requested_1': 0,
            'upper_exit_requested_10': 0,
            'upper_exit_requested_1': 0,
            'upper_rejects_10': 0,
            'upper_rejects_1': 0,
            'upper_status': 0,
            'lower_chk_requested_10': 0,
            'lower_chk_requested_1': 0,
            'lower_exit_requested_10': 0,
            'lower_exit_requested_1': 0,
            'lower_rejects_10': 0,
            'lower_rejects_1': 0,
            'lower_status': 0
        }

        # Hook
        response_frame[3] = int(response_frame[3])

        # Error while dispensing
        if len(response_frame) == 7:
            logger.debug('Dispenser_LCDM1000: len(response_frame) == 7')
            return False

        if len(response_frame) == 21:
            return self.__validate_dispense_both_response(
                response_frame, template)

        if response_frame[3] in [0x45, 0x55]:
            return self.__validate_dispense_single_response(
                response_frame, template)
예제 #18
0
	def __validate_dispense_single_response(response_frame, template):
		logger = Logger()

		if response_frame[3] == 0x45:
			logger.debug('Dispenser_LCDM2000: upper cassete validator')
			chk_sensor_exit_10  = 'upper_chk_requested_10'
			chk_sensor_exit_1   = 'upper_chk_requested_1'
			exit_requested_10   = 'upper_exit_requested_10'
			exit_requested_1	= 'upper_exit_requested_1'
			rejects_10		  = 'upper_rejects_10'
			rejects_1		   = 'upper_rejects_1'
			status			  = 'upper_status'

		elif response_frame[3] == 0x55:
			logger.debug('Dispenser_LCDM2000: lower cassete validator')
			chk_sensor_exit_10  = 'lower_chk_requested_10'
			chk_sensor_exit_1   = 'lower_chk_requested_1'
			exit_requested_10   = 'lower_exit_requested_10'
			exit_requested_1	= 'lower_exit_requested_1'
			rejects_10		  = 'lower_rejects_10'
			rejects_1		   = 'lower_rejects_1'
			status			  = 'lower_status'
		else:
			logger.debug('Dispenser_LCDM2000: UNKNOWN cassete')
			logger.debug('Dispenser_LCDM2000: response_frame[3] is %s' % response_frame[3])
			logger.debug('Dispenser_LCDM2000: response_frame[3] is %X' % response_frame[3])


		template[chk_sensor_exit_10]	= chr(response_frame[4])
		template[chk_sensor_exit_1]	 = chr(response_frame[5])
		template[exit_requested_10]	 = chr(response_frame[6])
		template[exit_requested_1]	  = chr(response_frame[7])
		template[rejects_10]			= chr(response_frame[10])
		template[rejects_1]			 = chr(response_frame[11])
		template['error_cause']		 = response_frame[8]
		template[status]				= response_frame[9]

		logger.debug('Dispenser_LCDM2000: Validate response_frame to dispense_single : %s' % response_frame)

		return template
예제 #19
0
	def validate_dispense_response(self, response_frame):
		logger = Logger()
		logger.debug('Dispenser_LCDM1000: validate "Dispense" response frame %s' % response_frame)

		template = {
			'status'					: False,
			'error_cause'			   : 0,
			'upper_chk_requested_10'	: 0,
			'upper_chk_requested_1'	 : 0,
			'upper_exit_requested_10'   : 0,
			'upper_exit_requested_1'	: 0,
			'upper_rejects_10'		  : 0,
			'upper_rejects_1'		   : 0,
			'upper_status'			  : 0,
			'lower_chk_requested_10'	: 0,
			'lower_chk_requested_1'	 : 0,
			'lower_exit_requested_10'   : 0,
			'lower_exit_requested_1'	: 0,
			'lower_rejects_10'		  : 0,
			'lower_rejects_1'		   : 0,
			'lower_status'			  : 0
		}


		# Hook
		response_frame[3] = int(response_frame[3])

		# Error while dispensing
		if len(response_frame) == 7:
			logger.debug('Dispenser_LCDM1000: len(response_frame) == 7')
			return False

		if len(response_frame) == 21:
			return self.__validate_dispense_both_response(response_frame, template)

		if response_frame[3] in [0x45, 0x55]:
			return self.__validate_dispense_single_response(response_frame, template)
예제 #20
0
class Cashcode_T1500 (Thread):
	
	parity			= 'N'
	stopbits		= 1
	bitesize		= 8
	baudrate		= 9600
	serial_timeout	= .5
	
	p_ack			= 0x00
	p_nck			= 0xFF
	p_sync			= 0x02
	p_adr			= 0x03
	
	c_reset			= 0x30
	c_status		= 0x31
	c_security		= 0x32
	c_poll			= 0x33
	c_bill_types	= 0x34
	c_stack			= 0x35
	c_return		= 0x36
	c_ident			= 0x37
	c_hold			= 0x38
	c_barcode		= 0x39
	c_barcode_extract	= 0x3A
	c_bill_table	= 0x41
	c_download		= 0x50
	c_crc32			= 0x51
	c_stats			= 0x60
	
	r_power_up				= 0x10
	r_power_up_validator	= 0x11
	r_power_up_stacker		= 0x12
	
	r_initialize	= 0x13
	r_idling		= 0x14
	r_accepting		= 0x15
	r_stacking		= 0x17
	r_returning		= 0x18
	r_unit_disabled	= 0x19
	r_holding		= 0x1A
	r_device_busy	= 0x1B
	
	#generic reject codes
	rj_reject		= 0x1C
	
	rj_drop_casset_full	= 0x41
	rj_drop_casset_out	= 0x42
	rj_validator_jammed	= 0x43
	rj_drop_casset_jammed	= 0x44
	rj_cheated			= 0x45
	rj_pause			= 0x46
	rf_failure			= 0x47
	
	#events
	re_escrow			= 0x80
	re_stacked			= 0x81
	re_returned			= 0x82

	# Errors
	e_insertation		= 0x60

	bill_types = {
		2: 10,
		3: 50,
		4: 100,
		5: 500,
		6: 1000,
		7: 5000
	}


	CRC_TABLE = [
		0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
		0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
		0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
		0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
		0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
		0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
		0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
		0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
		0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
		0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
		0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
		0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
		0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
		0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
		0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
		0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
		0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
		0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
		0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
		0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
		0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
		0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
		0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
		0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
		0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
		0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
		0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
		0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
		0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
		0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
		0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
		0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
	]

	__prefix	= '[Cashcode_t1500]'

	last_action = 0
	time_start	= 0
	timeout		= 0
	sleep_time	= .5
	running		= False
	events		= []
	
	def __init__ (self, device, baudrate = 9600, sleep_time = .5, timeout = 0):
		
		self.device, self.timeout = device, timeout
		self.logger = Logger()
		
		if baudrate:
			self.baudrate = baudrate
		
		if sleep_time > 0:
			self.sleep_time = sleep_time
		
		Thread.__init__(self)
	
	def connect(self):
		self.running = True
		
		try:
			self.serial = serial.serial_for_url(
				self.device,
				self.baudrate,
				parity	= self.parity,
				timeout = self.serial_timeout
			)
			
			self.check_status()
			self.logger.debug("Okay. Adapter has been connected to CASHCODE SM with data %s:%s" % (self.device, self.baudrate))
			
		except serial.SerialException as e:
			message = 'Error while connecting to bill acceptor'
			self.logger.warning(message)
			raise AcceptorError(message = message, code = 500)
		except Exception as e:
			message = 'Error while connecting to bill acceptor. Error details: %s' % e
			self.logger.warning(message)
			raise AcceptorError(message = message, code = 500)
		
		self.full_reset()
	
	def disconnect(self):
		self.running = False
		
		try:
			self.logger.debug('[Cashcode_T1500]: reset')
			self.full_reset()
			time.sleep(3)
			self.serial.close()
		except:
			self.logger.debug('[Cashcode_T1500]: stopping with error')
			pass
		self.logger.debug('[Cashcode_T1500]: serial port has been closed')

	def run(self):

		self.logger.debug('[Cashcode_T1500]: thread has ran')
		self.logger.debug('[Cashcode_T1500]: timeout is %s' % self.timeout)
		
		total_summ = 0
		response	= None
		self.last_action = time.time()
		self.enable_bill_types()
		self.check_status()

		while self._running(self.timeout):
			time.sleep(self.sleep_time)
			
			# Если родительский поток не забрал все сообщения
			if len( self.events ):
				continue
			
			try:
				response = self.wait(
					wait = [self.re_stacked],
					skip = [self.r_idling, self.r_initialize, self.r_accepting, self.r_stacking],
					timeout = self.timeout
				)
			except AcceptorError as e:
				message = 'Error while waiting %s. Response is %s. Error is %s' % (self.re_stacked, response, str(e.args))
				self.logger.debug(message)
				continue
			
			try:
				amount = self.bill_types[response[1]]
				total_summ += amount
				
				self.logger.debug('Response is %s' % response)
				self.logger.debug('Accepted amount is %d' % amount)
				self.send_ack()
				self.events.append({'id_trx': 0, 'count': 1, 'value': amount, 'total_amount': total_summ, 'sleep_time': self.sleep_time})
			except:
				pass
		
		self.logger.debug('[Cashcode_T1500]: run stop operations')
		self.disconnect()
	
	def pop_message(self):
		if not len(self.events):
			return False
		return self.events.pop(0)
	
	def _running(self, timeout):
		timestamp = int(time.time())
		
		if not self.running:
			self.logger.debug("Acceptor thread stopped by flag")
			return False
		
		if timeout > 0 and timestamp - self.last_action > timeout:
			self.logger.debug("Acceptor thread stopped by timeout")
			#print "timestamp: %s, last_action: %s, timeout: %s" % (timestamp, self.last_action, timeout)
			return False
		
		return True
	
	def __calc_crc(self, data):
		crc = 0;
		for i in data:
			crc = self.CRC_TABLE[ (crc ^ i) & 0xFF] ^ (crc >> 8);
		return crc;
	
	def _read_nbytes(self, length):
		return str2bytes(self.serial.read(length))
	
	def _read_byte(self):
		return ord(self.serial.read(1))
	
	#reads next 2 bytes (crc length)
	def _read_crc(self):
		return self._read_nbytes(2)
	
	#reads response packey, verify crc, check header(sync, adr)
	def _read_response(self):
		header = self._read_nbytes(3)
		
		try:
			message = 'Header error. Is Cashcode connected to %s? Header is %s' % (self.device, header)
			if (header[0] <> self.p_sync or header[1] <> self.p_adr):
				self.logger.debug( message )
				raise AcceptorError( message = message, code = 500 )
		except IndexError:
			message = 'Header error. Header is %s' % ( header )
			self.logger.debug( message )
			raise AcceptorError( message = message, code = 500 )
		
		packet	= self._read_nbytes( header[2] - 3 - 2 )
		crc	= self._read_crc()
		checkCrc = int2bytes( self.__calc_crc(header + packet) )
		
		if crc != checkCrc:
			self.logger.debug('%s: _read_response. Crc not satisfied their=%s , my=%s' % (self.__prefix, crc, checkCrc))
		
#		self.logger.debug('%s: _read_response. response(len=%d) dec=%s, hex=%s ' % (self.__prefix, len(packet), packet, ints2hex(packet) ))
		self.validate_statement(packet)
		return packet
	
	def free(self, t = 0.02):
		time.sleep(t)

	''' ******* SEND COMMANDS ************ '''
	def send_single_command(self, cmd, read_response = True):
		packet = [self.p_sync, self.p_adr, 0x06, cmd]
		packet = packet + int2bytes( self.__calc_crc(packet) )
		self.serial.write( serial.to_bytes( packet ) )

		if read_response:
			return self._read_response()
		return True

	def send_command(self, data, read_response = True):
		packet = [ self.p_sync, self.p_adr, 5 + len(data)] + data
		packet = packet+ int2bytes(self.__calc_crc(packet))

		self.serial.write(serial.to_bytes(packet) )

		if read_response:
			return self._read_response()
		return True

	def status(self):
		return self.check_status()

	''' ******* EXACT COMMANDS ************ '''
	def check_status(self):
		status_response = self.send_single_command( self.c_status, True )
		self.logger.debug( '%s: check_status: Status is "%s"' % (self.__prefix, status_response))
		self.send_ack()
		return status_response

	def poll(self):
		return self.send_single_command( self.c_poll )

	def full_reset(self):
		self.logger.debug( '%s: full_reset' % (self.__prefix) )
		return self.__is_ack( self.send_single_command(self.c_reset) )

	''' ******** ALIASES ************** '''
	def init_bill_types(self, b1, b2, b3, b4, b5, b6):
		init_response = self.send_command([self.c_bill_types, b3, b2, b1, b6, b5, b4])
		self.logger.debug( '%s: init_bill_types: Result is "%s"' % (self.__prefix, init_response))
		
		self.free()
		
		self.logger.debug( '%s: init_bill_types: poll' % (self.__prefix))
		self.poll()
		self.free()
		self.send_ack()
		self.wait( wait = [self.r_idling], skip = [self.r_initialize] )
		
		return True

	def enable_bill_types(self):
		self.logger.debug('[Cashcode_t1500->enable_bill_types]: Enable bill types')
		bills = 0
		for i in range(2,8):
			bills = bills | 1 << i
		return self.init_bill_types(bills,0,0,0,0,0)

	def disable_bill_types(self):
			return self.init_bills(0,0,0,0,0,0)

	def receive_start(self, timeout = 10):
		
		self.logger.debug('%s: Starting receive of banknotes.' % self.__prefix)
		self.last_action = time.time()
		
		if( not self.enable_bill_types() ):
			raise Exception('[Cashcode_t1500->receive_start]: Bill types was not setuped')

		while self._running( timeout ):

			response = self.wait(
				wait = [self.re_stacked],
				skip = [self.r_idling, self.r_initialize, self.r_accepting, self.r_stacking]
			)
			
			try:
				amount = self.bill_types[ response[1] ]
				self.logger.debug('Response is %s' % response)
				self.logger.debug('Accepted amount is %d' % amount)
				self.send_ack()
				return amount
			except:
				pass


		return 0

	def receive_commit(self):
		response = self.send_ack()
		self.logger.debug('%s: commit result is %s' % (self.__prefix, response) )
		
	def receive_rollback(self):
		response = self.send_single_command(self.c_return, read_response = True)
		self.logger.debug('%s: eject result is %s' % (self.__prefix, response) )
		
	def send_ack(self):
		return self.send_single_command(self.p_ack, False)

	def send_nck(self):
		return self.send_single_command(self.p_nck, False)

	def bill_table(self):
		response = self.send_single_command(self.c_bill_table)

		for i in range(0,23):
				bytes=response[i*5 : i*5+5]
				print 'bytes: %s, %s' % (bytes, byte2str(bytes[1:4]))

	def __is_ack(self, data):
		return len(data)==1 and data[0]==self.p_ack

	def __is_nck(self, data):
		return len(data)==1 and data[0]==self.p_nck

	def _stacked_handler(self):
		pass

	def _rejected_handler(self):
		pass

	def wait(self, wait=[], skip=[], timeout = 10):

		self.logger.debug('[Cashcode_t1500->wait] start waiting %s' % timeout)

		while self._running( timeout ):

			response = self.poll()

			if(response[0] in wait):
				self.logger.debug('Normal response waited')
				return response

			if(response[0] in skip):
				self.validate_statement( response )
				continue

			message = "[Cashcode_t1500->wait(%s, %s, %s)] got=%s" % (wait, skip, timeout, response)
			self.logger.debug( message )
			raise AcceptorError(message, response)

			self.free(.2)

	def validate_statement(self, packet):

		if packet == [self.r_power_up]:
			self.logger.debug('State is POWER_UP')
			return

		if packet == [self.r_accepting]:
			self.logger.debug('State is ACCEPTING')
			return

		if packet == [self.r_stacking]:
			self.logger.debug('State is STACKING')
			return

		if packet == [self.rj_reject, self.e_insertation]:
			self.logger.debug('State is Error. Details: [rj_reject, e_insertation]')
			return
예제 #21
0
class Cashcode_T1500(Thread):

    parity = 'N'
    stopbits = 1
    bitesize = 8
    baudrate = 9600
    serial_timeout = .5

    p_ack = 0x00
    p_nck = 0xFF
    p_sync = 0x02
    p_adr = 0x03

    c_reset = 0x30
    c_status = 0x31
    c_security = 0x32
    c_poll = 0x33
    c_bill_types = 0x34
    c_stack = 0x35
    c_return = 0x36
    c_ident = 0x37
    c_hold = 0x38
    c_barcode = 0x39
    c_barcode_extract = 0x3A
    c_bill_table = 0x41
    c_download = 0x50
    c_crc32 = 0x51
    c_stats = 0x60

    r_power_up = 0x10
    r_power_up_validator = 0x11
    r_power_up_stacker = 0x12

    r_initialize = 0x13
    r_idling = 0x14
    r_accepting = 0x15
    r_stacking = 0x17
    r_returning = 0x18
    r_unit_disabled = 0x19
    r_holding = 0x1A
    r_device_busy = 0x1B

    #generic reject codes
    rj_reject = 0x1C

    rj_drop_casset_full = 0x41
    rj_drop_casset_out = 0x42
    rj_validator_jammed = 0x43
    rj_drop_casset_jammed = 0x44
    rj_cheated = 0x45
    rj_pause = 0x46
    rf_failure = 0x47

    #events
    re_escrow = 0x80
    re_stacked = 0x81
    re_returned = 0x82

    # Errors
    e_insertation = 0x60

    bill_types = {2: 10, 3: 50, 4: 100, 5: 500, 6: 1000, 7: 5000}

    CRC_TABLE = [
        0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48,
        0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108,
        0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb,
        0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399,
        0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e,
        0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e,
        0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd,
        0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
        0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285,
        0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44,
        0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014,
        0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5,
        0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3,
        0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862,
        0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e,
        0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
        0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1,
        0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483,
        0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50,
        0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710,
        0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7,
        0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1,
        0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72,
        0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
        0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e,
        0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf,
        0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d,
        0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c,
        0x3de3, 0x2c6a, 0x1ef1, 0x0f78
    ]

    __prefix = '[Cashcode_t1500]'

    last_action = 0
    time_start = 0
    timeout = 0
    sleep_time = .5
    running = False
    events = []

    def __init__(self, device, baudrate=9600, sleep_time=.5, timeout=0):

        self.device, self.timeout = device, timeout
        self.logger = Logger()

        if baudrate:
            self.baudrate = baudrate

        if sleep_time > 0:
            self.sleep_time = sleep_time

        Thread.__init__(self)

    def connect(self):
        self.running = True

        try:
            self.serial = serial.serial_for_url(self.device,
                                                self.baudrate,
                                                parity=self.parity,
                                                timeout=self.serial_timeout)

            self.check_status()
            self.logger.debug(
                "Okay. Adapter has been connected to CASHCODE SM with data %s:%s"
                % (self.device, self.baudrate))

        except serial.SerialException as e:
            message = 'Error while connecting to bill acceptor'
            self.logger.warning(message)
            raise AcceptorError(message=message, code=500)
        except Exception as e:
            message = 'Error while connecting to bill acceptor. Error details: %s' % e
            self.logger.warning(message)
            raise AcceptorError(message=message, code=500)

        self.full_reset()

    def disconnect(self):
        self.running = False

        try:
            self.logger.debug('[Cashcode_T1500]: reset')
            self.full_reset()
            time.sleep(3)
            self.serial.close()
        except:
            self.logger.debug('[Cashcode_T1500]: stopping with error')
            pass
        self.logger.debug('[Cashcode_T1500]: serial port has been closed')

    def run(self):

        self.logger.debug('[Cashcode_T1500]: thread has ran')
        self.logger.debug('[Cashcode_T1500]: timeout is %s' % self.timeout)

        total_summ = 0
        response = None
        self.last_action = time.time()
        self.enable_bill_types()
        self.check_status()

        while self._running(self.timeout):
            time.sleep(self.sleep_time)

            # Если родительский поток не забрал все сообщения
            if len(self.events):
                continue

            try:
                response = self.wait(wait=[self.re_stacked],
                                     skip=[
                                         self.r_idling, self.r_initialize,
                                         self.r_accepting, self.r_stacking
                                     ],
                                     timeout=self.timeout)
            except AcceptorError as e:
                message = 'Error while waiting %s. Response is %s. Error is %s' % (
                    self.re_stacked, response, str(e.args))
                self.logger.debug(message)
                continue

            try:
                amount = self.bill_types[response[1]]
                total_summ += amount

                self.logger.debug('Response is %s' % response)
                self.logger.debug('Accepted amount is %d' % amount)
                self.send_ack()
                self.events.append({
                    'id_trx': 0,
                    'count': 1,
                    'value': amount,
                    'total_amount': total_summ,
                    'sleep_time': self.sleep_time
                })
            except:
                pass

        self.logger.debug('[Cashcode_T1500]: run stop operations')
        self.disconnect()

    def pop_message(self):
        if not len(self.events):
            return False
        return self.events.pop(0)

    def _running(self, timeout):
        timestamp = int(time.time())

        if not self.running:
            self.logger.debug("Acceptor thread stopped by flag")
            return False

        if timeout > 0 and timestamp - self.last_action > timeout:
            self.logger.debug("Acceptor thread stopped by timeout")
            #print "timestamp: %s, last_action: %s, timeout: %s" % (timestamp, self.last_action, timeout)
            return False

        return True

    def __calc_crc(self, data):
        crc = 0
        for i in data:
            crc = self.CRC_TABLE[(crc ^ i) & 0xFF] ^ (crc >> 8)
        return crc

    def _read_nbytes(self, length):
        return str2bytes(self.serial.read(length))

    def _read_byte(self):
        return ord(self.serial.read(1))

    #reads next 2 bytes (crc length)
    def _read_crc(self):
        return self._read_nbytes(2)

    #reads response packey, verify crc, check header(sync, adr)
    def _read_response(self):
        header = self._read_nbytes(3)

        try:
            message = 'Header error. Is Cashcode connected to %s? Header is %s' % (
                self.device, header)
            if (header[0] <> self.p_sync or header[1] <> self.p_adr):
                self.logger.debug(message)
                raise AcceptorError(message=message, code=500)
        except IndexError:
            message = 'Header error. Header is %s' % (header)
            self.logger.debug(message)
            raise AcceptorError(message=message, code=500)

        packet = self._read_nbytes(header[2] - 3 - 2)
        crc = self._read_crc()
        checkCrc = int2bytes(self.__calc_crc(header + packet))

        if crc != checkCrc:
            self.logger.debug(
                '%s: _read_response. Crc not satisfied their=%s , my=%s' %
                (self.__prefix, crc, checkCrc))

#		self.logger.debug('%s: _read_response. response(len=%d) dec=%s, hex=%s ' % (self.__prefix, len(packet), packet, ints2hex(packet) ))
        self.validate_statement(packet)
        return packet

    def free(self, t=0.02):
        time.sleep(t)

    ''' ******* SEND COMMANDS ************ '''

    def send_single_command(self, cmd, read_response=True):
        packet = [self.p_sync, self.p_adr, 0x06, cmd]
        packet = packet + int2bytes(self.__calc_crc(packet))
        self.serial.write(serial.to_bytes(packet))

        if read_response:
            return self._read_response()
        return True

    def send_command(self, data, read_response=True):
        packet = [self.p_sync, self.p_adr, 5 + len(data)] + data
        packet = packet + int2bytes(self.__calc_crc(packet))

        self.serial.write(serial.to_bytes(packet))

        if read_response:
            return self._read_response()
        return True

    def status(self):
        return self.check_status()

    ''' ******* EXACT COMMANDS ************ '''

    def check_status(self):
        status_response = self.send_single_command(self.c_status, True)
        self.logger.debug('%s: check_status: Status is "%s"' %
                          (self.__prefix, status_response))
        self.send_ack()
        return status_response

    def poll(self):
        return self.send_single_command(self.c_poll)

    def full_reset(self):
        self.logger.debug('%s: full_reset' % (self.__prefix))
        return self.__is_ack(self.send_single_command(self.c_reset))

    ''' ******** ALIASES ************** '''

    def init_bill_types(self, b1, b2, b3, b4, b5, b6):
        init_response = self.send_command(
            [self.c_bill_types, b3, b2, b1, b6, b5, b4])
        self.logger.debug('%s: init_bill_types: Result is "%s"' %
                          (self.__prefix, init_response))

        self.free()

        self.logger.debug('%s: init_bill_types: poll' % (self.__prefix))
        self.poll()
        self.free()
        self.send_ack()
        self.wait(wait=[self.r_idling], skip=[self.r_initialize])

        return True

    def enable_bill_types(self):
        self.logger.debug(
            '[Cashcode_t1500->enable_bill_types]: Enable bill types')
        bills = 0
        for i in range(2, 8):
            bills = bills | 1 << i
        return self.init_bill_types(bills, 0, 0, 0, 0, 0)

    def disable_bill_types(self):
        return self.init_bills(0, 0, 0, 0, 0, 0)

    def receive_start(self, timeout=10):

        self.logger.debug('%s: Starting receive of banknotes.' % self.__prefix)
        self.last_action = time.time()

        if (not self.enable_bill_types()):
            raise Exception(
                '[Cashcode_t1500->receive_start]: Bill types was not setuped')

        while self._running(timeout):

            response = self.wait(wait=[self.re_stacked],
                                 skip=[
                                     self.r_idling, self.r_initialize,
                                     self.r_accepting, self.r_stacking
                                 ])

            try:
                amount = self.bill_types[response[1]]
                self.logger.debug('Response is %s' % response)
                self.logger.debug('Accepted amount is %d' % amount)
                self.send_ack()
                return amount
            except:
                pass

        return 0

    def receive_commit(self):
        response = self.send_ack()
        self.logger.debug('%s: commit result is %s' %
                          (self.__prefix, response))

    def receive_rollback(self):
        response = self.send_single_command(self.c_return, read_response=True)
        self.logger.debug('%s: eject result is %s' % (self.__prefix, response))

    def send_ack(self):
        return self.send_single_command(self.p_ack, False)

    def send_nck(self):
        return self.send_single_command(self.p_nck, False)

    def bill_table(self):
        response = self.send_single_command(self.c_bill_table)

        for i in range(0, 23):
            bytes = response[i * 5:i * 5 + 5]
            print 'bytes: %s, %s' % (bytes, byte2str(bytes[1:4]))

    def __is_ack(self, data):
        return len(data) == 1 and data[0] == self.p_ack

    def __is_nck(self, data):
        return len(data) == 1 and data[0] == self.p_nck

    def _stacked_handler(self):
        pass

    def _rejected_handler(self):
        pass

    def wait(self, wait=[], skip=[], timeout=10):

        self.logger.debug('[Cashcode_t1500->wait] start waiting %s' % timeout)

        while self._running(timeout):

            response = self.poll()

            if (response[0] in wait):
                self.logger.debug('Normal response waited')
                return response

            if (response[0] in skip):
                self.validate_statement(response)
                continue

            message = "[Cashcode_t1500->wait(%s, %s, %s)] got=%s" % (
                wait, skip, timeout, response)
            self.logger.debug(message)
            raise AcceptorError(message, response)

            self.free(.2)

    def validate_statement(self, packet):

        if packet == [self.r_power_up]:
            self.logger.debug('State is POWER_UP')
            return

        if packet == [self.r_accepting]:
            self.logger.debug('State is ACCEPTING')
            return

        if packet == [self.r_stacking]:
            self.logger.debug('State is STACKING')
            return

        if packet == [self.rj_reject, self.e_insertation]:
            self.logger.debug(
                'State is Error. Details: [rj_reject, e_insertation]')
            return
예제 #22
0
	def validate_dispense_response(self, response_frame):
		logger = Logger()
		logger.debug('Dispenser_LCDM4000: validate "Dispense" response frame %s' % (["0x%02X" % x for x in response_frame]))

		template = {
			'status'					: False,
			'error_cause'			   : response_frame[4],
			'miss'					  : response_frame[5],
			'upper_exit_requested_1'	: response_frame[6] - 0x20,
			'upper_exit_requested_10'   : 0,
			'upper_chk_requested_1'	 : response_frame[6] - 0x20,
			'upper_chk_requested_10'	: 0,
			'upper_rejects_1'		   : chr(response_frame[7] - 0x20),
			'upper_rejects_10'		  : 0,
			'type_1'					: chr(response_frame[8]),

			'm_upper_exit_requested_1'  : response_frame[9] - 0x20,
			'm_upper_exit_requested_10' : 0,
			'm_upper_chk_requested_1'   : response_frame[9] - 0x20,
			'm_upper_chk_requested_10'  : 0,
			'm_upper_rejects_1'		 : chr(response_frame[10] - 0x20),
			'm_upper_rejects_10'		: 0,
			'type_2'					: chr(response_frame[11]),

			'm_lower_exit_requested_1'  : response_frame[12] - 0x20,
			'm_lower_exit_requested_10' : 0,
			'm_lower_chk_requested_1'   : response_frame[12] - 0x20,
			'm_lower_chk_requested_10'  : 0,
			'm_lower_rejects_1'		 : chr(response_frame[13] - 0x20),
			'm_lower_rejects_10'		: 0,
			'type_3'					: chr(response_frame[14]),

			'lower_exit_requested_1'	: response_frame[15] - 0x20,
			'lower_exit_requested_10'   : 0,
			'lower_chk_requested_1'	 : response_frame[15] - 0x20,
			'lower_chk_requested_10'	: 0,
			'lower_rejects_1'		   : chr(response_frame[16] - 0x20),
			'lower_rejects_10'		  : 0,
			'type_4'					: chr(response_frame[17]),

			'rsv'		   : 0,
			'etx'		   : 0,
			'bcc'		   : 0
		}

		for k,v in template.items():
			logger.debug(' > %s => %s' % (k,v))

		return template

		if len(response_frame) == 21:
			logger.debug('Allright. Len response_frame is 21')
		else:
			logger.debug('Allright. Len response_frame is %s. Frame is %s' % (len(response_frame), (["%02x" % x for x in response_frame]) ) )

		for k, v in template.items():
			logger.debug(" ]]] %s: %s" % (k, v))
		return False

		# Error while dispensing
		if len(response_frame) == 7:
			logger.debug('Dispenser_LCDM4000: len(response_frame) == 7')
			return False

		if len(response_frame) == 21:
			return self.__validate_dispense_both_response(response_frame, template)

		if response_frame[3] in [0x45, 0x55]:
			return self.__validate_dispense_single_response(response_frame, template)
예제 #23
0
class DispenserAdapter:
	
	DISPENSERS = {
		'PULOON-LCDM1000' : {'class' : 'Dispenser_LCDM1000', 'path' : 'services.dispenser.dispensers.puloon_lcdm1000'},
		'PULOON-LCDM2000' : {'class' : 'Dispenser_LCDM2000', 'path' : 'services.dispenser.dispensers.puloon_lcdm2000'},
		'PULOON-LCDM4000' : {'class' : 'Dispenser_LCDM4000', 'path' : 'services.dispenser.dispensers.puloon_lcdm4000'}
	}
	
	serial = None	   # Serial Object
	dispenser = None	# Dispenser model implementation
	
	def __init__(self, dispenser_model, port = False, baudrate = False, timeout = .6, parity = False):
		
		if dispenser_model not in self.DISPENSERS:
			raise DispenserError('%s is not supported. See DispenserController.DISPENSERS for available dispensers.')
		
		self.logger = Logger()
		self.dispenser = self.__import_class(
			self.DISPENSERS[dispenser_model]['path'],
			self.DISPENSERS[dispenser_model]['class']
		)()
		
		# Set data
		if port:
			self.dispenser.port = port
		
		if baudrate:
			self.dispenser.baudrate = baudrate
		
		if timeout:
			self.dispenser.timeout = timeout
		
		if parity:
			self.dispenser.parity = parity
	
	@staticmethod
	def __import_class(class_path, class_name):
		try:
			module =  __import__(class_path, fromlist = '*')
			return getattr(module, class_name)
		
		except Exception as e:
			message = "DispenserAdapter: Couldnt load class %s from path %s With error: %s" % (class_name, class_path, str(e.args))
			
			if __debug__:
				print message
			raise ImportError(message)
	
	def connect(self):
		''' Connect to serial port '''
		self.logger.debug('DispenserAdapter: port is %s'	 % self.dispenser.port)
		self.logger.debug('DispenserAdapter: parity is %s'   % self.dispenser.parity)
		self.logger.debug('DispenserAdapter: baudrate is %s' % self.dispenser.baudrate)
		self.logger.debug('DispenserAdapter: timeout is %s'  % self.dispenser.timeout)
		
		try:
			self.serial = serial.serial_for_url(
				self.dispenser.port,
				self.dispenser.baudrate,
				parity  = self.dispenser.parity,
				timeout = self.dispenser.timeout
			)
		except SerialException as e:
			raise DispenserError('Error while connecting to dispenser', 500)
		
		try:
			self.status()
		except:
			pass
	
	def status(self):
		
		self.logger.debug('DispenserAdapter: get_status command received')
		
		response = self.__send(
			command   = self.dispenser.command_status,
			validator = self.dispenser.validate_status_response
		)
		
		self.__write( [self.dispenser.p_ack] )
		
		if isinstance(self.dispenser, Dispenser_LCDM4000):
			''' Read the EOT for LCDM4000 '''
			try:
				self.__read( timeout = .6 )
			except Exception as e:
				self.logger.debug('DispenserAdapter::status [forr Puloon_LCDM4000] error catched. [%s]' % str(e.args))
		
		if not response:
			return False
		
		if 'status' in response:
			self.logger.debug('DispenserAdapter: [CMD STATUS] result: %s' % response['status'])
			self.logger.debug('DispenserAdapter: [CMD STATUS] info  : %s' % response)
			return response['status']
		
		return False
	
	def init(self):
		
		self.logger.debug('DispenserAdapter: init command received')
		
		response = self.__send(
			command = self.dispenser.command_init,
			validator = self.dispenser.validate_init_response
		)
		
		self.__write( [self.dispenser.p_ack] )
		
		if isinstance(self.dispenser, Dispenser_LCDM4000):
			''' Read the EOT for LCDM4000 '''
			time.sleep(2.1)
#			try:
#				self.__read( timeout = .7 )
#			except Exception as e:
#				self.logger.debug('DispenserAdapter::init [for Puloon_LCDM4000] error catched. [%s]' % str(e.args))
		
		self.logger.debug('DispenserAdapter: init finished with result %s' % bool(response))
		
		if response:
			return True
		return False
	
	def purge(self):
		
		self.logger.debug('DispenserAdapter: purge command received')
		
		response = self.__send(
			command   = self.dispenser.command_purge,
			validator = self.dispenser.validate_purge_response
		)
		
		self.__write( [self.dispenser.p_ack] )
		
		if isinstance(self.dispenser, Dispenser_LCDM4000):
			''' Read the EOT for LCDM4000 '''
			try:
				self.__read( timeout = .6 )
			except Exception as e:
				self.logger.debug('DispenserAdapter::purge [for Puloon_LCDM4000] error catched. [%s]' % str(e.args))
		
		if response:
			if 'status' in response:
				self.logger.debug('DispenserAdapter: [CMD PURGE] result: %s' % response['status'])
				self.logger.debug('DispenserAdapter: [CMD PURGE] info  : %s' % response)
				return response['status']
		return False
	
	def dispense(self, amount, cassets_info):
		''' Dispense the bills
			amount:
				amount to dispense
			cassets_info:
				Cassets infrormation in format {id_cassete : {'command' : 0x45, 'denomination' : 0, 'charging' : 100, 'position' : 'upper'},...}
		'''
		dispense_info = self.dispenser.get_dispense( amount, cassets_info )
		command = None
		
		self.logger.debug('DispenserAdapter: Starting dispense of amount %s' % amount)
		
		# Amount is not dispensable
		if dispense_info['to_dispense'] == amount:
			raise DispenserError('Amount is not dispensable', 500)
		
		if len(dispense_info['cassets']) > 1:
			command = self.dispenser.command_dispense_several( cassets_info, dispense_info['cassets'] )
		else:
			id_cassete, banknotes_count = dispense_info['cassets'].popitem()
			command = self.dispenser.command_dispense_single( cassets_info, id_cassete, banknotes_count )
		
		response = self.__send(
			command = command,
			validator = self.dispenser.validate_dispense_response
		)

		if not response:
			raise DispenserError('Error while dispensing (response from dispenser is None)', 500)
		
		self.__write( [self.dispenser.p_ack] )
		
		return response
	
	def __send(self, command, validator = False):
		
		if not self.serial:
			raise DispenserError( 'Serial port is not opened' )
		
		self.logger.debug('DispenserAdapter: send command in hex %s' % (["%02X" % x for x in command]))
		
		''' Пишем, спим полсекундочки и читаем '''
		self.logger.debug( '>> serial.write %s' % (["%02X" % x for x in command]) )
		bytes_written = self.__write(  command )
		
		self.logger.debug( '>> serial.read' )
		bytes_recd = self.__read( timeout = 1)
		self.logger.debug( '>> serial.read done' )
		
		if bytes_recd == [self.dispenser.p_nck]:
			raise DispenserError('DispenserAdapter.__send: bytes_recd == NCK')
		
		if bytes_recd == [self.dispenser.p_ack]:
			self.logger.debug('DispenserAdapter.__send: bytes_recd == ACK')
			bytes_recd = self.__read( timeout = 60)
			readed = (["%02X" % x for x in bytes_recd])
		else:
			if  isinstance(self.dispenser, Dispenser_LCDM4000):
				self.logger.debug( "Dispenser instance is Dispenser_LCDM4000. F**k this!" )
				if validator:
					return validator(bytes_recd)
			
			readed = (["%02X" % x for x in bytes_recd])
			self.logger.debug('DispenserAdapter.__send [read from dispenser]:  HEX is %s' % readed)
			raise DispenserError('Shit hap in HEX: %s' % readed)
		
		self.logger.debug('>> serial.read in HEX: %s' % readed)
		
#		if isinstance(self.dispenser, Dispenser_LCDM4000):
#			''' Read the EOT for LCDM4000 '''
#			try:
#				self.__read( timeout = .5 )
#			except Exception as e:
#				self.logger.debug('DispenserAdapter::dispense [for Puloon_LCDM4000] error catched. [%s]' % str(e.args))
		
		if validator:
			return validator(bytes_recd)
		
		self.logger.debug('DispenserAdapter: No validator presents. Return invalidated response.')
		return bytes_recd
	
	def __write(self, bytes):
		
		return self.serial.write(serial.to_bytes(bytes))
	
	def __read(self, timeout = 60):
		
		time_start = time.time()
		frame	= []
		data	= ''
		byte	= self.serial.read(1)
		data	+= byte
		
		try:
			first_byte = int(byte2hex( byte ), 16)
			
			self.logger.debug('DispenserAdapter.__read: first byte is: 0x%02X' % first_byte)
			
			if first_byte == self.dispenser.p_nck:
				self.logger.debug('DispenserAdapter.__read: first byte is NCK')
				return [self.dispenser.p_nck]
			
			if first_byte == self.dispenser.p_ack:
				self.logger.debug('DispenserAdapter.__read: first byte is: ACK')
				return [self.dispenser.p_ack]
			
			byte  = self.serial.read(1)
			data += byte
			
			self.logger.debug('DispenserAdapter.__read: Wrong first byte in response 0x%02X' % first_byte )
			self.logger.debug('DispenserAdapter.__read: Next byte in response is 0x%02X' % int(byte2hex( byte )) )
		
		except Exception as e:
			self.logger.debug('DispenserAdapter.__read: %s' % str(e.args) )
			pass
		
		self.logger.debug('DispenserAdapter.__read: first byte is detected')
		
		while True:
			if time.time() - time_start > timeout:
				self.logger.debug('DispenserAdapter.__read: timeout to read. ')
				for _, i in enumerate(serial.to_bytes(data)):
					frame.append( int(byte2hex(i), 16) )
				
				self.logger.debug('DispenserAdapter.__read: full dump of response: %s' % (["0x%02X" % x for x in frame]))
				raise DispenserError(message = 'Timeout to read', code = 500)
			
			try: 
				if int(byte2hex(byte), 16) == self.dispenser.p_etx:
					break
			except Exception as e:
				self.logger.debug('Error in read cycle: %s' % str(e.args) )
				time.sleep( .01 )
			
			byte  = self.serial.read(1)
			data += byte
		
		self.logger.debug('DispenserAdapter.__read: cycle is complete')
		
		data += self.serial.read(1)
		
		for _, i in enumerate(serial.to_bytes(data)):
			frame.append( int(byte2hex(i), 16) )
		
		self.logger.debug('DispenserAdapter.__read: full dump of response: [%s]' % ([x for x in frame]))
		return frame
예제 #24
0
class OrderManager(object):
	def __init__(self, exchangeClient, marketRulesManager, strategyManager):
		self.exchangeClient = exchangeClient
		self.marketRulesManager = marketRulesManager
		self.strategyManager = strategyManager

		self.shapeNewOrders = True
		self.validateOrders = True
		self.preventImmediateLimitOrder = True

		self.liveOrderCache = {}
		self.liveTradeCache = {}

		self.log = Logger()

	def setShapeNewOrders(self, shapeNewOrders):
		self.shapeNewOrders = shapeNewOrders

	def setValidateOrders(self, validateOrders):
		self.validateOrders = validateOrders

	def setPreventImmediateLimitOrder(self, preventImmediateLimitOrder):
		self.preventImmediateLimitOrder = preventImmediateLimitOrder

	def getLiveOrderCache(self):
		return self.liveOrderCache

	def getLiveTradeCache(self):
		return self.liveTradeCache

	def reset(self):
		self.liveTradeCache = {}
		self.liveOrderCache = {}

	def storeOrder(self, order):
		Db.Session().add(order)

		# flush in order to generate db maintained sequence
		Db.Session().flush()

		# update internal reference only when we got new order_id
		if not order.int_order_ref:
			order.int_order_ref = Settings.ORDER_REFERENCE_PREFIX + str(order.order_id)

		# track order updates in the cache.
		o = Order()
		o.setFromEntity(order)
		self.liveOrderCache[order.order_id] = o

	def _storeTrade(self, trade):
		Db.Session().add(trade)

		# flush in order to generate db maintained sequence
		Db.Session().flush()

		# track trade updates in the cache. if trade reached a final state, remove it from the cache
		t = orders.Trade.Trade()
		t.setFromEntity(trade)
		if not trade.trade_id in self.liveTradeCache:
			self.liveTradeCache[trade.trade_id] = t
		else:
			if trade.trade_state in TradeStateMapper.getDbValue([TradeState.CLOSED, TradeState.CLOSE_FAILED, TradeState.OPEN_FAILED]):
				self.liveTradeCache.pop(trade.trade_id, None)

				# once trade is removed, clean also cache of corresponding trade orders
				for orderKey in list(self.liveOrderCache.keys()):
					if self.liveOrderCache[orderKey].getTradeId() == t.getTradeId():
						self.liveOrderCache.pop(orderKey)
			else:
				self.liveTradeCache[trade.trade_id] = t

	def closeTrade(self, trade, closeTmstmp):
		self.log.info('Trade ' + str(trade.trade_id) + ' CLOSED.')
		trade.trade_state = TradeStateMapper.getDbValue(TradeState.CLOSED)
		trade.close_tmstmp = closeTmstmp

		# notify strategy about the closed trade
		self.strategyManager.getStrategy(trade.strategy_exec_id).tradeClosed(trade.trade_id)

		self._calcTradePerf(trade)

	def _calcTradePerf(self, trade):
		orders = TradeManager.getAllOrders(tradeId = trade.trade_id, orderState = OrderStateMapper.getDbValue(OrderState.FILLED))

		buyPrice = Decimal(0.0)
		sellPrice = Decimal(0.0)
		for order in orders:
			if order.order_side == OrderSideMapper.getDbValue(OrderSide.BUY):
				buyPrice += order.qty * Decimal(order.price)
			else:
				sellPrice += order.qty * Decimal(order.price)

		diff = sellPrice - buyPrice

		rules = self.marketRulesManager.getSymbolRules(trade.base_asset, trade.quote_asset)
		if diff < 0:
			color = Style.BRIGHT + Fore.LIGHTWHITE_EX + Back.RED
		else:
			color = Style.BRIGHT + Fore.LIGHTWHITE_EX + Back.GREEN
		self.log.info(color + 'Gain: ' + str(('{:.' + str(rules.quoteAssetPrecision) + 'f}').format(diff)) + Style.RESET_ALL)

	def processOrderUpdate(self, orderResponse):
		# update order in the database based on the update response
		order = TradeManager.getOrder(intOrderRef = orderResponse.getClientOrderId())
		order.order_state = OrderStateMapper.getDbValue(orderResponse.getOrderState())
		order.lst_upd_tmstmp = datetime.now()
		if orderResponse.getOrderState() == OrderState.OPENED:
			order.open_tmstmp = orderResponse.getOrderTmstmp()
		if orderResponse.getOrderState() == OrderState.FILLED:
			self.log.debug('Order ' + str(order.order_id) + ' FILLED.')
			order.filled_tmstmp = orderResponse.getOrderTmstmp()
		if orderResponse.getOrderState() == OrderState.CANCELED:
			self.log.debug('Order ' + str(order.order_id) + ' CANCELLED.')

		# update trade in the database based on the state of orders
		trade = TradeManager.getTrade(tradeId = order.trade_id)
		if trade.trade_type == TradeTypeMapper.getDbValue(TradeType.LONG):
			# open trade when first confirmed BUY is received
			if orderResponse.getOrderSide() == OrderSide.BUY and \
					orderResponse.getOrderState() == OrderState.OPENED and \
					trade.trade_state == TradeStateMapper.getDbValue(TradeState.OPEN_PENDING):
				trade.trade_state = TradeStateMapper.getDbValue(TradeState.OPENED)
				trade.open_tmstmp = orderResponse.getOrderTmstmp()

			# close trade when no pending order is left
			elif orderResponse.getOrderState() in [OrderState.CANCELED, OrderState.REJECTED, OrderState.EXPIRED]:
				openOrders = TradeManager.getPendOrders(trade.trade_id)
				if len(openOrders) == 0:
					self.closeTrade(trade, orderResponse.getOrderTmstmp())

			# handle closing of the trade based on the defined strategy
			elif orderResponse.getOrderState() == OrderState.FILLED:
				tradeCloseType = self.strategyManager.getStrategy(trade.strategy_exec_id).getTradeCloseType()
				TradeCloseStrategy.evalTradeClose(tradeCloseType, trade, orderResponse, self)
		else:
			raise Exception('Short trades not supported!')

		self.storeOrder(order)
		self._storeTrade(trade)

	def _validateOrders(self, baseAsset, quoteAsset, orders):
		rules = self.marketRulesManager.getSymbolRules(baseAsset, quoteAsset)

		for order in orders:
			qty = order.getQty()
			if qty < rules.minQty:
				raise Exception('Quantity is less than minimum quantity! Qty [' + str(qty) + '], minQty [' + str(rules.minQty) + ']')

			if qty > rules.maxQty:
				raise Exception('Quantity is greater than maximum quantity! Qty [' + str(qty) + '], maxQty [' + str(rules.maxQty) + ']')

			if (qty - rules.minQty) % rules.minQtyDenom != 0:
				raise Exception('Quantity is not multiply of denomination! qty [' + str(qty) + '], minQty [' + str(rules.minQty) + '],  minDenom [' + str(rules.minQtyDenom) + ']')

			price = order.getPrice()
			if price:
				if price < rules.minPrice:
					raise Exception('Price is less than minimum price! Price [' + str(price) + '], minPrice [' + str(rules.minPrice) + ']')

				if price > rules.maxPrice:
					raise Exception('Price is greater than maximum price! Price [' + str(price) + '], maxPrice [' + str(rules.maxPrice) + ']')

				if (price - rules.minPrice) % rules.minPriceDenom != 0:
					raise Exception('Price is not multiply of denomination! Price [' + str(price) + '], minPrice [' + str(rules.minPrice) + '],  minDenom [' + str(rules.minPriceDenom) + ']')

				if price * qty < rules.minNotional:
					raise Exception('Quantity and price is less than minimum notional value! Qty [' + str(qty) + '], price [' + str(price) + '],  minNotional [' + str(rules.minNotional) + ']')

			stopPrice = order.getStopPrice()
			if stopPrice:
				if stopPrice < rules.minPrice:
					raise Exception('Stop price is less than minimum price! Stop price [' + str(stopPrice) + '], minPrice [' + str(rules.minPrice) + ']')

				if stopPrice > rules.maxPrice:
					raise Exception('Stop price is greater than maximum price! Stop price [' + str(stopPrice) + '], maxPrice [' + str(rules.maxPrice) + ']')

				if (stopPrice - rules.minPrice) % rules.minPriceDenom != 0:
					raise Exception('Stop price is not multiply of denomination! Stop price [' + str(stopPrice) + '], minPrice [' + str(rules.minPrice) + '],  minDenom [' + str(rules.minPriceDenom) + ']')

				if stopPrice * qty < rules.minNotional:
					raise Exception('Quantity and stop price is less than minimum notional value! Qty [' + str(qty) + '], stop price [' + str(stopPrice) + '],  minNotional [' + str(rules.minNotional) + ']')

		return True

	def _validateImmediateLimitOrder(self, candle, orders):
		for order in orders:
			if order.getOrderSide() == OrderSide.BUY:
				if order.getOrderType() == OrderType.LIMIT and order.getPrice() >= candle.getClose():
					raise Exception("LIMIT BUY order will be executed immediately! Order price [" + str(order.getPrice()) + "], market price [" + str(candle.getClose()) + "]")
				if order.getOrderType() in [OrderType.STOP_LOSS_LIMIT, OrderType.STOP_LOSS_MARKET] and order.getStopPrice() <= candle.getClose():
					raise Exception("STOP LOSS BUY order will be executed immediately! Order stop price [" + str(order.getStopPrice()) + "], market price [" + str(candle.getClose()) + "]")
				if order.getOrderType() in [OrderType.TAKE_PROFIT_LIMIT, OrderType.TAKE_PROFIT_MARKET] and order.getStopPrice() >= candle.getClose():
					raise Exception("TAKE PROFIT BUY order will be executed immediately! Order stop price [" + str(order.getStopPrice()) + "], market price [" + str(candle.getClose()) + "]")

			if order.getOrderSide() == OrderSide.SELL:
				if order.getOrderType() == OrderType.LIMIT and order.getPrice() <= candle.getClose():
					raise Exception("LIMIT SELL order will be executed immediately! Order price [" + str(order.getPrice()) + "], market price [" + str(candle.getClose()) + "]")
				if order.getOrderType() in [OrderType.STOP_LOSS_LIMIT, OrderType.STOP_LOSS_MARKET] and order.getStopPrice() >= candle.getClose():
					raise Exception("STOP LOSS SELL order will be executed immediately! Order stop price [" + str(order.getStopPrice()) + "], market price [" + str(candle.getClose()) + "]")
				if order.getOrderType() in [OrderType.TAKE_PROFIT_LIMIT, OrderType.TAKE_PROFIT_MARKET] and order.getStopPrice() <= candle.getClose():
					raise Exception("TAKE PROFIT SELL order will be executed immediately! Order stop price [" + str(order.getStopPrice()) + "], market price [" + str(candle.getClose()) + "]")

	def _shapeValue(self, value, minVal, minDenom, precision):
		self.log.debug('Shaping value [' + str(value) + '], minVal [' + str(minVal) + '], minDenom [' + str(minDenom) + '], precision [' + str(precision) + ']')

		ret = Decimal(minVal) + Decimal(minDenom) * Decimal((Decimal(value) - Decimal(minVal)) / Decimal(minDenom)).quantize(Decimal('1'), rounding = ROUND_DOWN)
		#ret = Decimal(ret).quantize(Decimal('10') ** -precision)

		self.log.debug('Shaped value [' + str(ret) + ']')

		return ret

	def _shapeOrders(self, baseAsset, quoteAsset, orders):
		rules = self.marketRulesManager.getSymbolRules(baseAsset, quoteAsset)

		for order in orders:
			if order.getQty():
				self.log.debug('Shaping quantity')
				order.setQty(self._shapeValue(order.getQty(), rules.minQty, rules.minQtyDenom, rules.baseAssetPrecision))
			if order.getPrice():
				self.log.debug('Shaping price')
				order.setPrice(self._shapeValue(order.getPrice(), rules.minPrice, rules.minPriceDenom, rules.quoteAssetPrecision))
			if order.getStopPrice():
				self.log.debug('Shaping stop price')
				order.setStopPrice(self._shapeValue(order.getStopPrice(), rules.minPrice, rules.minPriceDenom, rules.quoteAssetPrecision))

	def openTrade(self, strategyExecId, tradeType, candle):
		trade = Trade()
		trade.strategy_exec_id = strategyExecId
		trade.base_asset = candle.getBaseAsset()
		trade.quote_asset = candle.getQuoteAsset()
		trade.init_tmstmp = candle.getCloseTime()
		trade.trade_state = TradeStateMapper.getDbValue(TradeState.OPEN_PENDING)
		trade.trade_type = TradeTypeMapper.getDbValue(tradeType)

		self._storeTrade(trade)

		return trade

	def openOrder(self, trade, candle, orders):
		self.log.debug('Orders to be created: ' + str(len(orders)))

		# adjust quantity and price such that it meets market rules
		if self.shapeNewOrders:
			self._shapeOrders(candle.getBaseAsset(), candle.getQuoteAsset(), orders)

		# validate orders before sending them to the exchange
		if self.validateOrders:
			self._validateOrders(candle.getBaseAsset(), candle.getQuoteAsset(), orders)

		# make sure that limit orders are not executed immediately
		if self.preventImmediateLimitOrder:
			self._validateImmediateLimitOrder(candle, orders)

		for order in orders:
			try:
				dbOrder = Orders()
				dbOrder.trade_id = trade.trade_id
				dbOrder.qty = order.getQty()
				dbOrder.stop_price = order.getStopPrice()
				if order.getOrderType() == OrderType.MARKET:
					dbOrder.price = candle.getClose()
				else:
					dbOrder.price = order.getPrice()
				dbOrder.order_side = OrderSideMapper.getDbValue(order.getOrderSide())
				dbOrder.order_type = OrderTypeMapper.getDbValue(order.getOrderType())
				dbOrder.order_state = OrderStateMapper.getDbValue(OrderState.OPEN_PENDING_INT)
				dbOrder.init_tmstmp = candle.getCloseTime()

				self.storeOrder(dbOrder)

				# save the generated order id
				order.setOrderId(dbOrder.order_id)
				# save the order state (for print)
				order.setOrderState(OrderState.OPEN_PENDING_INT)

				self.log.info('Order: ' + str(order))
			except:
				self.log.error('Could not create order ' + str(order))
				raise

	def sendOrders(self, cretenExecDetlId):
		ordersToBeSent = TradeManager.getAllOrders(cretenExecDetlId = cretenExecDetlId, orderState = OrderStateMapper.getDbValue([OrderState.OPEN_PENDING_INT, OrderState.CANCEL_PENDING_INT]))

		for dbOrder in ordersToBeSent:
			if dbOrder.order_state == OrderStateMapper.getDbValue(OrderState.OPEN_PENDING_INT):
				try:
					order = Order()
					order.setFromEntity(dbOrder)

					self.log.debug('Processing order [' + str(order) + ']')

					trade = TradeManager.getTrade(tradeId = dbOrder.trade_id)

					rules = self.marketRulesManager.getSymbolRules(trade.base_asset, trade.quote_asset)

					qty = ('{:.' + str(rules.baseAssetPrecision) + 'f}').format(order.getQty())
					price = ('{:.' + str(rules.quoteAssetPrecision) + 'f}').format(order.getPrice()) if order.getPrice() else None
					stopPrice = ('{:.' + str(rules.quoteAssetPrecision) + 'f}').format(order.getStopPrice()) if order.getStopPrice() else None
					self.log.debug('Values to be sent: qty [' + str(qty) + '], price [' + str(price) + '], stop price [' + str(stopPrice) + ']')

					response = self.exchangeClient.createOrder(order.getOrderSide(), order.getOrderType(),
					                                           trade.base_asset, trade.quote_asset, qty,
					                                            stopPrice, price, order.getIntOrderRef())

					self.log.debug('Response: ' + str(response.getRawData()))

					if not response.getOrderState() in [OrderState.OPENED, OrderState.FILLED]:
						raise Exception('Unexpected response received for order [' + str(order) + ']! Expected state [' + str([OrderState.OPENED, OrderState.FILLED]) + '], received [' + str(response.getOrderState()) + ']')

					if order.getOrderType() == OrderType.MARKET:
						dbOrder.price = response.getPrice()
					dbOrder.ext_order_ref = response.getExtOrderRef()
					dbOrder.order_state = OrderStateMapper.getDbValue(OrderState.OPEN_PENDING_EXT)
					self.storeOrder(dbOrder)
				except:
					self.log.error('Error while creating orders!')
					dbOrder.order_state = OrderStateMapper.getDbValue(OrderState.OPEN_FAILED)
					self.storeOrder(dbOrder)

					raise
			elif dbOrder.order_state == OrderStateMapper.getDbValue(OrderState.CANCEL_PENDING_INT):
				try:
					order = Order()
					order.setFromEntity(dbOrder)

					self.log.debug('Cancelling order [' + str(order) + ']')

					trade = TradeManager.getTrade(tradeId = dbOrder.trade_id)
					response = self.exchangeClient.cancelOrder(baseAsset = trade.base_asset, quoteAsset = trade.quote_asset, clientOrderId = order.getIntOrderRef())

					self.log.debug('Response: ' + str(response.getRawData()))

					if not response.getOrderState() in [OrderState.OPENED]:
						raise Exception('Unexpected response received for order [' + str(order) + ']! Expected state [' + str([OrderState.OPENED, OrderState.FILLED]) + '], received [' + str(response.getOrderState()) + ']')

					dbOrder.ext_order_ref = response.getExtOrderRef()
					dbOrder.order_state = OrderStateMapper.getDbValue(OrderState.CANCEL_PENDING_EXT)
					self.storeOrder(dbOrder)
				except:
					self.log.error('Error while cancelling orders!')
					dbOrder.order_state = OrderStateMapper.getDbValue(OrderState.CANCEL_FAILED)
					self.storeOrder(dbOrder)

					raise
			else:
				raise Exception('Creating orders for unsupporterd order state!')

	# TODO
	def init(self):
		pass
예제 #25
0
class ExchangeClient(object):
    __metaclass__ = ABCMeta

    def __init__(self, exchangeConfigPath):
        self.log = Logger(logForceDebug=False)

        self.exchangeConf = None
        if exchangeConfigPath:
            self.log.info('')
            self.log.info("Loading exchange configuration file '" +
                          exchangeConfigPath + "'")
            if not os.path.isfile(exchangeConfigPath):
                raise Exception(
                    "Exchange configuration file could not be loaded. '" +
                    os.path.realpath(exchangeConfigPath) +
                    "' does not correspond to a valid file.")

            with open(os.path.realpath(exchangeConfigPath)) as myfile:
                self.exchangeConf = json.loads(myfile.read())
                jsonschema.validate(self.exchangeConf,
                                    self.getExchangeConfigSchema())
        else:
            self.log.debug("No exchange configuration file provided.")

    def updateSymbolRuleFromConfig(self, symbolRule):
        rules = None
        if self.exchangeConf and "marketRules" in self.exchangeConf:
            for marketRule in self.exchangeConf['marketRules']:
                if marketRule['baseAsset'] + marketRule[
                        'quoteAsset'] == symbolRule.symbol:
                    rules = marketRule

        if rules:
            self.log.info('Updating market rules from configuration file')
            try:
                symbolRule.baseAssetPrecision = rules['baseAssetPrecision']
                self.log.debug('baseAssetPrecision updated')
            except KeyError:
                pass
            try:
                symbolRule.quoteAssetPrecision = rules['quoteAssetPrecision']
                self.log.debug('quoteAssetPrecision updated')
            except KeyError:
                pass
            try:
                symbolRule.minPrice = rules['minPrice']
                self.log.debug('minPrice updated')
            except KeyError:
                pass
            try:
                symbolRule.maxPrice = rules['maxPrice']
                self.log.debug('maxPrice updated')
            except KeyError:
                pass
            try:
                symbolRule.minPriceDenom = rules['minPriceDenom']
                self.log.debug('minPriceDenom updated')
            except KeyError:
                pass
            try:
                symbolRule.minQty = rules['minQty']
                self.log.debug('minQty updated')
            except KeyError:
                pass
            try:
                symbolRule.maxQty = rules['maxQty']
                self.log.debug('maxQty updated')
            except KeyError:
                pass
            try:
                symbolRule.minQtyDenom = rules['minQtyDenom']
                self.log.debug('minQtyDenom updated')
            except KeyError:
                pass
            try:
                symbolRule.minNotional = rules['minNotional']
                self.log.debug('minNotional updated')
            except KeyError:
                pass

    def getExchangeConfigSchema(self):
        return ExchangeConfigSchema.schema

    @abstractmethod
    def getRawClient(self):
        pass

    @abstractmethod
    def getCandles(self,
                   pair,
                   interval,
                   limit=None,
                   startTime=None,
                   endTime=None):
        pass

    @abstractmethod
    def getExchangeInfo(self, symbols=None):
        pass

    @abstractmethod
    def getPortfolio(self):
        pass

    @abstractmethod
    def createOrder(self, orderSide, orderType, baseAsset, quoteAsset, qty,
                    stopPrice, price, clientOrderId):
        pass

    @abstractmethod
    def cancelOrder(self,
                    baseAsset,
                    quoteAsset,
                    clientOrderId=None,
                    extOrderRef=None):
        pass
예제 #26
0
        Settings.client['DB_CONNECTION']['DB'],
        True if args['loglevel'] == 'debugalll' else False)

# must be imported after db session has been initialised
from db_entities.CretenExec import CretenExec
from db_entities.CretenExecDetl import CretenExecDetl
from db_entities.StrategyExec import StrategyExec
from strategy.StrategyPerformance import StrategyPerformance

log = Logger()

logging.info('')
with Db.session_scope():
	cretenExecs = Db.Session().query(CretenExec.creten_exec_id).order_by(desc(CretenExec.creten_exec_id)).limit(args['count']).all()

	log.debug('Executions to be evaluated: ' + str(cretenExecs))

	strategyExecs = Db.Session().query(StrategyExec).filter(CretenExecDetl.creten_exec_id.in_([x[0] for x in cretenExecs]),
	    CretenExecDetl.creten_exec_detl_id == StrategyExec.creten_exec_detl_id).order_by(desc(StrategyExec.strategy_exec_id)).all()

	f = None
	if args['file']:
		f = open('Crestat_{0}.log'.format(datetime.datetime.now().strftime('%Y%m%d%H%M%S')), 'w')

	t = PrettyTable()
	t.field_names = ['SEI', 'Trades', 'Won', 'Lost', 'Won [%]', 'Gain', 'Loss', 'Gross profit', 'Gain/Loss',
	                 'Avg gain', 'Max gain', 'Avg loss', 'Max loss', 'Avg trade length', 'Max trade length']

	if args['file']:
		f.write(';'.join(t.field_names) + '\n')
예제 #27
0
class Dispenser:
	
	''' Configuration '''
	port						= '/dev/ttyUSB'
	baudrate					= 9600
	timeout					 = 1
	data_bits				   = 8					 # 8 bits
	stop_bits				   = 1					 # 1 bit
	parity					  = serial.PARITY_NONE	# No parity
	bps						 = 3.5				   # Banknotes per second
	
	''' Limits for dispensing '''
	dispense_limit_single_10	= 6
	dispense_limit_single_1	 = 9
	dispense_limit_both_10	  = 6
	dispense_limit_both_1	   = 9
	dispense_limit			  = 60   # Limit banknotes count for dispensing from 1 cassete

	''' PROTOCOL > CONSTANTS '''
	p_soh					   = 0x01  # Start of Header
	p_stx					   = 0x02  # Start of Text
	p_etx					   = 0x03  # End of Text
	p_eot					   = 0x04  # Start of Transmission
	p_ack					   = 0x06  # ACK
	p_nck					   = 0x15  # NCK
	p_id						= 0x50  # Communication ID
	p_bcc					   = 0x00  # Block Check Character: BCC can be gotten through Exclusive-OR (XOR) from the start of each message to ETX

	@staticmethod
	def get_cassete_by_denomination(cassets, denomination):
		for id_cassete, cassete_info in cassets.items():
			if cassete_info['denomination'] == denomination:
			   return id_cassete
		return -1

	def get_dispense(self, amount, cassets_info):
		self.logger = Logger()
		
		'''
			Method calculates cash dispensing between cassets

			amount:
				Amount to dispense

			cassets_info:
				Cassets infrormation in format {id_cassete : {'command' : 0x45, 'denomination' : 0, 'charging' : 100, 'position' : 'upper'},...}
			return {
				'to_dispense'   : 5,
				'cassets'	   : {
					0 : {'amount' : 100, 'count' : 10, 'denomination'  : 10},
					1 : {'amount' : 1000, 'count' : 10, 'denomination'  : 100},
					...
				}
			}
		'''
		dispense = {'to_dispense': amount, 'cassets': {}}

		# Dynamic cassets information
		cassets = ([cassete_info['denomination'] for cassete_info in cassets_info.values()])
		cassets.sort()

		while dispense['to_dispense'] > 0 and len( cassets ):
			self.logger.debug('[DISPENSER]: Amount to dispense: %s' % dispense['to_dispense'])
			self.logger.debug('[DISPENSER]: Next cassets are available: %s' % cassets_info)

			# select max denomination
			denomination = cassets.pop()
			self.logger.debug( '[DISPENSER]: selected denomination: %s' % denomination )
			id_cassete = self.get_cassete_by_denomination( cassets_info, denomination )

			# if no available cassets
			if id_cassete == -1:
				self.logger.debug('[DISPENSER]: no more cassets are available to dispense amount %s' % dispense['to_dispense'])
				continue

			# check count of banknotes
			banknotes_count = min(
				int( dispense['to_dispense'] * 1. / denomination ),	 # Count of full-dispensing
				cassets_info[id_cassete]['charging'],				   # Count of available banknotes
				self.dispense_limit									 # Dispenser limit
			)

			# If no more banknotes
			if not banknotes_count:
				self.logger.debug( '[DISPENSER]: no such banknotes in cassete #%d' % id_cassete )
				continue

			self.logger.debug( '[DISPENSER]: from selected cassete #%d will exit %d banknotes with denomination %d' % (id_cassete, banknotes_count, denomination) )

			dispense['cassets'][id_cassete] = banknotes_count
			dispense['to_dispense'] -= banknotes_count * denomination

		return dispense
예제 #28
0
    def validate_dispense_response(self, response_frame):
        logger = Logger()
        logger.debug(
            'Dispenser_LCDM4000: validate "Dispense" response frame %s' %
            (["0x%02X" % x for x in response_frame]))

        template = {
            'status': False,
            'error_cause': response_frame[4],
            'miss': response_frame[5],
            'upper_exit_requested_1': response_frame[6] - 0x20,
            'upper_exit_requested_10': 0,
            'upper_chk_requested_1': response_frame[6] - 0x20,
            'upper_chk_requested_10': 0,
            'upper_rejects_1': chr(response_frame[7] - 0x20),
            'upper_rejects_10': 0,
            'type_1': chr(response_frame[8]),
            'm_upper_exit_requested_1': response_frame[9] - 0x20,
            'm_upper_exit_requested_10': 0,
            'm_upper_chk_requested_1': response_frame[9] - 0x20,
            'm_upper_chk_requested_10': 0,
            'm_upper_rejects_1': chr(response_frame[10] - 0x20),
            'm_upper_rejects_10': 0,
            'type_2': chr(response_frame[11]),
            'm_lower_exit_requested_1': response_frame[12] - 0x20,
            'm_lower_exit_requested_10': 0,
            'm_lower_chk_requested_1': response_frame[12] - 0x20,
            'm_lower_chk_requested_10': 0,
            'm_lower_rejects_1': chr(response_frame[13] - 0x20),
            'm_lower_rejects_10': 0,
            'type_3': chr(response_frame[14]),
            'lower_exit_requested_1': response_frame[15] - 0x20,
            'lower_exit_requested_10': 0,
            'lower_chk_requested_1': response_frame[15] - 0x20,
            'lower_chk_requested_10': 0,
            'lower_rejects_1': chr(response_frame[16] - 0x20),
            'lower_rejects_10': 0,
            'type_4': chr(response_frame[17]),
            'rsv': 0,
            'etx': 0,
            'bcc': 0
        }

        for k, v in template.items():
            logger.debug(' > %s => %s' % (k, v))

        return template

        if len(response_frame) == 21:
            logger.debug('Allright. Len response_frame is 21')
        else:
            logger.debug('Allright. Len response_frame is %s. Frame is %s' %
                         (len(response_frame),
                          (["%02x" % x for x in response_frame])))

        for k, v in template.items():
            logger.debug(" ]]] %s: %s" % (k, v))
        return False

        # Error while dispensing
        if len(response_frame) == 7:
            logger.debug('Dispenser_LCDM4000: len(response_frame) == 7')
            return False

        if len(response_frame) == 21:
            return self.__validate_dispense_both_response(
                response_frame, template)

        if response_frame[3] in [0x45, 0x55]:
            return self.__validate_dispense_single_response(
                response_frame, template)
예제 #29
0
class MarketRulesManager(object):
    def __init__(self, exchangeClient):
        self.exchangeClient = exchangeClient

        self.symbolRules = {}
        self.commission = 0.0

        self.log = Logger(logForceDebug=False)

    def init(self, symbols):
        rules = self.exchangeClient.getExchangeInfo(symbols)

        for rule in rules:
            self.log.debug('Market rules for ' + str(rule.symbol) + ':')
            self.log.debug('\tTrading status: ' + str(rule.status))
            self.log.debug('\tQuote asset precision: ' +
                           str(rule.quoteAssetPrecision))
            self.log.debug('\tBase asset precision: ' +
                           str(rule.baseAssetPrecision))
            self.log.debug('\tOrder types: ' + str(rule.orderTypes))
            self.log.debug('\tIceberg allowed: ' + str(rule.icebergAllowed))
            self.log.debug('\tMin price: ' + str(rule.minPrice))
            self.log.debug('\tMax price: ' + str(rule.maxPrice))
            self.log.debug('\tMin price denomination: ' +
                           str(rule.minPriceDenom))
            self.log.debug('\tMin quantity: ' + str(rule.minQty))
            self.log.debug('\tMax quantity: ' + str(rule.maxQty))
            self.log.debug('\tMin quantity denomination: ' +
                           str(rule.minQtyDenom))
            self.log.debug('\tMin notional: ' + str(rule.minNotional))

            self.symbolRules[rule.symbol] = rule

    def getSymbolRules(self, baseAsset, quoteAsset):
        return self.symbolRules[baseAsset + quoteAsset]

    def setCommission(self, commission):
        self.commission = commission

    def getCommission(self):
        return self.commission
예제 #30
0
class Dispenser:
    ''' Configuration '''
    port = '/dev/ttyUSB'
    baudrate = 9600
    timeout = 1
    data_bits = 8  # 8 bits
    stop_bits = 1  # 1 bit
    parity = serial.PARITY_NONE  # No parity
    bps = 3.5  # Banknotes per second
    ''' Limits for dispensing '''
    dispense_limit_single_10 = 6
    dispense_limit_single_1 = 9
    dispense_limit_both_10 = 6
    dispense_limit_both_1 = 9
    dispense_limit = 60  # Limit banknotes count for dispensing from 1 cassete
    ''' PROTOCOL > CONSTANTS '''
    p_soh = 0x01  # Start of Header
    p_stx = 0x02  # Start of Text
    p_etx = 0x03  # End of Text
    p_eot = 0x04  # Start of Transmission
    p_ack = 0x06  # ACK
    p_nck = 0x15  # NCK
    p_id = 0x50  # Communication ID
    p_bcc = 0x00  # Block Check Character: BCC can be gotten through Exclusive-OR (XOR) from the start of each message to ETX

    @staticmethod
    def get_cassete_by_denomination(cassets, denomination):
        for id_cassete, cassete_info in cassets.items():
            if cassete_info['denomination'] == denomination:
                return id_cassete
        return -1

    def get_dispense(self, amount, cassets_info):
        self.logger = Logger()
        '''
			Method calculates cash dispensing between cassets

			amount:
				Amount to dispense

			cassets_info:
				Cassets infrormation in format {id_cassete : {'command' : 0x45, 'denomination' : 0, 'charging' : 100, 'position' : 'upper'},...}
			return {
				'to_dispense'   : 5,
				'cassets'	   : {
					0 : {'amount' : 100, 'count' : 10, 'denomination'  : 10},
					1 : {'amount' : 1000, 'count' : 10, 'denomination'  : 100},
					...
				}
			}
		'''
        dispense = {'to_dispense': amount, 'cassets': {}}

        # Dynamic cassets information
        cassets = ([
            cassete_info['denomination']
            for cassete_info in cassets_info.values()
        ])
        cassets.sort()

        while dispense['to_dispense'] > 0 and len(cassets):
            self.logger.debug('[DISPENSER]: Amount to dispense: %s' %
                              dispense['to_dispense'])
            self.logger.debug('[DISPENSER]: Next cassets are available: %s' %
                              cassets_info)

            # select max denomination
            denomination = cassets.pop()
            self.logger.debug('[DISPENSER]: selected denomination: %s' %
                              denomination)
            id_cassete = self.get_cassete_by_denomination(
                cassets_info, denomination)

            # if no available cassets
            if id_cassete == -1:
                self.logger.debug(
                    '[DISPENSER]: no more cassets are available to dispense amount %s'
                    % dispense['to_dispense'])
                continue

            # check count of banknotes
            banknotes_count = min(
                int(dispense['to_dispense'] * 1. /
                    denomination),  # Count of full-dispensing
                cassets_info[id_cassete]
                ['charging'],  # Count of available banknotes
                self.dispense_limit  # Dispenser limit
            )

            # If no more banknotes
            if not banknotes_count:
                self.logger.debug(
                    '[DISPENSER]: no such banknotes in cassete #%d' %
                    id_cassete)
                continue

            self.logger.debug(
                '[DISPENSER]: from selected cassete #%d will exit %d banknotes with denomination %d'
                % (id_cassete, banknotes_count, denomination))

            dispense['cassets'][id_cassete] = banknotes_count
            dispense['to_dispense'] -= banknotes_count * denomination

        return dispense
예제 #31
0
파일: creten.py 프로젝트: w1r2p1/creten
        Settings.client['DB_CONNECTION']['PORT'],
        Settings.client['DB_CONNECTION']['DB'],
        True if args['loglevel'] == 'debugalll' else False)

# following packages must be imported only after database initilization
from engines.BackTester import BackTester
from engines.RealTimeSimulator import RealTimeSimulator
from db_managers.ExecManager import ExecManager

if not os.path.isfile(args['inputconfig']):
	raise Exception('Input configuration file could not be loaded. ' + os.path.realpath(args['inputconfig']) + " does not correspond to a valid file.")

with open(os.path.realpath(args['inputconfig'])) as myfile:
	inputConf = myfile.read()

with Db.session_scope():
	cretenExec = ExecManager.createCretenExec(args['mode'], inputConf)
	cretenExecId = cretenExec.creten_exec_id

log = Logger()

log.debug("Creating exchange client...")
client = ExchangeClientFactory.getExchangeClient(args)

log.debug("Creating creten engine...")
if args['mode'] == PARAM_MODE_BACKTEST:
	simulator = BackTester(client, inputConf, cretenExecId)
	simulator.run()
elif args['mode'] == PARAM_MODE_REALTIMETEST:
	simulator = RealTimeSimulator(client, inputConf, cretenExecId)
	simulator.run()
예제 #32
0
class ExchangeEventSimulator(object):
    def __init__(self, marketDataManager, orderManager, portfolioManager,
                 exchangeDataListener):
        self.marketDataManager = marketDataManager
        self.orderManager = orderManager
        self.portfolioManager = portfolioManager
        self.exchangeDataListener = exchangeDataListener

        self.cretenExecDetlId = None

        self.log = Logger(logPrefix='', logForceDebug=False)

    # Comparator to evaluate order in which trade orders are evaluated
    # Primary priority is order type and the priority is
    #   1. market orders
    #   2. stop loss market orders
    #   3. stop loss limit orders
    #   4. take profit market orders
    #   5. take profit limit orders
    #   6. limit orders
    # Secondary priority depends on order type and side and is as follows:
    #   market => order_id
    #   stop loss sell market => stop price desc
    #   stop loss sell limit => stop price desc
    #   stop loss buy market => stop price
    #   stop loss buy limit => stop price
    #   take profit sell market => stop price
    #   take profit sell limit => stop price
    #   take profit buy market => stop price desc
    #   take profit buy limit => stop price desc
    #   limit => price
    @staticmethod
    def orderIterComp(order):
        if order.getOrderType() == OrderType.MARKET:
            primaryPrio = 1
            secondaryPrio = order.getOrderId()
        elif order.getOrderType(
        ) == OrderType.STOP_LOSS_MARKET and order.getOrderSide(
        ) == OrderSide.SELL:
            primaryPrio = 2
            secondaryPrio = -1 * order.getStopPrice()
        elif order.getOrderType(
        ) == OrderType.STOP_LOSS_LIMIT and order.getOrderSide(
        ) == OrderSide.SELL:
            primaryPrio = 3
            secondaryPrio = -1 * order.getStopPrice()
        elif order.getOrderType(
        ) == OrderType.STOP_LOSS_MARKET and order.getOrderSide(
        ) == OrderSide.BUY:
            primaryPrio = 4
            secondaryPrio = order.getStopPrice()
        elif order.getOrderType(
        ) == OrderType.STOP_LOSS_LIMIT and order.getOrderSide(
        ) == OrderSide.BUY:
            primaryPrio = 5
            secondaryPrio = order.getStopPrice()
        elif order.getOrderType(
        ) == OrderType.TAKE_PROFIT_MARKET and order.getOrderSide(
        ) == OrderSide.SELL:
            primaryPrio = 6
            secondaryPrio = order.getStopPrice()
        elif order.getOrderType(
        ) == OrderType.TAKE_PROFIT_LIMIT and order.getOrderSide(
        ) == OrderSide.SELL:
            primaryPrio = 7
            secondaryPrio = order.getStopPrice()
        elif order.getOrderType(
        ) == OrderType.TAKE_PROFIT_MARKET and order.getOrderSide(
        ) == OrderSide.BUY:
            primaryPrio = 8
            secondaryPrio = -1 * order.getStopPrice()
        elif order.getOrderType(
        ) == OrderType.TAKE_PROFIT_LIMIT and order.getOrderSide(
        ) == OrderSide.BUY:
            primaryPrio = 9
            secondaryPrio = -1 * order.getStopPrice()
        elif order.getOrderType() == OrderType.LIMIT:
            primaryPrio = 10
            secondaryPrio = order.getPrice()
        else:
            raise Exception("Unknown order type for order id " +
                            str(order.getOrderId()))

        return primaryPrio, secondaryPrio

    def simulateEvent(self, candle):
        # Simulate events as long as there is a trade pending confirmation (opening, cancellation, ...). Several iterations
        # might be required since in some cases new pending orders are produced within previous iteration
        while True:
            # Trade order cache has to be iterated via keys since its content may change on the fly (e.g. a filled trade can
            # lead to cancellation of pending trades). Furthermore, the keys are ordered by priority (for more details
            # see comparator description)
            for orderKey in sorted(
                    self.orderManager.getLiveOrderCache().keys(),
                    key=lambda x: self.orderIterComp(self.orderManager.
                                                     getLiveOrderCache()[x])):
                # Retrieving order from the cache has to be encapsulated in a try block since some orders could
                # disappear during processing (e.g. when trade is closed)
                try:
                    order = self.orderManager.getLiveOrderCache()[orderKey]
                except KeyError:
                    break

                trade = self._findTrade(order.getTradeId())

                # always confirm new orders
                if order.getOrderState() == OrderState.OPEN_PENDING_EXT:
                    self.log.debug('>>> [' + str(candle) + ']')
                    self.log.debug('Confirming order ' +
                                   str(order.getOrderId()))
                    response = OrderResponse(
                        baseAsset=trade.getBaseAsset(),
                        quoteAsset=trade.getQuoteAsset(),
                        orderSide=order.getOrderSide(),
                        orderType=order.getOrderType(),
                        origQty=order.getQty(),
                        lastExecutedQty='0',
                        sumExecutedQty='0',
                        price=order.getPrice(),
                        orderState=OrderState.OPENED,
                        orderTmstmp=order.getInitTmstmp(),
                        clientOrderId=order.getIntOrderRef(),
                        extOrderRef=order.getIntOrderRef())
                    self.exchangeDataListener.processOrderUpdate(response)

                # always cancel orders pending cancellation
                if order.getOrderState() == OrderState.CANCEL_PENDING_EXT:
                    self.log.debug('>>> [' + str(candle) + ']')
                    self.log.debug('Cancelling order ' +
                                   str(order.getOrderId()))
                    response = OrderResponse(
                        baseAsset=trade.getBaseAsset(),
                        quoteAsset=trade.getQuoteAsset(),
                        orderSide=order.getOrderSide(),
                        orderType=order.getOrderType(),
                        origQty=order.getQty(),
                        lastExecutedQty='0',
                        sumExecutedQty='0',
                        price=order.getPrice(),
                        orderState=OrderState.CANCELED,
                        orderTmstmp=candle.getCloseTime(),
                        clientOrderId=order.getIntOrderRef(),
                        extOrderRef=order.getIntOrderRef())
                    self.exchangeDataListener.processOrderUpdate(response)

                # evaluate market orders
                if order.getOrderState() == OrderState.OPENED and \
                  order.getOrderType() == OrderType.MARKET:
                    self.log.debug('>>> [' + str(candle) + ']')
                    self.log.debug('Filling market order ' +
                                   str(order.getOrderId()))
                    response = OrderResponse(
                        baseAsset=trade.getBaseAsset(),
                        quoteAsset=trade.getQuoteAsset(),
                        orderSide=order.getOrderSide(),
                        orderType=order.getOrderType(),
                        origQty=order.getQty(),
                        lastExecutedQty=order.getQty(),
                        sumExecutedQty=order.getQty(),
                        price=order.getPrice(),
                        orderState=OrderState.FILLED,
                        orderTmstmp=order.getOpenTmstmp(),
                        clientOrderId=order.getIntOrderRef(),
                        extOrderRef=order.getIntOrderRef())
                    self.exchangeDataListener.processOrderUpdate(response)

                    self._updatePosition(response)

                # evaluate limit orders
                if order.getOrderState() == OrderState.OPENED and \
                  order.getOrderType() == OrderType.LIMIT and \
                  (
                   (order.getOrderSide() == OrderSide.SELL and candle.getUpperBody() >= order.getPrice()) or
                   (order.getOrderSide() == OrderSide.BUY and candle.getLowerBody() <= order.getPrice())
                  ):
                    self.log.info('>>> [' + str(candle) + ']')
                    self.log.info('Filling limit order ' +
                                  str(order.getOrderId()))
                    response = OrderResponse(
                        baseAsset=trade.getBaseAsset(),
                        quoteAsset=trade.getQuoteAsset(),
                        orderSide=order.getOrderSide(),
                        orderType=order.getOrderType(),
                        origQty=order.getQty(),
                        lastExecutedQty=order.getQty(),
                        sumExecutedQty=order.getQty(),
                        price=order.getPrice(),
                        orderState=OrderState.FILLED,
                        orderTmstmp=candle.getCloseTime(),
                        clientOrderId=order.getIntOrderRef(),
                        extOrderRef=order.getIntOrderRef())
                    self.exchangeDataListener.processOrderUpdate(response)

                    self._updatePosition(response)

                # evaluate stop loss market/limit orders
                if order.getOrderState() == OrderState.OPENED and \
                  order.getOrderType() in [OrderType.STOP_LOSS_LIMIT, OrderType.STOP_LOSS_MARKET] and \
                  (
                   (order.getOrderSide() == OrderSide.SELL and candle.getLow() <= order.getStopPrice()) or
                   (order.getOrderSide() == OrderSide.BUY and candle.getHigh() >= order.getStopPrice())
                  ):
                    self.log.info('>>> [' + str(candle) + ']')
                    self.log.info('Filling stop loss order ' +
                                  str(order.getOrderId()))
                    response = OrderResponse(
                        baseAsset=trade.getBaseAsset(),
                        quoteAsset=trade.getQuoteAsset(),
                        orderSide=order.getOrderSide(),
                        orderType=order.getOrderType(),
                        origQty=order.getQty(),
                        lastExecutedQty=order.getQty(),
                        sumExecutedQty=order.getQty(),
                        price=order.getStopPrice(),
                        orderState=OrderState.FILLED,
                        orderTmstmp=candle.getCloseTime(),
                        clientOrderId=order.getIntOrderRef(),
                        extOrderRef=order.getIntOrderRef())
                    self.exchangeDataListener.processOrderUpdate(response)

                    self._updatePosition(response)

                # evaluate take profit market/limit orders
                if order.getOrderState() == OrderState.OPENED and \
                  order.getOrderType() in [OrderType.TAKE_PROFIT_LIMIT, OrderType.TAKE_PROFIT_MARKET] and \
                  (
                   (order.getOrderSide() == OrderSide.SELL and candle.getLow() >= order.getStopPrice()) or
                   (order.getOrderSide() == OrderSide.BUY and candle.getHigh() <= order.getStopPrice())
                  ):
                    self.log.info('>>> [' + str(candle) + ']')
                    self.log.info('Filling take profit order ' +
                                  str(order.getOrderId()))
                    response = OrderResponse(
                        baseAsset=trade.getBaseAsset(),
                        quoteAsset=trade.getQuoteAsset(),
                        orderSide=order.getOrderSide(),
                        orderType=order.getOrderType(),
                        origQty=order.getQty(),
                        lastExecutedQty=order.getQty(),
                        sumExecutedQty=order.getQty(),
                        price=order.getStopPrice(),
                        orderState=OrderState.FILLED,
                        orderTmstmp=candle.getCloseTime(),
                        clientOrderId=order.getIntOrderRef(),
                        extOrderRef=order.getIntOrderRef())
                    self.exchangeDataListener.processOrderUpdate(response)

                    self._updatePosition(response)

            # if there is no order pending confirmation, do not reiterate
            if not self._existOrderPendingConf():
                break

    def setCretenExecDetlId(self, cretenExecDetlId):
        self.cretenExecDetlId = cretenExecDetlId

    def _existOrderPendingConf(self):
        for order in self.orderManager.getLiveOrderCache().values():
            if order.getOrderState() in [OrderState.OPEN_PENDING_EXT, OrderState.CANCEL_PENDING_EXT] or \
              (order.getOrderState() == OrderState.OPENED and order.getOrderType() == OrderType.MARKET):
                return True

        return False

    def _findTrade(self, tradeId):
        for trade in self.orderManager.getLiveTradeCache().values():
            if trade.getTradeId() == tradeId:
                return trade

        return None

    def _updatePosition(self, orderResponse):
        basePosition = self.portfolioManager.getPosition(
            orderResponse.getBaseAsset())
        quotePosition = self.portfolioManager.getPosition(
            orderResponse.getQuoteAsset())

        if orderResponse.getOrderSide() == OrderSide.BUY:
            basePosition.setFree(basePosition.getFree() +
                                 float(orderResponse.getOrigQty()))
            quotePosition.setFree(quotePosition.getFree() -
                                  float(orderResponse.getOrigQty() *
                                        orderResponse.getPrice()))
        else:
            basePosition.setFree(basePosition.getFree() -
                                 float(orderResponse.getOrigQty()))
            quotePosition.setFree(quotePosition.getFree() +
                                  float(orderResponse.getOrigQty() *
                                        orderResponse.getPrice()))

        self.exchangeDataListener.processPortfolioUpdate(basePosition)
        self.exchangeDataListener.processPortfolioUpdate(quotePosition)
예제 #33
0
class ExchangeDataListener:
    __metaclass__ = ABCMeta

    def __init__(self, exchangeClient, marketDataManager, marketRulesManager,
                 portfolioManager, orderManager):
        self.exchangeClient = exchangeClient
        self.marketDataManager = marketDataManager
        self.marketRulesManager = marketRulesManager
        self.portfolioManager = portfolioManager
        self.orderManager = orderManager

        self.candleSubscriptions = []
        self.candleListenerCallbackMap = {}
        self.callbackPortfolio = None
        self.callbackOrders = None

        self.cretenExecDetlId = None

        self.lock = RLock()

        self.log = Logger()

    @abstractmethod
    def start(self):
        pass

    @abstractmethod
    def parseCandleUpdate(self, msg):
        pass

    @abstractmethod
    def parseOrderUpdate(self, msg):
        pass

    @abstractmethod
    def parsePortfolioUpdate(self, msg):
        pass

    def setCretenExecDetlId(self, id):
        self.cretenExecDetlId = id

    def resetCandleListener(self):
        self.candleSubscriptions = []
        self.candleListenerCallbackMap = {}

    def resetUserDataListener(self):
        self.callbackPortfolio = None
        self.callbackOrders = None

    def registerCandleListener(self, pair, cretenInterval, callback):
        self.candleSubscriptions.append(
            CandleSubscriptionKey(pair, cretenInterval))
        self.candleListenerCallbackMap[CandleSubscriptionKey(
            pair, cretenInterval)] = makeList(callback)

    def registerUserDataListener(self,
                                 callbackPortfolio=None,
                                 callbackOrders=None):
        self.callbackPortfolio = makeList(callbackPortfolio)
        self.callbackOrders = makeList(callbackOrders)

    def processCandleUpdate(self, msg):
        with self.lock:
            self.log.debug('Candle message received: ' + str(msg))

            try:
                # 1. parse incoming message
                candle = self.parseCandleUpdate(msg)

                # 2. update market data manager
                self.marketDataManager.processCandle(candle)

                # 3. invoke custom callbacks
                for callback in self.candleListenerCallbackMap[
                        CandleSubscriptionKey(
                            Pair(candle.getBaseAsset(),
                                 candle.getQuoteAsset()),
                            candle.getInterval())]:
                    callback(candle)

                Db.Session().commit()
            except:
                self.log.error('Candle processing failed! Msg [' + str(msg) +
                               ']')

                Db.Session().rollback()
                raise
            finally:
                Db.Session.remove()

            try:
                # 4. create outbound orders
                self.orderManager.sendOrders(self.cretenExecDetlId)
                Db.Session().commit()
            except:
                self.log.error(
                    'Candle processing failed while generating outbound orders! Msg ['
                    + str(msg) + ']')

                Db.Session().rollback()
                raise
            finally:
                Db.Session.remove()

    def processOrderUpdate(self, msg):
        with self.lock:
            try:
                orderResponse = self.parseOrderUpdate(msg)

                # TODO verify that the order is incoming from this instance of creten
                # process only orders originating from creten
                if orderResponse.getClientOrderId(
                )[:len(Settings.ORDER_REFERENCE_PREFIX
                       )] == Settings.ORDER_REFERENCE_PREFIX:
                    self.orderManager.processOrderUpdate(orderResponse)

                    if self.callbackOrders:
                        for callback in self.callbackOrders:
                            callback(orderResponse)
                else:
                    self.log.info(
                        'Order message received for an order not originating from Creten. Msg ['
                        + str(msg) + ']')

                Db.Session().commit()
            except:
                self.log.error('Order update processing failed! Msg [' +
                               str(msg) + ']')

                Db.Session().rollback()
                raise
            finally:
                Db.Session.remove()

            try:
                # create outbound orders
                self.orderManager.sendOrders(self.cretenExecDetlId)
                Db.Session().commit()
            except:
                self.log.error(
                    'Order update processing failed while generating outbound orders! Msg ['
                    + str(msg) + ']')

                Db.Session().rollback()
                raise
            finally:
                Db.Session.remove()

    def processPortfolioUpdate(self, msg):
        with self.lock:
            try:
                positions = self.parsePortfolioUpdate(msg)
                for position in positions:
                    self.portfolioManager.addPosition(position)

                    if self.callbackPortfolio:
                        for callback in self.callbackPortfolio:
                            callback(position)

                Db.Session().commit()
            except:
                self.log.error('Portfolio update processing failed! Msg [' +
                               str(msg) + ']')

                Db.Session().rollback()
                raise
            finally:
                Db.Session.remove()
예제 #34
0
class DispenserAdapter:

    DISPENSERS = {
        "PULOON-LCDM1000": {"class": "Dispenser_LCDM1000", "path": "services.dispenser.dispensers.puloon_lcdm1000"},
        "PULOON-LCDM2000": {"class": "Dispenser_LCDM2000", "path": "services.dispenser.dispensers.puloon_lcdm2000"},
        "PULOON-LCDM4000": {"class": "Dispenser_LCDM4000", "path": "services.dispenser.dispensers.puloon_lcdm4000"},
    }

    serial = None  # Serial Object
    dispenser = None  # Dispenser model implementation

    def __init__(self, dispenser_model, port=False, baudrate=False, timeout=0.6, parity=False):

        if dispenser_model not in self.DISPENSERS:
            raise DispenserError("%s is not supported. See DispenserController.DISPENSERS for available dispensers.")

        self.logger = Logger()
        self.dispenser = self.__import_class(
            self.DISPENSERS[dispenser_model]["path"], self.DISPENSERS[dispenser_model]["class"]
        )()

        # Set data
        if port:
            self.dispenser.port = port

        if baudrate:
            self.dispenser.baudrate = baudrate

        if timeout:
            self.dispenser.timeout = timeout

        if parity:
            self.dispenser.parity = parity

    @staticmethod
    def __import_class(class_path, class_name):
        try:
            module = __import__(class_path, fromlist="*")
            return getattr(module, class_name)

        except Exception as e:
            message = "DispenserAdapter: Couldnt load class %s from path %s With error: %s" % (
                class_name,
                class_path,
                str(e.args),
            )

            if __debug__:
                print message
            raise ImportError(message)

    def connect(self):
        """ Connect to serial port """
        self.logger.debug("DispenserAdapter: port is %s" % self.dispenser.port)
        self.logger.debug("DispenserAdapter: parity is %s" % self.dispenser.parity)
        self.logger.debug("DispenserAdapter: baudrate is %s" % self.dispenser.baudrate)
        self.logger.debug("DispenserAdapter: timeout is %s" % self.dispenser.timeout)

        try:
            self.serial = serial.serial_for_url(
                self.dispenser.port,
                self.dispenser.baudrate,
                parity=self.dispenser.parity,
                timeout=self.dispenser.timeout,
            )
        except SerialException as e:
            raise DispenserError("Error while connecting to dispenser", 500)

        try:
            self.status()
        except:
            pass

    def status(self):

        self.logger.debug("DispenserAdapter: get_status command received")

        response = self.__send(command=self.dispenser.command_status, validator=self.dispenser.validate_status_response)

        self.__write([self.dispenser.p_ack])

        if isinstance(self.dispenser, Dispenser_LCDM4000):
            """ Read the EOT for LCDM4000 """
            try:
                self.__read(timeout=0.6)
            except Exception as e:
                self.logger.debug("DispenserAdapter::status [forr Puloon_LCDM4000] error catched. [%s]" % str(e.args))

        if not response:
            return False

        if "status" in response:
            self.logger.debug("DispenserAdapter: [CMD STATUS] result: %s" % response["status"])
            self.logger.debug("DispenserAdapter: [CMD STATUS] info  : %s" % response)
            return response["status"]

        return False

    def init(self):

        self.logger.debug("DispenserAdapter: init command received")

        response = self.__send(command=self.dispenser.command_init, validator=self.dispenser.validate_init_response)

        self.__write([self.dispenser.p_ack])

        if isinstance(self.dispenser, Dispenser_LCDM4000):
            """ Read the EOT for LCDM4000 """
            time.sleep(2.1)
        # 			try:
        # 				self.__read( timeout = .7 )
        # 			except Exception as e:
        # 				self.logger.debug('DispenserAdapter::init [for Puloon_LCDM4000] error catched. [%s]' % str(e.args))

        self.logger.debug("DispenserAdapter: init finished with result %s" % bool(response))

        if response:
            return True
        return False

    def purge(self):

        self.logger.debug("DispenserAdapter: purge command received")

        response = self.__send(command=self.dispenser.command_purge, validator=self.dispenser.validate_purge_response)

        self.__write([self.dispenser.p_ack])

        if isinstance(self.dispenser, Dispenser_LCDM4000):
            """ Read the EOT for LCDM4000 """
            try:
                self.__read(timeout=0.6)
            except Exception as e:
                self.logger.debug("DispenserAdapter::purge [for Puloon_LCDM4000] error catched. [%s]" % str(e.args))

        if response:
            if "status" in response:
                self.logger.debug("DispenserAdapter: [CMD PURGE] result: %s" % response["status"])
                self.logger.debug("DispenserAdapter: [CMD PURGE] info  : %s" % response)
                return response["status"]
        return False

    def dispense(self, amount, cassets_info):
        """ Dispense the bills
			amount:
				amount to dispense
			cassets_info:
				Cassets infrormation in format {id_cassete : {'command' : 0x45, 'denomination' : 0, 'charging' : 100, 'position' : 'upper'},...}
		"""
        dispense_info = self.dispenser.get_dispense(amount, cassets_info)
        command = None

        self.logger.debug("DispenserAdapter: Starting dispense of amount %s" % amount)

        # Amount is not dispensable
        if dispense_info["to_dispense"] == amount:
            raise DispenserError("Amount is not dispensable", 500)

        if len(dispense_info["cassets"]) > 1:
            command = self.dispenser.command_dispense_several(cassets_info, dispense_info["cassets"])
        else:
            id_cassete, banknotes_count = dispense_info["cassets"].popitem()
            command = self.dispenser.command_dispense_single(cassets_info, id_cassete, banknotes_count)

        response = self.__send(command=command, validator=self.dispenser.validate_dispense_response)

        if not response:
            raise DispenserError("Error while dispensing (response from dispenser is None)", 500)

        self.__write([self.dispenser.p_ack])

        return response

    def __send(self, command, validator=False):

        if not self.serial:
            raise DispenserError("Serial port is not opened")

        self.logger.debug("DispenserAdapter: send command in hex %s" % (["%02X" % x for x in command]))

        """ Пишем, спим полсекундочки и читаем """
        self.logger.debug(">> serial.write %s" % (["%02X" % x for x in command]))
        bytes_written = self.__write(command)

        self.logger.debug(">> serial.read")
        bytes_recd = self.__read(timeout=1)
        self.logger.debug(">> serial.read done")

        if bytes_recd == [self.dispenser.p_nck]:
            raise DispenserError("DispenserAdapter.__send: bytes_recd == NCK")

        if bytes_recd == [self.dispenser.p_ack]:
            self.logger.debug("DispenserAdapter.__send: bytes_recd == ACK")
            bytes_recd = self.__read(timeout=60)
            readed = ["%02X" % x for x in bytes_recd]
        else:
            if isinstance(self.dispenser, Dispenser_LCDM4000):
                self.logger.debug("Dispenser instance is Dispenser_LCDM4000. F**k this!")
                if validator:
                    return validator(bytes_recd)

            readed = ["%02X" % x for x in bytes_recd]
            self.logger.debug("DispenserAdapter.__send [read from dispenser]:  HEX is %s" % readed)
            raise DispenserError("Shit hap in HEX: %s" % readed)

        self.logger.debug(">> serial.read in HEX: %s" % readed)

        # 		if isinstance(self.dispenser, Dispenser_LCDM4000):
        # 			''' Read the EOT for LCDM4000 '''
        # 			try:
        # 				self.__read( timeout = .5 )
        # 			except Exception as e:
        # 				self.logger.debug('DispenserAdapter::dispense [for Puloon_LCDM4000] error catched. [%s]' % str(e.args))

        if validator:
            return validator(bytes_recd)

        self.logger.debug("DispenserAdapter: No validator presents. Return invalidated response.")
        return bytes_recd

    def __write(self, bytes):

        return self.serial.write(serial.to_bytes(bytes))

    def __read(self, timeout=60):

        time_start = time.time()
        frame = []
        data = ""
        byte = self.serial.read(1)
        data += byte

        try:
            first_byte = int(byte2hex(byte), 16)

            self.logger.debug("DispenserAdapter.__read: first byte is: 0x%02X" % first_byte)

            if first_byte == self.dispenser.p_nck:
                self.logger.debug("DispenserAdapter.__read: first byte is NCK")
                return [self.dispenser.p_nck]

            if first_byte == self.dispenser.p_ack:
                self.logger.debug("DispenserAdapter.__read: first byte is: ACK")
                return [self.dispenser.p_ack]

            byte = self.serial.read(1)
            data += byte

            self.logger.debug("DispenserAdapter.__read: Wrong first byte in response 0x%02X" % first_byte)
            self.logger.debug("DispenserAdapter.__read: Next byte in response is 0x%02X" % int(byte2hex(byte)))

        except Exception as e:
            self.logger.debug("DispenserAdapter.__read: %s" % str(e.args))
            pass

        self.logger.debug("DispenserAdapter.__read: first byte is detected")

        while True:
            if time.time() - time_start > timeout:
                self.logger.debug("DispenserAdapter.__read: timeout to read. ")
                for _, i in enumerate(serial.to_bytes(data)):
                    frame.append(int(byte2hex(i), 16))

                self.logger.debug(
                    "DispenserAdapter.__read: full dump of response: %s" % (["0x%02X" % x for x in frame])
                )
                raise DispenserError(message="Timeout to read", code=500)

            try:
                if int(byte2hex(byte), 16) == self.dispenser.p_etx:
                    break
            except Exception as e:
                self.logger.debug("Error in read cycle: %s" % str(e.args))
                time.sleep(0.01)

            byte = self.serial.read(1)
            data += byte

        self.logger.debug("DispenserAdapter.__read: cycle is complete")

        data += self.serial.read(1)

        for _, i in enumerate(serial.to_bytes(data)):
            frame.append(int(byte2hex(i), 16))

        self.logger.debug("DispenserAdapter.__read: full dump of response: [%s]" % ([x for x in frame]))
        return frame