0,0 → 1,1280 |
///============================================================================ |
/// MKLiveView |
/// Copyright © 2016 Steph |
/// |
///This file is part of MKLiveView. |
/// |
///MKLiveView is free software: you can redistribute it and/or modify |
///it under the terms of the GNU General Public License as published by |
///the Free Software Foundation, either version 3 of the License, or |
///(at your option) any later version. |
/// |
///MKLiveView is distributed in the hope that it will be useful, |
///but WITHOUT ANY WARRANTY; without even the implied warranty of |
///MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
///GNU General Public License for more details. |
/// |
///You should have received a copy of the GNU General Public License |
///along with cssRcon. If not, see <http://www.gnu.org/licenses/>. |
/// |
///============================================================================ |
///Credits: |
///Chootair (http://www.codeproject.com/script/Membership/View.aspx?mid=3941737) |
///for his "C# Avionic Instrument Controls" (http://www.codeproject.com/Articles/27411/C-Avionic-Instrument-Controls) |
///I used some of his code for displaying the compass |
/// |
///Tom Pyke (http://tom.pycke.be) |
///for his "Artifical horizon" (http://tom.pycke.be/mav/100/artificial-horizon) |
///Great job! |
/// |
/// and last but most of all to JOHN C. MACDONALD at Ira A. Fulton College of Engineering and Technology |
/// for his MIKROKOPTER SERIAL CONTROL TUTORIAL (http://hdl.lib.byu.edu/1877/2747) |
/// and the sourcode (http://hdl.lib.byu.edu/1877/2748) |
/// By his work I finally managed to get the communication with the Mikrokopter controllers to run |
/// Some of his code was used in this programm like the SimpelSerialPort class (with some changes) |
/// and the FilghtControllerMessage class |
/// |
///============================================================================ |
|
using System; |
using System.Data; |
using System.Drawing; |
using System.Text; |
using System.Windows.Forms; |
using System.IO; |
using System.Threading; |
using System.Diagnostics; |
using System.Runtime.InteropServices; |
|
namespace MKLiveView |
{ |
public partial class MainForm : Form |
{ |
|
[FlagsAttribute] |
enum NC_HWError0 : short |
{ |
None = 0, |
SPI_RX = 1, |
COMPASS_RX = 2, |
FC_INCOMPATIBLE = 4, |
COMPASS_INCOMPATIBLE = 8, |
GPS_RX = 16, |
COMPASS_VALUE = 32 |
}; |
[FlagsAttribute] |
enum FC_HWError0 : short |
{ |
None = 0, |
GYRO_NICK = 1, |
GYRO_ROLL = 2, |
GYRO_YAW = 4, |
ACC_NICK = 8, |
ACC_ROLL = 16, |
ACC_TOP = 32, |
PRESSURE = 64, |
CAREFREE = 128 |
}; |
[FlagsAttribute] |
enum FC_HWError1 : short |
{ |
None = 0, |
I2C = 1, |
BL_MISSING = 2, |
SPI_RX = 4, |
PPM = 8, |
MIXER = 16, |
RC_VOLTAGE = 32, |
ACC_NOT_CAL = 64, |
RES3 = 128 |
}; |
public enum LogMsgType { Incoming, Outgoing, Normal, Warning, Error }; |
// Various colors for logging info |
private Color[] LogMsgTypeColor = { Color.FromArgb(43, 145, 175), Color.Green, Color.Black, Color.Orange, Color.Red }; |
|
string[] sAnalogLabel = new string[32]; |
string[] sAnalogData = new string[32]; |
bool bReadContinously = false; |
bool check_HWError = false; |
bool _bCBInit = true; |
bool _init = true; |
bool _debugDataAutorefresh = true; |
bool _navCtrlDataAutorefresh = true; |
bool _blctrlDataAutorefresh = true; |
bool _OSDAutorefresh = true; |
int crcError = 0; |
int iLableIndex = 0; |
string filePath = Directory.GetCurrentDirectory(); |
string fileName = "NCLabelTexts.txt"; |
int _iCtrlAct = 0; |
int _iLifeCounter = 0; |
int iOSDPage = 0; |
int iOSDMax = 0; |
/// <summary> |
/// interval for sending debugdata (multiplied by 10ms) |
/// </summary> |
byte debugInterval = 25; //(=> 250ms) |
/// <summary> |
/// interval for sending BL-CTRL status (multiplied by 10ms) |
/// </summary> |
byte blctrlInterval = 45; |
/// <summary> |
/// interval for sending NAV-CTRL status (multiplied by 10ms) |
/// </summary> |
byte navctrlInterval = 80; |
/// <summary> |
/// interval for sending OSD page update (multiplied by 10ms) |
/// </summary> |
byte OSDInterval = 85; |
/// <summary> |
/// datatable for the debug data array - displayed on settings tabpage in datagridview |
/// </summary> |
DataTable dtAnalog = new DataTable(); |
public MainForm() |
{ |
InitializeComponent(); |
_readIni(); |
dtAnalog.Columns.Add("ID"); |
dtAnalog.Columns.Add("Value"); |
dataGridView1.DataSource = dtAnalog; |
simpleSerialPort.PortClosed += SimpleSerialPort_PortClosed; |
simpleSerialPort.PortOpened += SimpleSerialPort_PortOpened; |
simpleSerialPort.DataReceived += processMessage; |
chkbAutoBL.Checked = _blctrlDataAutorefresh; |
chkbAutoDbg.Checked = _debugDataAutorefresh; |
chkbAutoNav.Checked = _navCtrlDataAutorefresh; |
chkbAutoOSD.Checked = _OSDAutorefresh; |
labelTimingDebug.Text = (debugInterval * 10).ToString(); |
labelTimingBLCTRL.Text = (blctrlInterval * 10).ToString(); |
labelTimingNAV.Text = (navctrlInterval * 10).ToString(); |
labelTimingOSD.Text = (OSDInterval * 10).ToString(); |
tabControl1.TabPages.Remove(tabPageTesting); |
} |
#region events |
private void MainForm_Shown(object sender, EventArgs e) |
{ |
_loadLabelNames(); |
_init = false; |
} |
private void MainForm_FormClosed(object sender, FormClosedEventArgs e) |
{ |
_writeIni(); |
} |
private void SimpleSerialPort_PortOpened() |
{ |
btnConn.Invoke((Action)(() => btnConn.BackColor = Color.FromArgb(192, 255, 192))); |
btnConn.Invoke((Action)(() => btnConn.Text = "close" + Environment.NewLine + "serial port")); |
_getVersion(); |
Thread.Sleep(100); |
_OSDMenue(0); |
// _readCont(true); |
} |
private void SimpleSerialPort_PortClosed() |
{ |
btnConn.Invoke((Action)(() => btnConn.BackColor = Color.FromArgb(224, 224, 224))); |
btnConn.Invoke((Action)(() => btnConn.Text = "open" + Environment.NewLine + "serial port")); |
_readCont(false); |
} |
/// <summary> |
/// timer for refreshing subscription of subscribed data |
/// query lifecounter for connection failure |
/// </summary> |
private void timer1_Tick(object sender, EventArgs e) |
{ |
if(bReadContinously) |
{ |
if (_debugDataAutorefresh) { _readDebugData(true); Thread.Sleep(10); } |
|
if (_blctrlDataAutorefresh && _iCtrlAct == 2) { _readBLCtrl(true); Thread.Sleep(10); } |
|
if (_navCtrlDataAutorefresh && _iCtrlAct == 2) { _readNavData(true); Thread.Sleep(10); } |
check_HWError = true; |
_getVersion(); |
Thread.Sleep(10); |
if (_OSDAutorefresh) { _OSDMenueAutoRefresh(); } |
if (_iLifeCounter > 0) |
{ |
lblLifeCounter.BackColor = Color.FromArgb(0, 224, 0); |
_iLifeCounter = 0; |
} |
else |
{ |
Log(LogMsgType.Error, "No communication to NC/FC!"); |
lblLifeCounter.BackColor = Color.FromArgb(224, 0, 0); |
} |
} |
} |
private void cbOSD_SelectedIndexChanged(object sender, EventArgs e) |
{ |
if (!_bCBInit && cbOSD.SelectedIndex > -1) |
_OSDMenue(cbOSD.SelectedIndex); |
} |
|
private void chkbAutoDbg_CheckedChanged(object sender, EventArgs e) |
{ |
if(!_init) _debugDataAutorefresh = chkbAutoDbg.Checked; |
} |
private void chkbAutoNav_CheckedChanged(object sender, EventArgs e) |
{ |
if (!_init) _navCtrlDataAutorefresh = chkbAutoNav.Checked; |
} |
private void chkbAutoBL_CheckedChanged(object sender, EventArgs e) |
{ |
if (!_init) _blctrlDataAutorefresh = chkbAutoBL.Checked; |
} |
private void chkbAutoOSD_CheckedChanged(object sender, EventArgs e) |
{ |
if (!_init) _OSDAutorefresh = chkbAutoOSD.Checked; |
} |
|
private void cbTimingDebug_SelectedIndexChanged(object sender, EventArgs e) |
{ |
if (cbTimingDebug.SelectedIndex > -1) |
{ |
debugInterval = (byte)(Convert.ToInt16(cbTimingDebug.SelectedItem) / 10); |
labelTimingDebug.Text = (debugInterval * 10).ToString(); |
} |
} |
private void cbTimingNAV_SelectedIndexChanged(object sender, EventArgs e) |
{ |
if (cbTimingNAV.SelectedIndex > -1) |
{ |
navctrlInterval = (byte)(Convert.ToInt16(cbTimingNAV.SelectedItem) / 10); |
labelTimingNAV.Text = (navctrlInterval * 10).ToString(); |
} |
} |
private void cbTimingBLCTRL_SelectedIndexChanged(object sender, EventArgs e) |
{ |
if (cbTimingBLCTRL.SelectedIndex > -1) |
{ |
blctrlInterval = (byte)(Convert.ToInt16(cbTimingBLCTRL.SelectedItem) / 10); |
labelTimingBLCTRL.Text = (blctrlInterval * 10).ToString(); |
} |
} |
private void cbTimingOSD_SelectedIndexChanged(object sender, EventArgs e) |
{ |
if (cbTimingOSD.SelectedIndex > -1) |
{ |
OSDInterval = (byte)(Convert.ToInt16(cbTimingOSD.SelectedItem) / 10); |
labelTimingOSD.Text = (OSDInterval * 10).ToString(); |
} |
} |
#endregion events |
|
/// <summary> Log data to the terminal window. </summary> |
/// <param name="msgtype"> The type of message to be written. </param> |
/// <param name="msg"> The string containing the message to be shown. </param> |
private void Log(LogMsgType msgtype, string msg) |
{ |
rtfTerminal.Invoke(new EventHandler(delegate |
{ |
if (rtfTerminal.Lines.Length >= 1000) //Wenn Terminal mehr als 1000 Zeilen hat |
rtfTerminal.Select(42, (500 * 129)); //500 löschen |
rtfTerminal.Select(rtfTerminal.Text.Length, 0); |
rtfTerminal.SelectedText = string.Empty; |
rtfTerminal.SelectionFont = new Font(rtfTerminal.SelectionFont, FontStyle.Regular); |
rtfTerminal.SelectionColor = LogMsgTypeColor[(int)msgtype]; |
rtfTerminal.AppendText(msg + Environment.NewLine); |
rtfTerminal.ScrollToCaret(); |
})); |
} |
/// <summary> display the OSD text in 4 lines à 20 chars </summary> |
/// <param name="msgtype"> The type of message to be written. </param> |
/// <param name="msg"> The string containing the message to be shown. </param> |
private void OSD(LogMsgType msgtype, string msg) |
{ |
rtfOSD.Invoke(new EventHandler(delegate |
{ |
if (rtfOSD.Lines.Length > 4) |
rtfOSD.Clear(); |
rtfOSD.Select(rtfOSD.Text.Length,0); |
rtfOSD.SelectedText = string.Empty; |
rtfOSD.SelectionFont = new Font(rtfOSD.SelectionFont, FontStyle.Regular); |
rtfOSD.SelectionColor = LogMsgTypeColor[(int)msgtype]; |
rtfOSD.AppendText(msg + Environment.NewLine); |
if (rtfOSD.Text.IndexOf("ERR") > 0) |
{ |
rtfOSD.Select(rtfOSD.Text.IndexOf("ERR"), 40); |
rtfOSD.SelectionColor = LogMsgTypeColor[(int)LogMsgType.Error]; |
} |
})); |
} |
private void ErrorLog(LogMsgType msgtype, string msg) |
{ |
rtfError.Invoke(new EventHandler(delegate |
{ |
if (rtfError.Lines.Length > 4) |
rtfError.Clear(); |
rtfError.Focus(); |
rtfError.Select(rtfError.Text.Length, 0); |
rtfError.SelectedText = string.Empty; |
rtfError.SelectionFont = new Font(rtfError.SelectionFont, FontStyle.Regular); |
rtfError.SelectionColor = LogMsgTypeColor[(int)msgtype]; |
rtfError.AppendText(msg + Environment.NewLine); |
|
})); |
} |
|
#region functions |
|
/// <summary> Processing the messages and displaying them in the according form controls </summary> |
/// <param name="message"> message bytearray recieved by SimpleSerialPort class </param> |
private void processMessage(byte[] message) |
{ |
if (message.Length > 0) |
{ |
_iLifeCounter++; |
//Log(LogMsgType.Incoming, BitConverter.ToString(message)); |
//Log(LogMsgType.Incoming, message.Length.ToString()); |
string s = new string(ASCIIEncoding.ASCII.GetChars(message, 0, message.Length)); |
char cmdID; |
byte adr; |
byte[] data; |
if (message[0] != '#') |
Log(LogMsgType.Normal, s.Trim('\0', '\n','\r')); |
//Debug.Print(s); |
else |
{ |
FlightControllerMessage.ParseMessage(message, out cmdID, out adr, out data); |
|
if (adr == 255) { crcError++; } |
else crcError = 0; |
lblCRCErr.Invoke((Action)(() => lblCRCErr.Text = crcError.ToString())); |
|
if (adr > 0 && adr < 3 && adr != _iCtrlAct) //adr < 3: temporary workaround cause when I've connected the FC alone it always switches between mk3mag & FC every second...??? |
{ |
_iCtrlAct = adr; |
switch (adr) |
{ |
case 1: |
lblCtrl.Invoke((Action)(() => lblCtrl.Text = "FC")); |
lblNCCtrl.Invoke((Action)(() => lblNCCtrl.Text = "FC")); |
_setFieldsNA(); //display fields NA for FC |
break; |
case 2: |
lblCtrl.Invoke((Action)(() => lblCtrl.Text = "NC")); |
lblNCCtrl.Invoke((Action)(() => lblNCCtrl.Text = "NC")); |
break; |
case 3: |
lblCtrl.Invoke((Action)(() => lblCtrl.Text = "MK3MAG")); |
break; |
case 4: |
lblCtrl.Invoke((Action)(() => lblCtrl.Text = "BL-CTRL")); |
break; |
default: |
lblCtrl.Invoke((Action)(() => lblCtrl.Text = "....")); |
break; |
} |
_loadLabelNames(); |
} |
// else |
// Debug.Print("Address == 0?"); |
|
if (data != null && data.Length > 0) |
{ |
s = new string(ASCIIEncoding.ASCII.GetChars(data, 1, data.Length - 1)); |
s = s.Trim('\0', '\n'); |
|
switch (cmdID) |
{ |
case 'A': |
if (iLableIndex < 32) |
{ |
sAnalogLabel[iLableIndex] = s; |
if (dtAnalog.Rows.Count < 32) |
dtAnalog.Rows.Add(s, ""); |
else |
dtAnalog.Rows[iLableIndex].SetField(0, s); |
|
_getAnalogLabels(iLableIndex + 1); |
} |
Debug.Print(s); |
|
break; |
case 'D': |
if (data.Length == 66) |
{ |
int[] iAnalogData = new int[32]; |
|
int index = 0; |
Int16 i16 = 0; |
double dTemp = 0; |
for (int i = 2; i < 66; i += 2) |
{ |
i16 = data[i + 1]; |
i16 = (Int16)(i16 << 8); |
iAnalogData[index] = data[i] + i16; |
sAnalogData[index] = (data[i] + i16).ToString(); |
dtAnalog.Rows[index].SetField(1, sAnalogData[index]); |
|
if (adr == 2) //NC |
{ |
switch (index) |
{ |
case 0: //pitch (German: nick) |
artificialHorizon1.Invoke((Action)(() => artificialHorizon1.pitch_angle = ((double)iAnalogData[index] / (double)10))); |
lblNCPitch.Invoke((Action)(() => lblNCPitch.Text = ((double)iAnalogData[index] / (double)10).ToString("0.0°"))); |
break; |
case 1: //roll |
artificialHorizon1.Invoke((Action)(() => artificialHorizon1.roll_angle = ((double)iAnalogData[index] / (double)10))); |
lblNCRoll.Invoke((Action)(() => lblNCRoll.Text = ((double)iAnalogData[index] / (double)10).ToString("0.0°"))); |
break; |
case 4: //altitude |
lblNCAlt.Invoke((Action)(() => lblNCAlt.Text = ((double)iAnalogData[index] / (double)10).ToString("0.0 m"))); |
break; |
case 7: //Voltage |
lblNCVolt.Invoke((Action)(() => lblNCVolt.Text = ((double)iAnalogData[index] / (double)10).ToString("0.0 V"))); |
break; |
case 8: // Current |
lblNCCur.Invoke((Action)(() => lblNCCur.Text = ((double)iAnalogData[index] / (double)10).ToString("0.0 A"))); |
break; |
case 10: //heading |
lblNCCompass.Invoke((Action)(() => lblNCCompass.Text = sAnalogData[index] + "°")); |
headingIndicator1.Invoke((Action)(() => headingIndicator1.SetHeadingIndicatorParameters(iAnalogData[index]))); |
break; |
case 12: // SPI error |
lblNCSPI.Invoke((Action)(() => lblNCSPI.Text = sAnalogData[index])); |
break; |
case 14: //i2c error |
lblNCI2C.Invoke((Action)(() => lblNCI2C.Text = sAnalogData[index])); |
break; |
case 20: //Earthmagnet field |
lblNCMF.Invoke((Action)(() => lblNCMF.Text = sAnalogData[index] + "%")); |
break; |
case 21: //GroundSpeed |
lblNCGSpeed.Invoke((Action)(() => lblNCGSpeed.Text = ((double)iAnalogData[index] / (double)100).ToString("0.00 m/s"))); |
break; |
case 28: //Distance East from saved home position -> calculate distance with distance N + height |
dTemp = Math.Pow((double)iAnalogData[index],2) + Math.Pow((double)iAnalogData[index - 1],2); |
dTemp = Math.Sqrt(dTemp)/ (double)10; //'flat' distance from HP with N/E |
// lblNCDist.Invoke((Action)(() => lblNCDist.Text = dTemp.ToString("0.00"))); |
dTemp = Math.Pow(dTemp, 2) + Math.Pow(((double)iAnalogData[4] / (double)10), 2); //adding 'height' into calculation |
dTemp = Math.Sqrt(dTemp); |
lblNCDist.Invoke((Action)(() => lblNCDist.Text = dTemp.ToString("0 m"))); |
break; |
case 31: //Sats used |
lblNCSat.Invoke((Action)(() => lblNCSat.Text = sAnalogData[index])); |
break; |
} |
} |
if (adr == 1) //FC |
{ |
switch (index) |
{ |
case 0: //pitch (German: nick) |
artificialHorizon1.Invoke((Action)(() => artificialHorizon1.pitch_angle = ((double)iAnalogData[index] / (double)10))); |
lblNCPitch.Invoke((Action)(() => lblNCPitch.Text = ((double)iAnalogData[index] / (double)10).ToString("0.0°"))); |
break; |
case 1: //roll |
artificialHorizon1.Invoke((Action)(() => artificialHorizon1.roll_angle = ((double)iAnalogData[index] / (double)10))); |
lblNCRoll.Invoke((Action)(() => lblNCRoll.Text = ((double)iAnalogData[index] / (double)10).ToString("0.0°"))); |
break; |
case 5: //altitude |
lblNCAlt.Invoke((Action)(() => lblNCAlt.Text = ((double)iAnalogData[index] / (double)10).ToString("0.0 m"))); |
break; |
case 8: //heading |
lblNCCompass.Invoke((Action)(() => lblNCCompass.Text = sAnalogData[index] + "°")); |
headingIndicator1.Invoke((Action)(() => headingIndicator1.SetHeadingIndicatorParameters(iAnalogData[index]))); |
break; |
case 9: //Voltage |
lblNCVolt.Invoke((Action)(() => lblNCVolt.Text = ((double)iAnalogData[index] / (double)10).ToString("0.0 V"))); |
break; |
case 10: //Receiver quality |
lblNCRC.Invoke((Action)(() => lblNCRC.Text = sAnalogData[index])); |
break; |
case 22: // Current |
lblNCCur.Invoke((Action)(() => lblNCCur.Text = ((double)iAnalogData[index] / (double)10).ToString("0.0 A"))); |
break; |
case 23: //capacity used |
lblNCCap.Invoke((Action)(() => lblNCCap.Text = (iAnalogData[index]).ToString("0 mAh"))); |
break; |
case 27: // SPI error |
lblNCSPI.Invoke((Action)(() => lblNCSPI.Text = sAnalogData[index])); |
break; |
case 28: //i2c error |
lblNCI2C.Invoke((Action)(() => lblNCI2C.Text = sAnalogData[index])); |
break; |
} |
} |
index++; |
} |
} |
else |
Debug.Print("wrong data-length (66): " + data.Length.ToString()); |
break; |
|
case 'V': |
if (data.Length == 12) |
{ |
if (!check_HWError) |
{ |
string[] sVersionStruct = new string[10] { "SWMajor: ", "SWMinor: ", "ProtoMajor: ", "LabelTextCRC: ", "SWPatch: ", "HardwareError 1: ", "HardwareError 2: ", "HWMajor: ", "BL_Firmware: ", "Flags: " }; |
string sVersion = ""; |
//sbyte[] signed = Array.ConvertAll(data, b => unchecked((sbyte)b)); |
Log(LogMsgType.Warning, (adr == 1 ? "FC-" : "NC-") + "Version: "); |
sVersion = "HW V" + (data[7] / 10).ToString() + "." + (data[7] % 10).ToString(); |
Log(LogMsgType.Incoming, sVersion); |
sVersion = "SW V" + (data[0]).ToString() + "." + (data[1]).ToString() + ((char)(data[4] + 'a')).ToString(); |
Log(LogMsgType.Incoming, sVersion); |
Log(LogMsgType.Incoming, "BL-Firmware: V" + (data[8] / 100).ToString() + "." + (data[8] % 100).ToString()); |
} |
if(data[5] > 0) //error0 |
{ |
if(adr == 1) |
ErrorLog(LogMsgType.Error, "FC - HW-Error " + data[5].ToString() + ": " + ((FC_HWError0)data[5]).ToString()); |
if (adr == 2) |
ErrorLog(LogMsgType.Error, "NC - HW-Error " + data[5].ToString() + ": " + ((NC_HWError0)data[5]).ToString()); |
} |
if (data[6] > 0) //error1 |
{ |
if (adr == 1) |
ErrorLog(LogMsgType.Error, "FC - HW-Error " + data[6].ToString() + ": " + ((FC_HWError1)data[6]).ToString()); |
if (adr == 2) |
ErrorLog(LogMsgType.Error, "NC - Unknown HW-ERROR: " + data[6].ToString()); //@moment NC has only one error field |
} |
|
} |
check_HWError = false; |
break; |
|
case 'K'://BL-CTRL debug data from NC |
if (data.Length == 6 && data[0] < 8) |
{ |
Label lbCur = new Label(), lbTemp = new Label(); |
switch (data[0]) |
{ |
case 0: |
lbCur = LBLNCM1Cur; |
lbTemp = LBLNCM1Temp; |
break; |
case 1: |
lbCur = LBLNCM2Cur; |
lbTemp = LBLNCM2Temp; |
break; |
case 2: |
lbCur = LBLNCM3Cur; |
lbTemp = LBLNCM3Temp; |
break; |
case 3: |
lbCur = LBLNCM4Cur; |
lbTemp = LBLNCM4Temp; |
break; |
case 4: |
lbCur = LBLNCM5Cur; |
lbTemp = LBLNCM5Temp; |
break; |
case 5: |
lbCur = LBLNCM6Cur; |
lbTemp = LBLNCM6Temp; |
break; |
case 6: |
lbCur = LBLNCM7Cur; |
lbTemp = LBLNCM7Temp; |
break; |
case 7: |
lbCur = LBLNCM8Cur; |
lbTemp = LBLNCM8Temp; |
break; |
} |
if (lbCur != null) |
lbCur.Invoke((Action)(() => lbCur.Text = ((double)data[1] / (double)10).ToString("0.0 A"))); |
if (lbTemp != null) |
lbTemp.Invoke((Action)(() => lbTemp.Text = data[2].ToString("0 °C"))); |
|
} |
|
break; |
|
case 'O': //NC Data |
int i_16,iVal; |
i_16 = data[81]; |
i_16 = (Int16)(i_16 << 8); |
iVal = data[80] + i_16; |
lblNCCap.Invoke((Action)(() => lblNCCap.Text = iVal.ToString() + " mAh")); //Capacity used |
|
i_16 = data[56]; |
i_16 = (Int16)(i_16 << 8); |
iVal = data[55] + i_16; |
TimeSpan t = TimeSpan.FromSeconds(iVal); |
string Text = t.Hours.ToString("D2") + ":" + t.Minutes.ToString("D2") + ":" + t.Seconds.ToString("D2"); |
lblNCFlTime.Invoke((Action)(() => lblNCFlTime.Text = Text.ToString())); //Flying time |
|
lblNCRC.Invoke((Action)(() => lblNCRC.Text = data[66].ToString())); //RC quality |
lblNCErrNmbr.Invoke((Action)(() => lblNCErrNmbr.Text = data[69].ToString())); //NC Errornumber |
if (data[69] > 0) |
_readNCError(); |
break; |
|
case 'E': //NC error-string |
ErrorLog(LogMsgType.Error, "NC Error: " + s); |
|
break; |
|
case 'L': |
if(data.Length == 84) |
{ |
string sMessage = ""; |
iOSDPage = data[0]; |
iOSDMax = data[1]; |
if (cbOSD.Items.Count != iOSDMax) _initOSDCB(); |
sMessage = new string(ASCIIEncoding.ASCII.GetChars(data, 2, data.Length - 4)); |
OSD(LogMsgType.Incoming, sMessage.Substring(0,20)); |
OSD(LogMsgType.Incoming, sMessage.Substring(20,20)); |
OSD(LogMsgType.Incoming, sMessage.Substring(40,20)); |
OSD(LogMsgType.Incoming, sMessage.Substring(60,20)); |
lblOSDPageNr.Invoke((Action)(()=>lblOSDPageNr.Text = iOSDPage.ToString("[0]"))); |
|
} |
else |
OSD(LogMsgType.Incoming,"Wrong length: " + data.Length + " (should be 84)"); |
break; |
case 'H': |
if(data.Length == 81) |
{ |
string sMessage = ""; |
sMessage = new string(ASCIIEncoding.ASCII.GetChars(data, 0, data.Length - 1)); |
OSD(LogMsgType.Incoming, sMessage.Substring(0,20)); |
OSD(LogMsgType.Incoming, sMessage.Substring(20,20)); |
OSD(LogMsgType.Incoming, sMessage.Substring(40,20)); |
OSD(LogMsgType.Incoming, sMessage.Substring(60,20)); |
|
} |
else |
OSD(LogMsgType.Incoming, "Wrong length: " + data.Length + " (should be 81)"); |
break; |
|
//default: |
// Log(LogMsgType.Incoming, "cmd: " + cmdID.ToString()); |
// Log(LogMsgType.Incoming, BitConverter.ToString(data)); |
// break; |
} |
} |
//else |
//{ |
// Log(LogMsgType.Incoming, "cmd: " + cmdID.ToString()); |
// Log(LogMsgType.Incoming, BitConverter.ToString(data)); |
//} |
} |
} |
} |
|
/// <summary> send message to controller to request data |
/// for detailed info see http://wiki.mikrokopter.de/en/SerialProtocol/ |
/// </summary> |
/// <param name="CMDID"> the command ID </param> |
/// <param name="address"> the address of the controller: 0-any, 1-FC, 2-NC </param> |
private void _sendControllerMessage(char CMDID, byte address) |
{ |
if (simpleSerialPort.Port.IsOpen) |
{ |
Stream serialStream = simpleSerialPort.Port.BaseStream; |
byte[] bytes = FlightControllerMessage.CreateMessage(CMDID, address); |
serialStream.Write(bytes, 0, bytes.Length); |
|
} |
else |
Log(LogMsgType.Error, "NOT CONNECTED!"); |
} |
/// <summary> send message to controller to request data |
/// for detailed info see http://wiki.mikrokopter.de/en/SerialProtocol/ |
/// </summary> |
/// <param name="CMDID"> the command ID </param> |
/// <param name="address"> the address of the controller: 0-any, 1-FC, 2-NC </param> |
/// <param name="data"> additional data for the request</param> |
private void _sendControllerMessage(char CMDID, byte address, byte[]data) |
{ |
if (simpleSerialPort.Port.IsOpen) |
{ |
Stream serialStream = simpleSerialPort.Port.BaseStream; |
byte[] bytes = FlightControllerMessage.CreateMessage(CMDID, address,data); |
serialStream.Write(bytes, 0, bytes.Length); |
|
} |
else |
Log(LogMsgType.Error, "NOT CONNECTED!"); |
} |
|
/// <summary> |
/// read the analog-label names for the actual controller |
/// and load it into listbox |
/// </summary> |
void _loadLabelNames() |
{ |
if (_iCtrlAct > 0 && _iCtrlAct < 3) |
{ |
switch (_iCtrlAct) |
{ |
case 1: |
sAnalogLabel = Properties.Resources.FCLabelTexts.Split(new[] { Environment.NewLine }, StringSplitOptions.None); |
break; |
case 2: |
sAnalogLabel = Properties.Resources.NCLabelTexts.Split(new[] { Environment.NewLine }, StringSplitOptions.None); |
break; |
} |
for (int i = 0; i < 32; i++) |
{ |
if (dtAnalog.Rows.Count < 32) |
dtAnalog.Rows.Add(sAnalogLabel[i], ""); |
else |
dtAnalog.Rows[i].SetField(0, sAnalogLabel[i]); |
} |
dataGridView1.Invoke((Action)(()=>dataGridView1.Refresh())); |
} |
} |
/// <summary> |
/// no longer used... |
/// read the analog-label textfile for the actual controller |
/// </summary> |
private void _loadLabelFile() |
{ |
switch (_iCtrlAct) |
{ |
case 1: |
fileName = "FCLabelTexts.txt"; |
break; |
case 2: |
fileName = "NCLabelTexts.txt"; |
break; |
//default: |
// fileName = "NCLabelTexts.txt"; |
// break; |
} |
|
if (File.Exists(filePath + "\\" + fileName)) |
{ |
sAnalogLabel.Initialize(); |
sAnalogLabel = File.ReadAllLines(filePath + "\\" + fileName); |
lbLabels.Invoke((Action)(() => lbLabels.Items.Clear())); |
lbLabels.Invoke((Action)(() => lbLabels.Update())); |
lbLabels.Invoke((Action)(() => lbLabels.Items.AddRange(sAnalogLabel))); |
Console.WriteLine("Names loaded from file"); |
lblFileName.Invoke((Action)(() => lblFileName.Text = fileName)); |
} |
else |
{ |
_readCont(false); |
Log(LogMsgType.Error, "Label-file not found!"); |
Log(LogMsgType.Error, "Please go to settings-tab and load the label texts from the copter control (FC & NC)"); |
Log(LogMsgType.Error, "When done, you have to save the label texts with the 'save' button!"); |
} |
} |
/// <summary> |
/// no longer used... |
/// assign the analog-label names from the textfile to the datatable |
/// |
/// </summary> |
private void _assignLabelNames() |
{ |
if (lbLabels.Items.Count == 32) |
{ |
lbLabels.Items.CopyTo(sAnalogLabel, 0); |
for (int i = 0; i < 32; i++) |
{ |
if (dtAnalog.Rows.Count < 32) |
dtAnalog.Rows.Add(sAnalogLabel[i], ""); |
else |
dtAnalog.Rows[i].SetField(0, sAnalogLabel[i]); |
|
} |
} |
} |
/// <summary> |
/// get the version struct of actual controller |
/// </summary> |
/// <summary> |
/// get the labeltexts for the analog values |
/// </summary> |
private void _getAnalogLabels() |
{ |
if (simpleSerialPort.Port.IsOpen) |
{ |
iLableIndex = 0; |
for (int i = 0; i < 32; i++) |
{ |
Stream serialStream = simpleSerialPort.Port.BaseStream; |
byte[] bytes = FlightControllerMessage.CreateMessage('a', 0, new byte[1] { (byte)i }); |
serialStream.Write(bytes, 0, bytes.Length); |
Thread.Sleep(10); |
} |
} |
else |
Log(LogMsgType.Error, "NOT CONNECTED!"); |
} |
/// <summary> |
/// get the labeltext for a single label |
/// </summary> |
/// <param name="iIndex">index of the label</param> |
private void _getAnalogLabels(int iIndex) |
{ |
if (simpleSerialPort.Port.IsOpen) |
{ |
if (iIndex < 32) |
{ |
iLableIndex = iIndex; |
_sendControllerMessage('a', 0, new byte[1] { (byte)iLableIndex }); |
} |
} |
else |
Log(LogMsgType.Error, "NOT CONNECTED!"); |
} |
private void _getVersion() |
{ |
_sendControllerMessage('v', 0); |
} |
/// <summary> |
/// get FC version struct via NC |
/// by sending '1' as data (not documented in wiki...) |
/// returns HW error 255 (comment in uart1.c : tells the KopterTool that it is the FC-version) |
/// </summary> |
/// <param name="ctrl">controller number 1=FC</param> |
private void _getVersion(byte ctrl) |
{ |
_sendControllerMessage('v', 0, new byte[1] {ctrl}); |
} |
/// <summary> |
/// Switch back to NC by sending the 'Magic Packet' 0x1B,0x1B,0x55,0xAA,0x00 |
/// </summary> |
private void _switchToNC() |
{ |
if (simpleSerialPort.Port.IsOpen) |
{ |
Stream serialStream = simpleSerialPort.Port.BaseStream; |
byte[] bytes = new byte[5] { 0x1B,0x1B,0x55,0xAA,0x00 }; |
serialStream.Write(bytes, 0, bytes.Length); |
|
Thread.Sleep(100); |
_getVersion(); |
Thread.Sleep(100); |
_OSDMenue(0); |
} |
else |
Log(LogMsgType.Error, "NOT CONNECTED!"); |
} |
/// <summary> |
/// switch to FC |
/// </summary> |
private void _switchToFC() |
{ |
_sendControllerMessage('u', 2, new byte[1] { (byte)0 }); |
Thread.Sleep(100); |
_getVersion(); |
Thread.Sleep(100); |
_OSDMenue(0); |
} |
/// <summary> |
/// send RESET signal to FC |
/// </summary> |
private void _resetCtrl() |
{ |
_sendControllerMessage('R', 1); |
} |
/// <summary> |
/// poll the debug data (4sec subscription) |
/// </summary> |
/// <param name="auto"> onetimequery(false) or autoupdate(true) with set timing interval </param> |
private void _readDebugData(bool auto) |
{ |
byte interval = auto ? debugInterval : (byte)0; |
_sendControllerMessage('d', 0, new byte[1] { debugInterval }); |
} |
/// <summary> |
/// poll the BL-CTRL status via NC (4sec subscription) |
/// </summary> |
/// <param name="auto"> onetimequery(false) or autoupdate(true) with set timing interval </param> |
private void _readBLCtrl(bool auto) |
{ |
byte interval = auto ? blctrlInterval : (byte)0; |
_sendControllerMessage('k', 0, new byte[1] { interval }); |
} |
/// <summary> |
/// poll the NC data struct (4sec subscription) |
/// </summary> |
/// <param name="auto"> onetimequery(false) or autoupdate(true) with set timing interval </param> |
private void _readNavData(bool auto) |
{ |
byte interval = auto ? navctrlInterval : (byte)0; |
_sendControllerMessage('o', 2, new byte[1] { interval }); |
} |
/// <summary> |
/// get the errortext for pending NC error |
/// </summary> |
private void _readNCError() |
{ |
_sendControllerMessage('e', 2); |
} |
/// <summary> |
/// start/stop continous polling of controller values |
/// </summary> |
/// <param name="b">start/stop switch</param> |
void _readCont(bool b) |
{ |
bReadContinously = b; |
btnReadDebugCont.Invoke((Action)(() => btnReadDebugCont.Text = bReadContinously ? "stop automatic" + Environment.NewLine + "data refresh" : "start automatic" + Environment.NewLine + "data refresh")); |
btnReadDebugCont.Invoke((Action)(() => btnReadDebugCont.BackColor = bReadContinously ? Color.FromArgb(192, 255, 192) : Color.FromArgb(224, 224, 224))); |
if (bReadContinously) |
{ |
_readDebugData(true); |
if (_iCtrlAct == 2) { Thread.Sleep(10); _readBLCtrl(true);} |
if (_iCtrlAct == 2) { Thread.Sleep(10); _readNavData(true);} |
Thread.Sleep(10); |
_OSDMenueAutoRefresh(); |
lblLifeCounter.Invoke((Action)(() => lblLifeCounter.BackColor = Color.FromArgb(0, 224, 0))); |
} |
else |
lblLifeCounter.Invoke((Action)(() => lblLifeCounter.BackColor = Color.FromArgb(224, 224, 224))); |
_iLifeCounter = 0; |
} |
/// <summary> |
/// set fieldtexts to "NA" when not available with FC |
/// </summary> |
void _setFieldsNA() |
{ |
Thread.Sleep(100); |
Label lbCur = new Label(), lbTemp = new Label(); |
for (int i = 0; i < 8; i++) |
{ |
//BL-Ctrl Temp & Current |
switch (i) |
{ |
case 0: |
lbCur = LBLNCM1Cur; |
lbTemp = LBLNCM1Temp; |
break; |
case 1: |
lbCur = LBLNCM2Cur; |
lbTemp = LBLNCM2Temp; |
break; |
case 2: |
lbCur = LBLNCM3Cur; |
lbTemp = LBLNCM3Temp; |
break; |
case 3: |
lbCur = LBLNCM4Cur; |
lbTemp = LBLNCM4Temp; |
break; |
case 4: |
lbCur = LBLNCM5Cur; |
lbTemp = LBLNCM5Temp; |
break; |
case 5: |
lbCur = LBLNCM6Cur; |
lbTemp = LBLNCM6Temp; |
break; |
case 6: |
lbCur = LBLNCM7Cur; |
lbTemp = LBLNCM7Temp; |
break; |
case 7: |
lbCur = LBLNCM8Cur; |
lbTemp = LBLNCM8Temp; |
break; |
} |
if (lbCur != null) |
lbCur.Invoke((Action)(() => lbCur.Text = "NA")); |
if (lbTemp != null) |
lbTemp.Invoke((Action)(() => lbTemp.Text = "NA")); |
|
} |
lblNCFlTime.Invoke((Action)(() => lblNCFlTime.Text = "NA")); //FlightTime |
lblNCErrNmbr.Invoke((Action)(() => lblNCErrNmbr.Text = "NA")); //NC ErrorNr |
lblNCMF.Invoke((Action)(() => lblNCMF.Text = "NA")); //earth magnet field |
lblNCGSpeed.Invoke((Action)(() => lblNCGSpeed.Text = "NA")); //GroundSpeed |
lblNCDist.Invoke((Action)(() => lblNCDist.Text = "NA")); //Distance to HP |
lblNCSat.Invoke((Action)(() => lblNCSat.Text = "NA")); //Sats used |
} |
/// <summary> |
/// one time query of the OSD Menue with pagenumber |
/// </summary> |
/// <param name="iMenue">Menue page</param> |
void _OSDMenue(int iMenue) |
{ |
if (simpleSerialPort.Port.IsOpen) |
{ |
if (iMenue > iOSDMax) |
iMenue = 0; |
Stream serialStream = simpleSerialPort.Port.BaseStream; |
byte[] bytes = FlightControllerMessage.CreateMessage('l', 0, new byte[1] { (byte)iMenue }); |
serialStream.Write(bytes, 0, bytes.Length); |
} |
else |
Log(LogMsgType.Error, "NOT CONNECTED!"); |
|
} |
/// <summary> |
/// call the OSDMenue and start autorefresh |
/// usually by sending a menuekey |
/// a bit tricky - but by sending inverted value of 32 (32 = 0010 0000) you can start the OSD menue with autoupdate (abo) without switching the page with the keyvalues (0x1, 0x2) |
/// therefore the value has to be negative (inverted) in order to distinguish from old (2 line) menuestyle |
/// and must not have any bits of the menue keys 0x1 0x2 0x4 0x8 (0x10?) --> 0x20 = -33 |
/// </summary> |
void _OSDMenueAutoRefresh() |
{ |
_sendControllerMessage('h', 0, new byte[2] { unchecked((byte)(-33)),OSDInterval }); |
} |
void _OSDMenueAutoRefresh(byte key) |
{ |
_sendControllerMessage('h', 0, new byte[2] { unchecked((byte)~key), OSDInterval }); |
} |
/// <summary> |
/// initialize the OSD menue combobox |
/// combox is filled by numbers from 0 to max pagenumber |
/// </summary> |
void _initOSDCB() |
{ |
_bCBInit = true; |
if(iOSDMax == 0) |
{ |
_OSDMenue(0); |
Thread.Sleep(10); |
} |
cbOSD.Invoke((Action)(()=>cbOSD.Items.Clear())); |
for(int i = 0; i <= iOSDMax;i++) |
{ |
cbOSD.Invoke((Action)(() => cbOSD.Items.Add(i))); |
} |
cbOSD.Invoke((Action)(() => cbOSD.SelectedItem = iOSDPage)); |
_bCBInit = false; |
} |
void _readIni() |
{ |
if (!File.Exists(filePath + "\\MKLiveViewSettings.ini")) |
_writeIni(); |
IniFile ini = new IniFile("MKLiveViewSettings.ini"); |
ini.path = filePath + "\\MKLiveViewSettings.ini"; |
|
string sVal = ini.IniReadValue("default", "AutorefreshDebugData"); |
_debugDataAutorefresh = Convert.ToBoolean(sVal); |
sVal = ini.IniReadValue("default", "AutorefreshNavCtrlData"); |
_navCtrlDataAutorefresh = Convert.ToBoolean(sVal); |
sVal = ini.IniReadValue("default", "AutorefreshBLCtrlData"); |
_blctrlDataAutorefresh = Convert.ToBoolean(sVal); |
sVal = ini.IniReadValue("default", "AutorefreshOSDData"); |
_OSDAutorefresh = Convert.ToBoolean(sVal); |
|
sVal = ini.IniReadValue("default", "IntervalDebugData"); |
debugInterval = (byte)Convert.ToInt16(sVal); |
sVal = ini.IniReadValue("default", "IntervalNavCtrlData"); |
navctrlInterval = (byte)Convert.ToInt16(sVal); |
sVal = ini.IniReadValue("default", "IntervalBLCtrlData"); |
blctrlInterval = (byte)Convert.ToInt16(sVal); |
sVal = ini.IniReadValue("default", "IntervalOSDData"); |
OSDInterval = (byte)Convert.ToInt16(sVal); |
} |
void _writeIni() |
{ |
|
IniFile ini = new IniFile("MKLiveViewSettings.ini"); |
ini.path = filePath + "\\MKLiveViewSettings.ini"; |
|
ini.IniWriteValue("default", "AutorefreshDebugData", _debugDataAutorefresh ? "true":"false"); |
ini.IniWriteValue("default", "AutorefreshNavCtrlData", _navCtrlDataAutorefresh ? "true":"false"); |
ini.IniWriteValue("default", "AutorefreshBLCtrlData", _blctrlDataAutorefresh ? "true":"false"); |
ini.IniWriteValue("default", "AutorefreshOSDData", _OSDAutorefresh ? "true":"false"); |
|
ini.IniWriteValue("default", "IntervalDebugData", debugInterval.ToString()); |
ini.IniWriteValue("default", "IntervalNavCtrlData", navctrlInterval.ToString()); |
ini.IniWriteValue("default", "IntervalBLCtrlData", blctrlInterval.ToString()); |
ini.IniWriteValue("default", "IntervalOSDData", OSDInterval.ToString()); |
} |
|
#endregion functions |
|
#region buttons |
private void buttonReset_Click(object sender, EventArgs e) |
{ |
_resetCtrl(); |
} |
private void btnVersion_Click(object sender, EventArgs e) |
{ |
_getVersion(); |
} |
private void btnAnalogLabels_Click(object sender, EventArgs e) |
{ |
_getAnalogLabels(0); |
} |
private void btnDbgData_Click(object sender, EventArgs e) |
{ |
_readDebugData(false); //onetime reading of debug data --> subscription lasts 4sec - this means you will receive data for 4 seconds |
} |
private void btnSaveLabels_Click(object sender, EventArgs e) |
{ |
switch (_iCtrlAct) |
{ |
case 1: |
fileName = "FCLabelTexts.txt"; |
break; |
case 2: |
fileName = "NCLabelTexts.txt"; |
break; |
default: |
fileName = "NCLabelTexts.txt"; |
break; |
} |
if (sAnalogLabel[0] != null) |
{ |
File.WriteAllLines(filePath + "\\" + fileName, sAnalogLabel); |
Console.WriteLine("Names saved to file"); |
_loadLabelFile(); |
} |
else |
Log(LogMsgType.Warning, "there's no data -> read first from fc/nc!"); |
} |
private void btnLoadLabels_Click(object sender, EventArgs e) |
{ |
_assignLabelNames(); |
} |
private void btnReadLabelFile_Click(object sender, EventArgs e) |
{ |
_loadLabelFile(); |
} |
private void btnSwitchFC_Click(object sender, EventArgs e) |
{ |
_switchToFC(); |
} |
private void btnSwitchNC_Click(object sender, EventArgs e) |
{ |
_switchToNC(); |
} |
private void btnReadDbgCont_Click(object sender, EventArgs e) |
{ |
_readCont(!bReadContinously); |
} |
private void btnReadBLCtrl_Click(object sender, EventArgs e) |
{ |
|
if (_iCtrlAct == 2) _readBLCtrl(false); |
else Log(LogMsgType.Warning, "only available when connected to NC"); |
} |
private void btnGetNaviData_Click(object sender, EventArgs e) |
{ |
if (_iCtrlAct == 2) _readNavData(false); |
else Log(LogMsgType.Warning, "only available when connected to NC"); |
} |
private void btnConn_Click(object sender, EventArgs e) |
{ |
simpleSerialPort.Connect(!simpleSerialPort.Port.IsOpen); |
} |
private void button3_Click(object sender, EventArgs e) |
{ |
_getVersion(1); |
} |
private void button4_Click(object sender, EventArgs e) |
{ |
_getVersion(2); |
} |
private void btnOSD_Click(object sender, EventArgs e) |
{ |
if (iOSDPage > iOSDMax) |
iOSDPage = 0; |
_OSDMenue(iOSDPage); |
} |
private void btnOSDForward_Click(object sender, EventArgs e) |
{ |
iOSDPage++; |
if (iOSDPage > iOSDMax) |
iOSDPage = 0; |
|
_OSDMenue(iOSDPage); |
} |
private void btnOSDBackward_Click(object sender, EventArgs e) |
{ |
iOSDPage--; |
if (iOSDPage < 0) |
iOSDPage = iOSDMax; |
|
_OSDMenue(iOSDPage); |
} |
private void btnOSDAuto_Click(object sender, EventArgs e) |
{ |
_OSDMenueAutoRefresh(); |
} |
/// call the OSDMenue with Key 0x8 |
private void btnOSDLeave_Click(object sender, EventArgs e) |
{ |
_OSDMenueAutoRefresh(8); |
} |
/// call the OSDMenue with Key 0x4 |
private void btnOSDEnter_Click(object sender, EventArgs e) |
{ |
_OSDMenueAutoRefresh(4); |
} |
#endregion buttons |
|
} |
public class IniFile |
{ |
public string path; |
|
[DllImport("kernel32")] |
private static extern long WritePrivateProfileString(string section, |
string key, string val, string filePath); |
|
[DllImport("kernel32.dll", CharSet = CharSet.Auto)] |
static extern uint GetPrivateProfileSectionNames(IntPtr lpszReturnBuffer, |
uint nSize, string lpFileName); |
|
[DllImport("kernel32")] |
private static extern int GetPrivateProfileString(string section, |
string key, string def, StringBuilder retVal, |
int size, string filePath); |
|
public IniFile(string INIPath) |
{ |
path = INIPath; |
} |
|
public void IniWriteValue(string Section, string Key, string Value) |
{ |
WritePrivateProfileString(Section, Key, Value, this.path); |
} |
|
public string IniReadValue(string Section, string Key) |
{ |
StringBuilder temp = new StringBuilder(255); |
int i = GetPrivateProfileString(Section, Key, "", temp, 255, this.path); |
return temp.ToString(); |
} |
//Ini_sections auslesen in String-Array |
public string[] IniSectionNames() |
{ |
|
// uint MAX_BUFFER = 32767; |
uint MAX_BUFFER = 8388608; |
IntPtr pReturnedString = Marshal.AllocCoTaskMem((int)MAX_BUFFER); |
uint bytesReturned = GetPrivateProfileSectionNames(pReturnedString, MAX_BUFFER, this.path); |
if (bytesReturned == 0) |
{ |
Marshal.FreeCoTaskMem(pReturnedString); |
return null; |
} |
string local = Marshal.PtrToStringAuto(pReturnedString, (int)bytesReturned).ToString(); |
Marshal.FreeCoTaskMem(pReturnedString); |
//use of Substring below removes terminating null for split |
return local.Substring(0, local.Length - 1).Split('\0'); |
|
|
} |
} |
public static class ControlExtensions |
{ |
/// <summary> |
/// Execute a threadsafe operation, when accessing a control via another thread |
/// action is a lamdaexpression |
/// e.g. comboBox1.ExecuteThreadSafe(() => comboBox1.Enabled = true); |
/// </summary> |
/// <param name="control"> The control </param> |
/// <param name="action"> The 'action' to perform </param> |
public static void ExecuteThreadSafe(this Control control, Action action) |
{ |
if (control.InvokeRequired) |
{ |
control.BeginInvoke(action); //"BeginInvoke" is an async call -> threadsafety error when called to many times successively -> then take "Invoke" |
} |
else |
{ |
action.Invoke(); |
} |
} |
} |
|
} |