220 lines
9.2 KiB
C#
220 lines
9.2 KiB
C#
using Aurora.Api;
|
|
using AuroraPLC;
|
|
using System;
|
|
using System.Threading;
|
|
using ActUtlType64Lib;
|
|
using System.Linq;
|
|
|
|
namespace AuroraMXComponent
|
|
{
|
|
public class MXComponent : PLCNetwork
|
|
{
|
|
private readonly Logger Log = new Logger("PLC MXComponent");
|
|
|
|
private ActUtlType64Class _actUtlType;
|
|
//private DotUtlType64.DotUtlType64 _dotUtlType;
|
|
|
|
private const int _success = 0;
|
|
private bool IsConnected = false;
|
|
|
|
public MXComponent()
|
|
{
|
|
_actUtlType = new ActUtlType64Class();
|
|
//_dotUtlType = new DotUtlType64.DotUtlType64();
|
|
}
|
|
|
|
public override void SetInitParams(int logicalStationNo)
|
|
{
|
|
_actUtlType.ActLogicalStationNumber = logicalStationNo;
|
|
}
|
|
|
|
public override bool OpenToPLC()
|
|
{
|
|
try {
|
|
if (IsConnected) {
|
|
var result = _actUtlType.Close();
|
|
IsConnected = !result.Equals(_success);
|
|
Log.I($"Close: 0x{result:x8} [Hex]");
|
|
Thread.Sleep(500); // 미츠비시 MxComponent 문서의 최소 500 [ms] 간격
|
|
}
|
|
//================================================
|
|
//_actUtlType.ActLogicalStationNumber = stationNo;
|
|
var res = _actUtlType.Open();
|
|
IsConnected = res.Equals(_success);
|
|
Log.I($"Open: 0x{res:x8} [Hex]");
|
|
|
|
if (IsConnected == false) { // 연결 실패
|
|
// 타임 아웃 에러이면, 1회 재시도
|
|
if (res == 0x0180840B) {
|
|
Thread.Sleep(500); // 딜레이를 얼마를 줄까...
|
|
res = _actUtlType.Open();
|
|
IsConnected = res.Equals(_success);
|
|
Log.I($"Open: 2nd trial: 0x{res:x8} [Hex]");
|
|
}
|
|
}
|
|
return IsConnected;
|
|
} catch (Exception ex) {
|
|
Log.E($"Exception from Open: {ex.Message}");
|
|
IsConnected = false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public override bool CloseToPLC()
|
|
{
|
|
try {
|
|
var res = _actUtlType.Close();
|
|
IsConnected = !res.Equals(_success); // 닫기 성공이면, false
|
|
Log.I($"Close: 0x{res:x8} [Hex]");
|
|
Thread.Sleep(500); // 그냥 500 ms 딜레이를 항상 주자
|
|
/*
|
|
if (res == _success) {
|
|
// 문서상, Close 후, 다시 Open 실행할 때까지 최소 500 ms 이상 간격을 두라고 함
|
|
Thread.Sleep(500);
|
|
}
|
|
*/
|
|
} catch (Exception ex) {
|
|
Log.E($"Exception from Close: {ex.Message}");
|
|
// 실패이므로, 연결 flag 도 off 해놓자.
|
|
IsConnected = false;
|
|
return false;
|
|
}
|
|
// Disconnected --> 성공
|
|
return !IsConnected;
|
|
}
|
|
|
|
// Aurora 프로그램이 종료할 때만 호출이 필요함...(필요없을 것 같은데, 삭제 검토)
|
|
public override void Terminate()
|
|
{
|
|
// Aurora 의 비활성화가 완료된 후에 널 포인터로 정리하자..
|
|
// CloseToPLC 에서 널 포인터를 해버리면, 데이터 프로세싱 스레드에서 문제가 발생한다.
|
|
// 프로그램이 완전히 종료할 때 널을 할까?? 흠... 굳이 필요없을 것 같기도 하고....
|
|
_actUtlType = null;
|
|
Log.I("_actUtlType = null");
|
|
}
|
|
|
|
// (out 대신) 지역변수를 생성하지 않기 위해서, ref 로 readData 를 받음
|
|
public override bool ReceiveFromPLC(in string devLabel, int readByteSize, ref short[] readData)
|
|
{
|
|
/*
|
|
// 여긴 굳이 try-catch 가 불필요할 것 같은데..
|
|
//return mdReceive(pathOfChannel, stationNo, devType, startDevNo, ref readByteSize, ref readData).Equals(_success);
|
|
// 일단 그냥 테스트용....
|
|
// 2 바이트 단위로 읽음 (접미사 2 가 없으면 받는변수는 4바이트여서 post processing 필요, 메뉴얼 참고)
|
|
var res = _dotUtlType.ReadDeviceBlock2(ref devLabel, readByteSize, ref readData);
|
|
*/
|
|
int res = -1;
|
|
try {
|
|
// 되는 코드
|
|
//res = _actUtlType.ReadDeviceBlock2(devLabel, readByteSize, out readData[0]);
|
|
|
|
if (devLabel.Contains("M") && int.TryParse(devLabel.Substring(1), out int value))
|
|
{
|
|
int multiplevalue = (value / 16) * 16;
|
|
int remindervalue = value % 16;
|
|
string str = string.Format("M{0}", multiplevalue);
|
|
short[] tempreaddata = new short[readData.Count()];
|
|
res = _actUtlType.ReadDeviceBlock2(str, readByteSize, out tempreaddata[0]);
|
|
for (int i = 0; i < readData.Count(); i++)
|
|
{
|
|
int current = tempreaddata[i] & 0xFFFF;
|
|
|
|
if (i >= readData.Count() - 1)
|
|
{
|
|
readData[i] = (short)(current >> remindervalue);
|
|
}
|
|
else
|
|
{
|
|
int next = tempreaddata[i + 1] & 0xFFFF;
|
|
int combined = ((next & ((1 << remindervalue) - 1)) << (16 - remindervalue)) | (current >> remindervalue);
|
|
readData[i] = (short)(combined & 0xFFFF);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ res = _actUtlType.ReadDeviceBlock2(devLabel, readByteSize, out readData[0]); }
|
|
}
|
|
catch (Exception ex) {
|
|
#if DEBUG
|
|
//Log.E($"Exception (ReadDeviceBlock2): {ex.StackTrace}; {ex.Message}");
|
|
Log.E($"Exception (ReadDeviceBlock2): {ex.Message}");
|
|
//Console.WriteLine($"Exception is occured: {ex.StackTrace}; {ex.Message}");
|
|
#endif
|
|
}
|
|
return res.Equals(_success);
|
|
}
|
|
|
|
public override bool SendToPLC(in string devLabel, int writeSize, ref short[] writeData)
|
|
{
|
|
int res = -1;
|
|
try {
|
|
if (devLabel.Contains("M") && int.TryParse(devLabel.Substring(1), out int value))
|
|
{
|
|
int multiplevalue = (value / 16) * 16;
|
|
int remindervalue = value % 16;
|
|
string str = string.Format("M{0}", multiplevalue);
|
|
short[] tempWriteData = new short[writeData.Count()];
|
|
for (int i = 0; i < writeData.Count(); i++)
|
|
{
|
|
int current = (writeData[i] << remindervalue) & 0xFFFF;
|
|
|
|
if (i == 0)
|
|
{
|
|
tempWriteData[i] = (short)(current);
|
|
}
|
|
else
|
|
{
|
|
int before = writeData[i - 1] & 0xFFFF;
|
|
int combined = ((before >> (16 - remindervalue))&0xFFFF) | (current);
|
|
tempWriteData[i] = (short)(combined & 0xFFFF);
|
|
}
|
|
}
|
|
res = _actUtlType.WriteDeviceBlock2(str, writeSize, ref tempWriteData[0]);
|
|
}
|
|
else res = _actUtlType.WriteDeviceBlock2(devLabel, writeSize, ref writeData[0]);
|
|
} catch (Exception ex) {
|
|
#if DEBUG
|
|
Log.E($"Exception (WriteDeviceBlock2): {ex.Message}");
|
|
#endif
|
|
}
|
|
return res.Equals(_success);
|
|
}
|
|
|
|
//============================================================================
|
|
//-------- data update 스레드 이외에서 사용하는 API ------------//
|
|
//------------ B/W 이외의 장치 수신
|
|
public override bool ReceiveDeviceBlock2FromPLC(in string szDevice, int readSize, ref short[] readData)
|
|
{
|
|
if (IsConnected) {
|
|
// 솔더링: 멀티 스레드로 호출되는데, 문제 없었음
|
|
return ReceiveFromPLC(in szDevice, readSize, ref readData);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//===============================================
|
|
//-------- MX Component 한정
|
|
// 솔더링 코드에서 비트 입력에 write 하는 경우 때문에 업데이트 스레드에서 잠깐 사용 (코드 수정 후 사용안함)
|
|
public override bool SetDevice2(in string devLabel, short writeData)
|
|
{
|
|
int res = -1;
|
|
try {
|
|
res = _actUtlType.SetDevice2(devLabel, writeData);
|
|
} catch (Exception ex) {
|
|
#if DEBUG
|
|
Log.E($"Exception (SetDevice2): {ex.Message}");
|
|
//Console.WriteLine($"Exception is occured: {ex.StackTrace}; {ex.Message}");
|
|
#endif
|
|
}
|
|
// 실패에 대한 로그를 남겨야 할까??
|
|
if (res.Equals(_success) != true) { // 에러 발생
|
|
Log.E($"SetDevice2 fail: 0x{res:x8} [HEX]");
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|