Now that we know what Example II is about, we are ready to start with the main code: the code for handling vector channels.
In order to create our Output channel to support vectors, we will have to modify its mounting inside Dewesoft. It will accept an array containing values of the type Single. That is why we have to set the channel's dimension (property DimCount
) to 1. If we would need our Output channel to support matrix samples, we would have set DimCount
property to 2. We also set the size of the vector we want to output. In this method, we will set it to 1 and we will change this property later to fit the size of the input vectors.
void DewesoftBridge::mountChannels()
{
const std::vector<int> chIndexVector = {1};
outputChannel = pluginGroup->MountChannelEx(pluginGuid, long(chIndexVector.size()), fromStdVec(chIndexVector));
outputChannel->SetDataType(long(ChannelDataType::Single));
outputChannel->Name = "Latch";
outputChannel->Unit_ = "";
outputChannel->SetAsync(true);
outputChannel->ExpectedAsyncRate = 5;
outputChannel->Used = true;
outputChannel->ArrayChannel = true;
outputChannel->ArrayInfo->DimCount = 1;
outputChannel->ArrayInfo->DimSizes[0] = 1;
outputChannel->ArrayInfo->Init();
}
Every time we make changes to the dimension of the array channel by setting the DimCount or DimSizes properties, we need to call the Init() method.
There is one thing we need to keep an eye on: ExpectedAsyncRate
property of our output channel. This warrants a slight detour:
Expected async rate per second
If our module contains asynchronous output channels, we have to set their expected rate per second. You can think of this as "approximately how many samples will I be adding to this channel per second". We can change the value of this setting in DewesoftBridge::mountChannels()
by modifying the channel's ExpectedAsyncRate
. This setting is required because we need to help Dewesoft figure out much memory it needs to reserve for our channel. While we can calculate this value in any way we want, it can be useful if we know the rate of our output channel is somehow going to be connected to the rate of some other input channel, in which case we can simply set the outputChannel->ExpectedAsyncRate
to inputChannel->ExpectedAsyncRate
.
We can set ExpectedAsyncRate
to a completely arbitrary value, but if the ExpectedAsyncRate
is set too high Dewesoft will reserve too much memory and if it is set too low we might lose some important data. We don't need to set ExpectedAsyncRate
to the exact value, but we need to specify it to within an order of magnitude.
Note that since our output channel is now a vector channel, Dewesoft will be reserving ArrayInfo->ArraySize
times as much memory as it would if the channel were a simple scalar channel; so we have to be extra careful to not put in a number that is too high.
Method DewesoftBridge::onEstablishConnections()
is called when the acquisition is started (this occurs when ch. setup is entered). In this method, we will retrieve the Input and Criteria channels by their names. If names are not yet set (this will happen every time when no channel had been set in ComboBoxes containing channel names), we do not set any channels.
STDMETHODIMP DewesoftBridge::onEstablishConnections()
{
setInputChannel(inputChannelName.c_str());
setCriteriaChannel(criteriaChannelName.c_str());
return S_OK;
}
As mentioned before, we still need to set the size of the vectors we want to output. This needs to be done before Dewesoft reserves space to run our plugin, so we have to add an event that gets triggered before this happens. This event is called evPreInitiate
and we need to add it to plugin_impl.cpp file, where we also define the method that gets triggered when this event happens. The method needs to be declared in the plugin_impl.h file.
STDMETHODIMP eventBeforeReserveMemory();
STDMETHODIMP Plugin::raw_OnEvent(enum EventIDs eventID, VARIANT inParam, VARIANT* outParam)
{
switch (eventID)
{
case evPreInitiate:
returnValue = eventBeforeReserveMemory();
break;
return returnValue;
}
STDMETHODIMP Plugin::eventBeforeReserveMemory()
{
return bridge.onBeforeReserveMemory();
}
As we see in the code above the method that gets triggered when evPreInitiate happens triggers a method defined in DewesoftBridge. We will add the declaration and definition of this method to the bridge.
STDMETHODIMP onBeforeReserveMemory();
STDMETHODIMP DewesoftBridge::onBeforeReserveMemory()
{
if (!inputChannel)
return S_OK;
outputChannel->ArrayInfo->DimCount = 1;
outputChannel->ArrayInfo->DimSizes[0] = inputChannel->ArraySize;
outputChannel->ArrayInfo->Init();
outputChannel->ArrayInfo->AxisDef[0]->Name = inputChannel->ArrayInfo->AxisDef[0]->Name;
outputChannel->ArrayInfo->AxisDef[0]->_Unit = inputChannel->ArrayInfo->AxisDef[0]->_Unit;
outputChannel->ArrayInfo->AxisDef[0]->AxisType = atFloatLinearFunc;
outputChannel->ArrayInfo->AxisDef[0]->StartValue = inputChannel->ArrayInfo->AxisDef[0]->StartValue;
outputChannel->ArrayInfo->AxisDef[0]->StepValue = inputChannel->ArrayInfo->AxisDef[0]->StepValue;
return S_OK;
}
We have now successfully mounted an output channel to Dewesoft, set its axis values, and updated the Output channel to support outputting vector values from the Input channel. All there is left to do is to actually output vector values to the Output channel.
This will be done inside onGetData()
method. For precautionary reasons, we also check if the Input channel or Criteria Channel is nullptr, otherwise, the Input channel could be uninitialized when new setup was created. To insert data to the Output channel we will use the method AddAsyncData(...)
which allows insertion of a vector to the Output channel.
STDMETHODIMP DewesoftBridge::onGetData()
{
if (!inputChannel || !criteriaChannel)
return S_OK;
if (inputChannel->DBDataSize == 0 || criteriaChannel->DBDataSize == 0)
return S_OK;
int blockSizeCriteriaChannel =
(criteriaChannel->DBPos - (lastPosChecked % criteriaChannel->DBBufSize) + criteriaChannel->DBBufSize) % criteriaChannel->DBBufSize;
for (int i = 0; i < blockSizeCriteriaChannel - 1; i++)
{
float currentSampleCriteriaChannel = criteriaChannel->DBValues[lastPosChecked % criteriaChannel->DBBufSize];
float nextSampleCriteriaChannel = criteriaChannel->DBValues[(lastPosChecked + 1) % criteriaChannel->DBBufSize];
bool crossedEdgeCriteria = protutorial_latchmath_vector.checkCrossedEdgeCriteria(currentSampleCriteriaChannel,
nextSampleCriteriaChannel,
criteriaLimit,
edgeType);
if (crossedEdgeCriteria)
{
double time = criteriaChannel->DBTimeStamp[(lastPosChecked + 1) % criteriaChannel->DBBufSize];
int posInputChannel = (inputChannel->DBPos - 1);
std::vector<float> results;
for (int j = 0; j < outputChannel->ArraySize; j++)
{
float value = inputChannel->DBValues[posInputChannel * inputChannel->ArraySize + j];
results.push_back(value);
}
if (outputChannel)
outputChannel->AddAsyncData(fromStdVec(results), time);
}
lastPosChecked++;
}
return S_OK;
}