Imported core, m, ag and serial for refactoring
This commit is contained in:
parent
6f28ac0382
commit
5baa9b75d0
25 changed files with 918 additions and 4 deletions
0
ag/__init__.py
Normal file
0
ag/__init__.py
Normal file
160
ag/ag.Aggregate.pyx
Normal file
160
ag/ag.Aggregate.pyx
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
cdef class Aggregate:
|
||||||
|
def __init__(Aggregate self, *modules):
|
||||||
|
self._attrs = {} # attr_name, module
|
||||||
|
self._modules = OrderedDict() # module_name, module
|
||||||
|
self._provided = {} # keyword, module
|
||||||
|
self._common = {} # method_name, method_wrapper
|
||||||
|
|
||||||
|
for module in modules:
|
||||||
|
self._link_module(module)
|
||||||
|
|
||||||
|
def _list_modules(Aggregate self):
|
||||||
|
d = {}
|
||||||
|
|
||||||
|
for attr, module in self._attrs.items():
|
||||||
|
if module not in d:
|
||||||
|
d[module] = set()
|
||||||
|
|
||||||
|
d[module].add(attr)
|
||||||
|
|
||||||
|
return d
|
||||||
|
|
||||||
|
def _link_module(Aggregate self, Module module):
|
||||||
|
# check for name (=type) collision
|
||||||
|
if module._name in self._modules.keys():
|
||||||
|
raise ModuleCollision(module._name, None, "name", None)
|
||||||
|
|
||||||
|
# check if requirements are satisfied
|
||||||
|
unsatisfied_deps = module._requires - set(self._provided.keys())
|
||||||
|
if unsatisfied_deps:
|
||||||
|
raise ModuleDependencyError(module._name, unsatisfied_deps)
|
||||||
|
|
||||||
|
# check for new module declaring a common method that we already provide as non-common
|
||||||
|
new_commons = module._common - set(self._common.keys())
|
||||||
|
common_collisions = {nc for nc in new_commons if nc in self._attrs and not nc in self._common.keys()}
|
||||||
|
if common_collisions:
|
||||||
|
colliding_module_names = {self._attrs[x]._name for x in common_collisions}
|
||||||
|
raise ModuleCollision(module._name, colliding_module_names, "non-common method", common_collisions)
|
||||||
|
|
||||||
|
# check for an attr collision
|
||||||
|
module_attrs = {x for x in dir(module) if x[0] != "_"}
|
||||||
|
attr_collisions = (module_attrs - module._common) & (set(self._attrs.keys()) | set(self._common.keys()))
|
||||||
|
if attr_collisions:
|
||||||
|
colliding_module_names = set()
|
||||||
|
for collision in attr_collisions:
|
||||||
|
if collision in self._attrs:
|
||||||
|
colliding_module_names.add(self._attrs[collision]._name)
|
||||||
|
|
||||||
|
if collision in self._common:
|
||||||
|
colliding_module_names.add(self._common[collision]._name)
|
||||||
|
|
||||||
|
raise ModuleCollision(module._name, colliding_module_names, "attribute", attr_collisions)
|
||||||
|
|
||||||
|
# check for a provided keyword collision
|
||||||
|
provided_collisions = module._provides & set(self._provided.keys())
|
||||||
|
if provided_collisions:
|
||||||
|
colliding_module_names = {self._provided[x]._name for x in provided_collisions}
|
||||||
|
raise ModuleCollision(module._name, colliding_module_names, "provided keyword", provided_collisions)
|
||||||
|
|
||||||
|
# link the module
|
||||||
|
self._modules[module._name] = module
|
||||||
|
|
||||||
|
for keyword in module._provides:
|
||||||
|
self._provided[keyword] = module
|
||||||
|
|
||||||
|
for attr in (module_attrs - module._common):
|
||||||
|
self._attrs[attr] = module
|
||||||
|
|
||||||
|
# create and/or populate CommonMethod wrappers to common methods
|
||||||
|
for method_name in module._common:
|
||||||
|
if method_name not in module_attrs:
|
||||||
|
raise CommonMethodMissing(method_name, module._name)
|
||||||
|
|
||||||
|
if method_name not in self._common:
|
||||||
|
self._common[method_name] = CommonMethod(method_name)
|
||||||
|
|
||||||
|
self._common[method_name].link_module(module)
|
||||||
|
|
||||||
|
# hand the module a reference to us
|
||||||
|
module._top = self
|
||||||
|
|
||||||
|
# call the module's _on_link method, if it has one
|
||||||
|
if hasattr(module, "_on_link"):
|
||||||
|
module._on_link()
|
||||||
|
|
||||||
|
def _unlink_module(Aggregate self, str module_name):
|
||||||
|
if not module_name in self._modules:
|
||||||
|
raise ModuleDoesntExist(module_name)
|
||||||
|
|
||||||
|
module = self._modules[module_name]
|
||||||
|
|
||||||
|
# check reverse dependencies
|
||||||
|
global_deps = set()
|
||||||
|
for m in self._modules.values():
|
||||||
|
global_deps.update(m._requires)
|
||||||
|
|
||||||
|
reverse_deps = module._provides & global_deps
|
||||||
|
if reverse_deps:
|
||||||
|
raise ModuleDependencyError(module_name, reverse_deps, unlink=True)
|
||||||
|
|
||||||
|
# remove from all pools
|
||||||
|
for aname, mod in list(self._attrs.items()):
|
||||||
|
if mod._name == module_name:
|
||||||
|
del self._attrs[aname]
|
||||||
|
|
||||||
|
del self._modules[module_name]
|
||||||
|
|
||||||
|
for ename in module._provides:
|
||||||
|
del self._provided[ename]
|
||||||
|
|
||||||
|
# remove _common wrappers
|
||||||
|
for method_name in module._common:
|
||||||
|
self._common[method_name].unlink_module(module_name)
|
||||||
|
|
||||||
|
# clear _top reference
|
||||||
|
module._top = None
|
||||||
|
|
||||||
|
def _merge_in(Aggregate self, Aggregate other_ag):
|
||||||
|
for module_name, module in other_ag._modules.items():
|
||||||
|
if module_name not in self._modules:
|
||||||
|
self._link_module(module)
|
||||||
|
|
||||||
|
def __getattr__(Aggregate self, str aname):
|
||||||
|
if aname in self._attrs:
|
||||||
|
return getattr(self._attrs[aname], aname)
|
||||||
|
elif aname in self._common:
|
||||||
|
return self._common[aname]
|
||||||
|
else:
|
||||||
|
raise AttributeError("Aggregate has no attribute '%s'" %(aname))
|
||||||
|
|
||||||
|
def __setattr__(Aggregate self, str aname, avalue):
|
||||||
|
if aname not in self._attrs:
|
||||||
|
raise AttributeError("Aggregate has no attribute '%s'" %(aname))
|
||||||
|
else:
|
||||||
|
setattr(self._attrs[aname], aname, avalue)
|
||||||
|
|
||||||
|
def _get_type(Aggregate self):
|
||||||
|
return tuple(self._modules.keys())
|
||||||
|
|
||||||
|
def __repr__(Aggregate self):
|
||||||
|
module_count = len(self._modules)
|
||||||
|
|
||||||
|
if module_count:
|
||||||
|
lines = ["Aggregate("]
|
||||||
|
|
||||||
|
|
||||||
|
for i, module in enumerate(self._modules.values()):
|
||||||
|
if i + 1 < module_count:
|
||||||
|
comma = ","
|
||||||
|
else:
|
||||||
|
comma = ""
|
||||||
|
|
||||||
|
lines.append(" %s%s" %(repr(module), comma))
|
||||||
|
|
||||||
|
lines.append(")")
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
||||||
|
else:
|
||||||
|
return "Aggregate()"
|
30
ag/ag.CommonMethod.pyx
Normal file
30
ag/ag.CommonMethod.pyx
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
cdef class CommonMethod:
|
||||||
|
def __init__(CommonMethod self, str method_name):
|
||||||
|
self.method_name = method_name
|
||||||
|
self.module_names = []
|
||||||
|
self.modules = []
|
||||||
|
|
||||||
|
def link_module(CommonMethod self, Module module):
|
||||||
|
self.module_names.append(module._name)
|
||||||
|
self.modules.append(module)
|
||||||
|
|
||||||
|
def unlink_module(CommonMethod self, module_name):
|
||||||
|
i = self.module_names.index(module_name)
|
||||||
|
|
||||||
|
del self.module_names[i]
|
||||||
|
del self.modules[i]
|
||||||
|
|
||||||
|
def __call__(CommonMethod self, *args):
|
||||||
|
return_values = []
|
||||||
|
|
||||||
|
for module in self.modules:
|
||||||
|
return_values.append(getattr(module, self.method_name).__call__(*args))
|
||||||
|
|
||||||
|
return return_values
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "CommonMethod(name=%s)" %(self.method_name)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _name(self):
|
||||||
|
return repr(self)
|
20
ag/ag.Module.pyx
Normal file
20
ag/ag.Module.pyx
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
cdef class Module:
|
||||||
|
def __init__(self, name, provides=set(), requires=set(), common=set()):
|
||||||
|
"""
|
||||||
|
* Modules are identified by their name which has to be unique within the Aggregate's namespace.
|
||||||
|
* The provides sequence contains keywords which are copied into Aggregate's _provided list.
|
||||||
|
Two modules providing the same export can't be linked into the same Aggregate.
|
||||||
|
* The requires sequence contains keywords that have to be already present in the Aggregate's
|
||||||
|
_provided list before linking.
|
||||||
|
* All names in the Module's namespace that don't begin with an underscore will be exported
|
||||||
|
into the Aggregate's namespace.
|
||||||
|
* If you want to call one method on multiple modules, these modules must all export the method name
|
||||||
|
in their "common" set. Otherwise a name collision is raised. Their methods will be called in the
|
||||||
|
same order in which the modules were linked.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._top = None
|
||||||
|
self._name = name
|
||||||
|
self._provides = set(provides)
|
||||||
|
self._requires = set(requires)
|
||||||
|
self._common = set(common)
|
49
ag/ag.header.pyx
Normal file
49
ag/ag.header.pyx
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
"""
|
||||||
|
Overwatch Aggregate Object type
|
||||||
|
|
||||||
|
TODO Method overrides: _common gets renamed to _call_all and we add _call_last.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class ModuleCollision(Exception):
|
||||||
|
def __init__(self, colliding_module_name, resident_module_names, item_type, items):
|
||||||
|
self.colliding_module_name = colliding_module_name
|
||||||
|
self.resident_module_names = resident_module_names
|
||||||
|
self.item_type = item_type
|
||||||
|
self.items = items
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self.item_type == "name":
|
||||||
|
return "Unable to link module '%s': name already present in Aggregate." %(self.colliding_module_name)
|
||||||
|
else:
|
||||||
|
return "Unable to link module '%s': %s(s) '%s' already provided by module(s) '%s'." %(self.colliding_module_name,
|
||||||
|
self.item_type, "', '".join(self.items), "', '".join(self.resident_module_names))
|
||||||
|
|
||||||
|
class ModuleDependencyError(Exception):
|
||||||
|
def __init__(self, module_name, dependencies, unlink=False):
|
||||||
|
self.module_name = module_name
|
||||||
|
self.dependencies = dependencies
|
||||||
|
self.unlink = unlink
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self.unlink:
|
||||||
|
return "Unable to unlink module '%s': Aggregate depends on its export(s) '%s'." %(self.module_name,
|
||||||
|
"', '".join(self.dependencies))
|
||||||
|
else:
|
||||||
|
return "Unable to link module '%s': requirement '%s' unsatisfied by Aggregate." %(self.module_name,
|
||||||
|
"', '".join(self.dependencies))
|
||||||
|
|
||||||
|
class CommonMethodMissing(Exception):
|
||||||
|
def __init__(self, method_name, module_name):
|
||||||
|
self.method_name = method_name
|
||||||
|
self.module_name = module_name
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Unable to link module '%s': method '%s' (declared as common) does not exist in module." %(
|
||||||
|
self.module_name, self.method_name)
|
||||||
|
|
||||||
|
class ModuleDoesntExist(Exception):
|
||||||
|
def __init__(self, module_name):
|
||||||
|
self.module_name = module_name
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Unable to unlink module '%s': not linked to Aggregate." %(self.module_name)
|
0
ag/build.sh
Executable file
0
ag/build.sh
Executable file
|
@ -1,11 +1,10 @@
|
||||||
#! /bin/zsh
|
#! /bin/zsh
|
||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
|
|
||||||
for dir in src-[0-9][0-9]_*
|
for dir in ag core m serial
|
||||||
do
|
do
|
||||||
echo "Building in ${dir}"
|
echo "Building in ${dir}"
|
||||||
cd "$dir"
|
cd "$dir"
|
||||||
./build.sh
|
./build.sh
|
||||||
cd ..
|
cd ..
|
||||||
ln -s $dir/*.so .
|
|
||||||
done
|
done
|
0
core/__init__.py
Normal file
0
core/__init__.py
Normal file
0
m/__init__.py
Normal file
0
m/__init__.py
Normal file
23
m/m.header.pyx
Normal file
23
m/m.header.pyx
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
"""
|
||||||
|
Vector And Matrix Math
|
||||||
|
|
||||||
|
TODO some nice description
|
||||||
|
"""
|
||||||
|
|
||||||
|
from libc.stdlib cimport malloc, realloc, free
|
||||||
|
from libc.stdint cimport uint8_t, uint16_t, uint64_t
|
||||||
|
from libc.math cimport sin, cos, sqrt
|
||||||
|
|
||||||
|
class MathError(Exception):
|
||||||
|
def __init__(self, description):
|
||||||
|
self.description = description
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.description
|
||||||
|
|
||||||
|
class GeneralError(Exception):
|
||||||
|
def __init__(self, description):
|
||||||
|
self.description = description
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.description
|
332
m/m.mat4.pyx
Normal file
332
m/m.mat4.pyx
Normal file
|
@ -0,0 +1,332 @@
|
||||||
|
cdef class mat4:
|
||||||
|
"""
|
||||||
|
A float 4x4 matrix.
|
||||||
|
|
||||||
|
All arrays are column-major, i.e. OpenGL style:
|
||||||
|
|
||||||
|
0 4 8 12
|
||||||
|
1 5 9 13
|
||||||
|
2 6 10 14
|
||||||
|
3 7 11 15
|
||||||
|
|
||||||
|
The matrix implements stacking useful for graphics.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __cinit__(mat4 self):
|
||||||
|
to_alloc = 16 * sizeof(float)
|
||||||
|
self.stack = <float *>malloc(to_alloc)
|
||||||
|
|
||||||
|
if not self.stack:
|
||||||
|
raise MemoryError("Unable to malloc %d B for mat4." %(to_alloc))
|
||||||
|
|
||||||
|
self.m = self.stack
|
||||||
|
self.size = 1
|
||||||
|
|
||||||
|
def _debug(mat4 self):
|
||||||
|
print("--- self.stack = %d" %(<uint64_t>self.stack))
|
||||||
|
print("--- self.m = %d (+%d)" %(<uint64_t>self.m, self.m - self.stack))
|
||||||
|
print("--- self.size = %d" %(self.size))
|
||||||
|
|
||||||
|
def __init__(mat4 self, *args):
|
||||||
|
"""
|
||||||
|
Create a ma4t.
|
||||||
|
|
||||||
|
Accepts any number of parameters between 0 and 16 to fill the
|
||||||
|
matrix from the upper left corner going down (column-wise).
|
||||||
|
"""
|
||||||
|
|
||||||
|
length = len(args)
|
||||||
|
|
||||||
|
if length == 1 and isinstance(args[0], (list, tuple)):
|
||||||
|
args = args[0]
|
||||||
|
length = len(args)
|
||||||
|
|
||||||
|
if length > 16:
|
||||||
|
raise MathError("Attempt to initialize a mat4 with %d arguments." %(length))
|
||||||
|
|
||||||
|
self.load_from(args)
|
||||||
|
|
||||||
|
def __dealloc__(mat4 self):
|
||||||
|
free(self.stack)
|
||||||
|
|
||||||
|
def __getstate__(mat4 self):
|
||||||
|
state = []
|
||||||
|
|
||||||
|
for i in range(self.m - self.stack + 16):
|
||||||
|
state.append(self.stack[i])
|
||||||
|
|
||||||
|
return state
|
||||||
|
|
||||||
|
def __setstate__(mat4 self, state):
|
||||||
|
length = len(state)
|
||||||
|
matrices = length//16
|
||||||
|
|
||||||
|
if not matrices*16 == length:
|
||||||
|
raise GeneralError("mat4 __setstate__ got %d floats as a state" %(length))
|
||||||
|
|
||||||
|
self.m = self.stack
|
||||||
|
|
||||||
|
slot_full = False
|
||||||
|
for start in range(0, length, 16):
|
||||||
|
if slot_full:
|
||||||
|
self.push()
|
||||||
|
slot_full = False
|
||||||
|
|
||||||
|
self.load_from(state[start:start+16])
|
||||||
|
slot_full = True
|
||||||
|
|
||||||
|
def __getitem__(mat4 self, int i):
|
||||||
|
if i > 16 or i < 0:
|
||||||
|
raise IndexError("element index out of range(16)")
|
||||||
|
|
||||||
|
return self.m[i]
|
||||||
|
|
||||||
|
def __setitem__(self, int i, value):
|
||||||
|
if i > 16 or i < 0:
|
||||||
|
raise IndexError("element index out of range(16)")
|
||||||
|
|
||||||
|
self.m[i] = value
|
||||||
|
|
||||||
|
def push(mat4 self):
|
||||||
|
"""
|
||||||
|
Push the current matrix into the stack and load up an empty one (a zero matrix)
|
||||||
|
"""
|
||||||
|
|
||||||
|
# self.m points to the current matrix
|
||||||
|
# self.stack points to the first matrix
|
||||||
|
# self.size how many matrices are allocated
|
||||||
|
|
||||||
|
# ensure there's room for one more
|
||||||
|
cdef unsigned int used = 1 + (self.m - self.stack) / 16
|
||||||
|
cdef unsigned int empty = self.size - used
|
||||||
|
cdef float *tmp
|
||||||
|
|
||||||
|
if not empty:
|
||||||
|
self.size += 1
|
||||||
|
to_alloc = self.size * 16 * sizeof(float)
|
||||||
|
tmp = <float *>realloc(self.stack, to_alloc)
|
||||||
|
|
||||||
|
if tmp:
|
||||||
|
self.stack = tmp
|
||||||
|
else:
|
||||||
|
raise MemoryError("Unable to malloc %d B for mat4." %(to_alloc))
|
||||||
|
|
||||||
|
# advance the pointer to the new one
|
||||||
|
self.m = self.stack + 16 * used
|
||||||
|
|
||||||
|
# at this point there's at least enough space for one matrix
|
||||||
|
# copy the old matrix into the new one
|
||||||
|
cdef uint8_t i
|
||||||
|
cdef float *old_m = self.m - 16
|
||||||
|
for i in range(16):
|
||||||
|
self.m[i] = old_m[i]
|
||||||
|
|
||||||
|
def pop(mat4 self):
|
||||||
|
"""
|
||||||
|
Pop a matrix from the stack.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.m == self.stack:
|
||||||
|
raise IndexError("pop from an empty stack")
|
||||||
|
|
||||||
|
self.m -= 16
|
||||||
|
|
||||||
|
def get_list(mat4 self):
|
||||||
|
L = []
|
||||||
|
|
||||||
|
for i in range(16):
|
||||||
|
L.append(self.m[i])
|
||||||
|
|
||||||
|
return L
|
||||||
|
|
||||||
|
def load_from(mat4 self, L):
|
||||||
|
"""
|
||||||
|
Fill the current matrix from a either a list of values, column-major,
|
||||||
|
or another matrix. This method doesn't modify the stack, only the
|
||||||
|
current matrix is read and modified.
|
||||||
|
|
||||||
|
If the number of values isn't 16, it will be padded to 16 by zeros.
|
||||||
|
If it's larger, GeneralError will be raised.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if isinstance(L, mat4):
|
||||||
|
L = L.get_list()
|
||||||
|
length = 16
|
||||||
|
else:
|
||||||
|
length = len(L)
|
||||||
|
|
||||||
|
if length > 16:
|
||||||
|
raise GeneralError("supplied list is longer than 16")
|
||||||
|
|
||||||
|
for i in range(16):
|
||||||
|
if i < length:
|
||||||
|
self.m[i] = L[i]
|
||||||
|
else:
|
||||||
|
self.m[i] = 0.0
|
||||||
|
|
||||||
|
def zero(mat4 self):
|
||||||
|
"""Fill the matrix with zeroes."""
|
||||||
|
|
||||||
|
for i in range(16):
|
||||||
|
self.m[i] = 0.0
|
||||||
|
|
||||||
|
def identity(mat4 self):
|
||||||
|
"""Make the matrix an identity."""
|
||||||
|
|
||||||
|
self.zero()
|
||||||
|
|
||||||
|
self.m[0] = 1.0
|
||||||
|
self.m[5] = 1.0
|
||||||
|
self.m[10] = 1.0
|
||||||
|
self.m[15] = 1.0
|
||||||
|
|
||||||
|
def transpose(mat4 self):
|
||||||
|
"""Transpose the matrix."""
|
||||||
|
|
||||||
|
cdef float tmp
|
||||||
|
|
||||||
|
tmp = self.m[1]
|
||||||
|
self.m[1] = self.m[4]
|
||||||
|
self.m[4] = tmp
|
||||||
|
|
||||||
|
tmp = self.m[2]
|
||||||
|
self.m[2] = self.m[8]
|
||||||
|
self.m[8] = tmp
|
||||||
|
|
||||||
|
tmp = self.m[3]
|
||||||
|
self.m[3] = self.m[12]
|
||||||
|
self.m[12] = tmp
|
||||||
|
|
||||||
|
tmp = self.m[7]
|
||||||
|
self.m[7] = self.m[13]
|
||||||
|
self.m[13] = tmp
|
||||||
|
|
||||||
|
tmp = self.m[11]
|
||||||
|
self.m[11] = self.m[14]
|
||||||
|
self.m[14] = tmp
|
||||||
|
|
||||||
|
tmp = self.m[6]
|
||||||
|
self.m[6] = self.m[9]
|
||||||
|
self.m[9] = tmp
|
||||||
|
|
||||||
|
def invert(mat4 self):
|
||||||
|
"""Invert the matrix."""
|
||||||
|
|
||||||
|
cdef float tmp[16]
|
||||||
|
cdef float det
|
||||||
|
|
||||||
|
tmp[0] = self.m[5]*self.m[10]*self.m[15] - self.m[5]*self.m[11]*self.m[14] - self.m[9]*self.m[6]*self.m[15] + self.m[9]*self.m[7]*self.m[14] + self.m[13]*self.m[6]*self.m[11] - self.m[13]*self.m[7]*self.m[10]
|
||||||
|
tmp[4] = -self.m[4]*self.m[10]*self.m[15] + self.m[4]*self.m[11]*self.m[14] + self.m[8]*self.m[6]*self.m[15] - self.m[8]*self.m[7]*self.m[14] - self.m[12]*self.m[6]*self.m[11] + self.m[12]*self.m[7]*self.m[10]
|
||||||
|
tmp[8] = self.m[4]*self.m[9]*self.m[15] - self.m[4]*self.m[11]*self.m[13] - self.m[8]*self.m[5]*self.m[15] + self.m[8]*self.m[7]*self.m[13] + self.m[12]*self.m[5]*self.m[11] - self.m[12]*self.m[7]*self.m[9]
|
||||||
|
tmp[12] = -self.m[4]*self.m[9]*self.m[14] + self.m[4]*self.m[10]*self.m[13] + self.m[8]*self.m[5]*self.m[14] - self.m[8]*self.m[6]*self.m[13] - self.m[12]*self.m[5]*self.m[10] + self.m[12]*self.m[6]*self.m[9]
|
||||||
|
|
||||||
|
det = self.m[0]*tmp[0] + self.m[1]*tmp[4] + self.m[2]*tmp[8] + self.m[3]*tmp[12]
|
||||||
|
|
||||||
|
# epsilon pulled straight out of Uranus
|
||||||
|
if det < 0.00005 and det > -0.00005:
|
||||||
|
print("det=%.1f" %(det))
|
||||||
|
return
|
||||||
|
|
||||||
|
tmp[1] = -self.m[1]*self.m[10]*self.m[15] + self.m[1]*self.m[11]*self.m[14] + self.m[9]*self.m[2]*self.m[15] - self.m[9]*self.m[3]*self.m[14] - self.m[13]*self.m[2]*self.m[11] + self.m[13]*self.m[3]*self.m[10]
|
||||||
|
tmp[5] = self.m[0]*self.m[10]*self.m[15] - self.m[0]*self.m[11]*self.m[14] - self.m[8]*self.m[2]*self.m[15] + self.m[8]*self.m[3]*self.m[14] + self.m[12]*self.m[2]*self.m[11] - self.m[12]*self.m[3]*self.m[10]
|
||||||
|
tmp[9] = -self.m[0]*self.m[9]*self.m[15] + self.m[0]*self.m[11]*self.m[13] + self.m[8]*self.m[1]*self.m[15] - self.m[8]*self.m[3]*self.m[13] - self.m[12]*self.m[1]*self.m[11] + self.m[12]*self.m[3]*self.m[9]
|
||||||
|
tmp[13] = self.m[0]*self.m[9]*self.m[14] - self.m[0]*self.m[10]*self.m[13] - self.m[8]*self.m[1]*self.m[14] + self.m[8]*self.m[2]*self.m[13] + self.m[12]*self.m[1]*self.m[10] - self.m[12]*self.m[2]*self.m[9]
|
||||||
|
tmp[2] = self.m[1]*self.m[6]*self.m[15] - self.m[1]*self.m[7]*self.m[14] - self.m[5]*self.m[2]*self.m[15] + self.m[5]*self.m[3]*self.m[14] + self.m[13]*self.m[2]*self.m[7] - self.m[13]*self.m[3]*self.m[6]
|
||||||
|
tmp[6] = -self.m[0]*self.m[6]*self.m[15] + self.m[0]*self.m[7]*self.m[14] + self.m[4]*self.m[2]*self.m[15] - self.m[4]*self.m[3]*self.m[14] - self.m[12]*self.m[2]*self.m[7] + self.m[12]*self.m[3]*self.m[6]
|
||||||
|
tmp[10] = self.m[0]*self.m[5]*self.m[15] - self.m[0]*self.m[7]*self.m[13] - self.m[4]*self.m[1]*self.m[15] + self.m[4]*self.m[3]*self.m[13] + self.m[12]*self.m[1]*self.m[7] - self.m[12]*self.m[3]*self.m[5]
|
||||||
|
tmp[14] = -self.m[0]*self.m[5]*self.m[14] + self.m[0]*self.m[6]*self.m[13] + self.m[4]*self.m[1]*self.m[14] - self.m[4]*self.m[2]*self.m[13] - self.m[12]*self.m[1]*self.m[6] + self.m[12]*self.m[2]*self.m[5]
|
||||||
|
tmp[3] = -self.m[1]*self.m[6]*self.m[11] + self.m[1]*self.m[7]*self.m[10] + self.m[5]*self.m[2]*self.m[11] - self.m[5]*self.m[3]*self.m[10] - self.m[9]*self.m[2]*self.m[7] + self.m[9]*self.m[3]*self.m[6]
|
||||||
|
tmp[7] = self.m[0]*self.m[6]*self.m[11] - self.m[0]*self.m[7]*self.m[10] - self.m[4]*self.m[2]*self.m[11] + self.m[4]*self.m[3]*self.m[10] + self.m[8]*self.m[2]*self.m[7] - self.m[8]*self.m[3]*self.m[6]
|
||||||
|
tmp[11] = -self.m[0]*self.m[5]*self.m[11] + self.m[0]*self.m[7]*self.m[9] + self.m[4]*self.m[1]*self.m[11] - self.m[4]*self.m[3]*self.m[9] - self.m[8]*self.m[1]*self.m[7] + self.m[8]*self.m[3]*self.m[5]
|
||||||
|
tmp[15] = self.m[0]*self.m[5]*self.m[10] - self.m[0]*self.m[6]*self.m[9] - self.m[4]*self.m[1]*self.m[10] + self.m[4]*self.m[2]*self.m[9] + self.m[8]*self.m[1]*self.m[6] - self.m[8]*self.m[2]*self.m[5]
|
||||||
|
|
||||||
|
det = 1.0 / det
|
||||||
|
self.m[0] = tmp[0] * det
|
||||||
|
self.m[1] = tmp[1] * det
|
||||||
|
self.m[2] = tmp[2] * det
|
||||||
|
self.m[3] = tmp[3] * det
|
||||||
|
self.m[4] = tmp[4] * det
|
||||||
|
self.m[5] = tmp[5] * det
|
||||||
|
self.m[6] = tmp[6] * det
|
||||||
|
self.m[7] = tmp[7] * det
|
||||||
|
self.m[8] = tmp[8] * det
|
||||||
|
self.m[9] = tmp[9] * det
|
||||||
|
self.m[10] = tmp[10] * det
|
||||||
|
self.m[11] = tmp[11] * det
|
||||||
|
self.m[12] = tmp[12] * det
|
||||||
|
self.m[13] = tmp[13] * det
|
||||||
|
self.m[14] = tmp[14] * det
|
||||||
|
self.m[15] = tmp[15] * det
|
||||||
|
|
||||||
|
def mulm(mat4 self, mat4 B, bint inplace=False):
|
||||||
|
"""
|
||||||
|
Return a matrix that is the result of multiplying this matrix by another.
|
||||||
|
|
||||||
|
M = self * mat4 B
|
||||||
|
"""
|
||||||
|
|
||||||
|
cdef uint8_t i
|
||||||
|
cdef mat4 tmp = mat4()
|
||||||
|
|
||||||
|
tmp.m[0] = self.m[0] * B.m[0] + self.m[4] * B.m[1] + self.m[8] * B.m[2] + self.m[12] * B.m[3]
|
||||||
|
tmp.m[1] = self.m[1] * B.m[0] + self.m[5] * B.m[1] + self.m[9] * B.m[2] + self.m[13] * B.m[3]
|
||||||
|
tmp.m[2] = self.m[2] * B.m[0] + self.m[6] * B.m[1] + self.m[10] * B.m[2] + self.m[14] * B.m[3]
|
||||||
|
tmp.m[3] = self.m[3] * B.m[0] + self.m[7] * B.m[1] + self.m[11] * B.m[2] + self.m[15] * B.m[3]
|
||||||
|
tmp.m[4] = self.m[0] * B.m[4] + self.m[4] * B.m[5] + self.m[8] * B.m[6] + self.m[12] * B.m[7]
|
||||||
|
tmp.m[5] = self.m[1] * B.m[4] + self.m[5] * B.m[5] + self.m[9] * B.m[6] + self.m[13] * B.m[7]
|
||||||
|
tmp.m[6] = self.m[2] * B.m[4] + self.m[6] * B.m[5] + self.m[10] * B.m[6] + self.m[14] * B.m[7]
|
||||||
|
tmp.m[7] = self.m[3] * B.m[4] + self.m[7] * B.m[5] + self.m[11] * B.m[6] + self.m[15] * B.m[7]
|
||||||
|
tmp.m[8] = self.m[0] * B.m[8] + self.m[4] * B.m[9] + self.m[8] * B.m[10] + self.m[12] * B.m[11]
|
||||||
|
tmp.m[9] = self.m[1] * B.m[8] + self.m[5] * B.m[9] + self.m[9] * B.m[10] + self.m[13] * B.m[11]
|
||||||
|
tmp.m[10] = self.m[2] * B.m[8] + self.m[6] * B.m[9] + self.m[10] * B.m[10] + self.m[14] * B.m[11]
|
||||||
|
tmp.m[11] = self.m[3] * B.m[8] + self.m[7] * B.m[9] + self.m[11] * B.m[10] + self.m[15] * B.m[11]
|
||||||
|
tmp.m[12] = self.m[0] * B.m[12] + self.m[4] * B.m[13] + self.m[8] * B.m[14] + self.m[12] * B.m[15]
|
||||||
|
tmp.m[13] = self.m[1] * B.m[12] + self.m[5] * B.m[13] + self.m[9] * B.m[14] + self.m[13] * B.m[15]
|
||||||
|
tmp.m[14] = self.m[2] * B.m[12] + self.m[6] * B.m[13] + self.m[10] * B.m[14] + self.m[14] * B.m[15]
|
||||||
|
tmp.m[15] = self.m[3] * B.m[12] + self.m[7] * B.m[13] + self.m[11] * B.m[14] + self.m[15] * B.m[15]
|
||||||
|
|
||||||
|
if inplace:
|
||||||
|
for i in range(16):
|
||||||
|
self.m[i] = tmp.m[i]
|
||||||
|
else:
|
||||||
|
return tmp
|
||||||
|
|
||||||
|
def mulv(mat4 self, vec3 v):
|
||||||
|
"""
|
||||||
|
Return a vec3 that is the result of multiplying this matrix by a vec3.
|
||||||
|
|
||||||
|
u = self * v
|
||||||
|
"""
|
||||||
|
|
||||||
|
cdef mat4 tmp = vec3()
|
||||||
|
|
||||||
|
tmp.v[0] = v.v[0]*self.m[0] + v.v[1]*self.m[4] + v.v[2]*self.m[8] + self.m[12]
|
||||||
|
tmp.v[1] = v.v[0]*self.m[1] + v.v[1]*self.m[5] + v.v[2]*self.m[9] + self.m[13]
|
||||||
|
tmp.v[2] = v.v[0]*self.m[2] + v.v[1]*self.m[6] + v.v[2]*self.m[10] + self.m[14]
|
||||||
|
|
||||||
|
return tmp
|
||||||
|
|
||||||
|
def mulf(mat4 self, f):
|
||||||
|
"""
|
||||||
|
Return a matrix that is the result of multiplying this matrix by a scalar.
|
||||||
|
|
||||||
|
M = self * f
|
||||||
|
"""
|
||||||
|
|
||||||
|
cdef mat4 tmp = mat4()
|
||||||
|
cdef int i
|
||||||
|
|
||||||
|
for i in range(16):
|
||||||
|
tmp.m[i] = self.m[i] * f
|
||||||
|
|
||||||
|
return tmp
|
||||||
|
|
||||||
|
def __repr__(mat4 self):
|
||||||
|
lines = []
|
||||||
|
|
||||||
|
lines.append("mat4(%.1f %.1f %.1f %.1f" %(self.m[0], self.m[4], self.m[8], self.m[12]))
|
||||||
|
lines.append(" %.1f %.1f %.1f %.1f" %(self.m[1], self.m[5], self.m[9], self.m[13]))
|
||||||
|
lines.append(" %.1f %.1f %.1f %.1f" %(self.m[2], self.m[6], self.m[10], self.m[14]))
|
||||||
|
lines.append(" %.1f %.1f %.1f %.1f)" %(self.m[3], self.m[7], self.m[11], self.m[15]))
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
113
m/m.vec3.pyx
Normal file
113
m/m.vec3.pyx
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
cdef class vec3:
|
||||||
|
"""
|
||||||
|
A float 3D vector.
|
||||||
|
|
||||||
|
>>> v = vec3(1, 1, 0)
|
||||||
|
>>> w = vec3(0, 1, 1)
|
||||||
|
>>> v.length
|
||||||
|
1.4142135623730951
|
||||||
|
>>> v.dot(w)
|
||||||
|
1.0
|
||||||
|
>>> v.cross(w)
|
||||||
|
vec4(1.00, 1.00, 1.00)
|
||||||
|
>>> v + w
|
||||||
|
vec4(1.00, 2.00, 1.00)
|
||||||
|
>>> w - v
|
||||||
|
vec4(-1.00, 0.00, 1.00)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(vec3 self, *args):
|
||||||
|
"""
|
||||||
|
Create a vec3.
|
||||||
|
|
||||||
|
Accepts any number of parameters between 0 and 3 to fill the vector from the left.
|
||||||
|
"""
|
||||||
|
|
||||||
|
length = len(args)
|
||||||
|
|
||||||
|
if length == 1 and isinstance(args[0], (list, tuple)):
|
||||||
|
args = args[0]
|
||||||
|
length = len(args)
|
||||||
|
|
||||||
|
if length > 3:
|
||||||
|
raise MathError("Attempt to initialize a vec3 with %d arguments." %(length))
|
||||||
|
|
||||||
|
for i in range(3):
|
||||||
|
if i < length:
|
||||||
|
self.v[i] = args[i]
|
||||||
|
else:
|
||||||
|
self.v[i] = 0.0
|
||||||
|
|
||||||
|
def __getitem__(vec3 self, int i):
|
||||||
|
if i >= 3 or i < 0:
|
||||||
|
raise IndexError("element index out of range(3)")
|
||||||
|
|
||||||
|
return self.v[i]
|
||||||
|
|
||||||
|
def __setitem__(vec3 self, int i, float value):
|
||||||
|
if i >= 3 or i < 0:
|
||||||
|
raise IndexError("element index out of range(3)")
|
||||||
|
|
||||||
|
self.v[i] = value
|
||||||
|
|
||||||
|
def __repr__(vec3 self):
|
||||||
|
return "vec3(%.2f, %.2f, %.2f)" %(self.v[0], self.v[1], self.v[2])
|
||||||
|
|
||||||
|
def __getstate__(vec3 self):
|
||||||
|
return (self.v[0], self.v[1], self.v[2])
|
||||||
|
|
||||||
|
def __setstate__(vec3 self, state):
|
||||||
|
self.v[0] = state[0]
|
||||||
|
self.v[1] = state[1]
|
||||||
|
self.v[2] = state[2]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def length(vec3 self):
|
||||||
|
"""Contains the geometric length of the vector."""
|
||||||
|
|
||||||
|
return sqrt(self.v[0]**2 + self.v[1]**2 + self.v[2]**2)
|
||||||
|
|
||||||
|
def normalized(vec3 self):
|
||||||
|
"""Returns this vector, normalized."""
|
||||||
|
|
||||||
|
length = self.length
|
||||||
|
|
||||||
|
return vec3(self.v[0] / length, self.v[1] / length, self.v[2] / length)
|
||||||
|
|
||||||
|
def __add__(vec3 L, vec3 R):
|
||||||
|
return vec3(L.v[0] + R.v[0], L.v[1] + R.v[1], L.v[2] + R.v[2])
|
||||||
|
|
||||||
|
def __sub__(vec3 L, vec3 R):
|
||||||
|
return vec3(L.v[0] - R.v[0], L.v[1] - R.v[1], L.v[2] - R.v[2])
|
||||||
|
|
||||||
|
def __neg__(vec3 self):
|
||||||
|
return vec3(-self.v[0], -self.v[1], -self.v[2])
|
||||||
|
|
||||||
|
def dot(vec3 L, vec3 R):
|
||||||
|
"""
|
||||||
|
Returns the dot product of the two vectors.
|
||||||
|
|
||||||
|
E.g. u.dot(v) -> u . v
|
||||||
|
"""
|
||||||
|
|
||||||
|
return L.v[0] * R.v[0] + L.v[1] * R.v[1] + L.v[2] * R.v[2]
|
||||||
|
|
||||||
|
def cross(vec3 L, vec3 R):
|
||||||
|
"""
|
||||||
|
Returns the cross product of the two vectors.
|
||||||
|
|
||||||
|
E.g. u.cross(v) -> u x v
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
return vec3(L.v[1]*R.v[2] - L.v[2]*R.v[1], L.v[0]*R.v[2] - L.v[2]*R.v[0], L.v[0]*R.v[1] - L.v[1]*R.v[0])
|
||||||
|
|
||||||
|
def __mul__(vec3 L, R):
|
||||||
|
"""
|
||||||
|
Multiplication of a vec3 by a float.
|
||||||
|
|
||||||
|
The float has to be on the right.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return vec3(L.v[0] * R, L.v[1] * R, L.v[2] * R)
|
0
serial/__init__.py
Normal file
0
serial/__init__.py
Normal file
0
serial/build.sh
Executable file
0
serial/build.sh
Executable file
190
serial/com.py
Normal file
190
serial/com.py
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
#! /bin/env python3
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import time
|
||||||
|
import serial
|
||||||
|
|
||||||
|
from . import et
|
||||||
|
|
||||||
|
prefix = et.prefix
|
||||||
|
|
||||||
|
"""
|
||||||
|
===== OverTalk protocol spec =====
|
||||||
|
==== Transport layer ====
|
||||||
|
|
||||||
|
frame = [ 0x2a | destination (1) | source (1) | payload length (1) | payload (n) | checksum (1) ]
|
||||||
|
|
||||||
|
Any time an 0x2a is encountered the parser should flush its state and begin reading a new frame.
|
||||||
|
Bytes 0x2a, 0x2b, 0x11 and 0x13 are prefixed with 0x2b and inverted (XORed with 0xff) to avoid collisions.
|
||||||
|
|
||||||
|
Frames with destinations unknown to an OverShell are sent to the default interface, or dropped if
|
||||||
|
that's the interface they came from.
|
||||||
|
|
||||||
|
==== Command layer ====
|
||||||
|
Payload consists of one or more commands:
|
||||||
|
|
||||||
|
get_command = [ 0x00 | register (1) | target register (1) ]
|
||||||
|
set_command = [ 0x01 | register (1) | length (1) | value (n) ]
|
||||||
|
|
||||||
|
With a get_command the sender requests the receiver to read its own "register" and issue
|
||||||
|
a set_command that sets the sender's "target register" to that value.
|
||||||
|
|
||||||
|
A set_command does what is says on the box.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Transport:
|
||||||
|
"""
|
||||||
|
OverTalk Transport layer implementation.
|
||||||
|
|
||||||
|
Reads data from multiple interfaces and either router frames or receives them.
|
||||||
|
A received frame is then made available via a public attribute.
|
||||||
|
|
||||||
|
Interfaces are objects that implement methods read() (returns one byte of data as int), write(buffer, len),
|
||||||
|
and waiting property (contains number of bytes waiting).
|
||||||
|
"""
|
||||||
|
def __init__(self, my_id, interfaces, def_route):
|
||||||
|
"""
|
||||||
|
@param my_id OverTalk address of this Transport instance
|
||||||
|
@param interfaces a dict of interfaces keyed by their IDs
|
||||||
|
@param def_route ID of default interface for sending
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert my_id not in interfaces
|
||||||
|
assert def_route in interfaces
|
||||||
|
|
||||||
|
self._print = et.Output("over.com.Transport", default_suffix="\n", timestamp=True)
|
||||||
|
|
||||||
|
self.my_id = my_id
|
||||||
|
self.def_route = def_route
|
||||||
|
self.interfaces = interfaces
|
||||||
|
|
||||||
|
self.destination_unknown = 0
|
||||||
|
self.incoming = None
|
||||||
|
|
||||||
|
self._print("<Cg>on-line<C/>")
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self.incoming = None
|
||||||
|
|
||||||
|
for interface_id, interface in self.interfaces.items():
|
||||||
|
if interface.waiting:
|
||||||
|
interface.last_data_received = time.time()
|
||||||
|
byte = interface.read()
|
||||||
|
|
||||||
|
if byte == 0x2a:
|
||||||
|
interface.rxbuffer = []
|
||||||
|
interface.reading_escape = False
|
||||||
|
interface.reading_frame = True
|
||||||
|
|
||||||
|
if byte == 0x2b:
|
||||||
|
interface.reading_escape = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if interface.reading_escape:
|
||||||
|
byte ^= 0xff
|
||||||
|
interface.reading_escape = False
|
||||||
|
|
||||||
|
if interface.reading_frame:
|
||||||
|
interface.rxbuffer.append(byte)
|
||||||
|
|
||||||
|
if self.verify_frame(interface):
|
||||||
|
self.process_frame(interface_id, interface.rxbuffer)
|
||||||
|
interface.rxbuffer = []
|
||||||
|
|
||||||
|
def verify_frame(self, interface):
|
||||||
|
buffer_length = len(interface.rxbuffer)
|
||||||
|
|
||||||
|
if buffer_length >= 4:
|
||||||
|
# at this point we can read frame's declared size
|
||||||
|
if buffer_length >= interface.rxbuffer[3] + 5: # header (4) + payload + checksum (1)
|
||||||
|
# a frame has been read, huzzah!
|
||||||
|
interface.reading_frame = False
|
||||||
|
|
||||||
|
# checksum
|
||||||
|
if sum(interface.rxbuffer[:-1]) % 0x100 == interface.rxbuffer[-1]:
|
||||||
|
return True
|
||||||
|
|
||||||
|
else:
|
||||||
|
interface.malformed_frames += 1
|
||||||
|
self._print("broken frame received: <Cr>%s<C/>" %(interface.rxbuffer))
|
||||||
|
interface.rxbuffer = []
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def escape_frame(self, frame):
|
||||||
|
assert frame[0] == 0x2a
|
||||||
|
|
||||||
|
frame_escaped = [0x2a]
|
||||||
|
|
||||||
|
for byte in frame[1:]:
|
||||||
|
if byte in (0x2a, 0x2b, 0x11, 0x13):
|
||||||
|
frame_escaped.append(0x2b)
|
||||||
|
byte ^= 0xff
|
||||||
|
|
||||||
|
frame_escaped.append(byte)
|
||||||
|
|
||||||
|
return frame_escaped
|
||||||
|
|
||||||
|
def process_frame(self, source_interface_id, frame):
|
||||||
|
payload = frame[4:-1]
|
||||||
|
destination = frame[1]
|
||||||
|
|
||||||
|
if destination == self.my_id:
|
||||||
|
self.incoming = (frame[2], payload)
|
||||||
|
else:
|
||||||
|
if destination in self.interfaces:
|
||||||
|
self._print("routing frame to [%d]" %(destination))
|
||||||
|
self.interfaces[destination].write(self.escape_frame(frame))
|
||||||
|
else:
|
||||||
|
if source_interface_id == self.def_route:
|
||||||
|
self.destination_unknown += 1
|
||||||
|
self._print("unknown destination <Cr>%d<C/> for frame: <Cy>%s<C/>" %(destination,
|
||||||
|
repr(frame)), prefix.fail)
|
||||||
|
else:
|
||||||
|
self._print("routing frame to default route [%d]" %(self.def_route))
|
||||||
|
self.interfaces[self.def_route].write(self.escape_frame(frame))
|
||||||
|
|
||||||
|
def send_data(self, destination, data):
|
||||||
|
frame = [0x2a, destination, self.my_id, len(data)] + list(data)
|
||||||
|
frame.append(sum(frame) % 0x100)
|
||||||
|
frame = self.escape_frame(frame)
|
||||||
|
|
||||||
|
if destination in self.interfaces:
|
||||||
|
s = self.interfaces[destination]
|
||||||
|
else:
|
||||||
|
s = self.interfaces[self.def_route]
|
||||||
|
|
||||||
|
s.write(frame)
|
||||||
|
|
||||||
|
class TTL_Interface:
|
||||||
|
def __init__(self, interface="/dev/ttyUSB0", baudrate=57600):
|
||||||
|
try:
|
||||||
|
self.s = serial.Serial(interface, baudrate, timeout=1)
|
||||||
|
except serial.serialutil.SerialException:
|
||||||
|
self.s = None
|
||||||
|
|
||||||
|
self.rxbuffer = []
|
||||||
|
self.reading_escape = False
|
||||||
|
self.reading_frame = False
|
||||||
|
|
||||||
|
self.malformed_frames = 0
|
||||||
|
self.last_data_received = 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def waiting(self):
|
||||||
|
if self.s:
|
||||||
|
return self.s.inWaiting()
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
if self.s:
|
||||||
|
return ord(self.s.read())
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def write(self, data):
|
||||||
|
print("Sending:", ''.join([hex(x)[2:].zfill(2) for x in data]))
|
||||||
|
|
||||||
|
if self.s:
|
||||||
|
self.s.write(bytes(data))
|
|
@ -1,2 +0,0 @@
|
||||||
cdef class Managed:
|
|
||||||
pass
|
|
Loading…
Add table
Add a link
Reference in a new issue