Browse Source

增加设备OP调试中

shishenghui 1 month ago
parent
commit
8a4b97d0bc

+ 4 - 0
TIN001-PLC/Jet_Furance_PMC/Jet_Furance_PMC/Jet_Furance_PMC/FurancePMC/DEVICE.cpp

@@ -5,6 +5,7 @@
 #include"EV.h"
 #include"OP.h"
 #include"SC.h"
+#include"HeaterModule.h"
 #include"BoatModule.h"
 #include"ValveModule.h"
 #include"AlarmConditionModule.h"
@@ -85,6 +86,9 @@ void initEnvironment() {
 	DEVICE = &staticDEVICE;
 	initDEVICE();
 
+	static HeaterModule staticHeaterModule("HeaterModule", "heaterModule", MODULE_NAME);
+	heaterModule = &staticHeaterModule;
+
 	static BoatModule staticBoatModule("BoatModule","boatModule",MODULE_NAME);
 	boatModule = &staticBoatModule;
 	

+ 2 - 2
TIN001-PLC/Jet_Furance_PMC/Jet_Furance_PMC/Jet_Furance_PMC/FurancePMC/DeviceAttribute.cpp

@@ -289,7 +289,7 @@ char DeviceAttribute::getCharValue(){
     }
     return *((char *)(addr));
 }
-unsigned char DeviceAttribute::getUChaValue(){
+unsigned char DeviceAttribute::getUCharValue(){
     if(addr==NULL){
         return 0;
     }
@@ -467,7 +467,7 @@ void DeviceAttribute::setCharValue(char value){
     }
     *((char *)(addr))=value;
 }
-void DeviceAttribute::setUChaValue(unsigned char value){
+void DeviceAttribute::setUCharValue(unsigned char value){
     if(addr==NULL){
         return;
     }

+ 2 - 2
TIN001-PLC/Jet_Furance_PMC/Jet_Furance_PMC/Jet_Furance_PMC/FurancePMC/DeviceAttribute.h

@@ -54,7 +54,7 @@ class DeviceAttribute{
 
         PMCBOOL getBoolValue();
         char getCharValue();
-        unsigned char getUChaValue();
+        unsigned char getUCharValue();
         short getShortValue();
         unsigned short getUShortValue();
         int getIntValue();
@@ -67,7 +67,7 @@ class DeviceAttribute{
         ///////////
         void setBoolValue(PMCBOOL);
         void setCharValue(char);
-        void setUChaValue(unsigned char);
+        void setUCharValue(unsigned char);
         void setShortValue(short);
         void setUShortValue(unsigned short);
         void setIntValue(int);

+ 2 - 0
TIN001-PLC/Jet_Furance_PMC/Jet_Furance_PMC/Jet_Furance_PMC/FurancePMC/FurancePMC.vcxproj

@@ -63,6 +63,7 @@
     <ClInclude Include="EV.h" />
     <ClInclude Include="Fields.h" />
     <ClInclude Include="FurnaceSignalTower.h" />
+    <ClInclude Include="HeaterModule.h" />
     <ClInclude Include="IoAlarmSignal.h" />
     <ClInclude Include="IoAPC.h" />
     <ClInclude Include="IoFFU.h" />
@@ -120,6 +121,7 @@
     <ClCompile Include="EV.cpp" />
     <ClCompile Include="Fields.cpp" />
     <ClCompile Include="FurnaceSignalTower.cpp" />
+    <ClCompile Include="HeaterModule.cpp" />
     <ClCompile Include="IoAlarmSignal.cpp" />
     <ClCompile Include="IoAPC.cpp" />
     <ClCompile Include="IoFFU.cpp" />

+ 6 - 0
TIN001-PLC/Jet_Furance_PMC/Jet_Furance_PMC/Jet_Furance_PMC/FurancePMC/FurancePMC.vcxproj.filters

@@ -225,6 +225,9 @@
     <ClInclude Include="AlarmConditionModule.h">
       <Filter>Device Drivers</Filter>
     </ClInclude>
+    <ClInclude Include="HeaterModule.h">
+      <Filter>Device Drivers</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="TcPch.cpp">
@@ -353,6 +356,9 @@
     <ClCompile Include="AlarmConditionModule.cpp">
       <Filter>Device Drivers</Filter>
     </ClCompile>
+    <ClCompile Include="HeaterModule.cpp">
+      <Filter>Device Drivers</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="FurancePMC.rc">

+ 121 - 0
TIN001-PLC/Jet_Furance_PMC/Jet_Furance_PMC/Jet_Furance_PMC/FurancePMC/IoAPC.cpp

@@ -2,9 +2,130 @@
 #pragma hdrstop
 #include"IoAPC.h"
 #include"DEVICE.h"
+#include"OP.h"
+#include"SC.h"
+#include"EV.h"
+#include"errcode.h"
 #include"debug.h"
+
 IoAPC::IoAPC(const char* className,const char* name,const char* module):BaseDevice(className, name, module){
 }
+void IoAPC::initialize()
+{
+	char opName[MAX_NAME_LEN];
+	sprintf(opName, "%s.SetParameters", id);
+	auto op = OP->subscribe(opName, this);
+	op->addExec([]()->OperatorStatusEnum {
+		auto op = OP->currentRoot;
+		IoAPC* device =(IoAPC *) op->device;
+		if (op->params.length <= 11) {
+			return OperatorStatusEnum::SUCCESS;
+		}
+		if (strcmp(device->id, "APC") == 0) {
+			//APC
+			const char* cmd = op->params.get(0);
+			if (strcmp_ignoreAa(cmd, "Press1") == 0) {
+				device->command = APCCommand::Press1;
+				device->aoCommancValue = 7;
+			}
+			else if (strcmp_ignoreAa(cmd, "Press2") == 0) {
+				device->command = APCCommand::Press2;
+				device->aoCommancValue = 8;
+			}
+			else if (strcmp_ignoreAa(cmd, "Slow Vac") == 0) {
+				device->command = APCCommand::SlowVac;
+				device->aoCommancValue = 16;
+			}
+			else if (strcmp_ignoreAa(cmd, "Valve Angle") == 0) {
+				device->command = APCCommand::ValveAngle;
+				device->aoCommancValue = 2;
+			}
+			else if (strcmp_ignoreAa(cmd, "Full Open") == 0) {
+				device->command = APCCommand::FullOpen;
+				device->aoCommancValue = 6;
+			}
+			else if (strcmp_ignoreAa(cmd, "Full Close") == 0) {
+				device->command = APCCommand::FullClose;
+				device->aoCommancValue = 5;
+			}
+			else if (strcmp_ignoreAa(cmd, "Hold") == 0) {
+				device->command = APCCommand::Hold;
+				device->aoCommancValue = 4;
+			}
+			else if (strcmp_ignoreAa(cmd, "Home") == 0) {
+				device->command = APCCommand::Home;
+				device->aoCommancValue = 1;
+			}
+			else if (strcmp_ignoreAa(cmd, "WaitPressUp1") == 0) {
+				device->command = APCCommand::WaitPressUp1;
+			}
+			else if (strcmp_ignoreAa(cmd, "WaitPressUp2") == 0) {
+				device->command = APCCommand::WaitPressUp2;
+			}
+			else if (strcmp_ignoreAa(cmd, "WaitPressDown1") == 0) {
+				device->command = APCCommand::WaitPressDown1;
+			}
+			else if (strcmp_ignoreAa(cmd, "WaitPressDown2") == 0) {
+				device->command = APCCommand::WaitPressDown2;
+			}
+			else if (strcmp_ignoreAa(cmd, "Zero Set") == 0) {
+				device->command = APCCommand::ZeroSet;
+			}
+		}
+		else {
+			//APCVATGV
+			const char* cmd = op->params.get(0);
+			if (strcmp_ignoreAa(cmd, "Press1") == 0) {
+				device->command = APCCommand::Press1;
+				device->aoCommancValue = 5;
+			}
+			else if (strcmp_ignoreAa(cmd, "Press2") == 0) {
+				device->command = APCCommand::Press2;
+				device->aoCommancValue = 5;
+			}
+			else if (strcmp_ignoreAa(cmd, "Slow Vac") == 0) {
+				
+			}
+			else if (strcmp_ignoreAa(cmd, "Valve Angle") == 0) {
+				device->command = APCCommand::ValveAngle;
+				device->aoCommancValue = 2;
+			}
+			else if (strcmp_ignoreAa(cmd, "Full Open") == 0) {
+				device->command = APCCommand::FullOpen;
+				device->aoCommancValue = 4;
+			}
+			else if (strcmp_ignoreAa(cmd, "Full Close") == 0) {
+				device->command = APCCommand::FullClose;
+				device->aoCommancValue = 3;
+			}
+			else if (strcmp_ignoreAa(cmd, "Hold") == 0) {
+				device->command = APCCommand::Hold;
+				device->aoCommancValue = 6;
+			}
+			else if (strcmp_ignoreAa(cmd, "Home") == 0) {
+				device->command = APCCommand::Home;
+				device->aoCommancValue = 1;
+			}
+			else if (strcmp_ignoreAa(cmd, "WaitPressUp1") == 0) {
+				device->command = APCCommand::WaitPressUp1;
+			}
+			else if (strcmp_ignoreAa(cmd, "WaitPressUp2") == 0) {
+				device->command = APCCommand::WaitPressUp2;
+			}
+			else if (strcmp_ignoreAa(cmd, "WaitPressDown1") == 0) {
+				device->command = APCCommand::WaitPressDown1;
+			}
+			else if (strcmp_ignoreAa(cmd, "WaitPressDown2") == 0) {
+				device->command = APCCommand::WaitPressDown2;
+			}
+			else if (strcmp_ignoreAa(cmd, "Zero Set") == 0) {
+				device->command = APCCommand::ZeroSet;
+			}
+		}
+
+		return OperatorStatusEnum::RUNNING;
+	});
+}
 //AUTOIO_BEGIN
 IoAPC* _initAPCVATGV_0(){
 	static IoAPC staticAPCVATGV("IoAPC","APCVATGV",MODULE_NAME);

+ 5 - 0
TIN001-PLC/Jet_Furance_PMC/Jet_Furance_PMC/Jet_Furance_PMC/FurancePMC/IoAPC.h

@@ -1,9 +1,14 @@
 #ifndef __Device_IoAPC_H__
 #define __Device_IoAPC_H__
 #include"BaseDevice.h"
+enum class APCCommand {None,Press1,Press2,SlowVac,ValveAngle,FullOpen,FullClose,Hold,Home,WaitPressUp1,WaitPressUp2,WaitPressDown1,WaitPressDown2,ZeroSet};
+
 class IoAPC: BaseDevice {
 	public:
 		IoAPC(const char* className,const char* name,const char* module);
+		void initialize();
+		APCCommand command = APCCommand::None;
+		unsigned char aoCommancValue = 0;
 		//AUTOIO_BEGIN
 		static IoAPC* poAPCVATGV;
 		static IoAPC* poAPC;

+ 241 - 0
TIN001-PLC/Jet_Furance_PMC/Jet_Furance_PMC/Jet_Furance_PMC/FurancePMC/IoHeater.cpp

@@ -2,9 +2,250 @@
 #pragma hdrstop
 #include"IoHeater.h"
 #include"DEVICE.h"
+#include"OP.h"
+#include"EV.h"
+#include"SC.h"
+#include"errcode.h"
+#include"RecipeContext.h"
+#include"HeaterModule.h"
+#include"AlarmConditionModule.h"
+#include"pmc_types.h"
 #include"debug.h"
+
+
 IoHeater::IoHeater(const char* className,const char* name,const char* module):BaseDevice(className, name, module){
 }
+void IoHeater::setAlarmConditionTable(const char* alarmConditionKey, const char* deviceName)
+{
+	//因为SetParameter和SetAlarmConditionTable这两个OP的执行顺序不定,可能会造成参数初始化不完整
+	//要求SetAlarmConditionTable先执行(可执行多次没问题)
+	this->alarmCondition = NULL;
+	if (alarmConditionModule->alarmConditionKey[0] != '\0') {
+
+		RecipeContext* context = RecipeContext::GetInstance();
+		if (context != NULL) {
+			auto indexNode = context->alarmConditionDic.get(alarmConditionKey);
+			if (indexNode != NULL) {
+				auto deviceNode = indexNode->get(deviceName);
+				if (deviceNode != NULL) {
+					this->alarmCondition = deviceNode;
+
+				}
+			}
+		}
+
+	}
+}
+void IoHeater::initialize()
+{
+	char opName[MAX_NAME_LEN];
+	sprintf(opName, "%s.SetParameters", this->id);
+	auto op = OP->subscribe(opName);
+	op->addExec([]()->OperatorStatusEnum {
+		OperatorNode* op = OP->currentRoot;
+		IoHeater* device = (IoHeater*)op->device;
+		//参数0:温区名称
+		//参数1:温度设置值
+		
+		static char vName[MAX_NAME_LEN];
+		
+		if (op->params.length <2) {
+			logger->error("%s:Not enough parameter:[%d]", op->name, op->params.length);
+			char* err = EV->parseErrCode(ALARM_OP_FAILED, "name=%s;reason=parameters missing;", op->name);
+			strcpy(OP->alarmText, err);
+			return OperatorStatusEnum::ALARM;
+		}
+		double temperature = 0;
+		if (strcmp_ignoreAa(op->params.get(0), "Continue") == 0) {
+			//TODO:保持原设置值不变,严格来说,这里的保持不变是与上一个step比较
+			//有可能上一个step被skip了,则当前step的设置值就不能不变,而是要读取上一个step的设置了
+			//实际工艺中大概这种情况不多见,暂不考虑
+			return OperatorStatusEnum::SUCCESS;
+		}
+		else if (SC->isVP(op->params.get(0))) {
+			static char vpName[MAX_NAME_LEN];
+			sprintf(vpName, "%s.RecipeEditParameter.TempSetting.%s.%s", MODULE_NAME, device->id, SC->getBaseName(op->params.get(1), ':'));
+			const char* vpValue = SC->getStringValue(vpName);
+			if (vpValue != NULL && vpValue[0] != '\0') {
+				temperature=atof(vpValue);
+			}
+		}
+		else {
+			temperature=atof(op->params.get(0));
+		}
+		double profileTCCalib = 0;
+		if (heaterModule->correctKey[0] != '\0') {
+			auto keyNode = RecipeContext::GetInstance()->tempCorrectionDic.get(heaterModule->correctKey);
+			if (keyNode != NULL) {
+				device->correct = keyNode->get(device->id);
+				profileTCCalib = device->correct->profileTCCalib;
+			}
+		}
+		double setPointValue = temperature + profileTCCalib;
+		double feedBackValue = 0;
+		if (memcmp(heaterModule->controlMode, "Ou", 2) == 0) {
+			device->setPoint = &device->aoHeaterControlModeSetPoint;
+			device->setPoint->setDoubleValue(setPointValue);
+			device->feedBack = &device->aiHeaterPV;
+			feedBackValue = device->feedBack->getDoubleValue();
+		}else if (memcmp(heaterModule->controlMode, "In", 2) == 0) {
+			device->setPoint = &device->aoCascadeControlModeSetPoint;
+			device->setPoint->setDoubleValue(setPointValue);
+			device->feedBack = &device->aiCascadePV;
+			feedBackValue = device->feedBack->getDoubleValue();
+			
+		}
+		else {
+			//TODO:profile mode
+		}
+	 	
+		double ramp = 0;
+		if (op->params.length >= 4) {
+			ramp = atof(op->params.get(3));
+		}
+				
+		if (op->params.length >= 17) {
+			if (strcmp_ignoreAa(op->params.get(15), "open") == 0) {
+				device->DPR = TRUE;
+			}
+			if (strcmp_ignoreAa(op->params.get(16), "open") == 0) {
+				device->BWR = TRUE;
+			}
+		}
+		double downRate = 0;
+		double upRate = 0;
+		double rampTime = 0;
+		if (device->DPR && device->BWR)
+		{
+			downRate = 0;
+			upRate = 0;
+		}
+		else
+		{
+			const char* rampUnit = op->params.get(4);
+			if (rampUnit == NULL ||IS_ZERO(ramp))
+			{
+				 
+				downRate = FABS(setPointValue - feedBackValue);//单位是°C/min
+				upRate = FABS(feedBackValue - setPointValue);//单位是°C/min
+			}
+			else if (strcmp_ignoreAa(rampUnit, "time")==0)
+			{
+				downRate = FABS(feedBackValue - setPointValue) / ramp;//单位是°C/min
+				upRate = FABS(setPointValue - feedBackValue) / ramp;//单位是°C/min
+			}
+			else
+			{
+				downRate = ramp;//单位是°C/min
+				upRate = ramp;//单位是°C/min
+			}
+		}
+		device->aoUpRate.setDoubleValue(upRate);
+		device->aoDownRate.setDoubleValue(downRate);
+		if (op->params.length >= 8) {
+			//Heater相比MFC,waitHigh和waitLow要简单,就是以度数为单位不需要换算
+			device->waitHigh = atof(op->params.get(6));
+			device->waitLow = atof(op->params.get(7));
+		}
+		//设置时间
+		//从alarm condition 中取delay time和trigger time(check time)
+		device->setAlarmConditionTable(alarmConditionModule->alarmConditionKey, device->id);
+		if (device->alarmCondition == NULL) {
+			op->setTimes(TIME_DELAY_DEFAULT, TIME_STABLE_DEFAULT, TIME_ALARM_DEFAULT, 0);
+		}
+		else {
+			op->setTimes(device->alarmCondition->alarmDelayDetectTimeS * 1000, TIME_STABLE_DEFAULT, device->alarmCondition->alarmCheckTime, 0);
+		}
+		return OperatorStatusEnum::RUNNING;
+	});
+	op->addCheck([]()->OperatorStatusEnum {
+		OperatorNode* op = OP->currentRoot;
+		IoHeater* device = (IoHeater*)op->device;
+		if (IS_ZERO(device->waitHigh) || IS_ZERO(device->waitLow)) {
+			return OperatorStatusEnum::SUCCESS;
+		}
+		if (device->setPoint == NULL || device->feedBack == NULL) {
+			return OperatorStatusEnum::SUCCESS;
+		}
+		double high = device->waitHigh + device->setPoint->getDoubleValue();
+		double low = -device->waitLow + device->setPoint->getDoubleValue();
+		if (device->feedBack->getDoubleValue() >= low && device->feedBack->getDoubleValue() <= high) {
+			return OperatorStatusEnum::SUCCESS;
+		}
+		if (op->isTimeout.value) {
+			if (device->isWait) {
+				//需要wait
+				if (op->isTimeout.trigger()) {
+					//第一次超时,打印个日志
+					logger->error("%s:wait time out", op->name);
+					char* err = EV->parseErrCode(WARNING_WAIT_TIMEOUT, "name=%s", device->id);
+					EV->postWarningLog(err);
+					op->isTimeout.clearTrigger();
+
+				}
+				return OperatorStatusEnum::RUNNING;
+			}
+			else {
+				if (op->isTimeout.trigger()) {
+					//第一次超时,打印个日志
+					logger->error("%s:wait time out", op->name);
+					char* err = EV->parseErrCode(WARNING_WAIT_TIMEOUT, "name=%s", device->id);
+					EV->postWarningLog(err);
+					op->isTimeout.clearTrigger();
+
+				}
+				return OperatorStatusEnum::SUCCESS;
+			}
+		}
+		else {
+			return OperatorStatusEnum::RUNNING;
+		}
+	});
+	op->addAlarmCheck([]()->OperatorStatusEnum {
+		OperatorNode* op = OP->currentRoot;
+		IoHeater* device = (IoHeater*)op->device;
+		if (device->alarmCondition == NULL) {
+			return OperatorStatusEnum::SUCCESS;
+		}
+		if (IS_ZERO(device->alarmCondition->alarmHigh) || IS_ZERO(device->alarmCondition->alarmLow)) {
+			return OperatorStatusEnum::SUCCESS;
+		}
+		if (device->setPoint == NULL || device->feedBack == NULL) {
+			return OperatorStatusEnum::SUCCESS;
+		}
+		double high = device->alarmCondition->alarmHigh + device->setPoint->getDoubleValue();
+		double low = -device->alarmCondition->alarmLow + device->setPoint->getDoubleValue();
+		if (device->feedBack->getDoubleValue() >= low && device->feedBack->getDoubleValue() <= high) {
+			double warningHigh = device->alarmCondition->warningHigh + device->setPoint->getDoubleValue();
+			double warningLow = -device->alarmCondition->warningLow + device->setPoint->getDoubleValue();
+			if (device->feedBack->getDoubleValue() >= warningLow && device->feedBack->getDoubleValue() <= warningHigh) {
+				return OperatorStatusEnum::SUCCESS;
+			}
+			else {
+				logger->error("%s:alarm condition[%f][%f][%f] warning", op->name, warningLow, device->feedBack->getBoolValue(), warningHigh);
+				char* err = EV->parseErrCode(WARNING_CONDITION_TRIGGER, "name=%s;", device->id);
+				strcpy(OP->alarmText, err);
+				return OperatorStatusEnum::WARNING;
+			}
+		}
+		else {
+			logger->error("%s:alarm condition[%f][%f][%f] error", op->name, low, device->feedBack->getBoolValue(), high);
+			char* err = EV->parseErrCode(ALARM_CONDITION_TRIGGER, "name=%s;", device->id);
+			strcpy(OP->alarmText, err);
+			return OperatorStatusEnum::ALARM;
+		}
+		return OperatorStatusEnum::SUCCESS;
+	});
+	op->addAlarmCallback([]()->OperatorStatusEnum {
+		OperatorNode* op = OP->currentRoot;
+		IoHeater* device = (IoHeater*)op->device;
+		if (device->alarmCondition == NULL) {
+			return OperatorStatusEnum::ALARM;
+		}
+		//TODO
+		return OperatorStatusEnum::SUCCESS;
+	});
+}
 //AUTOIO_BEGIN
 IoHeater* _initHeaterSUB_0(){
 	static IoHeater staticHeaterSUB("IoHeater","HeaterSUB",MODULE_NAME);

+ 14 - 0
TIN001-PLC/Jet_Furance_PMC/Jet_Furance_PMC/Jet_Furance_PMC/FurancePMC/IoHeater.h

@@ -1,9 +1,23 @@
 #ifndef __Device_IoHeater_H__
 #define __Device_IoHeater_H__
 #include"BaseDevice.h"
+#include"TempCorrection.h"
+#include"AlarmCondition.h"
+#include"AlarmConditionModule.h"
 class IoHeater: BaseDevice {
 	public:
 		IoHeater(const char* className,const char* name,const char* module);
+		void setAlarmConditionTable(const char* alarmConditionKey, const char* deviceName);
+		void initialize();
+		TempCorrection* correct = NULL;
+		PMCBOOL isWait = FALSE;
+		PMCBOOL DPR = FALSE;
+		PMCBOOL BWR = FALSE;
+		double waitLow = 0;
+		double waitHigh = 0;
+		AlarmCondition* alarmCondition = NULL;
+		DeviceAttribute* setPoint=NULL;
+		DeviceAttribute* feedBack=NULL;
 		//AUTOIO_BEGIN
 		static IoHeater* poHeaterSUB;
 		static IoHeater* poHeaterU;

+ 6 - 0
TIN001-PLC/Jet_Furance_PMC/Jet_Furance_PMC/Jet_Furance_PMC/FurancePMC/IoMFC.cpp

@@ -36,6 +36,7 @@ void IoMFC::initialize()
 			//TODO:保持原设置值不变,严格来说,这里的保持不变是与上一个step比较
 			//有可能上一个step被skip了,则当前step的设置值就不能不变,而是要读取上一个step的设置了
 			//实际工艺中大概这种情况不多见,暂不考虑
+			return OperatorStatusEnum::SUCCESS;
 		}
 		else if (SC->isVP(op->params.get(0))) {
 			static char vpName[MAX_NAME_LEN];
@@ -91,6 +92,7 @@ void IoMFC::initialize()
 		else {
 			op->setTimes(device->alarmCondition->alarmDelayDetectTimeS*1000 , TIME_STABLE_DEFAULT, device->alarmCondition->alarmCheckTime, 0);
 		}
+		return OperatorStatusEnum::RUNNING;
 		
 	});
 	op->addCheck([]()->OperatorStatusEnum {
@@ -129,6 +131,10 @@ void IoMFC::initialize()
 				return OperatorStatusEnum::SUCCESS;
 			}
 		}
+		else {
+			return OperatorStatusEnum::RUNNING;
+		}
+		return OperatorStatusEnum::SUCCESS;
 	});
 	op->addAlarmCheck([]()->OperatorStatusEnum {
 		OperatorNode* op = OP->currentRoot;

+ 1 - 0
TIN001-PLC/Jet_Furance_PMC/Jet_Furance_PMC/Jet_Furance_PMC/FurancePMC/pmc_types.h

@@ -78,4 +78,5 @@ extern M_SDO* SDO;
 #define GE_ZERO(v) (v>=-0.00001)
 #define LT_ZERO(v) (v<-0.00001)
 #define LE_ZERO(v) (v<=0.00001)
+#define FABS(x) (x>=0?x:-x)
 #endif