aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Embedded_SW/Embedded/Modules/IDS/IDS_print.c
blob: d68c1c78394126f9e8be87e2d5539859b8de14df (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
24
#include "include.h"
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>

#include "driverlib/epi.h"
#include "inc/hw_memmap.h"
#include <driverlib/gpio.h>

#include "FPGA_COMM.h"
#include "driverlib/sysctl.h" //for SysCtlDelay
#include <driverlib/sysctl.h>
#include <DataDef.h>


unsigned short GPO_01_Reg;

int FPGA_Test()
{
    GP_Out_01 Gpo_01;

    Gpo_01.bits.F3_GPO_LED4 = 1;
    Gpo_01.bits.F3_GPO_LED3 = 1;
    Gpo_01.bits.F3_GPO_LED2 = 0;
    Gpo_01.bits.F3_GPO_LED1 = 1;
    Gpo_01.bits.F3_GPO_EXTWINDER_SSR11_CTRL = 1;
    Gpo_01.bits.F3_GPO_BUZZER = 0;
    Gpo_01.bits.F3_SPARE2_ROTENC_CLK = 1;
    Gpo_01.bits.F3_SPARE1_ROTENC_CLK = 1;
    Gpo_01.bits.RESERVE = 0xF5;

    GPO_01_Reg = Gpo_01.ushort;

    return 0;
}

int FPGA_Test_ReadBack(unsigned char FPGA_NUM, unsigned short Value, unsigned short *ReadBackValue)// = 0x1234)
{

    //TODO to update the deley
    unsigned short readValue = 0;
     if(FPGA_NUM == 1)
     {
         F1_Test = Value;
         SysCtlDelay(1000);
         readValue = F1_Test;
         *ReadBackValue = readValue;
         if(Value == (uint16_t) ~((unsigned int) readValue))
             return PASSED;
         if((Value == 0xFFFF) && (readValue == 0))
             return PASSED;
     }

     if(FPGA_NUM == 2)
     {
         F2_Test = Value;
         SysCtlDelay(100);
         readValue = F2_Test;
         *ReadBackValue = readValue;
         if(Value == (uint16_t) ~((unsigned int) readValue))
             return PASSED;
         if((Value == 0xFFFF) && (readValue == 0))
             return PASSED;
     }

     if(FPGA_NUM == 3)
     {
         F3_Test = Value;
         SysCtlDelay(100);
         readValue = F3_Test;
         *ReadBackValue = readValue;
         if(Value == (uint16_t) ~((unsigned int) readValue))
             return PASSED;
         if((Value == 0xFFFF) && (readValue == 0))
             return PASSED;
     }

    return FAILED;
}


int  FPGA_ReadVersion(unsigned char FPGA_NUM, unsigned char *Version, unsigned char *Year, unsigned char *Month, unsigned char *Day)
{
    VER1 Ver1;
    VER2 Ver2;

    switch(FPGA_NUM)
    {
        case 1:
                Ver1.ushort = F1_Ver1_D;
                Ver2.ushort = F1_Ver2_D;
            break;
        case 2:
                Ver1.ushort = F2_Ver1_D;
                Ver2.ushort = F2_Ver2_D;
            break;
        case 3:
                Ver1.ushort = F3_Ver1_D;
                Ver2.ushort = F3_Ver2_D;
            break;
        default:
            break;
    }

 /*   if( (Ver1.bytes.Month > 12) || (Ver1.bytes.Day > 31) || (Ver2.bytes.Year < 17) )
    {
        return FAILED;
    }
*/

    *Month = Ver1.bytes.Month;
    *Day = Ver1.bytes.Day;
    *Year = Ver2.bytes.Year;  // to check how many digits is needed
    *Version = Ver2.bytes.Ver_num;

    return PASSED;
}

void FPGA_Init()
{

#ifndef EVALUATION_BOARD
    ROM_GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, GPIO_PIN_6); // start FPGA (clear HW RESET)
    ROM_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_6, GPIO_PIN_6);
    // Enable EPI
    SysCtlPeripheralEnable(SYSCTL_PERIPH_EPI0);
    while (!(SysCtlPeripheralReady(SYSCTL_PERIPH_EPI0)));

    //PreScale + PWM
    //60MHz / PreScaler / (PWM High + PWM Low)
    //for SPI Motor driver the maximum is 5MHz the default is the FPGA is 4 (60/2/(2+3)) = 4

    //EPI Host-Bus 8 Configuration 3 (EPIHB8CFG3)
    //    Value Description
    //    0x0 Active WRn is 2 EPI clocks
    //    0x1 Active WRn is 4 EPI clocks <--
    //    0x2 Active WRn is 6 EPI clocks
    //    0x3 Active WRn is 8 EPI clocks

//    EPIModeSet(EPI0_BASE, EPI_MODE_HB16);
//    EPIConfigHB16Set(EPI0_BASE, EPI_HB16_MODE_ADMUX | EPI_HB16_WRWAIT_1 | EPI_HB16_RDWAIT_1 | EPI_HB16_ALE_LOW | EPI_HB16_WORD_ACCESS, 0);
//    EPIConfigHB16Set(EPI0_BASE, EPI_HB16_WRWAIT_1 | EPI_HB16_RDWAIT_1, 0);

    EPIModeSet(EPI0_BASE, EPI_MODE_GENERAL);
    //uint32_t temp = *(uint32_t)(EPI0_BASE+0x10);

//    volatile uint32_t *temp;
//    temp = (volatile uint32_t *) (EPI0_BASE+0x10);

    // Set EPI Mode
    EPIConfigGPModeSet(EPI0_BASE, EPI_GPMODE_CLKPIN |EPI_GPMODE_FRAME50 | EPI_GPMODE_ASIZE_12 | EPI_GPMODE_DSIZE_16, 4, 0);
    //Set EPI CLK
    EPIDividerSet(EPI0_BASE, 10);//60MHz

    EPIAddressMapSet(EPI0_BASE, EPI_ADDR_RAM_BASE_6  | EPI_ADDR_RAM_SIZE_64KB);

//    *temp |= 0x50;


    //FPGA_WRITE();
#endif

}


//---------------------------------- Limit_Switches ------------------------------------------------

//Limit_Switch1_REG   Limit_Switch1;
//Limit_Switch2_REG   Limit_Switch2;
//Limit_Switch3_REG   Limit_Switch3;
//
//void FPGA_Read_limit_Switches()
//{
//    Limit_Switch1.ushort = F1_GPI_LS1_D;
//    Limit_Switch2.ushort = F1_GPI_LS2_D;
//    Limit_Switch3.ushort = F1_GPI_LS3_D;
//}


uint32_t Read_Fans_Tacho()
{
    uint32_t Status = 0;

    // The big Fan in the drawer
    if( F1_Tacho_reg0 < 0x50 )
        Status|= (0x01<<0);// not working / Low Speed
    else
        Status&= ~(0x01<<0);//working (Speed ~0x400)

    // The 4 small Fans in the drawer
    // F1_gpi_FANS
    // �0� Fan not working
    // �1� Fan working
    if (F1_gpi_FANS == 0x0F )
    {
        Status&= ~(0x0F<<1);//working
    }
    else
    {
        bool F1_FAN1_TACH =  F1_gpi_FANS & 0x01;
        bool F1_FAN2_TACH = (F1_gpi_FANS & 0x02)>>0x01;
        bool F1_FAN3_TACH = (F1_gpi_FANS & 0x04)>>0x02;
        bool F1_FAN4_TACH = (F1_gpi_FANS & 0x08)>>0x03;

        if( F1_FAN1_TACH == 0) // Small Fan in the drawer
            Status|=  (0x01<<1);// not working
        else
            Status&= ~(0x01<<1);//working

        if( F1_FAN2_TACH == 0) // Small Fan in the drawer
            Status|=  (0x01<<2);// not working
        else
            Status&= ~(0x01<<2);//working

        if( F1_FAN3_TACH == 0) // Small Fan in the drawer
            Status|=  (0x01<<3);// not working
        else
            Status&= ~(0x01<<3);//working

        if( F1_FAN4_TACH == 0) // Small Fan in the drawer
            Status|=  (0x01<<4);// not working
        else
            Status&= ~(0x01<<4);//working
    }

    return Status;
}

//------------------------- WHS ----------------------
uint32_t WHS_Read_Blower_Tach()
{
    //TODO: check if we need to change the Prescaler
    //F2_Prescaler1_reg10  - prescaled clocks for counter of signal Blower Tacho. 8 bits
    return GPI_BLOWER_TACH;//TODO: RPM ?
}

void WHS_Control_Blower_PWM(uint8_t Freq,uint8_t High)
{
    // low + high = 0xFF in order use the same freq (and change the freq only by Add 0x112).
    // there is option to change only the high (low + freq constasnt) this will chnga the freq

    GPO_BLOWER_PWM_FREQ = Freq;
    GPO_BLOWER_PWM_HIGH = High;
    GPO_BLOWER_PWM_LOW  = 0xFF - High;
}
1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834
/************************************************************************************************************************
 * Ids_print.c
 * Printing module is responsible for :
 * operating the dispensers according to predefined dispensing rate from the UI
 **************************************************************************************************************************/
#include "include.h"
#include "ids.h"
#include "ids_ex.h"
#include "../control/control.h"
#include "../control/MillisecTask.h"
#include "../general/process.h"
#include "../control/pidalgo.h"
#include "../thread/thread.h"
#include "PMR/Hardware/Hardwaremotor.pb-c.h"
#include "PMR/Hardware/HardwareDispenser.pb-c.h"
#include "PMR/Printing/JobDescriptionFileBrushStop.pb-c.h"
#include "PMR/Printing/JobDescriptionFileSegment.pb-c.h"
#include "PMR/Printing/JobUploadStrategy.pb-c.h"
#include "PMR/Printing/JobSegment.pb-c.h"
#include "PMR/Printing/JobTicket.pb-c.h"
#include "StateMachines/Printing/printingSTM.h"
#include "drivers/motors/motor.h"
#include "drivers/valves/valve.h"

#include "Common/SWUpdate/FileSystem.h"
#include "drivers/Flash_Memory/fatfs/ff.h"

#include "modules/heaters/heaters.h"

#include "drivers/Flash_ram/FlashProgram.h"

typedef struct
{
    bool                m_isEnabled;
    float            m_SetParam;
    float            m_mesuredParam;
    float               m_preError;
    float               m_integral;
    float               m_calculatedError;
    bool                m_isReady;
    PID_Config_Params   m_params;
}DispenserControlConfig_t;
HardwarePidControl *DispensersControl;
HardwarePidControl DispensersCtrl[MAX_SYSTEM_DISPENSERS];
#define MAX_DYE_DISPENSERS 6
#define IDS_PRESEGMENT_TIME_STEP 50
#define INITIAL_CLEANER_SPEED 300

int32_t minimal_intersegment_length = 0;
int32_t DispenserSamples[MAX_SYSTEM_DISPENSERS][MAX_CONTROL_SAMPLES] = {0};
int DispenserSamplePointer[MAX_SYSTEM_DISPENSERS] = {0};
double DispenserNormalizedErrorCoEfficient[MAX_SYSTEM_DISPENSERS] = {0};
double lubricant_speed = 0.0;
HardwarePidControlType ThreadDispenserIdToControlId[MAX_SYSTEM_DISPENSERS] = {  HARDWARE_PID_CONTROL_TYPE__Dispenser1,HARDWARE_PID_CONTROL_TYPE__Dispenser2,HARDWARE_PID_CONTROL_TYPE__Dispenser3,HARDWARE_PID_CONTROL_TYPE__Dispenser4,HARDWARE_PID_CONTROL_TYPE__Dispenser5,HARDWARE_PID_CONTROL_TYPE__Dispenser6,HARDWARE_PID_CONTROL_TYPE__Dispenser7,HARDWARE_PID_CONTROL_TYPE__Dispenser8};
JobUploadStrategy uploadstrategy = JOB_UPLOAD_STRATEGY__Default;
bool    DispenserReady[MAX_SYSTEM_DISPENSERS] = {true};
bool IDS_Active = false;
bool ActuatorsCleaningSequence = true;

bool Special_Dispensers = false;
/******************** STRUCTURES AND ENUMs  ********************************************/
uint32_t IDS_Valve_DistanceToSpoolReady(uint32_t deviceID, uint32_t ReadValue);
uint32_t IDS_Valve_PresegmentReady(uint32_t deviceID, uint32_t ReadValue);
uint32_t IDSBrushStopRestartCallback(uint32_t IfIndex, uint32_t readValue);
//bool IDS_isDispenserUsedNextSegment(void *JobDetails,int DispenserId, int SegmentId);
/******************** GLOBAL PARAMETERS  ********************************************/
DispenserControlConfig_t DispenserControlConfig[MAX_SYSTEM_DISPENSERS];
uint32_t    ControlIdtoDispenserId [MAX_SYSTEM_DISPENSERS] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
int OriginalDispenserSpd_2PPS[MAX_SYSTEM_DISPENSERS] = {0,0,0,0,0,0,0,0};
bool         DispenserPreSegmentReady[MAX_SYSTEM_DISPENSERS] = {true,true,true,true,true,true,true,true};
bool         DispenserSegmentReady[MAX_SYSTEM_DISPENSERS] = {true,true,true,true,true,true,true,true};
bool         DispenserDistanceToSpoolReady[MAX_SYSTEM_DISPENSERS] = {true,true,true,true,true,true,true,true};
bool         DispenserUsedInJob[MAX_SYSTEM_DISPENSERS] = {false,false,false,false,false,false,false,false};
bool         DispenserUsedInSegment[MAX_SYSTEM_DISPENSERS] = {false,false,false,false,false,false,false,false};
bool         DispensersAlarmState[ MAX_SYSTEM_DISPENSERS] = {false,false,false,false,false,false,false,false};
int JobBrushStopId = 0;
int lInterSegmentLength = 0;
 int InterSegmentStepsLimit = 0,InterSegmentStepsCount = 0;
 bool PreSegmentWCFStarted = false;
 bool PreSegmentPrepareStarted = false;

 uint32_t InterSegmentStartSprayCleaner = 0;
 uint32_t InterSegmentStartRocking = 0;
 uint32_t InterSegmentCenterRockers = 0;
 uint32_t LeftRockerSpeed = 20;
 uint32_t RightRockerSpeed = 20;
 uint32_t CleaningDispenserSpeed = 40;
 uint32_t InterSegmentStartWFCFDispensers;
 uint32_t WFCF = 80;
 bool EnableCleaning = true;
bool JobEndSequence = false;
double Get_WFCF(void)
{
    return WFCF;
}
double Get_WFCF_Time(void)
{
    return InterSegmentStartWFCFDispensers;
}
void IDS_Dispenser_SetPreSegmentWFCFValues(double dispenserpresegmentwfcf, double  ids_presegment_wfcf_timebeforesegment)
 {
     if (ids_presegment_wfcf_timebeforesegment)
         InterSegmentStartWFCFDispensers = ids_presegment_wfcf_timebeforesegment;
     if (dispenserpresegmentwfcf)
         WFCF = dispenserpresegmentwfcf;
     Report("IDS_Dispenser_SetPreSegmentWFCFValues ",__FILE__,__LINE__,dispenserpresegmentwfcf,RpWarning,(int)ids_presegment_wfcf_timebeforesegment,0);

 }
 void IDS_Dispenser_SetPreSegmentCleaningValues(double ids_cleaningspeed,double ids_cleaningstartspraypresegmenttime ,double ids_cleaningstopbeforesegmenttime,double ids_leftcleaningmotorspeed,double ids_rightcleaningmotorspeed)
 {
     if ( ids_cleaningspeed)
         CleaningDispenserSpeed = ids_cleaningspeed;
     if ( ids_cleaningstartspraypresegmenttime )
     {
         InterSegmentStartSprayCleaner = ids_cleaningstartspraypresegmenttime;
         InterSegmentStartRocking = ids_cleaningstartspraypresegmenttime + 1000;
     }
     if ( ids_cleaningstopbeforesegmenttime)
         InterSegmentCenterRockers = ids_cleaningstopbeforesegmenttime;
     if ( ids_leftcleaningmotorspeed)
         LeftRockerSpeed = ids_leftcleaningmotorspeed;
     if ( ids_rightcleaningmotorspeed)
         RightRockerSpeed = ids_rightcleaningmotorspeed;
     minimal_intersegment_length = ids_cleaningstopbeforesegmenttime+ids_cleaningstartspraypresegmenttime+2000;
     Report("IDS_Dispenser_SetPreSegmentCleaningValues ",__FILE__,__LINE__,RightRockerSpeed,RpWarning,(int)LeftRockerSpeed,0);
     Report("IDS_Dispenser_SetPreSegmentCleaningValues ",__FILE__,minimal_intersegment_length,InterSegmentStartSprayCleaner,RpWarning,(int)InterSegmentCenterRockers,0);
     Report("IDS_Dispenser actuator times ",__FILE__,(int)LeftRockerSpeed/100*IDS_PRESEGMENT_TIME_STEP,(int)LeftRockerSpeed%100*IDS_PRESEGMENT_TIME_STEP,RpWarning,(int)RightRockerSpeed*IDS_PRESEGMENT_TIME_STEP,0);
 }

 uint32_t DispenserPreSegmentControlId = 0xFF;
 uint32_t BrushStopControlId = 0xFF;
 //uint32_t PreSegmentControlId = 0xFF;

/*uint32_t IDS_DispenserControlInit()
{
    DispensersControl = (void *)(DISPENSERS_MAP_IN_FLASH);
    DispensersControl = DispensersCtrl;

    //EraseFlashSection(DispensersControl,sizeof(HardwarePidControl)*MAX_SYSTEM_DISPENSERS);
    return OK;
}*/

uint32_t IDS_DispenserPidRequestMessage(HardwarePidControl* request)
{
    int Dispenser_i,i;
    //int temp;
    for (i=0;i<MAX_SYSTEM_DISPENSERS;i++)
    {
        if (ThreadDispenserIdToControlId[i] == request->hardwarepidcontroltype)
        {
            Dispenser_i = i;
            break;
        }
    }
    memcpy (&DispensersCtrl[Dispenser_i],request,sizeof(HardwarePidControl));

    //ReadAppAndProgram(&DispensersControl[Dispenser_i], sizeof(HardwarePidControl), request);

    for (i = 0;i < DispensersControl[Dispenser_i].pvinputfilterfactormode; i++)
        DispenserSamples[Dispenser_i][i] = 0;  //reset the samples value for control beginning

    DispenserControlConfig[Dispenser_i].m_params.MAX = 1;
    DispenserControlConfig[Dispenser_i].m_params.MIN = DispensersCtrl[Dispenser_i].outputproportionalpowerlimit*-1;
    DispenserControlConfig[Dispenser_i].m_params.Kd = DispensersCtrl[Dispenser_i].derivativetime;
    DispenserControlConfig[Dispenser_i].m_params.Kp = DispensersCtrl[Dispenser_i].proportionalgain;
    DispenserControlConfig[Dispenser_i].m_params.Ki = DispensersCtrl[Dispenser_i].integraltime;
    DispenserControlConfig[Dispenser_i].m_params.IntegralErrorMultiplier = DispensersCtrl[Dispenser_i].setpointramprateorsoftstartramp;
    DispenserControlConfig[Dispenser_i].m_params.ProportionalErrorMultiplier = DispensersCtrl[Dispenser_i].outputonoffhysteresisvalue;
    DispenserControlConfig[Dispenser_i].m_params.epsilon = DispensersCtrl[Dispenser_i].epsilon;
    DispenserControlConfig[Dispenser_i].m_params.dt = DispensersCtrl[Dispenser_i].controloutputtype;
    //DispenserControlConfig[Dispenser_i].m_ingnoreValue =  DispensersCtrl[Dispenser_i].sensorcorrectionadjustment; // the minimal change required to change the motor speed in pulses
    DispenserControlConfig[Dispenser_i].m_calculatedError = 0;
    DispenserControlConfig[Dispenser_i].m_integral = 0;
    DispenserControlConfig[Dispenser_i].m_isEnabled = true;
    DispenserControlConfig[Dispenser_i].m_isReady = true;
    DispenserControlConfig[Dispenser_i].m_mesuredParam = 0;
    DispenserControlConfig[Dispenser_i].m_preError = 0;
    DispenserControlConfig[Dispenser_i].m_SetParam = DispensersCtrl[Dispenser_i].outputproportionalcycletime;//need to update SetParams on presegment stage


    return OK;
}
uint32_t DispenserPidControlId[MAX_SYSTEM_DISPENSERS] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
uint32_t IDS_Pid_Testing_Func(uint32_t DispenserId, uint32_t ReadValue)
{
    float calculated_speed;
    DispenserControlConfig[DispenserId].m_mesuredParam = MillisecGetPressures(DispenserId);
    DispenserControlConfig[DispenserId].m_calculatedError = PIDAlgorithmCalculation((float)DispenserControlConfig[DispenserId].m_SetParam , (float)DispenserControlConfig[DispenserId].m_mesuredParam,
                                                      &DispenserControlConfig[DispenserId].m_params,   &DispenserControlConfig[DispenserId].m_preError, &DispenserControlConfig[DispenserId].m_integral);
    DispenserControlConfig[DispenserId].m_calculatedError = (-1*DispenserControlConfig[DispenserId].m_calculatedError);
    calculated_speed = (1-DispenserControlConfig[DispenserId].m_calculatedError)*CurrentDispenserSpeed[DispenserId];
    Report("IDS_Pid_Testing_Func ",__FILE__,DispenserId,(int)(DispenserControlConfig[DispenserId].m_mesuredParam*1000),RpWarning,(int)calculated_speed,0);

    return OK;
}
void IDS_Start_Pid_Testing(int DispenserId)
{
    DispenserPidControlId[DispenserId] = AddControlCallback(NULL, IDS_Pid_Testing_Func,eOneSecond , TemplateDataReadCBFunction,DispenserId,DispenserId, DispenserId );
    Report("IDS_Start_Pid_Testing ",__FILE__,__LINE__,DispenserId,RpWarning,(int)DispenserPidControlId[DispenserId],0);
}
void IDS_Stop_Pid_Testing(int DispenserId)
{
    //stop this control loop
    RemoveControlCallback(DispenserPidControlId[DispenserId], IDS_Pid_Testing_Func );
    DispenserPidControlId[DispenserId] = 0xFF;
    Report("IDS_Stop_Pid_Testing ",__FILE__,__LINE__,DispenserId,RpWarning,(int)DispenserPidControlId[DispenserId],0);
}
/*
 * IDS Printing support
 * Prepare: build pressure in all participating dispensers
 * Print - dispense ink to the printing head
 * stop - stop dispensing
 *
 * control processes:
 * on prepare stage - each 10msec against the pressure sensors
 * on print stage - every 10/100 msec against the speed sensor
 *
 * */
//
/*
 void OpenJobFile();
void CloseJobFile();
JobDescriptionFileSegment *GetNextSegmentFromJobFile();
void FreeSegmentFileData(JobDescriptionFileSegment *Segment);
JobDescriptionFileBrushStop *GetNextBrushStopFromJobFile();
void FreeBrushStopFileData(JobDescriptionFileBrushStop *BrushStop);

 */
JobDescriptionFileBrushStop *FirstBrushStop = NULL;

//********************************************************************************************************************
 //************************** IDS JOB BUILDUP *************************************************************************
 //********************************************************************************************************************
 /************************************************************************************************************************************/
 uint32_t IDS_MapDispenserUsedinFileJob(void *JobDetails)
 {
     JobTicket* JobTicket = JobDetails;
     JobDescriptionFileBrushStop *BrushStop = NULL;
     JobDescriptionFileSegment *Segment = NULL;
     int Dispenser_i, Brush_i,DispenserId;
     FRESULT Fresult = FR_OK;
     uint32_t status = OK;
     bool lookForLubrication = false;
     int brushCounter = 0;


     //GeneralHwReady = false;
     for (Dispenser_i = 0;Dispenser_i<MAX_SYSTEM_DISPENSERS;Dispenser_i++)
     {
         DispenserUsedInJob[Dispenser_i] = false;
     }
     n_segments = 0;
     if (MotorsCfg[DispenserIdToMotorId[CLEANER_DISPENSER]].hardwaremotortype == DispenserIdToMotorId[CLEANER_DISPENSER])
     {
         DispenserUsedInJob[CLEANER_DISPENSER] = true;
         EnableCleaning = true;
     }
     else
     {
         DispenserUsedInJob[CLEANER_DISPENSER] = false;
         EnableCleaning = false;
     }

     if (JobTicket->enablelubrication == true)
     {
         //DispenserUsedInJob[LUBRICANT_DISPENSER] = true;
         lookForLubrication = true;
     }

     Fresult = OpenJobFile();
     if (Fresult == FR_OK)
     {
         Segment = GetNextSegmentFromJobFile();
         while(Segment)
         {
             n_segments++;
             if ((Segment->has_brushstopscount)&&(Segment->brushstopscount))
             {
                 for (Brush_i=0;Brush_i<Segment->brushstopscount;Brush_i++)
                 {
                     BrushStop = GetNextBrushStopFromJobFile();
                     if (BrushStop)
                     {
                         if (FirstBrushStop == NULL)
                             FirstBrushStop = BrushStop;
                         if ((brushCounter % 100)==0)
                         {
                             SendJobProgress(0.0,0,false, "Processing file");
                             Control_WD(ENABLE,55);  //activate heaters/dispenser watchdog, 0.5 seconds
                         }
                         if ((brushCounter % 1000)==900)
                         {
                             Task_sleep(200);
                             Report("Sleep in file parsing",__FILE__, BrushStop->index, brushCounter, RpWarning,   Segment->brushstopscount, 0);
                             Control_WD(ENABLE,55);  //activate heaters/dispenser watchdog, 0.5 seconds
                         }
                         brushCounter++;
                         if (BrushStop->n_dispensers)
                         {
                             for (Dispenser_i = 0;Dispenser_i < BrushStop->n_dispensers;Dispenser_i++)
                             {
                                 //prepare the SW structures
                                 DispenserId = BrushStop->dispensers[Dispenser_i]->index;
                                 if (BrushStop->dispensers[Dispenser_i]->nanolitterpersecond>0.0)
                                 {
                                     DispenserUsedInJob[DispenserId] = true;
                                     if ((lookForLubrication == true)&&(DispenserId == LUBRICANT_DISPENSER))
                                     {
                                         lookForLubrication = false;
                                         lubricant_speed = BrushStop->dispensers[Dispenser_i]->nanolitterpersecond/BrushStop->dispensers[Dispenser_i]->nanoliterperpulse;
                                         REPORT_MSG (BrushStop->dispensers[Dispenser_i]->nanolitterpersecond*100, "LUBRICANT nl / sec");
                                         REPORT_MSG (BrushStop->dispensers[Dispenser_i]->nanoliterperpulse*100, "LUBRICANT nl / pulse");
                                         REPORT_MSG (BrushStop->dispensers[Dispenser_i]->dispenserstepdivision, "step division");
                                         if (BrushStop->dispensers[Dispenser_i]->dispenserstepdivision
                                                 != DISPENSER_STEP_DIVISION__Auto)
                                         {
                                             //MotorSetMicroStep(HW_Motor_Id, Dispensers[Dispenser_i]->dispenserstepdivision);
                                             lubricant_speed /=
                                                     BrushStop->dispensers[Dispenser_i]->dispenserstepdivision; //the dye supply is calculated based on a 1/8 microstep
                                         }
                                         else
                                         {
                                             lubricant_speed/=8;//MotorsCfg[HW_Motor_Id].microstep;  //the dye supply is calculated based on a 1/8 microstep
                                         }
                                         REPORT_MSG (lubricant_speed*100, "LUBRICANT_SPEED*100");
                                     }
                                 }
                             }//for dispenser
                         }//if dispensers
                         else
                         {
                             LOG_ERROR (BrushStop->index, "no dispensers in brushstop");
                         }
                         if (FirstBrushStop!= BrushStop)
                             FreeBrushStopFileData(BrushStop);
                         BrushStop = NULL;
                     }
                     else
                     {
                         LOG_ERROR (BrushStop, "malloc error");
                         status = ERROR;
                     }
                 }//for brushstops
             }// if brush stop count
             FreeSegmentFileData(Segment);
             Segment = GetNextSegmentFromJobFile();
         }
         FreeSegmentFileData(Segment);
         CloseJobFile();
     }
     //GeneralHwReady = true;
     int ActiveDispensers = 0;
     for (Dispenser_i = 0; Dispenser_i < MAX_DYE_DISPENSERS; Dispenser_i++)
     {
         if (DispenserUsedInJob[Dispenser_i] == true) //we actually should check for all dispensers
         {
             ActiveDispensers++;
         }
     }
     Report("Finished checking the file", __FILE__, __LINE__, n_segments, RpWarning,   ActiveDispensers, 0);
     return status;

 }
 /*************************************************************************************************************************************/
 /*
 Parsing the job description file.
The job description file simply contains an array of segments and their brush stops.
The job description file is meant to be read brush stop by brush stop while the job is in progress.
The following diagram represents a single job description file segment structure.
The process of reading the whole file is simply repeating that reading order.

Each JobDescriptionFileSegment contains a �BrushStopsCount� field that should be used to determine how many brush stops are associated
with the current segment and how many times the process of reading brush stops should be repeated.
1. 32bit integer containing the next JobFileDescriptionSegment message byte count
2. JobDescriptionFileSegment message
3. 32bit integer containing the next JobDescriptionFileBrushStop message byte count
4. JobDescriptionFileBrushStop message

1.  Read segment message length.
2.  Read segment message.
a.  Read brush stop message length.
b.  Read brush stop message.
c.  Go to step 2.a x Segment.BrushStopsCount.
3.  Go to step 1 until end of file.
************************************************************************************************************************************/
 bool IDS_MapDispenserUsedinJob(void *JobDetails)
 {
     JobTicket* JobTicket = JobDetails;
     int Dispenser_i, Segment_i,Brush_i,DispenserId;

     uploadstrategy = JobTicket->uploadstrategy;
     if (JobTicket->uploadstrategy == JOB_UPLOAD_STRATEGY__JobDescriptionFile)
     {
         return (IDS_MapDispenserUsedinFileJob(JobDetails));
         //return (IDS_MapDispenserUsedinFileJobshort(JobDetails));
     }
     else
     {
         for (Dispenser_i = 0;Dispenser_i<MAX_SYSTEM_DISPENSERS;Dispenser_i++)
         {
             DispenserUsedInJob[Dispenser_i] = false;
         }
         if (n_segments == 0)
             return false;

         for (Segment_i=0;Segment_i<n_segments;Segment_i++)
             {
             for (Brush_i=0;Brush_i<JobTicket->segments[Segment_i]->n_brushstops;Brush_i++)
                 {
                     if (JobTicket->segments[Segment_i]->brushstops[Brush_i]->n_dispensers)
                     {
                         for (Dispenser_i = 0;Dispenser_i < JobTicket->segments[Segment_i]->brushstops[Brush_i]->n_dispensers;Dispenser_i++)
                         {
                             //prepare the SW structures
                             DispenserId = JobTicket->segments[Segment_i]->brushstops[Brush_i]->dispensers[Dispenser_i]->index;
                             if (JobTicket->segments[Segment_i]->brushstops[Brush_i]->dispensers[Dispenser_i]->nanolitterpersecond>0.0)
                             {
                                 DispenserUsedInJob[DispenserId] = true;
                                 if(DispenserId == LUBRICANT_DISPENSER)
                                 {
                                    lubricant_speed = JobTicket->segments[0]->brushstops[0]->dispensers[Dispenser_i]->nanolitterpersecond/
                                                     JobTicket->segments[0]->brushstops[0]->dispensers[Dispenser_i]->nanoliterperpulse;
                                    if (JobTicket->segments[0]->brushstops[0]->dispensers[Dispenser_i]->dispenserstepdivision
                                            != DISPENSER_STEP_DIVISION__Auto)
                                    {
                                        //MotorSetMicroStep(HW_Motor_Id, Dispensers[Dispenser_i]->dispenserstepdivision);
                                        lubricant_speed /=
                                                JobTicket->segments[0]->brushstops[0]->dispensers[Dispenser_i]->dispenserstepdivision; //the dye supply is calculated based on a 1/8 microstep
                                    }
                                    else
                                    {
                                        lubricant_speed/=8;//MotorsCfg[HW_Motor_Id].microstep;  //the dye supply is calculated based on a 1/8 microstep
                                    }
                                    REPORT_MSG (lubricant_speed*100, "LUBRICANT_SPEED*100");
                                 }

                             }
                         }//for dispenser
                     }//if dispensers
                 }//for brush
             }//for segments
     }

     return true;

 }

//********************************************************************************************************************
//************************** IDS PREPARE BUILDUP *********************************************************************
//********************************************************************************************************************
//********************************************************************************************************************
 uint32_t DispenserPrepareControlId = 0xFF;
 int NumOfActiveDispensers = 0;
 int DispenserBuildTimeCounter = 0;
#define PRESSURE_READ_TIME_GAP 100
uint32_t InactiveDispenserHome(uint32_t DispenserId, uint32_t ReadValue)
{
    if (AutoHoming_Config >= AutoHoming_JobEnd_PowerOn_off)
    {
        if ((HomingActive[DispenserId] == false)&&(PrimingActive[DispenserId] == false))
        {
            Report("Homing Inactive dispenser",__FILE__,DispenserId,HomingActive[DispenserId],RpWarning,PrimingActive[DispenserId],0);
            IDS_HomeDispenser (DispenserId, 1000 , NULL);
        }
        else
        {
            Report("Inactive dispenser already homing",__FILE__,DispenserId,HomingActive[DispenserId],RpWarning,PrimingActive[DispenserId],0);
        }
    }
    return OK;
}
 void DispenserPrepareReady(void)
 {
     int i;
     for (i=0;i<MAX_SYSTEM_DISPENSERS;i++)
     {
         if (DispenserReady[i] == false)
         {
             return; //not all configured Dispensers are ready
         }
     }
     REPORT_MSG(Module_IDS,"All Dispensers Prepare Ready");
     PrepareReady(Module_IDS,ModuleDone);
     for (i=0;i<MAX_SYSTEM_DISPENSERS;i++)//close valve and home inactive dispensers
     {
         if (DispenserUsedInJob[i] == false)
         {
             if ((HomingActive[i] == false)&&(PrimingActive[i] == false))
             {
                 Report("Inactive dispenser close valve",__FILE__,i,HomingActive[i],RpWarning,PrimingActive[i],0);
                 Control3WayValvesWithCallback (i, MidTank_Dispenser, InactiveDispenserHome); //direction: MidTank_Dispenser or Dispenser_Mixer
             }
         }
     }
 }
 //********************************************************************************************************************
  uint32_t IDS_PrepareReady(uint32_t deviceID, uint32_t ReadValue)
  {
      if (IDS_Active == false)
      {
         REPORT_MSG(deviceID,"IDS_Active false");
         return ERROR;
      }
      DispenserReady[deviceID] = true;
      REPORT_MSG(deviceID,"Dispenser prepare ready");
      DispenserPrepareReady();

      return OK; // all configured heaters are ready
  }
  char IdsMessage[100];
  int SegmentPrepareWFCFCounter = 0;
  bool pressureReady = false,endOfPrepareWCF = false;
 //********************************************************************************************************************
 uint32_t IDS_Prepare_Callback(uint32_t DispenserId, uint32_t ReadValue)
 {
     int i;
     int NumofReadyDispensers = 0;
     TimerMotors_t  HW_Motor_Id;
     float updatedSpeed,tempSpeed;
     JobDispenser **Dispensers;
 //set the speed only before the first segment, speed is constant accros job
     int Dispenser_i,n_dispensers;
     double segmentfirst_speed;

     //REPORT_MSG((int)DispenserPrepareControlId, "Prepare Callback");

     DispenserBuildTimeCounter+=PRESSURE_READ_TIME_GAP;

     if (pressureReady == false)
     {
         if (Special_Dispensers == false)
         {
             if (DispenserBuildTimeCounter<(2*eOneSecond))
             {
                 //REPORT_MSG(DispenserBuildTimeCounter,"waiting 2 seconds for pressure changes");
                 return OK;
             }
         }
         for (i = 0; i < MAX_DYE_DISPENSERS; i++)
         {
             //IDS_StopHomeDispenser(i);
             if (DispenserUsedInJob[i] == true) //we actually should check for all dispensers
             {
                 HW_Motor_Id = DispenserIdToMotorId[i];
                 if (GetDispenserPressure(i) > DispenserPreparePressure*1.05)
                 {
                     NumofReadyDispensers++;
                     if (Special_Dispensers == true)
                     {
                         MotorStop(HW_Motor_Id, Hard_Hiz);
                         CurrentDispenserSpeed[i] = 0;
                     //Report("IDS stopped ready",__FILE__,i,(int)(DispenserPreparePressure*100),RpWarning,(int)NumofReadyDispensers,0);
                     }
                     else
                     {
                         tempSpeed = CurrentDispenserSpeed[i];
                         updatedSpeed = (tempSpeed*0.992>100)?tempSpeed*0.992:100;
                         MotorSetSpeed(HW_Motor_Id, updatedSpeed);
                         CurrentDispenserSpeed[i] = updatedSpeed;
                         //Report("IDS reduce speed",__FILE__,i,(int)tempSpeed,RpWarning,(int)updatedSpeed,0);
                     }
                 }
                 else
                 {
                     if (Special_Dispensers == true)
                     {
                         tempSpeed = (DispenserPreparePressure-GetDispenserPressure(i))*InitialDispenserSpeed;
                         if (tempSpeed<300)
                             tempSpeed = 300;
                     }
                     else
                     {
                         tempSpeed = CurrentDispenserSpeed[i];
                     }
                     updatedSpeed = (tempSpeed*1.03<InitialDispenserSpeed)?tempSpeed*1.03:InitialDispenserSpeed;
                     MotorSetSpeed(HW_Motor_Id, updatedSpeed);
                     CurrentDispenserSpeed[i] = updatedSpeed;
                 }
             }
         }
         if (GetDispenserPressure(CLEANER_DISPENSER) > 0.02)
         {
             HW_Motor_Id = DispenserIdToMotorId[CLEANER_DISPENSER];
             MotorStop(HW_Motor_Id, Hard_Hiz);
             CurrentDispenserSpeed[CLEANER_DISPENSER] = 0;
             //Report("IDS stop cleaner",__FILE__,CLEANER_DISPENSER,(int)(GetDispenserPressure(CLEANER_DISPENSER)*100),RpWarning,(int)CurrentDispenserSpeed[CLEANER_DISPENSER] ,0);
         }

         if ((NumofReadyDispensers)&&(DispenserBuildTimeCounter%1000 == 0))
         {
             Report("IDS_Prepare_Callback",__FILE__,DispenserBuildTimeCounter,NumofReadyDispensers,RpWarning,(int)NumOfActiveDispensers,0);
         }
         if (NumofReadyDispensers>=NumOfActiveDispensers)
         {
             pressureReady = true;
             Report("pressureReady = true;",__FILE__,DispenserBuildTimeCounter,NumofReadyDispensers,RpWarning,(int)NumOfActiveDispensers,0);
         }
         if ((DispenserBuildTimeCounter >= DispenserPrepareTimeout)||(pressureReady == true))
         {
             DispenserBuildTimeCounter = 0;
             pressureReady = true;
             Report("pressureReady = true  or timeout;",__FILE__,DispenserBuildTimeCounter,NumofReadyDispensers,RpWarning,(int)NumOfActiveDispensers,0);
         }
     }
     else if (endOfPrepareWCF == false)
     {
         if ((DispenserBuildTimeCounter == PRESSURE_READ_TIME_GAP)&&(FirstBrushStop))
         {
             Report("start prepare dispensers at rate * WFCF",__FILE__,__LINE__,InterSegmentStepsCount,RpWarning,(int)lInterSegmentLength,0);
             if (FirstBrushStop)
             {
                 REPORT_MSG(FirstBrushStop->index,"WFCFBrushStopRead Index");
                 Dispensers = FirstBrushStop->dispensers;
                 n_dispensers = FirstBrushStop->n_dispensers;
                 if (n_dispensers)
                 {
                     for (Dispenser_i = 0; Dispenser_i < n_dispensers; Dispenser_i++)
                     {
                         DispenserId = Dispensers[Dispenser_i]->index;
                         HW_Motor_Id = DispenserIdToMotorId[DispenserId];
                         if (MotorsCfg[HW_Motor_Id].hardwaremotortype
                                 != DispenserIdToMotorId[DispenserId])
                             continue;
                         if ((DispenserId == CLEANER_DISPENSER)||(DispenserId == LUBRICANT_DISPENSER))
                         {
                             continue;
                         }
                         //(Speed*uStep*PPR)/((2*PI*Dispenser_Radius)
                         segmentfirst_speed = Dispensers[Dispenser_i]->nanolitterpersecond
                                 / Dispensers[Dispenser_i]->nanoliterperpulse;
                         if (Dispensers[Dispenser_i]->dispenserstepdivision
                                 != DISPENSER_STEP_DIVISION__Auto)
                         {
                             //MotorSetMicroStep(HW_Motor_Id, Dispensers[Dispenser_i]->dispenserstepdivision);
                             segmentfirst_speed /=
                                     Dispensers[Dispenser_i]->dispenserstepdivision; //the dye supply is calculated based on a 1/8 microstep
                         }
                         else
                         {
                             segmentfirst_speed/=8;//MotorsCfg[HW_Motor_Id].microstep;  //the dye supply is calculated based on a 1/8 microstep
                         }
                         if (segmentfirst_speed > MINIMAL_MOTOR_SPEED)
                         {
                             char IdMessage[100];
                             segmentfirst_speed *= (100+WFCF);
                             segmentfirst_speed /= 100;
                             DispenserSegmentReady[DispenserId] = false;
                             //Control3WayValvesWithCallback (DispenserId, Dispenser_Mixer, NULL); //direction: MidTank_Dispenser or Dispenser_Mixer
                             if (Special_Dispensers == true)
                             {
                                 IDS_Dispenser_Start_Motor_and_Open_Valve(DispenserId,segmentfirst_speed,NULL);
                             }
                             else
                             {
                                 MotorSetSpeed(HW_Motor_Id, segmentfirst_speed);
                             }
                             CurrentDispenserSpeed[Dispenser_i] = segmentfirst_speed;

                             usnprintf(IdMessage, 80,
                                       "WFCF Dispenser %d nl/sec %d nl/pulse %d Pulse/sec %d speed %d",
                                       DispenserId,
                                       (int) Dispensers[Dispenser_i]->nanolitterpersecond,
                                       (int) (Dispensers[Dispenser_i]->nanoliterperpulse*100),
                                       (int) Dispensers[Dispenser_i]->pulsepersecond*1000,
                                       (int) segmentfirst_speed*1000);
                             //REPORT_MSG(segmentfirst_speed,IdsMessage);
                             Report(IdsMessage, __FILE__, __LINE__, Dispenser_i, RpWarning,   segmentfirst_speed, 0);
                             //SendJobProgress(0.0, 0, false, IdsMessage);
                         }
                         else
                         {
                             if (DispenserUsedInJob[DispenserId] == true)
                             {
                                 MotorStop(HW_Motor_Id, Hard_Hiz);
                                 CurrentDispenserSpeed[Dispenser_i] = 0;
                                 usnprintf(IdsMessage, 80,"Presegment Prepare Dispenser %d stopped",DispenserId,(int) segmentfirst_speed);
                                 Report(IdsMessage, __FILE__, __LINE__, Dispenser_i, RpWarning,   segmentfirst_speed, 0);
                             }
                         }
                     }//for
                 }//if n_dis
             }
         }
         if ((InterSegmentStartWFCFDispensers > 0)&&((InterSegmentStartWFCFDispensers-1000) == DispenserBuildTimeCounter))
         {
             //close waste valve one second before segment - trial
             Valve_Set(VALVE_MIXCHIP_WASTECH, Mixer_Head);
         }
         if (DispenserBuildTimeCounter >=  InterSegmentStartWFCFDispensers)
         {
             endOfPrepareWCF = true;
             FreeBrushStopFileData(FirstBrushStop);
             FirstBrushStop =NULL;
             Report("endOfPrepareWCF = true;",__FILE__,DispenserBuildTimeCounter,NumofReadyDispensers,RpWarning,(int)NumOfActiveDispensers,0);

         }
     }
     //start new stage
     if (endOfPrepareWCF == true)
     {
         if (CurrentDispenserSpeed[CLEANER_DISPENSER])
         {
             HW_Motor_Id = DispenserIdToMotorId[CLEANER_DISPENSER];
             MotorStop(HW_Motor_Id, Hard_Hiz);
             CurrentDispenserSpeed[CLEANER_DISPENSER] = 0;
             Report("IDS stop cleaner",__FILE__,CLEANER_DISPENSER,(int)(GetDispenserPressure(CLEANER_DISPENSER)*100),RpWarning,(int)CurrentDispenserSpeed[CLEANER_DISPENSER] ,0);
         }
         SafeRemoveControlCallback(DispenserPrepareControlId, IDS_Prepare_Callback );
         DispenserPrepareControlId = 0xFF;
         Report("IDS_Prepare_Callback SafeRemoveControlCallback",__FILE__,DispenserBuildTimeCounter,NumofReadyDispensers,RpWarning,(int)NumOfActiveDispensers,0);
         setRapidPressureRead(false);
         pressureReady = false;
         endOfPrepareWCF = false;
         //DispenserPrepareReady();
         PrepareReady(Module_IDS,ModuleDone);
     }
     return OK;
 }
 //********************************************************************************************************************

 //********************************************************************************************************************
 uint32_t IDSPrepareState(void *JobDetails)
{
    int Motor_i, Pid_Id;
    //start IDS control for all motors
    IDS_Active = true;
    double cleanerFlow = 0;
    Valve_Set(VALVE_MIXCHIP_WASTECH, Mixer_Waste);

    JobTicket* JobTicket = JobDetails;

    JobEndSequence = false;
    if ((pressurebuildup>0.1)&&(pressurebuildup<2.5))
    {
        Report("Setting pressure from RML feeder tension",__FILE__,__LINE__,(int)(pressurebuildup*100),RpWarning,(int)(DispenserPreparePressure*100),0);
        DispenserPreparePressure = pressurebuildup;
    }
    if (JobTicket->headcleaningparameters)
    {
        if (JobTicket->headcleaningparameters->has_cleanerflow)
                cleanerFlow =  JobTicket->headcleaningparameters->cleanerflow;
    }
    if ((cleanerFlow>200)&&(cleanerFlow<1150))
    {
        Report("Setting pressure from RML feeder tension",__FILE__,__LINE__,(int)(cleanerFlow),RpWarning,(int)CleaningDispenserSpeed,0);
        CleaningDispenserSpeed = cleanerFlow;
    }
    else if (cleanerFlow <= 10)
    {
        CleaningDispenserSpeed = 0;
        EnableCleaning = false;
    }

    for (Motor_i = 0;Motor_i < MAX_SYSTEM_DISPENSERS;Motor_i++)
    {
        TimerMotors_t  HW_Motor_Id = DispenserIdToMotorId[Motor_i];
        Pid_Id = Motor_i;/*IDSMotorIdToControlId[Motor_i];*/
            DispenserControlConfig[Motor_i].m_params.MAX = 1;
            DispenserControlConfig[Motor_i].m_params.MIN = DispensersControl[Pid_Id].outputproportionalpowerlimit*-1;
            DispenserControlConfig[Motor_i].m_params.Kd = DispensersControl[Pid_Id].derivativetime;
            DispenserControlConfig[Motor_i].m_params.Kp = DispensersControl[Pid_Id].proportionalgain;
            DispenserControlConfig[Motor_i].m_params.Ki = DispensersControl[Pid_Id].integraltime;
            DispenserControlConfig[Motor_i].m_params.epsilon = 0.01;
            DispenserControlConfig[Motor_i].m_params.dt = eHundredMillisecond;
            DispenserControlConfig[Motor_i].m_calculatedError = 0;
            DispenserControlConfig[Motor_i].m_integral = 0;
            DispenserControlConfig[Motor_i].m_isEnabled = true;
            DispenserControlConfig[Motor_i].m_isReady = true;
            DispenserControlConfig[Motor_i].m_mesuredParam = 0;
            DispenserControlConfig[Motor_i].m_preError = 0;
            DispenserControlConfig[Motor_i].m_SetParam = 0;//need to update SetParams on presegment stage

            MotorSetDirection((TimerMotors_t)HW_Motor_Id,MotorsCfg[HW_Motor_Id].directionthreadwize); //set the dispenser to the
            /*Start the dispensers to build initial pressure
             * check different handling for dispensers that participate in the first segment and idle dispensers
             * start control for initial pressure
             *
             */
            //ValveCommand (Enable,MixerDirection);
    }

    if (HeaterCheckReady() == true)
    {
        REPORT_MSG(1,"heaters ready, starting");
        IDSPrepareStart();
    }
    return OK;
}

 void IDSPrepareStart(void)
 {
     int i;
     double dispenserspeed = 0;

     if (IDS_Active == false)
     {
         LOG_ERROR(IDS_Active,"IDSPrepareStart Not in a job");
         return;
     }

     NumOfActiveDispensers = 0;
     if (lubricant_speed > 0.1)
         IDS_StopHomeDispenser(LUBRICANT_DISPENSER);
      if ((EnableIntersegment == true)&&(IntersegmentLength>0))
         IDS_StopHomeDispenser(CLEANER_DISPENSER);
     for (i = 0; i < MAX_DYE_DISPENSERS; i++)
     {
         if (DispenserUsedInJob[i] == true) //we actually should check for all dispensers
         {
             NumOfActiveDispensers++;
             if (HomingActive[i] == true)
             {
                 Report("Stop active dispenser homing ",__FILE__,i,HomingActive[i],RpWarning,CurrentDispenserSpeed[i],0);
                 IDS_StopHomeDispenser(i);
             }
         }
         else
         {
             if (AutoHoming_Config >= AutoHoming_JobEnd_PowerOn_off)
             {
                 if ((HomingActive[i] == false)&&(PrimingActive[i] == false))
                 {
                     Report("Homing Inactive dispenser",__FILE__,i,HomingActive[i],RpWarning,PrimingActive[i],0);
                     IDS_HomeDispenser (i, 1000 , NULL);
                 }
                 else
                 {
                     Report("Inactive dispenser already homing",__FILE__,i,HomingActive[i],RpWarning,PrimingActive[i],0);
                 }
             }
         }
     }
     if (NumOfActiveDispensers)
     {
         REPORT_MSG((int)NumOfActiveDispensers, "Num Of Active Dispensers");
         for (i = 0; i < MAX_DYE_DISPENSERS; i++)
         {
             dispenserspeed = DispenserPrepareSpeed;
             if (Special_Dispensers == true)
             {
                 dispenserspeed = 300;
             }
             TimerMotors_t  HW_Motor_Id = DispenserIdToMotorId[i];

             if (DispenserUsedInJob[i] == true) //we actually should check for all dispensers
             {
                 //if (HomingActive[i] == false)
                 {
                     Report("Prepare Speed",__FILE__,HomingActive[i],i,RpWarning,(int)dispenserspeed,0);
                     MotorSetDirection((TimerMotors_t)HW_Motor_Id,MotorsCfg[HW_Motor_Id].directionthreadwize); //set the dispenser to the
                     if (Special_Dispensers == true)
                     {
                         MotorSetSpeed((TimerMotors_t)HW_Motor_Id,dispenserspeed); //set the dispenser to the
                     }
                     else
                     {
                         IDS_Dispenser_Start_Motor_and_Open_Valve(i,dispenserspeed, NULL);
                     }
                 }
                 /*else
                 {
                     Report("IDSPrepare Stop Dispenser Homing",__FILE__,__LINE__,i,RpWarning,(int)HomingActive[i],0);
                     IDS_HomeDispenserWaitForHomingEnd(i,  DispenserPrepareTimeout/2 , IDSPrepareStartDispenserAfterHoming);
                 }*/
             }
         }
         TimerMotors_t  HW_Motor_Id = DispenserIdToMotorId[CLEANER_DISPENSER];
         MotorSetSpeed((TimerMotors_t)HW_Motor_Id,INITIAL_CLEANER_SPEED ); //set the dispenser to the
         CurrentDispenserSpeed[CLEANER_DISPENSER] = INITIAL_CLEANER_SPEED;
         Report("IDS start cleaner",__FILE__,CLEANER_DISPENSER,(int)(GetDispenserPressure(CLEANER_DISPENSER)*100),RpWarning,(int)CurrentDispenserSpeed[CLEANER_DISPENSER] ,0);


     }
     DispenserBuildTimeCounter = 0;
     DispenserPrepareControlId = AddControlCallback(NULL, IDS_Prepare_Callback, PRESSURE_READ_TIME_GAP,TemplateDataReadCBFunction ,0, 0, 0 );
     setRapidPressureRead(true);

 }
 //********************************************************************************************************************
 //************************** IDS PRESEGMENT BUILDUP ******************************************************************
 //********************************************************************************************************************

 JobDescriptionFileBrushStop * FileBrushStop;
 char IdsMessage[100];

 //********************************************************************************************************************
  uint32_t IDS_PreSegmentPrepare_Callback(uint32_t DispenserId, uint32_t ReadValue)
  {
      bool pressureReady = false;
      int i;
      int NumofReadyDispensers = 0;
      TimerMotors_t  HW_Motor_Id;
      float updatedSpeed,tempSpeed;
      //REPORT_MSG((int)DispenserPrepareControlId, "Prepare Callback");

      DispenserBuildTimeCounter+=PRESSURE_READ_TIME_GAP;

      if (DispenserBuildTimeCounter<(1*eOneSecond))
      {
          //REPORT_MSG(DispenserBuildTimeCounter,"waiting 1 seconds for pressure changes");
          return OK;
      }
      if (PreSegmentWCFStarted == true)
      {
          REPORT_MSG(PreSegmentWCFStarted,"PreSegmentWCFStarted is true, stopping prepare stage");
          //SafeRemoveControlCallback(DispenserPrepareControlId, IDS_Prepare_Callback );
          setRapidPressureRead(false);
		  return OK;
      }
      for (i = 0; i < MAX_DYE_DISPENSERS; i++)
      {
          //IDS_StopHomeDispenser(i);
          if (DispenserUsedInSegment[i] == true) //we actually should check for all dispensers
          {
              HW_Motor_Id = DispenserIdToMotorId[i];
              if (GetDispenserPressure(i) > DispenserPreparePressure)
              {
                  NumofReadyDispensers++;
                  tempSpeed = CurrentDispenserSpeed[i];
                  updatedSpeed = (tempSpeed*0.98>100)?tempSpeed*0.98:100;
                  MotorSetSpeed(HW_Motor_Id, updatedSpeed);
                  CurrentDispenserSpeed[i] = updatedSpeed;
                  //Report("IDS PreSegment reduce speed",__FILE__,i,(int)tempSpeed,RpWarning,(int)updatedSpeed,0);
              }
              else
              {
                  tempSpeed = CurrentDispenserSpeed[i];
                  updatedSpeed = (tempSpeed*1.04<1100)?tempSpeed*1.04:1100;
                  MotorSetSpeed(HW_Motor_Id, updatedSpeed);
                  CurrentDispenserSpeed[i] = updatedSpeed;
                  //Report("IDS PreSegment accelerate speed",__FILE__,i,(int)tempSpeed,RpWarning,(int)updatedSpeed,0);
              }
          }
      }

      if ((NumofReadyDispensers)&&(DispenserBuildTimeCounter%1000 == 0))
          Report("IDS_PreSegmentPrepare_Callback",__FILE__,DispenserBuildTimeCounter,InterSegmentStepsCount,RpWarning,(int)NumOfActiveDispensers,0);

      if (NumofReadyDispensers>=NumOfActiveDispensers)
          pressureReady = true;

      if ((DispenserBuildTimeCounter >= DispenserPrepareTimeout)||(pressureReady == true))
      {
          Report("IDS_PreSegmentPrepare_Callback SafeRemoveControlCallback",__FILE__,DispenserPrepareControlId,InterSegmentStepsCount,RpWarning,(int)NumOfActiveDispensers,0);
          SafeRemoveControlCallback(DispenserPrepareControlId, IDS_Prepare_Callback );
          DispenserPrepareControlId = 0xFF;
          PreSegmentWCFStarted = true;
          setRapidPressureRead(false);
      }
      return OK;
  }
  //********************************************************************************************************************
 void IDSPresegmentPrepareStart(void)
 {

     int Dispenser_i,n_dispensers,DispenserId;
     TimerMotors_t HW_Motor_Id;
     double segmentfirst_speed;
     JobDispenser **Dispensers;

     for (Dispenser_i = 0; Dispenser_i < MAX_DYE_DISPENSERS; Dispenser_i++)
     {
         DispenserUsedInSegment[Dispenser_i] = false;
     }
     if (FileBrushStop)
     {
         Report("start dispensers pre segment pressure build",__FILE__,__LINE__,FileBrushStop->index,RpWarning,(int)FileBrushStop->n_dispensers,0);
         NumOfActiveDispensers = FileBrushStop->n_dispensers;
         Dispensers = FileBrushStop->dispensers;
         n_dispensers = FileBrushStop->n_dispensers;
         if (n_dispensers)
         {
             for (Dispenser_i = 0; Dispenser_i < n_dispensers; Dispenser_i++)
             {
                 DispenserId = Dispensers[Dispenser_i]->index;
                 HW_Motor_Id = DispenserIdToMotorId[DispenserId];
                 if (MotorsCfg[HW_Motor_Id].hardwaremotortype
                         != DispenserIdToMotorId[DispenserId])
                     continue;
                 if ((DispenserId == CLEANER_DISPENSER)||(DispenserId == LUBRICANT_DISPENSER))
                 {
                     continue;
                 }
                 //(Speed*uStep*PPR)/((2*PI*Dispenser_Radius)
                 segmentfirst_speed = Dispensers[Dispenser_i]->nanolitterpersecond
                         / Dispensers[Dispenser_i]->nanoliterperpulse;
                 if (segmentfirst_speed > MINIMAL_MOTOR_SPEED)
                 {
                     DispenserUsedInSegment[Dispenser_i] = true;
                     MotorSetSpeed(HW_Motor_Id, DispenserPrepareSpeed);
                     CurrentDispenserSpeed[Dispenser_i] = DispenserPrepareSpeed;
                     usnprintf(IdsMessage, 80,"Presegment Prepare Dispenser %d speed %d",DispenserId,(int) DispenserPrepareSpeed);
                     //REPORT_MSG(segmentfirst_speed,IdsMessage);
                     Report(IdsMessage, __FILE__, __LINE__, Dispenser_i, RpWarning,   DispenserPrepareSpeed, 0);
                     //SendJobProgress(0.0, 0, false, IdsMessage);
                 }
                 else
                 {
                     if (DispenserUsedInJob[DispenserId] == true)
                     {
                         if (Special_Dispensers == true)
                         {
                             IDS_Dispenser_Close_Valve_And_Stop_Motor(DispenserId,NULL);
                         }
                         else
                         {
                             MotorStop(HW_Motor_Id, Hard_Hiz);
                         }
                         CurrentDispenserSpeed[Dispenser_i] = 0;
                         usnprintf(IdsMessage, 80,"Presegment Prepare Dispenser %d stopped",DispenserId,(int) segmentfirst_speed);
                         Report(IdsMessage, __FILE__, __LINE__, Dispenser_i, RpWarning,   segmentfirst_speed, 0);
                     }
                 }
             }
             DispenserBuildTimeCounter = 0;
             //DispenserPrepareControlId = AddControlCallback( IDS_PreSegmentPrepare_Callback, PRESSURE_READ_TIME_GAP,TemplateDataReadCBFunction ,0, 0, 0 );
             setRapidPressureRead(true);

         }
     }

 }
 //********************************************************************************************************************
 uint32_t IDS_Valve_PresegmentValveReady(uint32_t deviceID, uint32_t ReadValue)
 {
     //TimerMotors_t  HW_Motor_Id = DispenserIdToMotorId[deviceID];
     //MotorStop(HW_Motor_Id,Hard_Hiz);
     //REPORT_MSG(deviceID,"Dispenser PreSegment called");
     IDS_Valve_PresegmentReady( deviceID,  ReadValue);
     return OK;
 }
 //********************************************************************************************************************
 uint32_t IDS_Valve_PresegmentReady(uint32_t deviceID, uint32_t ReadValue)
 {
     int i;
	 if (IDS_Active == false)
	 	return ERROR;
     DispenserPreSegmentReady[deviceID] = true;
     //REPORT_MSG(deviceID,"IDS_Valve_Presegment Ready");
     for (i=0;i<MAX_SYSTEM_DISPENSERS;i++)
     {
         if (DispenserPreSegmentReady[i] == false)
         {
             //REPORT_MSG(i,"IDS_Valve_Presegment Not Ready");
             return OK; //not all configured heaters are ready
         }
     }
     REPORT_MSG(deviceID,"IDS Presegment Ready!!");
     PreSegmentReady(Module_IDS,ModuleDone);
  return OK; // all configured heaters are ready
 }


//********************************************************************************************************************
 uint32_t IDSPreSegmentStateCallbackRunner(uint32_t IfIndex, uint32_t ReadValue)
 {
     JobDispenser **Dispensers;
 //set the speed only before the first segment, speed is constant accros job
     int Dispenser_i,n_dispensers,DispenserId;
     TimerMotors_t HW_Motor_Id;
     double segmentfirst_speed;
/*
  IDS Pre-Segment (Inter-Segment) activity
1.     Cleaning
2.     Build pressure toward the waste valve
The IDS pre-segment process will be performed before all segments, including the first one.
The dye dispensers will be in one of two states:
1.     After being idle � with a high pressure, idle motor and closed valve (will be performed in job prepare or during the previous segment. For Ty time until pressure Pa is achieved)
2.     At work � pushing dye to the mixer.
At pre-segment, TW seconds before its end, the active dispensers will start working in the next segment's speed: Ssegment* WFCF.
After TU milliseconds the valve will open.
At segment start, the waste valve will be redirected to the head and the dispensers' speed will be reduced to the next segment's speed
Cleaning: the cleaning process involves: starting the cleaning dispenser in speed Sclean, starting the head rockers motor(s) in speed S1 and S2,
and stopping the dispenser, centering the rockers Tending milliseconds before segment start.
Segment state:
1.     Active dispensers are working.
2.     Idle dispensers are filling, until TX+TY seconds before segment end. (TX=backlash time, TY=pressure prepare time). Then they reverse direction and start building pressure until segment end or until pressure PA (= prepare pressure) is achieved.
This means that for each Pre-segment we must calculate: TW,TU,Tending,
This means that for each segment we must calculate: Tx,Ty.

  */
/*uint32_t InterSegmentStartSprayCleaner = 500;
uint32_t InterSegmentStartRocking = 1000;
uint32_t InterSegmentCenterRockers = 3000;
uint32_t InterSegmentStartWFCFDispensers = lInterSegmentLength-1500;
uint32_t IDS_Cleaning_Move_Rockers (int LeftRockerSpeed,int RightRockerSpeed);
uint32_t IDS_Cleaning_Center_And_Stop_Rockers (int timeout,callback_fptr callback);
uint32_t IDS_Cleaning_Spray_Cleaning_Solution (int dispenserSpeed,callback_fptr callback);
uint32_t IDS_Cleaning_Stop_Cleaning_Solution (callback_fptr callback);
*/
     //InterSegmentStepsLimit = lInterSegmentLength*10;//100 millisec steps
     InterSegmentStepsCount+=IDS_PRESEGMENT_TIME_STEP;
     if (InterSegmentStepsCount >= lInterSegmentLength)
     {
         //IDS_Valve_PresegmentReady(1,0);
         Report("End of Pre-segment Handling",__FILE__,__LINE__,InterSegmentStepsCount,RpWarning,(int)lInterSegmentLength,0);
         //PreSegmentReady(Module_IDS,ModuleDone);
         SafeRemoveControlCallback(DispenserPreSegmentControlId,IDSPreSegmentStateCallbackRunner);
         if (CurrentDispenserSpeed[CLEANER_DISPENSER]>0)
             IDS_Cleaning_Stop_Cleaning_Solution (NULL);
         setRapidPressureRead(false);
         if (JobEndSequence == true)
             DistanceToSpoolReady(Module_IDS,ModuleDone);
     }
     if (EnableCleaning == true)
     {
         if (( InterSegmentStartSprayCleaner )&&(InterSegmentStartSprayCleaner == InterSegmentStepsCount))
         {
             Report("Start Spray Cleaner",__FILE__,__LINE__,InterSegmentStepsCount,RpWarning,(int)lInterSegmentLength,0);
             IDS_Cleaning_Spray_Cleaning_Solution (CleaningDispenserSpeed,NULL);
         }
         /*if (InterSegmentStartRocking == InterSegmentStepsCount)
         {
             Report("Start cleaning rockers",__FILE__,__LINE__,InterSegmentStepsCount,RpWarning,(int)lInterSegmentLength,0);
             //IDS_Cleaning_Move_Rockers (int LeftRockerSpeed,int RightRockerSpeed);
         }*/
         if (( InterSegmentStartSprayCleaner )&&
            ((InterSegmentStepsCount>InterSegmentStartSprayCleaner)&&(InterSegmentStepsCount<(lInterSegmentLength-InterSegmentCenterRockers) )))
         {
             if (ActuatorsCleaningSequence)
                 IDS_Cleaning_Move_Actuators();
         }
         if (( InterSegmentCenterRockers)&&(InterSegmentCenterRockers == (lInterSegmentLength-InterSegmentStepsCount)))
         {
             Report("Stop spray and center rockers",__FILE__,__LINE__,InterSegmentStepsCount,RpWarning,(int)lInterSegmentLength,0);
             IDS_Cleaning_Stop_Cleaning_Solution (NULL);
             //IDS_Cleaning_Center_And_Stop_Rockers (int timeout,callback_fptr callback);
         }
     }
     if (FileBrushStop == NULL)
     {
         //cleaning
         return OK;
     }
     if ((InterSegmentStartWFCFDispensers > 12000)&&(InterSegmentStartWFCFDispensers == (InterSegmentStepsCount+12000)))
     {
	 	//start prepare 4 seconds before wcf
         IDSPresegmentPrepareStart();
         PreSegmentPrepareStarted = true;
     }
     if ((PreSegmentWCFStarted == false)&&(PreSegmentPrepareStarted == true))
     {
         IDS_PreSegmentPrepare_Callback(0,0);
     }
     if ((InterSegmentStartWFCFDispensers > 0)&&(InterSegmentStartWFCFDispensers == InterSegmentStepsCount))
     {
         Report("start dispensers at rate * WFCF",__FILE__,__LINE__,InterSegmentStepsCount,RpWarning,(int)lInterSegmentLength,0);
         PreSegmentWCFStarted = true; // stop any presegment prepare stages, if still exist
         PreSegmentPrepareStarted = false;
         setRapidPressureRead(false);
         if (FileBrushStop)
         {
             REPORT_MSG(FileBrushStop->index,"WFCFBrushStopRead Index");
             Dispensers = FileBrushStop->dispensers;
             n_dispensers = FileBrushStop->n_dispensers;
             if (n_dispensers)
             {
                 for (Dispenser_i = 0; Dispenser_i < n_dispensers; Dispenser_i++)
                 {
                     DispenserId = Dispensers[Dispenser_i]->index;
                     HW_Motor_Id = DispenserIdToMotorId[DispenserId];
                     if (MotorsCfg[HW_Motor_Id].hardwaremotortype
                             != DispenserIdToMotorId[DispenserId])
                         continue;
                     if ((DispenserId == CLEANER_DISPENSER)||(DispenserId == LUBRICANT_DISPENSER))
                     {
                         continue;
                     }
                     //(Speed*uStep*PPR)/((2*PI*Dispenser_Radius)
                     segmentfirst_speed = Dispensers[Dispenser_i]->nanolitterpersecond
                             / Dispensers[Dispenser_i]->nanoliterperpulse;
                     if (Dispensers[Dispenser_i]->dispenserstepdivision
                             != DISPENSER_STEP_DIVISION__Auto)
                     {
                         //MotorSetMicroStep(HW_Motor_Id, Dispensers[Dispenser_i]->dispenserstepdivision);
                         segmentfirst_speed /=
                                 Dispensers[Dispenser_i]->dispenserstepdivision; //the dye supply is calculated based on a 1/8 microstep
                     }
                     else
                     {
                         segmentfirst_speed/=8;//MotorsCfg[HW_Motor_Id].microstep;  //the dye supply is calculated based on a 1/8 microstep
                     }
                     if (segmentfirst_speed > MINIMAL_MOTOR_SPEED)
                     {
                         char IdMessage[100];
                         segmentfirst_speed *= (100+WFCF);
                         segmentfirst_speed /= 100;
                         DispenserSegmentReady[DispenserId] = false;
                         //Control3WayValvesWithCallback (DispenserId, Dispenser_Mixer, NULL); //direction: MidTank_Dispenser or Dispenser_Mixer
                         //IDS_Dispenser_Start_Motor_and_Open_Valve(DispenserId,segmentfirst_speed,NULL);
                         MotorSetSpeed(HW_Motor_Id, segmentfirst_speed);
                         CurrentDispenserSpeed[Dispenser_i] = segmentfirst_speed;

                         usnprintf(IdMessage, 80,
                                   "WFCF Dispenser %d nl/sec %d nl/pulse %d Pulse/sec %d speed %d",
                                   DispenserId,
                                   (int) Dispensers[Dispenser_i]->nanolitterpersecond,
                                   (int) Dispensers[Dispenser_i]->nanoliterperpulse,
                                   (int) Dispensers[Dispenser_i]->pulsepersecond*1000,
                                   (int) segmentfirst_speed*1000);
                         //REPORT_MSG(segmentfirst_speed,IdsMessage);
                         Report(IdsMessage, __FILE__, __LINE__, Dispenser_i, RpWarning,   segmentfirst_speed, 0);
                         //SendJobProgress(0.0, 0, false, IdsMessage);
                     }
                 }
             }
         }
         //startDispensersAtSegmentSpeed*1=WFCFClenerSpray(speed);
     }
     if ((InterSegmentStartWFCFDispensers > 0)&&((lInterSegmentLength-1000) == InterSegmentStepsCount))
     {
         //close waste valve one second before segment - trial
         Valve_Set(VALVE_MIXCHIP_WASTECH, Mixer_Head);
     }
     return OK;
 }
uint32_t IDSCheckSegmentData(void *SegmentDetails, int SegmentId)
{
    JobDescriptionFileSegment* PrevSegment = SegmentDetails;
    uint32_t status = OK;
    JobDescriptionFileBrushStop * tFileBrushStop;
    int Brush_i;

//check and close previous segment
    if (PrevSegment)
    {
        if (JobBrushStopId<PrevSegment->brushstopscount) //we did not finish reading the brushstops of the previous segment
        {
            Report("Unhandled brushstops remained",__FILE__,__LINE__,(int)JobBrushStopId,RpWarning,(int)PrevSegment->brushstopscount,0);
            //REPORT_MSG (Segment->brushstopscount, "Segment->brushstopscount");
            for (Brush_i=JobBrushStopId;Brush_i<PrevSegment->brushstopscount;Brush_i++)
            {
                if (status == ERROR)
                    break;
                tFileBrushStop = GetNextBrushStopFromJobFile();
                if (tFileBrushStop)
                {
                    FreeBrushStopFileData(tFileBrushStop);
                    Report("Unhandled brushstops handled",__FILE__,__LINE__,(int)Brush_i,RpWarning,(int)PrevSegment->brushstopscount,0);
                }
                else
                {
                    status = ERROR;
                    break;
                }
            }//for brushstops
        }
    }
    return status;
}
uint32_t IDSPreSegmentState(void *SegmentDetails, int SegmentId)
{
    JobSegment* Segment = SegmentDetails;
    JobDispenser **Dispensers;
//set the speed only before the first segment, speed is constant accros job
    int Dispenser_i,n_dispensers,DispenserId;
    TimerMotors_t HW_Motor_Id;

    JobBrushStopId = 0;

    // activate control fr all motors
    /* wait for all dispensers  to get to the required pressure
     * move the presegment ready when all dispensers are ready.
     */
    REPORT_MSG(Segment->n_brushstops,"IDSPreSegmentState");
    if (JobBrushStopId>=Segment->n_brushstops)
    {
        LOG_ERROR(Segment->n_brushstops,"Error JobBrushStopId");
        JobEndReason = JOB_OUT_OF_DYE;
        PreSegmentReady(Module_IDS,ModuleFail);
        return ERROR;
    }
    if (FileBrushStop)
    {
        //REPORT_MSG(FileBrushStop->index,"calling Free BrushStop file Read Index");
        FreeBrushStopFileData(FileBrushStop);
    }
    if ((EnableIntersegment == true)&&(IntersegmentLength>0))
    {
        Valve_Set(VALVE_MIXCHIP_WASTECH, Mixer_Waste); //if intersegment is defined throw the ink away
        if (SegmentId>0)
        {
            lInterSegmentLength = ((IntersegmentLength*100)*1000/dyeingspeed);

            lInterSegmentLength-=(lInterSegmentLength%100);   //round to a 100 multiplication
            InterSegmentStepsCount = 0;
            if (minimal_intersegment_length<((IntersegmentLength*100)*1000/dyeingspeed))
            {
                DispenserPreSegmentControlId = AddControlCallback(NULL,IDSPreSegmentStateCallbackRunner, IDS_PRESEGMENT_TIME_STEP,TemplateDataReadCBFunction ,0, 0, 0 );
                if (DispenserPreSegmentControlId == 0xFF)
                {
                    Report("Add control callback failed",__FILE__,__LINE__,(int)100,RpWarning,(int)0,0);
                    return ERROR;
                }
                Report("Add control callback ",__FILE__,__LINE__,(int)100,RpWarning,(int)lInterSegmentLength,0);
            }
            /*if (EnableCleaning == true)
            {
                InterSegmentStartSprayCleaner = 500;
                InterSegmentStartRocking = 1000;
                InterSegmentCenterRockers = 3000;
            }*/

            //InterSegmentStartWFCFDispensers = (lInterSegmentLength>5000)?lInterSegmentLength-5000:lInterSegmentLength;
        }
    }
    /*if (SegmentId == 0)//first segment - running the DTS out of the drier
    {
        Valve_Set(VALVE_MIXCHIP_WASTECH, Mixer_Waste); //if intersegment is defined throw the ink away
        for (Dispenser_i = 0; Dispenser_i < MAX_SYSTEM_DISPENSERS; Dispenser_i++)
        {
            HW_Motor_Id = DispenserIdToMotorId[Dispenser_i];
            //MotorStop(HW_Motor_Id,Hard_Hiz); //26/03/19 test  without valves
            if (DispenserUsedInJob[Dispenser_i]==true)//unconfigured dispenser
            {
                MotorStop(HW_Motor_Id, Soft_Hiz);
                CurrentDispenserSpeed[Dispenser_i] = 0;
            }
        }
        lInterSegmentLength = (dryerbufferCentimeters*1000/dyeingspeed);
        lInterSegmentLength-=(lInterSegmentLength%100);   //round to a 100 multiplication
        InterSegmentStepsCount = 0;
        DispenserPreSegmentControlId = AddControlCallback( IDSPreSegmentStateCallbackRunner, IDS_PRESEGMENT_TIME_STEP,TemplateDataReadCBFunction ,0, 0, 0 );
        if (DispenserPreSegmentControlId == 0xFF)
        {
            Report("Add control callback failed",__FILE__,__LINE__,(int)100,RpWarning,(int)0,0);
            return ERROR;
        }
        Report("Add control callback ",__FILE__,__LINE__,(int)100,RpWarning,(int)lInterSegmentLength,0);
        if (EnableCleaning == true)
        {
            InterSegmentStartSprayCleaner = 500;
            InterSegmentStartRocking = 1000;
            InterSegmentCenterRockers = 3000;
        }
        InterSegmentStartWFCFDispensers = lInterSegmentLength-5000;
    }*/

    if (uploadstrategy == JOB_UPLOAD_STRATEGY__Default)
    {
        Dispensers = Segment->brushstops[JobBrushStopId]->dispensers;
        n_dispensers = Segment->brushstops[JobBrushStopId]->n_dispensers;
    }
    else
    {
        if (BrushStopControlId != 0xFF)
        {
            RemoveControlCallback(BrushStopControlId,IDSBrushStopRestartCallback);
            BrushStopControlId = 0xFF;
        }
        FileBrushStop = GetNextBrushStopFromJobFile();
        if (FileBrushStop)
        {
            REPORT_MSG(FileBrushStop->index,"BrushStopRead Index");
            Dispensers = FileBrushStop->dispensers;
            n_dispensers = FileBrushStop->n_dispensers;
        }
        else
        {
            LOG_ERROR(FileBrushStop,"End run - BrushStopReadError");
            JobEndReason = JOB_OUT_OF_DYE;
            PreSegmentReady(Module_IDS,ModuleFail);
        }
    }
    if (n_dispensers)
    {
        for (Dispenser_i = 0;Dispenser_i < n_dispensers;Dispenser_i++)
        {
            DispenserId = Dispensers[Dispenser_i]->index;
            DispenserPreSegmentReady[DispenserId] = false;
        }
        for (Dispenser_i = 0;Dispenser_i < n_dispensers;Dispenser_i++)
        {
            DispenserId = Dispensers[Dispenser_i]->index;
            HW_Motor_Id = DispenserIdToMotorId[DispenserId];
            if (MotorsCfg[HW_Motor_Id].hardwaremotortype != DispenserIdToMotorId[DispenserId])//unconfigured dispenser
            {
                REPORT_MSG(DispenserId,"Dispenser PreSegment not configured");
                DispenserPreSegmentReady[DispenserId] = true; //27/03/19 check if job should be stopped
                IDS_Valve_PresegmentReady(DispenserId,0); //27/03/19 to be removed when the presegment handler will be added
                continue;
            }
            if ((DispenserId == CLEANER_DISPENSER)||(DispenserId == LUBRICANT_DISPENSER))
            {
                REPORT_MSG(DispenserId,"Dispenser PreSegment cleaner or lubricant");
                DispenserPreSegmentReady[DispenserId] = true; //27/03/19 check if job should be stopped
                IDS_Valve_PresegmentReady(DispenserId,0); //27/03/19 to be removed when the presegment handler will be added
                continue;
            }

            //REPORT_MSG(DispenserId,"IDS_Valve_Presegment start");
            if (Dispensers[Dispenser_i]->dispenserstepdivision != DISPENSER_STEP_DIVISION__Auto)
            {
                MotorSetMicroStep(HW_Motor_Id, Dispensers[Dispenser_i]->dispenserstepdivision);
            }
            else
            {
                MotorSetMicroStep(HW_Motor_Id, MotorsCfg[HW_Motor_Id].microstep);
            }
            if ((EnableIntersegment == true)&&(IntersegmentLength>0.1)&&(SegmentId>0))
            {
                if (DispenserUsedInJob[DispenserId] == true)
                {
                    if (Special_Dispensers == true)
                    {
                        IDS_Dispenser_Close_Valve_And_Stop_Motor(DispenserId,NULL);
                    }
                    else
                    {
                        MotorStop(HW_Motor_Id,Soft_Hiz); //26/03/19 test  without valves
                    }
                    CurrentDispenserSpeed[DispenserId] = 0;
                    //MotorSetSpeed(HW_Motor_Id, 5);
                    //CurrentDispenserSpeed[Dispenser_i] = 5;
                    DispenserPreSegmentReady[DispenserId] = true; //27/03/19 check if job should be stopped
                    REPORT_MSG(DispenserId,"Dispenser stopped pre Segment");
                }
            }
            IDS_Valve_PresegmentReady(DispenserId,0); //27/03/19 to be removed when the presegment handler will be added
        }
    }
    //Task_sleep(5);
    PreSegmentWCFStarted = false;
    PreSegmentPrepareStarted = false;
    //REPORT_MSG(PreSegmentWCFStarted,"START IDSPresegmentPrepareStart");

//    IDSPresegmentPrepareStart();
    return OK;
}
//********************************************************************************************************************

uint32_t SegmentNumOfBrushStops = 0;
double BrushStopTime = 0;
uint32_t BrushStopCounter = 0;
uint32_t BrushStopStartTime = 0,BrushStopLength = 0;

void IDS_StartBrushStop(int n_dispensers,  JobDispenser** Dispensers)
{
    int Dispenser_i,DispenserId;
    TimerMotors_t HW_Motor_Id;
    double segmentfirst_speed;
    Report("IDS_StartBrushStop",__FILE__,__LINE__,(int)JobBrushStopId,RpWarning,(int)0,0);

    if (n_dispensers)
    {
        for (Dispenser_i = 0; Dispenser_i < n_dispensers; Dispenser_i++)
        {
            DispenserId = Dispensers[Dispenser_i]->index;
            HW_Motor_Id = DispenserIdToMotorId[DispenserId];
            if (MotorsCfg[HW_Motor_Id].hardwaremotortype
                    != DispenserIdToMotorId[DispenserId])
                continue;
            if ((DispenserId == CLEANER_DISPENSER)||(DispenserId == LUBRICANT_DISPENSER))
            {
                continue;
            }
            //(Speed*uStep*PPR)/((2*PI*Dispenser_Radius)
            segmentfirst_speed = Dispensers[Dispenser_i]->nanolitterpersecond
                    / Dispensers[Dispenser_i]->nanoliterperpulse;
            if (Dispensers[Dispenser_i]->dispenserstepdivision != DISPENSER_STEP_DIVISION__Auto)
            {
                //MotorSetMicroStep(HW_Motor_Id, Dispensers[Dispenser_i]->dispenserstepdivision);
                segmentfirst_speed /=
                        Dispensers[Dispenser_i]->dispenserstepdivision; //the dye supply is calculated based on a 1/8 microstep
            }
            else
            {
                segmentfirst_speed/=8;//MotorsCfg[HW_Motor_Id].microstep;  //the dye supply is calculated based on a 1/8 microstep
            }
            if (segmentfirst_speed > MINIMAL_MOTOR_SPEED)
            {
                DispenserSegmentReady[DispenserId] = false;
                //Control3WayValvesWithCallback (DispenserId, Dispenser_Mixer, NULL); //direction: MidTank_Dispenser or Dispenser_Mixer
                /*IDS_Dispenser_Start_Motor_and_Open_Valve(DispenserId,
                                                         segmentfirst_speed,
                                                         NULL);*/
                //Control3WayValvesWithCallback (DispenserId, Dispenser_Mixer, NULL); //direction: MidTank_Dispenser or Dispenser_Mixer
                if (Special_Dispensers == true)
                {
                    IDS_Dispenser_Start_Motor_and_Open_Valve(DispenserId,segmentfirst_speed, NULL);
                }
                else
                {
                    MotorSetSpeed(HW_Motor_Id, segmentfirst_speed);
                }
                CurrentDispenserSpeed[DispenserId] = segmentfirst_speed;
                usnprintf(IdsMessage, 80,
                          "Dispenser %d nl/sec %d nl/pulse %d Pulse/sec %d speed %d steps %d/%d",
                          DispenserId,
                          (int) Dispensers[Dispenser_i]->nanolitterpersecond,
                          (int) Dispensers[Dispenser_i]->nanoliterperpulse,
                          (int) Dispensers[Dispenser_i]->pulsepersecond*1000,
                          (int) (segmentfirst_speed*1000),Dispensers[Dispenser_i]->dispenserstepdivision,MotorsCfg[HW_Motor_Id].microstep);
                //REPORT_MSG(segmentfirst_speed,IdsMessage);
                Report(IdsMessage, __FILE__, __LINE__, Dispenser_i, RpWarning,   segmentfirst_speed, 0);
                //SendJobProgress(0.0, 0, false, IdsMessage);
            }
            else
            {
                DispenserSegmentReady[DispenserId] = true;
                //IDS_Dispenser_Close_Valve_And_Stop_Motor(DispenserId,NULL);*/
                if (DispenserUsedInJob[DispenserId] == true)
                {
                    if (Special_Dispensers == true)
                    {
                        IDS_Dispenser_Close_Valve_And_Stop_Motor(DispenserId,NULL);
                    }
                    else
                    {
                        MotorStop(HW_Motor_Id, Hard_Hiz);
                    }
                    CurrentDispenserSpeed[DispenserId] = 0;
                }
                //Report("inActive dispenser stopped", __FILE__, __LINE__, DispenserId, RpWarning,   segmentfirst_speed, 0);
            }
        }
    }
}
uint32_t IDSBrushStopRestartCallback(uint32_t IfIndex, uint32_t readValue)
{
    JobDispenser **Dispensers = NULL;
    int n_dispensers = 0;
    JobSegment* Segment = (void *)IfIndex;
    Task_Stat statbuf;
    if(BrushStopStartTime+BrushStopLength+100 < msec_millisecondCounter   )
    {
        Report("!!!!!!!!!!!!Overtime!",__FILE__,msec_millisecondCounter,(int)BrushStopStartTime,RpWarning,(int)BrushStopLength,0);
    }

    if(++BrushStopCounter <  BrushStopTime)
    {
        return OK;
    }
    BrushStopCounter = 0;
    BrushStopStartTime = msec_millisecondCounter;


    if (JobEndSequence == true)
    {
        Report("JobEndSequence",__FILE__,__LINE__,(int)JobBrushStopId,RpWarning,(int)SegmentNumOfBrushStops,0);
        SafeRemoveControlCallback(BrushStopControlId,IDSBrushStopRestartCallback);
        BrushStopControlId = 0Xff;
        return OK;
    }
    if (uploadstrategy == JOB_UPLOAD_STRATEGY__Default)
    {
        Dispensers = Segment->brushstops[JobBrushStopId]->dispensers;
        n_dispensers = Segment->brushstops[JobBrushStopId]->n_dispensers;
    }
    else
    {
        if (FileBrushStop)
            FreeBrushStopFileData(FileBrushStop);
        FileBrushStop = GetNextBrushStopFromJobFile();
        if (FileBrushStop)
        {
            Task_stat(Control_Task_Handle,&statbuf);
            //REPORT_MSG(FileBrushStop->index,"BrushStopRead Index");
            Report("BrushStopRead Index",__FILE__,BrushStopTime,(int)FileBrushStop->index,RpWarning,(int)BrushStopStartTime,0);
            Dispensers = FileBrushStop->dispensers;
            n_dispensers = FileBrushStop->n_dispensers;
        }
        else
        {
            LOG_ERROR(FileBrushStop,"BrushStopReadError");
            JobEndReason = JOB_OUT_OF_DYE;
            SegmentReady(Module_IDS,ModuleFail);
        }
    }
    if (n_dispensers)
    {
        IDS_StartBrushStop(n_dispensers, Dispensers);
    }
    JobBrushStopId++;
    Report("brushstop",__FILE__,__LINE__,(int)JobBrushStopId,RpWarning,(int)SegmentNumOfBrushStops,0);
    if (JobBrushStopId >= SegmentNumOfBrushStops)
    {
        Report("last brushstop",__FILE__,__LINE__,(int)JobBrushStopId,RpWarning,(int)SegmentNumOfBrushStops,0);
        SafeRemoveControlCallback(BrushStopControlId,IDSBrushStopRestartCallback);
        BrushStopControlId = 0Xff;
    }
    return OK;
}
#define VALVE_DELAY 1
#ifdef VALVE_DELAY

uint32_t IDSValveControlId = 0xFF;
uint32_t IDSSegmentStateSetValve(uint32_t IfIndex, uint32_t BusyFlag)
{
    SafeRemoveControlCallback(IDSValveControlId,IDSSegmentStateSetValve);
    Valve_Set(VALVE_MIXCHIP_WASTECH, Mixer_Head);
    Report("IDSSegmentState set valve after 1 second",__FILE__,__LINE__,(int)0,RpWarning,(int)0,0);
    return OK;
}
#endif
//********************************************************************************************************************
uint32_t IDSSegmentState(void *SegmentDetails, int SegmentId)
{
     JobSegment* Segment = SegmentDetails;
     JobDispenser **Dispensers;
     int n_dispensers;

#ifdef VALVE_DELAY
     IDSValveControlId = AddControlCallback("VALVE_DELAY",IDSSegmentStateSetValve, eOneSecond,TemplateDataReadCBFunction,0,0,0);
#else
     Valve_Set(VALVE_MIXCHIP_WASTECH, Mixer_Head);
#endif
     if (CurrentDispenserSpeed[CLEANER_DISPENSER]>0)
         IDS_Cleaning_Stop_Cleaning_Solution(NULL);
     SegmentNumOfBrushStops = Segment->n_brushstops;
     BrushStopTime = Segment->length*1000/(double)SegmentNumOfBrushStops;  //brushstop in meters //brushstop in millisecond
     BrushStopTime = ((BrushStopTime*100)/dyeingspeed);//brushstop in seconds
     //BrushStopTime *= 1000;
     Report("IDSSegmentState bss time, num of bss",__FILE__,__LINE__,(int)(BrushStopTime),RpWarning,(int)SegmentNumOfBrushStops,0);
     if (uploadstrategy == JOB_UPLOAD_STRATEGY__Default)
     {
         Dispensers = Segment->brushstops[JobBrushStopId]->dispensers;
         n_dispensers = Segment->brushstops[JobBrushStopId]->n_dispensers;
     }
     else
     {
         if (FileBrushStop)
         {
             Dispensers = FileBrushStop->dispensers;
             n_dispensers = FileBrushStop->n_dispensers;
         }
         else
         {
             LOG_ERROR(FileBrushStop,"BrushStopReadError");
         }
     }
//handle ending of presegment
    if (DispenserPreSegmentControlId != 0xFF)
    {
        Report("IDS presegment not yet ended!!",__FILE__,DispenserPreSegmentControlId,(int)InterSegmentStepsCount,RpWarning,(int)lInterSegmentLength,0);
        RemoveControlCallback(DispenserPreSegmentControlId, IDSPreSegmentStateCallbackRunner );
        DispenserPreSegmentControlId = 0xFF;
    }

    IDS_StartBrushStop(n_dispensers, Dispensers);
    JobBrushStopId++;
    if ((BrushStopTime)&&(SegmentNumOfBrushStops > 1))
    {
        //BrushStopCounter = BrushStopTime+3; //move the IDS brush stop handling to highest priority in timing
        BrushStopControlId = AddControlCallback(NULL, IDSBrushStopRestartCallback, eOneMillisecond,TemplateDataReadCBFunction ,SegmentDetails, 0, 0 );
        BrushStopStartTime = msec_millisecondCounter;
        BrushStopLength = BrushStopTime;
        if (BrushStopControlId == 0xFF)
        {
            Report("Add control callback failed",__FILE__,__LINE__,(int)BrushStopTime,RpWarning,(int)0,0);
            return ERROR;
        }
        Report("Add control callback ",__FILE__,__LINE__,(int)BrushStopTime,RpWarning,(int)n_dispensers,0);

    }
    else
    {
        if (FileBrushStop)
            FreeBrushStopFileData(FileBrushStop);
        FileBrushStop = NULL;
    }

    return OK;
}
 //********************************************************************************************************************
 uint32_t IDS_Valve_DistanceToSpoolValveReady(uint32_t deviceID, uint32_t ReadValue)
 {
     //TimerMotors_t  HW_Motor_Id = DispenserIdToMotorId[deviceID];
     //MotorStop(HW_Motor_Id,Hard_Hiz);
     //REPORT_MSG(deviceID,"Dispenser DTS called");
     IDS_Valve_DistanceToSpoolReady( deviceID,  ReadValue);
     return OK;
}

 //********************************************************************************************************************
  uint32_t IDS_Valve_DistanceToSpoolReady(uint32_t deviceID, uint32_t ReadValue)
  {
      int i;
      DispenserDistanceToSpoolReady[deviceID] = true;
      REPORT_MSG(deviceID,"IDS_Valve_DistanceToSpoolReady End called");
      for (i=0;i<MAX_DYE_DISPENSERS;i++)
      {
          if (DispenserDistanceToSpoolReady[i] == false)
          {
              return OK; //not all configured heaters are ready
          }
      }
      DistanceToSpoolReady(Module_IDS,ModuleDone);
   return OK; // all configured heaters are ready
  }
 //********************************************************************************************************************
 uint32_t IDSDistanceToSpoolState(void)
 {
     int Dispenser_i;

     REPORT_MSG(NumOfActiveDispensers,"Dispenser DTS");
     Valve_Set(VALVE_MIXCHIP_WASTECH, Mixer_Waste);  //#bug 323
         for (Dispenser_i = 0;Dispenser_i < MAX_DYE_DISPENSERS;Dispenser_i++)
         {
             DispenserDistanceToSpoolReady[Dispenser_i] = true;
             if (DispenserUsedInJob[Dispenser_i]==true)//unconfigured dispenser
             {
                 DispenserDistanceToSpoolReady[Dispenser_i] = false;
                 if (Special_Dispensers == true)
                 {
                     IDS_Dispenser_Close_Valve_And_Stop_Motor(Dispenser_i,NULL);
                 }
                 else
                 {
                     MotorStop(DispenserIdToMotorId[Dispenser_i],Hard_Hiz);
                 }
                 Control3WayValvesWithCallback ((Valves_t)Dispenser_i, MidTank_Dispenser, IDS_Valve_DistanceToSpoolValveReady); //direction: MidTank_Dispenser or Dispenser_Mixer
                 CurrentDispenserSpeed[Dispenser_i] = 0;
                 //IDS_Dispenser_Close_Valve_And_Stop_Motor(Dispenser_i,IDS_Valve_DistanceToSpoolValveReady);
             }
         }

         //cleaning
         if (FileBrushStop)
             FreeBrushStopFileData(FileBrushStop);
         FileBrushStop = NULL;
         JobEndSequence = true;
         lInterSegmentLength = (dryerbufferCentimeters*1000/dyeingspeed);
         lInterSegmentLength-=(lInterSegmentLength%100);   //round to a 100 multiplication
         InterSegmentStepsCount = 0;
         DispenserPreSegmentControlId = AddControlCallback("IDS DTS", IDSPreSegmentStateCallbackRunner, IDS_PRESEGMENT_TIME_STEP,TemplateDataReadCBFunction ,0, 0, 0 );
         if (DispenserPreSegmentControlId == 0xFF)
         {
             Report("Add control callback failed",__FILE__,__LINE__,(int)100,RpWarning,(int)0,0);
             return ERROR;
         }
         Report("Add control callback ",__FILE__,__LINE__,(int)100,RpWarning,(int)lInterSegmentLength,0);
         /*if (EnableCleaning == true)
         {
             InterSegmentStartSprayCleaner = 500;
             InterSegmentStartRocking = 1000;
             InterSegmentCenterRockers = 3000;
         }*/
         //InterSegmentStartWFCFDispensers = 0;


     return OK;
 }
 //********************************************************************************************************************
 uint32_t IDS_Valve_EndValveReady(uint32_t deviceID, uint32_t ReadValue)
 {
     //TimerMotors_t  HW_Motor_Id = DispenserIdToMotorId[deviceID];
     //REPORT_MSG(deviceID,"Dispenser End called");
     //MotorStop(HW_Motor_Id,Hard_Hiz);
     if (AutoHoming_Config >= AutoHoming_JobEnd_PowerOn_off)
     {
         Report("IDS_Valve_EndValveReady start homing",__FILE__,deviceID,(int)AutoHoming_Config,RpWarning,(int)AutoHoming_JobEnd_PowerOn_off,0);

         if (deviceID!=LUBRICANT_DISPENSER)
             IDS_HomeDispenser (deviceID, 1000 , NULL);
     }
     return OK;
 }
//********************************************************************************************************************
 uint32_t IDSEndState(void )
{
     int Dispenser_i;
     IDS_Active = false;
     Valve_Set(VALVE_MIXCHIP_WASTECH, Mixer_Waste);
     REPORT_MSG(0,"Dispenser End Start");
     if (BrushStopControlId != 0xFF)
     {
         RemoveControlCallback(BrushStopControlId,IDSBrushStopRestartCallback);
         BrushStopControlId = 0xFF;
     }
     if (FileBrushStop)
         FreeBrushStopFileData(FileBrushStop);
     FileBrushStop = NULL;
     FirstBrushStop = NULL;
     if (DispenserPrepareControlId != 0xFF)
     {
         RemoveControlCallback(DispenserPrepareControlId, IDS_Prepare_Callback );
         DispenserPrepareControlId = 0xFF;
     }
     if (DispenserPreSegmentControlId != 0xFF)
     {
         RemoveControlCallback(DispenserPreSegmentControlId, IDSPreSegmentStateCallbackRunner );
         DispenserPreSegmentControlId = 0xFF;
     }

     IDS_Dispenser_Store_Data ();

     for ( Dispenser_i = 0;Dispenser_i < MAX_SYSTEM_DISPENSERS;Dispenser_i++)
     {
         if (DispenserUsedInJob[Dispenser_i] == true)
         {
             if (Special_Dispensers == true)
             {
                 IDS_Dispenser_Close_Valve_And_Stop_Motor(Dispenser_i,NULL);
             }
             else
             {
                 MotorStop(DispenserIdToMotorId[Dispenser_i],Hard_Hiz);
             }
            Control3WayValvesWithCallback (Dispenser_i, MidTank_Dispenser, IDS_Valve_EndValveReady); //direction: MidTank_Dispenser or Dispenser_Mixer
            CurrentDispenserSpeed[Dispenser_i] = 0;

            //IDS_Dispenser_Close_Valve_And_Stop_Motor(Dispenser_i,IDS_Valve_EndValveReady);
         }
         else
         {
//             RemoveControlCallback(ControlIdtoInactiveDispenserId[Dispenser_i], IDS_Check_Pressure_Callback );
//             ControlIdtoInactiveDispenserId[Dispenser_i] = 0xFF;
             IDS_Valve_EndValveReady(Dispenser_i,false);
         }

     }
	IDS_StopLubrication();
	Init_CleaningStageCounter();//set the counter before relocating
	HeadCard_Actuators_Relocate();
	return OK;
}
uint32_t IDS_StartLubrication(void)
{
    IDS_Dispenser_Start_Motor_and_Open_Valve(LUBRICANT_DISPENSER,lubricant_speed,NULL);
    CurrentDispenserSpeed[LUBRICANT_DISPENSER] = lubricant_speed;
    REPORT_MSG (lubricant_speed, "IDS_StartLubrication");
    Lubricant_2Way_Valve (START);
    return OK;
}
uint32_t IDS_StopLubrication(void)
{
    Lubricant_2Way_Valve (STOP);
    IDS_Dispenser_Close_Valve_And_Stop_Motor(LUBRICANT_DISPENSER,IDS_Valve_EndValveReady);
    CurrentDispenserSpeed[LUBRICANT_DISPENSER] = 0;
    IDS_HomeDispenser (LUBRICANT_DISPENSER, 1000 , NULL);
    REPORT_MSG (lubricant_speed, "IDS_StopLubrication and home dispenser");
    return OK;
}