From ce32292935127e098934cb2286335aaa4fa8aa8a Mon Sep 17 00:00:00 2001 From: alban Date: Fri, 3 Dec 2021 22:35:55 +0100 Subject: [PATCH] fix: missing files in first version --- .gitignore | 2 - lib/audio_plugin.py | 37 +++++++++ lib/blackboard.py | 5 ++ lib/debuggable.py | 14 ++++ lib/debugger.py | 176 +++++++++++++++++++++++++++++++++++++++++ lib/director.py | 73 +++++++++++++++++ lib/my_plugin.py | 121 ++++++++++++++++++++++++++++ lib/plan.py | 5 ++ lib/planner_content.py | 65 +++++++++++++++ lib/schedule.py | 33 ++++++++ lib/test.py | 50 ++++++++++++ 11 files changed, 579 insertions(+), 2 deletions(-) create mode 100644 lib/audio_plugin.py create mode 100644 lib/blackboard.py create mode 100644 lib/debuggable.py create mode 100644 lib/debugger.py create mode 100644 lib/director.py create mode 100644 lib/my_plugin.py create mode 100644 lib/plan.py create mode 100644 lib/planner_content.py create mode 100644 lib/schedule.py create mode 100644 lib/test.py diff --git a/.gitignore b/.gitignore index 793163d..cbebcb7 100644 --- a/.gitignore +++ b/.gitignore @@ -14,8 +14,6 @@ dist/ downloads/ eggs/ .eggs/ -lib/ -lib64/ parts/ sdist/ var/ diff --git a/lib/audio_plugin.py b/lib/audio_plugin.py new file mode 100644 index 0000000..65a1bb1 --- /dev/null +++ b/lib/audio_plugin.py @@ -0,0 +1,37 @@ +from debuggable import Debuggable +from random import randint, choice +from math import sqrt +from datetime import datetime, timedelta +from planning import Planning +from schedule import Schedule + + +class audioPlugin(Debuggable): + """Planner for general slots allocations""" + + name = "audioPlugin" + + def __init__(self): + super(MyPlugin, self).__init__() + + def sensor_base(self, blackboard): + # redis.get("redilysis.key") + # blackboard["audio_analysis"] = redilysis + pass + + def planner_base(self, blackboard, planning: Planning): + + interesting_list = [] + for schedule in planning.schedules: + start_at = schedule.start_at + now = datetime.now() + if (now - start_at).total_seconds() < 3 : + continue + interesting_list.append(schedule) + + mood = get_mood_from_audio() + for schedule in interesting_list: + schedule.content["mood"] = mood + + + diff --git a/lib/blackboard.py b/lib/blackboard.py new file mode 100644 index 0000000..089b53f --- /dev/null +++ b/lib/blackboard.py @@ -0,0 +1,5 @@ + +class Blackboard(object): + + def __init__( self ): + pass \ No newline at end of file diff --git a/lib/debuggable.py b/lib/debuggable.py new file mode 100644 index 0000000..b320a3c --- /dev/null +++ b/lib/debuggable.py @@ -0,0 +1,14 @@ +import debugger +# from var_dump import var_dump +class Debuggable(object): + + def __init__(self): + self._debugger = debugger.instance + + def debug(self, tags, msg): + if not self._debugger: + print(self, " missing debugger") + self._debugger.debug(tags, msg) + + # def var_dump(self,var): + # return var_dump(var) \ No newline at end of file diff --git a/lib/debugger.py b/lib/debugger.py new file mode 100644 index 0000000..a6f49da --- /dev/null +++ b/lib/debugger.py @@ -0,0 +1,176 @@ +import asyncio +import inspect +import os +import re +import time + +instance = object() + +def init(): + global instance + instance = Debugger() + + +class Debugger(object): + """A socket based debugger """ + + """ a list of tags to debug """ + tags = {} + knownTags = [] + selectedTags = [] + + # subscribed = {} + + def __init__(self): + self.socket_file = '/tmp/._my_debug_socket' + print('\nStarting the debugger on socket file \'{}\'.\nRequest help with \n echo help | nc -U {}\'\n '.format( + self.socket_file, + self.socket_file + )) + + def cleanup(self): + if os.path.exists(self.socket_file): + os.remove(self.socket_file) + + + def add_known_tags(self, tags): + if isinstance(tags, str): + tags = [tags] + self.tags = set(list(self.tags) + tags) + + def add_selected_tags(self, tags): + if isinstance(tags, str): + tags = [tags] + self.selectedTags = set(list(self.selectedTags)).union(set(tags)) + + def debug(self, tags, message): + if isinstance(tags, str): + tags = [tags] + self.add_known_tags(tags) + if len(set(tags) & set(self.selectedTags)): + print(message) + # else : + # print( "skipping message %s vs. %s" % (self.selectedTags, tags )) + + async def server(self): + + async def handler(reader, writer): + data = await reader.read(100) + if data: + msg = "" + req = data.split() + if not len(req): + msg = "Error: empty request" + else: + cmd = "cmd_" + str(req.pop(0), "utf-8") + print("Debugger server command: %s" % (cmd)) + if not hasattr(self, cmd): + msg = "Error: Invalid command '%s'" % (cmd) + else: + method = getattr(self, cmd) + if not callable(method): + msg = "Error: Called a property" + else: + try: + req = [str(i, 'utf-8') for i in req] + msg = method(req) + except Exception as e: + msg = "Error : %s " % (e) + print("Response:\n%s" % (msg)) + writer.write(bytes(msg, "utf-8")) + await writer.drain() + writer.close() + + self.cleanup() + return await asyncio.start_unix_server(handler, path=self.socket_file) + + def cmd_help(self, params): + """ + request general help + """ + members = inspect.getmembers(self) + out = [] + for name, obj in members: + if not re.match("^cmd_", name): + continue + out.append( + "- " + re.sub("^cmd_(.*)", lambda m: m.group(1), name) + (str(obj.__doc__) if obj.__doc__ else "")) + + return "\n".join(out) + "\n" + + def cmd_list_tags(self, params): + """ + List available debug tags + """ + msg = "" + if not len(self.tags): + msg += "No tags yet\n" + else: + msg += "Tags:" + for flag in sorted(self.tags): + msg += "\n - %s" % (flag) + msg += "\n" + return msg + + def cmd_add_tags(self, params): + """ + Add a debug tag + """ + msg = "" + if not len(params): + msg = "No flag provided" + else: + for flag in params: + if flag in self.selectedTags: + msg += "Noop: flag '%s' already added" % (flag) + else: + self.add_selected_tags([flag]) + msg += "Added flag '%s'" % (flag) + return msg + + def cmd_del_tags(self, params): + """ + Removes a tag + """ + msg = "" + if not len(params): + msg = "No flag provided" + else: + for flag in params: + if flag not in self.selectedTags: + msg += "Noop: flag '%s' not added" % (flag) + else: + self.selectedTags.remove(flag) + msg += "Removed flag '%s'" % (flag) + return msg + + # def cmd_list_objs(self, params): + # """ + # List available objects + # """ + # if not len(self.subscribed ): + # msg += "No subscribed object yet\n" + # else: + # msg += "Subscribed objects:" + # for i, (oid, obj) in enumerate(self.subscribed.items()) : + # msg+="\n - %s\t%s" % (oid,obj) + # + # return "\n".msg + # + # + # def cmd_add_objs(self, params): + # """ + # Debug a precise object + # """ + # oid = params[0] + # msg = "Unknown error" + # if not self.subscribed[oid]: + # + # msg = "No such object" + # else : + # obj = self.subscribed[oid] + # msg = obj.debugSelf() + # return msg + + # def subscribe( self, oid, obj ): + # self.subscribed[ oid ] = obj diff --git a/lib/director.py b/lib/director.py new file mode 100644 index 0000000..778d4bd --- /dev/null +++ b/lib/director.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +# -*- mode: Python -*- + + +import asyncio +import inspect +import re + +from debuggable import Debuggable +from plan import Plan +from blackboard import Blackboard +from schedule import Schedule +from planning import Planning + + +class Director(Debuggable): + + def __init__(self, debug_tags=[]): + super(Director, self).__init__() + if len(debug_tags): + self._debugger.add_selected_tags(debug_tags) + + self.plugins = {} + self.sensors = [] + self.planners = [] + self.communicators = [] + self.blackboard = {} # Blackboard() + self.planning = Planning() + + async def tick(self): + self.on_perceive() + self.on_plan() + self.on_communicate() + await asyncio.sleep(0.1) + + def on_perceive(self): + """Sensors are required to update the blackboard + """ + # Pass the World State to all sensors + for obj, meth in self.sensors: + getattr(self.plugins[obj], meth)(blackboard=self.blackboard) + + def on_plan(self): + """Planifiers are called + """ + for obj, meth in self.planners: + getattr(self.plugins[obj], meth)(blackboard=self.blackboard, planning=self.planning) + + def on_communicate(self): + """Communicators are called + """ + for obj, meth in self.communicators: + getattr(self.plugins[obj], meth)(planning=self.planning) + + async def run(self): + while 1: + await self.tick() + + def add_plugin(self, plugin): + if not hasattr(plugin, "name"): + raise Exception("No property 'name' in plugin '{}'".format(plugin)) + plugin_name = plugin.name + self.plugins[plugin_name] = plugin + for method_name, prop in inspect.getmembers(plugin): + if re.match("^(sensor.*)", method_name): + self.sensors.append((plugin_name, method_name)) + self.debug("config", "Added sensor {}::{}".format(plugin_name, method_name)) + elif re.match("^(planner.*)", method_name): + self.planners.append((plugin_name, method_name)) + self.debug("config", "Added planner {}::{}".format(plugin_name, method_name)) + elif re.match("^(communicator.*)", method_name): + self.communicators.append((plugin_name, method_name)) + self.debug("config", "Added communicator {}::{}".format(plugin_name, method_name)) diff --git a/lib/my_plugin.py b/lib/my_plugin.py new file mode 100644 index 0000000..ac4cc97 --- /dev/null +++ b/lib/my_plugin.py @@ -0,0 +1,121 @@ +from debuggable import Debuggable +from random import randint, choice +from math import sqrt +from datetime import datetime, timedelta +from planning import Planning +from schedule import Schedule + +def gen(N, n): + """ returns a list of integer numbers between 1 and n with exhaustion level N """ + res = [] + i = 0 + while N > 1: + r = randint(1, n) + N /= r + res.append(r) + i += 1 + return res + + +def abba(lst, item): + return [item, *lst, item] + + +def abab(lst, item): + return [item, *lst, item, *lst] + + +def baa(lst, item): + return [*lst, item, item] + + +def ab(lst, item): + return [item, *lst] + + +def ba(lst, item): + return [*lst, item] + + +def struct(lst): + fnList = ['abba', 'abab', 'baa', 'ab', 'ba'] + ret = [] + for i, w in enumerate(lst): + fn_name = choice(fnList) + fn = globals().copy().get(fn_name) + ret = fn(ret, {'id': i, 'weight': w}) + return ret + + +class MyPlugin(Debuggable): + """Planner for general slots allocations""" + + name = "myPlugin" + + def __init__(self): + super(MyPlugin, self).__init__() + self.updated = False + + def sensor_base(self, blackboard): + pass + + def communicator_base(self, planning): + if self.updated: + self.updated = False + self.debug("my_plugin_schedules", "Rescheduling at {}.".format(datetime.now().isoformat())) + for schedule in planning.schedules: + self.debug("my_plugin_schedules", schedule.to_string()) + + def planner_base(self, blackboard, planning: Planning): + """ + duration = 5 + L = [4, 2, 2] + C = [{2: 3}, {1: 2}, {0: 4}, {0: 4}, {2: 3}] + where each item of C is an {pattern_id: weight} + duration of a stitch = 1 / sqrt(weight) + R = [ 0.57, 0.7, 0.5, 0.5, 0.57] + Sum of duration weights = 2.84 + each stitch lasts for a fraction of the total length + comp = [{2: [3, 1.0]}, {1: [2, 1.23]}, {0: [4, 0.88]}, {0: [4, 0.88]}, {2: [3, 1.0]}] + + """ + last_schedule: Schedule = planning.get_last_schedule() + + if last_schedule: + self.debug("my_plugin_last_schedule","last_schedule end at {}".format(last_schedule.end_at.isoformat())) + start_at_base = last_schedule.end_at + if start_at_base > datetime.now(): + self.debug("my_plugin_skip","skip") + return + else: + start_at_base = datetime.now() + + duration = 10 + L = gen(randint(12, 20), randint(3, 5)) + C = struct(L) + R = [] + Rsum = 0 + Ret = [] + for i in C: + R.append(1 / sqrt(i['weight'])) + Rsum = sum(R) + for i, j in enumerate(C): + C[i]['duration'] = R[i] * duration / Rsum + + # [{'id': 1, 'weight': 4, 'duration': 0.11670090144936807}, {'id': 0, 'weight': 1, 'duration': 0.23340180289873613}, {'id': 0, 'weight': 1, 'duration': 0.23340180289873613}, {'id': 1, 'weight': 4, 'duration': 0.11670090144936807}, {'id': 2, 'weight': 2, 'duration': 0.1650399975708623}, {'id': 3, 'weight': 3, 'duration': 0.1347545937329293}] + + schedules = [] + elapsed: float = 0 + for item in C: + duration = item["duration"] + start_at = start_at_base + timedelta( seconds=elapsed) + schedules.append(Schedule( + start_at=start_at, + end_at=start_at + timedelta( seconds=duration), + name=item['id'], + content=item + )) + elapsed += duration + + planning.add(schedules) + self.updated = True diff --git a/lib/plan.py b/lib/plan.py new file mode 100644 index 0000000..d96b5aa --- /dev/null +++ b/lib/plan.py @@ -0,0 +1,5 @@ + +class Plan( object ): + + def __init__( self ): + pass \ No newline at end of file diff --git a/lib/planner_content.py b/lib/planner_content.py new file mode 100644 index 0000000..5c1b8fc --- /dev/null +++ b/lib/planner_content.py @@ -0,0 +1,65 @@ +from debuggable import Debuggable + + +class Pattern(Debuggable): + def __init__(self, arg): + super(Pattern, self).__init__() + for i in arg: + setattr(self, i, arg[i]) + +class ContentPlanner(Debuggable): + """Planner for slots content allocation """ + + # def __init__(self): + + # the patterns memory + # patternsList = [] + + # define the possible schedule contents + + # min_weight + # max_weight + # score_first + # score_same + # score_different + # score_final + + + + def plan(self,state, memory, plan): + + # Read previous actions from memory + actionsMemory = memory.getActionsMemory() + finished = False + energy = state.energy() + + finished = False + while not finished and energy > 0: + + actionsWeightSum = sum(actionsMemory.values()) if actionsMemory else 1 + print(__file__,actionsMemory, actionsWeightSum, self.weightTable) + + for action in self.actions: + if energy + action.energy < 0 : + continue + action_weight = ( (actionsWeightSum - actionsMemory[action.name] ) / actionsWeightSum ) if action.name in actionsMemory else 1 + print(__file__, action.name, actionsMemory,action.name in actionsMemory,action_weight) + self.weightTable[action.name] = action_weight + print( self.weightTable) + + high_score = max(self.weightTable, key=self.weightTable.get) + chosen_action = self.actions[self.actionsIndex[high_score]] + + energy += chosen_action.energy + plan.addAction( chosen_action ) + print ( energy, chosen_action.name ) + + if action.name in actionsMemory: + actionsMemory[chosen_action.name] += 1 + else : + actionsMemory[chosen_action.name] = 1 + if energy < 0: + finished = True + + + return plan \ No newline at end of file diff --git a/lib/schedule.py b/lib/schedule.py new file mode 100644 index 0000000..d1303fb --- /dev/null +++ b/lib/schedule.py @@ -0,0 +1,33 @@ +from debuggable import Debuggable +from datetime import datetime, timedelta +import json + +class Schedule(Debuggable): + start_at = datetime.now() + end_at = datetime.now() + content = {} + name = "default" + + def __init__(self, start_at=None, end_at=None, content={}, name=None): + super(Schedule, self).__init__() + self.start_at = start_at + self.end_at = end_at + self.content = content + self.name = name + self.debug("new_schedules","New schedule: {}".format(self.to_string())) + + def elapsed(self): + delta = datetime.now() - self.end_at + if delta.total_seconds() > 0 : + self.debug("schedule_elapsed_true","delta is {} for {}".format(delta, self.end_at.isoformat())) + return True + return False + + def to_string(self): + return json.dumps({ + "start_at": self.start_at.isoformat(), + "end_at": self.end_at.isoformat(), + "name": self.name, + "content": self.content + }) + diff --git a/lib/test.py b/lib/test.py new file mode 100644 index 0000000..a304e8a --- /dev/null +++ b/lib/test.py @@ -0,0 +1,50 @@ + +def gen( N, n ): + res = [] + i = 0 + while N > 1: + r = randint(1,n) + N /= r + res.append(r) + i += 1 + return res + +def abba( lst, item ): + return [ item, *lst, item] + +def abab( lst, item ): + return [ item, *lst, item, *lst] + +def baa( lst, item ): + return [ *lst, item, item ] + +def ab( lst, item ): + return [ item, *lst ] + +def ba( lst, item ): + return [ *lst, item ] + +def struct( lst ): + fnList = ['abba','abab','baa','ab','ba'] + ret = [] + for i,v in enumerate(lst): + fn_name = choice(fnList) + fn = globals().copy().get(fn_name) + ret = fn(ret,{i:v}) + return ret + + +L = gen(randint(12,20),randint(3,5)) +C = struct(L) +R = [] +Rsum = 0 +Ret = [] +for i in C : + R.append( 1/ sqrt(sum(i.values()) )) +Rsum = sum(R) +for i,j in enumerate(C) : + length = R[i] * duration / Rsum + print(i,j,length) + for id in j.keys(): + print(id) + Ret.append( { id : [j[id],length]} )