chronaxis.execution.record_time_indexes
源码基线:8904c2f744951857659abd40aaa3535d57faf53c
1"""只为周期选窗/事件时长保存一份所需驱动时刻,按索引定位。23每条只写一个实际整数时间;偏移只在单次查询中按记录索引计算,不保存改写时间轴。4"""5from fractions import Fraction6from struct import Struct7from tempfile import TemporaryFile8from ..results.errors import ResultClosedError9from ..results.spool import _reserve_owned_ram,_release_owned_ram,_cleanup_owner101112class RecordTimeIndexes:13 """拥有各必要驱动的一份定长时间文件,所有消费者借用。"""1415 def __init__(self,budget,path,drivers):16 """预付有限索引状态。参数: 预算/私有路径/唯一驱动名称。返回: owner。异常: 原构造错。"""17 self._budget,self._path=budget,path18 self._files={}19 self._counts={}20 self._last={}21 self._held_cost=self._fixed_cost=022 self._closed=False23 self._codec=Struct('>q')24 try:25 _reserve_owned_ram(self,65536+len(drivers)*32768,'_fixed_cost')26 self._counts=dict.fromkeys(drivers,0)27 self._last=dict.fromkeys(drivers)28 except BaseException as error:29 _cleanup_owner(self,error,'record_time_indexes_cleanup_owner','必要驱动索引构造')30 raise3132 def observe_group(self,group):33 """每驱动每实际同刻组只写一次。参数: 完整组。返回: None。异常: 顺序/IO/预算错误。"""34 if self._closed:35 raise ResultClosedError('驱动索引已关闭')36 names={sample.name for sample in group.samples}37 for driver in self._counts:38 if driver not in names:39 continue40 if self._last[driver] is not None and group.time_ns<=self._last[driver]:41 raise ValueError('驱动索引时间回退或重复')42 if driver not in self._files:43 self._files[driver]=TemporaryFile(mode='w+b',dir=self._path,prefix='driver-times-')44 self._budget.reserve_spool(8)45 handle=self._files[driver]46 handle.seek(0,2)47 if handle.write(self._codec.pack(group.time_ns))!=8:48 raise OSError('驱动索引短写')49 self._counts[driver]+=150 self._last[driver]=group.time_ns5152 def count(self,driver):53 """读取已写条数。参数: 驱动。返回: int。异常: 未声明驱动。"""54 return self._counts[driver]5556 def last(self,driver):57 """读取已写末时刻。参数: 驱动。返回: int或None。异常: 未声明驱动。"""58 return self._last[driver]5960 def at(self,driver,index):61 """只读取一个时间整数。参数: 驱动/索引。返回: int。异常: 越界/短读。"""62 if self._closed:63 raise ResultClosedError('驱动索引已关闭')64 if not 0<=index<self._counts[driver]:65 raise IndexError('驱动索引越界')66 handle=self._files[driver]67 handle.seek(index*8)68 data=handle.read(8)69 if len(data)!=8:70 raise ValueError('驱动索引短读')71 return self._codec.unpack(data)[0]7273 def lower(self,driver,time_ns):74 """二分首个不早于time_ns的时刻。参数: 驱动/实际时间。返回: 索引。异常: 原读取错。"""75 lo,hi=0,self._counts[driver]76 while lo<hi:77 mid=(lo+hi)//278 if self.at(driver,mid)<time_ns:79 lo=mid+180 else:81 hi=mid82 return lo8384 def position(self,driver,time_ns):85 """单次查询实际时刻位于哪对记录及其比例,不保存转换时间轴。8687 参数: 驱动/实际时间。返回: (Fraction记录位置,是否超出观测)。异常: 空索引或读取错。88 """89 if not self.count(driver):90 raise ValueError('记录位置需要真实驱动')91 count=self.count(driver)92 right=self.lower(driver,time_ns)93 if right==count:94 position=Fraction(count-1)95 outside=time_ns>self.at(driver,count-1)96 elif self.at(driver,right)==time_ns:97 position=Fraction(right)98 outside=False99 elif right==0:100 position=Fraction(0)101 outside=True102 else:103 left_time,right_time=self.at(driver,right-1),self.at(driver,right)104 position=Fraction(right-1)+Fraction(time_ns-left_time,right_time-left_time)105 outside=False106 return position,outside107108 def shift(self,driver,time_ns,offset_ns,step_ns):109 """按所选周期偏移记录位置,返回实际时刻及首尾裁剪标识。110111 参数: 驱动、实际锚点、带符号偏移ns、正周期ns。返回: (实际ns,是否裁剪)。112 非整周期按原实际间隔比例定位,最终纳秒沿既有最近偶数舍入;不补观测之外的记录。113 异常: 空索引/非法周期/实际读取错。114 """115 if step_ns<=0 or not self.count(driver):116 raise ValueError('周期选窗需要正周期及真实驱动记录')117 count=self.count(driver)118 position,outside=self.position(driver,time_ns)119 target=position+Fraction(offset_ns,step_ns)120 if target<0:121 return self.at(driver,0),True122 if target>count-1:123 return self.at(driver,count-1),True124 left=target.numerator//target.denominator125 if target==left:126 return self.at(driver,left),outside127 a,b=self.at(driver,left),self.at(driver,left+1)128 return round(Fraction(a)+(target-left)*(b-a)),outside129130 @property131 def closed(self):132 """返回关闭状态。参数: 无。返回: bool。异常: 无。"""133 return self._closed134135 def close(self):136 """只关闭本次必要文件,失败可重试。参数: 无。返回: None。异常: 原关闭错。"""137 if self._closed:138 return139 for driver in tuple(self._files):140 self._files[driver].close()141 del self._files[driver]142 _release_owned_ram(self,self._fixed_cost,'_fixed_cost')143 self._closed=True144