def testEmptyNotAllowed(self): try: enum = Enum() except AssertionError: pass else: fail("Expected assertion - empty enums not allowed")
class Messenger(): '''Messenger provides an abstraction layer for a pub/sub message bus. Clients work with messenger directly, and plumberjack wires the appropriate implementation in to the system. This class is an abstract base class and requires a concrete implementation to operate.''' __metaclass__ = ABCMeta TOPIC_SYSTEM = 'system' TOPIC_SYSTEM_CONTROL = 'system.control' TOPIC_COMPONENT_STATUS = 'component.status' TOPIC_COMPONENT_HEARTBEAT = 'component.heartbeat' TOPIC_SENSOR_DATA = 'component.sensor.data' MESSAGE_CATEGORY = Enum('Panic', 'Alert', 'Update', 'Command') @classmethod def buildPacket(self, msgtopic=TOPIC_SENSOR_DATA, msgcategory=MESSAGE_CATEGORY.Update, msgbus=None, msgdevice=None, msgvalue=None, msgdata=None): return MessagePacket(topic=msgtopic, category=msgcategory, bus=msgbus, device=msgdevice, value=msgvalue, data=msgdata, timestamp=time.time()) @abstractmethod def publish(self, sender, topic, message): '''Publish a message to the specified topic. The specified topic MUST have been previously registered via registerTopic or an exception will be thrown.''' pass @abstractmethod def subscribe(self, subscriber, topic, callback=None): '''Subscribe to the specified topic. Messages sent to a given topic will be sent to all subscribers. The specified topic MUST have been previously registered via registerTopic or an exception will be thrown.''' pass @abstractmethod def registerTopic(self, topic): '''Register topic to make it available for publish and/or subscribe.''' pass @abstractmethod def getTopics(self): '''Retrieve a list of known/registered topics''' pass @abstractmethod def unsubscribe(self, subscriber, topic): '''Remove a subscriber from a topic''' pass @abstractmethod def removeSubscriber(self, subscriber): '''Remove a subscriber from all topics''' pass
class BaseSphereEntity(): '''Base class for entities within Sphere''' DATA_TYPES = Enum('String','Integer','Float','Boolean','Date','Binary') UNKNOWN = '(Unknown)' def __init__(self, name = BaseSphereEntity.UNKNOWN, description = BaseSphereEntity.UNKNOWN): self._name = name self._description = description name = property(lambda self: self._name) description = property(lambda self: self._description)
class EnumTests(unittest.TestCase): def setUp(self): self.enum = Enum('Dog','Cat','Fish') def testEmptyNotAllowed(self): try: enum = Enum() except AssertionError: pass else: fail("Expected assertion - empty enums not allowed") def testInclusion(self): assert self.enum.Cat def testInclusion_case(self): try: items = self.enum.cat except: pass else: fail("Expected assertion - enums are case sensitive") def testExclusion(self): try: items = self.enum.Frog except: pass else: fail("Expected assertion - item not in set") def testEquals(self): cat = self.enum.Cat anotherCat = self.enum.Cat assert (cat == anotherCat) def testLength(self): assert(self.enum.__len__() == 3)
class Device(BaseSphereEntity): '''Represents a single Device within Sphere. A Device can represent nearly anything, from a physical item like a switch or a light to a virtual state machine. Devices within Sphere are typically contained within a Device Bus. Devices can be represented as nested items - devices can contain other devices. For example, a single device representing a relay board may contain several nested sensor and actuator devices, each representing a single controllable or readable resource.''' DEVICE_STATUS = Enum('Unknown', 'New', 'Available', 'Missing', 'Error') def __init__(self): BaseSphereEntity.__init__(self) self._address = None self._configuration = dict() self._status = Device.DEVICE_STATUS.Unknown self._lastUpdate = time.time() self._devices = list() self._deviceType = None address = property(lambda self: self._address) configuration = property(lambda self: self._configuration) status = property(lambda self: self._status) lastUpdate = property(lambda self: self._lastUpdate) devices = property(lambda self: self._devices) deviceType = property(lambda self: self._deviceType)
class EnumTests(unittest.TestCase): def setUp(self): self.enum = Enum('Dog', 'Cat', 'Fish') def testEmptyNotAllowed(self): try: enum = Enum() except AssertionError: pass else: fail("Expected assertion - empty enums not allowed") def testInclusion(self): assert self.enum.Cat def testInclusion_case(self): try: items = self.enum.cat except: pass else: fail("Expected assertion - enums are case sensitive") def testExclusion(self): try: items = self.enum.Frog except: pass else: fail("Expected assertion - item not in set") def testEquals(self): cat = self.enum.Cat anotherCat = self.enum.Cat assert (cat == anotherCat) def testLength(self): assert (self.enum.__len__() == 3)
class Controllable: '''ABC for controllable components (components with start/stop functionality) Adds basic features common to these components - lifecycle management, logging, and messenger.''' __metaclass__ = ABCMeta STATUS = Enum('Unknown', 'Starting', 'Started', 'Stopping', 'Stopped', 'Error') def __init__(self, name): self._name = name self._description = None self._status = Controllable.STATUS.Unknown self._messenger = messengerFactory.getMessenger(self) self._log = logFactory.getLogger(self) self._stop = threading.Event() name = property(lambda self: self._name) description = property(lambda self: self._description) status = property(lambda self: self._status) @abstractmethod def _doStart(self): '''Start the component's processing. Should perform actual startup. Base class takes care of logging, messaging, and state change. Must return boolean indicating success of operation. Implementing classes should throw ControllableException on error.''' pass def start(self): '''Perform actual startup, wraps call to _doStart. This method takes care of clearing the self._stop event. There is little benefit to overriding this method.''' if self._status in (Controllable.STATUS.Unknown, Controllable.STATUS.Stopped, Controllable.STATUS.Error): self._setStatus(Controllable.STATUS.Starting) try: self._log.debug("Starting component [%s]...", self._name) self._stop.clear() stat = self._doStart() if stat: self._log.debug("Component [%s] started.", self._name) else: self._log.warning("Unable to start component [%s]!", self._name) # TODO: subscribe to messenger if started successfully newStatus = Controllable.STATUS.Started if stat else Controllable.STATUS.Stopped self._setStatus(newStatus) except Exception as ex: # TODO: should this exception be surfaced? self._setStatus(Controllable.STATUS.Error) self._log.error("Exception at startup: %s", ex) @abstractmethod def _doStop(self): '''Stop the component's processing. Should perform actual shutdown. Base class takes care of logging, messaging, and state change. Must return boolean indicating success of operation. Implementing classes should throw ControllableException on error.''' pass @abstractmethod def _doRefresh(self): '''Perform component interval-based processing (if applicable)''' pass def stop(self): '''Perform actual stop, wraps call to _doStop. This method takes care of setting the self._stop event. There is little benefit to overriding this method.''' if self._status == Controllable.STATUS.Started: self._setStatus(Controllable.STATUS.Stopping) try: self._log.debug("Stopping component [%s]...", self._name) self._stop.set() stat = self._doStop() if stat: self._log.debug("Component [%s] stopped.", self._name) else: self._log.warning("Unable to stop component [%s]", self._name) # TODO: unsubscribe all if stopped newStatus = Controllable.STATUS.Stopped if stat else Controllable.STATUS.Started self._setStatus(newStatus) except Exception as ex: # TODO: should this exception be surfaced? # TODO: this could leave background thread in inconsistent state self._setStatus(Controllable.STATUS.Error) self._log.error("Exception at stop: %s", ex) def refresh(self): if self._status == Controllable.STATUS.Started: self._log.debug("[%s] Executing refresh loop", self._name) self._doRefresh() def _setStatus(self, status): if status <> self._status: self._status = status self._messenger.publish(self, Messenger.TOPIC_COMPONENT_STATUS, status)
def setUp(self): self.enum = Enum('Dog','Cat','Fish')
def setUp(self): self.enum = Enum('Dog', 'Cat', 'Fish')