def test_event_key(): prefix = Prefix(Subsystems.TCS, "ENCAssembly") prefix2 = Prefix(Subsystems.TCS, "ENC.Assembly") eventName = EventName("CurrentPosition") k1 = EventKey(prefix, eventName) k2 = EventKey.from_str("TCS.ENCAssembly.CurrentPosition") assert k1 == k2 k1 = EventKey(prefix2, eventName) k2 = EventKey.from_str("TCS.ENC.Assembly.CurrentPosition") assert k1 == k2
def __init__(self): self.count = 0 self.eventKey = EventKey(Prefix(Subsystems.CSW, "testassembly"), EventName("myAssemblyEvent")) self.eventSubscriber = EventSubscriber() self.eventThread = self.eventSubscriber.subscribe([self.eventKey], self.callback)
def test_connection_info(): prefix = Prefix(Subsystems.CSW, "MyComp") info = ConnectionInfo.make(prefix, ComponentType.Service, ConnectionType.HttpType) json = info.to_json() newInfo = ConnectionInfo.schema().loads(json) assert (newInfo == info)
def __init__(self): intParam = Parameter("IntValue", KeyType.IntKey, [42], Units.arcsec) floatParam = Parameter("floatValue", KeyType.FloatKey, [float(42.1)], Units.arcsec) longParam = Parameter("longValue", KeyType.LongKey, [42], Units.arcsec) shortParam = Parameter("shortValue", KeyType.ShortKey, [42], Units.arcsec) byteParam = Parameter("byteValue", KeyType.ByteKey, b'\xDE\xAD\xBE\xEF') booleanParam = Parameter("booleanValue", KeyType.BooleanKey, [True, False], Units.arcsec) intArrayParam = Parameter("IntArrayValue", KeyType.IntArrayKey, [[1, 2, 3, 4], [5, 6, 7, 8]]) floatArrayParam = Parameter("FloatArrayValue", KeyType.FloatArrayKey, [[1.2, 2.3, 3.4], [5.6, 7.8, 9.1]], Units.arcsec) doubleArrayParam = Parameter("DoubleArrayValue", KeyType.DoubleArrayKey, [[1.2, 2.3, 3.4], [5.6, 7.8, 9.1]], Units.arcsec) byteArrayParam = Parameter("ByteArrayValue", KeyType.ByteArrayKey, [b'\xDE\xAD\xBE\xEF', bytes([1, 2, 3, 4])]) intMatrixParam = Parameter("IntMatrixValue", KeyType.IntMatrixKey, [[[1, 2, 3, 4], [5, 6, 7, 8]], [[-1, -2, -3, -4], [-5, -6, -7, -8]]], Units.meter) eqCoord = EqCoord.make(ra="12:13:14.15 hours", dec="-30:31:32.3 deg", frame=EqFrame.FK5, pm=(0.5, 2.33)) solarSystemCoord = SolarSystemCoord.make("BASE", SolarSystemObject.Venus) minorPlanetCoord = MinorPlanetCoord.make("GUIDER1", 2000, "90 deg", "2 deg", "100 deg", 1.4, 0.234, "220 deg") cometCoord = CometCoord.make("BASE", 2000.0, "90 deg", "2 deg", "100 deg", 1.4, 0.234) altAzCoord = AltAzCoord.make("301 deg", "42.5 deg") coordsParam = Parameter("CoordParam", KeyType.CoordKey, [eqCoord, solarSystemCoord, minorPlanetCoord, cometCoord, altAzCoord]) prefix = Prefix(Subsystems.CSW, "testassembly") eventName = EventName("myAssemblyEvent") paramSet = [coordsParam, byteParam, intParam, floatParam, longParam, shortParam, booleanParam, byteArrayParam, intArrayParam, floatArrayParam, doubleArrayParam, intMatrixParam] event = SystemEvent(prefix, eventName, paramSet) self.pub.publish(event)
def _fromDict(obj): """ Returns a CurrentState for the given dict. """ prefix = Prefix.from_str(obj['prefix']) stateName = obj['stateName'] paramSet = list(map(lambda p: Parameter._fromDict(p), obj['paramSet'])) return CurrentState(prefix, stateName, paramSet)
def __init__(self): """ Events are posted to Redis. This is internal class used to access Redis. """ prefix = Prefix(Subsystems.CSW, "EventServer") conn = ConnectionInfo.make(prefix, ComponentType.Service, ConnectionType.TcpType) loc = LocationService().find(conn) uri = urlparse(loc.uri) sentinel = Sentinel([(uri.hostname, uri.port)], socket_timeout=0.1) self.__redis = sentinel.master_for('eventServer', socket_timeout=0.1) self.__redis_pubsub = self.__redis.pubsub()
def _fromDict(obj): """ Returns a ControlCommand for the given dict. """ typ = obj["_type"] source = Prefix.from_str(obj['source']) commandName = CommandName(obj['commandName']) maybeObsId = obj['maybeObsId'] if 'maybeObsId' in obj else "" paramSet = list(map(lambda p: Parameter._fromDict(p), obj['paramSet'])) assert (typ in {"Setup", "Observe"}) if typ == 'Setup': return Setup(source, commandName, maybeObsId, paramSet) else: return Observe(source, commandName, maybeObsId, paramSet)
def _fromDict(obj): """ Returns a Event for the given dict. """ typ = obj['_type'] assert (typ in {"SystemEvent", "ObserveEvent"}) paramSet = list( map(lambda p: Parameter._fromDict(p, True), obj['paramSet'])) eventTime = EventTime._fromDict(obj['eventTime']) prefix = Prefix.from_str(obj['source']) eventName = EventName(obj['eventName']) eventId = obj['eventId'] if typ == 'SystemEvent': return SystemEvent(prefix, eventName, paramSet, eventTime, eventId) else: return ObserveEvent(prefix, eventName, paramSet, eventTime, eventId)
def test_location_service_models(self): testDir = pathlib.Path(__file__).parent.absolute() with open(f"{testDir}/location-models.json") as json_file: data = json.load(json_file) for p in data['ComponentType']: assert (ComponentType[p].name == p) for p in data['Connection']: connectionInfo = ConnectionInfo.from_dict(p) self.log.debug(f"Connection: {connectionInfo}") assert (connectionInfo.to_dict() == p) for p in data['Registration']: regType = p['_type'] if regType == "HttpRegistration": registration = HttpRegistration.from_dict(p) elif regType == "TcpRegistration": registration = TcpRegistration.from_dict(p) elif regType == "AkkaRegistration": registration = AkkaRegistration.from_dict(p) assert (registration.to_dict() == p) for p in data['ComponentId']: componentId = ComponentId.from_dict(p) self.log.debug(f"ComponentId: {componentId}") assert (componentId.to_dict() == p) for p in data['Prefix']: prefix = Prefix.from_str(p) assert (str(prefix) == p) for p in data['ConnectionType']: assert (ConnectionType(p).value == p) for p in data['Subsystem']: assert (Subsystems[p].name == p) for p in data['Location']: locType = p['_type'] if locType == "AkkaLocation": loc = AkkaLocation.from_dict(p) elif locType == "HttpLocation": loc = HttpLocation.from_dict(p) elif locType == "TcpLocation": loc = TcpLocation.from_dict(p) self.log.debug(f"Location: {loc}") assert (loc.to_dict() == p)
def test_pub_sub(self): pub = EventPublisher() sub = EventSubscriber() prefix = Prefix(Subsystems.CSW, "assembly") eventName = EventName("test_event") eventKey = EventKey(prefix, eventName) keyName = "testEventValue" keyType = KeyType.IntKey values = [42] param = Parameter(keyName, keyType, values) paramSet = [param] event = SystemEvent(prefix, eventName, paramSet) thread = sub.subscribe([eventKey], self.callback) pub.publish(event) time.sleep(1) e = sub.get(eventKey) assert (e == event) assert (self.count == 1) sub.unsubscribe([eventKey]) thread.stop()
def test_location_service(): log = structlog.get_logger() locationService = LocationService() # List all registered connections log.debug("\nAll Locations:") allLocations = locationService.list() for i in allLocations: log.debug(" " + str(i)) # Check that the standard CSW services were found assert [ x for x in allLocations if x.connection.prefix == 'CSW.AAS' and x.connection.componentType == 'Service' ] assert [ x for x in allLocations if x.connection.prefix == 'CSW.AlarmServer' and x.connection.componentType == 'Service' ] assert [ x for x in allLocations if x.connection.prefix == 'CSW.DatabaseServer' and x.connection.componentType == 'Service' ] assert [ x for x in allLocations if x.connection.prefix == 'CSW.EventServer' and x.connection.componentType == 'Service' ] assert [ x for x in allLocations if x.connection.prefix == 'CSW.ConfigServer' and x.connection.componentType == 'Service' ] # List the registered HCDs log.debug("\nHCDs:") for i in locationService.list(ComponentType.HCD): log.debug(" " + str(i)) # List the registered http connections log.debug("\nConnections on 192.168.178.78") for i in locationService.list("192.168.178.78"): log.debug(" " + str(i)) # List the registered http connections log.debug("\nHTTP connections:") httpServices = locationService.list(ConnectionType.HttpType) for i in httpServices: log.debug(" " + str(i)) assert [ x for x in httpServices if x.connection.prefix == 'CSW.AAS' and x.connection.componentType == 'Service' ] assert not [ x for x in httpServices if x.connection.prefix == 'CSW.AlarmServer' and x.connection.componentType == 'Service' ] assert not [ x for x in httpServices if x.connection.prefix == 'CSW.DatabaseServer' and x.connection.componentType == 'Service' ] assert not [ x for x in httpServices if x.connection.prefix == 'CSW.EventServer' and x.connection.componentType == 'Service' ] assert [ x for x in httpServices if x.connection.prefix == 'CSW.ConfigServer' and x.connection.componentType == 'Service' ] # Register a connection prefix = Prefix(Subsystems.CSW, "myComp") connection = ConnectionInfo.make(prefix, ComponentType.Service, ConnectionType.HttpType) reg = HttpRegistration(connection, 8080, path="myservice/test") regResult = locationService.register(reg) log.debug("\nRegistration result: " + str(regResult)) assert regResult.componentType == ComponentType.Service.value assert regResult.prefix == 'CSW.myComp' assert regResult.connectionType == ConnectionType.HttpType.value # Find a connection location1 = locationService.find(connection) log.debug("location1 = " + str(location1)) assert location1.connection.componentType == ComponentType.Service.value assert location1.connection.prefix == 'CSW.myComp' assert location1.connection.connectionType == ConnectionType.HttpType.value # Resolve a connection (waiting if needed) location2 = locationService.resolve(connection) log.debug("location2 = " + str(location2)) assert location1 == location2 # Unregister unregResult = locationService.unregister(connection) log.debug("\nUnregister result: " + str(unregResult)) assert not locationService.find(connection)
class TestEventsWithAssembly: log = structlog.get_logger() dir = pathlib.Path(__file__).parent.absolute() inFileName = "PyTestAssemblyEventHandlers.in" outFileName = "PyTestAssemblyEventHandlers.out" tmpInFile = f"/tmp/{inFileName}" tmpOutFile = f"/tmp/{outFileName}" inFile = f"{dir}/{inFileName}" outFile = f"{dir}/{outFileName}" pub = EventPublisher() sub = EventSubscriber() prefix = Prefix(Subsystems.CSW, "TestPublisher") # def setup_method(self): # self.cleanup() def teardown_method(self): # pass self.cleanup() def cleanup(self): if os.path.exists(self.tmpInFile): os.remove(self.tmpInFile) if os.path.exists(self.tmpOutFile): os.remove(self.tmpOutFile) def test_pub_sub(self): time.sleep(1.0) self.log.debug("Starting test...") thread = self.sub.subscribe([ EventKey(self.prefix, EventName("testEvent1")), EventKey(self.prefix, EventName("testEvent2")), EventKey(self.prefix, EventName("testEvent3")) ], self.callback) try: self.publishEvent1() self.publishEvent2() self.publishEvent3() self.publishEvent4() self.log.debug("Published three events...") # make sure assembly has time to write the file time.sleep(3) # compare file created from received events below with known good version assert filecmp.cmp(self.inFile, self.tmpInFile, False) # compare file created by assembly with known good version assert filecmp.cmp(self.outFile, self.tmpOutFile, False) self.log.info("Event pub/sub tests passed") finally: self.log.debug("Stopping subscriber...") thread.stop() def publishEvent1(self): keyName = "assemblyEventValue" keyType = KeyType.DoubleKey values = [42.0] param = Parameter(keyName, keyType, values) paramSet = [param] event = SystemEvent(self.prefix, EventName("testEvent1"), paramSet) self.log.debug(f"Publishing event {event}") self.pub.publish(event) def publishEvent2(self): intParam = Parameter("IntValue", KeyType.IntKey, [42], Units.arcsec) intArrayParam = Parameter("IntArrayValue", KeyType.IntArrayKey, [[1, 2, 3, 4], [5, 6, 7, 8]]) floatArrayParam = Parameter("FloatArrayValue", KeyType.FloatArrayKey, [[1.2, 2.3, 3.4], [5.6, 7.8, 9.1]], Units.marcsec) intMatrixParam = Parameter("IntMatrixValue", KeyType.IntMatrixKey, [[[1, 2, 3, 4], [5, 6, 7, 8]], [[-1, -2, -3, -4], [-5, -6, -7, -8]]], Units.meter) paramSet = [intParam, intArrayParam, floatArrayParam, intMatrixParam] event = SystemEvent(self.prefix, EventName("testEvent2"), paramSet) self.pub.publish(event) def publishEvent3(self): intParam = Parameter("IntValue", KeyType.IntKey, [42], Units.arcsec) floatParam = Parameter("floatValue", KeyType.FloatKey, [float(42.1)], Units.arcsec) longParam = Parameter("longValue", KeyType.LongKey, [42], Units.arcsec) shortParam = Parameter("shortValue", KeyType.ShortKey, [42], Units.arcsec) byteParam = Parameter("byteValue", KeyType.ByteKey, b'\xDE\xAD\xBE\xEF') booleanParam = Parameter("booleanValue", KeyType.BooleanKey, [True, False], Units.arcsec) intArrayParam = Parameter("IntArrayValue", KeyType.IntArrayKey, [[1, 2, 3, 4], [5, 6, 7, 8]]) floatArrayParam = Parameter("FloatArrayValue", KeyType.FloatArrayKey, [[1.2, 2.3, 3.4], [5.6, 7.8, 9.1]], Units.arcsec) doubleArrayParam = Parameter("DoubleArrayValue", KeyType.DoubleArrayKey, [[1.2, 2.3, 3.4], [5.6, 7.8, 9.1]], Units.arcsec) byteArrayParam = Parameter( "ByteArrayValue", KeyType.ByteArrayKey, [b'\xDE\xAD\xBE\xEF', bytes([1, 2, 3, 4])]) intMatrixParam = Parameter("IntMatrixValue", KeyType.IntMatrixKey, [[[1, 2, 3, 4], [5, 6, 7, 8]], [[-1, -2, -3, -4], [-5, -6, -7, -8]]], Units.meter) eqCoord = EqCoord.make(ra="12:13:14.15 hours", dec="-30:31:32.3 deg", frame=EqFrame.FK5, pm=(0.5, 2.33)) solarSystemCoord = SolarSystemCoord.make("BASE", SolarSystemObject.Venus) minorPlanetCoord = MinorPlanetCoord.make("GUIDER1", 2000, "90 deg", "2 deg", "100 deg", 1.4, 0.234, "220 deg") cometCoord = CometCoord.make("BASE", 2000.0, "90 deg", "2 deg", "100 deg", 1.4, 0.234) altAzCoord = AltAzCoord.make("301 deg", "42.5 deg") coordsParam = Parameter("CoordParam", KeyType.CoordKey, [ eqCoord, solarSystemCoord, minorPlanetCoord, cometCoord, altAzCoord ]) paramSet = [ coordsParam, byteParam, intParam, floatParam, longParam, shortParam, booleanParam, byteArrayParam, intArrayParam, floatArrayParam, doubleArrayParam, intMatrixParam ] event = SystemEvent(self.prefix, EventName("testEvent3"), paramSet) self.pub.publish(event) def publishEvent4(self): keyName = "assemblyEventValue" keyType = KeyType.UTCTimeKey values = [UTCTime.from_str("2021-09-20T20:43:35.419053077Z")] param = Parameter(keyName, keyType, values) keyName2 = "assemblyEventValue2" keyType2 = KeyType.TAITimeKey values2 = [TAITime.from_str("2021-09-20T18:44:12.419084072Z")] param2 = Parameter(keyName2, keyType2, values2) paramSet = [param, param2] event = SystemEvent(self.prefix, EventName("testEvent4"), paramSet) self.log.debug(f"Publishing event {event}") self.pub.publish(event) # Event subscriber callback def callback(self, systemEvent): self.log.debug(f"Received system event '{systemEvent}'") # Save event to file as JSON like dict (Not JSON, since byte arrays are not serializable in python), # but change the date and id for comparison systemEvent.eventId = "test" systemEvent.eventTime = EventTime(0, 0) mode = "w" if (systemEvent.eventName.name == "testEvent1") else "a" f = open(self.tmpInFile, mode) jsonStr = str(systemEvent._asDict()) f.write(f"{jsonStr}\n") f.close()
from csw.Event import SystemEvent, EventName from csw.EventPublisher import EventPublisher from csw.KeyType import KeyType from csw.Parameter import Parameter from csw.Prefix import Prefix from csw.Subsystem import Subsystems # Test publishing events source = Prefix(Subsystems.CSW, "testassembly") eventName = EventName("myAssemblyEvent") keyName = "assemblyEventValue" keyType = KeyType.DoubleKey values = [42.0] param = Parameter(keyName, keyType, values) paramSet = [param] event = SystemEvent(source, eventName, paramSet) pub = EventPublisher() pub.publish(event)
def from_str(class_object, eventKeyStr: str): i = eventKeyStr.rindex('.') return EventKey(Prefix.from_str(eventKeyStr[:i]), EventName(eventKeyStr[i + 1:]))
class MyComponentHandlers(ComponentHandlers): prefix = Prefix(Subsystems.CSW, "pycswTest") async def longRunningCommand(self, runId: str, command: ControlCommand) -> CommandResponse: await asyncio.sleep(3) print("Long running task completed") # TODO: Do this in a timer task await self.publishCurrentStates() return Completed(runId) def onSubmit(self, runId: str, command: ControlCommand) -> (CommandResponse, Task): """ Overrides the base class onSubmit method to handle commands from a CSW component Args: runId (str): unique id for this command command (ControlCommand): contains the ControlCommand from CSW Returns: (CommandResponse, Task) a subclass of CommandResponse that is serialized and passed back to the CSW component """ n = len(command.paramSet) print( f"MyComponentHandlers Received setup {str(command)} with {n} params" ) # filt = command.get("filter").values[0] # encoder = command.get("encoder").values[0] # print(f"filter = {filt}, encoder = {encoder}") # --- Example return values --- # return Completed(runId), None # return Error(runId, "There is a problem ..."), None # return Invalid(runId, MissingKeyIssue("Missing required key XXX")), None # result = Result("tcs.filter", [Parameter("myValue", 'DoubleKey', [42.0])]) # return Completed(runId, result), None if command.commandName.name == "LongRunningCommand": task = asyncio.create_task(self.longRunningCommand(runId, command)) return Started(runId, "Long running task in progress..."), task elif command.commandName.name == "SimpleCommand": return Completed(runId), None elif command.commandName.name == "ResultCommand": result = Result([Parameter("myValue", KeyType.DoubleKey, [42.0])]) return Completed(runId, result), None else: return Invalid( runId, UnsupportedCommandIssue( f"Unknown command: {command.commandName.name}")), None def onOneway(self, runId: str, command: ControlCommand) -> CommandResponse: """ Overrides the base class onOneway method to handle commands from a CSW component. Args: runId (str): unique id for this command command (ControlCommand): contains the ControlCommand from CSW Returns: CommandResponse an instance of one of these command responses: Accepted, Invalid, Locked (OnewayResponse in CSW) """ n = len(command.paramSet) print( f"MyComponentHandlers Received oneway {str(command)} with {n} params" ) # filt = command.get("filter").values[0] # encoder = command.get("encoder").values[0] # print(f"filter = {filt}, encoder = {encoder}") return Accepted(runId) def validateCommand(self, runId: str, command: ControlCommand) -> CommandResponse: """ Overrides the base class validate method to verify that the given command is valid. Args: runId (str): unique id for this command command (ControlCommand): contains the ControlCommand from CSW Returns: CommandResponse an instance of one of these command responses: Accepted, Invalid, Locked (OnewayResponse in CSW) """ return Accepted(runId) # Returns the current state def currentStates(self) -> List[CurrentState]: intParam = Parameter("IntValue", KeyType.IntKey, [42], Units.arcsec) intArrayParam = Parameter("IntArrayValue", KeyType.IntArrayKey, [[1, 2, 3, 4], [5, 6, 7, 8]]) floatArrayParam = Parameter("FloatArrayValue", KeyType.FloatArrayKey, [[1.2, 2.3, 3.4], [5.6, 7.8, 9.1]], Units.marcsec) intMatrixParam = Parameter("IntMatrixValue", KeyType.IntMatrixKey, [[[1, 2, 3, 4], [5, 6, 7, 8]], [[-1, -2, -3, -4], [-5, -6, -7, -8]]], Units.meter) return [ CurrentState( self.prefix, "PyCswState", [intParam, intArrayParam, floatArrayParam, intMatrixParam]) ]