الكتل الوظيفية: بناء مكونات قابلة لإعادة الاستخدام
الدالة FUNCTION: حساب بدون ذاكرة
الدالة (FUNCTION) هي وحدة برمجية تستقبل مدخلات وتُرجع نتيجة واحدة. ليس لها ذاكرة: نفس المدخلات تُنتج دائماً نفس المخرج.
مثال: تحويل درجة الحرارة
FUNCTION FC_CelsiusToFahrenheit : REAL
VAR_INPUT
rCelsius : REAL;
END_VAR
FC_CelsiusToFahrenheit := (rCelsius * 9.0 / 5.0) + 32.0;
END_FUNCTION
مثال: التحويل الخطي (Scaling)
دالة مستخدمة في كل مشروع تقريباً لتحويل القراءة الخام إلى قيمة هندسية:
FUNCTION FC_Scale : REAL
VAR_INPUT
rRawValue : REAL; // القيمة الخام (مثلاً 0-27648)
rRawMin : REAL; // الحد الأدنى للخام
rRawMax : REAL; // الحد الأعلى للخام
rEngMin : REAL; // الحد الأدنى الهندسي (مثلاً 0.0)
rEngMax : REAL; // الحد الأعلى الهندسي (مثلاً 100.0)
END_VAR
IF (rRawMax - rRawMin) <> 0.0 THEN
FC_Scale := ((rRawValue - rRawMin) / (rRawMax - rRawMin))
* (rEngMax - rEngMin) + rEngMin;
ELSE
FC_Scale := 0.0; // حماية من القسمة على صفر
END_IF;
END_FUNCTION
استدعاء الدالة
// تحويل قراءة مستشعر ضغط: 0-27648 -> 0-10 بار
rPressure := FC_Scale(
rRawValue := INT_TO_REAL(iRawPressure),
rRawMin := 0.0,
rRawMax := 27648.0,
rEngMin := 0.0,
rEngMax := 10.0
);
كتلة الدالة FUNCTION_BLOCK: حساب مع ذاكرة
كتلة الدالة (FUNCTION_BLOCK) تختلف عن الدالة العادية في أنها تحتفظ بحالتها بين دورات المسح. هذا يجعلها مثالية للمؤقتات والعدّادات والتحكم PID.
مثال: كاشف الحافة الصاعدة
FUNCTION_BLOCK FB_RisingEdge
VAR_INPUT
bSignal : BOOL;
END_VAR
VAR_OUTPUT
bPulse : BOOL;
END_VAR
VAR
bPrevious : BOOL := FALSE; // ذاكرة الحالة السابقة
END_VAR
bPulse := bSignal AND NOT bPrevious;
bPrevious := bSignal;
END_FUNCTION_BLOCK
الفرق الجوهري
// الدالة: لا ذاكرة، يمكن استدعاؤها مباشرة
rResult := FC_Scale(rRawValue := 1000.0, ...);
// كتلة الدالة: لها ذاكرة، تحتاج إنشاء نسخة
VAR
fbMyEdge : FB_RisingEdge; // نسخة (Instance)
END_VAR
fbMyEdge(bSignal := bSensor); // استدعاء
IF fbMyEdge.bPulse THEN ... // قراءة المخرج
كل نسخة من كتلة الدالة لها ذاكرتها المستقلة. يمكنك إنشاء عدة نسخ لنفس الكتلة:
VAR
fbEdge1 : FB_RisingEdge; // لحسّاس 1
fbEdge2 : FB_RisingEdge; // لحسّاس 2
END_VAR
البرنامج PROGRAM: المستوى الأعلى
البرنامج (PROGRAM) هو المستوى الأعلى الذي يُنفّذه PLC مباشرة. يحتوي على المنطق الرئيسي ويستدعي الدوال والكتل الوظيفية.
PROGRAM PLC_PRG
VAR
// نسخ الكتل الوظيفية
fbMotor1 : FB_MotorControl;
fbMotor2 : FB_MotorControl;
fbFillCtrl : FB_FillControl;
// متغيرات البرنامج
eSystemState : E_SystemState := IDLE;
END_VAR
// استدعاء كتل التحكم بالمحركات
fbMotor1(
bStart := bStartBtn AND (eSystemState = RUNNING),
bStop := bStopBtn OR (eSystemState = FAULTED),
bEStop := bEmergencyStop
);
fbMotor2(
bStart := fbMotor1.bRunning, // المحرك 2 يعمل بعد 1
bStop := bStopBtn,
bEStop := bEmergencyStop
);
التسلسل الهرمي
PROGRAM (برنامج رئيسي)
├── FUNCTION_BLOCK (كتل وظيفية - لها ذاكرة)
│ ├── FUNCTION_BLOCK (كتل متداخلة)
│ └── FUNCTION (دوال مساعدة)
└── FUNCTION (حسابات بسيطة)
بناء كتلة وظيفية للتحكم بمحرك
تصميم الكتلة
FUNCTION_BLOCK FB_MotorControl
VAR_INPUT
bStart : BOOL; // أمر التشغيل
bStop : BOOL; // أمر الإيقاف
bEStop : BOOL; // إيقاف طوارئ
bReset : BOOL; // إعادة ضبط العطل
tStartDelay : TIME := T#0s; // تأخير بدء التشغيل
END_VAR
VAR_OUTPUT
bRunning : BOOL; // المحرك يعمل
bFaulted : BOOL; // حالة عطل
bReady : BOOL; // جاهز للتشغيل
tRunTime : TIME; // زمن التشغيل التراكمي
END_VAR
VAR
bLatch : BOOL; // قفل ذاتي داخلي
fbDelayOn : TON; // مؤقت تأخير التشغيل
fbRunTimer : TON; // مؤقت زمن التشغيل
bThermalTrip: BOOL; // حماية حرارية
END_VAR
// كشف العطل
IF bEStop OR bThermalTrip THEN
bFaulted := TRUE;
bLatch := FALSE;
END_IF;
// إعادة ضبط العطل
IF bReset AND NOT bEStop AND NOT bThermalTrip THEN
bFaulted := FALSE;
END_IF;
// حالة الجاهزية
bReady := NOT bFaulted AND NOT bEStop;
// منطق التشغيل
IF bStart AND bReady THEN
bLatch := TRUE;
ELSIF bStop OR bFaulted THEN
bLatch := FALSE;
END_IF;
// تأخير التشغيل
fbDelayOn(IN := bLatch, PT := tStartDelay);
IF tStartDelay > T#0s THEN
bRunning := fbDelayOn.Q;
ELSE
bRunning := bLatch;
END_IF;
// حساب زمن التشغيل
fbRunTimer(IN := bRunning, PT := T#24h);
tRunTime := fbRunTimer.ET;
END_FUNCTION_BLOCK
المكتبات: إعادة استخدام الكتل بين المشاريع
المكتبة (Library) هي مجموعة من الدوال والكتل الوظيفية المُختبرة التي تُضاف لأي مشروع جديد.
تنظيم المكتبة
MyFactoryLib/
├── Motors/
│ ├── FB_MotorControl // تحكم محرك أساسي
│ ├── FB_MotorVFD // تحكم عبر محول تردد
│ └── FB_StarDelta // تشغيل نجمة-مثلث
├── Sensors/
│ ├── FC_Scale // تحويل خطي
│ ├── FB_AnalogInput // معالجة مدخل تناظري
│ └── FB_SensorDiag // تشخيص أعطال المستشعر
├── Valves/
│ ├── FB_OnOffValve // صمام تشغيل/إيقاف
│ └── FB_ProportionalValve // صمام تناسبي
└── Utilities/
├── FB_AlarmHandler // إدارة الإنذارات
├── FC_Clamp // تحديد القيمة ضمن نطاق
└── FB_DataLogger // تسجيل البيانات
فوائد المكتبات
- توحيد: نفس الكود في كل مشروع
- جودة: كود مُختبر ومُثبت في الميدان
- سرعة: مشروع جديد يبدأ بأساس متين
- صيانة: تحديث المكتبة يُفيد كل المشاريع
مثال عملي: مكتبة تحكم خاصة بمصنعك
دالة تحديد النطاق (Clamp)
FUNCTION FC_Clamp : REAL
VAR_INPUT
rValue : REAL;
rMin : REAL;
rMax : REAL;
END_VAR
IF rValue < rMin THEN
FC_Clamp := rMin;
ELSIF rValue > rMax THEN
FC_Clamp := rMax;
ELSE
FC_Clamp := rValue;
END_IF;
END_FUNCTION
كتلة مراقبة عتبات المستشعر
FUNCTION_BLOCK FB_ThresholdMonitor
VAR_INPUT
rValue : REAL;
rHighHigh : REAL := 100.0;
rHigh : REAL := 80.0;
rLow : REAL := 20.0;
rLowLow : REAL := 0.0;
tDelay : TIME := T#2s;
END_VAR
VAR_OUTPUT
bAlarmHH : BOOL; // إنذار مرتفع جداً
bAlarmH : BOOL; // إنذار مرتفع
bAlarmL : BOOL; // إنذار منخفض
bAlarmLL : BOOL; // إنذار منخفض جداً
iAlarmCount: INT; // عدد الإنذارات النشطة
END_VAR
VAR
fbDelayHH : TON;
fbDelayH : TON;
fbDelayL : TON;
fbDelayLL : TON;
END_VAR
fbDelayHH(IN := rValue >= rHighHigh, PT := tDelay);
fbDelayH(IN := rValue >= rHigh, PT := tDelay);
fbDelayL(IN := rValue <= rLow, PT := tDelay);
fbDelayLL(IN := rValue <= rLowLow, PT := tDelay);
bAlarmHH := fbDelayHH.Q;
bAlarmH := fbDelayH.Q;
bAlarmL := fbDelayL.Q;
bAlarmLL := fbDelayLL.Q;
iAlarmCount := BOOL_TO_INT(bAlarmHH) + BOOL_TO_INT(bAlarmH)
+ BOOL_TO_INT(bAlarmL) + BOOL_TO_INT(bAlarmLL);
END_FUNCTION_BLOCK
الخلاصة
الدوال (FUNCTION) للحسابات بدون ذاكرة، والكتل الوظيفية (FUNCTION_BLOCK) للتحكم مع ذاكرة. بناء مكتبة كتل وظيفية مُختبرة يُسرّع تطوير المشاريع الجديدة ويرفع جودة الكود. في الدرس القادم سنستخدم هذه المفاهيم لمعالجة الإشارات التناظرية وبناء كتلة متكاملة لقراءة المستشعرات.