singleton内部线程中的python服务



我有一个单例,我想在其中从服务接收数据。由于我是python的新手,我唯一能弄清楚的方法就是服务通过方法指针将数据发送到singleton。我使用线程来阻止服务通过其无限循环锁定单例。

实际情况是,我想延长莫克。GPIO(RPi.GPIO的模拟实现(,https://github.com/codenio/Mock.GPIO,能够模拟在真实RPi上按下按钮。singleton表示简化的RPi板。

我想用以下内容调用套接字服务echo -n "22:HI" > /dev/tcp/127.0.0.1/5566

我希望它能产生

Service __init__
piBoardCallback
22:HI
22
HI
<something that represents event>

代码现在实际运行。

相反,我得到了5个错误,这是目前的问题!!!

Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 801, in __bootstrap_inner
self.run()
File "/usr/lib/python2.7/threading.py", line 754, in run
self.__target(*self.__args, **self.__kwargs)
File "/Extra/home/max/DEV/Code/workspaces/2019/Mock.GPIO/Mock/PiBoard.py", line 130, in run
self.svc.listen(self.piBoardCallback)
File "/Extra/home/max/DEV/Code/workspaces/2019/Mock.GPIO/Mock/PiBoard.py", line 108, in listen
self.serviceThreadCallback(data)
TypeError: piBoardCallback() takes exactly 2 arguments (1 given)

当涉及到自我、类、类(类型(和类(对象(时,我迷失了方向

请给我指正确的方向。

以下是我到目前为止的课程。他们还没有完成。

如果您有其他解决方案可以将信号更改传递给通过add_event_detect方法注册的回调方法,我愿意接受建议。

参考原始模型。GPIO代码,https://github.com/codenio/Mock.GPIO/blob/master/Mock/GPIO.py

我的源

import GPIO as GPIO
import threading
import time
import socket as sk

class Board():
channelConfigs = {}
channelEvents = {}
__instance = None
global serviceThread
global piBoardCallback

@staticmethod 
def getInstance():
""" Static access method. """
if Board.__instance == None:
Board()
return Board.__instance

def __init__(self):
if Board.__instance != None:
raise Exception("This class is a singleton!")
else:
self.serviceThread = ServiceThread()
self.serviceThread.piBoardCallback = piBoardCallback
self.serviceThread.threadify()

Board.__instance = self


def piBoardCallback(self,val):
print "piBoardCallback"
# This assumes that val is in format {channel:[HI|LOW]}
x = val.split(":")
print(val)
channel = x[0]
edge = x[1]
print(channel)
print(edge)
event = self.channelEvents[channel]
print(event)

def setChannelConfig(self, channel):
if channel != None:
self.channelConfigs[channel.chanel] = channel

def setChannelEvent(self, channel, edge, eventCallback):
if channel != None:
event = Event(edge,eventCallback)
self.channelEvents[channel] = Event(edge,eventCallback)

# class Event(object):
class Event:
'''
classdocs
'''

global eventCallback
global edge

def __init__(self,edge,eventCallback):
'''
Constructor
'''
self.edge = edge
self.eventCallback = eventCallback 

def getEventCallback():
return self.eventCallback

def setEventCallback(eventCallback):
self.eventCallback = eventCallback

class Service:

global serviceThreadCallback

def __init__(self):
print "Service __init__"

def listen(self,serviceThreadCallback):
self.serviceThreadCallback = serviceThreadCallback
connection = sk.socket(sk.AF_INET, sk.SOCK_STREAM)
connection.setsockopt(sk.SOL_SOCKET, sk.SO_REUSEADDR, 1)
connection.bind(('0.0.0.0', 5566))
connection.listen(10)
while True:
current_connection, address = connection.accept()
while True:
data = current_connection.recv(2048)

if data == 'quit\n':
current_connection.shutdown(1)
current_connection.close()
break

elif data == 'stop\n':
current_connection.shutdown(1)
current_connection.close()
exit()

elif data:
self.serviceThreadCallback(data)

else:
break         

def setCallback(self, serviceThreadCallback):
self.serviceThreadCallback = serviceThreadCallback

def getCallback(serviceThreadCallback):
return self.serviceThreadCallback

class ServiceThread:

global piBoardCallback
global thread
global svc

def __init__(self, interval=1):
self.interval = interval

def run(self):
self.svc = Service()
self.svc.listen(self.piBoardCallback)

def setPiBoardCallback(piBoardCallback):
self.piBoardCallback = piBoardCallback

def getPiBoardCallback():
return self.piBoardCallback

def threadify(self):
self.thread = threading.Thread(target=self.run)
self.thread.daemon = True  # Daemonize thread
self.thread.start()  # Start the execution

def serviceThreadCallback(val):
self.piBoardCallback(val)

def ext_callback(val):
print "ext_callback"
print val        

if __name__ == '__main__': 

try:
while True:
GPIO.setmode(GPIO.BCM)
GPIO.setup(22, GPIO.IN, 0, GPIO.PUD_UP)
GPIO.add_event_detect(22, GPIO.FALLING, ext_callback, bouncetime=1500)
time.sleep(1000)
except KeyboardInterrupt:
pass      


我的GPIO代码

"""
Mock Library for RPi.GPIO
"""

import time
import logging
import os
# import yaml
import PiBoard

logger = logging.getLogger(__name__)

log_level = os.getenv('LOG_LEVEL')

if log_level is not None:
if log_level == "Info":
logger.setLevel(logging.INFO)
if log_level == "Debug":
logger.setLevel(logging.DEBUG)
if log_level == "Warning":
logger.setLevel(logging.WARNING)
if log_level == "Error":
logger.setLevel(logging.ERROR)
if log_level == "Critical":
logger.setLevel(logging.CRITICAL)
else:
logger.setLevel(logging.ERROR)

stream_formatter = logging.Formatter('%(asctime)s:%(levelname)s: %(message)s')
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(stream_formatter)
logger.addHandler(stream_handler)

BCM = 11
BOARD = 10
BOTH = 33
FALLING = 32
HARD_PWM = 43
HIGH = 1
I2C = 42
IN = 1
LOW = 0
OUT = 0
PUD_DOWN = 21
PUD_OFF = 20
PUD_UP = 22
RISING = 31
RPI_INFO = {'MANUFACTURER': 'Sony', 'P1_REVISION': 3, 'PROCESSOR': 'BCM2837', 'RAM': '1G', 'REVISION': 'a020d3', 'TYPE': 'Pi 3 Model B+'}
RPI_REVISION = 3
SERIAL = 40
SPI = 41
UNKNOWN = -1
VERSION = '0.7.0'

_mode = 0

channel_config = {}

#flags
setModeDone = False

class Channel:
def __init__(self,channel, direction, initial=0,pull_up_down=PUD_OFF):
self.chanel = channel
self.direction = direction
self.initial = initial
self.current = initial
self.pull_up_down = pull_up_down


#GPIO LIBRARY Functions
def setmode(mode):
"""
Set up numbering mode to use for channels.
BOARD - Use Raspberry Pi board numbers
BCM   - Use Broadcom GPIO 00..nn numbers
"""

board = getBoard()

# GPIO = GPIO()
time.sleep(1)
if(mode == BCM):
setModeDone = True
_mode = mode

elif (mode == BOARD):
setModeDone = True
else:
setModeDone = False

def getmode():
"""
Get numbering mode used for channel numbers.
Returns BOARD, BCM or None
"""
board = getBoard()
return _mode

def setwarnings(flag):
"""
Enable or disable warning messages
"""
board = getBoard()
logger.info("Set Warings as {}".format(flag))

def setup(channel, direction, initial=0,pull_up_down=PUD_OFF):
"""
Set up a GPIO channel or list of channels with a direction and (optional) pull/up down control
channel        - either board pin number or BCM number depending on which mode is set.
direction      - IN or OUT
[pull_up_down] - PUD_OFF (default), PUD_UP or PUD_DOWN
[initial]      - Initial value for an output channel

"""
logger.info("setup channel : {} as {} with intial :{} and pull_up_dowm {}".format(channel,direction,initial,pull_up_down))
board = getBoard()
global channel_config
channel_config[channel] = Channel(channel, direction, initial, pull_up_down)
board.setChannelConfig(channel_config[channel])

def output(channel, value):
"""
Output to a GPIO channel or list of channels
channel - either board pin number or BCM number depending on which mode is set.
value   - 0/1 or False/True or LOW/HIGH

"""
board = getBoard()
logger.info("output channel : {} with value : {}".format(channel, value))

def input(channel):
"""
Input from a GPIO channel.  Returns HIGH=1=True or LOW=0=False
channel - either board pin number or BCM number depending on which mode is set.
"""
board = getBoard()
logger.info("reading from chanel {}".format(channel))

def wait_for_edge(channel,edge,bouncetime,timeout):
"""
Wait for an edge.  Returns the channel number or None on timeout.
channel      - either board pin number or BCM number depending on which mode is set.
edge         - RISING, FALLING or BOTH
[bouncetime] - time allowed between calls to allow for switchbounce
[timeout]    - timeout in ms
"""
board = getBoard()

logger.info("waiting for edge : {} on channel : {} with bounce time : {} and Timeout :{}".format(edge,channel,bouncetime,timeout))


def add_event_detect(channel,edge,callback,bouncetime):
"""
Enable edge detection events for a particular GPIO channel.
channel      - either board pin number or BCM number depending on which mode is set.
edge         - RISING, FALLING or BOTH
[callback]   - A callback function for the event (optional)
[bouncetime] - Switch bounce timeout in ms for callback
"""
#     board = 
getBoard().setChannelEvent(channel, edge, callback)
logger.info("Event detect added for edge : {} on channel : {} with bouce time : {} and callback {}".format(edge,channel,bouncetime,callback))

def event_detected(channel):
"""
Returns True if an edge has occurred on a given GPIO.  You need to enable edge detection using add_event_detect() first.
channel - either board pin number or BCM number depending on which mode is set.
"""
board = getBoard()
logger.info("Waiting for even detection on channel :{}".format(channel))

def add_event_callback(channel,callback):
"""
Add a callback for an event already defined using add_event_detect()
channel      - either board pin number or BCM number depending on which mode is set.
callback     - a callback function
"""
logger.info("Event Calback : {} added for channel : {}".format(callback,channel))

def remove_event_detect(channel):
"""
Remove edge detection for a particular GPIO channel
channel - either board pin number or BCM number depending on which mode is set.
"""
board = getBoard()
logger.info("Event Detect Removed for channel : {}".format(channel))

def gpio_function(channel):
"""
Return the current GPIO function (IN, OUT, PWM, SERIAL, I2C, SPI)
channel - either board pin number or BCM number depending on which mode is set.
"""
board = getBoard()
logger.info("GPIO function of Channel : {} is {}".format(channel,channel_config[channel].direction))


class PWM:
# initialise PWM channel
def __init__(self, channel, frequency):
"""
x.__init__(...) initializes x; see help(type(x)) for signature
"""
self.chanel = channel
self.frequency = frequency
self.dutycycle = 0
global channel_config
channel_config[channel] = Channel(channel,PWM,)
board = getBoard()
logger.info("Initialized PWM for Channel : {} at frequency : {}".format(channel,frequency))

# where dc is the duty cycle (0.0 <= dc <= 100.0)
def start(self, dutycycle):
"""
Start software PWM
dutycycle - the duty cycle (0.0 to 100.0)
"""
self.dutycycle = dutycycle
board = getBoard()
logger.info("start pwm on channel : {} with Duty cycle : {}".format(self.chanel,dutycycle))

# where freq is the new frequency in Hz
def ChangeFrequency(self, frequency):
"""
Change the frequency
frequency - frequency in Hz (freq > 1.0)
"""
board = getBoard()
logger.info("Freqency Changed for channel : {} from : {} -> to : {}".format(self.chanel,self.frequency,frequency))
self.frequency = frequency

# where 0.0 <= dc <= 100.0
def ChangeDutyCycle(self, dutycycle):
"""
Change the duty cycle
dutycycle - between 0.0 and 100.0
"""
board = getBoard()
self.dutycycle = dutycycle
logger.info("Dutycycle Changed for channel : {} from : {} -> to : {}".format(self.chanel,self.dutycycle,dutycycle))

# stop PWM generation
def stop(self):
board = getBoard()
logger.info("Stop pwm on channel : {} with Duty cycle : {}".format(self.chanel,self.dutycycle))


def cleanup(channel=None):
"""
Clean up by resetting all GPIO channels that have been used by this program to INPUT with no pullup/pulldown and no event detection
[channel] - individual channel or list/tuple of channels to clean up.  Default - clean every channel that has been used.
"""
board = getBoard()
if channel is not None:
logger.info("Cleaning Up Channel : {}".format(channel))
else:
logger.info("Cleaning Up all channels")

def getBoard():
rpib = PiBoard.Board.getInstance()
if rpib == None:
rpib = PiBoard.Board()
return rpib

正如你所看到的;仅";与原来的不同之处在于,我的GPIO实例化了PiBoard,并在方法中获得了这些实例化。在方法add_event_detect中,我调用PiBoard中的setChannelEvent

以下是一个非常丑陋的解决方案,但它确实有效。需要一些逻辑,但它在Singleton中的线程中有一个套接字服务。当调用服务时,最终会调用一个外部方法ext_callback。

import threading
import time
import socket as sk

class Board:
channelConfigs = {}
channelEvents = {}
__instance = None
serviceThread = None

@staticmethod 
def getInstance():
""" Static access method. """
if Board.__instance == None:
Board()
return Board.__instance

def __init__(self):
if Board.__instance != None:
raise Exception("This class is a singleton!")
else:
Board.__instance = self
Board.__instance.serviceThread = ServiceThread()
Board.__instance.serviceThread.setPiBoardCallback(Board.__instance.piBoardCallback)
Board.__instance.serviceThread.threadify()

def piBoardCallback(_piBoardInstance, _value):

global channelEvents
# This assumes that _value is in format {channel:[HI|LOW]}, i.e. 22:HI
values = _value.split(":")
channel = values[0]
edge = values[1]

event = _piBoardInstance.channelEvents[int(channel)]
# TODO: Handle logic on wether to call event callback or not.
event.eventCallback(event)

def setChannelConfig(_piBoardInstance, channel):
if channel != None:
_piBoardInstance.channelConfigs[channel.chanel] = channel

def setChannelEvent(_piBoardInstance, _channel, _edge, _channelEventCallback):

if _channel != None:
event = Event(_edge, _channelEventCallback, _channel)
_piBoardInstance.channelEvents[_channel] = event

class Event:
eventCallback = None
edge = None
channel = None
def __init__(self, _edge, _eventCallback, _channel):
self.eventCallback = _eventCallback
self.edge = _edge
self.channel = _channel

class Service:

serviceThreadCallback = None

def __init__(self):
print(self)

def listen(self, _serviceThreadCallback):
global serviceThreadCallback
serviceThreadCallback = _serviceThreadCallback
connection = sk.socket(sk.AF_INET, sk.SOCK_STREAM)
connection.setsockopt(sk.SOL_SOCKET, sk.SO_REUSEADDR, 1)
connection.bind(('0.0.0.0', 5566))
connection.listen(10)
while True:
current_connection, address = connection.accept()
while True:
data = current_connection.recv(2048)

if data == 'quit\n':
current_connection.shutdown(1)
current_connection.close()
break

elif data == 'stop\n':
current_connection.shutdown(1)
current_connection.close()
exit()

elif data:
_serviceThreadCallback(data)

else:
break         

def setCallback(_serviceThreadCallback):
global serviceThreadCallback
serviceThreadCallback = _serviceThreadCallback

class ServiceThread:
thread = None
svc = None
piBoardCallback = None
def __init__(self, interval=1):
self.interval = interval
def run(self):
global piBoardCallback
self.svc = Service()
self.svc.listen(piBoardCallback)
def setPiBoardCallback(_serviceThread, _piBoardCallback):
global piBoardCallback
piBoardCallback = _piBoardCallback

def threadify(self):
global thread
thread = threading.Thread(target=self.run)
thread.daemon = True  # Daemonize thread
thread.start()  # Start the execution

def ext_callback(_event):
print "ext_callback"
print _event.channel       
print _event.edge       
print _event.eventCallback
def getBoard():
_rpib = Board.getInstance()
if _rpib == None:
_rpib = Board()
return _rpib
class Channel:
def __init__(self,channel, direction, initial=0,pull_up_down=0):
self.chanel = channel
self.direction = direction
self.initial = initial
self.current = initial
self.pull_up_down = pull_up_down

if __name__ == '__main__': 

try:
while True:
rpib = getBoard()
rpib.setChannelConfig(Channel(22, 32, 0, 0))
rpib.setChannelEvent(22, 32, ext_callback)
time.sleep(1000)
except KeyboardInterrupt:
pass      

最新更新