BS 0.1.0
This commit is contained in:
@@ -1,107 +0,0 @@
|
|||||||
[返回](./Runtime-README.md)
|
|
||||||
|
|
||||||
# /Convention/Runtime/Architecture
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
核心架构模块,提供依赖注入、事件系统和时间线管理功能
|
|
||||||
|
|
||||||
## 接口定义
|
|
||||||
|
|
||||||
系统定义了以下核心接口来规范架构设计:
|
|
||||||
|
|
||||||
### ISignal
|
|
||||||
信号接口,用于事件系统的消息传递
|
|
||||||
|
|
||||||
### IModel
|
|
||||||
模型接口,定义数据模型的序列化和反序列化能力:
|
|
||||||
- `Save()` 保存数据到字符串
|
|
||||||
- `Load(string data)` 从字符串加载数据
|
|
||||||
|
|
||||||
### IConvertable<T>
|
|
||||||
转换接口,提供类型转换能力:
|
|
||||||
- `ConvertTo()` 转换为目标类型
|
|
||||||
|
|
||||||
### IConvertModel<T>
|
|
||||||
组合接口,同时继承 `IModel` 和 `IConvertable<T>`
|
|
||||||
|
|
||||||
## 核心类型
|
|
||||||
|
|
||||||
### SingletonModel<T>
|
|
||||||
泛型单例模型,提供类型安全的单例管理:
|
|
||||||
- 支持依赖注入和实例替换
|
|
||||||
- 提供隐式转换操作符
|
|
||||||
- 集成模型序列化功能
|
|
||||||
|
|
||||||
### DependenceModel
|
|
||||||
依赖模型,管理多个条件的组合验证:
|
|
||||||
- 支持多个 `IConvertModel<bool>` 的依赖关系
|
|
||||||
- 提供枚举接口遍历所有依赖
|
|
||||||
- 所有依赖条件均满足时返回true
|
|
||||||
|
|
||||||
## 注册系统
|
|
||||||
|
|
||||||
### 对象注册
|
|
||||||
- `Register(Type slot, object target, Action completer, params Type[] dependences)`
|
|
||||||
- `Register<T>(T target, Action completer, params Type[] dependences)`
|
|
||||||
|
|
||||||
提供基于类型的依赖注入容器:
|
|
||||||
- 支持依赖关系管理
|
|
||||||
- 支持注册完成回调
|
|
||||||
- 自动解析依赖顺序
|
|
||||||
|
|
||||||
### 注册查询
|
|
||||||
- `Contains(Type type)` / `Contains<T>()`
|
|
||||||
- `Get(Type type)` / `Get<T>()`
|
|
||||||
|
|
||||||
## 事件系统
|
|
||||||
|
|
||||||
### 消息监听
|
|
||||||
- `AddListener<Signal>(Type slot, Action<Signal> listener)`
|
|
||||||
- `SendMessage(Type slot, ISignal signal)`
|
|
||||||
- `SendMessage<Signal>(Signal signal)`
|
|
||||||
|
|
||||||
提供类型安全的事件分发机制:
|
|
||||||
- 支持按类型注册监听器
|
|
||||||
- 支持泛型消息类型
|
|
||||||
- 自动管理监听器生命周期
|
|
||||||
|
|
||||||
### Listening类
|
|
||||||
监听器管理类,提供:
|
|
||||||
- `StopListening()` 停止监听
|
|
||||||
|
|
||||||
## 时间线系统
|
|
||||||
|
|
||||||
### 时间线管理
|
|
||||||
- `CreateTimeline()` 创建新的时间线
|
|
||||||
- `AddStep(int timelineId, Func<bool> predicate, params Action[] actions)` 添加步骤
|
|
||||||
- `UpdateTimeline()` 更新所有时间线
|
|
||||||
- `ResetTimelineContext(int timelineId)` 重置时间线上下文
|
|
||||||
|
|
||||||
提供基于条件的任务调度:
|
|
||||||
- 支持多条时间线并行执行
|
|
||||||
- 基于谓词的条件触发
|
|
||||||
- 支持动态添加执行步骤
|
|
||||||
|
|
||||||
## 类型格式化
|
|
||||||
|
|
||||||
### 类型序列化
|
|
||||||
- `FormatType(Type type)` 格式化类型为字符串
|
|
||||||
- `LoadFromFormat(string data)` 从字符串加载类型
|
|
||||||
- `LoadFromFormat(string data, out Exception exception)` 安全加载类型
|
|
||||||
|
|
||||||
使用格式:`Assembly::FullTypeName`
|
|
||||||
|
|
||||||
## 内部管理
|
|
||||||
|
|
||||||
### 系统重置
|
|
||||||
- `InternalReset()` 重置所有内部状态
|
|
||||||
|
|
||||||
清理以下组件:
|
|
||||||
- 注册历史
|
|
||||||
- 未完成的目标
|
|
||||||
- 完成器回调
|
|
||||||
- 依赖关系
|
|
||||||
- 子对象容器
|
|
||||||
- 事件监听器
|
|
||||||
- 时间线队列
|
|
@@ -1,24 +1,24 @@
|
|||||||
from .Config import *
|
from .Config import *
|
||||||
from .Reflection import *
|
from .Reflection import *
|
||||||
from abs import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
class ISignal(ABC):
|
class ISignal(ABC):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class IModel(ABC):
|
class IModel(ABC):
|
||||||
@absstractmethod
|
@abstractmethod
|
||||||
def Save(self) -> str:
|
def Save(self) -> str:
|
||||||
pass
|
pass
|
||||||
@absstractmethod
|
@abstractmethod
|
||||||
def Load(self, data:str) -> None:
|
def Load(self, data:str) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class IConvertable[T](ABC):
|
class IConvertable[T](ABC):
|
||||||
@absstractmethod
|
@abstractmethod
|
||||||
def ConvertTo() -> T:
|
def ConvertTo(self) -> T:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class IConvertModel[T](ABC, IConventable[T], IModel):
|
class IConvertModel[T](IConvertable[T], IModel):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class SingletonModel[T](IModel):
|
class SingletonModel[T](IModel):
|
||||||
@@ -26,22 +26,23 @@ class SingletonModel[T](IModel):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def GetInstance(t:Typen[T]) -> T:
|
def GetInstance(t:Typen[T]) -> T:
|
||||||
return _InjectInstances[t]
|
return SingletonModel._InjectInstances[t]
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def SetInstance(t:Typen[T], obj:T) -> None:
|
def SetInstance(t:Typen[T], obj:T) -> None:
|
||||||
_InjectInstances[t] = obj
|
SingletonModel._InjectInstances[t] = obj
|
||||||
|
|
||||||
def __init__(self, t:Typen[T]) -> None:
|
def __init__(self, t:Typen[T]) -> None:
|
||||||
self.typen: type = t
|
self.typen: type = t
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def Save() -> str:
|
def Save(self) -> str:
|
||||||
return GetInstance(self.typen).Save()
|
return SingletonModel.GetInstance(self.typen).Save()
|
||||||
|
|
||||||
def DependenceModel(IConvertModel[bool]):
|
class DependenceModel(IConvertModel[bool]):
|
||||||
def __init__(self, queries:Sequence[IConvertModel[bool]]) -> None:
|
def __init__(self, queries:Sequence[IConvertModel[bool]]) -> None:
|
||||||
self.queries:list[IConventModel] = list(queries)
|
self.queries:list[IConvertModel[bool]] = list(queries)
|
||||||
|
|
||||||
|
@override
|
||||||
def ConvertTo(self) -> bool:
|
def ConvertTo(self) -> bool:
|
||||||
for query in self.queries:
|
for query in self.queries:
|
||||||
if query.ConvertTo() == False:
|
if query.ConvertTo() == False:
|
||||||
@@ -49,38 +50,54 @@ def DependenceModel(IConvertModel[bool]):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return self.queries
|
return iter(self.queries)
|
||||||
|
|
||||||
def Load(self, data:str):
|
def Load(self, data:str):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
def Save(self) -> str:
|
def Save(self) -> str:
|
||||||
return NotImplement
|
raise NotImplementedError()
|
||||||
|
|
||||||
class Achitecture:
|
SignalListener = Callable[[ISignal], None]
|
||||||
|
|
||||||
|
class Architecture:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def FormatType(t:type) -> str:
|
def FormatType(t:type) -> str:
|
||||||
return f"{t.__module__}::{t.__name__}"
|
return f"{t.__module__}::{t.__name__}"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def LoadFromFormat(data:str) -> type|None:
|
def LoadFromFormat(data:str, exception:Exception|None=None) -> type|None:
|
||||||
module,name = data.split("::")
|
try:
|
||||||
return StringWithModule2Type(module,name)
|
module,name = data.split("::")
|
||||||
|
return StringWithModel2Type(name, module=module)
|
||||||
|
except Exception as ex:
|
||||||
|
if exception is not None:
|
||||||
|
exception = ex
|
||||||
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def InternalReset(cls) -> None:
|
def InternalReset(cls) -> None:
|
||||||
# Register System
|
# Register System
|
||||||
|
cls._RegisterHistory.clear()
|
||||||
|
cls._UncompleteTargets.clear()
|
||||||
|
cls._Completer.clear()
|
||||||
|
cls._Dependences.clear()
|
||||||
|
cls._Childs.clear()
|
||||||
|
# Event Listener
|
||||||
|
cls._SignalListener.clear()
|
||||||
|
# Linear Chain for Dependence
|
||||||
|
cls._TimelineQueues.clear()
|
||||||
|
cls._TimelineContentID = 0
|
||||||
|
|
||||||
#region Objects Registered
|
#region Objects Registered
|
||||||
|
|
||||||
class TypeQuery(IConvertModel[bool]):
|
class TypeQuery(IConvertModel[bool]):
|
||||||
def __init__(self, queryType:type) -> None:
|
def __init__(self, queryType:type) -> None:
|
||||||
self._quertType = quertType
|
self._queryType = queryType
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def ConvertTo(self) -> bool:
|
def ConvertTo(self) -> bool:
|
||||||
return self._queryType in Architectrue.Childs
|
return self._queryType in Architecture._Childs
|
||||||
|
|
||||||
def Load(self, data:str) -> None:
|
def Load(self, data:str) -> None:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
@@ -100,31 +117,167 @@ class Achitecture:
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
def ConvertTo(self) -> bool:
|
def ConvertTo(self) -> bool:
|
||||||
return self._registerSlot in Architecture.Childs
|
return self._registerSlot in Architecture._Childs
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def Load(self,data:str) -> None:
|
def Load(self,data:str) -> None:
|
||||||
raise InvalidOperationError()
|
raise InvalidOperationError(f"Cannot use {self.__class__.__name__} to load type")
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def Save(self) -> str:
|
def Save(self) -> str:
|
||||||
raise InalidOperationError()
|
return f"{Architecture.FormatType(self._registerSlot)}[{self.ConvertTo()}]"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _InternalRegisteringComplete(cls) -> bool,Set[type]:
|
def _InternalRegisteringComplete(cls) -> tuple[bool,Set[type]]:
|
||||||
resultSet: Set[type] = set()
|
resultSet: Set[type] = set()
|
||||||
stats: bool = False
|
stats: bool = False
|
||||||
for dependence in cls._Dependences.keys:
|
for dependence in cls._Dependences.keys():
|
||||||
if cls._Dependences[dependence].ConventTo():
|
if cls._Dependences[dependence].ConvertTo():
|
||||||
resultSet.insert(dependence)
|
resultSet.add(dependence)
|
||||||
stats = True
|
stats = True
|
||||||
return stats,resultSet
|
return stats,resultSet
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _InternalRegisteringUpdate(cls, internalUpdateBuffer:Set[type]):
|
def _InternalRegisteringUpdate(cls, internalUpdateBuffer:Set[type]):
|
||||||
for complete in cls._InternalUpdateBuffer:
|
for complete in internalUpdateBuffer:
|
||||||
cls._Dependences.remove(complete)
|
cls._Dependences.pop(complete, None)
|
||||||
# TODO
|
for complete in internalUpdateBuffer:
|
||||||
|
cls._Completer[complete]()
|
||||||
|
cls._Completer.pop(complete, None)
|
||||||
|
for complete in internalUpdateBuffer:
|
||||||
|
cls._Childs[complete] = cls._UncompleteTargets[complete]
|
||||||
|
cls._UncompleteTargets.pop(complete, None)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def Register(cls, slot:type, target:object, completer:Action, *dependences:type) -> 'Architecture.Registering':
|
||||||
|
if slot in cls._RegisterHistory:
|
||||||
|
raise InvalidOperationError("Illegal duplicate registrations")
|
||||||
|
|
||||||
|
cls._RegisterHistory.add(slot)
|
||||||
|
cls._Completer[slot] = completer
|
||||||
|
cls._UncompleteTargets[slot] = target
|
||||||
|
|
||||||
|
# 过滤掉自身依赖
|
||||||
|
filtered_deps = [dep for dep in dependences if dep != slot]
|
||||||
|
type_queries = [cls.TypeQuery(dep) for dep in filtered_deps]
|
||||||
|
cls._Dependences[slot] = DependenceModel(type_queries)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
has_complete, buffer = cls._InternalRegisteringComplete()
|
||||||
|
if not has_complete:
|
||||||
|
break
|
||||||
|
cls._InternalRegisteringUpdate(buffer)
|
||||||
|
|
||||||
|
return cls.Registering(slot)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def RegisterGeneric[T](cls, target:T, completer:Action, *dependences:type) -> 'Architecture.Registering':
|
||||||
|
return cls.Register(type(target), target, completer, *dependences)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def Contains(cls, type_:type) -> bool:
|
||||||
|
return type_ in cls._Childs
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def ContainsGeneric[T](cls) -> bool:
|
||||||
|
return cls.Contains(type(T))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def InternalGet(cls, type_:type) -> object:
|
||||||
|
return cls._Childs[type_]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def Get(cls, type_:type) -> object:
|
||||||
|
return cls.InternalGet(type_)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def GetGeneric[T](cls) -> T:
|
||||||
|
return cls.Get(type(T))
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Signal & Update
|
||||||
|
|
||||||
|
_SignalListener: Dict[type, Set[SignalListener]] = {}
|
||||||
|
|
||||||
|
class Listening:
|
||||||
|
def __init__(self, action:SignalListener, type_:type):
|
||||||
|
self._action = action
|
||||||
|
self._type = type_
|
||||||
|
|
||||||
|
def StopListening(self):
|
||||||
|
if self._type in Architecture._SignalListener:
|
||||||
|
Architecture._SignalListener[self._type].discard(self._action)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def AddListenerGeneric[Signal](cls, slot:type, listener:SignalListener) -> 'Architecture.Listening':
|
||||||
|
if slot not in cls._SignalListener:
|
||||||
|
cls._SignalListener[slot] = set()
|
||||||
|
|
||||||
|
def action(signal:ISignal):
|
||||||
|
if isinstance(signal, slot):
|
||||||
|
listener(signal)
|
||||||
|
|
||||||
|
result = cls.Listening(action, slot)
|
||||||
|
cls._SignalListener[slot].add(action)
|
||||||
|
return result
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def SendMessage(cls, slot:type, signal:ISignal):
|
||||||
|
if slot in cls._SignalListener:
|
||||||
|
for action in cls._SignalListener[slot]:
|
||||||
|
action(signal)
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Timeline/Chain & Update
|
||||||
|
|
||||||
|
class TimelineQueueEntry:
|
||||||
|
def __init__(self):
|
||||||
|
self.predicate: Callable[[], bool] = lambda: False
|
||||||
|
self.actions: List[Action] = []
|
||||||
|
|
||||||
|
class Timeline:
|
||||||
|
def __init__(self):
|
||||||
|
self.predicate_mapper: Dict[Callable[[], bool], int] = {}
|
||||||
|
self.queue: List[Architecture.TimelineQueueEntry] = []
|
||||||
|
self.context: int = 0
|
||||||
|
|
||||||
|
_TimelineQueues: Dict[int, Timeline] = {}
|
||||||
|
_TimelineContentID: int = 0
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def CreateTimeline(cls) -> int:
|
||||||
|
cls._TimelineQueues[cls._TimelineContentID] = cls.Timeline()
|
||||||
|
cls._TimelineContentID += 1
|
||||||
|
return cls._TimelineContentID - 1
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def AddStep(cls, timeline_id:int, predicate:Callable[[], bool], *actions:Action):
|
||||||
|
timeline = cls._TimelineQueues[timeline_id]
|
||||||
|
if predicate in timeline.predicate_mapper:
|
||||||
|
time = timeline.predicate_mapper[predicate]
|
||||||
|
timeline.queue[time].actions.extend(actions)
|
||||||
|
else:
|
||||||
|
time = len(timeline.queue)
|
||||||
|
timeline.predicate_mapper[predicate] = time
|
||||||
|
entry = cls.TimelineQueueEntry()
|
||||||
|
entry.predicate = predicate
|
||||||
|
entry.actions = list(actions)
|
||||||
|
timeline.queue.append(entry)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def UpdateTimeline(cls):
|
||||||
|
for timeline in cls._TimelineQueues.values():
|
||||||
|
if timeline.context < len(timeline.queue):
|
||||||
|
if timeline.queue[timeline.context].predicate():
|
||||||
|
for action in timeline.queue[timeline.context].actions:
|
||||||
|
action()
|
||||||
|
timeline.context += 1
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def ResetTimelineContext(cls, timeline_id:int):
|
||||||
|
cls._TimelineQueues[timeline_id].context = 0
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@@ -10,6 +10,20 @@ import time
|
|||||||
import os
|
import os
|
||||||
from colorama import Fore as ConsoleFrontColor, Back as ConsoleBackgroundColor, Style as ConsoleStyle
|
from colorama import Fore as ConsoleFrontColor, Back as ConsoleBackgroundColor, Style as ConsoleStyle
|
||||||
|
|
||||||
|
class NotImplementedError(Exception):
|
||||||
|
def __init__(self, message:Optional[str]=None) -> None:
|
||||||
|
if message is not None:
|
||||||
|
super().__init__(message)
|
||||||
|
else:
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
class InvalidOperationError(Exception):
|
||||||
|
def __init__(self, message:Optional[str]=None) -> None:
|
||||||
|
if message is not None:
|
||||||
|
super().__init__(message)
|
||||||
|
else:
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
def format_traceback_info(char:str='\n', back:int=1):
|
def format_traceback_info(char:str='\n', back:int=1):
|
||||||
return char.join(traceback.format_stack()[:-back])
|
return char.join(traceback.format_stack()[:-back])
|
||||||
|
|
||||||
@@ -77,12 +91,7 @@ except ImportError:
|
|||||||
|
|
||||||
type Typen[_T] = type
|
type Typen[_T] = type
|
||||||
|
|
||||||
type Action[_T] = Callable[[_T], None]
|
type Action = Callable[[], None]
|
||||||
type Action2[_T1, _T2] = Callable[[_T1, _T2], None]
|
|
||||||
type Action3[_T1, _T2, _T3] = Callable[[_T1, _T2, _T3], None]
|
|
||||||
type Action4[_T1, _T2, _T3, _T4] = Callable[[_T1, _T2, _T3, _T4], None]
|
|
||||||
type Action5[_T1, _T2, _T3, _T4, _T5] = Callable[[_T1, _T2, _T3, _T4, _T5], None]
|
|
||||||
type ActionW = Callable[[Sequence[Any]], None]
|
|
||||||
type ClosuresCallable[_T] = Union[Callable[[Optional[None]], _T], Typen[_T]]
|
type ClosuresCallable[_T] = Union[Callable[[Optional[None]], _T], Typen[_T]]
|
||||||
|
|
||||||
def AssemblyTypen(obj:Any) -> str:
|
def AssemblyTypen(obj:Any) -> str:
|
||||||
|
@@ -1,13 +1,7 @@
|
|||||||
from pathlib import Path
|
|
||||||
from .Config import *
|
from .Config import *
|
||||||
import re
|
|
||||||
from pathlib import Path
|
|
||||||
import xml.etree.ElementTree as ET
|
|
||||||
from xml.dom import minidom
|
|
||||||
import math
|
|
||||||
|
|
||||||
def LimitStringLength(data, max_length:int=50) -> str:
|
def LimitStringLength(data, max_length:int=50) -> str:
|
||||||
s:str = data if data is str else str(data)
|
s:str = data if isinstance(data, str) else str(data)
|
||||||
if len(s) <= max_length:
|
if len(s) <= max_length:
|
||||||
return s
|
return s
|
||||||
else:
|
else:
|
||||||
@@ -24,7 +18,7 @@ def FillString(data:Any,
|
|||||||
fill_char: str = " ",
|
fill_char: str = " ",
|
||||||
side: Literal["left", "right", "center"] = "right"
|
side: Literal["left", "right", "center"] = "right"
|
||||||
) -> str:
|
) -> str:
|
||||||
s:str = data if data is str else str(data)
|
s:str = data if isinstance(data, str) else str(data)
|
||||||
char = fill_char[0]
|
char = fill_char[0]
|
||||||
if len(s) >= max_length:
|
if len(s) >= max_length:
|
||||||
return s
|
return s
|
||||||
|
Reference in New Issue
Block a user