#ifdef MODULE
#include <linux/module.h>
#include <linux/string.h>
#else
#include <stdio.h>
#include <string.h>
#endif
#ifdef __RTL__
#include <rtlinux_signal.h>
#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;NumRung<NBR_RUNGS;NumRung++)
{
RungArray[NumRung].Used = FALSE;
strcpy(RungArray[NumRung].Label,"");
strcpy(RungArray[NumRung].Comment,"");
for (y=0;y<RUNG_HEIGHT;y++)
{
for(x=0;x<RUNG_WIDTH;x++)
{
RungArray[NumRung].Element[x][y].Type = ELE_FREE;
RungArray[NumRung].Element[x][y].ConnectedWithTop = 0;
RungArray[NumRung].Element[x][y].VarType = 0;
RungArray[NumRung].Element[x][y].VarNum = 0;
RungArray[NumRung].Element[x][y].DynamicInput = 0;
RungArray[NumRung].Element[x][y].DynamicState = 0;
RungArray[NumRung].Element[x][y].DynamicVarBak = 0;
RungArray[NumRung].Element[x][y].DynamicOutput = 0;
}
}
}
InfosGene->FirstRung = 0;
InfosGene->LastRung = 0;
InfosGene->CurrentRung = 0;
RungArray[0].Used = TRUE;
}
void PrepareRungs()
{
int NumRung;
int x,y;
char StateElement;
for (NumRung=0;NumRung<NBR_RUNGS;NumRung++)
{
for (y=0;y<RUNG_HEIGHT;y++)
{
for(x=0;x<RUNG_WIDTH;x++)
{
if ( (RungArray[NumRung].Element[x][y].Type==ELE_RISING_INPUT)
|| (RungArray[NumRung].Element[x][y].Type==ELE_FALLING_INPUT) )
{
StateElement = ReadVar(RungArray[NumRung].Element[x][y].VarType,RungArray[NumRung].Element[x][y].VarNum);
if (RungArray[NumRung].Element[x][y].Type==ELE_FALLING_INPUT)
StateElement = !StateElement;
RungArray[NumRung].Element[x][y].DynamicVarBak = StateElement;
}
}
}
}
}
#ifdef OLD_TIMERS_MONOS_SUPPORT
void InitTimers()
{
int NumTimer;
for (NumTimer=0; NumTimer<NBR_TIMERS; NumTimer++)
{
TimerArray[NumTimer].Preset = 0;
TimerArray[NumTimer].Base = TIME_BASE_SECS;
strcpy( TimerArray[NumTimer].DisplayFormat, "%.1fs" );
}
}
void PrepareTimers()
{
int NumTimer;
for (NumTimer=0; NumTimer<NBR_TIMERS; NumTimer++)
{
TimerArray[NumTimer].Value = TimerArray[NumTimer].Preset;
TimerArray[NumTimer].InputEnable = 0;
TimerArray[NumTimer].InputControl = 0;
TimerArray[NumTimer].OutputDone = 0;
TimerArray[NumTimer].OutputRunning = 0;
}
}
void InitMonostables()
{
int NumMonostable;
for (NumMonostable=0; NumMonostable<NBR_MONOSTABLES; NumMonostable++)
{
MonostableArray[NumMonostable].Preset = 0;
MonostableArray[NumMonostable].Base = TIME_BASE_SECS;
strcpy( MonostableArray[NumMonostable].DisplayFormat, "%.1fs" );
}
}
void PrepareMonostables()
{
int NumMonostable;
for (NumMonostable=0; NumMonostable<NBR_MONOSTABLES; NumMonostable++)
{
MonostableArray[NumMonostable].Value = 0;
MonostableArray[NumMonostable].Input = 0;
MonostableArray[NumMonostable].OutputRunning = 0;
MonostableArray[NumMonostable].InputBak = 0;
}
}
#endif
void InitCounters()
{
int NumCounter;
for (NumCounter=0; NumCounter<NBR_COUNTERS; NumCounter++)
{
CounterArray[NumCounter].Preset = 0;
}
}
void PrepareCounters()
{
int NumCounter;
for (NumCounter=0; NumCounter<NBR_COUNTERS; NumCounter++)
{
CounterArray[NumCounter].Value = 0;
CounterArray[NumCounter].ValueBak = 0;
CounterArray[NumCounter].InputReset = 0;
CounterArray[NumCounter].InputPreset = 0;
CounterArray[NumCounter].InputCountUp = 0;
CounterArray[NumCounter].InputCountUpBak = 0;
CounterArray[NumCounter].InputCountDown = 0;
CounterArray[NumCounter].InputCountDownBak = 0;
CounterArray[NumCounter].OutputDone = 0;
CounterArray[NumCounter].OutputEmpty = 0;
CounterArray[NumCounter].OutputFull = 0;
}
}
void InitTimersIEC()
{
int NumTimer;
for (NumTimer=0; NumTimer<NBR_TIMERS_IEC; NumTimer++)
{
NewTimerArray[NumTimer].Preset = 0;
NewTimerArray[NumTimer].Base = TIME_BASE_SECS;
NewTimerArray[NumTimer].TimerMode = TIMER_IEC_MODE_ON;
strcpy( NewTimerArray[NumTimer].DisplayFormat, "%.1fs" );
}
}
void PrepareTimersIEC()
{
int NumTimer;
for (NumTimer=0; NumTimer<NBR_TIMERS_IEC; NumTimer++)
{
NewTimerArray[NumTimer].Value = 0;
NewTimerArray[NumTimer].Input = 0;
NewTimerArray[NumTimer].InputBak = 0;
NewTimerArray[NumTimer].Output = 0;
NewTimerArray[NumTimer].TimerStarted = 0;
NewTimerArray[NumTimer].ValueToReachOneBaseUnit = 0;
}
}
void PrepareAllDatasBeforeRun( )
{
#ifdef OLD_TIMERS_MONOS_SUPPORT
PrepareTimers( );
PrepareMonostables( );
#endif
PrepareCounters( );
PrepareTimersIEC( );
PrepareRungs( );
#ifdef SEQUENTIAL_SUPPORT
PrepareSequential( );
#endif
}
void InitArithmExpr()
{
int NumExpr;
for (NumExpr=0; NumExpr<NBR_ARITHM_EXPR; NumExpr++)
strcpy(ArithmExpr[NumExpr].Expr,"");
}
void InitIOConf( )
{
int NumConf;
int NbrConf;
int Pass;
StrIOConf * pConf;
for( Pass=0; Pass<2; Pass++)
{
NbrConf = (Pass==0)?NBR_INPUTS_CONF:NBR_OUTPUTS_CONF;
for( NumConf=0; NumConf<NbrConf; NumConf++ )
{
pConf = (Pass==0)?&InfosGene->InputsConf[ 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;
if (x==0)
return 1;
if (TheRung->Element[x-1][y].DynamicOutput)
State = 1;
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;
}
if (y<RUNG_HEIGHT-1)
{
PosY = y+1;
StillConnected = TheRung->Element[x][PosY].ConnectedWithTop;
while( (PosY<RUNG_HEIGHT) && StillConnected)
{
if (TheRung->Element[x-1][PosY].DynamicOutput)
State = 1;
PosY++;
if (PosY<RUNG_HEIGHT)
{
if ( !(TheRung->Element[x][PosY].ConnectedWithTop) )
StillConnected = FALSE;
}
}
}
return State;
}
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;
}
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;
}
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;
}
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;
else
State = 1;
WriteVar(UpdateRung->Element[x][y].VarType,UpdateRung->Element[x][y].VarNum,State);
}
return State;
}
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;
}
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
void CalcTypeTimer(int x,int y,StrRung * UpdateRung)
{
StrTimer * Timer;
Timer = &TimerArray[UpdateRung->Element[x][y].VarNum];
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;
}
void CalcTypeMonostable(int x,int y,StrRung * UpdateRung)
{
StrMonostable * Monostable;
Monostable = &MonostableArray[UpdateRung->Element[x][y].VarNum];
if (x==0)
{
Monostable->Input = 1;
}
else
{
Monostable->Input = StateOnLeft(x-1,y,UpdateRung);
}
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
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 );
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;
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 );
}
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;
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 (CurrentValue<PresetValue)
DoIncTime = TRUE;
else
OutputResult = 1;
}
break;
case TIMER_IEC_MODE_OFF:
if ( TimerIEC->Input )
{
OutputResult = 1;
CurrentValue = 0;
TimerIEC->TimerStarted = 0;
}
else
{
if ( !TimerIEC->Input && TimerIEC->InputBak )
TimerIEC->TimerStarted = 1;
}
break;
case TIMER_IEC_MODE_PULSE:
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 (CurrentValue<PresetValue)
{
DoIncTime = TRUE;
}
else
{
OutputResult = 0;
CurrentValue = 0;
TimerIEC->TimerStarted = 0;
}
}
}
if ( DoIncTime )
{
TimerIEC->ValueToReachOneBaseUnit = TimerIEC->ValueToReachOneBaseUnit+InfosGene->GeneralParams.PeriodicRefreshMilliSecs;
if ( TimerIEC->ValueToReachOneBaseUnit>=TimerIEC->Base )
{
CurrentValue++;
TimerIEC->ValueToReachOneBaseUnit = TimerIEC->ValueToReachOneBaseUnit-TimerIEC->Base;
}
}
TimerIEC->InputBak = TimerIEC->Input;
UpdateRung->Element[x][y].DynamicOutput = OutputResult;
WriteVar( VAR_TIMER_IEC_DONE, TimerNbr, OutputResult );
WriteVar( VAR_TIMER_IEC_PRESET, TimerNbr, PresetValue );
WriteVar( VAR_TIMER_IEC_VALUE, TimerNbr, CurrentValue );
}
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;
}
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 );
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)
{
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;
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);
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 ); 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( y<RUNG_HEIGHT && JumpToRung==-1 );
y = 0;
x++;
}
while( x<RUNG_WIDTH && JumpToRung==-1 );
*JumpTo = JumpToRung;
return TRUE;
}
void RefreshASection( StrSection * pSection )
{
int Goto;
int Done = FALSE;
int NumRung = pSection->FirstRung;
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 ) {
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);
}
#define SR_STACK 25
void ClassicLadder_RefreshAllSections()
{
int ScanMainSection;
StrSection * pScanSection;
CycleStart();
for ( ScanMainSection=0; ScanMainSection<NBR_SECTIONS; ScanMainSection++ )
{
pScanSection = &SectionArray[ ScanMainSection ];
if ( pScanSection->Used && pScanSection->SubRoutineNumber==-1 && pScanSection->Language==SECTION_IN_LADDER )
{
RefreshASection( pScanSection );
}
#ifdef SEQUENTIAL_SUPPORT
if ( pScanSection->Used && pScanSection->Language==SECTION_IN_SEQUENTIAL )
{
RefreshSequentialPage( pScanSection->SequentialPage );
}
#endif
}
CycleEnd();
}
void CopyRungToRung(StrRung * RungSrc,StrRung * RungDest)
{
memcpy(RungDest,RungSrc,sizeof(StrRung));
}