////// section begins /* use this afl when buy is different from cover and sell is different from short / RequestTimedRefresh( 1, onlyvisible = False ) ; _SECTION_BEGIN( "Algoji.com intraday.afl" ); intra = ParamToggle( "Activate Intraday Mode", "NO|YES" ); per10 = Param( "Trade Entry From(HHMM)", 920, 900, 2300, 1 ); per11 = Param( "Trade Entry Upto(HHMM)", 1445, 900, 2300, 1 ); per12 = Param( "Trade Exit(HHMM)", 1515, 900, 2300, 100 ); pop= ParamToggle( "Percentage or Points", "Points|Percentage"); slp = Param( "StopLoss", 0, 0, 1000, 0.1 ); tsl= Param("Trail Stop", 0, 0, 1000, 0.1); tgtp = Param( "Target", 0, 0, 1000, 0.1 ); Col = ParamColor( "Color of Modified Signals", colorYellow ); delay= ParamToggle("Trade Intrabar?", "YES|Candle Completion"); dlong= ParamToggle("Disable Long?", "NO|YES"); dshort= ParamToggle("Disable Short?", "NO|YES"); if(dlong){Buy=Sell=0;} if(dshort){Short=Cover=0;} dd= DaysSince1900(); d=prof= 0; if(delay) {Buy=Ref(Buy,-1); Sell=Ref(Sell,-1); Short= Ref(Short,-1); Cover= Ref(Cover,-1);} qt= Param("Trade Quantity", 0, 0, 1000000) ; exposure= Param("Exposure", 0, 0, 1000000) ; if(exposure>0) qt= round(exposure/ValueWhen(Day()!=Ref(Day(),-1), C)); maxl= Param("Qty using SL (MaxLoss)",0,0,100000 ); if(maxl>0 AND !pop) qt= round(maxl/slp); if(maxl>0 AND pop) { basicprice= LastValue(ValueWhen(Day()!=Ref(Day(),-1), C)); sl= slpbasicprice/100; qt= round(maxl/sl); } intraex = intra AND (TimeNum() > per12 * 100); intraen = !intra OR ( TimeNum() <= per11 * 100 AND TimeNum() >= per10 * 100 ); Buy1 = Buy; Sell1 = Sell; Short1 = Short; Cover1 = Cover; Buy=Sell=Short=Cover=0; bflag = sflag = sp=bp = 0; slarr = tgtarr = qtarr= Null; for ( i = 10; i < BarCount; i++ ) { if ( ( Cover1[i] OR intraex[i]OR( H[i] > slarr[i-1] AND (sl>0 OR tsl>0) ) OR ( L[i] < tgtarr[i-1] AND tgt > 0 ) ) AND sflag ) { Cover[i] = 1; CoverPrice[i]= C[i]; sflag = 0; d= dd[i]; prof= sp-C[i]; } if ( ( Sell1[i] OR intraex[i] OR( L[i] < slarr[i-1] AND (sl>0 OR tsl>0) ) OR ( H[i] > tgtarr[i-1] AND tgt > 0 ) ) AND bflag ) { Sell[i] = 1; SellPrice[i]= C[i]; bflag = 0; d= dd[i]; prof= C[i]- bp; } if ( Buy1[i] AND intraen[i] AND bflag == 0 ) { Buy[i] = 1; bflag = 1; bp= C[i]; sl=slp; tgt= tgtp; if(pop) {sl= slpbp/100; tgt= tgtpbp/100;} if ( slp ) slarr[i] = bp-sl; if ( tgtp ) tgtarr[i] = bp+tgt; } if ( bflag AND Buy[i]==0 ) { slarr[i] = slarr[i-1]; tgtarr[i] = tgtarr[i-1]; if(tsl>0 AND pop) slarr[i] = Max(slarr[i-1], H[i](1-tsl/100)); if(tsl>0 AND !pop) slarr[i] = Max(slarr[i-1], H[i]-tsl); } if ( Short1[i] AND intraen[i] AND sflag == 0 ) { Short[i] = 1; sflag = 1; Sp= C[i]; sl= slp; tgt= tgtp; if(pop) {sl= slpSp/100; tgt= tgtpSp/100;} if ( slp ) slarr[i] = sp + sl; if ( tgtp ) tgtarr[i] = sp - tgt; } if ( sflag AND Short[i] == 0 ) { slarr[i] = slarr[i-1]; tgtarr[i] = tgtarr[i-1]; if(tsl>0 AND pop) slarr[i] = Min(slarr[i-1], L[i](1+tsl/100)); if(tsl>0 AND !pop) slarr[i] = Min(slarr[i-1], L[i]+tsl); } } Plot( slarr, "SL", Col, styleThick ); Plot( tgtarr, "TGT", Col, styleThick ); PlotShapes( IIf( Buy, shapeUpArrow, shapeNone ), Col, 0, H, Offset = 15 ); PlotShapes( IIf( Short, shapeDownArrow, shapeNone ), Col, 0, L, Offset = 15 ); PlotShapes( IIf( Cover, shapeStar, shapeNone ), Col, 0, H, Offset = -25 ); PlotShapes( IIf( Sell, shapeStar, shapeNone ), Col, 0, L, Offset = -25 ); sig = IIf( BarsSince( Buy ) < BarsSince( Short ), 1, 0 ); messageboard = ParamToggle( "Message Board", "Show|Hide", 1 ); if ( messageboard == 1 ) { GfxSelectFont( "Tahoma", 13, 100 ); GfxSetBkMode( 1 ); GfxSetTextColor( colorWhite ); GfxSelectSolidBrush( colorDarkTeal ); // this is the box background color pxHeight = Status( "pxchartheight" ) ; xx = Status( "pxchartwidth" ); Left = 1100; width = 310; x = 5; x2 = 310; y = pxHeight; GfxSelectPen( colorGreen, 1 ); // broader color GfxRoundRect( x, y - 160, x2, y , 7, 7 ) ; GfxTextOut( ""+WriteIf(intra, "Intraday Mode Activated", "Intraday Mode Not Activated" ), 13, y-160 ); GfxTextOut( ( "Current Qty "+qt ), 13, y-140 ); GfxTextOut( ( "Last" + " Signal came " + ( BarsSince( Buy OR Short ) ) * Interval() / 60 + " mins ago" ), 13, y - 120 ) ; // The text format location GfxTextOut( ( "" + WriteIf( sig == 1, "BUY # " + ValueWhen(Buy,C) , "SHORT # " + ValueWhen(Short,C) ) ), 13, y - 100 ); GfxTextOut( "Stop Loss : " + WriteIf(slp==0, "Not Activated", ""+slarr), 13, y - 80 ); GfxTextOut( "Target : " + WriteIf(tgtp==0, "Not Activated", ""+tgtarr), 13, y - 60 ); GfxTextOut( ( "Current P/L : " + WriteVal( IIf( sig == 1, (C-ValueWhen(Buy,C))*qt, (ValueWhen(Short,C)-C)*qt ), 2.2 ) ), 13, y-40 ); // GfxTextOut( ( "jhjh " ), 13, y-20 ); } //section begins for auto trade instr= ParamList("Instrument Name","EQ|FUTIDX|FUTSTK|OPTIDX|OPTSTK|FUTCOM"); stag= ParamStr("Strategy Tag", "STG1"); qty= NumToStr(qt[BarCount-1], 1.0, False) ; bp= sp= NumToStr(Close[BarCount-1],1.2, False); if(dlong){Buy=Sell=0;} if(dshort){Short=Cover=0;} if(delay) {Buy=Ref(Buy,-1); Sell=Ref(Sell,-1); Short= Ref(Short,-1); Cover= Ref(Cover,-1);} global algoji; algoji = Name() + NumToStr( Interval() / 60, 1.0, False ) ; procedure aStaticVarSet( SName, Svalue ) { global algoji; StaticVarSet( Sname + algoji, Svalue ); } function aStaticVarGet( SName ) { global algoji; Var = StaticVarGet( Sname + algoji ); if ( IsNull( Var = StaticVarGet( Sname + algoji ) ) ) Var = 0; return Var; } sym = Name(); //_TRACE("t"+t); Checkdt=Nz(aStaticVarGet("lastdt")); dt = LastValue( DateTime() ); Checkdtss=Nz(aStaticVarGet("lastdtss")); dtss = LastValue( DateTime() ); Checkdtc=Nz(aStaticVarGet("lastdtc")); dtc = LastValue( DateTime() ); Checkdts=Nz(aStaticVarGet("lastdts")); dts = LastValue( DateTime() ); RTBuy = LastValue( Buy) AND Checkdt != dt; RTShort = LastValue( Short) AND Checkdtss != dtss; RTCover = LastValue( Cover) AND Checkdtc != dtc; RTSell = LastValue( Sell) AND Checkdts != dts; if ( RTCover ) { aStaticVarSet("lastdtc",dtc ); StaticVarSet("counter", Nz(StaticVarGet("counter"))+1 ); _TRACE( "#"+Nz(StaticVarGet("counter"))+",SX,"+sym+",,," +bp +","+qty+","+instr+",,"); Algoji_Signal(NumToStr(Nz(StaticVarGet("counter")),0,False), "SX",sym,"M","",bp,qty,instr,stag); } if ( RTSell ) { aStaticVarSet("lastdts",dts ); StaticVarSet("counter", Nz(StaticVarGet("counter"))+1 ); _TRACE( "#"+Nz(StaticVarGet("counter"))+",LX,"+sym+",,," +sp +","+qty+",,,"); Algoji_Signal(NumToStr(Nz(StaticVarGet("counter")),0,False), "LX",sym,"M","",sp,qty,instr,stag); } if ( RTBuy ) { aStaticVarSet("lastdt",dt ); StaticVarSet("counter", Nz(StaticVarGet("counter"))+1 ); _TRACE( "#"+Nz(StaticVarGet("counter"))+",LE,"+sym+",,," +bp +","+qty+","+instr+",,"); Algoji_Signal(NumToStr(Nz(StaticVarGet("counter")),0,False), "LE",sym,"M","",bp,qty,instr,stag); } if ( RTShort ) { aStaticVarSet("lastdtss",dtss ); StaticVarSet("counter", Nz(StaticVarGet("counter"))+1 ); sp= NumToStr(Close[BarCount-1],1.2, False); _TRACE( "#"+Nz(StaticVarGet("counter"))+",SE,"+sym+",,," +sp +","+qty+","+instr+",,"); Algoji_Signal(NumToStr(Nz(StaticVarGet("counter")),0,False), "SE",sym,"M","",bp,qty,instr,stag); } Button = ParamToggle( "Enable Button Trading", "YES|NO" ); expiry= ParamStr("Expiry",""); strike= ParamStr("Strike",""); type= ParamStr("Option Type", ""); sym = Name()+ "|"+expiry+ "|" +strike+ "|" +type; function GetSecondNum() { Time = Now( 4 ); Seconds = int( Time % 100 ); Minutes = int( Time / 100 % 100 ); Hours = int( Time / 10000 % 100 ); SecondNum = int( Hours * 60 * 60 + Minutes * 60 + Seconds ); return SecondNum; } function PopupWindowEx( popupID, bodytext, captiontext, timeout, left, top ) { displayText = bodytext + captiontext; if ( ( StaticVarGetText( "prevPopup" + popupID ) != displayText) OR ( StaticVarGet( "prevPopupTime" + popupID ) < GetSecondNum() ) ) { StaticVarSetText( "prevPopup" + popupID, displayText); StaticVarSet( "prevPopupTime" + popupID, GetSecondNum() + timeout ); PopupWindow( bodytext, Captiontext + popupID, timeout, Left, top ); } } x1= Status( "pxchartleft" )+10; y1= Status( "pxcharttop" )+20; if ( Button == 0 ) { click = GetCursorMouseButtons() == 9; Px = GetCursorXPosition( 1 ); Py = GetCursorYPosition( 1 ); x2 = x1 + 60; y2 = y1 + 60; GfxSelectSolidBrush( ColorRGB( 0, 102, 0 ) ); //buy GfxSelectFont( "Tahoma", 13, 100 ); GfxSetBkMode( 1 ); GfxSetTextColor( colorWhite ); GfxRoundRect( x1, y1, x2, y2 , 7, 7 ) ; GfxTextOut( "LE", x1 + 20, y1 + 20 ); sx1 = x2; sy1 = y1; sx2 = sx1 + 60; sy2 = sy1 + 60; GfxSelectSolidBrush( ColorRGB( 255, 204, 204 ) );//sell GfxRoundRect( sx1, sy1, sx2, sy2 , 7, 7 ) ; GfxSetTextColor( ColorRGB( 153, 0, 0 ) ); GfxTextOut( "SE", sx1 + 20, sy1 + 20 ); ex1 = x1; ey1 = y1+60; ex2 = ex1 + 60; ey2 = ey1 + 60; GfxSelectSolidBrush( ColorRGB( 255, 204, 204 ) );//sell GfxRoundRect( ex1, ey1, ex2, ey2 , 7, 7 ) ; GfxSetTextColor( ColorRGB( 153, 0, 0 ) ); GfxTextOut( "LX", ex1 + 20, ey1 + 20 ); GfxSelectSolidBrush( ColorRGB( 0, 102, 0 ) );//sell GfxRoundRect( ex2, ey1, ex2+60, ey2 , 7, 7 ) ; GfxSetTextColor( colorWhite ); GfxTextOut( "SX", ex2 + 20, ey1 + 20 ); if ( px > x1 AND pxy1 AND py < y2 AND Click ) { _TRACE( "# ," + NumToStr(Nz(StaticVarGet("counter")),0,False) + ", BUY triggered from button, " ); AlertIf( 1, "SOUND C:\Windows\Media\tada.wav", "Audio alert", 1, 2, 1 ); StaticVarSet("counter", Nz(StaticVarGet("counter"))+1 ); PopupWindowEx( "ID:1", "BUY", "Buy Triggered from Button "+Name(), 1, -1, -1 ); AlgoJi_Signal(NumToStr(Nz(StaticVarGet("counter")),0,False), "LE",sym,"M","",sp,qty,instr,stag); } //https://algoji.com/ if ( px > sx1 AND pxsy1 AND py < sy2 AND Click ) { _TRACE( "# ," + NumToStr( DateTime(), formatDateTime ) + ", SHORT triggered from button, " ); AlertIf( 2, "SOUND C:\Windows\Media\tada.wav", "Audio alert", 2, 2, 1 ); StaticVarSet("counter", Nz(StaticVarGet("counter"))+1 ); PopupWindowEx( "ID:3", "SHORT", "Short Triggered from Button "+Name(), 1, -1, -1 ); AlgoJi_Signal(NumToStr(Nz(StaticVarGet("counter")),0,False), "SE",sym,"M","",sp,qty,instr,stag); } //https://algoji.com/ if ( px > ex1 AND pxey1 AND py<ey2 AND Click ) { _TRACE( "# ," + NumToStr( DateTime(), formatDateTime ) + ", SELL triggered from button, " ); AlertIf( 3, "SOUND C:\Windows\Media\tada.wav", "Audio alert", 2, 2, 1 ); StaticVarSet("counter", Nz(StaticVarGet("counter"))+1 ); PopupWindowEx( "ID:3", "SELL", "Sell Triggered from Button "+Name(), 1, -1, -1 ); AlgoJi_Signal(NumToStr(Nz(StaticVarGet("counter")),0,False), "LX",sym,"M","",sp,qty,instr,stag); } //https://algoji.com/ if ( px > ex2 AND px<(ex2+60) AND py>ey1 AND py < ey2 AND Click ) { _TRACE( "# ," + NumToStr( DateTime(), formatDateTime ) + ", Cover triggered from button, " ); AlertIf( 4, "SOUND C:\Windows\Media\tada.wav", "Audio alert", 2, 2, 1 ); StaticVarSet("counter", Nz(StaticVarGet("counter"))+1 ); PopupWindowEx( "ID:3", "Cover", "Cover Triggered from Button "+Name(), 1, -1, -1 ); AlgoJi_Signal(NumToStr(Nz(StaticVarGet("counter")),0,False), "SX",sym,"M","",sp,qty,instr,stag); } }
It is not a Python, it is AFL. It will be much easier if you post it as a code.
If you run it in AFL Formula Editor it will help you to debug it - simply click "Apply" and you will have all errors highlined.
I've found that you missed some "*" in your formula.
My problem is:
Livolo switches have their own Zigbee gate. I want to connect them from zigbee2mqtt with CC2531 USB dongle. In general it works, but when I turn on/off switch button (on physical device), the switch sends an incorrect ZCL package.
I'm an absolutely newbie in microcontrollers programming and in Zigbee architecture. So I hope someone can help me and answer these questions:
Where I can intercept that malformed package?
How I can fix that package to support the Zigbee standard?
I use Z-STACK-HOME 1.2.2a firmware and compile it as described there:
https://github.com/Koenkk/Z-Stack-firmware/blob/master/coordinator/Z-Stack_Home_1.2/COMPILE.md
// Malformed ZCL package
// header
0x7c, // [0111 1100]
// 01 - frame type = "Command is specific or local to a cluster"
// 1 - manufacturer spec = manufacturer code present
// 1 - direction = "from server to client"
// 1 - disable default response
// 100 - reserved
0xd2, 0x15, // manufacturer
0xd8, // sequence
0x00, // read attrs command
// endpoint adress
0x12, 0x0f, 0x05, 0x18, 0x00, 0x4b, 0x12, 0x00,
0x22, 0x00, // ????? need more data from another switches
0x03 // 0x00|0x01|0x02|0x03 - switch state
upd:
I think, what I can intercept message in AF.c file in afIncomingData function and fix in afBuildMSGIncoming.
So now I hope, someone can help me with the right message format. Which can be processed standard ZCL parcer.
void afIncomingData( aps_FrameFormat_t *aff, zAddrType_t *SrcAddress, uint16 SrcPanId,
NLDE_Signal_t *sig, uint8 nwkSeqNum, uint8 SecurityUse,
uint32 timestamp, uint8 radius )
{
endPointDesc_t *epDesc = NULL;
epList_t *pList = epList;
#if !defined ( APS_NO_GROUPS )
uint8 grpEp = APS_GROUPS_EP_NOT_FOUND;
#endif
if ( ((aff->FrmCtrl & APS_DELIVERYMODE_MASK) == APS_FC_DM_GROUP) )
{
#if !defined ( APS_NO_GROUPS )
// Find the first endpoint for this group
grpEp = aps_FindGroupForEndpoint( aff->GroupID, APS_GROUPS_FIND_FIRST );
if ( grpEp == APS_GROUPS_EP_NOT_FOUND ) {
// No endpoint found, default to endpoint 1.
// In the original source code there is a return here.
// This prevent the messags from being forwarded.
// For our use-case we want to capture all messages.
// Even if the coordinator is not in the group.
epDesc = afFindEndPointDesc( 1 );
}
else {
epDesc = afFindEndPointDesc( grpEp );
}
if ( epDesc == NULL )
return; // Endpoint descriptor not found
pList = afFindEndPointDescList( epDesc->endPoint );
#else
return; // Not supported
#endif
}
else if ( aff->DstEndPoint == AF_BROADCAST_ENDPOINT )
{
// Set the list
if ( pList != NULL )
{
epDesc = pList->epDesc;
}
}
else if ( aff->DstEndPoint == 10 || aff->DstEndPoint == 11 ) {
if ( (epDesc = afFindEndPointDesc( 1 )) )
{
pList = afFindEndPointDescList( epDesc->endPoint );
}
}
else if ( (epDesc = afFindEndPointDesc( aff->DstEndPoint )) )
{
pList = afFindEndPointDescList( epDesc->endPoint );
}
while ( epDesc )
{
uint16 epProfileID = 0xFFFE; // Invalid Profile ID
if ( pList->pfnDescCB )
{
uint16 *pID = (uint16 *)(pList->pfnDescCB(
AF_DESCRIPTOR_PROFILE_ID, epDesc->endPoint ));
if ( pID )
{
epProfileID = *pID;
osal_mem_free( pID );
}
}
else if ( epDesc->simpleDesc )
{
epProfileID = epDesc->simpleDesc->AppProfId;
}
// First part of verification is to make sure that:
// the local Endpoint ProfileID matches the received ProfileID OR
// the message is specifically send to ZDO (this excludes the broadcast endpoint) OR
// if the Wildcard ProfileID is received the message should not be sent to ZDO endpoint
if ( (aff->ProfileID == epProfileID) ||
((epDesc->endPoint == ZDO_EP) && (aff->ProfileID == ZDO_PROFILE_ID)) ||
((epDesc->endPoint != ZDO_EP) && ( aff->ProfileID == ZDO_WILDCARD_PROFILE_ID )) )
{
// Save original endpoint
uint8 endpoint = aff->DstEndPoint;
// overwrite with descriptor's endpoint
aff->DstEndPoint = epDesc->endPoint;
afBuildMSGIncoming( aff, epDesc, SrcAddress, SrcPanId, sig,
nwkSeqNum, SecurityUse, timestamp, radius );
// Restore with original endpoint
aff->DstEndPoint = endpoint;
}
if ( ((aff->FrmCtrl & APS_DELIVERYMODE_MASK) == APS_FC_DM_GROUP) )
{
#if !defined ( APS_NO_GROUPS )
// Find the next endpoint for this group
grpEp = aps_FindGroupForEndpoint( aff->GroupID, grpEp );
if ( grpEp == APS_GROUPS_EP_NOT_FOUND )
return; // No endpoint found
epDesc = afFindEndPointDesc( grpEp );
if ( epDesc == NULL )
return; // Endpoint descriptor not found
pList = afFindEndPointDescList( epDesc->endPoint );
#else
return;
#endif
}
else if ( aff->DstEndPoint == AF_BROADCAST_ENDPOINT )
{
pList = pList->nextDesc;
if ( pList )
epDesc = pList->epDesc;
else
epDesc = NULL;
}
else
epDesc = NULL;
}
}
static void afBuildMSGIncoming( aps_FrameFormat_t *aff, endPointDesc_t *epDesc,
zAddrType_t *SrcAddress, uint16 SrcPanId, NLDE_Signal_t *sig,
uint8 nwkSeqNum, uint8 SecurityUse, uint32 timestamp, uint8 radius )
{
afIncomingMSGPacket_t *MSGpkt;
const uint8 len = sizeof( afIncomingMSGPacket_t ) + aff->asduLength;
uint8 *asdu = aff->asdu;
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_allocate( len );
if ( MSGpkt == NULL )
{
return;
}
MSGpkt->hdr.event = AF_INCOMING_MSG_CMD;
MSGpkt->groupId = aff->GroupID;
MSGpkt->clusterId = aff->ClusterID;
afCopyAddress( &MSGpkt->srcAddr, SrcAddress );
MSGpkt->srcAddr.endPoint = aff->SrcEndPoint;
MSGpkt->endPoint = epDesc->endPoint;
MSGpkt->wasBroadcast = aff->wasBroadcast;
MSGpkt->LinkQuality = sig->LinkQuality;
MSGpkt->correlation = sig->correlation;
MSGpkt->rssi = sig->rssi;
MSGpkt->SecurityUse = SecurityUse;
MSGpkt->timestamp = timestamp;
MSGpkt->nwkSeqNum = nwkSeqNum;
MSGpkt->macSrcAddr = aff->macSrcAddr;
MSGpkt->macDestAddr = aff->macDestAddr;
MSGpkt->srcAddr.panId = SrcPanId;
MSGpkt->cmd.TransSeqNumber = 0;
MSGpkt->cmd.DataLength = aff->asduLength;
MSGpkt->radius = radius;
if ( MSGpkt->cmd.DataLength )
{
MSGpkt->cmd.Data = (uint8 *)(MSGpkt + 1);
osal_memcpy( MSGpkt->cmd.Data, asdu, MSGpkt->cmd.DataLength );
}
else
{
MSGpkt->cmd.Data = NULL;
}
#if defined ( MT_AF_CB_FUNC )
// If ZDO or SAPI have registered for this endpoint, dont intercept it here
if (AFCB_CHECK(CB_ID_AF_DATA_IND, *(epDesc->task_id)))
{
MT_AfIncomingMsg( (void *)MSGpkt );
// Release the memory.
osal_msg_deallocate( (void *)MSGpkt );
}
else
#endif
{
// Send message through task message.
osal_msg_send( *(epDesc->task_id), (uint8 *)MSGpkt );
}
}
I don't think you're decoding the frame control byte correctly. Looking at some code I've written, I interpret it as follows:
0x7c, // [0111 1100]
// 011 - reserved
// 1 - disable default response
// 1 - direction = "from server to client"
// 1 - manufacturer spec = manufacturer code present
// 00 - frame type = "Command acts across entire profile"
This is based on an old ZCL spec (around 2008?) and perhaps the reserved bits have taken on some meaning in a later version of the spec.
I believe the manufacturer specific bit indicates that this is not a standard Zigbee command (Read Attributes). If it was Read Attributes, I think it should have an even number of bytes following it (16-bit attribute IDs).
What were the source and destination endpoints, profile ID and cluster ID for that frame?
Update:
It looks like you could modify afIncomingData() to look at the fields of aff to identify this exact message type (frame control, endpoints, cluster, profile), and then hand it off to your own function for processing (and responding if necessary).
But I'd also take a look at documentation for MT_AF_CB_FUNC and MT_AfIncomingMsg() to see if that's the "official" way to identify frames that you want to handle in your own code.
In AF.c file in functions afIncomingData and afBuildMSGIncoming added bloks marked by
#if defined ( LIVOLO_SUPPORT )
#endif
In afIncomingData I add:
#if defined ( LIVOLO_SUPPORT )
else if ( aff->DstEndPoint == 0x08 )
{
if ( (epDesc = afFindEndPointDesc( 1 )) )
{
pList = afFindEndPointDescList( epDesc->endPoint );
}
}
#endif
It's prewent filtering message sended to unknown destEndpoint
And in afBuildMSGIncoming function:
#if defined ( LIVOLO_SUPPORT )
uint8 fixedPackage[] = {
0x18, 0xd8, 0x01, // header
0x00, 0x00, // attrId
0x00, // success
0x10, // boolean
0x00
};
if (aff->SrcEndPoint == 0x06 && aff->DstEndPoint == 0x01
&& aff->ClusterID == 0x0001 && aff->ProfileID == 0x0104) {
const uint8 mlfrmdHdr[] = { 0x7c, 0xd2, 0x15, 0xd8, 0x00 };
if (osal_memcmp(asdu, mlfrmdHdr, 5) == TRUE) {
fixedPackage[7] = asdu[aff->asduLength - 1];
MSGpkt->cmd.DataLength = 8; // sizeof(fixedPackage)
MSGpkt->clusterId = 0x06; // genOnOff
asdu = fixedPackage;
}
}
#endif
It change unsupported package to readAttrResp package.
I found an interesting MT4 indicator which shows remaining time of the next bar in the chart. But that works for the particular pairs which I would choose.
But I want that program to work/applied on all opened pairs by just applying to any of the opened one pair.
Please check the code below::::
//--- input parameters
input string LabelFont = "Arial";
input int LabelSize = 15;
input color LabelColor = clrRed;
input int LabelDistance = 15;
const string LabelName = "TimeToNextCandle";
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//---
EventSetTimer(1);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
EventKillTimer();
ObjectDelete(0, LabelName);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
//---
CalcTime();
//--- return value of prev_calculated for next call
return(rates_total);
}
//+------------------------------------------------------------------+
//| Timer function |
//+------------------------------------------------------------------+
void OnTimer()
{
//---
CalcTime();
}
//+------------------------------------------------------------------+
void CalcTime(void)
{
// checking is there output label. create it if necessary
if (ObjectFind(LabelName) == -1)
{
ObjectCreate(0, LabelName, OBJ_LABEL, 0, 0, 0);
ObjectSetString(0, LabelName, OBJPROP_FONT, LabelFont);
ObjectSetInteger(0, LabelName, OBJPROP_FONTSIZE, LabelDistance);
ObjectSetInteger(0, LabelName, OBJPROP_COLOR, LabelColor);
ObjectSetInteger(0, LabelName, OBJPROP_ANCHOR, ANCHOR_RIGHT_LOWER);
ObjectSetInteger(0, LabelName, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
ObjectSetInteger(0, LabelName, OBJPROP_XDISTANCE, LabelDistance);
ObjectSetInteger(0, LabelName, OBJPROP_YDISTANCE, LabelDistance);
}
// calculating remaining time to next candle
datetime TimeTo = PeriodSeconds() - (TimeCurrent() - Time[0]);
// assembling the output string depending on current period on the chart
string Out = StringFormat("%.2d", TimeSeconds(TimeTo));
if (TimeTo >= 3600)
{
Out = StringFormat("%.2d:%s", TimeMinute(TimeTo), Out);
if (TimeTo >= 86400)
Out = StringFormat("%d day(s) %.2d:%s", int(TimeTo / 86400), TimeHour(TimeTo), Out);
else
Out = StringFormat("%d:%s", TimeHour(TimeTo), Out);
}
else
Out = StringFormat("%d:%s", TimeMinute(TimeTo), Out);
ObjectSetString(0, LabelName, OBJPROP_TEXT, StringFormat("%s (%.0f%s)", Out, 100.0 / PeriodSeconds() * TimeTo, "%"));
}
//+------------------------------------------------------------------+
Let's split the task into two parts:Part I.:How to measure the time.
Metatrader Terminal architecture has moved the code-execution units into three principal types:
- {0|1}-unique ExpertAdvisor-type of MQL4-code, per MT4.graph
- {0|1|..|n} CustomIndicatero-type of MQL4-code, per MT4.graph
- {0|1}-unique Script-type of MQL4-code, per MT4.graph
This may sound interesting to use more and more CustomIndicators, but there is a catch.
Catch XXII.
All CustomIndicators and all ExpertAdvisors are responsively executed upon a Market Event message arrival. That means, whenever a Market Event arrives, all ExpertAdvisors and all CustomIndicators are pushed to execute their respective processing ( OnTick(){...} function is called in ExpertAdvisor(s), OnCalculate(){...} function is called in CustomIndicator ). That still sounds reasonable. The Catch XXII. is hidden in the fact, that all CustomIndicators share a SINGLE THREAD. Yes, ALL !
This translated into plain english means, avoid everything that could block from CustomIndicators and put it elsewhere, but the CustomIndicator MQL4-code.
Using New-MQL4.56789 EventSetTimer()-facilities does not improve the already obvious problem, just the very opposite, so let's forget to use OnTimer().
So, how to safely design such feature?
The possible way is to use a low-profile service design, deployed in a helper-( non-trading )-MT4.graph for such utility service and best use a Script ( as it is absolutely under one's control, asynchronous to any Event-triggered EA / CI code-execution and works safe and sound even during times, when the Market is closed or the connection to the MetaTrader Server is down ).
This "central"-clock ( timing facility ) provides a unique, reference-time and stores it so as to be available to any "consumer" in other MT4.graphs.
// MQL4-Script
double assembleGV( int aTimeNOW,
int aPERIOD
){
#define BrokerServerGMToffset 0 // Yes, Real World isn't perfect :o)
int aTimeToEoB = PeriodSeconds( aPERIOD ) - ( aTimeNOW - ( iTime( _Symbol, aPERIOD, 0 ) - BrokerServerGMToffset ) );
return( aTimeToEoB + aTimeToEoB / PeriodSeconds( aPERIOD ) ); // INT + FRAC ( non-%, but having the value of the remainder )
}
while True{
Sleep( 250 ); int TimeNow = TimeGMT();
// -------------------------------------- COOL DOWN CPU-CORE(s)
GlobalVariableSet( "msMOD_TimeSERVICE_4_PERIOD=60", assembleGV( TimeNow, PERIOD_M1 ) ); // for PERIOD_M1
GlobalVariableSet( "msMOD_TimeSERVICE_4_PERIOD=300", assembleGV( TimeNow, PERIOD_M5 ) ); // for PERIOD_M5
GlobalVariableSet( "msMOD_TimeSERVICE_4_PERIOD=900", assembleGV( TimeNow, PERIOD_M15 ) ); // for PERIOD_M15
GlobalVariableSet( "msMOD_TimeSERVICE_4_PERIOD=1800", assembleGV( TimeNow, PERIOD_M30 ) ); // for PERIOD_M30
GlobalVariableSet( "msMOD_TimeSERVICE_4_PERIOD=3600", assembleGV( TimeNow, PERIOD_H1 ) ); // for PERIOD_H1
GlobalVariableSet( "msMOD_TimeSERVICE_4_PERIOD=14400", assembleGV( TimeNow, PERIOD_H4 ) ); // for PERIOD_H4
GlobalVariableSet( "msMOD_TimeSERVICE_4_PERIOD=86400", assembleGV( TimeNow, PERIOD_D1 ) ); // for PERIOD_D1
GlobalVariableSet( "msMOD_TimeSERVICE_4_PERIOD=604800", assembleGV( TimeNow, PERIOD_W1 ) ); // for PERIOD_W1
GlobalVariableSet( "msMOD_TimeSERVICE_4_PERIOD=2592000", assembleGV( TimeNow, PERIOD_MN1 ) ); // for PERIOD_MN1
GlobalVariableSet( "msMOD_TimeSERVICE_4_PERIOD=120", assembleGV( TimeNow, PERIOD_M2 ) ); // for PERIOD_M2
GlobalVariableSet( "msMOD_TimeSERVICE_4_PERIOD=180", assembleGV( TimeNow, PERIOD_M3 ) ); // for PERIOD_M3
GlobalVariableSet( "msMOD_TimeSERVICE_4_PERIOD=240", assembleGV( TimeNow, PERIOD_M4 ) ); // for PERIOD_M4
GlobalVariableSet( "msMOD_TimeSERVICE_4_PERIOD=360", assembleGV( TimeNow, PERIOD_M6 ) ); // for PERIOD_M6
GlobalVariableSet( "msMOD_TimeSERVICE_4_PERIOD=600", assembleGV( TimeNow, PERIOD_M10 ) ); // for PERIOD_M10
GlobalVariableSet( "msMOD_TimeSERVICE_4_PERIOD=720", assembleGV( TimeNow, PERIOD_M12 ) ); // for PERIOD_M12
GlobalVariableSet( "msMOD_TimeSERVICE_4_PERIOD=1200", assembleGV( TimeNow, PERIOD_M20 ) ); // for PERIOD_M20
GlobalVariableSet( "msMOD_TimeSERVICE_4_PERIOD=7200", assembleGV( TimeNow, PERIOD_H2 ) ); // for PERIOD_H2
GlobalVariableSet( "msMOD_TimeSERVICE_4_PERIOD=10800", assembleGV( TimeNow, PERIOD_H3 ) ); // for PERIOD_H3
GlobalVariableSet( "msMOD_TimeSERVICE_4_PERIOD=21600", assembleGV( TimeNow, PERIOD_H6 ) ); // for PERIOD_H6
GlobalVariableSet( "msMOD_TimeSERVICE_4_PERIOD=28800", assembleGV( TimeNow, PERIOD_H8 ) ); // for PERIOD_H8
GlobalVariableSet( "msMOD_TimeSERVICE_4_PERIOD=43200", assembleGV( TimeNow, PERIOD_H12 ) ); // for PERIOD_H12
}
Part II.:How to display the time in all MT4.graphsin a most lightweight form?
Given the central service takes care of respective ENUM_TIMEFRAMES listed ( being "visited" or not by other MT4.graphs ) and posting the pre-fabbed results of such "time-service" calculus, any potential "consumer"may just asynchronously check and react to the "time-service" posted values,in places, where feasible and safe in it's respective flow of code-execution, kind users are under full control to declare, where such lightweight GUI-call may appear and where all updates may benefit from ( a potentially deferred ) enforced GUI-repaint:
/* MQL4-ExpertAdvisor
or
other Script
or even
a lightweight add-on to a CustomIndicator */
#define LabelNAME "msMOD_StackOverflowDEMO"
bool aDeferred_GUI_REPAINT = False;
.
..
RepaintTIME();
..
.
if ( aDeferred_GUI_REPAINT ){
aDeferred_GUI_REPAINT = False;
WindowRedraw();
}
void RepaintTIME(){
static string aGlobalVariableNAME = "msMODmsMOD_TimeSERVICE_4_PERIOD=" + (string)PeriodSeconds();
ObjectSetString( 0,
LabelNAME, // external responsibility to set/create
OBJPROP_TEXT,
StringFormat( "%d (%.0f %%)",
int( GlobalVariableGet( aGlobalVariableNAME ) ), // SECONDS TILL EoB
( GlobalVariableGet( aGlobalVariableNAME ) % 1 ) * 100.0 ) // PER CENTO [%] EXPRESSED REMAINDER
);
aDeferred_GUI_REPAINT = True;
}
I'm trying to blend two images with alpha using Imlib library (this code is changing wallpaper).
So far I got this:
#include <stdio.h>
#include <X11/Xlib.h>
#include <Imlib2.h>
int main( int argc, char **argv )
{
Display *display;
Pixmap pixmap;
Window root;
Screen *screen;
int width,
height,
w1,
w2,
h1,
h2;
Imlib_Image img,
img2;
// loading images
img = imlib_load_image( "/usr/share/wallpapers/wallpaper1.jpg" );
img2 = imlib_load_image( "/usr/share/wallpapers/wallpaper2.jpg" );
if( !img || !img2 ) {
printf( "Unable to load image.\n" );
return 1;
}
// open display, get screen and root window
display = XOpenDisplay(NULL);
screen = DefaultScreenOfDisplay( display );
root = DefaultRootWindow( display );
width = screen->width;
height = screen->height;
// create pixmap
pixmap = XCreatePixmap( display, root, width, height, DefaultDepthOfScreen(screen) );
// #S == BLENDING 1 =========================
imlib_context_set_image( img );
w1 = imlib_image_get_width();
h1 = imlib_image_get_height();
imlib_context_set_image( img2 );
w2 = imlib_image_get_width();
h2 = imlib_image_get_height();
imlib_context_set_image( img );
imlib_blend_image_onto_image( img2, 0, 0, 0, width, height, 0, 0, width, height );
//imlib_blend_image_onto_image( img2, 0, 0, 0, w2, h2, 0, 0, w1, h1 );
// #E == BLENDING 1 =========================
// setting context
imlib_context_set_image( img );
imlib_context_set_display( display );
imlib_context_set_visual( DefaultVisualOfScreen(screen) );
imlib_context_set_colormap( DefaultColormapOfScreen(screen) );
imlib_context_set_drawable( pixmap );
// render image into pixmap
imlib_render_image_on_drawable_at_size( 0, 0, width, height );
// set pixmap as background
XSetWindowBackgroundPixmap( display, root, pixmap );
// clear window
XClearWindow( display, root );
XFlush( display );
// free...
XFreePixmap( display, pixmap );
imlib_context_set_image( img );
imlib_free_image();
imlib_context_set_image( img2 );
imlib_free_image();
// close display
XCloseDisplay( display );
return 0;
}
It's changing wallpaper, but I want to blend this two images with custom alpha, I mean, something like this: How to blend two image
So, how to set image transparency for blending operation in Imlib?
Compile with: gcc main.c -lX11 -lImlib2
Ok, I got it!
First I need to set alpha for image, create second imlib object and fill with rectangle in custom color with alpha. Then just copy alpha from one image to second... That's just... ehh...
imlib_context_set_image( img );
w1 = imlib_image_get_width();
h1 = imlib_image_get_height();
img3 = imlib_create_image( w1, h1 );
imlib_context_set_image( img3 );
imlib_image_set_has_alpha( 1 );
imlib_context_set_color( 0, 0, 0, 150 );
imlib_image_fill_rectangle( 0, 0, w1, h1 );
imlib_context_set_image( img );
imlib_image_set_has_alpha( 1 );
imlib_image_copy_alpha_to_image( img3, 0, 0 );
imlib_context_set_image( img2 );
imlib_context_set_operation( IMLIB_OP_COPY );
imlib_blend_image_onto_image( img, 1, 0, 0, w1, h1, 0, 0, w1, h1 );
Here img and img2 variables are pictures. It gives image blended onto image with given in imlib_context_set_color alpha.