18 KiB
Input
Reset Bool I_BottleStep_Chain Bool I_BottleStep_Doser Bool I_Doser_Encoder Real I_Chain_Encoder Real Min Accumulation Sensor Bool Max Accumulation Sensor Bool Jam Photocell Bool Chain Piston Min Sensor Bool Chain Piston Max Sensor Bool Rejector Feedback State Bool Safety Ok Bool OutFeed Guide Jam Bool Low Speed Sensor Bool High Speed Sensor Bool Bottle fallen infeed Bool Reset Alarms Bool Scan Time Real Chain Speed Real
Output
InOut
Main Chain Motor "UDT Motor" Belt Motor "UDT Motor" Doser Motor 1 "UDT Motor" Static Recipe "UDT Tilter_Recipe" PID "UDT AnalogDevice" Aux Struct Status "UDT Tilter_Status" Switches "UDT Tilter_Switches" FeedBack "UDT Tilter_Feedback" Alarms "UDT Tilter_Alarms" Fixed Paramters "UDT Tilter_Fixed Parameters" Doser Speed "FB Correct Speed F/Pulses" Diiference Phases "FB Calculate Diff Phase Pulses" Start Ramp "FB Calculate Ramp" Stop Ramp "FB Calculate Ramp" Offset Master Pulse "FB Offset Pulse" Offset Master Pulse Start "FB Offset Pulse" FB Speed Change FIFO "FB FIFO Manage DataType Real" Alarms List "FB Show Alarms"
Temp
Chain Speed Scaled Real n Int RePhase s Real Bottles From Start Infeed Int
Constant
Scaled Speed Int Scaled for Doser Int Scaled with gap Int Max Count Int Max Encoder Value Real
IF #"Scan Time" = 0 OR #"Scan Time" > 1 THEN // Check the for the First Cycle
RETURN;
END_IF;
IF ABS(#I_Doser_Encoder - #Aux."Last Doser Encoder") < #"Max Encoder Value" THEN
#Aux."Pos Doser Encoder" := #Aux."Pos Doser Encoder" + ABS(#I_Doser_Encoder - #Aux."Last Doser Encoder");
IF #Aux."Pos Doser Encoder" > #"Max Encoder Value" THEN
#Aux."Pos Doser Encoder" := #Aux."Pos Doser Encoder" - #"Max Encoder Value";
END_IF;
END_IF;
IF ABS(#I_Chain_Encoder - #Aux."Last Chain Encoder") < #"Max Encoder Value" THEN
#Aux."Pos Chain Encoder" := #Aux."Pos Chain Encoder" + ABS(#I_Chain_Encoder - #Aux."Last Chain Encoder");
IF #Aux."Pos Chain Encoder" > #"Max Encoder Value" THEN
#Aux."Pos Chain Encoder" := #Aux."Pos Chain Encoder" - #"Max Encoder Value";
END_IF;
END_IF;
#Aux."Last Doser Encoder" := #I_Doser_Encoder;
#Aux."Last Chain Encoder" := #I_Chain_Encoder;
IF #I_BottleStep_Doser AND NOT #Status."Infeed Home Sensor" THEN
IF #Aux."Pos Doser Encoder" > #Aux."Doser Homing Value" THEN
#Aux."Doser Ratio" := (#Aux."Pos Doser Encoder" - #Aux."Doser Homing Value") / 360.0;
END_IF;
#Aux."Doser Homing Value" := #Aux."Pos Doser Encoder";
END_IF;
IF #I_BottleStep_Chain AND NOT #Status."Chain Home Sensor" THEN
IF #Aux."Pos Chain Encoder" > #Aux."Chain Homing Value" THEN
#Aux."Chain Ratio" := (#Aux."Chain Ratio" * 99 + (#Aux."Pos Chain Encoder" - #Aux."Chain Homing Value") / 360.0) / 100.0;
END_IF;
#Aux."Chain Homing Value" := #Aux."Pos Chain Encoder";
END_IF;
IF #Aux."Pos Doser Encoder" < #Aux."Doser Homing Value" THEN
// If the value reach max we substract from last value to obtain a negative value
// of the position ocurred before reaching max limit
// this negative value will be added next when is used as substract with the ratio calculation
#Aux."Doser Homing Value" := #Aux."Doser Homing Value" - #"Max Encoder Value";
END_IF;
IF #Aux."Pos Chain Encoder" < #Aux."Chain Homing Value" THEN
#Aux."Chain Homing Value" := #Aux."Chain Homing Value" - #"Max Encoder Value";
END_IF;
#Status."Infeed Home Sensor" := #I_BottleStep_Doser;
#Status."Chain Home Sensor" := #I_BottleStep_Chain;
IF #Aux."Chain Ratio" <> 0 AND #Aux."Doser Ratio" <> 0 THEN
#Status."Infeed Encoder" := (#Aux."Pos Doser Encoder" - #Aux."Doser Homing Value") / #Aux."Doser Ratio";
#Status."Chain Encoder" := (#Aux."Pos Chain Encoder" - #Aux."Chain Homing Value") / #Aux."Chain Ratio";
END_IF;
IF #Status."Infeed Encoder" > 360 THEN
#Status."Infeed Encoder" := 360;
END_IF;
IF #Status."Chain Encoder" > 360 THEN
#Status."Chain Encoder" := 360;
END_IF;
// Main Chain
"FC Scale Speed w/Gap"(Speed:=#"Chain Speed",
IN_MIN:=0,
IN_MAX:=1000.0,
OUT_MIN:=0,
OUT_MAX:=#"Fixed Paramters"."Chain Speed b/h at 100hz",
"Bottle Diameter":=#Recipe."Bottle Diameter",
Step:=#"Fixed Paramters"."Chain Step",
Type:=#"Scaled Speed",
"Speed Scaled"=>#"Chain Speed Scaled");
// RUNG 2 : STATUS & TIMERS
#Aux.Timers[1].S := #"Min Accumulation Sensor";
#Aux.Timers[2].S := #"Max Accumulation Sensor";
#Aux.Timers[3].S := #"Low Speed Sensor";
#Aux.Timers[4].S := #"High Speed Sensor";
#Aux.Timers[5].S := #"Jam Photocell";
#Aux.Timers[6].S := NOT #"OutFeed Guide Jam";
#Aux.Timers[7].S := #Switches."Enable Auto Empting" AND NOT #Status."Bottles Inside Tilter" AND NOT #"Min Accumulation Sensor"; // Auto Empting
#Aux.Timers[8].S := #Switches."Enable Energy Saving" AND NOT #Status."Bottles Inside Tilter" AND NOT #"Min Accumulation Sensor"; // Energy Saving
#Status."Wait Min Accumulation" := NOT #Aux.Timers[1].Q;
#Status."Max Accumulation Reached" := #Aux.Timers[2].Q;
#Status."Low Speed" := #Aux.Timers[3].Q;
#Status."High Speed" := #Aux.Timers[4].Q;
#Status."Tilter in Auto Emptying" := #Switches."Enable Auto Empting" AND #Aux.Timers[7].Q AND NOT #Status."RePhase Doser Empty";
#Status."Tilter in Energy Save Mode" := #Switches."Enable Energy Saving" AND #Aux.Timers[8].Q;
#Status."Automatic Mode" := #"Main Chain Motor".M_Cycle_ON_AUTO AND #"Belt Motor".M_Cycle_ON_AUTO AND #"Doser Motor 1".M_Cycle_ON_AUTO
AND NOT #Status."Stop in Alarm";
// RUNG 3 : FEEDBACKs
#FeedBack."Belt is Running" := #"Belt Motor".STATUS_VFD_Run_FWD;
#FeedBack."Main Chain is Running" := #"Main Chain Motor".STATUS_VFD_Run_FWD;
#FeedBack."Doser is Running" := #"Doser Motor 1".STATUS_VFD_Run_FWD;
// RUNG 4 : Calculate Offset on Step Pulse
// STEP Calcs
IF #"Chain Speed Scaled" > 0 THEN
#Aux."Estimated Step Time s" := (3600.0 / #"Chain Speed Scaled");
ELSE
#Aux."Estimated Step Time s" := 0;
END_IF;
#"Offset Master Pulse"("Pulse In" := #"I_BottleStep_Chain",
"Step mm" := #"Fixed Paramters"."Chain Step",
"Ofsset in mm" := #"Fixed Paramters"."Distance Pht to Step sensor mm" + #Recipe."Offset Chain Phase mm",
"Auto Calc Step Time" := FALSE,
"Fix Step Time" := #Aux."Estimated Step Time s");
#"Offset Master Pulse Start"("Pulse In" := #"I_BottleStep_Chain",
"Step mm" := #"Fixed Paramters"."Chain Step",
"Ofsset in mm" := #"Fixed Paramters"."Distance Pht to Step sensor mm" + #Recipe."Offset Chain Phase Startup mm",
"Auto Calc Step Time" := FALSE,
"Fix Step Time" := #Aux."Estimated Step Time s");
// RUNG 5 : Count bottles entering on the Tilter
#Aux."Bottle Pulse" := #I_BottleStep_Doser AND NOT #Aux.ONs[1];
#Aux.ONs[1] := #I_BottleStep_Doser;
// Bottle Counts on Start Phase
IF #Aux."Bottle Pulse" AND #Aux.Counters[1] < #"Max Count" THEN
#Aux.Counters[1] := #Aux.Counters[1] + 1;
END_IF;
IF #Status."Doser Enable" THEN
#"Bottles From Start Infeed" := #Aux.Counters[1];
ELSE
#"Bottles From Start Infeed" := 0;
END_IF;
// Steps Counts between bottles
IF #"Offset Master Pulse"."Pulse Out" AND #Aux.Counters[2] < #"Max Count" THEN
#Aux.Counters[2] := #Aux.Counters[2] + 1;
END_IF;
IF #Aux."Bottle Pulse" OR NOT #Status."Doser Enable" THEN
#Aux.Counters[2] := 0;
END_IF;
// Steps Counts on start
IF #"Offset Master Pulse"."Pulse Out" AND #Aux.Counters[3] < #"Max Count" THEN
#Aux.Counters[3] := #Aux.Counters[3] + 1;
END_IF;
IF NOT #FeedBack."Main Chain is Running" THEN
#Aux.Counters[3] := 0;
END_IF;
IF #Aux."Bottle Pulse" THEN
#Aux."Bottles Inside Tilter" := #"Fixed Paramters"."Number of Slots on Main Chain";
END_IF;
IF #"Offset Master Pulse"."Pulse Out" AND #Aux."Bottles Inside Tilter" > 0 THEN
#Aux."Bottles Inside Tilter" := #Aux."Bottles Inside Tilter" - 1;
END_IF;
#Status."Bottles Inside Tilter" := #Aux."Bottles Inside Tilter" > 0;
// RUNG 6 : DOSER Start Stop
#Status."Conditions to Run Doser" := #Status."Automatic Mode" AND #Switches."Enable Infeed Selector" AND (NOT #Status."Wait Min Accumulation"
OR #Switches."Bypass Minimun Accumulation" OR #Status."Tilter in Auto Emptying") AND NOT #Status."Max Accumulation Reached" AND
#FeedBack."Belt is Running" AND #FeedBack."Main Chain is Running" AND #Aux.Counters[3] > 1
AND NOT #Alarms."Bottle Fallen Infeed" AND NOT #Alarms."Jam Infeed Chain" AND NOT #Status."Tilter in Energy Save Mode";
// With the next bottle we stop
// End STOP PHASE
IF #Status."Phase Stop Doser" AND #Aux."Bottle Pulse" THEN
#Status."Doser Enable" := FALSE;
#Status."Phase Stop Doser" := FALSE;
END_IF;
// STOP PHASE
IF NOT #Status."Conditions to Run Doser" AND #Status."Doser Enable" AND #Aux."Bottle Pulse" THEN
#Status."Phase Stop Doser" := TRUE;
END_IF;
// START PHASE
IF #Status."Conditions to Run Doser" AND NOT #Status."Doser Enable" AND #"Offset Master Pulse Start"."Pulse Out" THEN
#Status."Phase Start Doser" := TRUE;
#Status."Doser Enable" := TRUE;
#Aux.Counters[1] := 0;
END_IF;
// End START PHASE
IF #Status."Phase Start Doser" AND #Aux.Counters[1] >= 1 THEN
#Status."Phase Start Doser" := FALSE;
END_IF;
// Need Rephase
IF #Aux.Counters[2] > 3 THEN
#Status."RePhase Doser Empty" := TRUE;
END_IF;
IF #Status."RePhase Doser Empty" AND #Aux."Bottle Pulse" THEN
#Status."RePhase Doser Empty" := FALSE;
END_IF;
// Reset Logic if the machine is not in Automatic mode
IF NOT #Status."Automatic Mode" THEN
#Status."RePhase Doser Empty" := FALSE;
#Status."Phase Start Doser" := FALSE;
#Status."Phase Stop Doser" := FALSE;
#Status."Doser Enable" := FALSE;
END_IF;
// RUNG 7 : Main Chain && Belt
#Status."Chain Enable" := #Status."Automatic Mode" AND NOT #Status."Tilter in Energy Save Mode" AND NOT #Alarms."Jam Infeed Chain"; // Remove AND NOT #Alarms."Jam Infeed Chain"
#Status."Belt Enable" := #Status."Automatic Mode" AND NOT #Status."Tilter in Energy Save Mode" AND (NOT #Alarms."Bottle Fallen Infeed" OR #Status."Bottles Inside Tilter") AND NOT #Alarms."Jam Infeed Chain" ; // AND NOT #Alarms."Jam Infeed Chain"
// RUNG 8 : Expulsion Bar at Outfeed
#Status."Outfeed Expulsion" := FALSE;
// Need to finish
// RUNG 9 : SPEEDS
// Number of bottles between the Doser and the Chain
#Aux."Num of bottles From Doser To Chain" := REAL_TO_INT ( (#"Fixed Paramters"."Distance Pht to Step sensor mm" / #Recipe."Bottle Diameter") - 2 ); // 2 bott are inside the doser
IF #Aux."Num of bottles From Doser To Chain" < 0 THEN
#Aux."Num of bottles From Doser To Chain" := 0;
END_IF;
IF #"Bottles From Start Infeed" < #"Fixed Paramters"."Number of Slots on Main Chain" / 4 OR #Status."Low Speed" THEN
#Status."Chain Request Speed" := #Recipe."Low Speed";
ELSIF #Status."High Speed" AND #"Bottles From Start Infeed" > #"Fixed Paramters"."Number of Slots on Main Chain" THEN
#Status."Chain Request Speed" := #Recipe."Over Speed";
ELSE
#Status."Chain Request Speed" := #Recipe."Production Speed";
END_IF;
// For TEST
// *******************************************
IF #"Bottles From Start Infeed" < 20 OR NOT #Status."Doser Enable" THEN
#Status."Chain Request Speed" := #Recipe."Low Speed";
ELSIF #"Bottles From Start Infeed" > 40 THEN
#Status."Chain Request Speed" := #Recipe."Over Speed";
ELSE
#Status."Chain Request Speed" := #Recipe."Production Speed";
END_IF;
// *******************************************
IF NOT (#Status."Doser Enable" OR #Status."Bottles Inside Tilter") THEN
#Status."Chain Speed" := #Status."Chain Request Speed";
#Aux."Speed FFordward to Doser" := 0;
ELSE //
// Ramp slow with "Ramp Speed bot/step"
IF #"Offset Master Pulse"."Pulse Out" THEN
IF #Status."Chain Speed" < #Status."Chain Request Speed" THEN
#Status."Chain Speed" := #Status."Chain Speed" + #Recipe."Ramp Speed bot/step";
#Aux."Speed FFordward to Doser" := #Recipe."Ramp Speed bot/step" * #Aux."Num of bottles From Doser To Chain";
// Check if it reachs the max value
IF #Status."Chain Speed" + #Aux."Speed FFordward to Doser" > #Status."Chain Request Speed" THEN
#Aux."Speed FFordward to Doser" := #Status."Chain Request Speed" - #Status."Chain Speed";
END_IF;
ELSIF #Status."Chain Speed" > #Status."Chain Request Speed" THEN
#Status."Chain Speed" := #Status."Chain Speed" - #Recipe."Ramp Speed bot/step";
#Aux."Speed FFordward to Doser" := -#Recipe."Ramp Speed bot/step" * #Aux."Num of bottles From Doser To Chain";
// Check if it reachs the min value
IF #Status."Chain Speed" + #Aux."Speed FFordward to Doser" < #Status."Chain Request Speed" THEN
#Aux."Speed FFordward to Doser" := #Status."Chain Speed" - #Status."Chain Request Speed";
END_IF;
END_IF;
IF ABS(#Status."Chain Speed" - #Status."Chain Request Speed") < #Recipe."Ramp Speed bot/step" THEN
// If the difference is smaller than the step
#Status."Chain Speed" := #Status."Chain Request Speed";
#Aux."Speed FFordward to Doser" := 0;
END_IF;
END_IF;
END_IF;
IF #"Fixed Paramters"."Chain Step" = 0 THEN
#"Fixed Paramters"."Chain Step" := 1;
END_IF;
IF #Recipe."Bottle Diameter" = 0 THEN
#Recipe."Bottle Diameter" := 1;
END_IF;
#Status."Ratio Doser Chain" := 1; // #Recipe."Bottle Diameter" / #"Fixed Paramters"."Chain Step";
// Calculate the error of phase between clocks/steps.
// This also can be done with the encoders
#"Diiference Phases"("Master Clock":=#"Offset Master Pulse"."Pulse Out",
"Slave Clock":=#I_BottleStep_Doser,
"Master b/h":=#"Chain Speed Scaled",
Reset:=#Reset);
#PID."Calculate Correction" := TRUE;
#PID.PID."Enable PID" := #Status."Doser Enable" AND #Aux.Counters[1] >= 2;
#PID.PID.SetOutputDisabled := TRUE;
#PID.PID.Value := #"Diiference Phases"."Diff Phase %"; // This can be done with the encoders
#PID.PID."Out Scale"."In Max" := 100;
#PID.PID."Out Scale"."In Min" := -100;
#PID.PID."Out Scale"."Out Min" := - (#"Chain Speed Scaled" * #Status."Ratio Doser Chain") / 4.0; // 25 % of Change
#PID.PID."Out Scale"."Out Max" := - #PID.PID."Out Scale"."Out Min";
"FC Simple PID"(#PID);
// Belt Speed
// The Belt must have the same Ramps that the Chain so we use the internal Inverter Ramp that must
// be equal to the speed of the Main Chain
#Status."Belt Speed" := #Status."Chain Speed" * (1 + #Recipe."K correction Belt" / 100.0);
// Doser Speed
#Status."Doser Speed" := #"Chain Speed Scaled" * #Status."Ratio Doser Chain" * (1 + #Recipe."K coorection Doser" / 100.0) + #PID.PID."Output Scaled";
#"Stop Ramp"("Ramp s" := #Aux."Estimated Step Time s" * 2, // Two bottles to stop
"Scan Time" := #"Scan Time",
"Enable Ramp" := #Status."Phase Stop Doser",
"Ramp Type" := 0);
#"Start Ramp"("Ramp s":=#Aux."Estimated Step Time s" * 1.5, // One bottle to start
"Scan Time":=#"Scan Time",
"Enable Ramp":=#Status."Phase Start Doser");
IF #Status."Phase Stop Doser" OR NOT #Status."Doser Enable" THEN
#Status."Doser Speed" := #Status."Doser Speed" * (1-#"Stop Ramp"."Ramp Value");
END_IF;
IF #Status."RePhase Doser Empty" THEN
#Status."Doser Speed" := #Status."Doser Speed" * 0.6;
END_IF;
IF #Status."Phase Start Doser" THEN
#Status."Doser Speed" := #Status."Doser Speed" * #"Start Ramp"."Ramp Value";
END_IF;
// RUNG 10 : ALARMS
#Alarms."Waiting Infeed Selector Enable" := NOT #Switches."Enable Infeed Selector";
IF #"Reset Alarms" THEN
#Alarms."Main Chain Motor not OK" := FALSE;
END_IF;
IF #Status."Max Accumulation Reached" THEN
#Alarms."Max accumulation timeout" := TRUE;
END_IF;
IF #"Bottle fallen infeed" THEN
#Alarms."Bottle Fallen Infeed" := TRUE;
END_IF;
IF #"Chain Piston Max Sensor" THEN
#Alarms."Chain stretching piston position High" := TRUE;
END_IF;
IF #"Chain Piston Min Sensor" THEN
#Alarms."Chain stretching piston position Low" := TRUE;
END_IF;
IF #Aux.Timers[5].Q THEN
#Alarms."Jam Infeed Chain" := TRUE;
END_IF;
IF #Aux.Timers[6].Q THEN
#Alarms."Jam Outfeed Chain" := TRUE;
END_IF;
#Status."Stop in Alarm" := #Alarms."Jam Outfeed Chain" OR
#Alarms."Chain stretching piston position High" OR #Alarms."Chain stretching piston position Low"; // OR #Alarms."Jam Infeed Chain";
// RUNG 11 : ALARMS
#"Alarms List"("Alarm Code" := 1,
"Alarm Bit":= #Alarms."Main Chain Motor not OK");
#"Alarms List"("Alarm Code" := 2,
"Alarm Bit" := #Alarms."Belt motor not Ok");
#"Alarms List"("Alarm Code" := 3,
"Alarm Bit" := #Alarms."Doser motor not Ok");
#"Alarms List"("Alarm Code" := 4,
"Alarm Bit" := #Alarms."Chain Exit Sensor guide open");
#"Alarms List"("Alarm Code" := 5,
"Alarm Bit" := #Alarms."Chain pulse not ok");
#"Alarms List"("Alarm Code" := 6,
"Alarm Bit" := #Alarms."Chain stretching piston position High");
#"Alarms List"("Alarm Code" := 7,
"Alarm Bit" := #Alarms."Chain stretching piston position Low");
#"Alarms List"("Alarm Code" := 8,
"Alarm Bit" := #Alarms."Max accumulation timeout");
#"Alarms List"("Alarm Code" := 9,
"Alarm Bit" := #Alarms."Waiting Infeed Selector Enable");
#"Alarms List"("Alarm Code" := 10,
"Alarm Bit" := #Alarms."Jam Infeed Chain");
#"Alarms List"("Alarm Code" := 11,
"Alarm Bit" := #Alarms."Jam Outfeed Chain");
#"Alarms List"("Alarm Code" := 12,
"Alarm Bit" := #Alarms."Bottle Fallen Infeed");
//
// Automatically iterate on all the active Alarms every 2 seconds
//
#"Alarms List"("Alarm Code":=0,
Reset:=#"Reset Alarms",
"Time to shitch s":=2,
"Scan Time":=#"Scan Time",
"Show Alarm"=>#Alarms."Alarm Code");
// RUNG 12 : ALARMS
FOR #n := 1 TO 8 DO
"FC S_Everz_ON_OFF"("Recipe TON Time":=#Recipe."Timers ON"[#n],
"Recipe TOFF Time":=#Recipe."Timers OFF"[#n],
Timer:=#Aux.Timers[#n]);
END_FOR;