1 #include "stdafx.h"
2 #include "1WireByOWFS.h"
3 #include "../../main/mainworker.h"
4 #include "../../main/Logger.h"
5 
6 #include <fstream>
7 #include <algorithm>
8 
9 #ifdef WIN32
10 #include "../../main/dirent_windows.h"
11 #else
12 #include <dirent.h>
13 #endif
14 
15 #include "../../main/Helper.h"
16 
C1WireByOWFS(const std::string & path)17 C1WireByOWFS::C1WireByOWFS(const std::string& path):
18 	m_path(path)
19 {
20 	if (m_path.empty())
21 		m_path = "/mnt/1wire";
22 
23 	m_simultaneousTemperaturePath = m_path;
24 	m_simultaneousTemperaturePath.append("/simultaneous/temperature");
25 
26    _log.Log(LOG_STATUS,"Using 1-Wire support (OWFS)...");
27 }
28 
FindDevice(const std::string & sID,_t1WireDevice & device) const29 bool C1WireByOWFS::FindDevice(const std::string &sID, /*out*/_t1WireDevice& device) const
30 {
31     return FindDevice(m_path, sID, device);
32 }
33 
FindDevice(const std::string & inDir,const std::string & sID,_t1WireDevice & device) const34 bool C1WireByOWFS::FindDevice(const std::string &inDir, const std::string &sID, /*out*/_t1WireDevice& device) const
35 {
36     bool found = false;
37     DIR *d=opendir(inDir.c_str());
38     if (d != NULL)
39     {
40         struct dirent *de=NULL;
41         // Loop while not NULL or not found
42         while((de=readdir(d)) && !found)
43         {
44             // Check dir
45             if (!IsValidDir(de))
46                 continue;
47 
48             // Get the device from it's dirname
49             GetDevice(inDir, de->d_name, device);
50 
51             // Check if it's the device we are looking for
52             if (device.devid.compare(0,sID.length(),sID)==0)
53             {
54                 found=true;
55                 continue;
56             }
57 
58             // Else, try to scan hubs (recursively)
59             if (device.family==microlan_coupler)
60             {
61                 // Search in "main" and "aux" dir
62                 found=FindDevice(inDir + "/" + de->d_name + HUB_MAIN_SUB_PATH, sID, device);
63                 if(!found)
64                     found=FindDevice(inDir + "/" + de->d_name + HUB_AUX_SUB_PATH, sID, device);
65             }
66         }
67     }
68 
69     closedir(d);
70     return found;
71 }
72 
GetDevices(std::vector<_t1WireDevice> & devices) const73 void C1WireByOWFS::GetDevices(/*out*/std::vector<_t1WireDevice>& devices) const
74 {
75     return GetDevices(m_path, devices);
76 }
77 
GetDevices(const std::string & inDir,std::vector<_t1WireDevice> & devices) const78 void C1WireByOWFS::GetDevices(const std::string &inDir, /*out*/std::vector<_t1WireDevice>& devices) const
79 {
80     DIR *d=opendir(inDir.c_str());
81     if (d != NULL)
82     {
83         struct dirent *de=NULL;
84         // Loop while not NULL
85         while((de=readdir(d)))
86         {
87             // Check dir
88             if (!IsValidDir(de))
89                 continue;
90 
91             // Get the device from it's dirname
92             _t1WireDevice device;
93             GetDevice(inDir, de->d_name, device);
94 
95             // Add device to list
96 			_log.Debug(DEBUG_HARDWARE, "1Wire (OWFS): Add device %s", device.filename.c_str());
97             devices.push_back(device);
98 
99             // If device is a hub, scan for all hub connected devices (recursively)
100             if (device.family==microlan_coupler)
101             {
102                 // Scan in "main" and "aux" dir
103                 GetDevices(inDir + "/" + de->d_name + HUB_MAIN_SUB_PATH, devices);
104                 GetDevices(inDir + "/" + de->d_name + HUB_AUX_SUB_PATH, devices);
105             }
106         }
107     }
108 
109     closedir(d);
110 }
111 
readRawData(const std::string & filename) const112 std::string C1WireByOWFS::readRawData(const std::string& filename) const
113 {
114     std::ifstream file;
115     file.open(filename.c_str());
116     if (file.is_open())
117     {
118         std::string sLine;
119         getline(file, sLine);
120         file.close();
121 		if (is_number(sLine))
122 			return sLine;
123     }
124     return "";
125 }
126 
writeData(const _t1WireDevice & device,const std::string & propertyName,const std::string & value) const127 void C1WireByOWFS::writeData(const _t1WireDevice& device, const std::string &propertyName, const std::string &value) const
128 {
129     std::ofstream file;
130     file.open(std::string(device.filename+"/"+propertyName).c_str());
131     if (!file.is_open())
132         return;
133 
134     file << value;
135     file.close();
136 }
137 
SetLightState(const std::string & sId,int unit,bool value,const unsigned int level)138 void C1WireByOWFS::SetLightState(const std::string& sId,int unit,bool value, const unsigned int level)
139 {
140    _t1WireDevice device;
141    if (!FindDevice(sId, device))
142       return;
143 
144    switch(device.family)
145    {
146    case Addresable_Switch:
147       {
148          writeData(device,"PIO",value?"yes":"no");
149          break;
150       }
151    case dual_addressable_switch_plus_1k_memory:
152    case Temperature_IO:
153    case dual_channel_addressable_switch:
154       {
155          if (unit<0 || unit>1)
156             return;
157          writeData(device,std::string("PIO.").append(1,'A'+(char)unit),value?"yes":"no");
158          break;
159       }
160    case _8_channel_addressable_switch:
161       {
162          if (unit<0 || unit>7)
163             return;
164          writeData(device,std::string("PIO.").append(1,'0'+(char)unit),value?"yes":"no");
165          break;
166       }
167    case _4k_EEPROM_with_PIO:
168       {
169          if (unit<0 || unit>1)
170             return;
171          writeData(device,std::string("PIO.").append(1,'0'+(char)unit),value?"yes":"no");
172          break;
173       }
174    case microlan_coupler:
175       {
176          // 1 = Unconditionally off (non-conducting), 2 = Unconditionally on (conducting)
177          writeData(device,"control",value?"2":"1");
178          break;
179       }
180    case digital_potentiometer:
181    {
182 	   writeData(device, "chargepump", "1");
183 	   unsigned int wiper = static_cast<unsigned int>(level * (255.0 / 15.0));
184 	   writeData(device, "wiper", std::to_string(wiper));
185 	   break;
186    }
187 
188    default:
189       return;
190    }
191 }
192 
GetTemperature(const _t1WireDevice & device) const193 float C1WireByOWFS::GetTemperature(const _t1WireDevice& device) const
194 {
195    std::string readValue=readRawData(std::string(device.filename+"/temperature"));
196 
197    _log.Debug(DEBUG_HARDWARE, "1Wire (OWFS): Get Temperature from %s = %s", device.filename.c_str(), readValue.c_str());
198 
199    if (readValue.empty())
200       return -1000.0;
201    return static_cast<float>(atof(readValue.c_str()));
202 }
203 
GetHumidity(const _t1WireDevice & device) const204 float C1WireByOWFS::GetHumidity(const _t1WireDevice& device) const
205 {
206    std::string readValue=readRawData(std::string(device.filename+"/humidity"));
207 
208    _log.Debug(DEBUG_HARDWARE, "1Wire (OWFS): Get Humidity from %s = %s", device.filename.c_str(), readValue.c_str());
209 
210    if (readValue.empty())
211 	   return -1000.0;
212    return static_cast<float>(atof(readValue.c_str()));
213 }
214 
GetPressure(const _t1WireDevice & device) const215 float C1WireByOWFS::GetPressure(const _t1WireDevice& device) const
216 {
217    std::string realFilename = device.filename + nameHelper(device.filename, device.family); // for family 26 (DS2438) + pressure + HobbyBoards
218 
219    std::string readValue=readRawData(std::string(realFilename+"/pressure"));
220    if (readValue.empty())
221 	   return -1000.0;
222    return static_cast<float>(atof(readValue.c_str()));
223 }
224 
GetLightState(const _t1WireDevice & device,int unit) const225 bool C1WireByOWFS::GetLightState(const _t1WireDevice& device,int unit) const
226 {
227    std::string fileName(device.filename);
228 
229    switch(device.family)
230    {
231    case Addresable_Switch:
232       {
233          fileName.append("/sensed");
234          break;
235       }
236    case dual_addressable_switch_plus_1k_memory:
237    case Temperature_IO:
238    case dual_channel_addressable_switch:
239       {
240          if (unit<0 || unit>1)
241             return false;
242          fileName.append("/sensed.").append(1,'A'+(char)unit);
243          break;
244       }
245    case _8_channel_addressable_switch:
246       {
247          if (unit<0 || unit>7)
248             return false;
249          fileName.append("/sensed.").append(1,'0'+(char)unit);
250          break;
251       }
252    case _4k_EEPROM_with_PIO:
253       {
254          if (unit<0 || unit>1)
255             return false;
256          fileName.append("/sensed.").append(1,'0'+(char)unit);
257          break;
258       }
259    case microlan_coupler:
260       {
261          fileName.append("/control");
262          break;
263       }
264    }
265 
266    std::string readValue=readRawData(fileName);
267    if (readValue.empty())
268       return false;
269 
270    switch(device.family)
271    {
272    case Addresable_Switch:
273    case dual_addressable_switch_plus_1k_memory:
274    case Temperature_IO:
275    case dual_channel_addressable_switch:
276    case _8_channel_addressable_switch:
277    case _4k_EEPROM_with_PIO:
278         // Caution : read value correspond to voltage level, inverted from the transistor state
279         // We have to invert the read value
280       return (readValue!="1");
281    case microlan_coupler:
282       {
283          // 1 = Unconditionally off (non-conducting)
284          // 2 = Unconditionally on (conducting)
285          int iValue=atoi(readValue.c_str())==2;
286          if (iValue!=1 && iValue!=2)
287             return false;
288          else
289             return (iValue==2);
290       }
291    }
292    return false;
293 }
294 
GetNbChannels(const _t1WireDevice & device) const295 unsigned int C1WireByOWFS::GetNbChannels(const _t1WireDevice& device) const
296 {
297    std::string readValue=readRawData(std::string(device.filename+"/channels"));
298    if (readValue.empty())
299       return 0;
300    return atoi(readValue.c_str());
301 }
302 
GetCounter(const _t1WireDevice & device,int unit) const303 unsigned long C1WireByOWFS::GetCounter(const _t1WireDevice& device,int unit) const
304 {
305    // Depending on OWFS version, file can be "counter" or "counters". So try both.
306    std::string readValue=readRawData(std::string(device.filename+"/counter.").append(1,'A'+(char)unit));
307    if (readValue.empty())
308       readValue=readRawData(std::string(device.filename+"/counters.").append(1,'A'+(char)unit));
309    if (readValue.empty())
310 	   return 0;
311    return (unsigned long)atol(readValue.c_str());
312 }
313 
GetVoltage(const _t1WireDevice & device,int unit) const314 int C1WireByOWFS::GetVoltage(const _t1WireDevice& device,int unit) const
315 {
316    std::string fileName(device.filename);
317 
318    switch(device.family)
319    {
320    case smart_battery_monitor:
321       {
322          static const std::string unitNames[] = { "VAD", "VDD", "vis" };
323          if (unit >= int((sizeof(unitNames)/sizeof(unitNames[0]))))
324             return 0;
325          fileName.append("/").append(unitNames[unit]);
326          break;
327       }
328    default:
329       {
330          fileName.append("/volt.").append(1, static_cast<char>('A'+unit));
331          break;
332       }
333    }
334 
335    std::string readValue=readRawData(fileName);
336    if (readValue.empty())
337 	   return -1000;
338    return static_cast<int>((atof(readValue.c_str())*1000.0));
339 }
340 
GetIlluminance(const _t1WireDevice & device) const341 float C1WireByOWFS::GetIlluminance(const _t1WireDevice& device) const
342 {
343    // Both terms "illumination" and "illuminance" are in the OWFS documentation, so try both
344    std::string readValue=readRawData(std::string(device.filename+"/S3-R1-A/illuminance"));
345    if (readValue.empty())
346       readValue=readRawData(std::string(device.filename+"/S3-R1-A/illumination"));
347    if (readValue.empty())
348 	   return -1000.0;
349    return (float)(atof(readValue.c_str())*1000.0);
350 }
351 
GetWiper(const _t1WireDevice & device) const352 int C1WireByOWFS::GetWiper(const _t1WireDevice& device) const
353 {
354 	std::string readValue = readRawData(std::string(device.filename + "/wiper"));
355 	if (readValue.empty())
356 		return -1;
357 	return atoi(readValue.c_str());
358 }
359 
StartSimultaneousTemperatureRead()360 void C1WireByOWFS::StartSimultaneousTemperatureRead()
361 {
362 	_log.Debug(DEBUG_HARDWARE, "1Wire (OWFS): Initiating simultaneous temperature read");
363 	std::ofstream file;
364 	file.open(m_simultaneousTemperaturePath.c_str());
365 	if (file.is_open())
366 	{
367 		file << "1";
368 		file.close();
369 		_log.Debug(DEBUG_HARDWARE, "1Wire (OWFS): Simultaneous temperature read successful");
370 		sleep_milliseconds(800);
371 	}
372 }
373 
PrepareDevices()374 void C1WireByOWFS::PrepareDevices()
375 {
376 }
377 
IsValidDir(const struct dirent * const de)378 bool C1WireByOWFS::IsValidDir(const struct dirent*const de)
379 {
380     // Check dirent type
381     if (de->d_type!=DT_DIR)
382         return false;
383 
384     // Filter system dirs "." and ".."
385     std::string dirname = de->d_name;
386     if ((dirname==".")||(dirname=="..")||(dirname.size()<3))
387         return false;
388 
389     // Check dir name format (must be something like xx.xxxxxxxxxxxx where x is hexa)
390     if (dirname.size()!=15||
391        dirname[2]!='.'||
392        !isxdigit(dirname[0])||!isxdigit(dirname[1])||
393        !isxdigit(dirname[3])||!isxdigit(dirname[4])||
394        !isxdigit(dirname[5])||!isxdigit(dirname[6])||
395        !isxdigit(dirname[7])||!isxdigit(dirname[8])||
396        !isxdigit(dirname[9])||!isxdigit(dirname[10])||
397        !isxdigit(dirname[11])||!isxdigit(dirname[12])||
398        !isxdigit(dirname[13])||!isxdigit(dirname[14]))
399         return false;
400 
401     // The dir is a valid device name
402     return true;
403 }
404 
GetDevice(const std::string & inDir,const std::string & dirname,_t1WireDevice & device) const405 void C1WireByOWFS::GetDevice(const std::string &inDir, const std::string &dirname, /*out*/_t1WireDevice& device) const
406 {
407     // OWFS device name format, see http://owfs.org/uploads/owfs.1.html#sect30
408     // OWFS must be configured to use the default format ff.iiiiiiiiiiii, with :
409     // - ff : family (1 byte)
410     // - iiiiiiiiiiii : id (6 bytes)
411 
412     // Retrieve family from the first 2 chars
413     device.family=ToFamily(dirname.substr(0,2));
414 
415     // Device Id (6 chars after '.')
416     std::string id=dirname.substr(3,3+6*2);
417     // OWFS give us the device ID inverted, so reinvert it
418     for (int i=0;i<6/2;i++)
419     {
420 		char c;
421 		int left_position = i * 2;          // Point on first byte
422 		int right_position=(6-i-1)*2;   // Point on last byte
423 		c=id[left_position]; id[left_position]=id[right_position]; id[right_position]=c;
424 		c=id[left_position+1]; id[left_position+1]=id[right_position+1]; id[right_position+1]=c;
425     }
426     device.devid=id;
427 
428     device.filename=inDir;
429     if (device.family == Environmental_Monitors) {
430         device.filename+="/" + dirname + nameHelper(dirname, device.family);
431     } else {
432         device.filename+="/" + dirname;
433     }
434 }
435 
nameHelper(const std::string & dirname,const _e1WireFamilyType family) const436 std::string C1WireByOWFS::nameHelper(const std::string& dirname, const _e1WireFamilyType family) const {
437 	std::string name;
438 	DIR *d=NULL;
439 
440 	d=opendir(std::string(std::string(m_path) + "/" + dirname.c_str()).c_str());
441 	if (d != NULL)
442 	{
443 		struct dirent *de = NULL;
444 		while ((de = readdir(d)))
445 		{
446 			name = de->d_name;
447 			if (de->d_type == DT_DIR)
448 			{
449 				if ( ((family == Environmental_Monitors) && (name.compare(0, 3, "EDS") == 0))
450 				  || ((family == smart_battery_monitor) && (name.compare(0, 7, "B1-R1-A") == 0)) ) {
451 					closedir(d);
452 					return "/" + name;
453 				}
454 			}
455 		}
456 		closedir(d);
457 	}
458     return "";
459 }
460 
461