pyP2Monitor  1.0.0
Monitor a P2 furnace activity reading data on serial port
 All Data Structures Namespaces Functions Variables Groups Pages
p2com.py
1 # -*- coding: utf-8 -*-#
2 
3 # Copyright 2013, 2014 Weber Yann, Weber Laurent
4 #
5 # This file is part of pyP2Monitor.
6 #
7 # pyP2Monitor is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # pyP2Monitor is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with pyP2Monitor. If not, see <http://www.gnu.org/licenses/>.
19 #
20 
21 ##@package p2com Package handling serial port I/O with the furnace.
22 #
23 # This package define functions helping to read and write data to the Furnace.
24 #
25 
26 import sys
27 import serial
28 import logging
29 
30 import p2msg
31 from p2msg import P2Msg
32 
33 import utils
34 
35 ##Use to log
36 #@see utils.getLogger()
37 logger = utils.getLogger()
38 
39 ##Class handling I/O exception
40 #
41 # This class is an exception class used to handle serial port I/O errors such as timeout.
42 # @ingroup lowlevel
43 class P2ComError(Exception):
44 
45  ##Stores string representation for an error code.
46  ERR_STR = { 10: "Incomplete or timeout header recv",
47  11: "Timeout on data size recv",
48  12: "Incomplete or timeout data recv",
49  13: "Incomplete message or checksum or timeout on checksum recv",
50  2 : "Invalid checksum",
51  9 : "Global timeout, no frame received"}
52 
53  ##Error code for timeout
54  ERR_TIMEOUT = 9
55  ##Error code for header receive
56  ERR_HEADER = 10
57  ##Error code for invalid data size
58  ERR_DATASZ = 11
59  ##Error code for global data error
60  ERR_DATA = 12
61  ##Error code for invalid checksum on received frame
62  ERR_CHKSUM_RECV = 13
63  ##Error code for checksum error
64  ERR_CHKSUM = 2
65 
66  ##Instanciate a P2ComError object
67  #
68  # @param errno The error code
69  # @param data The data received on serial port
70  def __init__(self,errno, data = None):
71 
72  ##The associated error code
73  self.errno = errno
74  ##The associaed datas
75  self.data = data
76 
77  ##Cast a P2ComError into a string
78  def __str__(self):
79  return "Error "+str(self.errno)+" : "+P2ComError.ERR_STR[self.errno]
80 
81  ##Return the associated error code
82  def getErrno(self):
83  return self.errno
84 
85  ##Return the associated error string
86  def getErrStr(self):
87  return P2ComError.ERR_STR[self.errno]
88 
89  ##Return the associated data
90  def getData(self):
91  return self.data
92 
93 
94 
95 ##The class managing the communication with the furnace on serial port
96 # @ingroup lowlevel
97 class P2Com:
98 
99 
100  ##Instanciate a new P2Com object and open the serial port with the good parameters
101  #
102  #@param portFile The file name of the serial port (/dev/ttyS0 by default)
103  def __init__(self, portFile):
104  ##A small timeout
105  #
106  # This small tiemout is used as time between each read retry
107  self.timeout = 0.01
108  ##Read fail timeout
109  self.failTimeout = 10
110 
111  ##The serial port object
112  #self.ser = serial.Serial(portFile,9600,serial.EIGHTBITS,serial.PARITY_NONE,serial.STOPBITS_ONE,self.timeout,)
113  self.ser = serial.Serial(portFile,9600,serial.EIGHTBITS,serial.PARITY_NONE,serial.STOPBITS_ONE,self.timeout,)
114 
115  ##The last char of a sended frame "\r"
116  self.FRAME_END=0x0D;
117  ##The last char sended by the furnace in a frame (obsolete and false)
118  self.RECV_END=231;
119 
120  ##Return the serial port object
121  def getCom(self):
122  return self.ser
123 
124  ##Obsolete function (the checksum is not processed here now)
125  #
126  # Process a checksum given a message
127  def checksum(msg):
128  """@todo
129  Remove P2Com.checksum()
130  """
131  #Store the sum
132  buff = 0
133  #Work var
134  tmp = msg
135 
136  while len(tmp) > 0:
137  #Add to buff a new byte (the two last char of the string)
138  buff += int(tmp[-2:],16)
139  #Remove the byte from tmp
140  tmp=tmp[:-3]
141 
142  #Return buff converted as a 4 digits hex number
143  return "%04X" % buff
144 
145  ##Write raw datas on to the furnace after adding checksum to it (obsolete)
146  #
147  # Take as argument a string representing hexadecimal numbers
148  # and send the associated bytes array to the serial port.
149  #
150  # Exemple : a call to write("10050F") sends [16,5,15] to the serial port
151  #
152  # @param msg a string representing an hexadecimal number
153  # @return None
154  def write(self,msg):
155  msg_bck = msg
156  checksum = 0
157  res = []
158  tmp = ""
159  while len(msg) > 0:
160  #Store the two first hexadecimal digits
161  tmp = int(msg[0:2],16)
162  #and remove them from the string
163  msg = msg[2:]
164 
165  #The checksum is a simple addition
166  checksum += tmp
167 
168  res.append(tmp)
169 
170  #Adding the two bytes of the checksum to the data
171  res.append(checksum/0x100)
172  res.append(checksum%0x100)
173  #Adding the end of frame character
174  res.append(self.FRAME_END)
175 
176  #Send datas on the serial port
177  self.ser.write(str(bytearray(res)))
178 
179  logger.debug("Send '"+msg_bck+"%04X" % checksum+"'")
180 
181  pass
182 
183  ##Write data to serial port
184  #
185  #@param msg a P2Msg object
186  #@return None
187  def sendMsg(self, msg):
188  self.ser.write(msg.getRaw()+"\r")
189 
190  logger.debug("Send '"+msg.getStr()+"'")
191  pass
192 
193  ##Read datas on the serial port
194  #
195  # Reads datas from the serial port and returns a P2Msg
196  #
197  #@exception P2ComError On receive error
198  #@return P2Msg object
199  def read(self):
200  recvBuff = ""
201  res = P2Msg()
202  retry = 0
203 
204  logger.debug("Waiting for data on serial port")
205 
206  while retry * self.timeout <= self.failTimeout:
207  recvBuff = self.ser.read(1024)
208  if len(recvBuff) > 0:
209  #Frame receive
210  if len(recvBuff) >= 1 and not res.setHeader(recvBuff[0:2]):
211  raise P2ComError(10,res)
212  if len(recvBuff) >= 3 and not res.setDataSz(recvBuff[2:3]):
213  raise P2ComError(11,res)
214  if len(recvBuff) >= 5 and not res.setData(recvBuff[3:-2]):
215  raise P2ComError(12,res)
216  if not res.setChecksum(recvBuff[-2:]):
217  raise P2ComError(13,res)
218  logger.debug("Frame received "+res.getStr())
219  return res
220 
221  retry += 1
222  raise P2ComError(9)
223 
224  ##Close the serial port
225  #
226  #@return None
227  def close(self):
228  self.ser.close();
229