Skip to content

Commit

Permalink
Refactor code to the new CommStack API
Browse files Browse the repository at this point in the history
  • Loading branch information
ivankravets committed Jan 22, 2016
1 parent c52b28f commit d6afc38
Show file tree
Hide file tree
Showing 6 changed files with 277 additions and 86 deletions.
2 changes: 1 addition & 1 deletion smartanthill/cc/embedded
94 changes: 50 additions & 44 deletions smartanthill/device/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,37 @@ def __init__(self, id_, options):
self.options = options
self.board = BoardFactory.newBoard(options['boardId'])

def get_id(self):
return self.id_

def get_name(self):
return self.options.get(
"name", "Device #%d, %s" % (self.id_, self.board.get_name()))

@staticmethod
def get_config_key_by_id(device_id):
return "services.device.options.devices.%d" % device_id

@staticmethod
def get_config_dir_by_id(device_id):
return join(ConfigProcessor().get("workspace"),
"devices", "%d") % (device_id,)

def get_conf_dir(self):
path = self.get_config_dir_by_id(self.id_)
if not isdir(path):
makedirs(path)
return path

def get_encryption_key(self): # TODO
return bytearray(range(0, 16))

def is_retransmitter(self): # TODO
return self.options.get("retransmitter", False)

def is_enabled(self):
return self.options.get("enabled", True)

@memoized
def get_bodyparts(self):
return bodyparts_to_objects(
Expand All @@ -65,12 +96,26 @@ def get_buses(self):
join(get_service_named("sas").workspace_dir, "transports")
)

def get_id(self):
return self.id_
def get_settings_hash(self):
settings = deepcopy(self.options)
for key in ["name", "prevId", "enabled", "firmware", "status"]:
if key in settings:
del settings[key]
for items in (settings.get("bodyparts", []),
settings.get("buses", [])):
for item in items:
del item['name']
settings['currentFirmwareVersion'] = FIRMWARE_VERSION[:2]
return sha1(dumps(settings, sort_keys=True)).hexdigest()

def get_name(self):
return self.options.get(
"name", "Device #%d, %s" % (self.id_, self.board.get_name()))
def get_firmware_settings_hash(self):
return self.options.get("firmware", {}).get("settingsHash")

def get_status(self):
if self.get_settings_hash() != self.get_firmware_settings_hash():
return DeviceStatus.WAITFORTRAINIT.value
else:
return DeviceStatus.IDLE.value

def execute_bodypart(self, name, request_fields):
bodypart = None
Expand Down Expand Up @@ -122,42 +167,3 @@ def _on_result(result, project_dir):
)
d.addBoth(_on_result, project_dir)
return d

def get_conf_dir(self):
path = self.get_config_dir_by_id(self.id_)
if not isdir(path):
makedirs(path)
return path

@staticmethod
def get_config_key_by_id(device_id):
return "services.device.options.devices.%d" % device_id

@staticmethod
def get_config_dir_by_id(device_id):
return join(ConfigProcessor().get("workspace"),
"devices", "%d") % (device_id,)

def is_enabled(self):
return self.options.get("enabled", True)

def get_settings_hash(self):
settings = deepcopy(self.options)
for key in ["name", "prevId", "enabled", "firmware", "status"]:
if key in settings:
del settings[key]
for items in (settings.get("bodyparts", []),
settings.get("buses", [])):
for item in items:
del item['name']
settings['currentFirmwareVersion'] = FIRMWARE_VERSION[:2]
return sha1(dumps(settings, sort_keys=True)).hexdigest()

def get_firmware_settings_hash(self):
return self.options.get("firmware", {}).get("settingsHash")

def get_status(self):
if self.get_settings_hash() != self.get_firmware_settings_hash():
return DeviceStatus.WAITFORTRAINIT.value
else:
return DeviceStatus.IDLE.value
127 changes: 116 additions & 11 deletions smartanthill/network/commstack.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,27 @@

from binascii import hexlify
from os.path import exists, join
from struct import pack, unpack

from platformio.util import get_systype
from twisted.internet import protocol, reactor
from twisted.python.filepath import FilePath
from twisted.python.util import sibpath

from smartanthill.configprocessor import ConfigProcessor
from smartanthill.exception import NetworkCommStackServerNotExists
from smartanthill.log import Logger
from smartanthill.network.protocol import (CommStackClientProtocol,
CommStackProcessProtocol,
ControlMessage)
from smartanthill.service import SAMultiService
from smartanthill.util import get_service_named
from smartanthill.util import get_service_named, load_config, singleton


class CommStackServerService(SAMultiService):

def __init__(self, name, options):
assert set(["port", "eeprom_path"]) <= set(options.keys())
assert "port" in options
SAMultiService.__init__(self, name, options)
self._litemq = get_service_named("litemq")
self._process = None
Expand All @@ -45,8 +48,7 @@ def startService(self):

self._process = reactor.spawnProcess(
p, self.get_server_bin(),
["--port=%d" % self.options['port'],
"--psp=%s" % self.options['eeprom_path']]
["--port=%d" % self.options['port']]
)

SAMultiService.startService(self)
Expand Down Expand Up @@ -96,11 +98,6 @@ def __init__(self, name):
self._litemq = get_service_named("litemq")
self._protocol = None

def buildProtocol(self, addr):
self._protocol = CommStackClientProtocol()
self._protocol.factory = self
return self._protocol

def startFactory(self):
self._litemq.consume(
"network",
Expand All @@ -118,6 +115,79 @@ def stopFactory(self):
for direction in ("in", "out"):
self._litemq.unconsume("network", "%s.%s" % (self.name, direction))

def buildProtocol(self, addr):
self._protocol = CommStackClientProtocol()
self._protocol.factory = self

self._init_devices()

return self._protocol

def _init_devices(self):
devices = get_service_named("device").get_devices()

# initialise linked devices
counter = 0
for (id_, device) in devices.iteritems():
self._protocol.send_data(
CommStackClientProtocol.COMMLAYER_FROM_CU_STATUS_INITIALIZER,
counter,
self._get_device_payload(device)
)
counter += 1

# finish initialisation
self._protocol.send_data(
CommStackClientProtocol.COMMLAYER_FROM_CU_STATUS_INITIALIZER_LAST,
counter
)

def _get_device_payload(self, device):
device_id = device.get_id()
payload = bytearray([device_id & 0xFF, device_id >> 8])
payload.extend(device.get_encryption_key())
payload.append(1 if device.is_retransmitter() else 0)
payload.append(len(device.get_buses())) # bus_count
payload.append(len(device.get_buses())) # TODO bus_type_count
for bus in device.get_buses():
payload.append(0) # TODO append valid bus type
return payload

def on_initialization_done(self, total_inited, error_code):
assert error_code == CommStackClientProtocol.COMMLAYER_TO_CU_STATUS_OK
assert len(get_service_named("device").get_devices()) == total_inited

def on_status_sync_request(self, address, command, data):
key = data[:3]
if command == CommStackClientProtocol.REQUEST_TO_CU_READ_DATA:
_read_data = CommStackStorage().read(key)
payload = bytearray(
[CommStackClientProtocol.RESPONSE_FROM_CU_READ_DATA])
payload.extend(key)
payload.extend(pack("<H", len(_read_data)))
payload.extend(_read_data)
self._protocol.send_data(
CommStackClientProtocol.COMMLAYER_FROM_CU_STATUS_SYNC_RESPONSE,
address,
payload
)
elif command == CommStackClientProtocol.REQUEST_TO_CU_WRITE_DATA:
_write_size = unpack("<H", data[3:5])[0]
_write_data = (data[5:5 + _write_size]
if _write_size else bytearray())
_written_size = CommStackStorage().write(key, _write_data)
payload = bytearray(
[CommStackClientProtocol.RESPONSE_FROM_CU_WRITE_DATA])
payload.extend(key)
payload.extend(pack("<H", _written_size))
self._protocol.send_data(
CommStackClientProtocol.COMMLAYER_FROM_CU_STATUS_SYNC_RESPONSE,
address,
payload
)
else:
raise NotImplementedError("Sync request command %d" % command)

def from_client_callback(self, message, properties):
self.log.debug("Incoming from Client: %s and properties=%s" %
(message, properties))
Expand All @@ -128,7 +198,7 @@ def from_client_callback(self, message, properties):
data.append(0x02) # SACCP_NEW_PROGRAM
data.extend(message.data)
self._protocol.send_data(
CommStackClientProtocol.PACKET_DIRECTION_CLIENT_TO_COMMSTACK,
CommStackClientProtocol.COMMLAYER_FROM_CU_STATUS_FOR_SLAVE,
message.destination,
data
)
Expand All @@ -148,7 +218,7 @@ def from_hub_callback(self, message, properties):
self.log.debug("Incoming from Hub: %s and properties=%s" %
(hexlify(message), properties))
self._protocol.send_data(
CommStackClientProtocol.PACKET_DIRECTION_HUB_TO_COMMSTACK,
CommStackClientProtocol.COMMLAYER_FROM_CU_STATUS_FROM_SLAVE,
0, # bus_id, @TODO (0x0, 0x0)
message
)
Expand All @@ -158,3 +228,38 @@ def to_hub_callback(self, data):
self.log.debug("Outgoing to Hub: %s" % hexlify(data))
self._litemq.produce(
"network", "commstack->hub", data, dict(binary=True))


@singleton
class CommStackStorage():

def __init__(self):
self.dat_file = FilePath(join(
ConfigProcessor().get("workspace"), "commstack.dat"))

if self.dat_file.isfile():
self._data = load_config(self.dat_file.path)
else:
self._data = {}

def __str__(self):
return str(self._data)

@staticmethod
def key_to_index(key):
assert isinstance(key, bytearray)
assert len(key) == 3
_key = key[:]
_key.insert(0, 0)
return unpack("<I", _key)[0]

def read(self, key):
index = self.key_to_index(key)
if index in self._data:
return self._data[index]
else:
return bytearray()

def write(self, key, value):
self._data[self.key_to_index(key)] = value
return len(value)

0 comments on commit d6afc38

Please sign in to comment.