660 lines
22 KiB
C#
660 lines
22 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Data;
|
|
using System.Drawing;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
|
|
namespace Test_ProPLC
|
|
{
|
|
public partial class Form1 : Form
|
|
{
|
|
arDev.FakePLC dev2;
|
|
public Form1()
|
|
{
|
|
InitializeComponent();
|
|
this.serialPort1.DataReceived += SerialPort1_DataReceived;
|
|
dev2 = new arDev.FakePLC();
|
|
this.dev2.ValueChanged += Dev2_ValueChanged;
|
|
this.dev2.Message += Dev2_Message;
|
|
this.FormClosed += Form1_FormClosed;
|
|
}
|
|
|
|
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
|
|
{
|
|
timer1.Stop();
|
|
}
|
|
|
|
private void Dev2_ValueChanged(object sender, arDev.Arduino.DIO.IOValueEventArgs e)
|
|
{
|
|
addMsg2($"val({e.ArrIDX}) -> {e.NewValue}");
|
|
|
|
}
|
|
|
|
byte[] buffer1;
|
|
List<byte> Tempbuffer1 = new List<byte>();
|
|
byte[] buffer2;
|
|
List<byte> Tempbuffer2 = new List<byte>();
|
|
|
|
|
|
Boolean STX11 = false;
|
|
Boolean STX21 = false;
|
|
Boolean ETX11 = false;
|
|
|
|
Boolean STX12 = false;
|
|
Boolean STX22 = false;
|
|
Boolean ETX12 = false;
|
|
|
|
|
|
//MAIN PLC
|
|
byte[] DIpinList1 = new byte[] { 22, 23, 24, 25, 26, 27, 28, 29, 41 };
|
|
string[] DIpinName1 = new string[] { "EMERGENCTY", "OVR_L", "LIDAR_STOP", "LIDAR_SLOW", "OVR_R", "MARK", "GATEOUT_B", "GATEOUT_F", "ALIGN" };
|
|
|
|
byte[] DOpinList1 = new byte[] { 33, 34, 35, 36, 37, 43, 44, 39, 40 };
|
|
string[] DopinName1 = new string[] { "MOT_POWER_GUIDE", "MOT_POWER_L", "MOT_POWER_R", "LIDAR_DIR", "CHARGE_ON", "MOT_DIR_L", "MOT_RUN_L", "MOT_DIR_R", "MOT_RUN_R" };
|
|
|
|
|
|
// SUB PLC
|
|
byte[] DIpinList2 = new byte[] { 22, 23, 24, 25, 41, 42, 43, 45, 38 };
|
|
string[] DIpinName2 = new string[] { "BTN_GUIDE_UP", "BTN_GUIDE_DN", "BTN_QA1", "BTN_QC", "LIM_HIGH_R", "LIM_LOW_R", "LIM_HIGH_L", "BTN_PACK", "LIM_LOW_L" };
|
|
|
|
|
|
byte[] DOpinList2 = new byte[] { 30, 31, 32, 33 };
|
|
string[] DopinName2 = new string[] { "GMOT_INT", "GMOT_DIR", "GMOT_RUN", "GMOT_STA" };
|
|
|
|
|
|
byte LEN1 = 0;
|
|
byte CHK1 = 0;
|
|
int bufferCount1 = 0;
|
|
|
|
byte LEN2 = 0;
|
|
byte CHK2 = 0;
|
|
int bufferCount2 = 0;
|
|
private void SerialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
|
|
{
|
|
bufferCount1 = this.serialPort1.BytesToRead;
|
|
byte[] rxBuffer = new byte[bufferCount1];
|
|
serialPort1.Read(rxBuffer, 0, bufferCount1);
|
|
foreach (byte incomingByte in rxBuffer)
|
|
{
|
|
if (STX11 == false)
|
|
{
|
|
if (incomingByte != '@')
|
|
{
|
|
STX21 = false;
|
|
ETX11 = false;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
STX11 = true;
|
|
Tempbuffer1.Add(incomingByte);
|
|
}
|
|
}
|
|
else if (STX21 == false)
|
|
{
|
|
if (Tempbuffer1.Count != 1 || Tempbuffer1.Count < 1 || Tempbuffer1[0] != '@' || incomingByte != '@')
|
|
{
|
|
STX11 = false;
|
|
ETX11 = false;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
STX21 = true;
|
|
Tempbuffer1.Add(incomingByte);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Tempbuffer1.Add(incomingByte);
|
|
|
|
//여기서부터는무조건 누적한다.
|
|
if (Tempbuffer1.Count == 3)
|
|
{
|
|
if (Tempbuffer1[0] != 0x40 || Tempbuffer1[1] != '@')
|
|
{
|
|
STX11 = false;
|
|
STX21 = false;
|
|
ETX11 = false;
|
|
LEN1 = 0;
|
|
Tempbuffer1.Clear();
|
|
}
|
|
else LEN1 = incomingByte; //데이터 길이가온다
|
|
}
|
|
else if (Tempbuffer1.Count == LEN1 + 2 + 1 + 1) //체크섬이 왔다
|
|
{
|
|
CHK1 = incomingByte;
|
|
}
|
|
else if (Tempbuffer1.Count == LEN1 + 2 + 1 + 1 + 1) //ETX1
|
|
{
|
|
if (incomingByte != 0x0D)
|
|
{
|
|
//ETX가 와야하는데 다른데이터가 왔다
|
|
STX11 = false;
|
|
STX21 = false;
|
|
ETX11 = false;
|
|
Console.WriteLine("에러 모두 파기");
|
|
Tempbuffer1.Clear();
|
|
}
|
|
}
|
|
else if (Tempbuffer1.Count == LEN1 + 2 + 1 + 1 + 1 + 1)
|
|
{
|
|
//전체길이를 만족햇다.
|
|
if (incomingByte != 0x0A)
|
|
{
|
|
//ETX가 와야하는데 다른데이터가 왔다
|
|
STX11 = false;
|
|
STX21 = false;
|
|
ETX11 = false;
|
|
Console.WriteLine("에러 모두 파기");
|
|
Tempbuffer1.Clear();
|
|
}
|
|
else
|
|
{
|
|
STX11 = false;
|
|
STX21 = false;
|
|
ETX11 = false;
|
|
if (buffer1 == null) buffer1 = new byte[Tempbuffer1.Count];
|
|
else if (buffer1.Length != Tempbuffer1.Count) Array.Resize(ref buffer1, Tempbuffer1.Count);
|
|
Tempbuffer1.CopyTo(buffer1);
|
|
Tempbuffer1.Clear();
|
|
parser(buffer1, tbMsg1, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
private void Dev2_Message(object sender, arDev.arRS232.MessageEventArgs e)
|
|
{
|
|
addMsg2($"{e.Message}");
|
|
}
|
|
|
|
|
|
delegate void DisplayMessage(string message);
|
|
|
|
void parser(byte[] buffer, Action<string> func, int type)
|
|
{
|
|
// DisplayMessage messageTarget;
|
|
|
|
var hexString = buffer.GetHexString();
|
|
var len = buffer[2];
|
|
if (len + 6 != buffer.Length)
|
|
{
|
|
|
|
func(string.Format("길이오류 예상:{0},수신:{1},{2}", len + 5, buffer.Length, hexString));
|
|
}
|
|
else if (buffer[0] != 0x40 || buffer[1] != 0x40 || buffer[buffer.Length - 2] != 0x0D || buffer[buffer.Length - 1] != 0x0A)
|
|
{
|
|
func(string.Format("헤더 오류 : {0}", hexString));
|
|
}
|
|
else
|
|
{
|
|
|
|
var cmd = buffer[3];
|
|
if (cmd == 'I') //IOData
|
|
{
|
|
if (len != 15)
|
|
{
|
|
func("IO수신길이오류:" + DateTime.Now.ToString("mm:ss.fff") + " 수신 : " + hexString);
|
|
}
|
|
else
|
|
{
|
|
var IOData = BitConverter.ToUInt32(buffer, 4);
|
|
var An1 = BitConverter.ToUInt16(buffer, 8);
|
|
var An2 = BitConverter.ToUInt16(buffer, 10);
|
|
var Ao11 = buffer[12];
|
|
var Ao12 = buffer[13];
|
|
var FGData = BitConverter.ToInt32(buffer, 14);
|
|
|
|
byte[] buf_iodata = new byte[4];
|
|
Array.Copy(buffer, 4, buf_iodata, 0, 4);
|
|
|
|
var ba = new System.Collections.BitArray(buf_iodata);
|
|
for (int i = 0; i < 32; i++)
|
|
{
|
|
Boolean val = ba[i];
|
|
if (val)
|
|
{
|
|
|
|
}
|
|
if (type == 0) this.gv1.setValue(i, val);
|
|
else this.gv2.setValue(i, val);
|
|
}
|
|
|
|
|
|
var binstr = Convert.ToString(IOData, 2).PadLeft(32, '0');
|
|
string msg = string.Format(DateTime.Now.ToString("mm:ss.fff") + " IO:{0},AN={1:0000},{2:0000},FG={3},AO={4},{5}",
|
|
binstr, An1, An2, FGData, Ao11, Ao12);
|
|
func(msg);
|
|
|
|
if (type == 0) gv1.Invalidate();
|
|
else gv2.Invalidate();
|
|
|
|
}
|
|
}
|
|
else if (cmd == 'T')
|
|
{
|
|
Boolean errorMessage = buffer[4] == 1;
|
|
var message = System.Text.Encoding.Default.GetString(buffer, 5, len - 2);//데이터타입과 에러여부 제거
|
|
if (type == 0)
|
|
addMsg1("Rx : " + hexString + ", 메세지: " + message);
|
|
else
|
|
addMsg2("Rx : " + hexString + ", 메세지: " + message);
|
|
}
|
|
else
|
|
{
|
|
if (type == 0)
|
|
addMsg1(DateTime.Now.ToString("mm:ss.fff") + " 수신 : " + hexString + "\n" + "Data=" + System.Text.Encoding.Default.GetString(buffer));
|
|
else
|
|
addMsg2(DateTime.Now.ToString("mm:ss.fff") + " 수신 : " + hexString + "\n" + "Data=" + System.Text.Encoding.Default.GetString(buffer));
|
|
|
|
//func();
|
|
}
|
|
|
|
|
|
|
|
}
|
|
//Console.WriteLine("Rx : " + hexString);
|
|
}
|
|
void tbMsg1(string m)
|
|
{
|
|
if (textBox3.InvokeRequired)
|
|
{
|
|
textBox3.BeginInvoke(new Action(() =>
|
|
{
|
|
textBox3.Text = (m + "\n");
|
|
// textBox3.ScrollToCaret();
|
|
}));
|
|
}
|
|
else
|
|
{
|
|
textBox3.Text = (m + "\n");
|
|
//textBox3.ScrollToCaret();
|
|
|
|
}
|
|
}
|
|
void tbMsg2(string m)
|
|
{
|
|
if (textBox4.InvokeRequired)
|
|
{
|
|
textBox4.BeginInvoke(new Action(() =>
|
|
{
|
|
textBox4.Text = (m + "\n");
|
|
// richTextBox2.ScrollToCaret();
|
|
}));
|
|
}
|
|
else
|
|
{
|
|
textBox4.Text = (m + "\n");
|
|
//richTextBox2.ScrollToCaret();
|
|
|
|
}
|
|
}
|
|
|
|
private void button1_Click(object sender, EventArgs e)
|
|
{
|
|
if (this.dev2.IsOpen)
|
|
{
|
|
this.serialPort1.Close();
|
|
this.button1.BackColor = SystemColors.Control;
|
|
}
|
|
else
|
|
{
|
|
if (textbox1.Text.isEmpty()) return;
|
|
this.serialPort1.PortName = textbox1.Text;
|
|
this.serialPort1.BaudRate = 250000;
|
|
this.serialPort1.Open();
|
|
button1.BackColor = Color.Lime;
|
|
}
|
|
}
|
|
|
|
|
|
private void timer1_Tick(object sender, EventArgs e)
|
|
{
|
|
this.Text = bufferCount1.ToString() + "/" + bufferCount2.ToString();
|
|
}
|
|
|
|
private void button2_Click(object sender, EventArgs e)
|
|
{
|
|
if (textbox2.Text.isEmpty()) return;
|
|
if (this.dev2.IsOpen)
|
|
{
|
|
this.dev2.Close();
|
|
this.button2.BackColor = SystemColors.Control;
|
|
}
|
|
else
|
|
{
|
|
this.dev2.PortName = textbox2.Text;
|
|
this.dev2.BaudRate = int.Parse(comboBox1.Text);
|
|
this.dev2.Open();
|
|
button2.BackColor = Color.Lime;
|
|
// timer1.Start();
|
|
}
|
|
addMsg2($"open:{this.dev2.IsOpen}");
|
|
|
|
|
|
}
|
|
|
|
private void Form1_Load(object sender, EventArgs e)
|
|
{
|
|
textbox1.Items.Clear();
|
|
textbox2.Items.Clear();
|
|
foreach (var port in System.IO.Ports.SerialPort.GetPortNames())
|
|
{
|
|
textbox1.Items.Add(port);
|
|
textbox2.Items.Add(port);
|
|
}
|
|
// textbox1.Text = "COM15";
|
|
// textbox2.Text = "COM16";
|
|
|
|
List<Boolean> values = new List<bool>();
|
|
for (int i = 0; i < 32; i++)
|
|
values.Add(false);
|
|
|
|
this.gv1.setValue(values.ToArray());
|
|
this.gv2.setValue(values.ToArray());
|
|
|
|
//그리드뷰 값초기화
|
|
for (int i = 0; i < 32; i++)
|
|
{
|
|
string pinName = i.ToString();
|
|
|
|
|
|
if (i < 16 && i < DIpinName1.Length)
|
|
pinName = string.Format("[{0}:{2}] {1}", i, DIpinName1[i], DIpinList1[i]);
|
|
else if (i >= 16 && (i - 16) < DopinName1.Length)
|
|
pinName = string.Format("[{0}:{2}] {1}", i, DopinName1[i - 16], DOpinList1[i - 16]);
|
|
|
|
|
|
|
|
this.gv1.setTitle(i, pinName);
|
|
this.gv1.setValue(i, 0);
|
|
}
|
|
|
|
for (int i = 0; i < 32; i++)
|
|
{
|
|
string pinName = i.ToString();
|
|
|
|
if (i < 16)
|
|
{
|
|
var pin = (arDev.FakePLC.DIName)i;
|
|
pinName = $"[{i}] {pin}";
|
|
}
|
|
else
|
|
{
|
|
var pin = (arDev.FakePLC.DOName)(i-16);
|
|
pinName = $"[{i}] {pin}";
|
|
}
|
|
|
|
this.gv2.setTitle(i, pinName);
|
|
this.gv2.setValue(i, 0);
|
|
}
|
|
timer1.Start();
|
|
|
|
}
|
|
|
|
private void gv1_ItemClick(object sender, arFrame.Control.GridView.ItemClickEventArgs e)
|
|
{
|
|
//sub item click
|
|
if (e.idx < 16)
|
|
{
|
|
Util.MsgE("입력은 상태를 변경할 수 없습니다");
|
|
}
|
|
else
|
|
{
|
|
//이 인덱스에 해당하는 포트번호를 가져왕야함
|
|
|
|
var dataIndex = e.idx - 16;
|
|
if (dataIndex >= 0)
|
|
{
|
|
var pinNo = DOpinList1[e.idx - 16];
|
|
var curVal = gv1.getValue(e.idx);
|
|
byte newval = (byte)(curVal == 0 ? 1 : 0);
|
|
|
|
Sendcommand1(eCommand1.SET_DOUTPUT, pinNo, newval);
|
|
}
|
|
else Util.MsgE("해당 주소는 허용되지 않습니다.");
|
|
}
|
|
}
|
|
|
|
byte makeChecksum(byte[] buffer)
|
|
{
|
|
//return 0;
|
|
byte chk = 0;
|
|
foreach (var b in buffer)
|
|
chk = (byte)(chk ^ b);
|
|
return chk;
|
|
}
|
|
|
|
enum eCommand1 : byte
|
|
{
|
|
LOAD = 0, //EEPROM 불러오기
|
|
SAVE, //EEPROM 저장
|
|
RESET, //초기화
|
|
SET_PINMODE, //PINMODE 설정
|
|
SET_DOUTPUT, //디지털출력설정(포트번호,값[1,0])
|
|
SET_AOUTPUT, //아날로그출력설정(포트GET_SETTING = 50, 포트번호,값(0~255)
|
|
GET_SETTING = 50, //셋팅값 요청
|
|
|
|
MOT = 100, //p1(0=left,1=right), p2(0=stop,1=pos run,2=neg run)
|
|
MOVE_GO,
|
|
MOVE_BACK,
|
|
MOVE_LEFT,
|
|
MOVE_RIGHT,
|
|
MOVE_STOP
|
|
}
|
|
|
|
|
|
enum eCommand2 : byte
|
|
{
|
|
LOAD = 0, //EEPROM 불러오기
|
|
SAVE, //EEPROM 저장
|
|
RESET, //초기화
|
|
SET_PINMODE, //PINMODE 설정
|
|
SET_DOUTPUT, //디지털출력설정(포트번호,값[1,0])
|
|
SET_AOUTPUT, //아날로그출력설정(포트번호,값[0~255])
|
|
GET_SETTING = 50, //셋팅값 요청
|
|
GUIDE_MOT,
|
|
SET_GUIDE_SPD = 100,//가이드커버(양쪽) 속도 설정(0~255)
|
|
}
|
|
void addMsg1(string m)
|
|
{
|
|
if (logMain.InvokeRequired)
|
|
{
|
|
logMain.BeginInvoke(new Action(() =>
|
|
{
|
|
if (m.IndexOf('\n') != -1) m = m.Replace("\r", "").Replace("\n", "");
|
|
logMain.AddMsg(m);
|
|
}));
|
|
}
|
|
else
|
|
{
|
|
if (m.IndexOf('\n') != -1) m = m.Replace("\r", "").Replace("\n", "");
|
|
logMain.AddMsg(m);
|
|
|
|
}
|
|
|
|
}
|
|
void addMsg2(string m)
|
|
{
|
|
if (logSub.InvokeRequired)
|
|
{
|
|
logSub.BeginInvoke(new Action(() =>
|
|
{
|
|
if (m.IndexOf('\n') != -1) m = m.Replace("\r", "").Replace("\n", "");
|
|
logSub.AddMsg(m);
|
|
|
|
}));
|
|
}
|
|
else
|
|
{
|
|
if (m.IndexOf('\n') != -1) m = m.Replace("\r", "").Replace("\n", "");
|
|
logSub.AddMsg(m);
|
|
}
|
|
|
|
}
|
|
private void gv2_ItemClick(object sender, arFrame.Control.GridView.ItemClickEventArgs e)
|
|
{
|
|
//sub item click
|
|
if (e.idx < 16)
|
|
{
|
|
Util.MsgE("입력은 상태를 변경할 수 없습니다");
|
|
}
|
|
else
|
|
{
|
|
//이 인덱스에 해당하는 포트번호를 가져왕야함
|
|
|
|
var dataIndex = e.idx - 16;
|
|
if (dataIndex >= 0)
|
|
{
|
|
var pinNo = DOpinList2[e.idx - 16];
|
|
var curVal = gv2.getValue(e.idx);
|
|
byte newval = (byte)(curVal == 0 ? 1 : 0);
|
|
|
|
// Sendcommand2(eCommand2.SET_DOUTPUT, pinNo, newval);
|
|
}
|
|
else Util.MsgE("해당 주소는 허용되지 않습니다.");
|
|
|
|
}
|
|
}
|
|
|
|
|
|
void Sendcommand1(eCommand1 cmd, byte p1, byte p2)
|
|
{
|
|
|
|
//이번호의 상태를 on/off 해줘야함
|
|
List<byte> buffer = new List<byte>();
|
|
|
|
buffer.Add((byte)cmd); //command
|
|
buffer.Add(p1);
|
|
buffer.Add(p2);
|
|
|
|
byte dataLen = (byte)buffer.Count; //데이터길이
|
|
byte checksum = makeChecksum(buffer.ToArray());
|
|
|
|
buffer.Insert(0, (byte)'@'); //stx
|
|
buffer.Insert(1, (byte)'@'); //stx
|
|
buffer.Insert(2, dataLen);
|
|
buffer.Add(checksum); //길이를 제외한 실제 데이터 영역만 체크섬
|
|
buffer.Add(0x0D); //etx
|
|
buffer.Add(0x0A); //etx
|
|
|
|
addMsg1("Tx Buffer : " + buffer.ToArray().GetHexString());
|
|
|
|
if (this.serialPort1.IsOpen)
|
|
serialPort1.Write(buffer.ToArray(), 0, buffer.Count);
|
|
else
|
|
addMsg1("포트(2)가 열리지 않았습니다");
|
|
}
|
|
private void button3_Click(object sender, EventArgs e)
|
|
{
|
|
//-zup
|
|
dev2.ZMot(arDev.FakePLC.ZMotDirection.Up);
|
|
}
|
|
|
|
private void button4_Click(object sender, EventArgs e)
|
|
{
|
|
//z-stop
|
|
dev2.ZMot(arDev.FakePLC.ZMotDirection.Stop);
|
|
}
|
|
|
|
private void button5_Click(object sender, EventArgs e)
|
|
{
|
|
//z-down
|
|
dev2.ZMot(arDev.FakePLC.ZMotDirection.Down);
|
|
}
|
|
|
|
private void button8_Click(object sender, EventArgs e)
|
|
{
|
|
Sendcommand1(eCommand1.MOT, 0, 1);
|
|
}
|
|
|
|
private void button6_Click(object sender, EventArgs e)
|
|
{
|
|
Sendcommand1(eCommand1.MOT, 0, 0);
|
|
}
|
|
|
|
private void button7_Click(object sender, EventArgs e)
|
|
{
|
|
Sendcommand1(eCommand1.MOT, 0, 2);
|
|
}
|
|
|
|
private void button10_Click(object sender, EventArgs e)
|
|
{
|
|
Sendcommand1(eCommand1.MOT, 1, 2);
|
|
}
|
|
|
|
private void button9_Click(object sender, EventArgs e)
|
|
{
|
|
Sendcommand1(eCommand1.MOT, 1, 0);
|
|
}
|
|
|
|
private void button11_Click(object sender, EventArgs e)
|
|
{
|
|
Sendcommand1(eCommand1.MOT, 1, 1);
|
|
}
|
|
|
|
private void button14_Click(object sender, EventArgs e)
|
|
{
|
|
Sendcommand1(eCommand1.MOVE_GO, 0, 0);
|
|
}
|
|
|
|
private void button12_Click(object sender, EventArgs e)
|
|
{
|
|
Sendcommand1(eCommand1.MOVE_STOP, 0, 0);
|
|
}
|
|
|
|
private void button13_Click(object sender, EventArgs e)
|
|
{
|
|
Sendcommand1(eCommand1.MOVE_BACK, 0, 0);
|
|
}
|
|
|
|
private void button15_Click(object sender, EventArgs e)
|
|
{
|
|
//지정된 %의 값을 전송한다
|
|
var but = sender as Button;
|
|
var percValue = byte.Parse(but.Text.ToString());
|
|
var realValue = (percValue / 100.0) * 255.0;
|
|
|
|
Sendcommand1(eCommand1.SET_AOUTPUT, 11, (byte)realValue);
|
|
Sendcommand1(eCommand1.SET_AOUTPUT, 12, (byte)realValue);
|
|
}
|
|
|
|
private void button34_Click(object sender, EventArgs e)
|
|
{
|
|
//지정된 %의 값을 전송한다
|
|
var but = sender as Button;
|
|
var percValue = byte.Parse(but.Text.ToString());
|
|
var realValue = (percValue / 100.0) * 255.0;
|
|
|
|
// Sendcommand2(eCommand2.SET_AOUTPUT, 11, (byte)realValue); //Z축 모터는 동일하게 사용한다
|
|
}
|
|
|
|
private void timer1_Tick_1(object sender, EventArgs e)
|
|
{
|
|
timer1.Stop();
|
|
if (dev2 != null && dev2.IsOpen)
|
|
{
|
|
for(byte i = 0; i < 32;i++)
|
|
{
|
|
bool v = false;
|
|
|
|
if (i < 16) v = dev2.GetValueI(i);
|
|
else v = dev2.GetValueO((byte)(i-16));
|
|
|
|
this.gv2.setValue(i, v);
|
|
}
|
|
this.gv2.Invalidate();
|
|
}
|
|
|
|
|
|
timer1.Start();
|
|
}
|
|
}
|
|
}
|