Commit 31c894b5 authored by yannick legoc's avatar yannick legoc
Browse files
parents 72906231 3465e4fa
......@@ -62,10 +62,13 @@ CAENCfdController::CAENCfdController(const string& name) : CAENAdcController(nam
cfdSignalZero.init(this, NOSAVE, "signal_zero");
dynamicRange.init(this, NOSAVE, "dynamic_range");
cfdTrap.init(this, SAVE, "cfd_trap");
adcTemperature.init(this, NOSAVE, "adc_temperature");
m_dgtzDriver.init(this, "cfd_driver");
registerFunction(TYPE);
adcTemperature.resize(16);
}
/*
......@@ -113,31 +116,33 @@ void CAENCfdController::postConfiguration() {
registerRefresher(cfdSignalZero, &CAENCfdController::refreshCfdSignalZeroProperty, this);
registerRefresher(dynamicRange, &CAENCfdController::refreshDynamicRangeProperty, this);
registerUpdater(m_dgtzDriver->adcTemperature, &CAENCfdController::updateAdcTemperatureProperty, this);
updateBoardProperties();
}
void CAENCfdController::refreshRecordLengthProperty(int32 aValue) throw (CannotSetValue) {
m_dgtzDriver->recordLength = aValue;
m_dgtzDriver->recordLength.update(aValue);
}
void CAENCfdController::refreshcfdTrapProperty(int32 aValue) throw (CannotSetValue) {
m_dgtzDriver->cfdTrap = aValue;
m_dgtzDriver->cfdTrap.update(aValue);
}
void CAENCfdController::refreshNbChannelsProperty(int32 aValue) throw (CannotSetValue) {
minRange = 0;
maxRange = aValue - 1;
m_dgtzDriver->nbChannels = aValue;
m_dgtzDriver->nbChannels.update(aValue);
if (displayChannel() >= aValue)
displayChannel = aValue - 1;
}
void CAENCfdController::refreshDppAcqModeProperty(int32 aValue) throw (CannotSetValue) {
CAENAdcController::refreshDppAcqModeProperty(aValue);
m_dgtzDriver->dppAcqMode = aValue;
m_dgtzDriver->dppAcqMode.update(aValue);
boost::thread td(boost::bind(&CAENCfdController::initBoard, this));
}
......@@ -212,9 +217,9 @@ void CAENCfdController::refreshDisplayChannelProperty(int32 aChannel) throw (Can
digitalGain.update(m_dgtzDriver->digitalGain.get(aChannel));
int32 trapGain = m_dgtzDriver->trapezoidGain.get(aChannel);
if (trapGain == 0)
trapGain = 1;
float64 trapGain = m_dgtzDriver->trapezoidGain.get(aChannel);
if (trapGain == 0.0)
trapGain = 1.0;
trapezoidGain.update(trapGain);
decimation.update(m_dgtzDriver->decimation.get(aChannel));
......@@ -359,6 +364,7 @@ void CAENCfdController::refreshChannelActiveProperty(int32 aValue) throw (Cannot
mask += (m_dgtzDriver->channelActive.get(i) << i);
}
m_dgtzDriver->channelMask = mask;
boost::thread td(boost::bind(&CAENCfdController::writeParam, this));
}
void CAENCfdController::refreshDcOffsetProperty(int32 aValue) throw (CannotSetValue) {
......@@ -441,12 +447,12 @@ void CAENCfdController::updateBoardProperties() {
void CAENCfdController::writeParam() {
m_dgtzDriver.execute(caen_cfd::CAENCfdDriver::WRITE_PARAMETERS_COMMAND, false);
m_dgtzDriver.execute(caen_cfd::CAENCfdDriver::WRITE_PARAMETERS_COMMAND, true);
}
void CAENCfdController::initBoard() {
m_dgtzDriver.execute(caen_cfd::CAENCfdDriver::SET_MODE_COMMAND, false);
m_dgtzDriver.execute(caen_cfd::CAENCfdDriver::SET_MODE_COMMAND, true);
}
void CAENCfdController::initChannel(int32 aChannel) {
......@@ -493,4 +499,10 @@ void CAENCfdController::clearData() {
commandProgression = 100;
}
void CAENCfdController::updateAdcTemperatureProperty(int32 index) {
adcTemperature.set(index, m_dgtzDriver->adcTemperature(index));
}
}
......@@ -82,6 +82,7 @@ public:
Property<int32> cfdSignalZero;
Property<int32> dynamicRange;
Property<int32> cfdTrap;
DynamicProperty<int32> adcTemperature;
// Channels properties outside dppParam
Property<int32> useFirstDerivative;
......@@ -92,7 +93,6 @@ protected:
private:
virtual void refreshNbChannelsProperty(int32 aValue) throw (CannotSetValue);
virtual void refreshDisplayChannelProperty(int32 aValue) throw (CannotSetValue);
virtual void refreshRecordLengthProperty(int32 aValue) throw (CannotSetValue);
......@@ -130,6 +130,7 @@ private:
virtual void updateBoardProperties();
void updateNbBitsProperty();
void updateAdcTemperatureProperty(int32 index);
virtual void clearData();
virtual void writeParam();
......
......@@ -43,6 +43,8 @@ DataBlock* decodeListModeBlock(AsciiDataFileManager* dataFileManager, AsciiDataF
DPPAcquisitionController::DPPAcquisitionController(const string& name) :
TimeAcquisitionController(name) {
writeParamDone = false;
setFamily(family::ACQUISITION, family::SETTING);
nbAdcControllers.init(this, SAVE, "nb_adc_controllers");
......@@ -148,9 +150,6 @@ void DPPAcquisitionController::postConfiguration() {
listModeContext.crateBoard.crates[0].boards[board].nbChannels = adcController[board]->nbChannels();
}
}
cout << "DPP context" << endl;
cout << listModeContext << endl;
}
/*
......@@ -306,7 +305,7 @@ void DPPAcquisitionController::refreshUseListModeProperty(bool aValue) throw (Ca
}
/*
* refreshUseListModeProperty
* refreshUsePn1CoincidenceModeProperty
*/
void DPPAcquisitionController::refreshUsePn1CoincidenceModeProperty(int32 aValue) throw (CannotSetValue) {
......@@ -366,62 +365,68 @@ void DPPAcquisitionController::resize() throw (CannotOpenFile, SlicesFileError,
*/
void DPPAcquisitionController::writeParams() {
int32 totalAdc = 0;
for (int32 i = 0; i < nDrivers(); ++i) {
totalAdc += channelsPerBoard.get(i);
}
totalChannels = totalAdc;
log(Level::s_Debug) << name << cursor << "Sending DPP parameters to acquisition card : listmode " << useListMode()
<< " , wanted adc " << totalChannels() << " , wanted resolution " << nbBits() << endlog;
if (writeParamDone == false) {
Parallel::begin();
for (int32 adc = 0; adc < nbAdcControllers(); ++adc) {
adcController[adc]->clearDataParallel();
}
Parallel::end();
int32 totalAdc = 0;
for (int32 i = 0; i < nDrivers(); ++i) {
totalAdc += channelsPerBoard.get(i);
}
totalChannels = totalAdc;
if (isModeChanged() == true) {
log(Level::s_Debug) << name << cursor << "Sending DPP parameters to acquisition card : listmode " << useListMode()
<< " , wanted adc " << totalChannels() << " , wanted resolution " << nbBits() << endlog;
// cout << "Sending DPP parameters to acquisition card : listmode " << useListMode()
// << " , wanted adc " << totalChannels() << " , wanted resolution " << nbBits() << endl;
Parallel::begin();
for (int32 i = 0; i < nDrivers(); ++i) {
dppDriver[i]->wantedMode = m_AcquisitionMode;
dppDriver[i]->nbBits = nbBits();
dppDriver[i]->nbChannels = channelsPerBoard.get(i);
dppDriver[i]->scopeChannel = scopeChannel();
dppDriver[i]->gateWidth = gateWidth();
dppDriver[i]->usePn1Coincidence = usePn1Coincidence();
dppDriver[i]->useListMode = (useListMode() == true ? 1 : 0);
dppDriver[i]->useScope = useScope();
for (int32 adc = 0; adc < nbAdcControllers(); ++adc) {
adcController[adc]->clearDataParallel();
}
Parallel::end();
if (isModeChanged() == true) {
Parallel::begin();
for (int32 i = 0; i < nDrivers(); ++i) {
dppDriver[i]->wantedMode = m_AcquisitionMode;
dppDriver[i]->nbBits = nbBits();
dppDriver[i]->nbChannels = channelsPerBoard.get(i);
dppDriver[i]->scopeChannel = scopeChannel();
dppDriver[i]->gateWidth = gateWidth();
dppDriver[i]->usePn1Coincidence = usePn1Coincidence();
dppDriver[i]->useListMode = (useListMode() == true ? 1 : 0);
dppDriver[i]->useScope = useScope();
/*
* Execute the write param command
*/
dppDriver[i].executeParallel(AcquisitionCommon::WRITE_PARAMETERS_COMMAND);
}
Parallel::end();
/*
* Execute the write param command
*/
dppDriver[i].executeParallel(AcquisitionCommon::WRITE_PARAMETERS_COMMAND);
}
Parallel::end();
/*
* Execute the write param command
*/
for (int32 i = 0; i < nDrivers(); ++i) {
dppDriver[i].execute(AcquisitionCommon::LIST_MODE_COMMAND, false, true);
}
for (int32 i = 0; i < nDrivers(); ++i) {
dppDriver[i].execute(AcquisitionCommon::LIST_MODE_COMMAND, false, true);
}
// starting DPPCoincidence
if (useListMode() && m_liveProcessController.isAssigned()) {
// starting DPPCoincidence
if (useListMode() && m_liveProcessController.isAssigned()) {
// set the lst context
setContext();
// set the lst context
setContext();
// start the process in parallel to avoid blocking
m_liveProcessController->startParallel();
// start the process in parallel to avoid blocking
m_liveProcessController->startParallel();
if (checkStatus() == true) {
log(Level::s_Debug) << name << emptycursor << "DPP parameters accepted by acquisition card(s): " << totalAdc
<< " x " << nbBits() << endlog;
if (checkStatus() == true) {
log(Level::s_Debug) << name << emptycursor << "DPP parameters accepted by acquisition card(s): " << totalAdc
<< " x " << nbBits() << endlog;
}
}
}
writeParamDone = true;
}
}
......
......@@ -145,6 +145,8 @@ private:
ControllerPtr<DPPLiveProcess> m_histogramController;
ControllerPtr<DPPLiveProcess> m_coincidenceController;
ControllerPtr<DPPLiveProcess> m_liveProcessController;
bool writeParamDone;
};
}
......
<plotdatas>
<plotdata key="pha_histogram_plot" legend_key="psd_controller.histogramLegend" color="FFFFFF" plugins="SETUP">
<plotdata key="psd_histogram_plot" legend_key="psd_controller.histogramLegend" color="FFFFFF" plugins="SETUP">
<dataY p_role="psd_controller1" p_name="curve0_table"/>
</plotdata>
<plotdata key="pha_input_plot" legend_key="psd_controller.inputLegend" color="F60B0B" plugins="SETUP">
<plotdata key="psd_input_plot" legend_key="psd_controller.inputLegend" color="F60B0B" plugins="SETUP">
<dataY p_role="psd_controller1" p_name="curve1_table"/>
</plotdata>
<plotdata key="pha_trapezoid_plot" legend_key="psd_controller.trapezoidLegend" color="0DC93D" plugins="SETUP">
<plotdata key="psd_trapezoid_plot" legend_key="psd_controller.trapezoidLegend" color="0DC93D" plugins="SETUP">
<dataY p_role="psd_controller1" p_name="curve2_table"/>
</plotdata>
<plotdata key="pha_trigger_plot" legend_key="psd_controller.triggerLegend" color="FFFD18" plugins="SETUP">
<plotdata key="psd_trigger_plot" legend_key="psd_controller.triggerLegend" color="FFFD18" plugins="SETUP">
<dataY p_role="psd_controller1" p_name="curve3_table"/>
</plotdata>
<plotdata key="pha_peaking_plot" legend_key="psd_controller.peakingLegend" color="18F0FF" plugins="SETUP">
<plotdata key="psd_peaking_plot" legend_key="psd_controller.peakingLegend" color="18F0FF" plugins="SETUP">
<dataY p_role="psd_controller1" p_name="curve4_table"/>
</plotdata>
......
......@@ -7,10 +7,10 @@
<newLine/>
<combo role="psd_controller1" property="dpp_acq_mode" prefix="psd_controller.acqMode" valuesAndLabels="psd_controller.oscilloscope,psd_controller.histogram,psd_controller.waveform"/>
<switchable_composite switcher_key="acqModeSwitcher" switch_values="0">
<plot_launcher role="psd_controller1" data="pha_input_plot,pha_trapezoid_plot,pha_trigger_plot,pha_peaking_plot" title="psd_controller.inputPlotTitle" height="20"/>
<plot_launcher role="psd_controller1" data="psd_input_plot,psd_trapezoid_plot,psd_trigger_plot,psd_peaking_plot" title="psd_controller.inputPlotTitle" height="20"/>
</switchable_composite>
<switchable_composite switcher_key="acqModeSwitcher" switch_values="2">
<text role="pha_controller1" property="record_length" prefix="psd_controller.recordLength"/>
<text role="psd_controller1" property="record_length" prefix="psd_controller.recordLength"/>
</switchable_composite>
</group>
......
......@@ -73,16 +73,8 @@ void RealCAENCfdDriver::init() {
readInfos();
open();
ret = 0;
// Enable channels
boardParams.ChannelMask = owner()->channelMask();
ret = CAEN_DGTZ_SetChannelEnableMask(m_boardHandle, boardParams.ChannelMask);
if (ret)
cerr << "Error CAEN_DGTZ_SetChannelEnableMask: " << ret << endl;
close();
// Set channel enable
setChannelEnable();
// Set acquisition mode
setAcqMode();
......@@ -102,7 +94,6 @@ void RealCAENCfdDriver::init() {
ret |= CAEN_DGTZ_WriteRegister(m_boardHandle, CAEN_DGTZ_ACQ_CONTROL_ADD, (d32 & 0xFFFFFFF0) | 0xD);
if (ret)
cerr << "Error setting start acquisition mode: " << ret << endl;
// Disable trig_in mode
ret |= CAEN_DGTZ_WriteRegister(m_boardHandle, CAEN_DGTZ_TRIGGER_SRC_ENABLE_ADD, 0x80000000);
if (ret)
......@@ -245,6 +236,8 @@ void RealCAENCfdDriver::reset() {
*/
void RealCAENCfdDriver::writeParam() {
setChannelEnable();
open();
for (int32 i=0; i<owner()->channels(); ++i) {
......@@ -387,6 +380,28 @@ void RealCAENCfdDriver::setAcqMode() {
if (ret)
cerr << "Error CAEN_DGTZ_SetDPPAcquisitionMode: " << ret << endl;
// Buffer organization
ret |= CAEN_DGTZ_SetDPPEventAggregation(m_boardHandle, boardParams.EventAggr, 0);
if (ret)
cerr << "Error CAEN_DGTZ_SetDPPEventAggregation: " << ret << endl;
close();
}
/*!
* \brief Set acquisition mode command implementation
*/
void RealCAENCfdDriver::setChannelEnable() {
open();
ret = 0;
// Enable channels
boardParams.ChannelMask = owner()->channelMask();
ret = CAEN_DGTZ_SetChannelEnableMask(m_boardHandle, boardParams.ChannelMask);
if (ret)
cerr << "Error CAEN_DGTZ_SetChannelEnableMask: " << ret << endl;
close();
}
......@@ -417,7 +432,9 @@ void RealCAENCfdDriver::readStatus() {
uint32_t val;
for (int32 i=0; i<owner()->channels(); ++i) {
ret |= CAEN_DGTZ_ReadRegister(m_boardHandle, 0x10A8 + (i<<8), &val);
owner()->adcTemperature.set(i, val);
if ((val > 0) || (val <100)) {
owner()->adcTemperature.set(i, val);
}
}
if (ret)
cerr << "Error CAEN_DGTZ_ReadRegister" << endl;
......@@ -466,7 +483,6 @@ void RealCAENCfdDriver::useCoincidence() {
ret |= CAEN_DGTZ_WriteRegister(m_boardHandle, addr, val);
if (ret)
cerr << "Error CAEN_DGTZ_WriteRegister" << endl;
}
// Channel Trigout Width (it determines the coincidence window).
......
......@@ -73,6 +73,11 @@ public:
*/
virtual void setAcqMode();
/*!
* \brief Set channel enable command implementation
*/
virtual void setChannelEnable();
/*!
* \brief Read Infos command implementation
*/
......
......@@ -25,9 +25,8 @@
<newLine/>
<group title="caen_cfd.channelParamsTitle">
<table_composite nbColumns="21">
<table_composite nbColumns="20">
<simple_label prefix="caen_cfd.channelActive"/>
<simple_label prefix="caen_cfd.dynamicRange"/>
<simple_label prefix="caen_cfd.pulsePolarity"/>
<simple_label prefix="caen_cfd.dcOffset"/>
<simple_label prefix="caen_cfd.threshold"/>
......@@ -47,12 +46,11 @@
<simple_label prefix="caen_cfd.fraction"/>
<simple_label prefix="caen_cfd.delay"/>
<simple_label prefix="caen_cfd.baseLine"/>
<dynamic_composite role="caen_cfd1" properties="channel_active_str,dynamic_range_str,pulse_polarity_str,
<dynamic_composite role="caen_cfd1" properties="channel_active_str,pulse_polarity_str,
dc_offset,trigger_threshold,trigger_smoothing,trigger_holdoff,rise_time,decay_time,trapezoid_rise,
trapezoid_flat,base_mean,trapezoid_gain,flat_top,peak_mean,base_holdoff,peak_holdoff,
cfd_active_str,cfd_fraction_str,cfd_delay,signal_zero">
<label role="caen_cfd1" property="channel_active_str"/>
<label role="caen_cfd1" property="dynamic_range_str"/>
<label role="caen_cfd1" property="pulse_polarity_str"/>
<label role="caen_cfd1" property="dc_offset"/>
<label role="caen_cfd1" property="trigger_threshold"/>
......
......@@ -144,7 +144,7 @@ void RealCAENPsdDriver::init() {
boardParams.EventAggr = 0; //2
}
else if (owner()->dppAcqMode() == acquisition::DPPAcquisitionCommon::SCOPE_MODE) {
boardParams.EventAggr = 2; //10
boardParams.EventAggr = 1; //10
}
else if (owner()->dppAcqMode() == acquisition::DPPAcquisitionCommon::WAVEFORM_MODE) {
boardParams.EventAggr = 1; //10
......@@ -243,6 +243,7 @@ void RealCAENPsdDriver::reset() {
*/
void RealCAENPsdDriver::writeParam() {
cout << owner()->getName() << " driver set acq mode " << owner()->dppAcqMode() << endl;
// Load DPP parameters into structure
for (int32 i=0; i<owner()->nbChannels(); ++i) {
dppParams.trgho = owner()->triggerHoldOff();
......
......@@ -139,7 +139,7 @@ void PerfectDfk31Au03Driver::read() {
p += 3;
}
}
ostringstream dec;
std::ostringstream dec;
img.write_stream(dec);
int32 arraySize = dec.str().size() / 4;
if (arraySize * 4 < dec.str().size()) {
......
......@@ -84,7 +84,7 @@ int32 RealDfk31Au03Driver::xioctl(int32 fd, int32 request, void *arg) {
void RealDfk31Au03Driver::process_image(const void *p_buf, const int32 len_buf) {
v4lconvert_yuyv_to_rgb24((uint8*) p_buf, (uint8*) dest_buf, XSIZE, YSIZE, len_buf);
owner()->RGBData = string(reinterpret_cast<const char *>(dest_buf), XSIZE * YSIZE * 3);
owner()->RGBData = std::string(reinterpret_cast<const char *>(dest_buf), XSIZE * YSIZE * 3);
uint32 p = 0;
png::image<png::rgb_pixel> img(XSIZE, YSIZE);
......@@ -94,7 +94,7 @@ void RealDfk31Au03Driver::process_image(const void *p_buf, const int32 len_buf)
p += 3;
}
}
ostringstream dec;
std::ostringstream dec;
img.write_stream(dec);
int32 arraySize = dec.str().size() / 4;
if (arraySize * 4 < dec.str().size()) {
......@@ -269,7 +269,7 @@ void RealDfk31Au03Driver::init_device() {
}
}
stringstream myStreamString;
std::stringstream myStreamString;
myStreamString << cap.card;
owner()->version = myStreamString.str();
......@@ -347,7 +347,7 @@ void RealDfk31Au03Driver::close_device() {
}
void RealDfk31Au03Driver::open_device() {
string video = "/dev/video0";
std::string video = "/dev/video0";
fd = open(video.c_str(), O_RDWR /* required */| O_NONBLOCK, 0);
if (fd <= 0) {
fprintf(stderr, "Cannot open '%s': %d, %s\n", video.c_str(), errno, strerror(errno));
......
......@@ -90,21 +90,38 @@ void RealQE65000Driver::start() {
//Open device
int32 result = sbapi_open_device(deviceId, &error);
if (result == 0) {
common::Date startTime = common::Date();
//Get size
int32 error = 0;
owner()->size = sbapi_spectrometer_get_formatted_spectrum_length(deviceId, spectrometerId, &error); //needed here??
common::Date time1 = common::Date();
//Set integration time
setIntegrationTime(owner()->integrationTime());
common::Date time2 = common::Date();
//Retrieve data
getFormattedSpectrum();
common::Date time3 = common::Date();
getWavelength();
common::Date time4 = common::Date();
owner()->commandProgression = PROGRESSION_END_DEVICE_CONTAINER;
sbapi_close_device(deviceId, &error);
// common::Date endTime = common::Date();
// common::Duration duration1 = time1 - startTime;
// common::Duration duration2 = time2 - time1;
// common::Duration duration3 = time3 - time2;
// common::Duration duration4 = time4 - time3;
// common::Duration duration5 = endTime - time4;
// cout << "TIMES " << duration1.getMilliseconds() << " // " << duration2.getMilliseconds() << " // " << duration3.getMilliseconds() << " // " << duration4.getMilliseconds() << " // " << duration5.getMilliseconds() << endl;
// cout << "TOTAL TIME " << (endTime - startTime).getMilliseconds() << endl;
} else {
cout << "error opening the device" << endl;
owner()->openError = true;
......@@ -179,7 +196,7 @@ void RealQE65000Driver::setIntegrationTime(int32 integrationTime) {
if (minimumIntegrationTime > integrationTime) {
integrationTime = minimumIntegrationTime;
}
cout << "in real qe65000 driver setting integration time to " << integrationTime << " minIntegration time " << minimumIntegrationTime <<endl;
//cout << "in real qe65000 driver setting integration time to " << integrationTime << " minIntegration time " << minimumIntegrationTime <<endl;
//api: spectrometerSetIntegrationTimeMicros (long deviceID, long featureID, int *errorCode, unsigned long integrationTimeMicros)
sbapi_spectrometer_set_integration_time_micros(deviceId, spectrometerId, &error, integrationTime);
}
......
......@@ -54,17 +54,13 @@ void SimulatedQE65000Driver::init() {
}
void SimulatedQE65000Driver::start() {
std::cout << "in start of simulated QE65000 driver " << std::endl;
// std::cout << "in start of simulated QE65000 driver " << std::endl;
owner()->commandProgression = 0;
//Simulate integrationTime taken into account
usleep(owner()->integrationTime());
std::cout << "in start of simulated QE65000 driver (1) " << std::endl;
//delete
delete[] owner()->xData();
delete[] owner()->yData();
// std::cout << "in start of simulated QE65000 driver (1) " << std::endl;
float64* m_tempXData = new float64[owner()->size()];
float64* m_tempYData = new float64[owner()->size()];
......@@ -85,7 +81,7 @@ void SimulatedQE65000Driver::start() {
m_tempYData[j] = 10000 * pdf(nd, j); //j + 100;
}
std::cout << "in start of simulated QE65000 driver (2) " << std::endl;
// std::cout << "in start of simulated QE65000 driver (2) " << std::endl;
owner()->xData.setSize(owner()->size());
owner()->xData.set(m_tempXData);
......@@ -93,7 +89,14 @@ void SimulatedQE65000Driver::start() {
owner()->yData.set(m_tempYData);
owner()->yData.setSize(owner()->size());
std::cout << "in start of simulated QE65000 driver (3) " << std::endl;
// std::cout << "in start of simulated QE65000 driver (3) " << std::endl;
// Delete local pointers
// delete[] m_tempXData;
// m_tempXData = nullptr;
// delete[] m_tempYData;
// m_tempYData = nullptr;
m_owner->sendProgressEvent(PROGRESSION_END_DEVICE_CONTAINER);
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment