class MyServer(AsyncDeviceServer): VERSION_INFO = ("example-api", 1, 0) BUILD_INFO = ("example-implementation", 0, 1, "") # Optionally set the KATCP protocol version and features. Defaults to # the latest implemented version of KATCP, with all supported optional # featuresthat's all of the receivers PROTOCOL_INFO = ProtocolFlags(5, 0, set([ ProtocolFlags.MULTI_CLIENT, ProtocolFlags.MESSAGE_IDS, ])) FRUIT = [ "apple", "banana", "pear", "kiwi", ] def setup_sensors(self): """Setup some server sensors.""" self._add_result = Sensor.float("add.result", "Last ?add result.", "", [-10000, 10000]) self._time_result = Sensor.timestamp("time.result", "Last ?time result.", "") self._eval_result = Sensor.string("eval.result", "Last ?eval result.", "") self._fruit_result = Sensor.discrete("fruit.result", "Last ?pick-fruit result.", "", self.FRUIT) self._device_armed = Sensor.boolean( "device-armed", description="Is the CAM server armed?", initial_status=Sensor.NOMINAL, default=True) self._bandwidth = Sensor.float("bandwidth", default=300) self._sourcename = Sensor.string("sourcename", default="none") self._source_ra = Sensor.string("source_RA", default=0) self._source_dec = Sensor.string("source_DEC", default=0) self._exposure_time = Sensor.float("EXP_time", default=0) self.add_sensor(self._sourcename) self.add_sensor(self._source_ra) self.add_sensor(self._source_dec) self.add_sensor(self._exposure_time) self.add_sensor(self._bandwidth) self.add_sensor(self._device_armed) self.add_sensor(self._add_result) self.add_sensor(self._time_result) self.add_sensor(self._eval_result) self.add_sensor(self._fruit_result) self._systemp_result = Sensor.float("add.result", "Last ?add result.", "", [-10000, 10000]) self.add_sensor(self._systemp_result) ##self._bandwidth = Sensor.float("bandwidth", default=300) #self.add_sensor(self._bandwidth) @request() @return_reply(Str()) def request_bandwidth(self, req, bw): """Return the Bandwidth""" #req.inform("checking armed status", self._device_armed.value()) req.reply("ok", bw) raise AsyncReply @request() @return_reply(Str()) def request_status_armed(self, req): """Return the state of the Armed/Disarmed""" req.inform("checking armed status", self._device_armed.value()) req.reply("ok", self._device_armed.value()) raise AsyncReply @request(Float()) @return_reply() def request_long_action(self, req, t): """submit a long action command for testing using coroutine""" @tornado.gen.coroutine def wait(): yield tornado.gen.sleep(t) req.reply("slept for", t, "second") self.ioloop.add_callback(wait) raise AsyncReply @request(Float(), Float()) @return_reply(Str()) def request_radec(self, req, ra, dec): """testing to read in the RA DEC fomr a client""" # test=ra+dec self.ra = ra self.dec = dec return ("ok", "%f %f" % (self.ra, self.dec)) @request(Float(), Float()) @return_reply(Float()) def request_add(self, req, x, y): """Add two numbers""" r = x + y self._add_result.set_value(r) return ("ok", r) @request() @return_reply(Str()) def request_arm(self, req): """Arm the controller""" @tornado.gen.coroutine def start_controller(): req.inform("processing", "command processing") try: yield tornado.gen.sleep(10) except Exception as error: req.reply("fail", "Unknown error: {0}".format(str(error))) else: req.reply("ok", "effcam armed") self._device_armed.set_value(True) if self._device_armed.value(): return ("fail", "Effcam is already armed") self.ioloop.add_callback(start_controller) raise AsyncReply @request() @return_reply(Str()) def request_disarm(self, req): """disarm the controller""" @tornado.gen.coroutine # @coroutine def stop_controller(): req.inform("processing", "processing command") try: yield tornado.gen.sleep(10) # yield self._controller.stop() except Exception as error: req.reply("fail", "Unknown error: {0}".format(str(error))) else: req.reply("ok", "effcam disarmed") self._device_armed.set_value(False) if self._device_armed.value() == False: return ("fail", "Effcam is already disarmed") self.ioloop.add_callback(stop_controller) raise AsyncReply @request() @return_reply(Str()) def request_status_temp(self, req): """Return the current temp""" #r = time.time() t = "36" # self._time_result.set_value(r) return ("ok", t) @request() @return_reply(Timestamp()) def request_status_time(self, req): """Return the current time in seconds since the Unix Epoch.""" req.inform("processing", "processing command") r = time.time() # self._time_result.set_value(r) req.reply("ok", r) raise AsyncReply # return ("ok", r) @request() @return_reply(Timestamp(), Str()) def request_status_time_and_temp(self, req): """Return the current time in seconds since the Unix Epoch.""" req.inform("processing", "processing command") r = time.time() # self._time_result.set_value(r) t = "36" req.reply("ok", r, t) raise AsyncReply @request(Str()) @return_reply() def request_configure(self, req, config): """Return ok.""" print "{} received configuration {}".format(Time.now(),config) self.config = config time.sleep(1) req.reply("ok",) raise AsyncReply @request(Str()) @return_reply() def request_provision(self, req, config): """Return ok.""" print "{} received provision {}".format(Time.now(),config) self.config = config time.sleep(1) req.reply("ok",) raise AsyncReply @request(Str()) @return_reply() def request_measurement_prepare(self, req, config): """Return ok.""" print "{} received measurement prepare {}".format(Time.now(),config) self.config = config time.sleep(1) req.reply("ok",) raise AsyncReply @request(Str()) @return_reply() def request_configure(self, req, config): """Return ok.""" print "{} received configuration {}".format(Time.now(),config) self.config = config time.sleep(1) req.reply("ok",) raise AsyncReply @request() @return_reply(Str()) def request_status_config(self, req): """Return ok.""" req.reply("ok", "{}".format(self.config)) raise AsyncReply @request() @return_reply() def request_capture_start(self, req): """Return ok.""" print "{} received capture start request on port :{}".format(Time.now(), server_port) req.reply("ok") raise AsyncReply @request() @return_reply() def request_capture_stop(self, req): """Return ok.""" print "{} received capture stop request on port :{}".format(Time.now(), server_port) req.reply("ok") raise AsyncReply @request() @return_reply() def request_measurement_start(self, req): """Return ok.""" print "{} received measurement start request on port :{}".format(Time.now(), server_port) req.reply("ok") raise AsyncReply @request() @return_reply() def request_measurement_stop(self, req): """Return ok.""" print "{} received measurement stop request on port :{}".format(Time.now(), server_port) req.reply("ok") raise AsyncReply @request() @return_reply() def request_deconfigure(self, req): """Return ok.""" print "{} received deconfigure request on port :{}".format(Time.now(), server_port) req.reply("ok") raise AsyncReply @request() @return_reply() def request_deprovision(self, req): """Return ok.""" print "{} received deprovision request on port :{}".format(Time.now(), server_port) req.reply("ok") raise AsyncReply() @return_reply() def request_start(self, req): """Return ok.""" print "{} received start request on port :{}".format(Time.now(), server_port) req.reply("ok") raise AsyncReply @request() @return_reply() def request_stop(self, req): """Return ok.""" print "{} received stop request on port :{}".format(Time.now(), server_port) req.reply("ok") raise AsyncReply
class MyServer(DeviceServer): VERSION_INFO = ("example-api", 1, 0) BUILD_INFO = ("example-implementation", 0, 1, "") # Optionally set the KATCP protocol version and features. Defaults to # the latest implemented version of KATCP, with all supported optional # features PROTOCOL_INFO = ProtocolFlags(5, 0, set([ ProtocolFlags.MULTI_CLIENT, ProtocolFlags.MESSAGE_IDS, ])) FRUIT = [ "apple", "banana", "pear", "kiwi", ] def setup_sensors(self): """Setup some server sensors.""" self._add_result = Sensor.float("add.result", "Last ?add result.", "", [-10000, 10000]) self._add_result.set_value(0, Sensor.UNREACHABLE) self._time_result = Sensor.timestamp("time.result", "Last ?time result.", "") self._time_result.set_value(0, Sensor.INACTIVE) self._eval_result = Sensor.string("eval.result", "Last ?eval result.", "") self._eval_result.set_value('', Sensor.UNKNOWN) self._fruit_result = Sensor.discrete("fruit.result", "Last ?pick-fruit result.", "", self.FRUIT) self._fruit_result.set_value('apple', Sensor.ERROR) self.add_sensor(self._add_result) self.add_sensor(self._time_result) self.add_sensor(self._eval_result) self.add_sensor(self._fruit_result) @request(Float(), Float()) @return_reply(Float()) def request_add(self, req, x, y): """Add two numbers""" r = x + y self._add_result.set_value(r) return ("ok", r) @request() @return_reply(Timestamp()) def request_time(self, req): """Return the current time in ms since the Unix Epoch.""" r = time.time() self._time_result.set_value(r) return ("ok", r) @request(Str()) @return_reply(Str()) def request_eval(self, req, expression): """Evaluate a Python expression.""" r = str(eval(expression)) self._eval_result.set_value(r) return ("ok", r) @request() @return_reply(Discrete(FRUIT)) def request_pick_fruit(self, req): """Pick a random fruit.""" r = random.choice(self.FRUIT + [None]) if r is None: return ("fail", "No fruit.") delay = random.randrange(1,5) req.inform("Picking will take %d seconds" % delay) def pick_handler(): self._fruit_result.set_value(r) req.reply("ok", r) handle_timer = threading.Timer(delay, pick_handler) handle_timer.start() raise AsyncReply @request(Str()) @return_reply() def request_set_sensor_inactive(self, req, sensor_name): """Set sensor status to inactive""" sensor = self.get_sensor(sensor_name) ts, status, value = sensor.read() sensor.set_value(value, sensor.INACTIVE, ts) return('ok',) @request(Str()) @return_reply() def request_set_sensor_unreachable(self, req, sensor_name): """Set sensor status to unreachable""" sensor = self.get_sensor(sensor_name) ts, status, value = sensor.read() sensor.set_value(value, sensor.UNREACHABLE, ts) return('ok',) def request_raw_reverse(self, req, msg): """ A raw request handler to demonstrate the calling convention if @request decoraters are not used. Reverses the message arguments. """ # msg is a katcp.Message.request object reversed_args = msg.arguments[::-1] # req.make_reply() makes a katcp.Message.reply using the correct request # name and message ID return req.make_reply(*reversed_args)
class KATCPServer (DeviceServer): VERSION_INFO = ("ptuse-api", 1, 0) BUILD_INFO = ("ptuse-implementation", 0, 1, "") # Optionally set the KATCP protocol version and features. Defaults to # the latest implemented version of KATCP, with all supported optional # features PROTOCOL_INFO = ProtocolFlags(5, 0, set([ ProtocolFlags.MULTI_CLIENT, ProtocolFlags.MESSAGE_IDS, ])) def __init__ (self, server_host, server_port, script): self.script = script self._host_sensors = {} self._beam_sensors = {} self._data_product = {} self._data_product["id"] = "None" self.data_product_res = [] self.data_product_res.append(re.compile ("^[a-zA-Z]+_1")) self.data_product_res.append(re.compile ("^[a-zA-Z]+_2")) self.data_product_res.append(re.compile ("^[a-zA-Z]+_3")) self.data_product_res.append(re.compile ("^[a-zA-Z]+_4")) self.script.log(2, "KATCPServer::__init__ starting DeviceServer on " + server_host + ":" + str(server_port)) DeviceServer.__init__(self, server_host, server_port) DEVICE_STATUSES = ["ok", "degraded", "fail"] def setup_sensors(self): """Setup server sensors.""" self.script.log(2, "KATCPServer::setup_sensors()") self._device_status = Sensor.discrete("device-status", description="Status of entire system", params=self.DEVICE_STATUSES, default="ok") self.add_sensor(self._device_status) self._beam_name = Sensor.string("beam-name", description="name of configured beam", unit="", default="") self.add_sensor(self._beam_name) # setup host based sensors self._host_name = Sensor.string("host-name", description="hostname of this server", unit="", default="") self.add_sensor(self._host_name) self.script.log(2, "KATCPServer::setup_sensors lmc="+str(self.script.lmc)) (host, port) = self.script.lmc.split(":") self.setup_sensors_host (host, port) self.script.log(2, "KATCPServer::setup_sensors beams="+str(self.script.beam)) self.setup_sensors_beam (self.script.beam_name) # add sensors based on the reply from the specified host def setup_sensors_host (self, host, port): self.script.log(2, "KATCPServer::setup_sensors_host ("+host+","+port+")") sock = sockets.openSocket (DL, host, int(port), 1) if sock: self.script.log(2, "KATCPServer::setup_sensors_host sock.send(" + self.script.lmc_cmd + ")") sock.send (self.script.lmc_cmd + "\r\n") lmc_reply = sock.recv (65536) sock.close() xml = xmltodict.parse(lmc_reply) self._host_sensors = {} # Disk sensors self.script.log(2, "KATCPServer::setup_sensors_host configuring disk sensors") disk_prefix = host+".disk" self._host_sensors["disk_size"] = Sensor.float(disk_prefix+".size", description=host+": disk size", unit="MB", params=[8192,1e9], default=0) self._host_sensors["disk_available"] = Sensor.float(disk_prefix+".available", description=host+": disk available space", unit="MB", params=[1024,1e9], default=0) self.add_sensor(self._host_sensors["disk_size"]) self.add_sensor(self._host_sensors["disk_available"]) # Server Load sensors self.script.log(2, "KATCPServer::setup_sensors_host configuring load sensors") self._host_sensors["num_cores"] = Sensor.integer (host+".num_cores", description=host+": disk available space", unit="MB", params=[1,64], default=0) self._host_sensors["load1"] = Sensor.float(host+".load.1min", description=host+": 1 minute load ", unit="", default=0) self._host_sensors["load5"] = Sensor.float(host+".load.5min", description=host+": 5 minute load ", unit="", default=0) self._host_sensors["load15"] = Sensor.float(host+".load.15min", description=host+": 15 minute load ", unit="", default=0) self.add_sensor(self._host_sensors["num_cores"]) self.add_sensor(self._host_sensors["load1"]) self.add_sensor(self._host_sensors["load5"]) self.add_sensor(self._host_sensors["load15"]) cpu_temp_pattern = re.compile("cpu[0-9]+_temp") fan_speed_pattern = re.compile("fan[0-9,a-z]+") power_supply_pattern = re.compile("ps[0-9]+_status") self.script.log(2, "KATCPServer::setup_sensors_host configuring other metrics") if not xml["lmc_reply"]["sensors"] == None: for sensor in xml["lmc_reply"]["sensors"]["metric"]: name = sensor["@name"] if name == "system_temp": self._host_sensors[name] = Sensor.float((host+".system_temp"), description=host+": system temperature", unit="C", params=[-20,150], default=0) self.add_sensor(self._host_sensors[name]) if cpu_temp_pattern.match(name): (cpu, junk) = name.split("_") self._host_sensors[name] = Sensor.float((host+"." + name), description=host+": "+ cpu +" temperature", unit="C", params=[-20,150], default=0) self.add_sensor(self._host_sensors[name]) if fan_speed_pattern.match(name): self._host_sensors[name] = Sensor.float((host+"." + name), description=host+": "+name+" speed", unit="RPM", params=[0,20000], default=0) self.add_sensor(self._host_sensors[name]) if power_supply_pattern.match(name): self._host_sensors[name] = Sensor.boolean((host+"." + name), description=host+": "+name, unit="", default=0) self.add_sensor(self._host_sensors[name]) # TODO consider adding power supply sensors: e.g. # device-status-kronos1-powersupply1 # device-status-kronos1-powersupply2 # device-status-kronos2-powersupply1 # device-status-kronos2-powersupply2 # TODO consider adding raid/disk sensors: e.g. # device-status-<host>-raid # device-status-<host>-raid-disk1 # device-status-<host>-raid-disk2 self.script.log(2, "KATCPServer::setup_sensors_host done!") else: self.script.log(2, "KATCPServer::setup_sensors_host no sensors found") else: self.script.log(-2, "KATCPServer::setup_sensors_host: could not connect to LMC") # setup sensors for each beam def setup_sensors_beam (self, beam): b = str(beam) self._beam_sensors = {} self.script.log(2, "KATCPServer::setup_sensors_beam ="+b) self._beam_sensors["observing"] = Sensor.boolean("observing", description="Beam " + b + " is observing", unit="", default=0) self.add_sensor(self._beam_sensors["observing"]) self._beam_sensors["snr"] = Sensor.float("snr", description="SNR of Beam "+b, unit="", params=[0,1e9], default=0) self.add_sensor(self._beam_sensors["snr"]) self._beam_sensors["power"] = Sensor.float("power", description="Power Level of Beam "+b, unit="", default=0) self.add_sensor(self._beam_sensors["power"]) self._beam_sensors["integrated"] = Sensor.float("integrated", description="Length of integration for Beam "+b, unit="", default=0) self.add_sensor(self._beam_sensors["integrated"]) @request() @return_reply(Str()) def request_beam(self, req): """Return the configure beam name.""" return ("ok", self._beam_name.value()) @request() @return_reply(Str()) def request_host_name(self, req): """Return the name of this server.""" return ("ok", self._host_name.value()) @request() @return_reply(Float()) def request_snr(self, req): """Return the SNR for this beam.""" return ("ok", self._beam_sensors["snr"].value()) @request() @return_reply(Float()) def request_power(self, req): """Return the standard deviation of the 8-bit power level.""" return ("ok", self._beam_sensors["power"].value()) @request(Str(), Float()) @return_reply(Str()) def request_sync_time (self, req, data_product_id, adc_sync_time): """Set the ADC_SYNC_TIME for beam of the specified data product.""" if not data_product_id == self._data_product["id"]: return ("fail", "data product " + str (data_product_id) + " was not configured") self.script.beam_config["lock"].acquire() self.script.beam_config["ADC_SYNC_TIME"] = str(adc_sync_time) self.script.beam_config["lock"].release() return ("ok", "") @request(Str(), Str()) @return_reply(Str()) def request_target_start (self, req, data_product_id, target_name): """Commence data processing on specific data product and beam using target.""" self.script.log (1, "request_target_start(" + data_product_id + "," + target_name+")") self.script.beam_config["lock"].acquire() self.script.beam_config["ADC_SYNC_TIME"] = self.script.cam_config["ADC_SYNC_TIME"] self.script.beam_config["OBSERVER"] = self.script.cam_config["OBSERVER"] self.script.beam_config["ANTENNAE"] = self.script.cam_config["ANTENNAE"] self.script.beam_config["SCHEDULE_BLOCK_ID"] = self.script.cam_config["SCHEDULE_BLOCK_ID"] self.script.beam_config["EXPERIMENT_ID"] = self.script.cam_config["EXPERIMENT_ID"] self.script.beam_config["DESCRIPTION"] = self.script.cam_config["DESCRIPTION"] self.script.beam_config["lock"].release() # check the pulsar specified is listed in the catalog (result, message) = self.test_pulsar_valid (target_name) if result != "ok": return (result, message) # check the ADC_SYNC_TIME is valid for this beam if self.script.beam_config["ADC_SYNC_TIME"] == "0": return ("fail", "ADC Synchronisation Time was not valid") # set the pulsar name, this should include a check if the pulsar is in the catalog self.script.beam_config["lock"].acquire() if self.script.beam_config["MODE"] == "CAL": target_name = target_name + "_R" self.script.beam_config["SOURCE"] = target_name self.script.beam_config["lock"].release() host = self.script.tcs_host port = self.script.tcs_port self.script.log (2, "request_target_start: opening socket for beam " + beam_id + " to " + host + ":" + str(port)) sock = sockets.openSocket (DL, host, int(port), 1) if sock: xml = self.script.get_xml_config() sock.send(xml + "\r\n") reply = sock.recv (65536) xml = self.script.get_xml_start_cmd() sock.send(xml + "\r\n") reply = sock.recv (65536) sock.close() return ("ok", "") else: return ("fail", "could not connect to TCS") @request(Str()) @return_reply(Str()) def request_target_stop (self, req, data_product_id): """Cease data processing with target_name.""" self.script.log (1, "request_target_stop(" + data_product_id+")") self.script.beam_config["lock"].acquire() self.script.beam_config["SOURCE"] = "" self.script.beam_config["lock"].release() host = self.script.tcs_host port = self.script.tcs_port sock = sockets.openSocket (DL, host, int(port), 1) if sock: xml = self.script.get_xml_stop_cmd () sock.send(xml + "\r\n") reply = sock.recv (65536) sock.close() return ("ok", "") else: return ("fail", "could not connect to tcs[beam]") @request(Str()) @return_reply(Str()) def request_capture_init (self, req, data_product_id): """Prepare the ingest process for data capture.""" self.script.log (1, "request_capture_init: " + str(data_product_id) ) if not data_product_id == self._data_product["id"]: return ("fail", "data product " + str (data_product_id) + " was not configured") return ("ok", "") @request(Str()) @return_reply(Str()) def request_capture_done(self, req, data_product_id): """Terminte the ingest process for the specified data_product_id.""" self.script.log (1, "request_capture_done: " + str(data_product_id)) if not data_product_id == self._data_product["id"]: return ("fail", "data product " + str (data_product_id) + " was not configured") return ("ok", "") @return_reply(Str()) def request_configure(self, req, msg): """Prepare and configure for the reception of the data_product_id.""" self.script.log (1, "request_configure: nargs= " + str(len(msg.arguments)) + " msg=" + str(msg)) if len(msg.arguments) == 0: self.script.log (-1, "request_configure: no arguments provided") return ("ok", "configured data products: TBD") # the sub-array identifier data_product_id = msg.arguments[0] if len(msg.arguments) == 1: self.script.log (1, "request_configure: request for configuration of " + str(data_product_id)) if data_product_id == self._data_product["id"]: configuration = str(data_product_id) + " " + \ str(self._data_product['antennas']) + " " + \ str(self._data_product['n_channels']) + " " + \ str(self._data_product['cbf_source']) self.script.log (1, "request_configure: configuration of " + str(data_product_id) + "=" + configuration) return ("ok", configuration) else: self.script.log (-1, "request_configure: no configuration existed for " + str(data_product_id)) return ("fail", "no configuration existed for " + str(data_product_id)) if len(msg.arguments) == 4: # if the configuration for the specified data product matches extactly the # previous specification for that data product, then no action is required self.script.log (1, "configure: configuring " + str(data_product_id)) if data_product_id == self._data_product["id"] and \ self._data_product['antennas'] == msg.arguments[1] and \ self._data_product['n_channels'] == msg.arguments[2] and \ self._data_product['cbf_source'] == msg.arguments[3]: response = "configuration for " + str(data_product_id) + " matched previous" self.script.log (1, "configure: " + response) return ("ok", response) # the data product requires configuration else: self.script.log (1, "configure: new data product " + data_product_id) # determine which sub-array we are matched against the_sub_array = -1 for i in range(4): self.script.log (1, "configure: testing self.data_product_res[" + str(i) +"].match(" + data_product_id +")") if self.data_product_res[i].match (data_product_id): the_sub_array = i + 1 if the_sub_array == -1: self.script.log (1, "configure: could not match subarray from " + data_product_id) return ("fail", "could not data product to sub array") self.script.log (1, "configure: restarting pubsub for subarray " + str(the_sub_array)) self.script.pubsub.set_sub_array (the_sub_array, self.script.beam_name) self.script.pubsub.restart() antennas = msg.arguments[1] n_channels = msg.arguments[2] cbf_source = msg.arguments[3] # check if the number of existing + new beams > available (cfreq, bwd, nchan) = self.script.cfg["SUBBAND_CONFIG_0"].split(":") if nchan != n_channels: self._data_product.pop(data_product_id, None) response = "PTUSE configured for " + nchan + " channels" self.script.log (-1, "configure: " + response) return ("fail", response) self._data_product['id'] = data_product_id self._data_product['antennas'] = antennas self._data_product['n_channels'] = n_channels self._data_product['cbf_source'] = cbf_source # parse the CBF_SOURCE to determine multicast groups (addr, port) = cbf_source.split(":") (mcast, count) = addr.split("+") self.script.log (2, "configure: parsed " + mcast + "+" + count + ":" + port) if not count == "1": response = "CBF source did not match ip_address+1:port" self.script.log (-1, "configure: " + response) return ("fail", response) mcasts = ["",""] ports = [0, 0] quartets = mcast.split(".") mcasts[0] = ".".join(quartets) quartets[3] = str(int(quartets[3])+1) mcasts[1] = ".".join(quartets) ports[0] = int(port) ports[1] = int(port) self.script.log (1, "configure: connecting to RECV instance to update configuration") for istream in range(int(self.script.cfg["NUM_STREAM"])): (host, beam_idx, subband) = self.script.cfg["STREAM_" + str(istream)].split(":") beam = self.script.cfg["BEAM_" + beam_idx] if beam == self.script.beam_name: # reset ADC_SYNC_TIME on the beam self.script.beam_config["lock"].acquire() self.script.beam_config["ADC_SYNC_TIME"] = "0"; self.script.beam_config["lock"].release() port = int(self.script.cfg["STREAM_RECV_PORT"]) + istream self.script.log (3, "configure: connecting to " + host + ":" + str(port)) sock = sockets.openSocket (DL, host, port, 1) if sock: req = "<?req version='1.0' encoding='ISO-8859-1'?>" req += "<recv_cmd>" req += "<command>configure</command>" req += "<params>" req += "<param key='DATA_MCAST_0'>" + mcasts[0] + "</param>" req += "<param key='DATA_MCAST_1'>" + mcasts[1] + "</param>" req += "<param key='DATA_PORT_0'>" + str(ports[0]) + "</param>" req += "<param key='DATA_PORT_1'>" + str(ports[1]) + "</param>" req += "<param key='META_MCAST_0'>" + mcasts[0] + "</param>" req += "<param key='META_MCAST_1'>" + mcasts[1] + "</param>" req += "<param key='META_PORT_0'>" + str(ports[0]) + "</param>" req += "<param key='META_PORT_1'>" + str(ports[1]) + "</param>" req += "</params>" req += "</recv_cmd>" self.script.log (1, "configure: sending XML req") sock.send(req) recv_reply = sock.recv (65536) self.script.log (1, "configure: received " + recv_reply) sock.close() return ("ok", "data product " + str (data_product_id) + " configured") else: response = "expected 0, 1 or 4 arguments" self.script.log (-1, "configure: " + response) return ("fail", response) @return_reply(Str()) def request_deconfigure(self, req, msg): """Deconfigure for the data_product.""" if len(msg.arguments) == 0: self.script.log (-1, "request_configure: no arguments provided") return ("fail", "expected 1 argument") # the sub-array identifier data_product_id = msg.arguments[0] self.script.log (1, "configure: deconfiguring " + str(data_product_id)) # check if the data product was previously configured if not data_product_id == self._data_product["id"]: response = str(data_product_id) + " did not match configured data product [" + self._data_product["id"] + "]" self.script.log (-1, "configure: " + response) return ("fail", response) for istream in range(int(self.script.cfg["NUM_STREAM"])): (host, beam_idx, subband) = self.script.cfg["STREAM_" + str(istream)].split(":") if self.script.beam_name == self.script.cfg["BEAM_" + beam_idx]: # reset ADC_SYNC_TIME on the beam self.script.beam_config["lock"].acquire() self.script.beam_config["ADC_SYNC_TIME"] = "0"; self.script.beam_config["lock"].release() port = int(self.script.cfg["STREAM_RECV_PORT"]) + istream self.script.log (3, "configure: connecting to " + host + ":" + str(port)) sock = sockets.openSocket (DL, host, port, 1) if sock: req = "<?req version='1.0' encoding='ISO-8859-1'?>" req += "<recv_cmd>" req += "<command>deconfigure</command>" req += "</recv_cmd>" sock.send(req) recv_reply = sock.recv (65536) sock.close() # remove the data product self._data_product["id"] = "None" response = "data product " + str(data_product_id) + " deconfigured" self.script.log (1, "configure: " + response) return ("ok", response) @request(Int()) @return_reply(Str()) def request_output_channels (self, req, nchannels): """Set the number of output channels.""" if not self.test_power_of_two (nchannels): return ("fail", "number of channels not a power of two") if nchannels < 64 or nchannels > 4096: return ("fail", "number of channels not within range 64 - 2048") self.script.beam_config["OUTNCHAN"] = str(nchannels) return ("ok", "") @request(Int()) @return_reply(Str()) def request_output_bins(self, req, nbin): """Set the number of output phase bins.""" if not self.test_power_of_two(nbin): return ("fail", "nbin not a power of two") if nbin < 64 or nbin > 2048: return ("fail", "nbin not within range 64 - 2048") self.script.beam_config["OUTNBIN"] = str(nbin) return ("ok", "") @request(Int()) @return_reply(Str()) def request_output_tsubint (self, req, tsubint): """Set the length of output sub-integrations.""" if tsubint < 10 or tsubint > 60: return ("fail", "length of output subints must be between 10 and 600 seconds") self.script.beam_config["OUTTSUBINT"] = str(tsubint) return ("ok", "") @request(Float()) @return_reply(Str()) def request_dm(self, req, dm): """Set the value of dispersion measure to be removed""" if dm < 0 or dm > 2000: return ("fail", "dm not within range 0 - 2000") self.script.beam_config["DM"] = str(dm) return ("ok", "") @request(Float()) @return_reply(Str()) def request_cal_freq(self, req, cal_freq): """Set the value of noise diode firing frequecny in Hz.""" if cal_freq < 0 or cal_freq > 1000: return ("fail", "CAL freq not within range 0 - 1000") self.script.beam_config["CALFREQ"] = str(cal_freq) if cal_freq == 0: self.script.beam_config["MODE"] = "PSR" else: self.script.beam_config["MODE"] = "CAL" return ("ok", "") # test if a number is a power of two def test_power_of_two (self, num): return num > 0 and not (num & (num - 1)) # test whether the specified target exists in the pulsar catalog def test_pulsar_valid (self, target): self.script.log (2, "test_pulsar_valid: get_psrcat_param (" + target + ", jname)") (reply, message) = self.get_psrcat_param (target, "jname") if reply != "ok": return (reply, message) self.script.log (2, "test_pulsar_valid: get_psrcat_param () reply=" + reply + " message=" + message) if message == target: return ("ok", "") else: return ("fail", "pulsar " + target + " did not exist in catalog") def get_psrcat_param (self, target, param): cmd = "psrcat -all " + target + " -c " + param + " -nohead -o short" rval, lines = self.script.system (cmd, 3) if rval != 0 or len(lines) <= 0: return ("fail", "could not use psrcat") if lines[0].startswith("WARNING"): return ("fail", "pulsar " + target_name + " did not exist in catalog") parts = lines[0].split() if len(parts) == 2 and parts[0] == "1": return ("ok", parts[1])
class FitsInterfaceServer(AsyncDeviceServer): """ Class providing an interface between EDD processes and the Effelsberg FITS writer """ VERSION_INFO = ("edd-fi-server-api", 1, 0) BUILD_INFO = ("edd-fi-server-implementation", 0, 1, "") DEVICE_STATUSES = ["ok", "degraded", "fail"] PROTOCOL_INFO = ProtocolFlags( 5, 0, set([ ProtocolFlags.MULTI_CLIENT, ProtocolFlags.MESSAGE_IDS, ])) def __init__(self, interface, port, capture_interface, capture_port, fw_ip, fw_port): """ @brief Initialization of the FitsInterfaceServer object @param interface Interface address to serve on @param port Port number to serve on @param capture_interface Interface to capture data on @param capture_port Port to capture data on from instruments @param fw_ip IP address of the FITS writer @param fw_port Port number to connected to on FITS writer """ self._configured = False self._no_active_beams = None self._nchannels = None self._integ_time = None self._blank_phase = None self._capture_interface = capture_interface self._capture_port = capture_port self._fw_connection_manager = FitsWriterConnectionManager( fw_ip, fw_port) self._capture_thread = None self._shutdown = False super(FitsInterfaceServer, self).__init__(interface, port) def start(self): """ @brief Start the server """ self._fw_connection_manager.start() super(FitsInterfaceServer, self).start() def stop(self): """ @brief Stop the server """ self._shutdown = True self._stop_capture() self._fw_connection_manager.stop() super(FitsInterfaceServer, self).stop() @property def nbeams(self): return self._active_beams_sensor.value() @nbeams.setter def nbeams(self, value): self._active_beams_sensor.set_value(value) @property def nchannels(self): return self._nchannels_sensor.value() @nchannels.setter def nchannels(self, value): self._nchannels_sensor.set_value(value) @property def integration_time(self): return self._integration_time_sensor.value() @integration_time.setter def integration_time(self, value): self._integration_time_sensor.set_value(value) @property def nblank_phases(self): return self._nblank_phases_sensor.value() @nblank_phases.setter def nblank_phases(self, value): self._nblank_phases_sensor.set_value(value) def setup_sensors(self): """ @brief Setup monitoring sensors """ self._device_status_sensor = Sensor.discrete( "device-status", description="Health status of FIServer", params=self.DEVICE_STATUSES, default="ok", initial_status=Sensor.UNKNOWN) self.add_sensor(self._device_status_sensor) self._active_beams_sensor = Sensor.float( "nbeams", description="Number of beams that are currently active", default=1, initial_status=Sensor.UNKNOWN) self.add_sensor(self._active_beams_sensor) self._nchannels_sensor = Sensor.float( "nchannels", description="Number of channels in each beam", default=1, initial_status=Sensor.UNKNOWN) self.add_sensor(self._nchannels_sensor) self._integration_time_sensor = Sensor.float( "integration-time", description="The integration time for each beam", default=1, initial_status=Sensor.UNKNOWN) self.add_sensor(self._integration_time_sensor) self._nblank_phases_sensor = Sensor.integer( "nblank-phases", description="The number of blank phases", default=1, initial_status=Sensor.UNKNOWN) self.add_sensor(self._nblank_phases_sensor) def _stop_capture(self): if self._capture_thread: log.debug("Cleaning up capture thread") self._capture_thread.stop() self._capture_thread.join() self._capture_thread = None log.debug("Capture thread cleaned") @request(Int(), Int(), Int(), Int()) @return_reply() def request_configure(self, req, beams, channels, int_time, blank_phases): """ @brief Configure the FITS interface server @param beams The number of beams expected @param channels The number of channels expected @param int_time The integration time (milliseconds int) @param blank_phases The number of blank phases (1-4) @return katcp reply object [[[ !configure ok | (fail [error description]) ]]] """ message = ("nbeams={}, nchannels={}, integration_time={}," " nblank_phases={}").format(beams, channels, int_time, blank_phases) log.info("Configuring FITS interface server with params: {}".format( message)) self.nbeams = beams self.nchannels = channels self.integration_time = int_time self.nblank_phases = blank_phases self._fw_connection_manager.drop_connection() self._stop_capture() self._configured = True return ("ok", ) @request() @return_reply() def request_start(self, req): """ @brief Start the FITS interface server capturing data @return katcp reply object [[[ !configure ok | (fail [error description]) ]]] """ log.info("Received start request") if not self._configured: msg = "FITS interface server is not configured" log.error(msg) return ("fail", msg) try: fw_socket = self._fw_connection_manager.get_transmit_socket() except Exception as error: log.exception(str(error)) return ("fail", str(error)) log.info("Starting FITS interface capture") self._stop_capture() buffer_size = 4 * (self.nchannels + 2) handler = R2SpectrometerHandler(2, self.nchannels, self.integration_time, self.nblank_phases, fw_socket) self._capture_thread = CaptureData(self._capture_interface, self._capture_port, buffer_size, handler) self._capture_thread.start() return ("ok", ) @request() @return_reply() def request_stop(self, req): """ @brief Stop the FITS interface server capturing data @return katcp reply object [[[ !configure ok | (fail [error description]) ]]] """ log.info("Received stop request") if not self._configured: msg = "FITS interface server is not configured" log.error(msg) return ("fail", msg) log.info("Stopping FITS interface capture") self._stop_capture() self._fw_connection_manager.drop_connection() return ("ok", )
class ScamSimulator(DeviceServer): VERSION_INFO = ("scam-simulator-version", 1, 0) BUILD_INFO = ("scam-simulator-build", 0, 1, "") # Optionally set the KATCP protocol version and features. Defaults to # the latest implemented version of KATCP, with all supported optional # features PROTOCOL_INFO = ProtocolFlags(4, 0, set([ ProtocolFlags.MULTI_CLIENT, #ProtocolFlags.MESSAGE_IDS, ])) def setup_sensors(self): # Position sensors self._SCM_request_azim = Sensor.float("SCM.request-azim", "Sky-space requested azimuth position.", "Degrees CW of north") self.add_sensor(self._SCM_request_azim) self._SCM_request_elev = Sensor.float("SCM.request-elev", "Sky-space requested elevation position.", "Degrees CW of north") self.add_sensor(self._SCM_request_elev) self._SCM_desired_azim = Sensor.float("SCM.desired-azim", "Sky-space desired azimuth position.", "Degrees CW of north") self.add_sensor(self._SCM_desired_azim) self._SCM_desired_elev = Sensor.float("SCM.desired-elev", "Sky-space desired elevation position.", "Degrees CW of north") self.add_sensor(self._SCM_desired_elev) self._SCM_actual_azim = Sensor.float("SCM.actual-azim", "Sky-space actual azimuth position.", "Degrees CW of north") self.add_sensor(self._SCM_actual_azim) self._SCM_actual_elev = Sensor.float("SCM.actual-elev", "Sky-space actual elevation position.", "Degrees CW of north") self.add_sensor(self._SCM_actual_elev) # Pointing model self._SCM_pmodel1 = Sensor.float("SCM.pmodel1", "Pointing model parameter 1") self.add_sensor(self._SCM_pmodel1) self._SCM_pmodel2 = Sensor.float("SCM.pmodel2", "Pointing model parameter 2") self.add_sensor(self._SCM_pmodel2) self._SCM_pmodel3 = Sensor.float("SCM.pmodel3", "Pointing model parameter 3") self.add_sensor(self._SCM_pmodel3) self._SCM_pmodel4 = Sensor.float("SCM.pmodel4", "Pointing model parameter 4") self.add_sensor(self._SCM_pmodel4) self._SCM_pmodel5 = Sensor.float("SCM.pmodel5", "Pointing model parameter 5") self.add_sensor(self._SCM_pmodel5) self._SCM_pmodel6 = Sensor.float("SCM.pmodel6", "Pointing model parameter 6") self.add_sensor(self._SCM_pmodel6) self._SCM_pmodel7 = Sensor.float("SCM.pmodel7", "Pointing model parameter 7") self.add_sensor(self._SCM_pmodel7) self._SCM_pmodel8 = Sensor.float("SCM.pmodel8", "Pointing model parameter 8") self.add_sensor(self._SCM_pmodel8) self._SCM_pmodel9 = Sensor.float("SCM.pmodel9", "Pointing model parameter 9") self.add_sensor(self._SCM_pmodel9) self._SCM_pmodel10 = Sensor.float("SCM.pmodel10", "Pointing model parameter 10") self.add_sensor(self._SCM_pmodel10) self._SCM_pmodel11 = Sensor.float("SCM.pmodel11", "Pointing model parameter 11") self.add_sensor(self._SCM_pmodel11) self._SCM_pmodel12 = Sensor.float("SCM.pmodel12", "Pointing model parameter 12") self.add_sensor(self._SCM_pmodel12) self._SCM_pmodel13 = Sensor.float("SCM.pmodel13", "Pointing model parameter 13") self.add_sensor(self._SCM_pmodel13) self._SCM_pmodel14= Sensor.float("SCM.pmodel14", "Pointing model parameter 14") self.add_sensor(self._SCM_pmodel14) self._SCM_pmodel15= Sensor.float("SCM.pmodel15", "Pointing model parameter 15") self.add_sensor(self._SCM_pmodel15) self._SCM_pmodel16 = Sensor.float("SCM.pmodel16", "Pointing model parameter 16") self.add_sensor(self._SCM_pmodel16) self._SCM_pmodel17 = Sensor.float("SCM.pmodel17", "Pointing model parameter 17") self.add_sensor(self._SCM_pmodel17) self._SCM_pmodel18 = Sensor.float("SCM.pmodel18", "Pointing model parameter 18") self.add_sensor(self._SCM_pmodel18) self._SCM_pmodel19 = Sensor.float("SCM.pmodel19", "Pointing model parameter 19") self.add_sensor(self._SCM_pmodel19) self._SCM_pmodel20 = Sensor.float("SCM.pmodel20", "Pointing model parameter 20") self.add_sensor(self._SCM_pmodel20) self._SCM_pmodel21 = Sensor.float("SCM.pmodel21", "Pointing model parameter 21") self.add_sensor(self._SCM_pmodel21) self._SCM_pmodel22 = Sensor.float("SCM.pmodel22", "Pointing model parameter 22") self.add_sensor(self._SCM_pmodel22) self._SCM_pmodel23 = Sensor.float("SCM.pmodel23", "Pointing model parameter 23") self.add_sensor(self._SCM_pmodel23) self._SCM_pmodel24 = Sensor.float("SCM.pmodel24", "Pointing model parameter 24") self.add_sensor(self._SCM_pmodel24) self._SCM_pmodel25 = Sensor.float("SCM.pmodel25", "Pointing model parameter 25") self.add_sensor(self._SCM_pmodel25) self._SCM_pmodel26 = Sensor.float("SCM.pmodel26", "Pointing model parameter 26") self.add_sensor(self._SCM_pmodel26) self._SCM_pmodel27 = Sensor.float("SCM.pmodel27", "Pointing model parameter 27") self.add_sensor(self._SCM_pmodel27) self._SCM_pmodel28 = Sensor.float("SCM.pmodel28", "Pointing model parameter 28") self.add_sensor(self._SCM_pmodel28) self._SCM_pmodel29 = Sensor.float("SCM.pmodel29", "Pointing model parameter 29") self.add_sensor(self._SCM_pmodel29) self._SCM_pmodel30 = Sensor.float("SCM.pmodel30", "Pointing model parameter 30") self.add_sensor(self._SCM_pmodel30) # # Target self._SCM_Target = Sensor.string("SCM.Target", "Target description string in katpoint format") self.add_sensor(self._SCM_Target) # Antenna activity self._SCM_Antenna_Activity = Sensor.string("SCM.AntennaActivity", "Antenna activity label") self.add_sensor(self._SCM_Antenna_Activity) # RF sensor information self._SCM_LcpAttenuation = Sensor.float("SCM.LcpAttenuation", "Variable attenuator setting on LCP") self.add_sensor(self._SCM_LcpAttenuation) self._SCM_RcpAttenuation = Sensor.float("SCM.RcpAttenuation", "Variable attenuator setting on RCP") self.add_sensor(self._SCM_RcpAttenuation) self._RFC_LcpFreqSel = Sensor.boolean("RFC.LcpFreqSel", "LCP Frequency Select Switch") self.add_sensor(self._RFC_LcpFreqSel) self._RFC_RcpFreqSel = Sensor.boolean("RFC.RcpFreqSel", "RCP Frequency Select Switch") self.add_sensor(self._RFC_RcpFreqSel) self._RFC_IntermediateStage_5GHz = Sensor.float("RFC.IntermediateStage_5GHz", "5 GHz Intermediate Stage LO frequency") self.add_sensor(self._RFC_IntermediateStage_5GHz) self._RFC_IntermediateStage_6_7GHz = Sensor.float("RFC.IntermediateStage_6_7GHz", "6.7 GHz Intermediate Stage LO frequency") self.add_sensor(self._RFC_IntermediateStage_6_7GHz) self._RFC_FinalStage = Sensor.float("RFC.FinalStage", "Final Stage LO frequency") self.add_sensor(self._RFC_FinalStage) # Noise diode sensor information self._RFC_NoiseDiode_1 = Sensor.integer("RFC.NoiseDiode_1", "All noise diode data (bitfield)") self.add_sensor(self._RFC_NoiseDiode_1) # EMS information self._EMS_WindDirection = Sensor.float("EMS.WindDirection", "Wind direction") self.add_sensor(self._EMS_WindDirection) self._EMS_WindSpeed = Sensor.float("EMS.WindSpeed", "Wind speed") self.add_sensor(self._EMS_WindSpeed) self._EMS_AirTemperature = Sensor.float("EMS.AirTemperature", "Air temperature") self.add_sensor(self._EMS_AirTemperature) self._EMS_AbsolutePressure = Sensor.float("EMS.AbsolutePressure", "Air pressure") self.add_sensor(self._EMS_AbsolutePressure) self._EMS_RelativeHumidity = Sensor.float("EMS.RelativeHumidity", "Ambient relative humidity") self.add_sensor(self._EMS_RelativeHumidity) self.animation_thread = threading.Thread(target=self.sensor_value_thread_function) self.animation_thread.start() def sensor_value_thread_function(self): antenna_str = "ant1, 5:45:2.48, -0:18:17.92, 116, 32.0, 0 0 0, %s" % ("0 " * 23) antenna = katpoint.Antenna(antenna_str) target_str = "name1 | *name 2, radec, 12:34:56.7, -04:34:34.2, (1000.0 2000.0 1.0)" self._SCM_Target.set_value(target_str) self._SCM_Antenna_Activity.set_value("idle") my_target = AVNTarget(target_str, antenna=antenna) self._SCM_pmodel1.set_value(random.random()) self._SCM_pmodel2.set_value(random.random()) self._SCM_pmodel3.set_value(random.random()) self._SCM_pmodel4.set_value(random.random()) self._SCM_pmodel5.set_value(random.random()) self._SCM_pmodel6.set_value(random.random()) self._SCM_pmodel7.set_value(random.random()) self._SCM_pmodel8.set_value(random.random()) self._SCM_pmodel9.set_value(random.random()) self._SCM_pmodel10.set_value(random.random()) self._SCM_pmodel11.set_value(random.random()) self._SCM_pmodel12.set_value(random.random()) self._SCM_pmodel13.set_value(random.random()) self._SCM_pmodel14.set_value(random.random()) self._SCM_pmodel15.set_value(random.random()) self._SCM_pmodel16.set_value(random.random()) self._SCM_pmodel17.set_value(random.random()) self._SCM_pmodel18.set_value(random.random()) self._SCM_pmodel19.set_value(random.random()) self._SCM_pmodel20.set_value(random.random()) self._SCM_pmodel21.set_value(random.random()) self._SCM_pmodel22.set_value(random.random()) self._SCM_pmodel23.set_value(random.random()) self._SCM_pmodel24.set_value(random.random()) self._SCM_pmodel25.set_value(random.random()) self._SCM_pmodel26.set_value(random.random()) self._SCM_pmodel27.set_value(random.random()) self._SCM_pmodel28.set_value(random.random()) self._SCM_pmodel29.set_value(random.random()) self._SCM_pmodel30.set_value(random.random()) # There shouldn't be step-changes in the environment data, so set initial values. WindDirection = 360*random.random() WindSpeed = 50*random.random() AirTemperature = 50*random.random() AbsolutePressure = 10*random.random() + 1010.0 RelativeHumidity = 100*random.random() while True: target_azel = my_target.azel() self._SCM_request_azim.set_value(np.degrees(target_azel[0])) self._SCM_request_elev.set_value(np.degrees(target_azel[1])) self._SCM_desired_azim.set_value(np.trunc(10*self._SCM_request_azim.value())/10) self._SCM_desired_elev.set_value(np.trunc(10*self._SCM_request_elev.value())/10) self._SCM_actual_azim.set_value(self._SCM_desired_azim.value() + random.random()/25) self._SCM_actual_elev.set_value(self._SCM_desired_elev.value() + random.random()/25) if random.random() > 0.5: coin_toss = int(4*random.random()) if coin_toss == 0: self._SCM_Antenna_Activity.set_value("idle") elif coin_toss == 1: self._SCM_Antenna_Activity.set_value("slew") elif coin_toss == 2: self._SCM_Antenna_Activity.set_value("track") elif coin_toss == 3: self._SCM_Antenna_Activity.set_value("scan") else: self._SCM_Antenna_Activity.set_value("stop") # Shouldn't happen, but for logical completeness... attenuation = float(random.randint(0, 63))/2 self._SCM_LcpAttenuation.set_value(attenuation) self._SCM_RcpAttenuation.set_value(attenuation) # Frequency band doesn't need to be changed as frequently as the other stuff. if random.random() > 0.925: freq_sel = bool(random.randint(0, 1)) self._RFC_LcpFreqSel.set_value(freq_sel) self._RFC_RcpFreqSel.set_value(freq_sel) self._RFC_IntermediateStage_5GHz.set_value(50e6*random.random() + 1.5e9) self._RFC_IntermediateStage_6_7GHz.set_value(50e6*random.random() + 3.2e9) self._RFC_FinalStage.set_value(50e6*random.random() + 2.85e9) # Noise diode info also doesn't need to change every tick. if random.random() > 0.6: input_source = random.randint(0, 3) bit_2 = 0 enable = random.randint(0, 1) noise_diode_select = random.randint(1, 15) # Not actually sure if this is supposed to be one-hot pwm_mark = random.randint(0, 63) freq_sel = random.randint(0, 3) bitfield = input_source + bit_2*2**2 + enable*2**3 + noise_diode_select*2**4 + pwm_mark*2**8\ + freq_sel*2**14 self._RFC_NoiseDiode_1.set_value(bitfield) # Climate information only needs to change occasionally too if random.random() > 0.35: WindDirection += 2*random.random() - 1 self._EMS_WindDirection.set_value(WindDirection) WindSpeed += random.random() - 0.5 self._EMS_WindSpeed.set_value(WindSpeed) AirTemperature += 0.5*random.random() - 0.25 self._EMS_AirTemperature.set_value(AirTemperature) AbsolutePressure += 0.1*random.random() - 0.05 self._EMS_AbsolutePressure.set_value(AbsolutePressure) RelativeHumidity += 0.1*random.random() - 0.05 self._EMS_RelativeHumidity.set_value(RelativeHumidity) print "\n\nSensors as at {}".format(time.time()) print "============================================" for element_name in iter(dir(self)): element = getattr(self, element_name) #print "\nElement: {}".format(element) #print "Type: {}".format(type(element)) #print "Isinstance: {}".format(isinstance(element, Sensor)) if isinstance(element, Sensor): print "{} {} {}".format(element._timestamp, element.name, element._value) sys.stdout.flush() time.sleep(random.random()*4 + 1)
class KATCPServer(DeviceServer): VERSION_INFO = ("ptuse-api", 2, 0) BUILD_INFO = ("ptuse-implementation", 0, 1, "") # Optionally set the KATCP protocol version and features. Defaults to # the latest implemented version of KATCP, with all supported optional # features PROTOCOL_INFO = ProtocolFlags( 5, 0, set([ ProtocolFlags.MULTI_CLIENT, ProtocolFlags.MESSAGE_IDS, ])) def __init__(self, server_host, server_port, script): self.script = script self._host_sensors = {} self._beam_sensors = {} self._data_product = {} self._data_product["id"] = "None" self._data_product["state"] = "None" self.data_product_res = [] self.data_product_res.append(re.compile("^[a-zA-Z]+_1")) self.data_product_res.append(re.compile("^[a-zA-Z]+_2")) self.data_product_res.append(re.compile("^[a-zA-Z]+_3")) self.data_product_res.append(re.compile("^[a-zA-Z]+_4")) self.script.log( 2, "KATCPServer::__init__ starting DeviceServer on " + server_host + ":" + str(server_port)) DeviceServer.__init__(self, server_host, server_port) DEVICE_STATUSES = ["ok", "degraded", "fail"] def setup_sensors(self): """Setup server sensors.""" self.script.log(2, "KATCPServer::setup_sensors()") self._device_status = Sensor.discrete( "device-status", description="Status of entire system", params=self.DEVICE_STATUSES, default="ok") self.add_sensor(self._device_status) self._beam_name = Sensor.string("beam-name", description="name of configured beam", unit="", default="") self.add_sensor(self._beam_name) # setup host based sensors self._host_name = Sensor.string("host-name", description="hostname of this server", unit="", default="") self.add_sensor(self._host_name) # GUI URL TODO remove hardcoding guis = [{ "title": "PTUSE Web Interface", "description": "Live Pulsar timing monitoring plots", "href": self.script.cfg["SPIP_ADDRESS"] }] encoded = json.dumps(guis) self._gui_urls = Sensor.string("gui-urls", description="PTUSE GUI URL", unit="", default=encoded) self.add_sensor(self._gui_urls) self._gui_urls.set_value(encoded) # give LMC some time to prepare the socket time.sleep(5) self.script.log( 1, "KATCPServer::setup_sensors lmc=" + str(self.script.lmc)) (host, port) = self.script.lmc.split(":") self.setup_sensors_host(host, port) self.script.log( 2, "KATCPServer::setup_sensors beams=" + str(self.script.beam)) self.setup_sensors_beam(self.script.beam_name) # add sensors based on the reply from the specified host def setup_sensors_host(self, host, port): self.script.log( 1, "KATCPServer::setup_sensors_host (" + host + "," + port + ")") sock = sockets.openSocket(DL, host, int(port), 1) if sock: self.script.log( 2, "KATCPServer::setup_sensors_host sock.send(" + self.script.lmc_cmd + ")") sock.send(self.script.lmc_cmd + "\r\n") lmc_reply = sock.recv(65536) sock.close() xml = xmltodict.parse(lmc_reply) self.script.log( 2, "KATCPServer::setup_sensors_host sock.recv=" + str(xml)) self._host_sensors = {} # Disk sensors self.script.log( 2, "KATCPServer::setup_sensors_host configuring disk sensors") disk_prefix = host + ".disk" self._host_sensors["disk_size"] = Sensor.float( disk_prefix + ".size", description=host + ": disk size", unit="MB", params=[8192, 1e9], default=0) self._host_sensors["disk_available"] = Sensor.float( disk_prefix + ".available", description=host + ": disk available space", unit="MB", params=[1024, 1e9], default=0) self.add_sensor(self._host_sensors["disk_size"]) self.add_sensor(self._host_sensors["disk_available"]) # Server Load sensors self.script.log( 2, "KATCPServer::setup_sensors_host configuring load sensors") self._host_sensors["num_cores"] = Sensor.integer( host + ".num_cores", description=host + ": disk available space", unit="MB", params=[1, 64], default=0) self._host_sensors["load1"] = Sensor.float(host + ".load.1min", description=host + ": 1 minute load ", unit="", default=0) self._host_sensors["load5"] = Sensor.float(host + ".load.5min", description=host + ": 5 minute load ", unit="", default=0) self._host_sensors["load15"] = Sensor.float(host + ".load.15min", description=host + ": 15 minute load ", unit="", default=0) self._host_sensors["local_time_synced"] = Sensor.boolean( "local_time_synced", description=host + ": NTP server synchronisation", unit="", default=0) self.add_sensor(self._host_sensors["num_cores"]) self.add_sensor(self._host_sensors["num_cores"]) self.add_sensor(self._host_sensors["load1"]) self.add_sensor(self._host_sensors["load5"]) self.add_sensor(self._host_sensors["load15"]) self.add_sensor(self._host_sensors["local_time_synced"]) cpu_temp_pattern = re.compile("cpu[0-9]+_temp") fan_speed_pattern = re.compile("fan[0-9,a-z]+") power_supply_pattern = re.compile("ps[0-9]+_status") self.script.log( 2, "KATCPServer::setup_sensors_host configuring other metrics") if not xml["lmc_reply"]["sensors"] == None: for sensor in xml["lmc_reply"]["sensors"]["metric"]: name = sensor["@name"] if name == "system_temp": self._host_sensors[name] = Sensor.float( (host + ".system_temp"), description=host + ": system temperature", unit="C", params=[-20, 150], default=0) self.add_sensor(self._host_sensors[name]) if cpu_temp_pattern.match(name): (cpu, junk) = name.split("_") self._host_sensors[name] = Sensor.float( (host + "." + name), description=host + ": " + cpu + " temperature", unit="C", params=[-20, 150], default=0) self.add_sensor(self._host_sensors[name]) if fan_speed_pattern.match(name): self._host_sensors[name] = Sensor.float( (host + "." + name), description=host + ": " + name + " speed", unit="RPM", params=[0, 20000], default=0) self.add_sensor(self._host_sensors[name]) if power_supply_pattern.match(name): self._host_sensors[name] = Sensor.boolean( (host + "." + name), description=host + ": " + name, unit="", default=0) self.add_sensor(self._host_sensors[name]) # TODO consider adding power supply sensors: e.g. # device-status-kronos1-powersupply1 # device-status-kronos1-powersupply2 # device-status-kronos2-powersupply1 # device-status-kronos2-powersupply2 # TODO consider adding raid/disk sensors: e.g. # device-status-<host>-raid # device-status-<host>-raid-disk1 # device-status-<host>-raid-disk2 self.script.log(2, "KATCPServer::setup_sensors_host done!") else: self.script.log( 2, "KATCPServer::setup_sensors_host no sensors found") else: self.script.log( -2, "KATCPServer::setup_sensors_host: could not connect to LMC") # setup sensors for each beam def setup_sensors_beam(self, beam): b = str(beam) self._beam_sensors = {} self.script.log(2, "KATCPServer::setup_sensors_beam beam=" + b) self._beam_sensors["observing"] = Sensor.boolean("observing", description="Beam " + b + " is observing", unit="", default=0) self.add_sensor(self._beam_sensors["observing"]) self._beam_sensors["snr"] = Sensor.float("snr", description="SNR of Beam " + b, unit="", params=[0, 1e9], default=0) self.add_sensor(self._beam_sensors["snr"]) self._beam_sensors["beamformer_stddev_polh"] = Sensor.float( "beamformer_stddev_polh", description="Standard deviation of beam voltages for pol H", unit="", params=[0, 127], default=0) self.add_sensor(self._beam_sensors["beamformer_stddev_polh"]) self._beam_sensors["beamformer_stddev_polv"] = Sensor.float( "beamformer_stddev_polv", description="Standard deviation of beam voltages for pol V", unit="", params=[0, 127], default=0) self.add_sensor(self._beam_sensors["beamformer_stddev_polv"]) self._beam_sensors["integrated"] = Sensor.float( "integrated", description="Length of integration for Beam " + b, unit="", default=0) self.add_sensor(self._beam_sensors["integrated"]) self._beam_sensors["input_channels"] = Sensor.integer( "input_channels", description="Number of configured input channels for Beam " + b, unit="", default=0) self.add_sensor(self._beam_sensors["input_channels"]) @request() @return_reply(Str()) def request_beam(self, req): """Return the configure beam name.""" return ("ok", self._beam_name.value()) @request() @return_reply(Str()) def request_host_name(self, req): """Return the name of this server.""" return ("ok", self._host_name.value()) @request() @return_reply(Float()) def request_snr(self, req): """Return the SNR for this beam.""" return ("ok", self._beam_sensors["snr"].value()) @request() @return_reply(Float()) def request_beamformer_stddev_polh(self, req): """Return the standard deviation of the 8-bit power level of pol H.""" return ("ok", self._beam_sensors["beamformer_stddev_polh"].value()) @request() @return_reply(Float()) def request_beamformer_stddev_polv(self, req): """Return the standard deviation of the 8-bit power level of pol V.""" return ("ok", self._beam_sensors["beamformer_stddev_polv"].value()) @request() @return_reply(Float()) def request_local_time_synced(self, req): """Return the sychronisation with NTP time""" return ("ok", self._beam_sensors["local_time_synced"].value()) @request(Float()) @return_reply(Str()) def request_sync_time(self, req, adc_sync_time): """Set the ADC_SYNC_TIME for beam of the data product.""" if self._data_product["id"] == "None": return ("fail", "data product was not configured") self.script.beam_config["lock"].acquire() self.script.beam_config["ADC_SYNC_TIME"] = str(adc_sync_time) self.script.beam_config["lock"].release() return ("ok", "") @request(Str()) @return_reply(Str()) def request_proposal_id(self, req, proposal_id): """Set the PROPOSAL_ID for the data product.""" if self._data_product["id"] == "None": return ("fail", "data product was not configured") self.script.beam_config["lock"].acquire() self.script.beam_config["PROPOSAL_ID"] = proposal_id self.script.beam_config["lock"].release() return ("ok", "") # ensure state changes work def change_state(self, command): state = self._data_product["state"] reply = "ok" message = "" if command == "configure": if state != "unconfigured": message = "received " + command + " command when in " + state + " state" else: new_state = "configured" elif command == "capture_init": if state != "configured": message = "received " + command + " command when in " + state + " state" else: new_state = "ready" elif command == "target_start": if state != "ready": message = "received " + command + " command when in " + state + " state" else: new_state = "recording" elif command == "target_stop": if state != "recording": message = "received " + command + " command when in " + state + " state" else: new_state = "ready" elif command == "capture_done": if state != "ready" and state != "configured": message = "received " + command + " command when in " + state + " state" else: new_state = "configured" elif command == "deconfigure": if state != "configured": message = "received " + command + " command when in " + state + " state" else: new_state = "unconfigured" if message == "": self.script.log( 1, "change_state: " + self._data_product["state"] + " -> " + new_state) self._data_product["state"] = new_state else: self.script.log(-1, "change_state: " + message) reply = "fail" return (reply, message) @request(Str()) @return_reply(Str()) def request_target_start(self, req, target_name): """Commence data processing using target.""" self.script.log(1, "request_target_start(" + target_name + ")") if self._data_product["id"] == "None": return ("fail", "data product was not configured") self.script.log( 1, "request_target_start ADC_SYNC_TIME=" + self.script.cam_config["ADC_SYNC_TIME"]) self.script.beam_config["lock"].acquire() self.script.beam_config["TARGET"] = self.script.cam_config["TARGET"] if self.script.cam_config["ADC_SYNC_TIME"] != "0": self.script.beam_config["ADC_SYNC_TIME"] = self.script.cam_config[ "ADC_SYNC_TIME"] self.script.beam_config["NCHAN_PER_STREAM"] = self.script.cam_config[ "NCHAN_PER_STREAM"] self.script.beam_config[ "PRECISETIME_FRACTION_POLV"] = self.script.cam_config[ "PRECISETIME_FRACTION_POLV"] self.script.beam_config[ "PRECISETIME_FRACTION_POLH"] = self.script.cam_config[ "PRECISETIME_FRACTION_POLH"] self.script.beam_config[ "PRECISETIME_UNCERTAINTY_POLV"] = self.script.cam_config[ "PRECISETIME_UNCERTAINTY_POLV"] self.script.beam_config[ "PRECISETIME_UNCERTAINTY_POLH"] = self.script.cam_config[ "PRECISETIME_UNCERTAINTY_POLH"] self.script.beam_config["TFR_KTT_GNSS"] = self.script.cam_config[ "TFR_KTT_GNSS"] self.script.beam_config["ITRF"] = self.script.cam_config["ITRF"] self.script.beam_config["OBSERVER"] = self.script.cam_config[ "OBSERVER"] self.script.beam_config["ANTENNAE"] = self.script.cam_config[ "ANTENNAE"] self.script.beam_config["SCHEDULE_BLOCK_ID"] = self.script.cam_config[ "SCHEDULE_BLOCK_ID"] self.script.beam_config["PROPOSAL_ID"] = self.script.cam_config[ "PROPOSAL_ID"] self.script.beam_config["EXPERIMENT_ID"] = self.script.cam_config[ "EXPERIMENT_ID"] self.script.beam_config["DESCRIPTION"] = self.script.cam_config[ "DESCRIPTION"] self.script.beam_config["lock"].release() # check the pulsar specified is listed in the catalog (result, message) = self.test_pulsar_valid(target_name) if result != "ok": return (result, message) # check the ADC_SYNC_TIME is valid for this beam if self.script.beam_config["ADC_SYNC_TIME"] == "0": return ("fail", "ADC Synchronisation Time was not valid") # change the state (result, message) = self.change_state("target_start") if result != "ok": self.script.log(-1, "target_start: change_state failed: " + message) return (result, message) # set the pulsar name, this should include a check if the pulsar is in the catalog self.script.beam_config["lock"].acquire() self.script.beam_config["SOURCE"] = target_name self.script.beam_config["lock"].release() host = self.script.tcs_host port = self.script.tcs_port self.script.log( 2, "request_target_start: opening socket to " + host + ":" + str(port)) sock = sockets.openSocket(DL, host, int(port), 1) if sock: xml = self.script.get_xml_config() self.script.log(2, "request_target_start: get_xml_config=" + str(xml)) sock.send(xml + "\r\n") reply = sock.recv(65536) self.script.log(2, "request_target_start: reply=" + str(reply)) xml = self.script.get_xml_start_cmd() self.script.log( 2, "request_target_start: get_xml_start_cmd=" + str(xml)) sock.send(xml + "\r\n") reply = sock.recv(65536) self.script.log(2, "request_target_start: reply=" + str(reply)) sock.close() return ("ok", "") else: return ("fail", "could not connect to TCS") @request() @return_reply(Str()) def request_target_stop(self, req): """Cease data processing with target_name.""" self.script.log(1, "request_target_stop()") return self.target_stop() def target_stop(self): if self._data_product["id"] == "None": return ("fail", "data product was not configured") # change the state (result, message) = self.change_state("target_stop") if result != "ok": self.script.log(-1, "target_stop: change_state failed: " + message) return (result, message) self.script.reset_beam_config() host = self.script.tcs_host port = self.script.tcs_port sock = sockets.openSocket(DL, host, int(port), 1) if sock: xml = self.script.get_xml_stop_cmd() sock.send(xml + "\r\n") reply = sock.recv(65536) sock.close() return ("ok", "") else: return ("fail", "could not connect to tcs[beam]") @request() @return_reply(Str()) def request_capture_init(self, req): """Prepare the ingest process for data capture.""" self.script.log(1, "request_capture_init()") # change the state (result, message) = self.change_state("capture_init") if result != "ok": self.script.log(-1, "capture_init: change_state failed: " + message) return (result, message) return ("ok", "") @request() @return_reply(Str()) def request_capture_done(self, req): """Terminte the ingest process.""" self.script.log(1, "request_capture_done()") return self.capture_done() def capture_done(self): # in case the observing was terminated early if self._data_product["state"] == "recording": (result, message) = self.target_stop() # change the state (result, message) = self.change_state("capture_done") if result != "ok": self.script.log(-1, "capture_done: change_state failed: " + message) return (result, message) return ("ok", "") @return_reply(Str()) def request_configure(self, req, msg): """Prepare and configure for the reception of the data_product_id.""" self.script.log( 1, "request_configure: nargs= " + str(len(msg.arguments)) + " msg=" + str(msg)) if len(msg.arguments) == 0: self.script.log(-1, "request_configure: no arguments provided") return ("ok", "configured data products: TBD") # the sub-array identifier data_product_id = msg.arguments[0] if len(msg.arguments) == 1: self.script.log( 1, "request_configure: request for configuration of " + str(data_product_id)) if data_product_id == self._data_product["id"]: configuration = str(data_product_id) + " " + \ str(self._data_product['antennas']) + " " + \ str(self._data_product['n_channels']) + " " + \ str(self._data_product['cbf_source']) + " " + \ str(self._data_product['proxy_name']) self.script.log( 1, "request_configure: configuration of " + str(data_product_id) + "=" + configuration) return ("ok", configuration) else: self.script.log( -1, "request_configure: no configuration existed for " + str(data_product_id)) return ("fail", "no configuration existed for " + str(data_product_id)) if len(msg.arguments) == 5: # if the configuration for the specified data product matches extactly the # previous specification for that data product, then no action is required self.script.log(1, "configure: configuring " + str(data_product_id)) if data_product_id == self._data_product["id"] and \ self._data_product['antennas'] == msg.arguments[1] and \ self._data_product['n_channels'] == msg.arguments[2] and \ self._data_product['cbf_source'] == str(msg.arguments[3]) and \ self._data_product['proxy_name'] == str(msg.arguments[4]): response = "configuration for " + str( data_product_id) + " matched previous" self.script.log(1, "configure: " + response) return ("ok", response) # the data product requires configuration else: self.script.log( 1, "configure: new data product " + data_product_id) # TODO decide what to do regarding preconfigured params (e.g. FREQ, BW) vs CAM supplied values # determine which sub-array we are matched against the_sub_array = -1 for i in range(4): self.script.log( 1, "configure: testing self.data_product_res[" + str(i) + "].match(" + data_product_id + ")") if self.data_product_res[i].match(data_product_id): the_sub_array = i + 1 if the_sub_array == -1: self.script.log( 1, "configure: could not match subarray from " + data_product_id) return ("fail", "could not data product to sub array") antennas = msg.arguments[1] n_channels = msg.arguments[2] cbf_source = str(msg.arguments[3]) streams = json.loads(msg.arguments[3]) proxy_name = str(msg.arguments[4]) self.script.log(2, "configure: streams=" + str(streams)) # check if the number of existing + new beams > available # (cfreq, bwd, nchan1) = self.script.cfg["SUBBAND_CONFIG_0"].split(":") # (cfreq, bwd, nchan2) = self.script.cfg["SUBBAND_CONFIG_1"].split(":") # nchan = int(nchan1) + int(nchan2) #if nchan != int(n_channels): # self._data_product.pop(data_product_id, None) # response = "PTUSE configured for " + str(nchan) + " channels" # self.script.log (-1, "configure: " + response) # return ("fail", response) self._data_product['id'] = data_product_id self._data_product['antennas'] = antennas self._data_product['n_channels'] = n_channels self._data_product['cbf_source'] = cbf_source self._data_product['streams'] = str(streams) self._data_product['proxy_name'] = proxy_name self._data_product['state'] = "unconfigured" # change the state (result, message) = self.change_state("configure") if result != "ok": self.script.log( -1, "configure: change_state failed: " + message) return (result, message) # determine the CAM metadata server and update pubsub cam_server = "None" fengine_stream = "None" polh_stream = "None" polv_stream = "None" self.script.log( 2, "configure: streams.keys()=" + str(streams.keys())) self.script.log( 2, "configure: streams['cam.http'].keys()=" + str(streams['cam.http'].keys())) if 'cam.http' in streams.keys( ) and 'camdata' in streams['cam.http'].keys(): cam_server = streams['cam.http']['camdata'] self.script.log(2, "configure: cam_server=" + str(cam_server)) if 'cbf.antenna_channelised_voltage' in streams.keys(): stream_name = streams[ 'cbf.antenna_channelised_voltage'].keys()[0] fengine_stream = stream_name.split(".")[0] self.script.log( 2, "configure: fengine_stream=" + str(fengine_stream)) if 'cbf.tied_array_channelised_voltage' in streams.keys(): for s in streams[ 'cbf.tied_array_channelised_voltage'].keys(): if s.endswith('y'): polv_stream = s if s.endswith('x'): polh_stream = s self.script.log( 2, "configure: polh_stream=" + str(polh_stream) + " polv_stream=" + str(polv_stream)) if cam_server != "None" and fengine_stream != "None" and polh_stream != "None": self.script.pubsub.update_cam(cam_server, fengine_stream, polh_stream, polv_stream, antennas) else: response = "Could not extract streams[cam.http][camdata]" self.script.log(1, "configure: cam_server=" + cam_server) self.script.log( 1, "configure: fengine_stream=" + fengine_stream) self.script.log(1, "configure: polh_stream=" + polh_stream) self.script.log(-1, "configure: " + response) return ("fail", response) # restart the pubsub service self.script.log( 1, "configure: restarting pubsub for new meta-data") self.script.pubsub.restart() # determine the X and Y tied array channelised voltage streams mcasts = {} ports = {} key = 'cbf.tied_array_channelised_voltage' if key in streams.keys(): stream = 'i0.tied-array-channelised-voltage.0x' if stream in streams[key].keys(): (mcast, port) = self.parseStreamAddress(streams[key][stream]) mcasts['x'] = mcast ports['x'] = int(port) else: response = "Could not extract streams[" + key + "][" + stream + "]" self.script.log(-1, "configure: " + response) return ("fail", response) stream = 'i0.tied-array-channelised-voltage.0y' if stream in streams[key].keys(): (mcast, port) = self.parseStreamAddress(streams[key][stream]) mcasts['y'] = mcast ports['y'] = int(port) else: response = "Could not extract streams[" + key + "][" + stream + "]" self.script.log(-1, "configure: " + response) return ("fail", response) # if the backend nchan is < CAM nchan self.script.log(1, "configure: n_channels=" + str(n_channels)) if int(n_channels) == 1024 and False: nchan = 992 self.script.log( 1, "configure: reconfiguring MCAST groups from " + mcasts['x'] + ", " + mcasts['y']) (mcast_base_x, mcast_ngroups_x) = mcasts['x'].split("+") (mcast_base_y, mcast_ngroups_y) = mcasts['y'].split("+") nchan_per_group = int(n_channels) / int(mcast_ngroups_x) new_ngroups = nchan / nchan_per_group offset = (int(mcast_ngroups_x) - new_ngroups) / 2 self.script.log( 1, "configure: nchan_per_group=" + str(nchan_per_group) + " new_ngroups=" + str(new_ngroups) + " offset=" + str(offset)) parts_x = mcast_base_x.split(".") parts_y = mcast_base_y.split(".") parts_x[3] = str(int(parts_x[3]) + offset) parts_y[3] = str(int(parts_y[3]) + offset) mcasts['x'] = ".".join(parts_x) + "+" + str(new_ngroups) mcasts['y'] = ".".join(parts_y) + "+" + str(new_ngroups) self.script.log( 1, "configure: reconfigured MCAST groups to " + mcasts['x'] + ", " + mcasts['y']) self.script.log( 1, "configure: connecting to RECV instance to update configuration" ) for istream in range(int(self.script.cfg["NUM_STREAM"])): (host, beam_idx, subband) = self.script.cfg["STREAM_" + str(istream)].split(":") beam = self.script.cfg["BEAM_" + beam_idx] self.script.log( 1, "configure: istream=" + str(istream) + " beam=" + beam + " script.beam_name=" + self.script.beam_name) if beam == self.script.beam_name: # reset ADC_SYNC_TIME on the beam self.script.beam_config["lock"].acquire() self.script.beam_config["ADC_SYNC_TIME"] = "0" self.script.beam_config["lock"].release() port = int( self.script.cfg["STREAM_RECV_PORT"]) + istream self.script.log( 1, "configure: connecting to " + host + ":" + str(port)) sock = sockets.openSocket(DL, host, port, 1) if sock: req = "<?req version='1.0' encoding='ISO-8859-1'?>" req += "<recv_cmd>" req += "<command>configure</command>" req += "<params>" req += "<param key='DATA_MCAST_0'>" + mcasts[ 'x'] + "</param>" req += "<param key='DATA_PORT_0'>" + str( ports['x']) + "</param>" req += "<param key='META_MCAST_0'>" + mcasts[ 'x'] + "</param>" req += "<param key='META_PORT_0'>" + str( ports['x']) + "</param>" req += "<param key='DATA_MCAST_1'>" + mcasts[ 'y'] + "</param>" req += "<param key='DATA_PORT_1'>" + str( ports['y']) + "</param>" req += "<param key='META_MCAST_1'>" + mcasts[ 'y'] + "</param>" req += "<param key='META_PORT_1'>" + str( ports['y']) + "</param>" req += "</params>" req += "</recv_cmd>" self.script.log( 1, "configure: sending XML req [" + req + "]") sock.send(req) self.script.log( 1, "configure: send XML, receiving reply") recv_reply = sock.recv(65536) self.script.log( 1, "configure: received " + recv_reply) sock.close() else: response = "configure: could not connect to stream " + str( istream) + " at " + host + ":" + str(port) self.script.log(-1, "configure: " + response) return ("fail", response) return ("ok", "data product " + str(data_product_id) + " configured") else: response = "expected 0, 1 or 5 arguments, received " + str( len(msg.arguments)) self.script.log(-1, "configure: " + response) return ("fail", response) # parse address of from spead://AAA.BBB.CCC.DDD+NN:PORT into def parseStreamAddress(self, stream): self.script.log(2, "parseStreamAddress: parsing " + stream) (prefix, spead_address) = stream.split("//") (mcast, port) = spead_address.split(":") self.script.log(2, "parseStreamAddress: parsed " + mcast + ":" + port) return (mcast, port) @return_reply(Str()) def request_deconfigure(self, req, msg): """Deconfigure for the data_product.""" self.script.log(1, "request_deconfigure()") # in case the observing was terminated early if self._data_product["state"] == "recording": (result, message) = self.target_stop() if self._data_product["state"] == "ready": (result, message) = self.capture_done() data_product_id = self._data_product["id"] # check if the data product was previously configured if not data_product_id == self._data_product["id"]: response = str( data_product_id ) + " did not match configured data product [" + self._data_product[ "id"] + "]" self.script.log(-1, "configure: " + response) return ("fail", response) # change the state (result, message) = self.change_state("deconfigure") if result != "ok": self.script.log(-1, "deconfigure: change_state failed: " + message) return (result, message) for istream in range(int(self.script.cfg["NUM_STREAM"])): (host, beam_idx, subband) = self.script.cfg["STREAM_" + str(istream)].split(":") if self.script.beam_name == self.script.cfg["BEAM_" + beam_idx]: # reset ADC_SYNC_TIME on the beam self.script.beam_config["lock"].acquire() self.script.beam_config["ADC_SYNC_TIME"] = "0" self.script.beam_config["lock"].release() port = int(self.script.cfg["STREAM_RECV_PORT"]) + istream self.script.log( 3, "configure: connecting to " + host + ":" + str(port)) sock = sockets.openSocket(DL, host, port, 1) if sock: req = "<?req version='1.0' encoding='ISO-8859-1'?>" req += "<recv_cmd>" req += "<command>deconfigure</command>" req += "</recv_cmd>" sock.send(req) recv_reply = sock.recv(65536) sock.close() # remove the data product self._data_product["id"] = "None" response = "data product " + str(data_product_id) + " deconfigured" self.script.log(1, "configure: " + response) return ("ok", response) @request(Int()) @return_reply(Str()) def request_output_channels(self, req, nchannels): """Set the number of output channels.""" self.script.log(1, "request_output_channels: nchannels=" + str(nchannels)) if not self.test_power_of_two(nchannels): self.script.log( -1, "request_output_channels: " + str(nchannels) + " not a power of two") return ("fail", "number of channels not a power of two") if nchannels < 64 or nchannels > 4096: self.script.log( -1, "request_output_channels: " + str(nchannels) + " not within range 64 - 4096") return ("fail", "number of channels not within range 64 - 4096") self.script.beam_config["lock"].acquire() self.script.beam_config["OUTNCHAN"] = str(nchannels) self.script.beam_config["lock"].release() return ("ok", "") @request(Int()) @return_reply(Str()) def request_output_bins(self, req, nbin): """Set the number of output phase bins.""" self.script.log(1, "request_output_bins: nbin=" + str(nbin)) if not self.test_power_of_two(nbin): self.script.log( -1, "request_output_bins: " + str(nbin) + " not a power of two") return ("fail", "nbin not a power of two") if nbin < 64 or nbin > 2048: self.script.log( -1, "request_output_bins: " + str(nbin) + " not within range 64 - 2048") return ("fail", "nbin not within range 64 - 2048") self.script.beam_config["lock"].acquire() self.script.beam_config["OUTNBIN"] = str(nbin) self.script.beam_config["lock"].release() return ("ok", "") @request(Int()) @return_reply(Str()) def request_output_tsubint(self, req, tsubint): """Set the length of output sub-integrations.""" self.script.log(1, "request_output_tsubint: tsubint=" + str(tsubint)) if tsubint < 10 or tsubint > 60: self.script.log( -1, "request_output_tsubint: " + str(tsubint) + " not within range 10 - 60") return ( "fail", "length of output subints must be between 10 and 60 seconds") self.script.beam_config["lock"].acquire() self.script.beam_config["OUTTSUBINT"] = str(tsubint) self.script.beam_config["lock"].release() return ("ok", "") @request(Float()) @return_reply(Str()) def request_dispersion_measure(self, req, dm): """Set the value of dispersion measure to be removed""" self.script.log(1, "request_dispersion_measure: dm=" + str(dm)) if dm > 2000: self.script.log( -1, "request_dispersion_measure: " + str(dm) + " > 2000") return ("fail", "dm greater than limit of 2000") self.script.beam_config["lock"].acquire() self.script.beam_config["DM"] = str(dm) self.script.beam_config["lock"].release() return ("ok", "") @request(Float()) @return_reply(Str()) def request_calibration_freq(self, req, cal_freq): """Set the value of noise diode firing frequecny in Hz.""" self.script.log(1, "request_calibration_freq: cal_freq=" + str(cal_freq)) if cal_freq < 0 or cal_freq > 1000: return ("fail", "CAL freq not within range 0 - 1000") self.script.beam_config["lock"].acquire() self.script.beam_config["CALFREQ"] = str(cal_freq) if cal_freq == 0: self.script.beam_config["MODE"] = "PSR" else: self.script.beam_config["MODE"] = "CAL" self.script.beam_config["lock"].release() return ("ok", "") @request(Int()) @return_reply(Str()) def request_output_npol(self, req, outnpol): """Set the number of output pol parameters.""" self.script.log(1, "request_output_npol: outnpol=" + str(outnpol)) if outnpol != 1 and outnpol != 2 and outnpol != 3 and outnpol != 4: self.script.log( -1, "request_output_npol: " + str(outnpol) + " not 1, 2 or 4") return ("fail", "output npol must be between 1, 2 or 4") self.script.beam_config["lock"].acquire() self.script.beam_config["OUTNPOL"] = str(outnpol) self.script.beam_config["lock"].release() return ("ok", "") @request(Int()) @return_reply(Str()) def request_output_nbit(self, req, outnbit): """Set the number of bits per output sample.""" self.script.log(1, "request_output_nbit: outnbit=" + str(outnbit)) if outnbit != 1 and outnbit != 2 and outnbit != 4 and outnbit != 8: self.script.log( -1, "request_output_nbit: " + str(outnbit) + " not 1, 2, 4 or 8") return ("fail", "output nbit must be between 1, 2, 4 or 8") self.script.beam_config["lock"].acquire() self.script.beam_config["OUTNBIT"] = str(outnbit) self.script.beam_config["lock"].release() return ("ok", "") @request(Int()) @return_reply(Str()) def request_output_tdec(self, req, outtdec): """Set the number of input samples integrated into 1 output sample.""" self.script.log(1, "request_output_tdec: outtdec=" + str(outtdec)) if outtdec < 16 or outtdec > 131072: self.script.log( -1, "request_output_tdec: " + str(outtdec) + " not in range [16..131072]") return ("fail", "output tdec must be between 16 and 131072") self.script.beam_config["lock"].acquire() self.script.beam_config["OUTTDEC"] = str(outtdec) self.script.beam_config["lock"].release() return ("ok", "") @request() @return_reply(Str()) def request_fold_mode(self, req): """Set the processing mode to produce folded archives.""" self.script.beam_config["lock"].acquire() self.script.beam_config["PERFORM_FOLD"] = "1" self.script.beam_config["PERFORM_SEARCH"] = "0" self.script.log(1, "request_search_mode: PERFORM_FOLD=1") self.script.beam_config["lock"].release() return ("ok", "") @request() @return_reply(Str()) def request_search_mode(self, req): """Set the processing mode to produce filterbank data.""" self.script.beam_config["lock"].acquire() self.script.beam_config["PERFORM_FOLD"] = "0" self.script.beam_config["PERFORM_SEARCH"] = "1" self.script.log(1, "request_search_mode: PERFORM_SEARCH=1") self.script.beam_config["lock"].release() return ("ok", "") @request() @return_reply(Str()) def request_disable_zeroed_buffers(self, req): """Disable zeroing of ring buffers, enabling stats mode.""" self.script.beam_config["lock"].acquire() self.script.log(1, "request_disable_zeroed_buffers: ZERO_COPY=0") self.script.beam_config["ZERO_COPY"] = "0" self.script.beam_config["lock"].release() return ("ok", "") @request() @return_reply(Str()) def request_enable_zeroed_buffers(self, req): """Enable zeroing of ring buffers, disabling stats mode.""" self.script.beam_config["lock"].acquire() self.script.log(1, "request_enable_zeroed_buffers: ZERO_COPY=1") self.script.beam_config["ZERO_COPY"] = "1" self.script.beam_config["lock"].release() return ("ok", "") # test if a number is a power of two def test_power_of_two(self, num): return num > 0 and not (num & (num - 1)) # test whether the specified target exists in the pulsar catalog def test_pulsar_valid(self, target): self.script.log(2, "test_pulsar_valid: target='[" + target + "]") # remove the _R suffix if target.endswith('_R'): target = target[:-2] # check if the target matches the fluxcal.on file cmd = "grep " + target + " " + self.script.cfg[ "CONFIG_DIR"] + "/fluxcal.on | wc -l" rval, lines = self.script.system(cmd, 3) if rval == 0 and len(lines) == 1 and int(lines[0]) > 0: return ("ok", "") # check if the target matches the fluxcal.off file cmd = "grep " + target + " " + self.script.cfg[ "CONFIG_DIR"] + "/fluxcal.off | wc -l" rval, lines = self.script.system(cmd, 3) if rval == 0 and len(lines) == 1 and int(lines[0]) > 0: return ("ok", "") self.script.log( 2, "test_pulsar_valid: get_psrcat_param (" + target + ", jname)") (reply, message) = self.get_psrcat_param(target, "jname") if reply != "ok": return (reply, message) self.script.log( 2, "test_pulsar_valid: get_psrcat_param () reply=" + reply + " message=" + message) if message == target: return ("ok", "") else: return ("fail", "pulsar " + target + " did not exist in catalog") def get_psrcat_param(self, target, param): # remove the _R suffix if target.endswith('_R'): target = target[:-2] cmd = "psrcat -all " + target + " -c " + param + " -nohead -o short" rval, lines = self.script.system(cmd, 3) if rval != 0 or len(lines) <= 0: return ("fail", "could not use psrcat") if lines[0].startswith("WARNING"): return ("fail", "pulsar " + target_name + " did not exist in catalog") parts = lines[0].split() if len(parts) == 2 and parts[0] == "1": return ("ok", parts[1])