Yet Another WebIOPi+
 All Classes Namespaces Files Functions Variables Macros Pages
__init__.py
Go to the documentation of this file.
1 # Copyright 2014 Andreas Riegg - t-h-i-n-x.net
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 #
15 # Changelog
16 #
17 # 1.0 2014-06-26 Initial release.
18 # 1.1 2014-08-28 Added bit access.
19 # 1.2 2014-08-31 Added NON-REST multiple read/write byte methods
20 # to speed up direct Python access.
21 # 1.3 2014-11-13 Changed parameter order for writeMemoryBytes and
22 # optimized it.
23 # 1.4 2014-12-08 Simplified multiple read/write byte methods and
24 # made start/stop bounds checking more strict.
25 # Added REST mapping for multiple bytes reading.
26 # Made addressing scheme uniform for all slot types.
27 #
28 # Usage remarks
29 #
30 # - The smallest possible memory unit is 1 byte (8 bits)
31 # - Addressed slots can be
32 # - bits ( 1 bit)
33 # - bytes ( 8 bits)
34 # - words (16 bits)
35 # - longs (32 bits)
36 # - All memory address slots are mapped strictly sequential in ascending
37 # order like channel numbers starting at 0 with MSB first for non
38 # single-bit slots. This results in the following address slot mapping:
39 # |<- bit 0 bit 31 ->|
40 # 01010101010101010101010101010101
41 # |byte 0| byte 1| byte 2| byte 3|
42 # | -- word 0 -- | -- word 1 -- |
43 # | ---------- long 0 ---------- |
44 # - Where applicable, start and stop have the same meaning as range and
45 # list slices in Python. Start is included, stop is excluded.
46 #
47 
48 from webiopi.decorators.rest import request, response
49 from webiopi.utils.types import toint, M_JSON
50 
51 class Memory():
52 
53  def __init__(self, byteCount):
54  self._byteCount = byteCount
55 
56 
57 #---------- Abstraction framework contracts ----------
58 
59  def __family__(self):
60  return "Memory"
61 
62 #---------- Memory abstraction REST implementation ----------
63 
64  @request("GET", "memory/bit/*")
65  @response(contentType=M_JSON)
66  def memoryBitWildcard(self):
67  values = {}
68  for i in range(self.byteCount()):
69  valbyte = self.readMemoryByte(i)
70  for j in range(8):
71  position = 7 - j
72  values[i*8 + j] = "%d" % ((valbyte & (1 << position)) >> position)
73  return values
74 
75  # {
76  # "0": "0",
77  # "1": "0",
78  # "2": "0",
79  # "3": "1"
80  # "4": "0"
81  # "5": "0"
82  # "6": "1"
83  # "7": "0"
84  # ...
85  # }
86 
87  @request("GET", "memory/byte/*")
88  @response(contentType=M_JSON)
89  def memoryByteWildcard(self):
90  values = {}
91  byteValues = self.readMemoryBytes()
92  for i in range(len(byteValues)):
93  values[i] = "0x%02X" % byteValues[i]
94  return values
95 
96  # {
97  # "0": "0x12",
98  # "1": "0x34",
99  # "2": "0xDE",
100  # "3": "0xFF"
101  # }
102 
103  @request("GET", "memory/bytes/%(bounds)s")
104  @response(contentType=M_JSON)
105  def memoryBytes(self, bounds):
106  (start, stop) = bounds.split(",")
107  start = toint(start)
108  stop = toint(stop)
109  values = {}
110  byteValues = self.readMemoryBytes(start, stop)
111  for i in range(start, stop):
112  values[i] = "0x%02X" % byteValues[i - start]
113  return values
114 
115  # {
116  # "1": "0x34",
117  # "2": "0xDE",
118  # }
119 
120  @request("GET", "memory/word/*")
121  @response(contentType=M_JSON)
123  values = {}
124  for i in range(self.wordCount()):
125  values[i] = "0x%04X" % self.readMemoryWord(i)
126  return values
127 
128  # {
129  # "0": "0x1234",
130  # "1": "0xDEFF"
131  # }
132 
133  @request("GET", "memory/long/*")
134  @response(contentType=M_JSON)
136  values = {}
137  for i in range(self.longCount()):
138  values[i] = "0x%08X" % self.readMemoryLong(i)
139  return values
140 
141  # {
142  # "0": "0x1234DEFF"
143  # }
144 
145  @request("GET", "memory/bit/count")
146  @response("%d")
147  def bitCount(self):
148  return self._byteCount * 8
149 
150  @request("GET", "memory/byte/count")
151  @response("%d")
152  def byteCount(self):
153  return self._byteCount
154 
155  @request("GET", "memory/word/count")
156  @response("%d")
157  def wordCount(self):
158  return self._byteCount >> 1
159 
160  @request("GET", "memory/long/count")
161  @response("%d")
162  def longCount(self):
163  return self._byteCount >> 2
164 
165  @request("GET", "memory/bit/%(address)s")
166  @response("%d")
167  def readMemoryBit(self, address):
168  address = toint(address)
169  self.checkBitAddress(address)
170  return self.__readMemoryBit__(address)
171 
172  @request("POST", "memory/bit/%(address)s/%(value)s")
173  @response("%d")
174  def writeMemoryBit(self, address, value):
175  address = toint(address)
176  self.checkBitAddress(address)
177  value = toint(value)
178  self.checkBitValue(value)
179  self.__writeMemoryBit__(address, value)
180  return self.readMemoryBit(address)
181 
182  @request("GET", "memory/byte/%(address)s")
183  @response("0x%02X")
184  def readMemoryByte(self, address):
185  address = toint(address)
186  self.checkByteAddress(address)
187  return self.__readMemoryByte__(address)
188 
189  @request("POST", "memory/byte/%(address)s/%(value)s")
190  @response("0x%02X")
191  def writeMemoryByte(self, address, value):
192  address = toint(address)
193  self.checkByteAddress(address)
194  value = toint(value)
195  self.checkByteValue(value)
196  self.__writeMemoryByte__(address, value)
197  return self.readMemoryByte(address)
198 
199  @request("GET", "memory/word/%(address)s")
200  @response("0x%04X")
201  def readMemoryWord(self, address):
202  address = toint(address)
203  self.checkWordAddress(address)
204  return self.__readMemoryWord__(address)
205 
206  @request("POST", "memory/word/%(address)s/%(value)s")
207  @response("0x%04X")
208  def writeMemoryWord(self, address, value):
209  address = toint(address)
210  self.checkWordAddress(address)
211  value = toint(value)
212  self.checkWordValue(value)
213  self.__writeMemoryWord__(address, value)
214  return self.readMemoryWord(address)
215 
216  @request("GET", "memory/long/%(address)s")
217  @response("0x%08X")
218  def readMemoryLong(self, address):
219  address = toint(address)
220  self.checkLongAddress(address)
221  return self.__readMemoryLong__(address)
222 
223  @request("POST", "memory/long/%(address)s/%(value)s")
224  @response("0x%08X")
225  def writeMemoryLong(self, address, value):
226  address = toint(address)
227  self.checkLongAddress(address)
228  value = toint(value)
229  self.checkLongValue(value)
230  self.__writeMemoryLong__(address, value)
231  return self.readMemoryLong(address)
232 
233 #---------- Memory abstraction NON-REST implementation ----------
234 
235  def readMemoryBytes(self, start=0, stop=None):
236  maxCount = self.byteCount()
237  if stop is None:
238  stop = maxCount
239  self.checkByteAddress(start)
240  self.checkStopByteAddress(stop)
241  byteValues = []
242  if start > stop:
243  raise ValueError("Stop address must be >= start address")
244  for i in range(start, stop):
245  byteValues.append(self.readMemoryByte(i))
246  return byteValues
247 
248  def writeMemoryBytes(self, start=0, byteValues=[]):
249  self.checkByteAddress(start)
250  stop = start + len(byteValues)
251  self.checkStopByteAddress(stop)
252  i = 0
253  for byte in byteValues: # do nothing if list is empty
254  position = i + start
255  self.writeMemoryByte(position, byte)
256  i += 1
257 
258 
259 #---------- Memory abstraction contracts ----------
260 
261  def __readMemoryByte__(self, address):
262  raise NotImplementedError
263 
264  def __writeMemoryByte__(self, address, value):
265  raise NotImplementedError
266 
267 #---------- Memory abstraction contracts with default implementations ---------
268 
269  def __readMemoryBit__(self, address):
270  byteAddress, rawPosition = divmod(address, 8)
271  bitPosition = 7 - rawPosition
272  return (self.__readMemoryByte__(byteAddress) & (1 << bitPosition)) >> bitPosition
273 
274  def __writeMemoryBit__(self, address, value):
275  byteAddress, rawPosition = divmod(address, 8)
276  bitPosition = 7 - rawPosition
277  changeMask = 1 << bitPosition
278  byteValue = self.__readMemoryByte__(byteAddress)
279  if value:
280  byteValue |= changeMask
281  else:
282  byteValue &= ~changeMask
283  self.__writeMemoryByte__(byteAddress, byteValue)
284 
285  def __readMemoryWord__(self, address):
286  byte0 = self.__readMemoryByte__(address * 2)
287  byte1 = self.__readMemoryByte__((address * 2) + 1)
288  return (byte0 << 8) + byte1
289 
290  def __writeMemoryWord__(self, address, value):
291  byte0 = (value >> 8) & 0xFF
292  byte1 = value & 0xFF
293  self.__writeMemoryByte__(address * 2, byte0)
294  self.__writeMemoryByte__((address * 2) + 1, byte1)
295 
296  def __readMemoryLong__(self, address):
297  byte0 = self.__readMemoryByte__(address * 4)
298  byte1 = self.__readMemoryByte__((address * 4) + 1)
299  byte2 = self.__readMemoryByte__((address * 4) + 2)
300  byte3 = self.__readMemoryByte__((address * 4) + 3)
301  return (byte0 << 24) + (byte1 << 16) + (byte2 << 8) + byte3
302 
303  def __writeMemoryLong__(self, address, value):
304  byte0 = (value >> 24) & 0xFF
305  byte1 = (value >> 16) & 0xFF
306  byte2 = (value >> 8) & 0xFF
307  byte3 = value & 0xFF
308  self.__writeMemoryByte__(address * 4, byte0)
309  self.__writeMemoryByte__((address * 4) + 1, byte1)
310  self.__writeMemoryByte__((address * 4) + 2, byte2)
311  self.__writeMemoryByte__((address * 4) + 3, byte3)
312 
313 
314 #---------- Value checks ----------
315 
316  def checkBitAddress(self, address):
317  if not 0 <= address < self.byteCount() * 8:
318  raise ValueError("Bit address [%d] out of range [%d..%d]" % (address, 0, (self.byteCount() * 8) - 1))
319 
320  def checkBitValue(self, value):
321  if not value in range(2):
322  raise ValueError("Bit value [%d] out of range [0..1]" % value)
323 
324  def checkByteAddress(self, address):
325  if not 0 <= address < self.byteCount():
326  raise ValueError("Byte address [%d] out of range [%d..%d]" % (address, 0, self.byteCount() - 1))
327 
328  def checkStopByteAddress(self, address):
329  if not 0 <= address <= self.byteCount():
330  raise ValueError("Stop byte address [%d] out of range [%d..%d]" % (address, 0, self.byteCount()))
331 
332  def checkByteValue(self, value):
333  if not value in range(0x00,0xFF + 1):
334  raise ValueError("Byte value [0x%02X] out of range [0x%02X..0x%02X]" % (value, 0x00,0xFF))
335 
336  def checkWordAddress(self, address):
337  if not 0 <= address < self.wordCount():
338  raise ValueError("Word address [%d] out of range [%d..%d]" % (address, 0, (self.wordCount() - 1)))
339 
340  def checkWordValue(self, value):
341  if not value in range(0x00,0xFFFF + 1):
342  raise ValueError("Word value [0x%04X] out of range [0x%04X..0x%04X]" % (value, 0x00,0xFFFF))
343 
344  def checkLongAddress(self, address):
345  if not 0 <= address < self.longCount():
346  raise ValueError("Long address [%d] out of range [%d..%d]" % (address, 0, (self.longCount() - 1)))
347 
348  def checkLongValue(self, value):
349  if not value in range(0x00,0xFFFFFFFF + 1):
350  raise ValueError("Long value [0x%08X] out of range [0x%08X..0x%08X]" % (value, 0x00,0xFFFFFFFF))
351 
352 
353 #---------- Driver lookup ----------
354 
355 DRIVERS = {}
356 DRIVERS["filememory"] = ["PICKLEFILE"]
357 DRIVERS["at24"] = ["EE24BASIC", "EE24X32", "EE24X64", "EE24X128", "EE24X256", "EE24X512", "EE24X1024_2"]
358 
tuple response
Definition: coap-client.py:9