Subversion Repositories heizung

Rev

Rev 4 | Rev 7 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 4 Rev 5
Line 1... Line 1...
1
/*
1
/*
2
 * (C) Copyright 2010 by Andreas Theofilu <andreas@theosys.at>
2
 * (C) Copyright 2010, 2011 by Andreas Theofilu <andreas@theosys.at>
3
 * All rights reserved!
3
 * All rights reserved!
4
 */
4
 */
5
 
5
 
6
#include <stdio.h>
6
#include <stdio.h>
7
#include <string.h>
7
#include <string.h>
Line 21... Line 21...
21
#include <netinet/in.h>
21
#include <netinet/in.h>
22
#include <sys/param.h>
22
#include <sys/param.h>
23
#include <sys/ioctl.h>
23
#include <sys/ioctl.h>
24
#include <pwd.h>
24
#include <pwd.h>
25
#include <grp.h>
25
#include <grp.h>
26
 
-
 
27
typedef struct
26
#include "sensor.h"
28
{
-
 
29
      char User[32];
27
#include "usb_comm.h"
30
      char Grp[32];
-
 
31
      char HeizPath[256];
28
#include "heizung.h"
32
      int port;
-
 
33
}CONFIGURE;
-
 
34
 
29
 
35
typedef struct
30
typedef struct
36
{
31
{
37
      int wday;
32
      int wday;
38
      ulong start;
33
      ulong start;
Line 49... Line 44...
49
CONFIGURE configs;
44
CONFIGURE configs;
50
HEIZINDEX *HeizFirst;
45
HEIZINDEX *HeizFirst;
51
float ActTemperature;
46
float ActTemperature;
52
float ActPressure;
47
float ActPressure;
53
 
48
 
54
static pthread_t pthr_pars;
49
static pthread_t pthr_pars, pthr_temp_pars;
55
 
50
 
56
// Prototypes
51
// Prototypes
57
void daemon_start(int ignsigcld);
52
void daemon_start(int ignsigcld);
-
 
53
void *pthr_temperature(void *pV_data);
58
void *pthr_parser(void *pV_data);
54
void *pthr_parser(void *pV_data);
59
void sig_child(void);
55
void sig_child(void);
60
int parseCommand(int s1, char *cmd);
56
int parseCommand(int s1, char *cmd);
61
void changeToUser(char *, char *);
57
void changeToUser(char *, char *);
62
 
58
 
Line 73... Line 69...
73
char *readLine(int fd, char *buf, int bufLen);
69
char *readLine(int fd, char *buf, int bufLen);
74
char *trim(char *str);
70
char *trim(char *str);
75
char *remove_string(char *str, char *search, char *ret);
71
char *remove_string(char *str, char *search, char *ret);
76
 
72
 
77
static pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
73
static pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
-
 
74
static pthread_mutex_t fastmutex_ser = PTHREAD_MUTEX_INITIALIZER;
78
 
75
 
79
/*
76
/*
80
 * The main program, initializing everything and start the daemon.
77
 * The main program, initializing everything and start the daemon.
81
 */
78
 */
82
int main(int argc, char *argv[])
79
int main(int argc, char *argv[])
83
{
80
{
84
int fd, index;
81
int fd, index;
85
 
82
 
86
	HeizFirst = NULL;
83
	HeizFirst = NULL;
87
	readConf();
84
	readConf();
-
 
85
	// Initialize USB
-
 
86
	serialDev.fd = -1;
-
 
87
	serialDev.switch_fd = -1;
-
 
88
	serialDev.handle = NULL;
88
	// Now daemonize this application
89
	// Now daemonize this application
89
	daemon_start(0);
90
	daemon_start(0);
90
	changeToUser(&configs.User[0], &configs.Grp[0]);
91
	changeToUser(&configs.User[0], &configs.Grp[0]);
91
	// Now start our Thread
92
	// Now start our Thread
92
	if (pthread_create(&pthr_pars, NULL, pthr_parser, (void *)0) != 0)
93
	if (pthread_create(&pthr_pars, NULL, pthr_parser, (void *)0) != 0)
93
	{
94
	{
94
	   syslog (LOG_DAEMON,"Create of thread \"pthr_parser\" failed!");
95
	   syslog (LOG_DAEMON,"Create of thread \"pthr_parser\" failed!");
95
	   return 1;
96
	   return 1;
96
	}
97
	}
97
 
98
 
-
 
99
	// Here we start another thread to read the temperature
-
 
100
	if (pthread_create(&pthr_temp_pars, NULL, pthr_temperature, (void *)0) != 0)
-
 
101
	{
-
 
102
	   syslog(LOG_DAEMON,"Create of thread \"pthr_temperature\" failed!");
-
 
103
	   pthread_cancel(pthr_pars);
-
 
104
	   return 1;
-
 
105
	}
-
 
106
 
98
	while (1)
107
	while (1)
99
	   sleep(3600);
108
	   sleep(3600);
100
 
109
 
-
 
110
	pthread_exit(NULL);
101
	return 0;
111
	return 0;
102
}
112
}
103
 
113
 
104
/*
114
/*
105
 * Detach application from console and make it a daemon.
115
 * Detach application from console and make it a daemon.
Line 216... Line 226...
216
	   if(userpwd->pw_dir)
226
	   if(userpwd->pw_dir)
217
	      setenv("HOME", userpwd->pw_dir, 1);
227
	      setenv("HOME", userpwd->pw_dir, 1);
218
        }
228
        }
219
}
229
}
220
 
230
 
-
 
231
void *pthr_temperature(void *pV_data)
-
 
232
{
-
 
233
time_t t, tstart;
-
 
234
struct tm *zeit, sz;
-
 
235
int sleep_sec;
-
 
236
 
-
 
237
	// Initialize the serial port.
-
 
238
	serial_set_method((strlen(configs.Device)) ? 1 : 0, configs.VID, configs.PID);
-
 
239
 
-
 
240
	if (strlen(configs.Device))
-
 
241
	   serial_set_device(configs.Device);
-
 
242
 
-
 
243
	if (!serial_open())
-
 
244
	   return NULL;
-
 
245
 
-
 
246
	if (serialDev.switch_fd == -1)
-
 
247
	{
-
 
248
	   if (!serial_open())
-
 
249
	      return NULL;
-
 
250
	}
-
 
251
 
-
 
252
	pthread_mutex_lock (&fastmutex_ser);
-
 
253
 
-
 
254
	while(1)
-
 
255
	{
-
 
256
	   HEIZINDEX *act;
-
 
257
 
-
 
258
	   ActTemperature = GetTemp();
-
 
259
	   sleep_sec = 300;
-
 
260
 
-
 
261
	   // Compare the actual temperature with the one that should be
-
 
262
	   // and switch heating on or off
-
 
263
	   if (HeizFirst)
-
 
264
	   {
-
 
265
	      int wday;
-
 
266
	      unsigned long loctm;
-
 
267
 
-
 
268
	      t = time (NULL);
-
 
269
	      sleep_sec = 300 - (int)((t + 300L) % 300L);
-
 
270
	      sleep_sec++;
-
 
271
	      zeit = localtime (&t);
-
 
272
 
-
 
273
	      wday = (zeit->tm_wday == 0) ? 7 : zeit->tm_wday;
-
 
274
	      memset (&sz, 0, sizeof (struct tm));
-
 
275
	      sz.tm_min = 0;
-
 
276
	      sz.tm_hour = 0;
-
 
277
	      sz.tm_mon = zeit->tm_mon;
-
 
278
	      sz.tm_mday = zeit->tm_mday;
-
 
279
	      sz.tm_year = zeit->tm_year;
-
 
280
	      sz.tm_isdst = zeit->tm_isdst;
-
 
281
	      tstart = mktime(&sz);
-
 
282
	      loctm = t - tstart;	// Seconds since midnight
-
 
283
	      act = HeizFirst;
-
 
284
syslog(LOG_INFO,"wday: %d, loctm: %lu", wday, loctm);
-
 
285
	      while(act)
-
 
286
	      {
-
 
287
		 if (act->heizung->wday == wday && loctm >= act->heizung->start && loctm <= act->heizung->end)
-
 
288
		 {
-
 
289
syslog(LOG_INFO,"wday: %d, loctm: %lu, endt: %lu, solltemp: %.1f", wday, loctm, act->heizung->start, act->heizung->temp);
-
 
290
		    if (ActTemperature == 9999.0)
-
 
291
		       SwitchOff();		// No temperature, no heating
-
 
292
		    else if (ActTemperature < 5.0)
-
 
293
		       SwitchOn();		// Make sure it will not freeze
-
 
294
		    else if (ActTemperature > 30.0)
-
 
295
		       SwitchOff();		// Don't over heat
-
 
296
		    else if (ActTemperature <= (act->heizung->temp - 0.5))
-
 
297
		       SwitchOn();
-
 
298
		    else if (ActTemperature > act->heizung->temp)
-
 
299
		       SwitchOff();
-
 
300
		 }
-
 
301
 
-
 
302
		 act = act->next;
-
 
303
	      }
-
 
304
	   }
-
 
305
	   else
-
 
306
	      syslog(LOG_INFO,"Structure not initialized!");
-
 
307
 
-
 
308
	   sleep(sleep_sec);		// Wait 5 Minutes
-
 
309
	}
-
 
310
 
-
 
311
	pthread_mutex_unlock(&fastmutex_ser);
-
 
312
}
-
 
313
 
221
void *pthr_parser( void *pV_data )
314
void *pthr_parser( void *pV_data )
222
{
315
{
223
int i;
316
int i;
224
char ch, buf[128];
317
char ch, buf[128];
225
// socket structure
318
// socket structure
Line 306... Line 399...
306
	      }
399
	      }
307
 
400
 
308
	      i++;
401
	      i++;
309
	   }
402
	   }
310
 
403
 
311
	   pthread_mutex_unlock(&fastmutex);
-
 
312
	   close (s1);
404
	   close (s1);
-
 
405
	   pthread_mutex_unlock(&fastmutex);
313
	}
406
	}
314
 
407
 
-
 
408
	serial_close();
315
	return NULL;
409
	return NULL;
316
}
410
}
317
 
411
 
318
int parseCommand(int s1, char *cmd)
412
int parseCommand(int s1, char *cmd)
319
{
413
{
Line 330... Line 424...
330
	   strncpy(bef,cmd,p - cmd);
424
	   strncpy(bef,cmd,p - cmd);
331
	   strncpy(par,p+1,strlen(p+1));
425
	   strncpy(par,p+1,strlen(p+1));
332
	}
426
	}
333
	else
427
	else
334
	   strncpy(bef,cmd,strlen(cmd)-1);
428
	   strncpy(bef,cmd,strlen(cmd)-1);
335
sprintf(&hv0[0], "%s --> %s\n", bef, par);
-
 
336
write(s1, hv0, strlen(hv0));
-
 
-
 
429
 
337
	if (!strcasecmp(bef, "LIST"))		// Write out current list
430
	if (!strcasecmp(bef, "LIST"))		// Write out current list
338
	{
431
	{
339
	   act = HeizFirst;
-
 
340
	   i = 1;
432
	   int wday = 1;
341
 
433
 
-
 
434
	   i = 1;	// The line counter
-
 
435
	   // We will write out the week days in the correct order.
-
 
436
	   // So we need 2 loops. The first one is runnung for every week day
-
 
437
	   // and the 2nd one will find every entry for the actual week day.
342
	   while (act)
438
	   while (wday <= 7)
343
	   {
439
	   {
-
 
440
	      act = HeizFirst;
-
 
441
 
-
 
442
	      while (act)
-
 
443
	      {
-
 
444
		 if (act->heizung->wday == wday)
-
 
445
		 {
344
	      sprintf(&hv0[0], "LINE:%d:%d:%lu:%lu:%f;", i, act->heizung->wday,
446
		    sprintf(&hv0[0], "LINE:%d:%d:%lu:%lu:%.1f;", i, act->heizung->wday,
345
		      act->heizung->start, act->heizung->end, act->heizung->temp);
447
			act->heizung->start, act->heizung->end, act->heizung->temp);
346
	      write(s1, &hv0[0], strlen(hv0));
448
		    write(s1, &hv0[0], strlen(hv0));
347
	      i++;
449
		    i++;
-
 
450
		 }
-
 
451
 
348
	      act = act->next;
452
		 act = act->next;
-
 
453
	      }
-
 
454
 
-
 
455
	      wday++;
349
	   }
456
	   }
350
	}
457
	}
351
 
458
 
352
	if (!strcasecmp(bef, "GET WDAY"))		// Write out a particular week day
459
	if (!strcasecmp(bef, "GET WDAY"))		// Write out a particular week day
353
	{
460
	{
Line 374... Line 481...
374
 
481
 
375
	   i = 1;
482
	   i = 1;
376
 
483
 
377
	   while (act && act->heizung->wday == wday)
484
	   while (act && act->heizung->wday == wday)
378
	   {
485
	   {
379
	      sprintf(&hv0[0], "LINE:%d:%d:%lu:%lu:%f;", i, act->heizung->wday,
486
	      sprintf(&hv0[0], "LINE:%d:%d:%lu:%lu:%.1f;", i, act->heizung->wday,
380
		      act->heizung->start, act->heizung->end, act->heizung->temp);
487
		      act->heizung->start, act->heizung->end, act->heizung->temp);
381
	      write(s1, &hv0[0], strlen(hv0));
488
	      write(s1, &hv0[0], strlen(hv0));
382
	      i++;
489
	      i++;
383
	      act = act->next;
490
	      act = act->next;
384
	   }
491
	   }
385
	}
492
	}
386
 
493
 
387
	if (!strcasecmp(bef, "GET TEMP"))	// Return actual temperature
494
	if (!strcasecmp(bef, "GET TEMP"))	// Return actual temperature
388
	{
495
	{
389
	   sprintf(&hv0[0], "TEMP:%f;", ActTemperature);
496
	   sprintf(&hv0[0], "TEMP:%.1f;", ActTemperature);
390
	   write(s1, &hv0[0], strlen(hv0));
497
	   write(s1, &hv0[0], strlen(hv0));
391
	}
498
	}
392
 
499
 
393
	if (!strcasecmp(bef, "GET PRESSURE"))	// Return the actual air pressure
500
	if (!strcasecmp(bef, "GET PRESSURE"))	// Return the actual air pressure
394
	{
501
	{
395
	   sprintf(&hv0[0], "PRESSURE:%f;", ActPressure);
502
	   sprintf(&hv0[0], "PRESSURE:%.3f;", ActPressure);
396
	   write(s1, &hv0[0], strlen(hv0));
503
	   write(s1, &hv0[0], strlen(hv0));
397
	}
504
	}
398
 
505
 
399
	// SET DAY:<count>:<day>:<end1>:<temp>[:<end2>:<temp>[:...]];
506
	// SET DAY:<count>:<day>:<end1>:<temp>[:<end2>:<temp>[:...]];
400
	// <count>   number of entries following
507
	// <count>   number of entries following
Line 434... Line 541...
434
		 act->heizung->wday = wday;
541
		 act->heizung->wday = wday;
435
		 act->heizung->end = endt;
542
		 act->heizung->end = endt;
436
		 act->heizung->temp = temp;
543
		 act->heizung->temp = temp;
437
 
544
 
438
		 if (last)
545
		 if (last)
439
		    act->heizung->start = act->heizung->end;
546
		    act->heizung->start = last->heizung->end;
440
		 else
547
		 else
441
		    act->heizung->start = 0L;
548
		    act->heizung->start = 0L;
442
 
549
 
443
		 last = act;
550
		 last = act;
444
	      }
551
	      }
-
 
552
 
-
 
553
	      writeHeizPlan();
445
	   }
554
	   }
446
	}
555
	}
447
 
556
 
448
	// SET TEMP:<wday>:<end>:<temp>;
557
	// SET TEMP:<wday>:<end>:<temp>;
449
	if (!strcasecmp(bef, "SET TEMP:"))	// Set the temperature for a particular day and line
558
	if (!strcasecmp(bef, "SET TEMP"))	// Set the temperature for a particular day and line
450
	{
559
	{
451
	int wday;
560
	int wday;
452
	unsigned long endt;
561
	unsigned long endt;
453
	float tmp;
562
	float tmp;
454
 
563
 
Line 456... Line 565...
456
	   remove_string(par, ":", &hv0[0]);
565
	   remove_string(par, ":", &hv0[0]);
457
	   endt = atol(par);
566
	   endt = atol(par);
458
	   remove_string(par, ":", &hv0[0]);
567
	   remove_string(par, ":", &hv0[0]);
459
	   tmp = atof(par);
568
	   tmp = atof(par);
460
	   insertMember(wday, endt, tmp);
569
	   insertMember(wday, endt, tmp);
-
 
570
	   writeHeizPlan();
461
	}
571
	}
462
 
572
 
463
	return 1;
573
	return 1;
464
}
574
}
465
 
575
 
Line 529... Line 639...
529
 
639
 
530
	   next = act->next;
640
	   next = act->next;
531
	   free(act);
641
	   free(act);
532
	   act = next;
642
	   act = next;
533
	}
643
	}
-
 
644
 
-
 
645
	HeizFirst = NULL;
534
}
646
}
535
 
647
 
536
/*
648
/*
537
 * Free only one entry in the chain
649
 * Free only one entry in the chain
538
 */
650
 */
Line 691... Line 803...
691
	   }
803
	   }
692
	}
804
	}
693
	else
805
	else
694
	   confFile[0] = 0;
806
	   confFile[0] = 0;
695
 
807
 
696
        memset(&configs, 0, sizeof(configs));
808
	memset(&configs, 0, sizeof(configs));
697
        strcpy(configs.User,"nobody");
809
	strcpy(configs.User,"nobody");
698
        strcpy(configs.Grp,"nobody");
810
	strcpy(configs.Grp,"nobody");
699
        strcpy(configs.HeizPath, "/var/www/.HeizPlan.conf");
811
	strcpy(configs.HeizPath, "/var/www/.HeizPlan.conf");
-
 
812
	strcpy(configs.Werte, "/var/log/werte.dat");
-
 
813
	strcpy(configs.Device, "/dev/ttyS1");
700
        configs.port = 11001;
814
	configs.port = 11001;
701
 
815
 
702
	if (!confFile[0] || (fd = open(confFile,O_RDONLY)) == -1)
816
	if (!confFile[0] || (fd = open(confFile,O_RDONLY)) == -1)
703
	{
817
	{
704
	   if (confFile[0])
818
	   if (confFile[0])
705
	      syslog(LOG_WARNING,"Error opening the config file %s! Using built in defaults. (%s)", confFile, strerror(errno));
819
	      syslog(LOG_WARNING,"Error opening the config file %s! Using built in defaults. (%s)", confFile, strerror(errno));
Line 761... Line 875...
761
	   if (!strcasecmp(hv0, "heizpath"))
875
	   if (!strcasecmp(hv0, "heizpath"))
762
	   {
876
	   {
763
	      syslog(LOG_INFO,"Found \"heizpath\": %s", hv1);
877
	      syslog(LOG_INFO,"Found \"heizpath\": %s", hv1);
764
	      strncpy (configs.HeizPath, hv1, sizeof(configs.HeizPath));
878
	      strncpy (configs.HeizPath, hv1, sizeof(configs.HeizPath));
765
	   }
879
	   }
-
 
880
 
-
 
881
	   if (!strcasecmp(hv0, "Werte"))
-
 
882
	   {
-
 
883
	      syslog(LOG_INFO,"Found \"Werte\": %s", hv1);
-
 
884
	      strncpy (configs.Werte, hv1, sizeof(configs.Werte));
-
 
885
	   }
-
 
886
 
-
 
887
	   if (!strcasecmp(hv0, "Device"))
-
 
888
	   {
-
 
889
	      syslog(LOG_INFO,"Found \"Device\": %s", hv1);
-
 
890
	      strncpy (configs.Device, hv1, sizeof(configs.Device));
-
 
891
	   }
-
 
892
 
-
 
893
	   if (!strcasecmp(hv0, "VID"))
-
 
894
	   {
-
 
895
	      syslog(LOG_INFO,"Found VendorID: %04x", atoi(hv1));
-
 
896
	      configs.VID = atoi(hv1);
-
 
897
	   }
-
 
898
 
-
 
899
	   if (!strcasecmp(hv0, "PID"))
-
 
900
	   {
-
 
901
	      syslog(LOG_INFO,"Found ProductID: %04x", atoi(hv1));
-
 
902
	      configs.PID = atoi(hv1);
-
 
903
	   }
766
	}
904
	}
767
 
905
 
768
	close (fd);
906
	close (fd);
769
}
907
}
770
 
908
 
Line 800... Line 938...
800
	{
938
	{
801
	   int len;
939
	   int len;
802
 
940
 
803
	   trim (&line[0]);
941
	   trim (&line[0]);
804
 
942
 
805
	   if (line[0] == '#')
943
	   if (line[0] == '#' || !strlen(line) || strchr(line, ',') == NULL)
806
	      continue;
944
	      continue;
807
 
945
 
808
	   // We need a place to store the information
946
	   // We need a place to store the information
809
	   prev = act;
947
	   prev = act;
810
 
948
 
Line 853... Line 991...
853
		       hour = atoi(p);
991
		       hour = atoi(p);
854
		       min = atoi(xp+1);
992
		       min = atoi(xp+1);
855
 
993
 
856
		       if (hour >= 0 && hour <= 23 && min >= 0 && min <= 59)
994
		       if (hour >= 0 && hour <= 23 && min >= 0 && min <= 59)
857
		       {
995
		       {
858
			  act->heizung->end = hour * 3600 + (min + 1) * 60;
996
			  act->heizung->end = hour * 3600 + min * 60 + 59;
859
 
997
 
860
			  if (prev && prev->heizung->wday == act->heizung->wday)
998
			  if (prev && prev->heizung->wday == act->heizung->wday)
861
			     act->heizung->start = prev->heizung->end;
999
			     act->heizung->start = prev->heizung->end;
862
		       }
1000
		       }
863
		    }
1001
		    }
Line 896... Line 1034...
896
char hv0[512];
1034
char hv0[512];
897
HEIZINDEX *act;
1035
HEIZINDEX *act;
898
 
1036
 
899
	fd = -1;
1037
	fd = -1;
900
 
1038
 
901
	if (!access(configs.HeizPath, R_OK))
1039
	if (access(configs.HeizPath, R_OK))
902
	   return 0;
1040
	   return 0;
903
 
1041
 
904
	if ((fd = open(configs.HeizPath, O_RDWR | O_TRUNC)) == -1)
1042
	if ((fd = open(configs.HeizPath, O_RDWR | O_TRUNC)) == -1)
905
	   return 0;
1043
	   return 0;
906
 
1044
 
907
	act = HeizFirst;
1045
	act = HeizFirst;
908
 
1046
 
909
	while(act)
1047
	while(act)
910
	{
1048
	{
-
 
1049
	   if (act->heizung->wday)
-
 
1050
	   {
-
 
1051
	      int hour, min;
-
 
1052
 
-
 
1053
	      hour = act->heizung->end / 3600;
-
 
1054
	      min = (act->heizung->end - (hour * 3600)) / 60;
-
 
1055
 
-
 
1056
	      if (hour > 23)
-
 
1057
	      {
-
 
1058
		 hour = 23;
-
 
1059
		 min = 59;
-
 
1060
	      }
-
 
1061
 
911
	   sprintf(&hv0[0], "%d,%lu,%f\n",
1062
	      sprintf(&hv0[0], "%d,%02d:%02d,%.1f\n",
912
		   act->heizung->wday, act->heizung->end,
1063
		   act->heizung->wday, hour, min,
913
		   act->heizung->temp);
1064
		   act->heizung->temp);
914
	   write(fd, &hv0[0], strlen(hv0));
1065
	      write(fd, &hv0[0], strlen(hv0));
-
 
1066
	   }
-
 
1067
 
915
	   act = act->next;
1068
	   act = act->next;
916
	}
1069
	}
917
 
1070
 
918
	close(fd);
1071
	close(fd);
919
	return 1;
1072
	return 1;
Line 1014... Line 1167...
1014
	if ((p = strstr(str, search)) != NULL)
1167
	if ((p = strstr(str, search)) != NULL)
1015
	{
1168
	{
1016
	int len = strlen(search);
1169
	int len = strlen(search);
1017
 
1170
 
1018
	   strncpy(ret, str, p - str + len);
1171
	   strncpy(ret, str, p - str + len);
-
 
1172
	   ret[p - str + len] = 0;
1019
	   memmove(str, p + len + 1, strlen(p+len));
1173
	   memmove(str, p + len, strlen(p+len));
-
 
1174
	   str[strlen(p+len)] = 0;
1020
	   return ret;
1175
	   return ret;
1021
	}
1176
	}
1022
 
1177
 
1023
	return NULL;
1178
	return NULL;
1024
}
1179
}