def update_my_record(): '''Updates the local cerebrate's record (mostly IP address, but also sets defaults for any attributes without values). ''' my_record = get_cerebrate_record( record_attribute=Record.MAC, attribute_value=mysysteminfo.get_mac_address()) or default_dictionary( ) my_record[Record.NAME] = my_record.get(Record.NAME, MY_RECORD_DEFAULT_NAME) my_record[Record.MAC] = my_record.get(Record.MAC, mysysteminfo.get_mac_address()) my_record[Record.IP] = mysysteminfo.get_ip_address() my_record[Record.LASTCONTACT] = datetime.datetime.now() my_record[Record.STATUS] = Status.AWAKE my_record[Record.ROLE] = my_record.get(Record.ROLE, Role.DRONE) update_cerebrate_record(cerebrate_record=my_record)
async def update_resources(msg): '''Message header must contain str(Resource.SECTION):section. Message data must contain a dict of resources. Updates the local resources with the given ones. ''' dprint("updating resources from ", msg.sender_mac) propagate = ( cerebratesinfo.get_overmind_mac() == mysysteminfo.get_mac_address()) section = None for header in msg.header: if str(Resource.SECTION) in header: split_header = header.split(':') if len(split_header) <= 1: continue section = split_header[len(split_header) - 1] if section: break if not section: return cc.CLOSE_CONNECTION, "no section supplied in header" for resources in msg.data: #if Overmind receives new information then propagate it to other cerebrates if resource_handler.update_resources( section=section, resources=resources) and propagate: communication.Secretary.broadcast_message( msg=communication.Message("update_resources", ':'.join((str(Resource.SECTION), section)), data=[resources])) return cc.CLOSE_CONNECTION, "resources updated"
async def send_message(msg): data = msg.data recipients = [] for cerebrate in cerebratesinfo.get_cerebrate_records(): name = cerebrate.get(cerebratesinfo.Record.NAME, "") if name in data: mac = cerebrate.get(cerebratesinfo.Record.MAC, "") if not mac in recipients: recipients.append(mac) communication.distill_msg(msg, name) for cerebrate in cerebratesinfo.get_cerebrate_records(): location = cerebrate.get(cerebratesinfo.Record.LOCATION, "") if location in data: mac = cerebrate.get(cerebratesinfo.Record.MAC, "") if not mac in recipients: recipients.append(mac) communication.distill_msg(msg, location) if recipients.__len__() <= 0: recipients = cerebratesinfo.get_cerebrate_macs() recipients.remove(mysysteminfo.get_mac_address()) for recipient in recipients: await communication.Secretary.communicate_message( cerebrate_mac=recipient, msg=communication.Message('display_message', data=msg.data)) # speak msg #aprint("Message communicated") return True
async def acknowledge(msg): '''Initiates file and record updating between cerebrates. When a cerebrate comes online it broadcasts to other cerebrates asking for acknowledgment. Only the Overmind responds. ''' #Only respond if local is the Overmind if cerebratesinfo.get_overmind_mac() != mysysteminfo.get_mac_address(): return cc.CLOSE_CONNECTION, cc.FINISHED dprint("acknowledging") #Send a current list of records await communication.Secretary.communicate_message( cerebrate_mac=msg.sender_mac, msg=communication.Message( "update_records", cc.RECIPROCATE, cc.OVERRULE, data=cerebratesinfo.get_cerebrate_records_list())) #Send a current list of resources await _send_all_resources(cerebrate_mac=msg.sender_mac) #Get a current list of their resources await communication.Secretary.communicate_message( cerebrate_mac=msg.sender_mac, msg=communication.Message("send_resources")) #Ensure up-to-date files if msg.data.get("version", None): return await check_version(msg=msg) return cc.CLOSE_CONNECTION, cc.SUCCESS
def get_overmind_record(): with shelve.open(CEREBRATE_RECORDS, flag='r') as db: return next((db[key] for key in db if db[key].get(Record.ROLE, Role.DRONE) == Role.OVERMIND), get_cerebrate_record( record_attribute=Record.MAC, attribute_value=mysysteminfo.get_mac_address()))
def update_resources(section: str, resources: dict): '''Updates resources if the given resources are more recent. Returns a dict of the updated resources. ''' # NOT FULLY CORRECT: If local copy of resource has had more recent changes it will not accept the given resource even if given contains changes local has not seen updated_resources = {} try: with shelve.open(filename=_get_file_location(section=section), flag='c', writeback=True) as s: for key, value in resources.items(): if key not in s.keys(): s[key] = value updated_resources[key] = value elif value.get(MODIFIED_TIME, datetime.datetime( 1, 1, 1)) > s[key].get(MODIFIED_TIME, datetime.datetime(1, 1, 1)): try: #if resource is derived from Resource_BC s[key][RESOURCE_VALUE].update(value[RESOURCE_VALUE]) s[key][MODIFIED_TIME] = value[MODIFIED_TIME] except: #resource is not derived from Resource_BC s[key] = value updated_resources[key] = s[key] except: raise if len(updated_resources) > 0 and get_mac_address( ) == cerebratesinfo.get_overmind_mac(): propagate_resources(section=section, timestamped_resources=updated_resources) return updated_resources
def say_hello(): #Assume everyone is asleep for mac in cerebratesinfo.get_cerebrate_macs(): cerebratesinfo.update_cerebrate_attribute( mac=mac, record_attribute=cerebratesinfo.Record.STATUS, attribute_value=cerebratesinfo.Status.ASLEEP) #Set self status to awake cerebratesinfo.update_cerebrate_attribute( mac=mysysteminfo.get_mac_address(), record_attribute=cerebratesinfo.Record.STATUS, attribute_value=cerebratesinfo.Status.AWAKE) #Assume we are the only cerebrate up, temporarily designate ourself as Overmind cerebratesinfo.designate_overmind(mac=mysysteminfo.get_mac_address()) #Contact existing Overmind, if there is one communication.Secretary.broadcast_message(msg=communication.Message( 'acknowledge', data={"version": cc.my_version}))
def datagram_received(self, data, addr): msg = pickle.loads(data) if Secretary.terminating: return if msg.sender_mac == mysysteminfo.get_mac_address(): return try: asyncio.ensure_future(handle_message(msg=msg)) except Exception as _: traceback.print_exc()
def propagate_resources(section: str, timestamped_resources: dict): overmind_mac = cerebratesinfo.get_overmind_mac() msg = communication.Message("update_resources", ':'.join((str(Resource.SECTION), section)), data=[timestamped_resources]) if get_mac_address() == overmind_mac: run_coroutine(communication.Secretary.broadcast_message(msg=msg)) else: run_coroutine( communication.Secretary.communicate_message( cerebrate_mac=overmind_mac, msg=msg))
async def say_goodbye(): #Set someone else as Overmind successor_mac = await designate_successor() #Tell Overmind that we are going to sleep # If no other cerebrate is online we end up sending this to ourselves, which is ignored cerebratesinfo.update_cerebrate_attribute( mac=mysysteminfo.get_mac_address(), record_attribute=cerebratesinfo.Record.STATUS, attribute_value=cerebratesinfo.Status.ASLEEP) cerebratesinfo.update_cerebrate_contact_time( mac=mysysteminfo.get_mac_address()) await communication.Secretary.communicate_message( cerebrate_mac=successor_mac, msg=communication.Message( "update_records", data=[ cerebratesinfo.get_cerebrate_record( record_attribute=cerebratesinfo.Record.MAC, attribute_value=mysysteminfo.get_mac_address()) ]))
async def designate_successor(): '''Establishes a new Overmind among the remaining (awake) cerebrates. Returns the new Overmind's mac address. ''' successor_mac = cerebratesinfo.get_overmind_mac() if mysysteminfo.get_mac_address() == successor_mac: for record in cerebratesinfo.get_cerebrate_records(): if record.get(cerebratesinfo.Record.MAC ) == mysysteminfo.get_mac_address(): continue if record.get(cerebratesinfo.Record.STATUS, cerebratesinfo.Status. UNKNOWN) == cerebratesinfo.Status.AWAKE: if cerebratesinfo.designate_overmind( mac=record.get(cerebratesinfo.Record.MAC, None)): received = await communication.Secretary.communicate_message( cerebrate_mac=record.get(cerebratesinfo.Record.MAC, None), msg=communication.Message("assume_overmind", data=None)) if received == cc.SUCCESS: successor_mac = record.get(cerebratesinfo.Record.MAC) break return successor_mac
async def _designate_overmind(mac): if cerebratesinfo.get_overmind_mac() == mac: return dprint("designating ", mac) cerebratesinfo.designate_overmind(mac=mac) if mac != mysysteminfo.get_mac_address(): '''shouldn't need to ask for acknowledgment, cerebrates are already up to date''' #dprint("asking for acknowledgment") #await communication.Secretary.communicate_message(cerebrate_mac=mac, msg=communication.Message("acknowledge", data={"version": cc.my_version})) else: communication.Secretary.broadcast_message(msg=communication.Message( "update_records", cc.OVERRULE, data=[cerebratesinfo.get_overmind_record()]))
def store_resources(section: str, resources: dict): '''Saves resources for later reference, overwriting existing records with the same keys. Returns False if unsuccessful. ''' try: if len(resources) <= 0: return True timestamped_resources = _update_resources_modified_time(resources) update_resources(section=section, resources=resources) if get_mac_address() != cerebratesinfo.get_overmind_mac(): propagate_resources(section=section, timestamped_resources=timestamped_resources) except Exception: traceback.print_exc() return False return True
async def run_command(msg): '''Given a Message, runs the contained command if possible Returns an action, data pair. ''' if cc.my_state_event[cc.State.TERMINATING].is_set(): return cc.CLOSE_CONNECTION, "cerebrate terminating" formatted_header = command.format_header(header=msg.header) cerebratesinfo.update_cerebrate_contact_time( mac=mysysteminfo.get_mac_address()) for key, _ in commands.items(): if key in formatted_header: dprint("before: ", cerebratesinfo.get_overmind_mac()) result = await commands[key][command.Command.FUNCTION](msg) dprint("after: ", cerebratesinfo.get_overmind_mac()) return result return cc.CLOSE_CONNECTION, "command not recognized"
async def __communicate(reader, writer): ''' Communicates with the cerebrate on the other end of reader, writer pair. Returns the reason (as string) for the end of communication. ''' while not Secretary.terminating: msg = await Secretary._read_message(reader=reader) #print("Received: ", msg.header) if msg.sender_mac == mysysteminfo.get_mac_address(): return "schizophrenia" action, message = await handle_message(msg=msg) if action == cc.CLOSE_CONNECTION: return message elif action == cc.FILE_TRANSFER: return await Secretary.__receive_files(reader=reader, writer=writer) else: await Secretary._write_message(writer=writer, msg=message) return "secretary terminating"
async def __initiate_connection(cerebrate_mac): '''Tries to open a connection with given cerebrate. Throws an asyncio.TimeoutError if no connection is made. Returns a reader, writer pair. Both will be None if no connection is made. ''' if cerebrate_mac == mysysteminfo.get_mac_address(): return None, None global event_loop cerebrate_ip = cerebratesinfo.get_cerebrate_attribute(cerebrate_mac=cerebrate_mac, record_attribute=cerebratesinfo.Record.IP) reader, writer = None, None try: fut = asyncio.open_connection(host=cerebrate_ip, port=TCP_PORT, loop=event_loop) reader, writer = await asyncio.wait_for(fut, timeout=3) #Secretary._made_contact(mac=cerebrate_mac, ip=cerebrate_ip) except asyncio.TimeoutError: print("Failed to connect to ", cerebrate_ip) raise asyncio.TimeoutError() return reader, writer
async def update_records(msg): '''Message data must contain a list of cerebrate records. Updates the local cerebrate records with the given ones, if the given ones contain more recent information. If CC.RECIPROCATE is in the header a Message containing the local copy of updated cerebrate records will be returned. ''' if cc.OVERRULE in msg.header: dprint("Being overruled") await _designate_overmind(mac=msg.sender_mac) dprint("updating records from ", msg.sender_mac) propagate = ( cerebratesinfo.get_overmind_mac() == mysysteminfo.get_mac_address()) for record in msg.data: #if Overmind receives new information then propagate it to other cerebrates if cerebratesinfo.update_cerebrate_record( cerebrate_record=record) and propagate: communication.Secretary.broadcast_message( msg=communication.Message("update_records", data=[record])) if cc.RECIPROCATE in msg.header: return cc.REMOTE_COMMAND, communication.Message( "update_records", data=cerebratesinfo.get_cerebrate_records_list()) return cc.CLOSE_CONNECTION, "records updated"
async def change(msg): attribute_recognized = False while True: cmd = command.format_command(msg.data) if "_mic" in cmd: attribute_recognized = True audio.list_microphones() input = prompt(prompt_string="\nMicrophone index: ") index = int(re.search(r'\d+', input).group()) if index: if audio.setup_microphone(mic_index=index): print("Microphone ", index, " is active") else: print("Could not set microphone ", index) else: print("index not recognized") record = cerebratesinfo.get_cerebrate_record( record_attribute=cerebratesinfo.Record.MAC, attribute_value=mysysteminfo.get_mac_address()) if record is None: aprint("My record has been misplaced") return False if "_name_" in cmd: attribute_recognized = True current_name = record.get(cerebratesinfo.Record.NAME, "unknown") aprint("Current name: ", current_name) record[cerebratesinfo.Record.NAME] = await prompt( prompt_string="New name") cerebratesinfo.update_cerebrate_record(cerebrate_record=record) aprint(current_name, " => ", record.get(cerebratesinfo.Record.NAME, "unknown")) if "_location_" in cmd: attribute_recognized = True current_location = record.get(cerebratesinfo.Record.LOCATION, "unknown") aprint("Current location: ", current_location) record[cerebratesinfo.Record.LOCATION] = await prompt( prompt_string="New location") cerebratesinfo.update_cerebrate_record(cerebrate_record=record) aprint(current_location, " => ", record.get(cerebratesinfo.Record.LOCATION, "unknown")) ''' if "_role_" in cmd: attribute_recognized = True current_role = record.get(cerebratesinfo.Record.ROLE, "unknown") aprint("Current Role: ", current_role) for role in cerebratesinfo.Role: aprint(role.value, ") ", role.name) number = int(await prompt("New role (number)")) for role in cerebratesinfo.Role: if role.value == number: cerebratesinfo.update_cerebrate_attribute(mysysteminfo.get_mac_address(), cerebratesinfo.Record.ROLE, role) aprint(current_role.name, " => ", role.name) break ''' response = None if not attribute_recognized: return False #response = ''.join(("_", await prompt(prompt_string="What do you want changed? "), "_")) else: response = ''.join( ("_", await prompt(prompt_string="Any other changes? "), "_")) if "_nothing_" in response or "_no_" in response: break msg.data = response aprint("") #broadcast the changes we made to other cerebrates cerebratesinfo.update_cerebrate_contact_time( mac=mysysteminfo.get_mac_address()) msg = communication.Message( "update_records", data=[ cerebratesinfo.get_cerebrate_record( record_attribute=cerebratesinfo.Record.MAC, attribute_value=mysysteminfo.get_mac_address()) ]) if cerebratesinfo.get_overmind_mac() == mysysteminfo.get_mac_address(): communication.Secretary.broadcast_message(msg=msg) else: await communication.Secretary.communicate_message( cerebrate_mac=cerebratesinfo.get_overmind_mac(), msg=msg) return True
def __init__(self, *headers, data:list=None): self.sender_mac = mysysteminfo.get_mac_address() self.sender_ip = mysysteminfo.get_ip_address() self.header = [header for header in headers if header] self.data = data
async def assume_overmind(msg): '''Sets self as Overmind, broadcasts the change to others. ''' dprint("Assuming Overmind") await _designate_overmind(mac=mysysteminfo.get_mac_address()) return cc.CLOSE_CONNECTION, cc.SUCCESS