/*program to control external devices connected to the serial port*/ /*todo: random,*/ /*nove zmeny z verze 17.12.97, delka 18226*/ /*Y2000 requires on amiga change on all places with localtime*/ #include #include #include #include #include #include #ifdef unix #include #include #include #include #include #endif #ifdef amiga #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif #ifdef amiga char *strdup(str) char *str; { char *ret; ret=(char *)malloc((size_t)(1+strlen(str))); if(ret) strcpy(ret,str); return ret; } int sleep2(sec,micro) unsigned int sec, micro; { static struct IntuitionBase *IntuitionBase = NULL; static struct timerequest *timerreq=NULL; /* request structure for timer waits*/ if(!IntuitionBase) { if ((IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 0)) == (void *)NULL) return -1; /*nrerror("sleep: cannot open intuition.library");*/ } if(!timerreq) { timerreq=(struct timerequest *) malloc(sizeof(struct timerequest)); if(!timerreq) return -2; /*nrerror("cannot allocate struct timerequest");*/ /* fill in the struture with what we are dealing with */ timerreq->tr_node.io_Message.mn_Node.ln_Type = NT_MESSAGE; timerreq->tr_node.io_Message.mn_Node.ln_Pri = 0; timerreq->tr_node.io_Message.mn_ReplyPort = &(((struct Process *)FindTask(NULL))->pr_MsgPort); /* and open us a timer. Note that we are using VBLANK since it is the */ /* lowest system overhead. We are not critical on the timing and only */ /* want to run as often as possible without killing the system */ if (OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest*)timerreq, 0)) return -3; /*nrerror("cannot open timer device");*/ } /* Use the timer to wait our required number of seconds */ timerreq->tr_node.io_Command = TR_ADDREQUEST; timerreq->tr_time.tv_secs = (long) sec; /* seconds */ timerreq->tr_time.tv_micro = (long) micro; /* micro seconds */ DoIO((struct IORequest *)timerreq); /* sleep now */ return 0; } /* cleanup CloseDevice((struct IORequest *)timerreq); if (timerreq != NULL) free (timerreq); if (IntuitionBase != NULL) CloseLibrary((struct Library *)IntuitionBase); */ int sleep(sec) unsigned int sec; { return sleep2(sec,0); } #endif #ifdef unix static void myignore(){} int sleep2(sec,mic) unsigned int sec,mic; { struct itimerval itv; memset((void *)&itv,0,sizeof(itv)); itv.it_value.tv_sec = (unsigned long) sec; itv.it_value.tv_usec = (long) mic; signal(SIGALRM,myignore); setitimer (ITIMER_REAL, &itv, NULL); pause(); if(errno==EINTR) return errno=0; else return errno; } #endif /*system-independent sleep routine*/ int dsleep(t) double t; { double tt; unsigned int i; i=t; tt=i; if(t==tt) {return sleep((unsigned int) tt);} else { i=(unsigned int) (1000000.*(t-tt)); return sleep2((unsigned int) tt, i); } } /*-----------------------------------------------------------------*/ /*system specific low level routines, invertors in hardware of amiga are shielded by this, in unix kernel does such job and we use ioctl*/ #ifdef amiga #define peek(a) (*((unsigned char *)a)) #define poke(a,x) (*((unsigned char *)a)= (unsigned char)x) /*signal(SIGINT,5);*/ unsigned char *port=0xbfd000; unsigned char *datadirection=0xbfd2000; unsigned char dd; /*initialite data direcion*/ void init(dummy) char *dummy; { dd=peek(datadirection); dd &=1+2+4; dd |=128+64; poke(datadirection,dd); } /*functions to read and write data*/ #define readport() (peek(port)) /*OBSOLETE unsigned char writeport(d) unsigned char d; { unsigned char t; t=peek(port); t &=0x3f; d &=0xc0; t |=d; poke(port,t); return t; } */ /* #nezavisle rizeni portu #p= 0 .. prava zasuvka, #p= 1 .. leva zasuvka */ void switchport(p,d) unsigned char p,d; { unsigned char t; if(p>1) {fprintf(stderr,"unsupported OUT%d\n",p); exit(1);} t=readport(); d^=1; d= d<<(7-p); t &= ~(1<<(7-p)); t |=d; poke(port,t); } unsigned char checkport(p) unsigned char p; { unsigned char t; if(p>1) {fprintf(stderr,"unsupported OUT%d\n",p); exit(1);} t=readport(); t=t>>(7-p); return (~t) &1; } /* #p=0 fotonka, #p=1 tlacitko */ unsigned char getport(p) unsigned char p; { unsigned char t; if(p>2) {fprintf(stderr,"unsupported IN%d\n",p); exit(1);} t=readport(); if(p==2) t=t>>5; else t=t>>(4-p); return (~t)&1; } void tidy() {} #endif /*amiga*/ #ifdef unix static int serialfd; void init(name) /* open device file */ char *name; { errno=0; serialfd= open(name, O_RDWR | O_NDELAY); if(serialfd <0) { fprintf(stderr, "cannot open serial device %s\n",name); perror(""); exit(10); } } void tidy() /*close device file */ { close(serialfd); } /*definitions of signals*/ static int outputs[2]={TIOCM_DTR,TIOCM_RTS}; static int inputs[4]={TIOCM_CTS,TIOCM_DSR,TIOCM_RI,TIOCM_CAR}; /* #nezavisle rizeni portu #p= 0 .. prava zasuvka, #p= 1 .. leva zasuvka */ void switchport(p,d)/*unix*/ unsigned char p,d; { int r; if(p>1) {fprintf(stderr,"unsupported OUT%d\n",p); exit(1);} r=ioctl(serialfd,(d?TIOCMBIS:TIOCMBIC),&outputs[p]); if(r<0) perror("ioctl failed"); } unsigned char checkport(p)/*unix*/ unsigned char p; { int r,flags; if(p>1) {fprintf(stderr,"unsupported OUT%d\n",p); exit(1);} r=ioctl(serialfd,TIOCMGET,&flags); if(r<0) perror("ioctl failed"); return (flags&outputs[p]?1:0); } /* #p=0 fotonka, #p=1 tlacitko */ unsigned char getport(p)/*unix*/ unsigned char p; { int r,flags; if(p>3) {fprintf(stderr,"unsupported IN%d\n",p); exit(1);} r=ioctl(serialfd,TIOCMGET,&flags); if(r<0) perror("ioctl failed"); return (flags&inputs[p]?1:0); } #endif /*unix*/ /******************************************************************************/ /* the rest of this program should be fully portable */ static time_t ttt; static struct tm *tms; int waitforlight(t) int t; { int state0; state0=getport(0); while(t-=1) { time(&ttt); tms=localtime(&ttt); #ifdef amiga tms->tm_year+=1900; #endif if(t&1) fprintf(stderr," %d %02d %02d - %02d:%02d:%02d out01:%d %d in0123: %d %d %d %d state: waiting\r", tms->tm_year, tms->tm_mon+1, tms->tm_mday, tms->tm_hour, tms->tm_min, tms->tm_sec, checkport(0),checkport(1),getport(0),getport(1),getport(2),getport(3)); fflush(stderr); if(getport(0)!=state0) {return(1);} dsleep(.5); } return(0); } int waitforbutoff(t) int t; { while(t-=1) { time(&ttt); tms=localtime(&ttt); #ifdef amiga tms->tm_year+=1900; #endif if(t&4) fprintf(stderr," %02d %02d - %02d:%02d:%02d out01:%d %d in0123: %d %d %d %d state: waiting\r", tms->tm_mon+1, tms->tm_mday, tms->tm_hour, tms->tm_min, tms->tm_sec, checkport(0),checkport(1),getport(0),getport(1),getport(2),getport(3)); fflush(stderr); if(!getport(1)) {return(0);} dsleep(.2); } return(1); } /*# better is to construct timer value from day, time! mktime() not impl. on amiga! */ int waitfortime(h,m,s) int h,m,s; { while(1) { time(&ttt); tms=localtime(&ttt); #ifdef amiga tms->tm_year +=1900; #endif if(tms->tm_hour==h && (tms->tm_min>m || tms->tm_min==m && tms->tm_sec>=s) ) {return(1);} sleep(1); } } /*#also switch port 0 according to button 1*/ int waitfortimeB(h,m,s) int h,m,s; { int x; x=0; while(1) { x++; if(getport(1)) {switchport(0,1);} else {switchport(0,0);} time(&ttt); tms=localtime(&ttt); #ifdef amiga tms->tm_year+=1900; #endif if(x&1) fprintf(stderr," %d %02d %02d - %02d:%02d:%02d out01:%d %d in0123: %d %d %d %d state: sleeping\r", tms->tm_year, tms->tm_mon+1, tms->tm_mday, tms->tm_hour, tms->tm_min, tms->tm_sec, checkport(0),checkport(1),getport(0),getport(1),getport(2),getport(3)); fflush(stderr); if(tms->tm_hour==h && (tms->tm_min>m || tms->tm_min==m && tms->tm_sec>=s) ) {switchport(0,0);return(1);} dsleep(.5); } } void wakeup(h,m,s,t) int h,m,s,t; { waitfortimeB(h,m,s); switchport(0,1); fprintf(stderr,"\nWakeup!!!\n"); waitforlight(t*60); switchport(0,0); } static int modulo,smodulo; void usage() { fprintf(stderr,"This is a program for reading/writing to individual bits of serial port to control some device\n\ Usage: portcontrol [-D /dev/tty...] [-m printmodulo] [-M schedulemod] [-l loopnum]\n\ [-s h|\"now\" m s pgm0 pgm1|- (this is cron specification)]\n\ [-S year(4digits) mon day h m s pgm0 pgm1 (this is at specifikation)] [-P prociters] [-d delay]\n\ [-W waveform] where waveform is output0+1 sequence like 01230123 NOTE:for RS232@TTL 3=LOW voltage at both outputs\n\ -i|-t port| -w h m s timeout| -p port program| port state\n"); fprintf(stderr,"Program is string of 01s (and letter e for exit) for giving switches for input states index by binary in2,out1,out0,in1,in0\nNOTE on unix, the option -D MUST be first\n\ used signals:\n\ ============ pin 9 pin 25\n\ --- GND 5 7\n\ OUT0 DTR 4 20\n\ OUT1 RTS 7 4\n\ IN0 CTS 8 5\n\ IN1 DSR 6\n\ IN2 DCD(CAR) 1 8\n\ IN3 RI (unix only) 9\n\ IN3 is not supported for state machine programming\n\ \n\ Example for a specific hardware configuration:\n\ \tport -d .5 -s 20 0 0 0011 00 -S 1999 1 30 7 30 0 0101 00\n\ or\n\ \tport -d .5 -s 20 0 0 0011 00 -s 7 0 0 11 00 -s 7 10 0 10 00\n\ "); #ifdef unix fprintf(stderr,"Do not forget to switch off getty on the controlled port!\n"); #endif exit(10); } /*change must be accompanied by a change in processevent procedure*/ #define MAXSTATE 32 /*types for event queue*/ struct schedule { struct schedule *next; struct tm time; int port; char *pgm0; char *pgm1; } ; static struct schedule **schedules=NULL; static int n_schedules=0; void freeschedules() { int i; if(!schedules) return; for(i=0; ipgm1) free(schedules[i]->pgm1); if(schedules[i]->pgm0)free( schedules[i]->pgm0); free(schedules[i]);} free(schedules); } void processevent(f0,f1) char *f0,*f1; { unsigned int state; unsigned char v; state=(checkport(1)<<3) | (checkport(0)<<2) | (getport(1) <<1) | getport(0) |(getport(2) <<4); if(f0 && *f0) { unsigned int s,l; s=state;l=strlen(f0); if(l<32) s &= 15; if(l<16) s &= 7; if(l<8) s &= 3; if(l<4) s &= 1; if(l<2) s=0; v= f0[s]-'0'; if(v == 'e'-'0') {freeschedules(); exit(0);} switchport(0,v); } if(f1 && *f1) { unsigned int s,l; s=state;l=strlen(f1); if(l<32) s &= 15; if(l<16) s &= 7; if(l<8) s &= 3; if(l<4) s &= 1; if(l<2) s=0; v= f1[s]-'0'; if(v == 'e'-'0') {freeschedules(); exit(0);} switchport(1,v); } } int timecmp(t0,t1,prefercron)/* for table of schedule, prefer cron events over at ones*/ struct tm *t0, *t1; int prefercron; { int t; if(prefercron) { if(t0->tm_year) { if(t1->tm_year) {if(t= t0->tm_year-t1->tm_year) return t;} else return -1;} else {if(t1->tm_year) return 1;} if(t0->tm_mon>=0) { if(t1->tm_mon>=0) {if(t= t0->tm_mon-t1->tm_mon) return t;} else return -1;} else {if(t1->tm_mon>=0) return 1;} if(t0->tm_mday) { if(t1->tm_mday) {if(t= t0->tm_mday-t1->tm_mday) return t;} else return -1;} else {if(t1->tm_mday) return 1;} } else { if(t0->tm_year && t1->tm_year) if(t= t0->tm_year-t1->tm_year) return t; if(t0->tm_mon>=0 && t1->tm_mon>=0) if(t= t0->tm_mon-t1->tm_mon) return t; if(t0->tm_mday && t1->tm_mday) if(t= t0->tm_mday-t1->tm_mday) return t; } if(t= t0->tm_hour-t1->tm_hour) return t; if(t= t0->tm_min-t1->tm_min) return t; t= t0->tm_sec-t1->tm_sec; return t; } #define gencmp(i,j) timecmp(schedules[i],schedules[j],0) static struct schedule *tmp; #define genswap(i,j) {tmp=schedules[i]; schedules[i]=schedules[j]; schedules[j]=tmp;} void genqsort(l,r) /*safer version for worst case*/ register int l,r; { register int i,j,piv; /* other method for small arrays recommended in NUMREC is not used here does not give so large gain for moderate arrays and complicates the things, but would be worth trying (cf. profile) */ if(r<=l) return; /*1 element*/ if(gencmp(r,l)<0) genswap(l,r); if(r-l==1) return; /*2 elements and preparation for median*/ piv= (l+r)/2; /*pivoting by median of 3 - safer */ if(gencmp(piv,l)<0) genswap(l,piv); /*and change the pivot element implicitly*/ if(gencmp(r,piv)<0) genswap(r,piv); /*and change the pivot element implicitly*/ if(r-l==2) return; /*in the case of 3 elements we are finished too */ /*general case , l-th r-th already processed*/ i=l+1; j=r-1; do{ /*important sharp inequality - stops at sentinel element for efficiency*/ /* this is inefficient if all keys are equal - unnecessary n log n swaps are done, but we assume that it is atypical input*/ while(gencmp(i++,piv)<0); i--; while(gencmp(j--,piv)>0); j++; if(inext; } n_schedules=i; schedules= (struct schedule **) malloc(sizeof(struct schedule *)); if(!schedules) {fprintf(stderr,"no memory\n"); exit(100);} q=p; for(i=0; inext;} genqsort(0,n_schedules-1); /*reconnect to create a cyclic queue, sorted according to time*/ for(i=0; inext = schedules[i+1]; #ifdef oldversion schedules[n_schedules-1]->next=schedules[0]; #else schedules[n_schedules-1]->next=NULL; /*not a cyclic queue*/ #endif for(i=0; itime.tm_year ,schedules[i]->time.tm_mon+1 ,schedules[i]->time.tm_mday ,schedules[i]->time.tm_hour ,schedules[i]->time.tm_min ,schedules[i]->time.tm_sec ,schedules[i]->pgm0?schedules[i]->pgm0:"" ,schedules[i]->pgm1?schedules[i]->pgm1:"" ); } } void eventloop(niter,delay,f0,f1) double delay; int niter; char *f0,*f1; { struct schedule *current; int i; int firstpass=1; char *f0new, *f1new; #ifdef oldversion current=NULL; if(schedules) { time(&ttt); tms=localtime(&ttt); #ifdef amiga tms->tm_year+=1900; #endif for(i=0;itime,0)<=0) break; } } #endif while(niter-->0) { time(&ttt); tms=localtime(&ttt); #ifdef amiga tms->tm_year+=1900; #endif /*process schedules, if any NOTE!!!! does not work for mixing of daily events and once events!!!*/ if(schedules && (firstpass || (1+niter)%smodulo==0)) { firstpass=0; #ifdef oldversion if(timecmp(tms,current->time,0) <0 ) { if(timecmp(tms,&(schedules[0]->time),0)>=0) { current=schedules[0]; } } else /* current allowed*/ { if(current!= schedules[n_schedules-1]) { if(timecmp(tms,(¤t->next->time),0)>=0) current=current->next; } } if(current->pgm0) f0=current->pgm0; if(current->pgm1) f1=current->pgm1; #else /* run through the whole table without break (far next at will not hide nearer cron event accumulate the last (per def. most recent event separately for pgm0, pgm1 check that at's are not from previous day or older - such ones ignore*/ current=schedules[0]; f0new=f1new=NULL; /*search linear list - time consum, thus only in each smodulo-th step*/ while(current) { if(timecmp(tms,&(current->time),0)>=0 && (current->time.tm_mday == 0 /*cron*/ || current->time.tm_mday == tms->tm_mday && current->time.tm_mon == tms->tm_mon && current->time.tm_year == tms->tm_year ) ) { if(current->pgm0) f0new=current->pgm0; if(current->pgm1) f1new=current->pgm1; } current=current->next; } if(f0new) f0=f0new; if(f1new) f1=f1new; /* deleting old at events from queue not implemented yet - not crucial when queue is not extremely long*/ #endif } processevent(f0,f1); if(niter%modulo==0) fprintf(stderr," %d %02d %02d - %02d:%02d:%02d out01:%d %d in0123: %d %d %d %d state: run:%s:%s:\r", tms->tm_year, tms->tm_mon+1, tms->tm_mday, tms->tm_hour, tms->tm_min, tms->tm_sec, checkport(0),checkport(1),getport(0),getport(1),getport(2),getport(3),f0,f1); fflush(stderr); dsleep(delay); } } int main(argc,argv) int argc; char **argv; { char *waveform=NULL; int y,mo,d,program,h,m,s,t,p,v,l,i,niter; double delay; char prog[2][MAXSTATE+1]; struct schedule *sch,*ss; char name[256]; l=1; delay=1.0; prog[0][0]=prog[1][0]=0; program=0; niter=1000000000; modulo=1; smodulo=1; sch=NULL; /*option -D MUST be first!*/ if(argc>=3 && !strcmp(argv[1],"-D")) { strcpy(name,argv[2]); argv+=2; argc-=2; } else strcpy(name,"/dev/ttyS3"); init(name); while((--argc>0) && ((*++argv)[0] == '-')) { if(argc==0) usage(); switch(argv[0][1]) { case 'H': usage(); break; case 't': sscanf(*++argv,"%d",&p);argc--; v=checkport(p); switchport(p,v^1); break; case 'w': sscanf(*++argv,"%d",&h); sscanf(*++argv,"%d",&m); sscanf(*++argv,"%d",&s); sscanf(*++argv,"%d",&t); argc-=4; wakeup(h,m,s,t); break; case 'S': sscanf(*++argv,"%d",&y); sscanf(*++argv,"%d",&mo); sscanf(*++argv,"%d",&d); argc-=3; goto sS; case 's':y=d=mo=0; sS: program=1; {int now; ss=(struct schedule *) malloc(sizeof(struct schedule)); if(!ss) {fprintf(stderr,"no memory\n"); exit(100);} now=(sscanf(*++argv,"%d",&h)!= 1); sscanf(*++argv,"%d",&m); sscanf(*++argv,"%d",&s); argc-=3; if(now) { time(&ttt); tms=localtime(&ttt); #ifdef amiga tms->tm_year+=1900; #endif ss->time.tm_year=tms->tm_year; ss->time.tm_mon=tms->tm_mon; ss->time.tm_mday=tms->tm_mday; ss->time.tm_hour=tms->tm_hour; ss->time.tm_min=tms->tm_min; ss->time.tm_sec=tms->tm_sec; } else { ss->time.tm_year=y; ss->time.tm_mon=mo-1; ss->time.tm_mday=d; ss->time.tm_hour=h; ss->time.tm_min=m; ss->time.tm_sec=s; } if(**++argv != '-') {ss->pgm0=strdup(*argv);} else ss->pgm0=NULL; argc--; if(**++argv != '-') {ss->pgm1=strdup(*argv);} else ss->pgm1=NULL; argc--; ss->next=sch; sch=ss; } break; case 'l': sscanf(*++argv,"%d",&l);argc--; break; case 'P': sscanf(*++argv,"%d",&niter);argc--; break; case 'm': sscanf(*++argv,"%d",&modulo);argc--; break; case 'M': sscanf(*++argv,"%d",&smodulo);argc--; break; case 'd': sscanf(*++argv,"%lf",&delay);argc--; break; case 'p': program=1; sscanf(*++argv,"%d",&i);argc--; sscanf(*++argv,"%s",prog[i]);argc--; if(strlen(prog[i])<2) prog[i][0]=0; break; case 'W': waveform=strdup(*++argv); --argc; break; case 'i': default: break; } } if(argc!=0 && argc!=2) usage(); if(waveform) { while(*waveform) { switchport(0,((*waveform)-'0')&1); switchport(1,(((*waveform)-'0')&2)>>1); dsleep(delay); ++waveform; } goto ending; } if(sch) sortschedules(sch); if(program) eventloop(niter,delay,prog[0],prog[1]); if(argc==2) { sscanf(argv[0],"%d",&p); sscanf(argv[1],"%d",&v); switchport(p,v); sleep(5); } /*always print time and status*/ while(l-->0) { time(&ttt); tms=localtime(&ttt); #ifdef amiga tms->tm_year+=1900; #endif if(l%modulo==0) fprintf(stderr," %d %02d %02d - %02d:%02d:%02d out01:%d %d in0123: %d %d %d %d state: looping\r", tms->tm_year, tms->tm_mon+1, tms->tm_mday, tms->tm_hour, tms->tm_min, tms->tm_sec, checkport(0),checkport(1),getport(0),getport(1),getport(2),getport(3)); fflush(stderr); if(l>0) dsleep(delay); } ending: tidy(); fprintf(stderr,"\nDone.\n"); if(sch) freeschedules(); return 0; }