/* Classic Ladder Project */ /* Copyright (C) 2001-2007 Marc Le Douarain */ /* http://membres.lycos.fr/mavati/classicladder/ */ /* http://www.sourceforge.net/projects/classicladder */ /* February 2001 */ /* -------------- */ /* Refresh a rung */ /* -------------- */ /* This library is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU Lesser General Public */ /* License as published by the Free Software Foundation; either */ /* version 2.1 of the License, or (at your option) any later version. */ /* This library is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ /* Lesser General Public License for more details. */ /* You should have received a copy of the GNU Lesser General Public */ /* License along with this library; if not, write to the Free Software */ /* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef MODULE #include #include #else #include #include #endif #ifdef __RTL__ #include #endif #include "classicladder.h" #include "global.h" #include "vars_access.h" #include "arithm_eval.h" #include "manager.h" #ifdef SEQUENTIAL_SUPPORT #include "calc_sequential.h" #endif #include "calc.h" void InitRungs() { int NumRung; int x,y; for (NumRung=0;NumRungFirstRung = 0; InfosGene->LastRung = 0; InfosGene->CurrentRung = 0; RungArray[0].Used = TRUE; } /* Set DynamicVarBak (Element) to the right value before calculating the rungs */ /* for detecting rising/falling edges used in some elements */ void PrepareRungs() { int NumRung; int x,y; char StateElement; for (NumRung=0;NumRungInputsConf[ NumConf ]:&InfosGene->OutputsConf[ NumConf ]; pConf->FirstClassicLadderIO = -1; pConf->DeviceType = 0; pConf->SubDevOrAdr = 0; pConf->FirstChannel = 0; pConf->NbrConsecutivesChannels = 1; pConf->FlagInverted = 0; } } } char StateOnLeft(int x,int y,StrRung * TheRung) { char State = 0; int PosY; char StillConnected; // directly connected to the "left"? if yes, ON ! if (x==0) return 1; /* Direct on left */ if (TheRung->Element[x-1][y].DynamicOutput) State = 1; /* Up */ PosY = y; StillConnected = TheRung->Element[x][PosY].ConnectedWithTop; while( (PosY>0) && StillConnected) { PosY--; if (TheRung->Element[x-1][PosY].DynamicOutput) State = 1; if ( !(TheRung->Element[x][PosY].ConnectedWithTop) ) StillConnected = FALSE; } /* Down */ if (yElement[x][PosY].ConnectedWithTop; while( (PosYElement[x-1][PosY].DynamicOutput) State = 1; PosY++; if (PosYElement[x][PosY].ConnectedWithTop) ) StillConnected = FALSE; } } } return State; } /* Elements : -| |- and -|/|- */ char CalcTypeInput(int x,int y,StrRung * UpdateRung,char IsNot,char OnlyFronts) { char State; char StateElement; char StateVar; StateElement = ReadVar(UpdateRung->Element[x][y].VarType,UpdateRung->Element[x][y].VarNum); if (IsNot) StateElement = !StateElement; StateVar = StateElement; if (OnlyFronts) { if (StateElement && UpdateRung->Element[x][y].DynamicVarBak) StateElement = 0; } UpdateRung->Element[x][y].DynamicState = StateElement; if (x==0) { State = StateElement; } else { UpdateRung->Element[x][y].DynamicInput = StateOnLeft(x,y,UpdateRung); State = StateElement && UpdateRung->Element[x][y].DynamicInput; } UpdateRung->Element[x][y].DynamicOutput = State; UpdateRung->Element[x][y].DynamicVarBak = StateVar; return State; } /* Element : --- */ char CalcTypeConnection(int x,int y,StrRung * UpdateRung) { char State; char StateElement; StateElement = 1; if (x==0) { State = StateElement; } else { UpdateRung->Element[x][y].DynamicInput = StateOnLeft(x,y,UpdateRung); State = StateElement && UpdateRung->Element[x][y].DynamicInput; } UpdateRung->Element[x][y].DynamicState = State; UpdateRung->Element[x][y].DynamicOutput = State; return State; } /* Elements : -( )- and -(/)- */ char CalcTypeOutput(int x,int y,StrRung * UpdateRung,char IsNot) { char State; State = StateOnLeft(x,y,UpdateRung); UpdateRung->Element[x][y].DynamicInput = State; UpdateRung->Element[x][y].DynamicState = State; if (IsNot) State = !State; WriteVar(UpdateRung->Element[x][y].VarType,UpdateRung->Element[x][y].VarNum,State); return State; } /* Elements : -(S)- and -(R)- */ char CalcTypeOutputSetReset(int x,int y,StrRung * UpdateRung,char IsReset) { char State; UpdateRung->Element[x][y].DynamicInput = StateOnLeft(x,y,UpdateRung); State = UpdateRung->Element[x][y].DynamicInput; UpdateRung->Element[x][y].DynamicState = State; if (State) { if (IsReset) State = 0; /* reset */ else State = 1; /* set */ WriteVar(UpdateRung->Element[x][y].VarType,UpdateRung->Element[x][y].VarNum,State); } return State; } /* Element : -(J)- */ int CalcTypeOutputJump(int x,int y,StrRung * UpdateRung) { char State; int Goto = -1; State = StateOnLeft(x,y,UpdateRung); if (State) Goto = UpdateRung->Element[x][y].VarNum; UpdateRung->Element[x][y].DynamicInput = State; UpdateRung->Element[x][y].DynamicState = State; return Goto; } /* Element : -(C)- */ int CalcTypeOutputCall(int x,int y,StrRung * UpdateRung) { char State; int CallSrSection = -1; State = StateOnLeft(x,y,UpdateRung); if (State) CallSrSection = SearchSubRoutineWithItsNumber( UpdateRung->Element[x][y].VarNum ); UpdateRung->Element[x][y].DynamicInput = State; UpdateRung->Element[x][y].DynamicState = State; return CallSrSection; } #ifdef OLD_TIMERS_MONOS_SUPPORT /* Element : Timer (2x2 Blocks) */ // Marc added a control pin to the old timers to add features // For EMC, force (C) control pin to always be true so it doesn't // break older programs void CalcTypeTimer(int x,int y,StrRung * UpdateRung) { StrTimer * Timer; Timer = &TimerArray[UpdateRung->Element[x][y].VarNum]; // directly connected to the "left"? if yes, ON ! if (x==0) { Timer->InputEnable = 1; } else { Timer->InputEnable = StateOnLeft(x-1,y,UpdateRung); } if (x==0) { Timer->InputControl = 1; } else { Timer->InputControl = StateOnLeft(x-1,y+1,UpdateRung); } if (!Timer->InputEnable) { Timer->OutputRunning = 0; Timer->OutputDone = 0; Timer->Value = Timer->Preset; } else { if (Timer->Value>0) { if ( Timer->InputControl ) { Timer->Value = Timer->Value - InfosGene->GeneralParams.PeriodicRefreshMilliSecs; Timer->OutputRunning = 1; Timer->OutputDone = 0; } } else { Timer->OutputRunning = 0; Timer->OutputDone = 1; } } UpdateRung->Element[x][y].DynamicOutput = Timer->OutputDone; UpdateRung->Element[x][y+1].DynamicOutput = Timer->OutputRunning; } /* Element : Monostable (2x2 Blocks) */ void CalcTypeMonostable(int x,int y,StrRung * UpdateRung) { StrMonostable * Monostable; Monostable = &MonostableArray[UpdateRung->Element[x][y].VarNum]; // directly connected to the "left"? if yes, ON ! if (x==0) { Monostable->Input = 1; } else { Monostable->Input = StateOnLeft(x-1,y,UpdateRung); } /* detecting impulse on input, the monostable is not retriggerable */ if (Monostable->Input && !Monostable->InputBak && (Monostable->Value==0) ) { Monostable->OutputRunning = 1; Monostable->Value = Monostable->Preset; } if (Monostable->Value>0) Monostable->Value = Monostable->Value - InfosGene->GeneralParams.PeriodicRefreshMilliSecs; else Monostable->OutputRunning = 0; Monostable->InputBak = Monostable->Input; UpdateRung->Element[x][y].DynamicOutput = Monostable->OutputRunning; } #endif /* Element : Counter (2x4 Blocks) */ void CalcTypeCounter(int x,int y,StrRung * UpdateRung) { int CounterNbr = UpdateRung->Element[x][y].VarNum; StrCounter * Counter = &CounterArray[ CounterNbr ]; char DoneResult, EmptyResult, FullResult; int CurrentValue = ReadVar( VAR_COUNTER_VALUE, CounterNbr ); int PresetValue = ReadVar( VAR_COUNTER_PRESET, CounterNbr ); // directly connected to the "left"? if yes, ON ! if ( x==0 ) { Counter->InputReset = 1; Counter->InputPreset = 1; Counter->InputCountUp = 1; Counter->InputCountDown = 1; } else { Counter->InputReset = StateOnLeft(x-1,y,UpdateRung); Counter->InputPreset = StateOnLeft(x-1,y+1,UpdateRung); Counter->InputCountUp = StateOnLeft(x-1,y+2,UpdateRung); Counter->InputCountDown = StateOnLeft(x-1,y+3,UpdateRung); } if ( Counter->InputCountUp && Counter->InputCountUpBak==0 ) { Counter->ValueBak = CurrentValue; CurrentValue++; if ( CurrentValue>9999 ) CurrentValue = 0; } if ( Counter->InputCountDown && Counter->InputCountDownBak==0 ) { Counter->ValueBak = CurrentValue; CurrentValue--; if ( CurrentValue<0 ) CurrentValue = 9999; } if ( Counter->InputPreset ) { Counter->ValueBak = CurrentValue; CurrentValue = PresetValue; } if ( Counter->InputReset ) { Counter->ValueBak = CurrentValue; CurrentValue = 0; } Counter->InputCountUpBak = Counter->InputCountUp; Counter->InputCountDownBak = Counter->InputCountDown; DoneResult = ( CurrentValue==PresetValue )?1:0; EmptyResult = ( CurrentValue==9999 && Counter->ValueBak==0 )?1:0; FullResult = ( CurrentValue==0 && Counter->ValueBak==9999 )?1:0; UpdateRung->Element[x][y + 1].DynamicOutput = DoneResult; UpdateRung->Element[x][y].DynamicOutput = EmptyResult; UpdateRung->Element[x][y + 2].DynamicOutput = FullResult; // now update public vars // (we could have directly written in the counter structure) // (but on another project, vars can be mapped in another way) WriteVar( VAR_COUNTER_DONE, CounterNbr, DoneResult ); WriteVar( VAR_COUNTER_EMPTY, CounterNbr, EmptyResult ); WriteVar( VAR_COUNTER_FULL, CounterNbr, FullResult ); WriteVar( VAR_COUNTER_PRESET, CounterNbr, PresetValue ); WriteVar( VAR_COUNTER_VALUE, CounterNbr, CurrentValue ); } /* Element : New IEC Timer with many modes (2x2 Blocks) */ void CalcTypeTimerIEC(int x,int y,StrRung * UpdateRung) { int TimerNbr = UpdateRung->Element[x][y].VarNum; StrTimerIEC * TimerIEC = &NewTimerArray[ TimerNbr ]; int CurrentValue = ReadVar( VAR_TIMER_IEC_VALUE, TimerNbr ); int PresetValue = ReadVar( VAR_TIMER_IEC_PRESET, TimerNbr ); char OutputResult = ReadVar( VAR_TIMER_IEC_DONE, TimerNbr ); char DoIncTime = FALSE; // directly connected to the "left"? if yes, ON ! if (x==0) { TimerIEC->Input = 1; } else { TimerIEC->Input = StateOnLeft(x-1,y,UpdateRung); } switch( TimerIEC->TimerMode ) { case TIMER_IEC_MODE_ON: if (!TimerIEC->Input) { OutputResult = 0; CurrentValue = 0; } else { if (CurrentValueInput ) { OutputResult = 1; CurrentValue = 0; TimerIEC->TimerStarted = 0; } else { /* detecting falling edge on input */ if ( !TimerIEC->Input && TimerIEC->InputBak ) TimerIEC->TimerStarted = 1; } break; case TIMER_IEC_MODE_PULSE: /* detecting rising edge on input, the monostable is not retriggerable */ if (TimerIEC->Input && !TimerIEC->InputBak && TimerIEC->TimerStarted==0 ) { OutputResult = 1; CurrentValue = 0; TimerIEC->TimerStarted = 1; } break; } if ( TimerIEC->TimerMode==TIMER_IEC_MODE_OFF || TimerIEC->TimerMode==TIMER_IEC_MODE_PULSE ) { if (TimerIEC->TimerStarted ) { if (CurrentValueTimerStarted = 0; } } } if ( DoIncTime ) { TimerIEC->ValueToReachOneBaseUnit = TimerIEC->ValueToReachOneBaseUnit+InfosGene->GeneralParams.PeriodicRefreshMilliSecs; if ( TimerIEC->ValueToReachOneBaseUnit>=TimerIEC->Base ) { CurrentValue++; // do not lost the little too-much time part... TimerIEC->ValueToReachOneBaseUnit = TimerIEC->ValueToReachOneBaseUnit-TimerIEC->Base; } } TimerIEC->InputBak = TimerIEC->Input; UpdateRung->Element[x][y].DynamicOutput = OutputResult; // now update public vars // (we could have directly written in the IEC Timer structure) // (but on another project, vars can be mapped in another way) WriteVar( VAR_TIMER_IEC_DONE, TimerNbr, OutputResult ); WriteVar( VAR_TIMER_IEC_PRESET, TimerNbr, PresetValue ); WriteVar( VAR_TIMER_IEC_VALUE, TimerNbr, CurrentValue ); } /* Element : Compar (3 Horizontal Blocks) */ char CalcTypeCompar(int x,int y,StrRung * UpdateRung) { char State; char StateElement; StateElement = EvalCompare(ArithmExpr[UpdateRung->Element[x][y].VarNum].Expr); UpdateRung->Element[x][y].DynamicState = StateElement; if (x==2) { State = StateElement; } else { UpdateRung->Element[x-2][y].DynamicInput = StateOnLeft(x-2,y,UpdateRung); State = StateElement && UpdateRung->Element[x-2][y].DynamicInput; } UpdateRung->Element[x][y].DynamicOutput = State; return State; } /* Element : Operate (3 Horizontal Blocks) */ char CalcTypeOutputOperate(int x,int y,StrRung * UpdateRung) { char State; State = StateOnLeft(x-2,y,UpdateRung); if (State) MakeCalc(ArithmExpr[UpdateRung->Element[x][y].VarNum].Expr,FALSE /* verify mode */); UpdateRung->Element[x][y].DynamicInput = State; UpdateRung->Element[x][y].DynamicState = State; return State; } int RefreshRung(StrRung * Rung, int * JumpTo) { int x = 0, y = 0; int JumpToRung = -1; int SectionToCall = -1; do { do { switch(Rung->Element[x][y].Type) { /* MLD,16/5/2001,V0.2.8 , fixed for drawing */ case ELE_FREE: case ELE_UNUSABLE: if (StateOnLeft(x,y,Rung)) Rung->Element[x][y].DynamicInput = 1; else Rung->Element[x][y].DynamicInput = 0; break; /* End fix */ case ELE_INPUT: CalcTypeInput(x,y,Rung,FALSE,FALSE); break; case ELE_INPUT_NOT: CalcTypeInput(x,y,Rung,TRUE,FALSE); break; case ELE_RISING_INPUT: CalcTypeInput(x,y,Rung,FALSE,TRUE); break; case ELE_FALLING_INPUT: CalcTypeInput(x,y,Rung,TRUE,TRUE); break; case ELE_CONNECTION: CalcTypeConnection(x,y,Rung); break; #ifdef OLD_TIMERS_MONOS_SUPPORT case ELE_TIMER: CalcTypeTimer(x,y,Rung); break; case ELE_MONOSTABLE: CalcTypeMonostable(x,y,Rung); break; #endif case ELE_COUNTER: CalcTypeCounter(x,y,Rung); break; case ELE_TIMER_IEC: CalcTypeTimerIEC(x,y,Rung); break; case ELE_COMPAR: CalcTypeCompar(x,y,Rung); break; case ELE_OUTPUT: CalcTypeOutput(x,y,Rung,FALSE); break; case ELE_OUTPUT_NOT: CalcTypeOutput(x,y,Rung,TRUE); break; case ELE_OUTPUT_SET: CalcTypeOutputSetReset(x,y,Rung,FALSE); break; case ELE_OUTPUT_RESET: CalcTypeOutputSetReset(x,y,Rung,TRUE); break; case ELE_OUTPUT_JUMP: JumpToRung = CalcTypeOutputJump(x,y,Rung); // we will now abort the refresh of the rung immediately... break; case ELE_OUTPUT_CALL: SectionToCall = CalcTypeOutputCall(x,y,Rung); if ( SectionToCall!=-1 ) { StrSection * pSubRoutineSection = &SectionArray[ SectionToCall ]; if ( pSubRoutineSection->Used && pSubRoutineSection->SubRoutineNumber>=0 ) RefreshASection( pSubRoutineSection ); //recursive call! ;-) else debug_printf("Refresh rungs aborted - call to a sub-routine undefined or programmed as main !!!"); } break; case ELE_OUTPUT_OPERATE: CalcTypeOutputOperate(x,y,Rung); break; } y++; }while( yFirstRung; int MadLoopBreak = 0; do { RefreshRung(&RungArray[NumRung], &Goto); if ( Goto!=-1 ) { if (!RungArray[Goto].Used) { Done = TRUE; debug_printf("Refresh rungs aborted - jump to an undefined rung found in rung No%d...\n",Goto); } NumRung = Goto; MadLoopBreak++; if ( MadLoopBreak>99999 ) //value to set here?... or else measuring time? { Done = TRUE; debug_printf("Refresh rungs aborted - mad loop jump detected - STOPPED...!\n"); InfosGene->LadderState = STATE_STOP; } } else { if (NumRung == pSection->LastRung) Done = TRUE; else NumRung = RungArray[ NumRung ].NextRung; } } while(!Done); } // All the sections 'main' are refreshed in the order defined. #define SR_STACK 25 void ClassicLadder_RefreshAllSections() { int ScanMainSection; StrSection * pScanSection; CycleStart(); for ( ScanMainSection=0; ScanMainSectionUsed && pScanSection->SubRoutineNumber==-1 && pScanSection->Language==SECTION_IN_LADDER ) { RefreshASection( pScanSection ); } #ifdef SEQUENTIAL_SUPPORT // current section defined and is in sequential language if ( pScanSection->Used && pScanSection->Language==SECTION_IN_SEQUENTIAL ) { RefreshSequentialPage( pScanSection->SequentialPage ); } #endif }// for( ) CycleEnd(); //TODO: times measures should be moved directly in the module task // time measurement has been moved to module_hal.c for EMC } void CopyRungToRung(StrRung * RungSrc,StrRung * RungDest) { memcpy(RungDest,RungSrc,sizeof(StrRung)); }