feat: add mask tool
This commit is contained in:
+90
-32
@@ -15,7 +15,7 @@ using namespace std::chrono_literals;
|
||||
std::mutex mutex;
|
||||
std::condition_variable changed;
|
||||
std::deque<ZCANDataObj> incoming;
|
||||
std::atomic<int> opens{0},closes{0},resets{0},mergeCalls{0},txCalls{0},rxCalls{0};
|
||||
std::atomic<int> opens{0},closes{0},initializations{0},starts{0},resets{0},terminationCalls{0},mergeCalls{0},txCalls{0},rxCalls{0};
|
||||
std::atomic<bool> unavailable{false},initFailure{false},receiveFailure{false},txFailure{false};
|
||||
ZCAN_20_MSG sentCan{};ZCAN_FD_MSG sentFd{};
|
||||
std::vector<ZCAN_20_MSG> sentCanFrames;
|
||||
@@ -30,7 +30,7 @@ template<class F> void waitFor(F f) {
|
||||
}
|
||||
template<class F> void rejects(F f){bool failed=false;try{f();}catch(std::exception const&){failed=true;}check(failed,"Expected rejection");}
|
||||
void reset() {
|
||||
opens=closes=resets=mergeCalls=txCalls=rxCalls=0;
|
||||
opens=closes=initializations=starts=resets=terminationCalls=mergeCalls=txCalls=rxCalls=0;
|
||||
unavailable=initFailure=receiveFailure=txFailure=false;
|
||||
sentCanFrames.clear();failedTxChannel=-1;
|
||||
blockTx=false;txBlocked=false;
|
||||
@@ -49,16 +49,19 @@ extern "C" {
|
||||
U32 VCI_OpenDevice(U32 type,U32,U32){check(type==33,"Device type changed");++opens;return unavailable?0:1;}
|
||||
U32 VCI_CloseDevice(U32,U32){++closes;return 1;}
|
||||
U32 VCI_InitCAN(U32,U32,U32 port,ZCAN_INIT* init){
|
||||
check(port==0,"CH1 initialized");
|
||||
check(port<2,"Unexpected CAN channel initialized");++initializations;
|
||||
check(init->clk==60000000&&init->mode==0&&init->aset.brp==5&&init->dset.brp==1,"Reference timing not retained");
|
||||
return initFailure?0:1;
|
||||
}
|
||||
U32 VCI_SetReference(U32,U32,U32 port,U32 ref,void* data){
|
||||
check(port==0,"CH1 configured");
|
||||
if(ref==ZCAN_CMD_SET_CHNL_RECV_MERGE){check(port==0&&*static_cast<U32*>(data)==1,"Invalid merged receive configuration");++mergeCalls;}return 1;
|
||||
check(port<2,"Unexpected CAN channel configured");
|
||||
if(ref==CMD_CAN_TRES){check(*static_cast<U32*>(data)==1,"Invalid termination configuration");++terminationCalls;}
|
||||
else if(ref==ZCAN_CMD_SET_CHNL_RECV_MERGE){check(port==0&&*static_cast<U32*>(data)==1,"Invalid merged receive configuration");++mergeCalls;}
|
||||
else check(false,"Unexpected CAN reference configuration");
|
||||
return 1;
|
||||
}
|
||||
U32 VCI_StartCAN(U32,U32,U32 port){check(port==0,"CH1 started");return 1;}
|
||||
U32 VCI_ResetCAN(U32,U32,U32 port){check(port==0,"CH1 reset");++resets;return 1;}
|
||||
U32 VCI_StartCAN(U32,U32,U32 port){check(port<2,"Unexpected CAN channel started");++starts;return 1;}
|
||||
U32 VCI_ResetCAN(U32,U32,U32 port){check(port<2,"Unexpected CAN channel reset");++resets;return 1;}
|
||||
U32 VCI_ReceiveData(U32,U32,U32 port,ZCANDataObj* out,U32 capacity,U32){
|
||||
check(port==0,"Multiple merged receive readers");++rxCalls;
|
||||
if(receiveFailure)return std::numeric_limits<U32>::max();
|
||||
@@ -67,7 +70,7 @@ U32 VCI_ReceiveData(U32,U32,U32 port,ZCANDataObj* out,U32 capacity,U32){
|
||||
U32 count=0;while(!incoming.empty()&&count<capacity){out[count++]=incoming.front();incoming.pop_front();}return count;
|
||||
}
|
||||
U32 VCI_Transmit(U32,U32,U32 port,ZCAN_20_MSG* message,U32 count){
|
||||
check(port==0&&count==1&&port==message->hdr.chn,"Invalid CAN send call");++txCalls;sentCan=*message;
|
||||
check(port==1&&count==1&&port==message->hdr.chn,"CAN send did not use CH1");++txCalls;sentCan=*message;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(txMutex);sentCanFrames.push_back(*message);
|
||||
if(blockTx) {
|
||||
@@ -78,14 +81,14 @@ U32 VCI_Transmit(U32,U32,U32 port,ZCAN_20_MSG* message,U32 count){
|
||||
return txFailure||int(port)==failedTxChannel?0:1;
|
||||
}
|
||||
U32 VCI_TransmitFD(U32,U32,U32 port,ZCAN_FD_MSG* message,U32 count){
|
||||
check(port==0&&count==1&&port==message->hdr.chn,"Invalid CANFD send call");++txCalls;sentFd=*message;return txFailure?0:1;
|
||||
check(port==1&&count==1&&port==message->hdr.chn,"CANFD send did not use CH1");++txCalls;sentFd=*message;return txFailure?0:1;
|
||||
}
|
||||
}
|
||||
int main()try {
|
||||
using namespace cms;
|
||||
reset();{
|
||||
CanConfig c;c.enabled=false;CanBus bus(c);bus.start();
|
||||
check(bus.snapshot().status=="DISABLED"&&!bus.send({0,0x123,false,false,false,{1,2}}),"Disabled CAN used hardware");
|
||||
check(bus.snapshot().status=="DISABLED"&&!bus.send({1,0x123,false,false,false,{1,2}}),"Disabled CAN used hardware");
|
||||
}check(opens==0,"Disabled CAN opened device");
|
||||
reset();unavailable=true;{
|
||||
CanBus bus({});bus.start();check(bus.snapshot().status=="UNAVAILABLE","Missing adapter not reported");
|
||||
@@ -95,7 +98,8 @@ int main()try {
|
||||
}check(closes==1&&resets==0,"Partial initialization leaked device");
|
||||
reset();{
|
||||
CanConfig c;c.timeout_ms=150;CanBus bus(c);bus.start();
|
||||
check(mergeCalls==1,"Merged receive configured more than once");
|
||||
check(initializations==2&&starts==2&&terminationCalls==2&&mergeCalls==1,
|
||||
"CH0/CH1 initialization or merged receive configuration is wrong");
|
||||
CanBus duplicate(c);rejects([&]{duplicate.start();});
|
||||
inject(0,false,0x391,{1,37,128,255},123);
|
||||
waitFor([&]{return bus.snapshot().received==1;});
|
||||
@@ -113,41 +117,42 @@ int main()try {
|
||||
s=bus.snapshot();check(s.channels[0].received_monotonic_ns==timestamp&&s.receive_errors==1,"Invalid input refreshed signal");
|
||||
waitFor([&]{return !bus.snapshot().channels[0].valid;});
|
||||
inject(0,false,0x391,{0,1,2,3});waitFor([&]{return bus.snapshot().channels[0].valid;});
|
||||
check(bus.send({0,0x321,false,false,false,{1,2,3}}),"CAN send failed");
|
||||
check(bus.send({1,0x321,false,false,false,{1,2,3}}),"CAN send failed");
|
||||
check(sentCan.hdr.len==3&&!sentCan.hdr.inf.sef&&sentCan.dat[2]==3,"CAN encoding incorrect");
|
||||
CanFrame fd{0,0x12345,true,true,true,std::vector<std::uint8_t>(64,0xa5)};
|
||||
CanFrame fd{1,0x12345,true,true,true,std::vector<std::uint8_t>(64,0xa5)};
|
||||
check(bus.send(fd)&&sentFd.hdr.len==64&&sentFd.hdr.inf.brs&&sentFd.hdr.inf.sef&&sentFd.dat[63]==0xa5,"CANFD encoding incorrect");
|
||||
fd.data.resize(9);rejects([&]{bus.send(fd);});
|
||||
rejects([&]{bus.send({1,0x391,false,false,false,{}});});
|
||||
rejects([&]{bus.send({0,0x800,false,false,false,{}});});
|
||||
rejects([&]{bus.send({0,0x391,false,false,true,{}});});
|
||||
txFailure=true;check(!bus.send({}),"SDK send failure ignored");
|
||||
rejects([&]{bus.send({0,0x391,false,false,false,{}});});
|
||||
rejects([&]{bus.send({1,0x800,false,false,false,{}});});
|
||||
rejects([&]{bus.send({1,0x391,false,false,true,{}});});
|
||||
txFailure=true;check(!bus.send({1,0,false,false,false,{}}),"SDK send failure ignored");
|
||||
check(bus.snapshot().transmitted==2&&bus.snapshot().transmit_errors==1,"TX counters wrong");
|
||||
receiveFailure=true;waitFor([&]{return bus.snapshot().status=="ERROR";});
|
||||
check(!bus.snapshot().channels[0].valid&&!bus.send({}),"Receive error left bus live");
|
||||
bus.requestStop();bus.join();check(closes==1&&resets==1,"Shutdown did not release CH0");
|
||||
check(!bus.send({}),"Transmission after stop");
|
||||
check(!bus.snapshot().channels[0].valid&&!bus.send({1,0,false,false,false,{}}),"Receive error left bus live");
|
||||
bus.requestStop();bus.join();check(closes==1&&resets==2,"Shutdown did not release CH0 and CH1");
|
||||
check(!bus.send({1,0,false,false,false,{}}),"Transmission after stop");
|
||||
}check(closes==1,"Device closed twice");
|
||||
reset();{
|
||||
CanBus bus({});bus.start();
|
||||
for(auto level:{"clear","watch","warning","danger","unavailable","unknown","","clear"}) {
|
||||
auto sent=bus.sendRisk(level);
|
||||
check(sent==std::array<bool,2>{true,false},"Risk must use CH0 only");
|
||||
check(sent==std::array<bool,2>{false,true},"Risk must use CH1 only");
|
||||
}
|
||||
check(sentCanFrames.size()==8,"Risk must send exactly once per channel per result");
|
||||
for(std::size_t i=0;i<sentCanFrames.size();++i) {
|
||||
auto const& message=sentCanFrames[i];
|
||||
check(message.hdr.id==0x1f6&&message.hdr.len==8&&message.hdr.chn==0,
|
||||
check(message.hdr.id==0x1f6&&message.hdr.len==8&&message.hdr.chn==1,
|
||||
"Risk ID, DLC or channel incorrect");
|
||||
check(!message.hdr.inf.fmt&&!message.hdr.inf.sef&&!message.hdr.inf.brs&&!message.hdr.inf.sdf,
|
||||
"Risk must use a standard classic CAN data frame");
|
||||
check(message.dat[0]==(i==0||i==7?0x00:0x11),"Risk must fill both nibbles and reset both for clear");
|
||||
check(message.dat[0]==(i==0||i==1||i==4||i==7?0x00:0x11),
|
||||
"Risk must encode clear, watch and unavailable as zero in both nibbles");
|
||||
for(int byte=1;byte<8;++byte)check(message.dat[byte]==0,"Risk reserved bits not zero");
|
||||
}
|
||||
failedTxChannel=0;
|
||||
check(bus.sendRisk("danger")==std::array<bool,2>{false,false},"CH0 failure reported success");
|
||||
failedTxChannel=1;
|
||||
check(bus.sendRisk("danger")==std::array<bool,2>{false,false},"CH1 failure reported success");
|
||||
failedTxChannel=-1;
|
||||
check(bus.sendRisk("clear")==std::array<bool,2>{true,false},"CH0 did not recover after send failure");
|
||||
check(bus.sendRisk("clear")==std::array<bool,2>{false,true},"CH1 did not recover after send failure");
|
||||
check(bus.snapshot().transmitted==9&&bus.snapshot().transmit_errors==1,"Risk TX counters wrong");
|
||||
auto calls=txCalls.load();bus.requestStop();
|
||||
check(bus.sendRisk("danger")==std::array<bool,2>{false,false}&&txCalls==calls,"Risk sent after stop");
|
||||
@@ -172,11 +177,11 @@ int main()try {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(txMutex);
|
||||
check(sentCanFrames.size()==2,"Stale pending results accumulated");
|
||||
for(int i=0;i<2;++i)check(sentCanFrames[i].hdr.chn==0&&
|
||||
for(int i=0;i<2;++i)check(sentCanFrames[i].hdr.chn==1&&
|
||||
sentCanFrames[i].dat[0]==(i==0?0x11:0x00),"Async risk or latest result incorrect");
|
||||
}
|
||||
failedTxChannel=0;
|
||||
check(bus.queueRisk("unavailable"),"Ready queue rejected unavailable risk");
|
||||
failedTxChannel=1;
|
||||
check(bus.queueRisk("danger"),"Ready queue rejected changed risk");
|
||||
waitFor([&]{auto s=bus.snapshot();return s.transmitted==2&&s.transmit_errors==1;});
|
||||
failedTxChannel=-1;txBlocked=false;blockTx=true;
|
||||
check(bus.queueRisk("clear"),"Ready queue rejected clear risk");waitFor([]{return txBlocked.load();});
|
||||
@@ -184,8 +189,61 @@ int main()try {
|
||||
auto calls=txCalls.load();bus.requestStop();
|
||||
check(!bus.queueRisk("danger"),"Stopped bus accepted risk");
|
||||
{std::lock_guard<std::mutex> lock(txMutex);blockTx=false;}txWake.notify_all();
|
||||
bus.join();check(txCalls==calls,"Stop sent a pending result or second channel");
|
||||
check(closes==1&&resets==1,"Async shutdown leaked hardware");
|
||||
bus.join();check(txCalls==calls,"Stop sent a pending result");
|
||||
check(closes==1&&resets==2,"Async shutdown leaked hardware");
|
||||
}
|
||||
std::cout<<"PASS CAN/CANFD RX decoding, CH0-only reception, expiry/recovery, TX validation, binary risk 0x1F6 on CH0 only, errors and lifecycle cleanup\n";
|
||||
reset();{
|
||||
CanBus bus({});bus.start();
|
||||
check(bus.queueRisk("clear"),"Initial clear rejected");
|
||||
waitFor([&]{return bus.snapshot().transmitted==1;});
|
||||
for(int i=0;i<1000;++i)check(bus.queueRisk("clear"),"Unchanged risk rejected");
|
||||
std::this_thread::sleep_for(30ms);
|
||||
check(txCalls==1,"Repeated clear sent redundant frames");
|
||||
check(bus.queueRisk("watch"),"Watch risk rejected");
|
||||
std::this_thread::sleep_for(30ms);
|
||||
check(txCalls==1,"Watch resent clear's zero value");
|
||||
check(bus.queueRisk("warning"),"Risk change rejected");
|
||||
waitFor([&]{return bus.snapshot().transmitted==2;});
|
||||
for(auto level:{"warning","danger","unknown",""})
|
||||
check(bus.queueRisk(level),"Equivalent binary risk rejected");
|
||||
std::this_thread::sleep_for(30ms);
|
||||
check(txCalls==2,"Equivalent non-clear levels resent binary risk");
|
||||
check(bus.queueRisk("unavailable"),"Unavailable transition rejected");
|
||||
waitFor([&]{return bus.snapshot().transmitted==3;});
|
||||
check(sentCanFrames.back().dat[0]==0,"Unavailable risk must send zero");
|
||||
check(bus.queueRisk("clear")&&bus.queueRisk("watch"),"Zero-risk transition rejected");
|
||||
std::this_thread::sleep_for(30ms);
|
||||
check(bus.snapshot().transmitted==3,"Clear/watch resent unavailable's zero value");
|
||||
failedTxChannel=1;
|
||||
check(bus.queueRisk("danger"),"Failed-send fixture rejected");
|
||||
waitFor([&]{return bus.snapshot().transmit_errors==1;});
|
||||
for(int i=0;i<1000;++i)check(bus.queueRisk("warning"),"Unchanged failed state rejected");
|
||||
std::this_thread::sleep_for(30ms);
|
||||
check(txCalls==4,"Unchanged failed state retried automatically");
|
||||
failedTxChannel=-1;
|
||||
check(bus.queueRisk("clear"),"Recovery transition rejected");
|
||||
waitFor([&]{return bus.snapshot().transmitted==4;});
|
||||
check(bus.queueRisk("danger"),"Second danger transition rejected");
|
||||
waitFor([&]{return bus.snapshot().transmitted==5;});
|
||||
bus.join();
|
||||
check(sentCanFrames.size()==6,"Unexpected change-triggered frame count");
|
||||
for(std::size_t i=0;i<sentCanFrames.size();++i)
|
||||
check(sentCanFrames[i].dat[0]==(i%2?0x11:0),"Risk transition payload incorrect");
|
||||
}
|
||||
reset();{
|
||||
CanBus bus({});bus.start();blockTx=true;
|
||||
check(bus.queueRisk("danger"),"Initial non-clear rejected");
|
||||
waitFor([]{return txBlocked.load();});
|
||||
check(bus.queueRisk("clear")&&bus.queueRisk("warning"),"Busy transitions rejected");
|
||||
{std::lock_guard<std::mutex> lock(txMutex);blockTx=false;}txWake.notify_all();
|
||||
waitFor([&]{return bus.snapshot().transmitted==1;});
|
||||
std::this_thread::sleep_for(30ms);
|
||||
check(txCalls==1,"Returning to in-flight risk sent redundant frame");
|
||||
check(bus.queueRisk("clear"),"Later clear change lost");
|
||||
waitFor([&]{return bus.snapshot().transmitted==2;});
|
||||
bus.join();
|
||||
check(sentCanFrames.size()==2&&sentCanFrames[0].dat[0]==0x11&&sentCanFrames[1].dat[0]==0,
|
||||
"Latest-state coalescing lost final clear");
|
||||
}
|
||||
std::cout<<"PASS CAN/CANFD CH0 reception, CH1 transmission, expiry/recovery, TX validation, change-triggered binary risk 0x1F6, errors and lifecycle cleanup\n";
|
||||
}catch(std::exception const& e){std::cerr<<e.what()<<'\n';return 1;}
|
||||
|
||||
Reference in New Issue
Block a user