#include #include #include "include.h" #include "Modules/Control/control.h" // use for FPGA IO #include "drivers/FPGA/FPGA_GPIO/FPGA_GPIO.h"//#include "FPGA_GPIO.h" // use for FPGA IO #include "drivers/Valves/Valve.h" #include "Common/report/report.h" #include "Modules/Waste/Waste_ex.h" #include "modules/heaters/heaters_ex.h" #include #include #include "Drivers/I2C_Communication/RFID_NFC/logi-tag/LT_RFID.h" #include "Drivers/I2C_Communication/RFID_NFC/NFC.h" #include #include "PMR/IFS/CartridgeState.pb-c.h" #include #include "Modules/AlarmHandling/AlarmHandling.h" #include "StateMachines/Initialization/PowerOffSequence.h" #include "drivers/I2C_Communication/ADC_MUX/ADC_MUX.h" #include "CartridgeValidationRequest.pb-c.h" #include "CartridgeValidationResponse.pb-c.h" #include "Modules/General/buttons.h" #include "modules/General/process.h" #include "Modules/General/MachineStatus.h" #include "Modules/IFS/ifs.h" #include #include #include #include #include uint8_t VocPpmAlarmLimit = 10;//PPM uint8_t VocPpmLimitForXSec = 10;//Sec bool ReadingVocEverySec = false; bool DoorState = OPEN; uint32_t timeout_counter = 0; int wasteLevel = 0; WasteCartridgeEnum SelectedCard = MaxWasteCartridges; CartridgeStateEnum CartState[MaxWasteCartridges];// = {CartridgeStateOUT,CartridgeStateOUT,CartridgeStateOUT}; uint32_t Cartridge_Cover_Control; button *CartLed[MaxWasteCartridges] = {0, &cart2, &cart3}; WasteTankStateEnum WasteTankState = WasteTankStateIdle; extern char WasteEmptyingToken[36+1]; int init = 1; bool setWasteValve (WasteCartridgeEnum WasteCartridge); #define WASTE_CARTRIDGE_SIZE 1500 #define WASTE_LEVEL_OVERFLOW 2700 #define WASTE_LEVEL_FULL 2300 #define WASTE_LEVEL_EMPTY 900 #define WASTE_EMPTING_TIMEOUT 1500 double wasteLevelOverflow = WASTE_LEVEL_OVERFLOW; double wasteLevelFull = WASTE_LEVEL_FULL; double wasteLevelEmpty = WASTE_LEVEL_EMPTY; double wasteLevelOverFlow = WASTE_LEVEL_OVERFLOW; //#define WHS_DEBUG /////////////////////////////// Cartridges ////////////////////////////////////// void Waste_StateMachine(void); void cartCART_INSERTED (WasteCartridgeEnum CartId) { assert (CartIdcolor = colorON; Report("Cartridges Cart inserted", __FILE__, __LINE__, CartId, RpMessage, CartState[CartId], 0); } void cartCART_EXTRACTED(WasteCartridgeEnum CartId) //{called from polling function. set state to out, if was ACTIVE – notify waste module) { assert (CartIdcolor = colorOFF; if (init==0) WHS_Set_IFS_Clearing_Suction(NUM_OF_MIDTANKS); } WasteCartridgeEnum cartSELECT_CART(void) //{ if there is no SELECTED cartridge select the first cartridge in IN state. If there is none, return -1} { //Report("Cartridges select cart", __FILE__, __LINE__, CartState[WasteCartridge_middle], RpMessage, CartState[WasteCartridge_lower], 0); if ((CartState[WasteCartridge_middle] == CartridgeStateSELECTED)||(CartState[WasteCartridge_middle] == CartridgeStateACTIVE)) return WasteCartridge_middle; if ((CartState[WasteCartridge_lower] == CartridgeStateSELECTED)||(CartState[WasteCartridge_lower] == CartridgeStateACTIVE)) return WasteCartridge_lower; //no selected/active cartridge yet if (CartState[WasteCartridge_middle] == CartridgeStateIN) { CartState[WasteCartridge_middle] = CartridgeStateSELECTED; return WasteCartridge_middle; } if (CartState[WasteCartridge_lower] == CartridgeStateIN) { CartState[WasteCartridge_lower] = CartridgeStateSELECTED; return WasteCartridge_lower; } return MaxWasteCartridges; } void cartFILLING_START(void) //{set state of SELECTED to ACTIVE} { if (CartState[WasteCartridge_middle] == CartridgeStateSELECTED) { Report("Cartridges middle Cart active", __FILE__, __LINE__, WasteCartridge_middle, RpMessage, CartState[WasteCartridge_middle], 0); CartState[WasteCartridge_middle] = CartridgeStateACTIVE; CartLed[WasteCartridge_middle]->color = BLINK; } else if (CartState[WasteCartridge_lower] == CartridgeStateSELECTED) { Report("Cartridges lower Cart active", __FILE__, __LINE__, WasteCartridge_lower, RpMessage, CartState[WasteCartridge_lower], 0); CartState[WasteCartridge_lower] = CartridgeStateACTIVE; CartLed[WasteCartridge_lower]->color = BLINK; } else { Report("Cartridges start with no selected cartridge", __FILE__, __LINE__, CartState[WasteCartridge_middle], RpMessage, CartState[WasteCartridge_lower], 0); } } void cartFILLING_END(void) { //{set state of ACTIVE to FULL} if (CartState[WasteCartridge_middle] == CartridgeStateACTIVE) { CartState[WasteCartridge_middle] = CartridgeStateFULL; CartLed[WasteCartridge_middle]->color = colorOFF; } else if (CartState[WasteCartridge_lower] == CartridgeStateACTIVE) { CartState[WasteCartridge_lower] = CartridgeStateFULL; CartLed[WasteCartridge_lower]->color = colorOFF; } else { Report("Cartridges end with no active cartridge", __FILE__, __LINE__, CartState[WasteCartridge_middle], RpMessage, CartState[WasteCartridge_lower], 0); } setWasteValve(WasteCartridge_middle); } CartridgeStateEnum cartGetState(WasteCartridgeEnum CartId) { assert (CartId= wasteLevelFull) return 1; //full } return 0; } bool RdWasteTankOverFlowSensor() { //double WasteLevel = 0.0; #ifndef WHS_DEBUG if (WHS_Type == WHS_TYPE_UNKNOWN) { return WHS_GPI_WASTE_OVERFULL(); } else #endif {//new WHS //WasteLevel = GetWHSWasteTankLevelMiliLiter(); if (wasteLevel > wasteLevelOverflow) return 1; //overflow } return 0; } float WHS_GetCartridgeFillPercent() { return (100 - (((wasteLevel - wasteLevelEmpty) * 100) / WASTE_CARTRIDGE_SIZE)); } int WHS_GetCartridgeFill() { return (wasteLevelFull - wasteLevel); } bool WHS_IsContainerFull() { return RdWasteTankFullSensor(); } bool WHS_IsContainerEmpty() { return RdWasteTankEmptySensor(); } bool WHS_IsContainerOverflow() { return RdWasteTankOverFlowSensor(); } bool WHS_WasteCartridgeLowerPresent() { return Is_Cartridge_Present(CART_3); } bool WHS_WasteCartridgeMiddlePresent() { return Is_Cartridge_Present(CART_2); } bool WHS_IsEmptying() { if (WasteTankState == WasteTankStateEmptying) { return true; } else { return false; } } void Waste_Init() { CartState[WasteCartridge_middle] = CartridgeStateOUT; CartState[WasteCartridge_lower] = CartridgeStateOUT; DoorState = OPEN; } bool cartCart_door()//Polled by polling function. Notify waste. Poll each 1 second. During active filling poll each 100msec { if(Get_COVER_1_State(CartridgesDoor))//to check the polarity { DoorState = OPEN; } else { DoorState = CLOSE; } #ifdef WHS_DEBUG DoorState = CLOSE; #endif return DoorState; } uint32_t Waste_DoorOpenDuringEmptying(uint32_t IfIndex, uint32_t ReadValue) { cartCart_door(); if ((DoorState == OPEN) && (WasteTankState == WasteTankStateEmptying)) { SetWastePump(CLOSE); WasteTankState = WasteTankStatePaused; Report("move to WasteTankStatePaused", __FILE__, __LINE__, DoorState, RpMessage, WasteTankState, 0); } if ((DoorState == CLOSE) && (WasteTankState == WasteTankStatePaused)) { if (cartGetActiveCart() != MaxWasteCartridges) { SetWastePump(OPEN); WasteTankState = WasteTankStateEmptying; Report("return to WasteTankStateEmptying", __FILE__, __LINE__, DoorState, RpMessage, WasteTankState, 0); } } return OK; } void Waste_StateMachine_OneSecond_Call(void) { static WasteTankStateEnum prev_state = 0; static int cart, prev_wasteLevel = 0; static int insertUpdate[3] = {0, 0, 0}; //call cartridge state if ((Is_Cartridge_Present(CART_2) != cartGetPresence(WasteCartridge_middle)) || (init)) { if (Is_Cartridge_Present(CART_2)) { cartCART_INSERTED(WasteCartridge_middle); insertUpdate[CARTRIDGE_SLOT__WasteMiddle-1] = 1; } else { cartCART_EXTRACTED(WasteCartridge_middle); } } if ((Is_Cartridge_Present(CART_3) != cartGetPresence(WasteCartridge_lower)) || (init)) { if (Is_Cartridge_Present(CART_3)) { cartCART_INSERTED(WasteCartridge_lower); insertUpdate[CARTRIDGE_SLOT__WasteLower-1] = 1; } else { cartCART_EXTRACTED(WasteCartridge_lower); } } //update ppc for (cart = WasteCartridge_middle; cart <= WasteCartridge_lower; cart++) { if (WasteEmptyingToken[0]) { if (insertUpdate[cart]) { CartridgeStateUpdate(cart, cart-1, CARTRIDGE_STATE__Inserted, 0); insertUpdate[cart] = 0; } if (Is_Cartridge_Present(cart+1)) { CartridgeStateUpdate(cart, cart-1, CARTRIDGE_STATE__Present, 0); } else { CartridgeStateUpdate(cart, cart-1, CARTRIDGE_STATE__Absent, 0); } } } //call door state cartCart_door(); //call waste tank level #ifndef WHS_DEBUG wasteLevel = GetWHSWasteTankLevelMiliLiter(); #endif if ((WasteTankState != prev_state) || (abs(wasteLevel - prev_wasteLevel) > 50)) { Report("Waste_StateMachine_OneSecond_Call", __FILE__, WasteTankState, SelectedCard, RpMessage, wasteLevel, 0); prev_state = WasteTankState; prev_wasteLevel = wasteLevel; } //call state machine Waste_StateMachine(); if(init) init = 0; #ifdef WHS_DEBUG wasteLevel += 50; #endif } void Waste_StateMachine(void) { uint32_t activeCart; char str[100]; switch (WasteTankState) { case WasteTankStateIdle: if(WHS_IsContainerOverflow()) { AlarmHandlingSetAlarm( EVENT_TYPE__WASTE_CONTAINER_OVERFLOW, true); } else { AlarmHandlingSetAlarm( EVENT_TYPE__WASTE_CONTAINER_OVERFLOW, false); } if (cartNotAllFull() == true) { AlarmHandlingSetAlarm( EVENT_TYPE__ALL_WASTE_CARTRIDGES_FULL, false); } if(WHS_IsContainerFull()) { //check if power down in process if ( PowerOffInProcessGetState() ) { //Report("Power-down in process", __FILE__, __LINE__, 0, RpMessage, 0, 0); break; } // select cartridge SelectedCard = cartSELECT_CART(); if (SelectedCard == MaxWasteCartridges) { //Report("No Available cart", __FILE__, __LINE__, SelectedCard, RpMessage, 0, 0); AlarmHandlingSetAlarm( EVENT_TYPE__NO_WASTE_CARTRIDGE_AVAILABLE, true); break; } else { AlarmHandlingSetAlarm( EVENT_TYPE__NO_WASTE_CARTRIDGE_AVAILABLE, false); } //check door //moved door handling down - I want to see the alarm even if the door is open if (DoorState == OPEN) { //Report("Close cartridge cover", __FILE__, __LINE__, DoorState, RpMessage, 0, 0); break; } // check RFID - check cartridge OK (inkEmpty) // RFID change status - set WasteEmpty //move to next state WasteTankState = WasteTankStateFull; Report("move to WasteTankStateFull", __FILE__, __LINE__, DoorState, RpMessage, WasteTankState, 0); } break; case WasteTankStateFull: //set cart Active cartFILLING_START(); // set valve selection activeCart = cartGetActiveCart(); //Report("WasteTankStateEmptying set valve", __FILE__, __LINE__, activeCart, RpMessage, 0, 0); setWasteValve(activeCart); // set count down timeout_counter = 1; AlarmHandlingSetAlarm( EVENT_TYPE__WASTE_CONTAINER_EMPTYING_TIMEOUT, false); // set emptying limit wasteLevelEmpty = wasteLevel - WASTE_CARTRIDGE_SIZE; wasteLevelFull = wasteLevel; waste_seq_step1_cont(); // start a timer to observe door opening Cartridge_Cover_Control = AddControlCallback("Cartridge Cover Door", Waste_DoorOpenDuringEmptying, 100, cartCart_door, 0, 0, 0); Report("add control Cartridge Cover Door", __FILE__, __LINE__, 0, RpMessage, 0, 0); SetWastePump(OPEN); Report("WasteTankStateEmptying set Pump Open", __FILE__, activeCart, wasteLevel, RpMessage, wasteLevelEmpty, 0); //start emptying // RFID change status - set wasteFilling WasteTankState = WasteTankStateEmptying; CartridgeStateUpdate(activeCart, activeCart-1, CARTRIDGE_STATE__Emptying, 0); break; case WasteTankStateEmptying: // if time out or emptying done - idle activeCart = cartGetActiveCart(); if (timeout_counter++ > WASTE_EMPTING_TIMEOUT) { SetWastePump(CLOSE); cartFILLING_END(); WasteTankState = WasteTankStateIdle; usnprintf(str, 100, "WasteTankStateEmptying Timeout. time:%d:%d volume:%d", timeout_counter/60, timeout_counter%60, WHS_GetCartridgeFill()); Report(str, __FILE__, __LINE__, 0, RpMessage, 0, 0); CartridgeStateUpdate(activeCart, activeCart-1, CARTRIDGE_STATE__Error, WHS_GetCartridgeFillPercent()); AlarmHandlingSetAlarm( EVENT_TYPE__WASTE_CONTAINER_EMPTYING_TIMEOUT, true); RemoveControlCallback(Cartridge_Cover_Control, Waste_DoorOpenDuringEmptying); timeout_counter = 0; } else if (WHS_IsContainerEmpty()) { SetWastePump(CLOSE); cartFILLING_END(); waste_seq_cont_stop(); // RFID change status - set wasteFull WasteTankState = WasteTankStateIdle; RemoveControlCallback(Cartridge_Cover_Control, Waste_DoorOpenDuringEmptying); //if (cartNotAllFull() == false) AlarmHandlingSetAlarm( EVENT_TYPE__ALL_WASTE_CARTRIDGES_FULL, true); usnprintf(str, 100, "WasteTankStateEmptying done. time:%d:%d volume:%d", timeout_counter/60, timeout_counter%60, WHS_GetCartridgeFill()); Report(str, __FILE__, __LINE__, 0, RpMessage, 0, 0); CartridgeStateUpdate(activeCart, activeCart-1, CARTRIDGE_STATE__EmptyingCompleted, WHS_GetCartridgeFillPercent()); timeout_counter = 0; } if (WasteTankState == WasteTankStateEmptying) CartridgeStateUpdate(activeCart, activeCart-1, CARTRIDGE_STATE__Emptying, WHS_GetCartridgeFillPercent()); #ifdef WHS_DEBUG wasteLevel -= 100; timeout_counter += 10; #endif break; case WasteTankStatePaused: Report("WasteTankStatePaused...", __FILE__, __LINE__, 0, RpMessage, 0, 0); activeCart = cartGetActiveCart(); if (activeCart == MaxWasteCartridges) { SetWastePump(CLOSE); cartFILLING_END(); waste_seq_cont_stop(); // RFID change status - TBD WasteTankState = WasteTankStateIdle; RemoveControlCallback(Cartridge_Cover_Control, Waste_DoorOpenDuringEmptying); if (cartNotAllFull() == false) AlarmHandlingSetAlarm( EVENT_TYPE__ALL_WASTE_CARTRIDGES_FULL, true); Report("WasteTankStatePaused done", __FILE__, __LINE__, 0, RpMessage, 0, 0); timeout_counter = 0; CartridgeStateUpdate(activeCart, activeCart-1, CARTRIDGE_STATE__Error, WHS_GetCartridgeFillPercent()); } break; default: Report("wrong state", __FILE__, WasteTankState, 0, RpMessage, SelectedCard, 0); break; } } uint32_t Waste_CheckState(int *AlarmId) { //check if the waste is ready to run - waste level not above overflow level and no cartridge in the slots if (wasteLevel > wasteLevelOverflow) { Report("cannot start a job with waste tank overflow", __FILE__, __LINE__, wasteLevel, RpMessage, wasteLevelOverflow, 0); JobEndReason = JOB_SAFETY_CRITICAL_ALARM; usnprintf(AlarmReasonStr, 100, "cannot start a job with waste tank overflow"); AlarmHandlingSetAlarm( EVENT_TYPE__WASTE_CONTAINER_OVERFLOW, true); *AlarmId = EVENT_TYPE__WASTE_CONTAINER_OVERFLOW; //PrepareReady(Module_Waste,ModuleFail); return JOB_SAFETY_CRITICAL_ALARM; } if ((WHS_IsEmptying())||(NoCartAvailable())) { //PrepareReady(Module_Waste,ModuleDone); return JOB_OK; } else { Report("cannot start a job with cartridges in the IFS", __FILE__, __LINE__, wasteLevel, RpMessage, wasteLevelOverflow, 0); JobEndReason = JOB_WASTE_HANDLING_PROBLEM; AlarmHandlingSetAlarm( EVENT_TYPE__ALL_WASTE_CARTRIDGES_FULL, true); *AlarmId = EVENT_TYPE__ALL_WASTE_CARTRIDGES_FULL; usnprintf(AlarmReasonStr, 100, "cannot start a job with cartridges in the IFS"); //PrepareReady(Module_Waste,ModuleFail); return JOB_WASTE_HANDLING_PROBLEM; } } int Waste_Prepare_Timeout = 0; bool WHS_FlowReady = false,HeadIn_FlowReady = false,HeadOut_FlowReady = false; uint32_t WasteReadyControlId = 0xFF; float AllowedRangeForHeadBlowerDeviation = 0.07,AllowedRangeForWasteBlowerDeviation = 0.20; void setWastePrepareValues(float HeadLimit,float WasteLimit) { AllowedRangeForHeadBlowerDeviation = HeadLimit; AllowedRangeForWasteBlowerDeviation = WasteLimit; } uint32_t Waste_PrepareCallbak(uint32_t IfIndex, uint32_t ReadValue) { bool ready = false, headready = false,whsready = false; double HeadFlow = GetWHSAirFlow(HEAD_FLOW_METER); double HeadIn_Flow = PressureSensorGetPressure(HEAD_FAN_RIGHT); double HeadOut_Flow = PressureSensorGetPressure(HEAD_FAN_LEFT); if(WHS_Type == WHS_TYPE_UNKNOWN) WHS_FlowReady = true; //LP machine if ((HeadFlow<(headairflow*(1+AllowedRangeForWasteBlowerDeviation))) &&(HeadFlow>(headairflow*(1-AllowedRangeForWasteBlowerDeviation)))) { if (WHS_FlowReady == false) Report("Module waste WHS_FlowReady ready", __FILE__, __LINE__, (int)(HeadFlow*100), RpMessage, WHS_FlowReady, 0); WHS_FlowReady = true; } if ((HeadIn_Flow<(headBlowersFlow[0]*(1+AllowedRangeForHeadBlowerDeviation))) &&(HeadIn_Flow>(headBlowersFlow[0]*(1-AllowedRangeForHeadBlowerDeviation)))) { if (HeadIn_FlowReady == false) Report("Module waste HeadIn_FlowReady ready", __FILE__, __LINE__, (int)(HeadIn_Flow*100), RpMessage, HeadIn_FlowReady, 0); HeadIn_FlowReady = true; } if ((HeadOut_Flow<(headBlowersFlow[1]*(1+AllowedRangeForHeadBlowerDeviation))) &&(HeadOut_Flow>(headBlowersFlow[1]*(1-AllowedRangeForHeadBlowerDeviation)))) { if (HeadOut_FlowReady == false) Report("Module waste HeadOut_FlowReady ready", __FILE__, __LINE__, (int)(HeadOut_Flow*100), RpMessage, WHS_FlowReady, 0); HeadOut_FlowReady = true; } if (Head_Type <=HEAD_TYPE_FLAT) headready = true; else if ((HeadIn_FlowReady == true)&&(HeadOut_FlowReady == true)) headready = true; if (WHS_Type == WHS_TYPE_UNKNOWN) whsready = true; else if (WHS_FlowReady == true) whsready = true; if ((headready == true)&&(whsready == true)) ready = true; if ((Waste_Prepare_Timeout%30 == 0)||(Waste_Prepare_Timeout>250)) { Report("Module waste preparing values", __FILE__, (int)(HeadFlow*100), (int)(HeadIn_Flow*100), RpMessage, (int)(HeadOut_Flow*100), 0); } if (Waste_Prepare_Timeout++>300) { SafeRemoveControlCallback(WasteReadyControlId,Waste_PrepareCallbak); WasteReadyControlId = 0xFF; JobEndReason = JOB_PRESSURE_ALARM; usnprintf(AlarmReasonStr, 100, "Module waste prepare - air flow not stabilized"); Report("Module waste prepare failed!", __FILE__, __LINE__, headready, RpMessage, headready, 0); PrepareReady(Module_Waste,ModuleFail); } if (ready == true) { SafeRemoveControlCallback(WasteReadyControlId,Waste_PrepareCallbak); WasteReadyControlId = 0xFF; Report("Module waste prepare ready!", __FILE__, __LINE__, headready, RpMessage, headready, 0); PrepareReady(Module_Waste,ModuleDone); } Report("Module waste prepare", __FILE__, __LINE__, headready, RpMessage, whsready, 0); return OK; } uint32_t Waste_Check_Flows(void) { WHS_FlowReady = false;HeadIn_FlowReady = false;HeadOut_FlowReady = false; if (WasteReadyControlId != 0xFF) RemoveControlCallback(WasteReadyControlId,Waste_PrepareCallbak); Waste_Prepare_Timeout = 0; //check that blowers are within 10% of designated flow: WHS, head blowers WasteReadyControlId = AddControlCallback("Cartridge Cover Door", Waste_PrepareCallbak, eOneSecond, TemplateDataReadCBFunction, 0, 0, 0); Report("Waste_Check_Flows prepare", __FILE__, __LINE__, Head_Type, RpMessage, WHS_Type, 0); return OK; } uint32_t Waste_Prepare(void) { if (HeaterCheckReady() == true) { HeadBlowersV0Init(); } Report("Module waste prepare", __FILE__, HeaterCheckReady(), Head_Type, RpMessage, WHS_Type, 0); return OK; } bool WHS_IsVocPpmOverAlarmLimit() { static uint8_t counter = 0; uint8_t VocPpmAlarmLimit90Percent = 0; uint32_t Temp = 0; Temp = (uint32_t)VocPpmAlarmLimit * 90; Temp /= 100; VocPpmAlarmLimit90Percent = (uint8_t)Temp; if( ( Latest_Gas_Sens_PPM > VocPpmAlarmLimit ) || //PPM ( (Latest_Gas_Sens_PPM > VocPpmAlarmLimit90Percent) && (ReadingVocEverySec == true) ) ) //Hysteresis { //Report("VOC nearing alarm state", __FILE__, (int)(Latest_Gas_Sens_PPM*100), VocPpmAlarmLimit, RpMessage, counter, 0); //reading every sec ReadingVocEverySec = true; if(counter < 0xFF) { counter++; } /*if(counter > VocPpmLimitForXSec)//Sec { AlarmHandlingSetAlarm(EVENT_TYPE__VOC_SENSOR_ALARM_SLOPE,true); return true;//Buzzer ON if defined }*/ } else { AlarmHandlingSetAlarm(EVENT_TYPE__VOC_SENSOR_ALARM_SLOPE,false); //move back to noraml reading every Min. ReadingVocEverySec = false;//Turn off the buzzer on the first time sampling a measurment lower than (VocPpmAlarmLimit - 10%) counter = 0; } return false; }