This won't copy&paste, compile and run, but should give you a leg up. It was written quite a while ago, and I'm not really a C# programmer, nor have I learnt much of the .Net framework, so I make no claims as to its quality.onyak wrote:soares, can I get copy of your source code? I want to build my own tools for the Concept2 in C# and have no idea where to start.
Code: Select all
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Threading;
using System.Runtime.InteropServices; // this is for DllImport
using System.Timers;
namespace PM3App
{
enum CSAFE : uint
{
CSAFE_GETSTATUS_CMD = 0x80,
CSAFE_RESET_CMD = 0x81,
CSAFE_GOIDLE_CMD = 0x82,
CSAFE_GOHAVEID_CMD = 0x83,
CSAFE_GOINUSE_CMD = 0x85,
CSAFE_GOFINISHED_CMD = 0x86,
CSAFE_GOREADY_CMD = 0x87,
CSAFE_BADID_CMD = 0x88,
CSAFE_GETVERSION_CMD = 0x91,
CSAFE_GETID_CMD = 0x92,
CSAFE_GETUNITS_CMD = 0x93,
CSAFE_GETSERIAL_CMD = 0x94,
CSAFE_GETLIST_CMD = 0x98,
CSAFE_GETUTILIZATION_CMD = 0x99,
CSAFE_GETMOTORCURRENT_CMD = 0x9A,
CSAFE_GETODOMETER_CMD = 0x9B,
CSAFE_GETERRORCODE_CMD = 0x9C,
CSAFE_GETSERVICECODE_CMD = 0x9D,
CSAFE_GETUSERCFG1_CMD = 0x9E,
CSAFE_GETUSERCFG2_CMD = 0x9F,
CSAFE_GETTWORK_CMD = 0xA0,
CSAFE_GETHORIZONTAL_CMD = 0xA1,
CSAFE_GETVERTICAL_CMD = 0xA2,
CSAFE_GETCALORIES_CMD = 0xA3,
CSAFE_GETPROGRAM_CMD = 0xA4,
CSAFE_GETSPEED_CMD = 0xA5,
CSAFE_GETPACE_CMD = 0xA6,
CSAFE_GETCADENCE_CMD = 0xA7,
CSAFE_GETGRADE_CMD = 0xA8,
CSAFE_GETGEAR_CMD = 0xA9,
CSAFE_GETUPLIST_CMD = 0xAA,
CSAFE_GETUSERINFO_CMD = 0xAB,
CSAFE_GETTORQUE_CMD = 0xAC,
CSAFE_GETHRCUR_CMD = 0xB0,
CSAFE_GETHRTZONE_CMD = 0xB2,
CSAFE_GETMETS_CMD = 0xB3,
CSAFE_GETPOWER_CMD = 0xB4,
CSAFE_GETHRAVG_CMD = 0xB5,
CSAFE_GETHRMAX_CMD = 0xB6,
CSAFE_GETUSERDATA1_CMD = 0xBE,
CSAFE_GETUSERDATA2_CMD = 0xBF,
CSAFE_SETUSERCFG1_CMD = 0x1A,
CSAFE_SETTWORK_CMD = 0x20,
CSAFE_SETHORIZONTAL_CMD = 0x21,
CSAFE_SETPROGRAM_CMD = 0x24,
CSAFE_SETTARGETHR_CMD = 0x30,
CSAFE_PM_GET_WORKDISTANCE = 0xA3,
CSAFE_PM_GET_WORKTIME = 0xA0,
CSAFE_PM_SET_SPLITDURATION = 0x05,
CSAFE_PM_GET_FORCEPLOTDATA = 0x6B,
CSAFE_PM_GET_DRAGFACTOR = 0xC1,
CSAFE_PM_GET_STROKESTATE = 0xBF,
CSAFE_UNITS_METER = 0x24
}
enum StrokeState { Idle = 0, Catch = 1, Drive = 2, Dwell = 3, Recovery = 4 }
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
[ DllImport("RPPM3DDI.dll", CallingConvention=CallingConvention.Cdecl) ]
public static extern ushort tkcmdsetDDI_init();
[ DllImport("RPPM3DDI.dll", CallingConvention=CallingConvention.Cdecl) ]
public static extern ushort tkcmdsetDDI_discover_pm3s(
string product_name,
ushort starting_address,
ref ushort num_units);
[ DllImport("RPPM3Csafe.dll", CallingConvention=CallingConvention.Cdecl) ]
public static extern ushort tkcmdsetCSAFE_init_protocol(ushort timeout);
[ DllImport("RPPM3Csafe.dll", CallingConvention=CallingConvention.Cdecl) ]
public static extern ushort tkcmdsetCSAFE_command(
ushort unit_address,
ushort cmd_data_size,
uint [] cmd_data,
ref ushort rsp_data_size,
uint [] rsp_data);
void PM3GetStatusThread()
{
StrokeState PreviousStrokeState = StrokeState.Idle;
bool bNewStroke = false;
bool bEndOfDrive = false;
double dWorkTime = 0;
double dWorkDistanceRowed = 0;
DateTime timeStart = DateTime.Now;
DateTime timeStop = DateTime.Now;
long timeDrive = 0;
long timeRecovery = 0;
while (!bTerminate)
{
if (!bPause && bConnected && !bRaceFinished)
{
StrokeState CurrentStrokeState = StrokeState.Idle;
ushort ecode = 0;
uint CurrentSPM = 0;
uint CurrentHR = 0;
bNewStroke = false;
bEndOfDrive = false;
uint [] cmd_data = new uint [64];
ushort cmd_data_size;
uint [] rsp_data = new uint [1024];
ushort rsp_data_size = 0;
cmd_data_size = 0;
// PM3 extension commands wrapped up
cmd_data[cmd_data_size++] = (uint)CSAFE.CSAFE_SETUSERCFG1_CMD;
cmd_data[cmd_data_size++] = 0x04; //4 commands -- REMEMBER to make this match
cmd_data[cmd_data_size++] = (uint)CSAFE.CSAFE_PM_GET_STROKESTATE;
cmd_data[cmd_data_size++] = (uint)CSAFE.CSAFE_PM_GET_DRAGFACTOR;
cmd_data[cmd_data_size++] = (uint)CSAFE.CSAFE_PM_GET_WORKDISTANCE;
cmd_data[cmd_data_size++] = (uint)CSAFE.CSAFE_PM_GET_WORKTIME;
// standard commands follow
cmd_data[cmd_data_size++] = (uint)CSAFE.CSAFE_GETPACE_CMD;
cmd_data[cmd_data_size++] = (uint)CSAFE.CSAFE_GETPOWER_CMD;
cmd_data[cmd_data_size++] = (uint)CSAFE.CSAFE_GETCADENCE_CMD;
cmd_data[cmd_data_size++] = (uint)CSAFE.CSAFE_GETHRCUR_CMD;
// CSAFE_GETHRCUR_CMD
ecode = tkcmdsetCSAFE_command(0, cmd_data_size, cmd_data, ref rsp_data_size, rsp_data);
if (0 == ecode)
{
uint currentbyte = 0;
uint datalength = 0;
if (rsp_data[currentbyte] == (uint)CSAFE.CSAFE_SETUSERCFG1_CMD)
{
currentbyte+=2;
}
if (rsp_data[currentbyte] == (uint)CSAFE.CSAFE_PM_GET_STROKESTATE)
{
currentbyte++;
datalength = rsp_data[currentbyte];
currentbyte++;
switch (rsp_data[currentbyte])
{
case 0:
case 1:
CurrentStrokeState = StrokeState.Catch;
break;
case 2:
CurrentStrokeState = StrokeState.Drive;
break;
case 3:
CurrentStrokeState = StrokeState.Dwell;
break;
case 4:
CurrentStrokeState = StrokeState.Recovery;
break;
}
currentbyte+=datalength;
}
if (!bRaceStarted && CurrentStrokeState != StrokeState.Catch)
{
bFalseStart = true;
}
if (rsp_data[currentbyte] == (uint)CSAFE.CSAFE_PM_GET_DRAGFACTOR)
{
currentbyte++;
datalength = rsp_data[currentbyte];
currentbyte++;
uint DF = rsp_data[currentbyte];
currentbyte+=datalength;
}
if (rsp_data[currentbyte] == (uint)CSAFE.CSAFE_PM_GET_WORKDISTANCE)
{
currentbyte++;
datalength = rsp_data[currentbyte];
currentbyte++;
uint Distance = (rsp_data[currentbyte] + (rsp_data[currentbyte+1] << 8) + (rsp_data[currentbyte+2] << 16) + (rsp_data[currentbyte+3] << 24)) / 10;
uint fraction = rsp_data[currentbyte+4];
dWorkDistance = Distance + (fraction/10.0);
dCurrentDistance = dWorkDistance - dWorkDistancePreviousStage;
currentbyte+=datalength;
}
if (rsp_data[currentbyte] == (uint)CSAFE.CSAFE_PM_GET_WORKTIME)
{
currentbyte++;
datalength = rsp_data[currentbyte];
currentbyte++;
if (datalength == 5)
{
uint TimeInSeconds = (rsp_data[currentbyte] + (rsp_data[currentbyte+1] << 8) + (rsp_data[currentbyte+2] << 16) + (rsp_data[currentbyte+3] << 24)) / 100;
uint fraction = rsp_data[currentbyte+4];
uint SS = TimeInSeconds % 60;
uint MM = (TimeInSeconds / 60) % 60;
uint HH = (TimeInSeconds / 3600);
dWorkTime = TimeInSeconds + (fraction/100.0);
}
currentbyte+=datalength;
}
if (rsp_data[currentbyte] == (uint)CSAFE.CSAFE_GETPACE_CMD)
{
currentbyte++;
datalength = rsp_data[currentbyte];
currentbyte++;
// Pace is in seconds/Km
uint Pace = rsp_data[currentbyte] + (rsp_data[currentbyte+1] << 8);
// get pace in seconds / 500m
double fPace = Pace / 2.0;
// convert it to mins/500m
double minutes = Math.Floor(fPace / 60);
double seconds = fPace - (minutes * 60);
currentbyte+=datalength;
}
if (rsp_data[currentbyte] == (uint)CSAFE.CSAFE_GETPOWER_CMD)
{
currentbyte++;
datalength = rsp_data[currentbyte];
currentbyte++;
uint Power = rsp_data[currentbyte] + (rsp_data[currentbyte+1] << 8);
currentbyte+=datalength;
}
if (rsp_data[currentbyte] == (uint)CSAFE.CSAFE_GETCADENCE_CMD)
{
currentbyte++;
datalength = rsp_data[currentbyte];
currentbyte++;
CurrentSPM = rsp_data[currentbyte];
if (0 < CurrentSPM)
{
nSPM += CurrentSPM;
nSPMReads++;
avgSPM = (nSPM * 1.0) / (nSPMReads * 1.0);
}
currentbyte+=datalength;
}
if (rsp_data[currentbyte] == (uint)CSAFE.CSAFE_GETHRCUR_CMD)
{
currentbyte++;
datalength = rsp_data[currentbyte];
currentbyte++;
CurrentHR = rsp_data[currentbyte];
if (0 < CurrentHR)
{
nHR += CurrentHR;
nHRReads++;
avgHR = (nHR * 1.0) / (nHRReads * 1.0);
}
currentbyte+=datalength;
}
// END OF REQUESTED DATA
} // didn't get the data... (ecode != 0)
PreviousStrokeState = CurrentStrokeState;
}
// Sleep for 50ms as that's the minimum inter-frame gap, so let's be sure
//Thread.Sleep(50);
}
}
void PM3GetStatusThreadStart()
{
PM3GetStatusThread();
}
void UIUpdateThread()
{
while (!bTerminate)
{
if (!bPause && bConnected)
{
if (bUpdateUI)
{
// update UI
bUpdateUI = false;
}
}
Thread.Sleep(50);
}
}
void UIUpdateThreadStart()
{
UIUpdateThread();
}
public Form1()
{
InitializeComponent();
ushort ecode = 0;
ecode = tkcmdsetDDI_init();
if (0 == ecode)
{
// Init CSAFE protocol
ecode = tkcmdsetCSAFE_init_protocol(1000);
if (0 == ecode)
{
String sname = "Concept2 Performance Monitor 3 (PM3)";
ecode = tkcmdsetDDI_discover_pm3s(sname, 0, ref num_units);
if (0 == ecode)
{
if (num_units > 0)
{
bConnected = true;
labelPM3.BackColor = System.Drawing.Color.Green;
// start a thread to check the PM3(s)
Thread thread_PM3GetStatus = new Thread (new ThreadStart(PM3GetStatusThreadStart));
thread_PM3GetStatus.Start();
// start a thread to update the display(s)
Thread thread_UIUpdate = new Thread (new ThreadStart(UIUpdateThreadStart));
thread_UIUpdate.Start();
}
else
{
labelPM3.BackColor = System.Drawing.Color.Red;
}
}
else
{
labelPM3.BackColor = System.Drawing.Color.Red;
}
}
}
}
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private void Form1_Load(object sender, System.EventArgs e)
{
}
private void buttonReset_Click(object sender, System.EventArgs e)
{
uint [] cmd_data = new uint [64];
ushort cmd_data_size;
uint [] rsp_data = new uint [64];
ushort rsp_data_size = 0;
Level = 0;
Stage = 0;
StageSecondsLeft = 0;
nFailures = 0;
bRaceStarted = false;
bRaceFinished = false;
bFalseStart = false;
cmd_data_size = 0;
// re-set
cmd_data[cmd_data_size++] = (uint)CSAFE.CSAFE_GOFINISHED_CMD;
cmd_data[cmd_data_size++] = (uint)CSAFE.CSAFE_GOIDLE_CMD;
// go, go, go
cmd_data[cmd_data_size++] = (uint)CSAFE.CSAFE_GOHAVEID_CMD;
cmd_data[cmd_data_size++] = (uint)CSAFE.CSAFE_GOINUSE_CMD;
tkcmdsetCSAFE_command(0, cmd_data_size, cmd_data, ref rsp_data_size, rsp_data);
}
private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
bPause = true;
bTerminate = true;
}
}
}