| # |
| # Copyright (c) 2019 Google, LLC. |
| # All rights reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| |
| # |
| # @file |
| # Python interface for WeaveDataManagementClient |
| # |
| |
| """Weave Data Management Client interface |
| """ |
| |
| import functools |
| import sys |
| import os |
| import re |
| import copy |
| import binascii |
| import datetime |
| import time |
| import glob |
| import platform |
| import struct |
| from threading import Thread, Lock, Event |
| from ctypes import * |
| from WeaveStack import * |
| from WeaveTLV import * |
| |
| |
| __all__ = [ 'GenericTraitUpdatableDataSink', 'WDMClient' ] |
| |
| ElementTypes = { |
| 0x00:"Signed Integer 1-byte value", |
| 0x01:"Signed Integer 2-byte value", |
| 0x02:"Signed Integer 4-byte value", |
| 0x03:"Signed Integer 8-byte value", |
| 0x04:"Unsigned Integer 1-byte value", |
| 0x05:"Unsigned Integer 2-byte value", |
| 0x06:"Unsigned Integer 4-byte value", |
| 0x07:"Unsigned Integer 8-byte value", |
| 0x08:"Boolean False", |
| 0x09:"Boolean True", |
| 0x0A:"Floating Point 4-byte value", |
| 0x0B:"Floating Point 8-byte value", |
| 0x0C:"UTF-8 String 1-byte length", |
| 0x0D:"UTF-8 String 2-byte length", |
| 0x0E:"UTF-8 String 4-byte length", |
| 0x0F:"UTF-8 String 8-byte length", |
| 0x10:"Byte String 1-byte length", |
| 0x11:"Byte String 2-byte length", |
| 0x12:"Byte String 4-byte length", |
| 0x13:"Byte String 8-byte length", |
| 0x14:"Null", |
| 0x15:"Structure", |
| 0x16:"Array", |
| 0x17:"Path", |
| 0x18:"End of Collection", |
| "Signed Integer 1-byte value":0x00, |
| "Signed Integer 2-byte value":0x01, |
| "Signed Integer 4-byte value":0x02, |
| "Signed Integer 8-byte value":0x03, |
| "Unsigned Integer 1-byte value":0x04, |
| "Unsigned Integer 2-byte value":0x05, |
| "Unsigned Integer 4-byte value":0x06, |
| "Unsigned Integer 8-byte value":0x07, |
| "Boolean True":0x08, |
| "Boolean False":0x09, |
| "Floating Point 4-byte value":0x0A, |
| "Floating Point 8-byte value":0x0B, |
| "UTF-8 String 1-byte length":0x0C, |
| "UTF-8 String 2-byte length":0x0D, |
| "UTF-8 String 4-byte length":0x0E, |
| "UTF-8 String 8-byte length":0x0F, |
| "Byte String 1-byte length":0x10, |
| "Byte String 2-byte length":0x11, |
| "Byte String 4-byte length":0x12, |
| "Byte String 8-byte length":0x13, |
| "Null":0x14, |
| "Structure":0x15, |
| "Array":0x16, |
| "Path":0x17, |
| "End of Collection":0x18, |
| } |
| |
| TagControlTypes = { |
| 0x00:"Anonymous", |
| 0x20:"Context 1-byte", |
| 0x40:"Core Profile 2-byte", |
| 0x60:"Core Profile 4-byte", |
| 0x80:"Implicit Profile 2-byte", |
| 0xA0:"Implicit Profile 4-byte", |
| 0xC0:"Fully Qualified 6-byte", |
| 0xE0:"Fully Qualified 8-byte", |
| "Anonymous":0x00, |
| "Context 1-byte":0x20, |
| "Core Profile 2-byte":0x40, |
| "Core Profile 4-byte":0x60, |
| "Implicit Profile 2-byte":0x80, |
| "Implicit Profile 4-byte":0xA0, |
| "Fully Qualified 6-byte":0xC0, |
| "Fully Qualified 8-byte":0xE0, |
| } |
| |
| _CompleteFunct = CFUNCTYPE(None, c_void_p, c_void_p) |
| _ErrorFunct = CFUNCTYPE(None, c_void_p, c_void_p, c_ulong, POINTER(DeviceStatusStruct)) |
| _ConstructBytesArrayFunct = CFUNCTYPE(None, c_void_p, c_uint32) |
| |
| class GenericTraitUpdatableDataSink: |
| def __init__(self, traitInstance, wdmClient): |
| self.traitInstance = traitInstance |
| self._wdmClient = wdmClient |
| self._weaveStack = WeaveStack() |
| self._generictraitupdatabledatasinkLib = None |
| self._InitLib() |
| |
| def __del__(self): |
| self.close() |
| |
| def close(self): |
| if self.traitInstance != None: |
| res = self._weaveStack.Call( |
| lambda: self._generictraitupdatabledatasinkLib.nl_Weave_GenericTraitUpdatableDataSink_Close(self.traitInstance) |
| ) |
| if (res != 0): |
| raise self._weaveStack.ErrorToException(res) |
| self.traitInstance = None |
| |
| self._wdmClient = None |
| self._weaveStack = None |
| self._generictraitupdatabledatasinkLib = None |
| |
| def refreshData(self): |
| if self.traitInstance == None: |
| return |
| |
| self._weaveStack.CallAsync( |
| lambda: self._generictraitupdatabledatasinkLib.nl_Weave_GenericTraitUpdatableDataSink_RefreshData(self.traitInstance, self._wdmClient, self._weaveStack.cbHandleComplete, self._weaveStack.cbHandleError) |
| ) |
| |
| def setData(self, path, val, isConditional=False): |
| if self.traitInstance == None: |
| return |
| if (isConditional): |
| print "not support conditional update" |
| return |
| writer = TLVWriter() |
| writer.put(None, val) |
| |
| dataBuf = WeaveStack.ByteArrayToVoidPtr(writer.encoding) |
| dataLen = len(writer.encoding) if (writer.encoding != None) else 0 |
| |
| res = self._weaveStack.Call( |
| lambda: self._generictraitupdatabledatasinkLib.nl_Weave_GenericTraitUpdatableDataSink_SetBytes(self.traitInstance, path, dataBuf, dataLen, isConditional) |
| ) |
| |
| del writer |
| if (res != 0): |
| raise self._weaveStack.ErrorToException(res) |
| |
| def getData(self, path): |
| if self.traitInstance == None: |
| return |
| |
| data = self._getLeafBytes(path) |
| try : |
| return self._getDataHelper(data) |
| except: |
| raise ValueError('fail to get data') |
| |
| def _getDataHelper(self, data): |
| """ |
| Unpack weave tlv data from bytestring |
| Todo: create new class tlvreader to process bytestring |
| """ |
| buffer = data |
| item = {} |
| |
| ### Grab control byte and parse it |
| control_byte, = struct.unpack("<B", buffer[:1]) |
| controltypeIndex = control_byte & 0x20 |
| item["tagcontrol"] = TagControlTypes[controltypeIndex] |
| elementtypeIndex = control_byte & 0x1f |
| item["type"] = ElementTypes[elementtypeIndex] |
| buffer = buffer[1:] |
| |
| ### Grab the next few bytes as the tag |
| if "1-byte" in item["tagcontrol"]: |
| item["tag"], = struct.unpack("<B", buffer[:1]) |
| item["taglength"] = 1 |
| buffer = buffer[1:] |
| elif "2-byte" in item["tagcontrol"]: |
| item["tag"], = struct.unpack("<H", buffer[:2]) |
| item["taglength"] = 2 |
| buffer = buffer[2:] |
| elif "4-byte" in item["tagcontrol"]: |
| item["tag"], = struct.unpack("<L", buffer[:4]) |
| item["taglength"] = 4 |
| buffer = buffer[4:] |
| elif "8-byte" in item["tagcontrol"]: |
| item["tag"], = struct.unpack("<Q", buffer[:8]) |
| item["taglength"] = 8 |
| buffer = buffer[8:] |
| else: |
| item["tag"] = None |
| item["taglength"] = 0 |
| |
| ### If the element type needs a length field grab the next bytes as length |
| if "length" in item["type"]: |
| if "1-byte" in item["type"]: |
| item["datalength"], = struct.unpack("<B", buffer[:1]) |
| item["lenlength"] = 1 |
| buffer = buffer[1:] |
| elif "2-byte" in item["type"]: |
| item["datalength"], = struct.unpack("<H", buffer[:2]) |
| item["lenlength"] = 2 |
| buffer = buffer[2:] |
| elif "4-byte" in item["type"]: |
| item["datalength"], = struct.unpack("<L", buffer[:4]) |
| item["lenlength"] = 4 |
| buffer = buffer[4:] |
| elif "8-byte" in item["type"]: |
| item["datalength"], = struct.unpack("<Q", buffer[:8]) |
| item["lenlength"] = 8 |
| buffer = buffer[8:] |
| else: |
| item["lenlength"] = 0 |
| |
| ### Grab the data |
| if item["type"] == "Null" or item["type"] == "End of Collection": |
| item["value"] = None |
| elif item["type"] == "Boolean True": |
| item["value"] = True |
| elif item["type"] == "Boolean False": |
| item["value"] = False |
| elif item["type"] == "Unsigned Integer 1-byte value": |
| item["value"], = struct.unpack("<B", buffer[:1]) |
| buffer = buffer[1:] |
| elif item["type"] == "Signed Integer 1-byte value": |
| item["value"], = struct.unpack("<b", buffer[:1]) |
| buffer = buffer[1:] |
| elif item["type"] == "Unsigned Integer 2-byte value": |
| item["value"], = struct.unpack("<H", buffer[:2]) |
| buffer = buffer[2:] |
| elif item["type"] == "Signed Integer 2-byte value": |
| item["value"], = struct.unpack("<h", buffer[:2]) |
| buffer = buffer[2:] |
| elif item["type"] == "Unsigned Integer 4-byte value": |
| item["value"], = struct.unpack("<L", buffer[:4]) |
| buffer = buffer[4:] |
| elif item["type"] == "Signed Integer 4-byte value": |
| item["value"], = struct.unpack("<l", buffer[:4]) |
| buffer = buffer[4:] |
| elif item["type"] == "Unsigned Integer 8-byte value": |
| item["value"], = struct.unpack("<Q", buffer[:8]) |
| buffer = buffer[8:] |
| elif item["type"] == "Signed Integer 8-byte value": |
| item["value"], = struct.unpack("<q", buffer[:8]) |
| buffer = buffer[8:] |
| elif item["type"] == "Floating Point 4-byte value": |
| item["value"], = struct.unpack("<f", buffer[:4]) |
| buffer = buffer[4:] |
| elif item["type"] == "Floating Point 8-byte value": |
| item["value"], = struct.unpack("<d", buffer[:8]) |
| buffer = buffer[8:] |
| elif "String" in item["type"]: |
| item["value"], = struct.unpack("<%ds" % item["datalength"], buffer[:item["datalength"]]) |
| buffer = buffer[item["datalength"]:] |
| else: |
| raise ValueError('Attempt to TLV decode unsupported value') |
| |
| return item["value"] |
| |
| def _getLeafBytes(self, path): |
| def HandleConstructBytesArray(dataBuf, dataLen): |
| self._weaveStack.callbackRes = WeaveStack.VoidPtrToByteArray(dataBuf, dataLen) |
| |
| cbHandleConstructBytesArray = _ConstructBytesArrayFunct(HandleConstructBytesArray) |
| if self.traitInstance != None: |
| return self._weaveStack.Call( |
| lambda: self._generictraitupdatabledatasinkLib.nl_Weave_GenericTraitUpdatableDataSink_GetLeafBytes(self.traitInstance, path, cbHandleConstructBytesArray) |
| ) |
| |
| def getBytes(self, path): |
| def HandleConstructBytesArray(dataBuf, dataLen): |
| self._weaveStack.callbackRes = WeaveStack.VoidPtrToByteArray(dataBuf, dataLen) |
| |
| cbHandleConstructBytesArray = _ConstructBytesArrayFunct(HandleConstructBytesArray) |
| if self.traitInstance != None: |
| return self._weaveStack.Call( |
| lambda: self._generictraitupdatabledatasinkLib.nl_Weave_GenericTraitUpdatableDataSink_GetBytes(self.traitInstance, path, cbHandleConstructBytesArray) |
| ) |
| # ----- Private Members ----- |
| def _InitLib(self): |
| if (self._generictraitupdatabledatasinkLib == None): |
| self._generictraitupdatabledatasinkLib = CDLL(self._weaveStack.LocateWeaveDLL()) |
| self._generictraitupdatabledatasinkLib.nl_Weave_GenericTraitUpdatableDataSink_RefreshData.argtypes = [ c_void_p, c_void_p, _CompleteFunct, _ErrorFunct ] |
| self._generictraitupdatabledatasinkLib.nl_Weave_GenericTraitUpdatableDataSink_RefreshData.restype = c_uint32 |
| self._generictraitupdatabledatasinkLib.nl_Weave_GenericTraitUpdatableDataSink_SetBytes.argtypes = [ c_void_p, c_char_p, c_void_p, c_uint32, c_bool ] |
| self._generictraitupdatabledatasinkLib.nl_Weave_GenericTraitUpdatableDataSink_SetBytes.restype = c_uint32 |
| |
| self._generictraitupdatabledatasinkLib.nl_Weave_GenericTraitUpdatableDataSink_GetLeafBytes.argtypes = [ c_void_p, c_char_p, _ConstructBytesArrayFunct ] |
| self._generictraitupdatabledatasinkLib.nl_Weave_GenericTraitUpdatableDataSink_GetLeafBytes.restype = c_uint32 |
| self._generictraitupdatabledatasinkLib.nl_Weave_GenericTraitUpdatableDataSink_GetBytes.argtypes = [ c_void_p, c_char_p, _ConstructBytesArrayFunct ] |
| self._generictraitupdatabledatasinkLib.nl_Weave_GenericTraitUpdatableDataSink_GetBytes.restype = c_uint32 |
| |
| class WDMClient(): |
| def __init__(self): |
| self._wdmClient = None |
| self._weaveStack = WeaveStack() |
| self._datamanagmentLib = None |
| self._traitTable = {} |
| self._InitLib() |
| |
| wdmClient = c_void_p(None) |
| res = self._datamanagmentLib.nl_Weave_DataManagementClient_NewWDMClient(pointer(wdmClient), self._weaveStack.devMgr) |
| if (res != 0): |
| raise self._weaveStack.ErrorToException(res) |
| |
| self._wdmClient = wdmClient |
| |
| def __del__(self): |
| self.close() |
| |
| def close(self): |
| if (self._wdmClient != None): |
| for val in self._traitTable.values(): |
| val.close() |
| self._traitTable.clear() |
| self._traitTable = None |
| self._weaveStack.Call( |
| lambda: self._datamanagmentLib.nl_Weave_DataManagementClient_DeleteWDMClient(self._wdmClient) |
| ) |
| self._wdmClient = None |
| |
| self._weaveStack = None |
| self._datamanagmentLib = None |
| |
| def newDataSink(self, resourceType, resourceId, resourceIdLen, profileId, instanceId, path): |
| traitInstance = c_void_p(None) |
| if (self._wdmClient == None): |
| print "wdmClient is not ready" |
| return |
| |
| res = self._weaveStack.Call( |
| lambda: self._datamanagmentLib.nl_Weave_DataManagementClient_NewDataSink(self._wdmClient, resourceType, resourceId, resourceIdLen, profileId, instanceId, path, pointer(traitInstance)) |
| ) |
| if (res != 0): |
| raise self._weaveStack.ErrorToException(res) |
| |
| addr = id(traitInstance) |
| if addr not in self._traitTable.keys(): |
| self._traitTable[addr] = GenericTraitUpdatableDataSink(traitInstance, self._wdmClient) |
| |
| return self._traitTable[addr] |
| |
| def flushUpdate(self): |
| if (self._wdmClient == None): |
| print "wdmClient is not ready" |
| return |
| |
| self._weaveStack.CallAsync( |
| lambda: self._datamanagmentLib.nl_Weave_DataManagementClient_FlushUpdate(self._wdmClient, self._weaveStack.cbHandleComplete, self._weaveStack.cbHandleError) |
| ) |
| |
| def refreshData(self): |
| if (self._wdmClient == None): |
| print "wdmClient is not ready" |
| return |
| |
| self._weaveStack.CallAsync( |
| lambda: self._datamanagmentLib.nl_Weave_DataManagementClient_RefreshData(self._wdmClient, self._weaveStack.cbHandleComplete, self._weaveStack.cbHandleError) |
| ) |
| |
| # ----- Private Members ----- |
| def _InitLib(self): |
| if (self._datamanagmentLib == None): |
| self._datamanagmentLib = CDLL(self._weaveStack.LocateWeaveDLL()) |
| |
| self._datamanagmentLib.nl_Weave_DataManagementClient_Init.argtypes = [ ] |
| self._datamanagmentLib.nl_Weave_DataManagementClient_Init.restype = c_uint32 |
| |
| self._datamanagmentLib.nl_Weave_DataManagementClient_Shutdown.argtypes = [ ] |
| self._datamanagmentLib.nl_Weave_DataManagementClient_Shutdown.restype = c_uint32 |
| |
| self._datamanagmentLib.nl_Weave_DataManagementClient_NewWDMClient.argtypes = [ POINTER(c_void_p), c_void_p] |
| self._datamanagmentLib.nl_Weave_DataManagementClient_NewWDMClient.restype = c_uint32 |
| |
| self._datamanagmentLib.nl_Weave_DataManagementClient_DeleteWDMClient.argtypes = [ c_void_p ] |
| self._datamanagmentLib.nl_Weave_DataManagementClient_DeleteWDMClient.restype = c_uint32 |
| |
| self._datamanagmentLib.nl_Weave_DataManagementClient_NewDataSink.argtypes = [ c_void_p, c_uint16, c_void_p, c_uint32, c_uint32, c_uint64, c_char_p, c_void_p ] |
| self._datamanagmentLib.nl_Weave_DataManagementClient_NewDataSink.restype = c_uint32 |
| |
| self._datamanagmentLib.nl_Weave_DataManagementClient_FlushUpdate.argtypes = [ c_void_p, _CompleteFunct, _ErrorFunct ] |
| self._datamanagmentLib.nl_Weave_DataManagementClient_FlushUpdate.restype = c_uint32 |
| |
| self._datamanagmentLib.nl_Weave_DataManagementClient_RefreshData.argtypes = [ c_void_p, _CompleteFunct, _ErrorFunct ] |
| self._datamanagmentLib.nl_Weave_DataManagementClient_RefreshData.restype = c_uint32 |
| |
| res = self._datamanagmentLib.nl_Weave_DataManagementClient_Init() |
| if (res != 0): |
| raise self._weaveStack.ErrorToException(res) |